Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 1 | // Copyright 2020 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 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 5 | #ifndef PATCHPANEL_ROUTING_SERVICE_H_ |
| 6 | #define PATCHPANEL_ROUTING_SERVICE_H_ |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 7 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 8 | #include <arpa/inet.h> |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 9 | #include <stdint.h> |
| 10 | #include <sys/socket.h> |
| 11 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 12 | #include <array> |
Hugo Benichi | 3a9162b | 2020-09-09 15:47:40 +0900 | [diff] [blame] | 13 | #include <ostream> |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 14 | #include <string> |
| 15 | |
| 16 | #include <base/strings/stringprintf.h> |
| 17 | |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 18 | #include <patchpanel/proto_bindings/patchpanel_service.pb.h> |
| 19 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 20 | namespace patchpanel { |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 21 | |
Hugo Benichi | af9d8a7 | 2020-08-26 13:28:13 +0900 | [diff] [blame] | 22 | // Constant used for establishing a stable mapping between routing table ids |
| 23 | // and interface indexes. An interface with ifindex 2 will be assigned the |
| 24 | // routing table with id 1002 by the routing layer. This stable mapping is used |
| 25 | // for configuring ip rules, iptables fwmark mangle rules, and the |
| 26 | // accept_ra_rt_table sysctl for all physical interfaces. |
| 27 | // TODO(b/161507671) Consolidate with shill::kInterfaceTableIdIncrement |
| 28 | // in platform2/shill/routing_table.cc once routing and ip rule configuration |
| 29 | // is migrated to patchpanel. |
| 30 | constexpr const uint32_t kInterfaceTableIdIncrement = 1000; |
| 31 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 32 | // The list of all sources of traffic that need to be distinguished |
| 33 | // for routing or traffic accounting. Currently 6 bits are used for encoding |
| 34 | // the TrafficSource enum in a fwmark. The enum is split into two groups:local |
| 35 | // sources and forwarded sources. The enum values of forwarded sources are |
| 36 | // offset by 0x20 so that their most significant bit is always set and can be |
| 37 | // easily matched separately from local sources. |
| 38 | enum TrafficSource { |
| 39 | UNKNOWN = 0, |
| 40 | |
| 41 | // Local sources: |
| 42 | // Traffic corresponding to uid "chronos". |
| 43 | CHROME = 1, |
| 44 | // Other uids classified as "user" for traffic purposes: debugd, cups, |
| 45 | // tlsdate, pluginvm, etc. |
| 46 | USER = 2, |
| 47 | // Traffic from Update engine. |
| 48 | UPDATE_ENGINE = 3, |
| 49 | // Other system traffic. |
| 50 | SYSTEM = 4, |
| 51 | // Traffic emitted on an underlying physical network by the built-in OpenVPN |
| 52 | // and L2TP clients, or Chrome 3rd party VPN Apps. This traffic constitutes |
| 53 | // the VPN tunnel. |
| 54 | HOST_VPN = 5, |
| 55 | |
| 56 | // Forwarded sources: |
| 57 | // ARC++ and ARCVM. |
| 58 | ARC = 0x20, |
| 59 | // Crostini VMs and lxc containers. |
| 60 | CROSVM = 0x21, |
| 61 | // Other plugin VMs. |
| 62 | PLUGINVM = 0x22, |
| 63 | // A tethered downstream network. Currently reserved for future use. |
| 64 | TETHER_DOWNSTREAM = 0x23, |
| 65 | // Traffic emitted by Android VPNs for their tunnelled connections. |
| 66 | ARC_VPN = 0x24, |
| 67 | }; |
| 68 | |
Hugo Benichi | 93306e5 | 2020-12-04 16:08:00 +0900 | [diff] [blame^] | 69 | const std::string& TrafficSourceName(TrafficSource source); |
| 70 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 71 | // A representation of how fwmark bits are split and used for tagging and |
| 72 | // routing traffic. The 32 bits of the fwmark are currently organized as such: |
| 73 | // 0 1 2 3 |
| 74 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| 75 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 76 | // | routing table id |VPN|source enum| reserved |*| |
| 77 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 78 | // |
| 79 | // routing table id (16bits): the routing table id of a physical device managed |
| 80 | // by shill or of a virtual private network. |
| 81 | // VPN (2bits): policy bits controlled by host application to force VPN routing |
| 82 | // or bypass VPN routing. |
| 83 | // source enum(6bits): policy bits controlled by patchpanel for grouping |
| 84 | // originated traffic by domain. |
| 85 | // reserved(7bits): no usage at the moment. |
| 86 | // legacy SNAT(1bit): legacy bit used for setting up SNAT for ARC, Crostini, and |
| 87 | // PluginVMs with iptables MASQUERADE. |
| 88 | // |
| 89 | // Note that bitfields are not a portable way to define a |
| 90 | // stable Fwmark. Also note that the in-memory representation of values of |
| 91 | // this union changes depending on endianness, so care must be taken when |
| 92 | // serializing or deserializing Fwmark values, or when aliasing with raw bytes |
| 93 | // through pointers. In practice client code should not rely on a specific |
| 94 | // memory representation and should instead use ToString() and Value(). |
| 95 | union Fwmark { |
| 96 | struct { |
| 97 | // The LSB is currently only used for applying IPv4 SNAT to egress traffic |
| 98 | // from ARC and other VMs; indicated by a value of 1. |
| 99 | uint8_t legacy; |
| 100 | // The 3rd byte is used to store the intent and policy to be applied to the |
| 101 | // traffic. The first 2 bits are used for host processes to select a VPN |
| 102 | // routing intent via patchpanel SetVpnIntent API. The next 6 bits of are |
| 103 | // used for tagging the traffic with a source. |
| 104 | uint8_t policy; |
| 105 | // The 2 upper bytes corresponds to the routing table id associated with |
| 106 | // a shill device or a VPN. |
| 107 | uint16_t rt_table_id; |
| 108 | }; |
| 109 | // The raw memory representation of this fwmark as a uint32_t. |
| 110 | uint32_t fwmark; |
| 111 | |
| 112 | // Returns a String representation of this Fwmark. This should |
| 113 | std::string ToString() const { |
| 114 | return base::StringPrintf("0x%04x%02x%02x", rt_table_id, policy, legacy); |
| 115 | } |
| 116 | |
| 117 | // Returns the logical uint32_t value of this Fwmark. |
| 118 | uint32_t Value() const { return rt_table_id << 16 | policy << 8 | legacy; } |
| 119 | |
| 120 | constexpr TrafficSource Source() { |
| 121 | return static_cast<TrafficSource>(policy & 0x3f); |
| 122 | } |
| 123 | |
| 124 | constexpr bool operator==(Fwmark that) const { return fwmark == that.fwmark; } |
| 125 | |
| 126 | constexpr Fwmark operator|(Fwmark that) const { |
| 127 | return {.fwmark = fwmark | that.fwmark}; |
| 128 | } |
| 129 | |
| 130 | constexpr Fwmark operator&(Fwmark that) const { |
| 131 | return {.fwmark = fwmark & that.fwmark}; |
| 132 | } |
| 133 | |
| 134 | constexpr Fwmark operator~() const { return {.fwmark = ~fwmark}; } |
| 135 | |
| 136 | static Fwmark FromSource(TrafficSource source) { |
| 137 | return { |
| 138 | .policy = static_cast<uint8_t>(source), .legacy = 0, .rt_table_id = 0}; |
| 139 | } |
Hugo Benichi | af9d8a7 | 2020-08-26 13:28:13 +0900 | [diff] [blame] | 140 | |
| 141 | static Fwmark FromIfIndex(uint32_t ifindex) { |
| 142 | uint32_t table_id = ifindex + kInterfaceTableIdIncrement; |
| 143 | return {.policy = 0, |
| 144 | .legacy = 0, |
| 145 | .rt_table_id = static_cast<uint16_t>(table_id)}; |
| 146 | } |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 147 | }; |
| 148 | |
Hugo Benichi | 3a9162b | 2020-09-09 15:47:40 +0900 | [diff] [blame] | 149 | // Specifies how the local traffic originating from a given source should be |
| 150 | // tagged in mangle OUTPUT. A source is either identified by a uid or by a |
| 151 | // cgroup classid identifier. |
| 152 | struct LocalSourceSpecs { |
| 153 | TrafficSource source_type; |
| 154 | const char* uid_name; |
| 155 | uint32_t classid; |
| 156 | bool is_on_vpn; |
| 157 | }; |
| 158 | |
| 159 | std::ostream& operator<<(std::ostream& stream, const LocalSourceSpecs& source); |
| 160 | |
| 161 | // This block defines the names of uids whose traffic is always routed through a |
| 162 | // VPN connection. Chrome and nacl applications |
| 163 | constexpr char kUidChronos[] = "chronos"; |
| 164 | // Crosh terminal and feedback reports |
| 165 | constexpr char kUidDebugd[] = "debugd"; |
| 166 | constexpr char kUidCups[] = "cups"; |
| 167 | // Chrome OS Kerberos daemon |
| 168 | constexpr char kUidKerberosd[] = "kerberosd"; |
| 169 | // Kerberos third party untrusted code |
| 170 | constexpr char kUidKerberosdExec[] = "kerberosd-exec"; |
| 171 | // While tlsdate is not user traffic, time sync should be attempted over |
| 172 | // VPN. It is OK to send tlsdate traffic over VPN because it will also try |
| 173 | // to sync time immediately after boot on the sign-in screen when no VPN can |
| 174 | // be active. |
| 175 | constexpr char kUidTlsdate[] = "tlsdate"; |
| 176 | // Plugin vm problem report utility (b/160916677) |
| 177 | constexpr char kUidPluginvm[] = "pluginvm"; |
| 178 | // smbfs SMB filesystem daemon |
| 179 | constexpr char kUidFuseSmbfs[] = "fuse-smbfs"; |
| 180 | |
| 181 | // The list of all local sources to tag in mangle OUTPUT with the VPN intent |
Hugo Benichi | 7e3b1fc | 2020-11-19 15:47:05 +0900 | [diff] [blame] | 182 | // bit, or with a source tag, or with both. This arrays specifies: 1) the source |
| 183 | // type, 2) the uid name of the source or empty cstring if none is defined (the |
| 184 | // cstring must be defined and cannot be null), 3) the cgroup classid of the |
| 185 | // source (or 0 if none is defined), and 4) if the traffic originated from that |
| 186 | // source should be routed through VPN connections by default or not. |
| 187 | constexpr std::array<LocalSourceSpecs, 9> kLocalSourceTypes{{ |
Hugo Benichi | 3a9162b | 2020-09-09 15:47:40 +0900 | [diff] [blame] | 188 | {TrafficSource::CHROME, kUidChronos, 0, true}, |
| 189 | {TrafficSource::USER, kUidDebugd, 0, true}, |
| 190 | {TrafficSource::USER, kUidCups, 0, true}, |
| 191 | {TrafficSource::SYSTEM, kUidKerberosd, 0, true}, |
| 192 | {TrafficSource::SYSTEM, kUidKerberosdExec, 0, true}, |
| 193 | {TrafficSource::SYSTEM, kUidTlsdate, 0, true}, |
| 194 | {TrafficSource::USER, kUidPluginvm, 0, true}, |
| 195 | {TrafficSource::SYSTEM, kUidFuseSmbfs, 0, true}, |
Hugo Benichi | 7e3b1fc | 2020-11-19 15:47:05 +0900 | [diff] [blame] | 196 | // The classid value for update engine must stay in sync with |
| 197 | // src/aosp/system/update_engine/init/update-engine.conf. |
| 198 | {TrafficSource::UPDATE_ENGINE, "", 0x10001, false}, |
Hugo Benichi | 3a9162b | 2020-09-09 15:47:40 +0900 | [diff] [blame] | 199 | }}; |
| 200 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 201 | // All local sources |
| 202 | constexpr std::array<TrafficSource, 5> kLocalSources{ |
| 203 | {CHROME, USER, UPDATE_ENGINE, SYSTEM, HOST_VPN}}; |
| 204 | |
| 205 | // All forwarded sources |
| 206 | constexpr std::array<TrafficSource, 5> kForwardedSources{ |
| 207 | {ARC, CROSVM, PLUGINVM, TETHER_DOWNSTREAM, ARC_VPN}}; |
| 208 | |
Hugo Benichi | af2021b | 2020-11-20 14:07:16 +0900 | [diff] [blame] | 209 | // All sources |
| 210 | constexpr std::array<TrafficSource, 10> kAllSources{ |
| 211 | {CHROME, USER, UPDATE_ENGINE, SYSTEM, HOST_VPN, ARC, CROSVM, PLUGINVM, |
| 212 | TETHER_DOWNSTREAM, ARC_VPN}}; |
| 213 | |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 214 | // Constant fwmark value for tagging traffic with the "route-on-vpn" intent. |
| 215 | constexpr const Fwmark kFwmarkRouteOnVpn = {.policy = 0x80}; |
| 216 | // Constant fwmark value for tagging traffic with the "bypass-vpn" intent. |
| 217 | constexpr const Fwmark kFwmarkBypassVpn = {.policy = 0x40}; |
| 218 | // constexpr const Fwmark kFwmarkVpnMask = kFwmarkRouteOnVpn | kFwmarkBypassVpn; |
| 219 | constexpr const Fwmark kFwmarkVpnMask = {.policy = 0xc0}; |
| 220 | // A mask for matching fwmarks on the routing table id. |
| 221 | constexpr const Fwmark kFwmarkRoutingMask = {.rt_table_id = 0xffff}; |
| 222 | // A mask for matching fwmarks on the source. |
| 223 | constexpr const Fwmark kFwmarkAllSourcesMask = {.policy = 0x3f}; |
| 224 | // A mast for matching fwmarks of forwarded sources. |
| 225 | constexpr const Fwmark kFwmarkForwardedSourcesMask = {.policy = 0x20}; |
Hugo Benichi | 3a9162b | 2020-09-09 15:47:40 +0900 | [diff] [blame] | 226 | // A mask for matching fwmarks on the policy byte. |
| 227 | constexpr const Fwmark kFwmarkPolicyMask = {.policy = 0xff}; |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 228 | // Both the mask and fwmark values for legacy SNAT rules used for ARC and other |
| 229 | // containers. |
| 230 | constexpr const Fwmark kFwmarkLegacySNAT = {.legacy = 0x1}; |
| 231 | |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 232 | // Service implementing routing features of patchpanel. |
| 233 | // TODO(hugobenichi) Explain how this coordinates with shill's RoutingTable. |
| 234 | class RoutingService { |
| 235 | public: |
| 236 | RoutingService(); |
| 237 | RoutingService(const RoutingService&) = delete; |
| 238 | RoutingService& operator=(const RoutingService&) = delete; |
| 239 | virtual ~RoutingService() = default; |
| 240 | |
| 241 | // Sets the VPN bits of the fwmark for the given socket according to the |
| 242 | // given policy. Preserves any other bits of the fwmark already set. |
| 243 | bool SetVpnFwmark(int sockfd, |
| 244 | patchpanel::SetVpnIntentRequest::VpnRoutingPolicy policy); |
| 245 | |
| 246 | // Sets the fwmark on the given socket with the given mask. |
| 247 | // Preserves any other bits of the fwmark already set. |
Hugo Benichi | 0880597 | 2020-07-15 22:34:57 +0900 | [diff] [blame] | 248 | bool SetFwmark(int sockfd, Fwmark mark, Fwmark mask); |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 249 | |
| 250 | protected: |
| 251 | // Can be overridden in tests. |
| 252 | virtual int GetSockopt( |
| 253 | int sockfd, int level, int optname, void* optval, socklen_t* optlen); |
| 254 | virtual int SetSockopt( |
| 255 | int sockfd, int level, int optname, const void* optval, socklen_t optlen); |
| 256 | }; |
| 257 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 258 | } // namespace patchpanel |
Hugo Benichi | 7d9d8db | 2020-03-30 15:56:56 +0900 | [diff] [blame] | 259 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 260 | #endif // PATCHPANEL_ROUTING_SERVICE_H_ |