Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 1 | // Copyright 2019 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 "arc/network/datapath.h" |
| 6 | |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 7 | #include <base/strings/string_number_conversions.h> |
| 8 | |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 9 | namespace arc_networkd { |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 10 | |
Garrick Evans | 8a949dc | 2019-07-18 16:17:53 +0900 | [diff] [blame] | 11 | constexpr char kDefaultNetmask[] = "255.255.255.252"; |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 12 | |
| 13 | std::string ArcVethHostName(std::string ifname) { |
| 14 | return "veth_" + ifname; |
| 15 | } |
| 16 | |
| 17 | std::string ArcVethPeerName(std::string ifname) { |
| 18 | return "peer_" + ifname; |
| 19 | } |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 20 | |
| 21 | Datapath::Datapath(MinijailedProcessRunner* process_runner) |
| 22 | : process_runner_(process_runner) { |
| 23 | CHECK(process_runner_); |
| 24 | } |
| 25 | |
Garrick Evans | 8a949dc | 2019-07-18 16:17:53 +0900 | [diff] [blame] | 26 | bool Datapath::AddBridge(const std::string& ifname, |
| 27 | const std::string& ipv4_addr) { |
| 28 | // Configure the persistent Chrome OS bridge interface with static IP. |
| 29 | if (process_runner_->Run({kBrctlPath, "addbr", ifname}) != 0) { |
| 30 | return false; |
| 31 | } |
| 32 | |
| 33 | if (process_runner_->Run({kIfConfigPath, ifname, ipv4_addr, "netmask", |
| 34 | kDefaultNetmask, "up"}) != 0) { |
| 35 | RemoveBridge(ifname); |
| 36 | return false; |
| 37 | } |
| 38 | |
| 39 | // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules. |
| 40 | if (process_runner_->Run({kIpTablesPath, "-t", "mangle", "-A", "PREROUTING", |
| 41 | "-i", ifname, "-j", "MARK", "--set-mark", "1", |
| 42 | "-w"}) != 0) { |
| 43 | RemoveBridge(ifname); |
| 44 | return false; |
| 45 | } |
| 46 | |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | void Datapath::RemoveBridge(const std::string& ifname) { |
| 51 | process_runner_->Run({kIpTablesPath, "-t", "mangle", "-D", "PREROUTING", "-i", |
| 52 | ifname, "-j", "MARK", "--set-mark", "1", "-w"}); |
| 53 | process_runner_->Run({kIfConfigPath, ifname, "down"}); |
| 54 | process_runner_->Run({kBrctlPath, "delbr", ifname}); |
| 55 | } |
| 56 | |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 57 | std::string Datapath::AddVirtualBridgedInterface(const std::string& ifname, |
| 58 | const std::string& mac_addr, |
| 59 | const std::string& br_ifname) { |
| 60 | const std::string veth = ArcVethHostName(ifname); |
| 61 | const std::string peer = ArcVethPeerName(ifname); |
| 62 | |
| 63 | RemoveInterface(veth); |
| 64 | if (process_runner_->Run({kIpPath, "link", "add", veth, "type", "veth", |
| 65 | "peer", "name", peer}) != 0) { |
| 66 | return ""; |
| 67 | } |
| 68 | |
| 69 | if (process_runner_->Run({kIfConfigPath, veth, "up"}) != 0) { |
| 70 | RemoveInterface(veth); |
| 71 | RemoveInterface(peer); |
| 72 | return ""; |
| 73 | } |
| 74 | |
| 75 | if (process_runner_->Run({kIpPath, "link", "set", "dev", peer, "addr", |
| 76 | mac_addr, "down"}) != 0) { |
| 77 | RemoveInterface(veth); |
| 78 | RemoveInterface(peer); |
| 79 | return ""; |
| 80 | } |
| 81 | |
| 82 | if (process_runner_->Run({kBrctlPath, "addif", br_ifname, veth}) != 0) { |
| 83 | RemoveInterface(veth); |
| 84 | RemoveInterface(peer); |
| 85 | return ""; |
| 86 | } |
| 87 | |
| 88 | return peer; |
| 89 | } |
| 90 | |
| 91 | void Datapath::RemoveInterface(const std::string& ifname) { |
| 92 | process_runner_->Run({kIpPath, "link", "delete", ifname}, false /* log */); |
| 93 | } |
| 94 | |
| 95 | bool Datapath::AddInterfaceToContainer(int ns, |
| 96 | const std::string& src_ifname, |
| 97 | const std::string& dst_ifname, |
| 98 | const std::string& dst_ipv4, |
| 99 | bool fwd_multicast) { |
| 100 | const std::string pid = base::IntToString(ns); |
| 101 | return (process_runner_->Run( |
| 102 | {kIpPath, "link", "set", src_ifname, "netns", pid}) == 0) && |
| 103 | (process_runner_->AddInterfaceToContainer(src_ifname, dst_ifname, |
| 104 | dst_ipv4, kDefaultNetmask, |
| 105 | fwd_multicast, pid) == 0); |
| 106 | } |
| 107 | |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 108 | bool Datapath::AddLegacyIPv4DNAT(const std::string& ipv4_addr) { |
| 109 | // Forward "unclaimed" packets to Android to allow inbound connections |
| 110 | // from devices on the LAN. |
| 111 | if (process_runner_->Run( |
| 112 | {kIpTablesPath, "-t", "nat", "-N", "dnat_arc", "-w"}) != 0) |
| 113 | return false; |
| 114 | |
| 115 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "dnat_arc", "-j", |
| 116 | "DNAT", "--to-destination", ipv4_addr, "-w"}) != |
| 117 | 0) { |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 118 | RemoveLegacyIPv4DNAT(); |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 119 | return false; |
| 120 | } |
| 121 | |
| 122 | // This chain is dynamically updated whenever the default interface |
| 123 | // changes. |
| 124 | if (process_runner_->Run( |
| 125 | {kIpTablesPath, "-t", "nat", "-N", "try_arc", "-w"}) != 0) { |
| 126 | RemoveLegacyIPv4DNAT(); |
| 127 | return false; |
| 128 | } |
| 129 | |
| 130 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 131 | "-m", "socket", "--nowildcard", "-j", "ACCEPT", |
| 132 | "-w"}) != 0) { |
| 133 | RemoveLegacyIPv4DNAT(); |
| 134 | return false; |
| 135 | } |
| 136 | |
| 137 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 138 | "-p", "tcp", "-j", "try_arc", "-w"}) != 0) { |
| 139 | RemoveLegacyIPv4DNAT(); |
| 140 | return false; |
| 141 | } |
| 142 | |
| 143 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 144 | "-p", "udp", "-j", "try_arc", "-w"}) != 0) { |
| 145 | RemoveLegacyIPv4DNAT(); |
| 146 | return false; |
| 147 | } |
| 148 | |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | void Datapath::RemoveLegacyIPv4DNAT() { |
| 153 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-p", |
| 154 | "udp", "-j", "try_arc", "-w"}); |
| 155 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-p", |
| 156 | "tcp", "-j", "try_arc", "-w"}); |
| 157 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-m", |
| 158 | "socket", "--nowildcard", "-j", "ACCEPT", "-w"}); |
| 159 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "try_arc", "-w"}); |
| 160 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-X", "try_arc", "-w"}); |
| 161 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "dnat_arc", "-w"}); |
| 162 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-X", "dnat_arc", "-w"}); |
| 163 | } |
| 164 | |
Garrick Evans | 5486162 | 2019-07-19 09:05:09 +0900 | [diff] [blame^] | 165 | bool Datapath::AddLegacyIPv4InboundDNAT(const std::string& ifname) { |
| 166 | return (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "try_arc", |
| 167 | "-i", ifname, "-j", "dnat_arc", "-w"}) != 0); |
| 168 | } |
| 169 | |
| 170 | void Datapath::RemoveLegacyIPv4InboundDNAT() { |
| 171 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "try_arc", "-w"}); |
| 172 | } |
| 173 | |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 174 | bool Datapath::AddInboundIPv4DNAT(const std::string& ifname, |
| 175 | const std::string& ipv4_addr) { |
| 176 | // Direct ingress IP traffic to existing sockets. |
| 177 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 178 | "-i", ifname, "-m", "socket", "--nowildcard", "-j", |
| 179 | "ACCEPT", "-w"}) != 0) |
| 180 | return false; |
| 181 | |
| 182 | // Direct ingress TCP & UDP traffic to ARC interface for new connections. |
| 183 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 184 | "-i", ifname, "-p", "tcp", "-j", "DNAT", |
| 185 | "--to-destination", ipv4_addr, "-w"}) != 0) { |
| 186 | RemoveInboundIPv4DNAT(ifname, ipv4_addr); |
| 187 | return false; |
| 188 | } |
| 189 | if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING", |
| 190 | "-i", ifname, "-p", "udp", "-j", "DNAT", |
| 191 | "--to-destination", ipv4_addr, "-w"}) != 0) { |
| 192 | RemoveInboundIPv4DNAT(ifname, ipv4_addr); |
| 193 | return false; |
| 194 | } |
| 195 | |
| 196 | return true; |
| 197 | } |
| 198 | |
| 199 | void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname, |
| 200 | const std::string& ipv4_addr) { |
| 201 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i", |
| 202 | ifname, "-p", "udp", "-j", "DNAT", "--to-destination", |
| 203 | ipv4_addr, "-w"}); |
| 204 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i", |
| 205 | ifname, "-p", "tcp", "-j", "DNAT", "--to-destination", |
| 206 | ipv4_addr, "-w"}); |
| 207 | process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i", |
| 208 | ifname, "-m", "socket", "--nowildcard", "-j", "ACCEPT", |
| 209 | "-w"}); |
| 210 | } |
| 211 | |
| 212 | bool Datapath::AddOutboundIPv4(const std::string& ifname) { |
| 213 | return process_runner_->Run({kIpTablesPath, "-t", "filter", "-A", "FORWARD", |
| 214 | "-o", ifname, "-j", "ACCEPT", "-w"}) == 0; |
| 215 | } |
| 216 | |
| 217 | void Datapath::RemoveOutboundIPv4(const std::string& ifname) { |
| 218 | process_runner_->Run({kIpTablesPath, "-t", "filter", "-D", "FORWARD", "-o", |
| 219 | ifname, "-j", "ACCEPT", "-w"}); |
| 220 | } |
| 221 | |
Garrick Evans | f0ab713 | 2019-06-18 14:50:42 +0900 | [diff] [blame] | 222 | } // namespace arc_networkd |