iptables.cc revision 5affd8895f6879153fce488c3f92271349eeadc9
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(uint16_t in_port) {
30  return PunchHole(in_port, &tcp_holes_, kProtocolTcp);
31}
32
33bool IpTables::PunchUdpHole(uint16_t in_port) {
34  return PunchHole(in_port, &udp_holes_, kProtocolUdp);
35}
36
37bool IpTables::PlugTcpHole(uint16_t in_port) {
38  return PlugHole(in_port, &tcp_holes_, kProtocolTcp);
39}
40
41bool IpTables::PlugUdpHole(uint16_t in_port) {
42  return PlugHole(in_port, &udp_holes_, kProtocolUdp);
43}
44
45bool IpTables::PunchHole(uint16_t port,
46                         std::unordered_set<uint16_t>* holes,
47                         enum ProtocolEnum protocol) {
48  if (port == 0) {
49    // Port 0 is not a valid TCP/UDP port.
50    return false;
51  }
52
53  if (holes->find(port) != holes->end()) {
54    // We have already punched a hole for |port|.
55    // Be idempotent: do nothing and succeed.
56    return true;
57  }
58
59  std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
60  LOG(INFO) << "Punching hole for " << sprotocol << " port " << port;
61  if (!IpTables::AddAllowRule(protocol, port)) {
62    // If the 'iptables' command fails, this method fails.
63    LOG(ERROR) << "Calling 'iptables' failed";
64    return false;
65  }
66
67  // Track the hole we just punched.
68  holes->insert(port);
69
70  return true;
71}
72
73bool IpTables::PlugHole(uint16_t port,
74                        std::unordered_set<uint16_t>* holes,
75                        enum ProtocolEnum protocol) {
76  if (port == 0) {
77    // Port 0 is not a valid TCP/UDP port.
78    return false;
79  }
80
81  if (holes->find(port) == holes->end()) {
82    // There is no firewall hole for |port|.
83    // Even though this makes |PlugHole| not idempotent,
84    // and Punch/Plug not entirely symmetrical, fail. It might help catch bugs.
85    return false;
86  }
87
88  std::string sprotocol = protocol == kProtocolTcp ? "TCP" : "UDP";
89  LOG(INFO) << "Plugging hole for " << sprotocol << " port " << port;
90  if (!IpTables::DeleteAllowRule(protocol, port)) {
91    // If the 'iptables' command fails, this method fails.
92    LOG(ERROR) << "Calling 'iptables' failed";
93    return false;
94  }
95
96  // Stop tracking the hole we just plugged.
97  holes->erase(port);
98
99  return true;
100}
101
102void IpTables::PlugAllHoles() {
103  // Copy the container so that we can remove elements from the original.
104  // TCP
105  std::unordered_set<uint16_t> holes = tcp_holes_;
106  for (auto port : holes) {
107    PlugHole(port, &tcp_holes_, kProtocolTcp);
108  }
109
110  // UDP
111  holes = udp_holes_;
112  for (auto port : holes) {
113    PlugHole(port, &udp_holes_, kProtocolUdp);
114  }
115
116  CHECK(tcp_holes_.size() == 0) << "Failed to plug all TCP holes.";
117  CHECK(udp_holes_.size() == 0) << "Failed to plug all UDP holes.";
118}
119
120bool IpTables::AddAllowRule(enum ProtocolEnum protocol,
121                            uint16_t port) {
122  chromeos::ProcessImpl iptables;
123  iptables.AddArg(executable_path_);
124  iptables.AddArg("-A");  // append
125  iptables.AddArg("INPUT");
126  iptables.AddArg("-p");  // protocol
127  iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp");
128  iptables.AddArg("--dport");  // destination port
129  std::string port_number = base::StringPrintf("%d", port);
130  iptables.AddArg(port_number.c_str());
131  iptables.AddArg("-j");
132  iptables.AddArg("ACCEPT");
133
134  return iptables.Run() == 0;
135}
136
137bool IpTables::DeleteAllowRule(enum ProtocolEnum protocol,
138                               uint16_t port) {
139  chromeos::ProcessImpl iptables;
140  iptables.AddArg(executable_path_);
141  iptables.AddArg("-D");  // delete
142  iptables.AddArg("INPUT");
143  iptables.AddArg("-p");  // protocol
144  iptables.AddArg(protocol == kProtocolTcp ? "tcp" : "udp");
145  iptables.AddArg("--dport");  // destination port
146  std::string port_number = base::StringPrintf("%d", port);
147  iptables.AddArg(port_number.c_str());
148  iptables.AddArg("-j");
149  iptables.AddArg("ACCEPT");
150
151  return iptables.Run() == 0;
152}
153
154}  // namespace firewalld
155