blob: 8588e058a70d3fc657b516a4816a564c62262dee [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "firewalld/iptables.h"
#include <string>
#include <vector>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <chromeos/process.h>
namespace {
const char kIptablesPath[] = "/sbin/iptables";
}
namespace firewalld {
IpTables::IpTables() : IpTables{kIptablesPath} {}
IpTables::IpTables(const std::string& path) : executable_path_{path} {}
IpTables::~IpTables() {
// Plug all holes when destructed.
PlugAllHoles();
}
bool IpTables::PunchTcpHole(uint16_t in_port) {
return PunchHole(in_port, &tcp_holes_, kProtocolTcp);
}
bool IpTables::PunchUdpHole(uint16_t in_port) {
return PunchHole(in_port, &udp_holes_, kProtocolUdp);
}
bool IpTables::PlugTcpHole(uint16_t in_port) {
return PlugHole(in_port, &tcp_holes_, kProtocolTcp);
}
bool IpTables::PlugUdpHole(uint16_t in_port) {
return PlugHole(in_port, &udp_holes_, kProtocolUdp);
}
bool IpTables::PunchHole(uint16_t port,
std::unordered_set<uint16_t>* holes,
enum ProtocolEnum protocol) {
if (port == 0) {
// Port 0 is not a valid TCP/UDP port.
return false;
}
if (holes->find(port) != holes->end()) {
// We have already punched a hole for |port|.
// Be idempotent: do nothing and succeed.
return true;
}
std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
LOG(INFO) << "Punching hole for " << sprotocol << " port " << port;
if (!IpTables::AddAllowRule(protocol, port)) {
// If the 'iptables' command fails, this method fails.
LOG(ERROR) << "Calling 'iptables' failed";
return false;
}
// Track the hole we just punched.
holes->insert(port);
return true;
}
bool IpTables::PlugHole(uint16_t port,
std::unordered_set<uint16_t>* holes,
enum ProtocolEnum protocol) {
if (port == 0) {
// Port 0 is not a valid TCP/UDP port.
return false;
}
if (holes->find(port) == holes->end()) {
// There is no firewall hole for |port|.
// Even though this makes |PlugHole| not idempotent,
// and Punch/Plug not entirely symmetrical, fail. It might help catch bugs.
return false;
}
std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
LOG(INFO) << "Plugging hole for " << sprotocol << " port " << port;
if (!IpTables::DeleteAllowRule(protocol, port)) {
// If the 'iptables' command fails, this method fails.
LOG(ERROR) << "Calling 'iptables' failed";
return false;
}
// Stop tracking the hole we just plugged.
holes->erase(port);
return true;
}
void IpTables::PlugAllHoles() {
// Copy the container so that we can remove elements from the original.
// TCP
std::unordered_set<uint16_t> holes = tcp_holes_;
for (auto port : holes) {
PlugHole(port, &tcp_holes_, kProtocolTcp);
}
// UDP
holes = udp_holes_;
for (auto port : holes) {
PlugHole(port, &udp_holes_, kProtocolUdp);
}
CHECK(tcp_holes_.size() == 0) << "Failed to plug all TCP holes.";
CHECK(udp_holes_.size() == 0) << "Failed to plug all UDP holes.";
}
bool IpTables::AddAllowRule(enum ProtocolEnum protocol,
uint16_t port) {
chromeos::ProcessImpl iptables;
iptables.AddArg(executable_path_);
iptables.AddArg("-I"); // insert
iptables.AddArg("INPUT");
iptables.AddArg("-p"); // protocol
iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp");
iptables.AddArg("--dport"); // destination port
std::string port_number = base::StringPrintf("%d", port);
iptables.AddArg(port_number.c_str());
iptables.AddArg("-j");
iptables.AddArg("ACCEPT");
return iptables.Run() == 0;
}
bool IpTables::DeleteAllowRule(enum ProtocolEnum protocol,
uint16_t port) {
chromeos::ProcessImpl iptables;
iptables.AddArg(executable_path_);
iptables.AddArg("-D"); // delete
iptables.AddArg("INPUT");
iptables.AddArg("-p"); // protocol
iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp");
iptables.AddArg("--dport"); // destination port
std::string port_number = base::StringPrintf("%d", port);
iptables.AddArg(port_number.c_str());
iptables.AddArg("-j");
iptables.AddArg("ACCEPT");
return iptables.Run() == 0;
}
} // namespace firewalld