iptables.cc revision 0e7a658e0f72b0d2113f5c06136620236dde96f9
1// Copyright 2014 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "firewalld/iptables.h" 6 7#include <string> 8#include <vector> 9 10#include <base/logging.h> 11#include <base/strings/stringprintf.h> 12#include <chromeos/process.h> 13 14namespace { 15const char kIptablesPath[] = "/sbin/iptables"; 16} 17 18namespace firewalld { 19 20IpTables::IpTables() : IpTables{kIptablesPath} {} 21 22IpTables::IpTables(const std::string& path) : executable_path_{path} {} 23 24IpTables::~IpTables() { 25 // Plug all holes when destructed. 26 PlugAllHoles(); 27} 28 29bool IpTables::PunchTcpHole(chromeos::ErrorPtr* error, 30 uint16_t in_port, 31 bool* out_success) { 32 *out_success = PunchHole(in_port, &tcp_holes_, kProtocolTcp); 33 return true; 34} 35 36bool IpTables::PunchUdpHole(chromeos::ErrorPtr* error, 37 uint16_t in_port, 38 bool* out_success) { 39 *out_success = PunchHole(in_port, &udp_holes_, kProtocolUdp); 40 return true; 41} 42 43bool IpTables::PlugTcpHole(chromeos::ErrorPtr* error, 44 uint16_t in_port, 45 bool* out_success) { 46 *out_success = PlugHole(in_port, &tcp_holes_, kProtocolTcp); 47 return true; 48} 49 50bool IpTables::PlugUdpHole(chromeos::ErrorPtr* error, 51 uint16_t in_port, 52 bool* out_success) { 53 *out_success = PlugHole(in_port, &udp_holes_, kProtocolUdp); 54 return true; 55} 56 57bool IpTables::PunchHole(uint16_t port, 58 std::unordered_set<uint16_t>* holes, 59 enum ProtocolEnum protocol) { 60 if (port == 0) { 61 // Port 0 is not a valid TCP/UDP port. 62 return false; 63 } 64 65 if (holes->find(port) != holes->end()) { 66 // We have already punched a hole for |port|. 67 // Be idempotent: do nothing and succeed. 68 return true; 69 } 70 71 std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP"; 72 LOG(INFO) << "Punching hole for " << sprotocol << " port " << port; 73 if (!IpTables::AddAllowRule(protocol, port)) { 74 // If the 'iptables' command fails, this method fails. 75 LOG(ERROR) << "Calling 'iptables' failed"; 76 return false; 77 } 78 79 // Track the hole we just punched. 80 holes->insert(port); 81 82 return true; 83} 84 85bool IpTables::PlugHole(uint16_t port, 86 std::unordered_set<uint16_t>* holes, 87 enum ProtocolEnum protocol) { 88 if (port == 0) { 89 // Port 0 is not a valid TCP/UDP port. 90 return false; 91 } 92 93 if (holes->find(port) == holes->end()) { 94 // There is no firewall hole for |port|. 95 // Even though this makes |PlugHole| not idempotent, 96 // and Punch/Plug not entirely symmetrical, fail. It might help catch bugs. 97 return false; 98 } 99 100 std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP"; 101 LOG(INFO) << "Plugging hole for " << sprotocol << " port " << port; 102 if (!IpTables::DeleteAllowRule(protocol, port)) { 103 // If the 'iptables' command fails, this method fails. 104 LOG(ERROR) << "Calling 'iptables' failed"; 105 return false; 106 } 107 108 // Stop tracking the hole we just plugged. 109 holes->erase(port); 110 111 return true; 112} 113 114void IpTables::PlugAllHoles() { 115 // Copy the container so that we can remove elements from the original. 116 // TCP 117 std::unordered_set<uint16_t> holes = tcp_holes_; 118 for (auto port : holes) { 119 PlugHole(port, &tcp_holes_, kProtocolTcp); 120 } 121 122 // UDP 123 holes = udp_holes_; 124 for (auto port : holes) { 125 PlugHole(port, &udp_holes_, kProtocolUdp); 126 } 127 128 CHECK(tcp_holes_.size() == 0) << "Failed to plug all TCP holes."; 129 CHECK(udp_holes_.size() == 0) << "Failed to plug all UDP holes."; 130} 131 132bool IpTables::AddAllowRule(enum ProtocolEnum protocol, 133 uint16_t port) { 134 chromeos::ProcessImpl iptables; 135 iptables.AddArg(executable_path_); 136 iptables.AddArg("-A"); // append 137 iptables.AddArg("INPUT"); 138 iptables.AddArg("-p"); // protocol 139 iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp"); 140 iptables.AddArg("--dport"); // destination port 141 std::string port_number = base::StringPrintf("%d", port); 142 iptables.AddArg(port_number.c_str()); 143 iptables.AddArg("-j"); 144 iptables.AddArg("ACCEPT"); 145 146 return iptables.Run() == 0; 147} 148 149bool IpTables::DeleteAllowRule(enum ProtocolEnum protocol, 150 uint16_t port) { 151 chromeos::ProcessImpl iptables; 152 iptables.AddArg(executable_path_); 153 iptables.AddArg("-D"); // delete 154 iptables.AddArg("INPUT"); 155 iptables.AddArg("-p"); // protocol 156 iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp"); 157 iptables.AddArg("--dport"); // destination port 158 std::string port_number = base::StringPrintf("%d", port); 159 iptables.AddArg(port_number.c_str()); 160 iptables.AddArg("-j"); 161 iptables.AddArg("ACCEPT"); 162 163 return iptables.Run() == 0; 164} 165 166} // namespace firewalld 167