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