blob: 57db00efeb79561f47d0e3457904b174d8545971 [file] [log] [blame]
Hugo Benichi7d9d8db2020-03-30 15:56:56 +09001// 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 Evans3388a032020-03-24 11:25:55 +09005#ifndef PATCHPANEL_ROUTING_SERVICE_H_
6#define PATCHPANEL_ROUTING_SERVICE_H_
Hugo Benichi7d9d8db2020-03-30 15:56:56 +09007
Hugo Benichi08805972020-07-15 22:34:57 +09008#include <arpa/inet.h>
Hugo Benichi7d9d8db2020-03-30 15:56:56 +09009#include <stdint.h>
10#include <sys/socket.h>
11
Hugo Benichi08805972020-07-15 22:34:57 +090012#include <array>
Hugo Benichi3a9162b2020-09-09 15:47:40 +090013#include <ostream>
Hugo Benichi08805972020-07-15 22:34:57 +090014#include <string>
15
16#include <base/strings/stringprintf.h>
17
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090018#include <patchpanel/proto_bindings/patchpanel_service.pb.h>
19
Garrick Evans3388a032020-03-24 11:25:55 +090020namespace patchpanel {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090021
Hugo Benichiaf9d8a72020-08-26 13:28:13 +090022// 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.
30constexpr const uint32_t kInterfaceTableIdIncrement = 1000;
31
Hugo Benichi08805972020-07-15 22:34:57 +090032// 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.
38enum 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
69// A representation of how fwmark bits are split and used for tagging and
70// routing traffic. The 32 bits of the fwmark are currently organized as such:
71// 0 1 2 3
72// 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
73// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74// | routing table id |VPN|source enum| reserved |*|
75// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76//
77// routing table id (16bits): the routing table id of a physical device managed
78// by shill or of a virtual private network.
79// VPN (2bits): policy bits controlled by host application to force VPN routing
80// or bypass VPN routing.
81// source enum(6bits): policy bits controlled by patchpanel for grouping
82// originated traffic by domain.
83// reserved(7bits): no usage at the moment.
84// legacy SNAT(1bit): legacy bit used for setting up SNAT for ARC, Crostini, and
85// PluginVMs with iptables MASQUERADE.
86//
87// Note that bitfields are not a portable way to define a
88// stable Fwmark. Also note that the in-memory representation of values of
89// this union changes depending on endianness, so care must be taken when
90// serializing or deserializing Fwmark values, or when aliasing with raw bytes
91// through pointers. In practice client code should not rely on a specific
92// memory representation and should instead use ToString() and Value().
93union Fwmark {
94 struct {
95 // The LSB is currently only used for applying IPv4 SNAT to egress traffic
96 // from ARC and other VMs; indicated by a value of 1.
97 uint8_t legacy;
98 // The 3rd byte is used to store the intent and policy to be applied to the
99 // traffic. The first 2 bits are used for host processes to select a VPN
100 // routing intent via patchpanel SetVpnIntent API. The next 6 bits of are
101 // used for tagging the traffic with a source.
102 uint8_t policy;
103 // The 2 upper bytes corresponds to the routing table id associated with
104 // a shill device or a VPN.
105 uint16_t rt_table_id;
106 };
107 // The raw memory representation of this fwmark as a uint32_t.
108 uint32_t fwmark;
109
110 // Returns a String representation of this Fwmark. This should
111 std::string ToString() const {
112 return base::StringPrintf("0x%04x%02x%02x", rt_table_id, policy, legacy);
113 }
114
115 // Returns the logical uint32_t value of this Fwmark.
116 uint32_t Value() const { return rt_table_id << 16 | policy << 8 | legacy; }
117
118 constexpr TrafficSource Source() {
119 return static_cast<TrafficSource>(policy & 0x3f);
120 }
121
122 constexpr bool operator==(Fwmark that) const { return fwmark == that.fwmark; }
123
124 constexpr Fwmark operator|(Fwmark that) const {
125 return {.fwmark = fwmark | that.fwmark};
126 }
127
128 constexpr Fwmark operator&(Fwmark that) const {
129 return {.fwmark = fwmark & that.fwmark};
130 }
131
132 constexpr Fwmark operator~() const { return {.fwmark = ~fwmark}; }
133
134 static Fwmark FromSource(TrafficSource source) {
135 return {
136 .policy = static_cast<uint8_t>(source), .legacy = 0, .rt_table_id = 0};
137 }
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900138
139 static Fwmark FromIfIndex(uint32_t ifindex) {
140 uint32_t table_id = ifindex + kInterfaceTableIdIncrement;
141 return {.policy = 0,
142 .legacy = 0,
143 .rt_table_id = static_cast<uint16_t>(table_id)};
144 }
Hugo Benichi08805972020-07-15 22:34:57 +0900145};
146
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900147// Specifies how the local traffic originating from a given source should be
148// tagged in mangle OUTPUT. A source is either identified by a uid or by a
149// cgroup classid identifier.
150struct LocalSourceSpecs {
151 TrafficSource source_type;
152 const char* uid_name;
153 uint32_t classid;
154 bool is_on_vpn;
155};
156
157std::ostream& operator<<(std::ostream& stream, const LocalSourceSpecs& source);
158
159// This block defines the names of uids whose traffic is always routed through a
160// VPN connection. Chrome and nacl applications
161constexpr char kUidChronos[] = "chronos";
162// Crosh terminal and feedback reports
163constexpr char kUidDebugd[] = "debugd";
164constexpr char kUidCups[] = "cups";
165// Chrome OS Kerberos daemon
166constexpr char kUidKerberosd[] = "kerberosd";
167// Kerberos third party untrusted code
168constexpr char kUidKerberosdExec[] = "kerberosd-exec";
169// While tlsdate is not user traffic, time sync should be attempted over
170// VPN. It is OK to send tlsdate traffic over VPN because it will also try
171// to sync time immediately after boot on the sign-in screen when no VPN can
172// be active.
173constexpr char kUidTlsdate[] = "tlsdate";
174// Plugin vm problem report utility (b/160916677)
175constexpr char kUidPluginvm[] = "pluginvm";
176// smbfs SMB filesystem daemon
177constexpr char kUidFuseSmbfs[] = "fuse-smbfs";
178
179// The list of all local sources to tag in mangle OUTPUT with the VPN intent
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +0900180// bit, or with a source tag, or with both. This arrays specifies: 1) the source
181// type, 2) the uid name of the source or empty cstring if none is defined (the
182// cstring must be defined and cannot be null), 3) the cgroup classid of the
183// source (or 0 if none is defined), and 4) if the traffic originated from that
184// source should be routed through VPN connections by default or not.
185constexpr std::array<LocalSourceSpecs, 9> kLocalSourceTypes{{
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900186 {TrafficSource::CHROME, kUidChronos, 0, true},
187 {TrafficSource::USER, kUidDebugd, 0, true},
188 {TrafficSource::USER, kUidCups, 0, true},
189 {TrafficSource::SYSTEM, kUidKerberosd, 0, true},
190 {TrafficSource::SYSTEM, kUidKerberosdExec, 0, true},
191 {TrafficSource::SYSTEM, kUidTlsdate, 0, true},
192 {TrafficSource::USER, kUidPluginvm, 0, true},
193 {TrafficSource::SYSTEM, kUidFuseSmbfs, 0, true},
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +0900194 // The classid value for update engine must stay in sync with
195 // src/aosp/system/update_engine/init/update-engine.conf.
196 {TrafficSource::UPDATE_ENGINE, "", 0x10001, false},
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900197}};
198
Hugo Benichi08805972020-07-15 22:34:57 +0900199// All local sources
200constexpr std::array<TrafficSource, 5> kLocalSources{
201 {CHROME, USER, UPDATE_ENGINE, SYSTEM, HOST_VPN}};
202
203// All forwarded sources
204constexpr std::array<TrafficSource, 5> kForwardedSources{
205 {ARC, CROSVM, PLUGINVM, TETHER_DOWNSTREAM, ARC_VPN}};
206
207// Constant fwmark value for tagging traffic with the "route-on-vpn" intent.
208constexpr const Fwmark kFwmarkRouteOnVpn = {.policy = 0x80};
209// Constant fwmark value for tagging traffic with the "bypass-vpn" intent.
210constexpr const Fwmark kFwmarkBypassVpn = {.policy = 0x40};
211// constexpr const Fwmark kFwmarkVpnMask = kFwmarkRouteOnVpn | kFwmarkBypassVpn;
212constexpr const Fwmark kFwmarkVpnMask = {.policy = 0xc0};
213// A mask for matching fwmarks on the routing table id.
214constexpr const Fwmark kFwmarkRoutingMask = {.rt_table_id = 0xffff};
215// A mask for matching fwmarks on the source.
216constexpr const Fwmark kFwmarkAllSourcesMask = {.policy = 0x3f};
217// A mast for matching fwmarks of forwarded sources.
218constexpr const Fwmark kFwmarkForwardedSourcesMask = {.policy = 0x20};
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900219// A mask for matching fwmarks on the policy byte.
220constexpr const Fwmark kFwmarkPolicyMask = {.policy = 0xff};
Hugo Benichi08805972020-07-15 22:34:57 +0900221// Both the mask and fwmark values for legacy SNAT rules used for ARC and other
222// containers.
223constexpr const Fwmark kFwmarkLegacySNAT = {.legacy = 0x1};
224
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900225// Service implementing routing features of patchpanel.
226// TODO(hugobenichi) Explain how this coordinates with shill's RoutingTable.
227class RoutingService {
228 public:
229 RoutingService();
230 RoutingService(const RoutingService&) = delete;
231 RoutingService& operator=(const RoutingService&) = delete;
232 virtual ~RoutingService() = default;
233
234 // Sets the VPN bits of the fwmark for the given socket according to the
235 // given policy. Preserves any other bits of the fwmark already set.
236 bool SetVpnFwmark(int sockfd,
237 patchpanel::SetVpnIntentRequest::VpnRoutingPolicy policy);
238
239 // Sets the fwmark on the given socket with the given mask.
240 // Preserves any other bits of the fwmark already set.
Hugo Benichi08805972020-07-15 22:34:57 +0900241 bool SetFwmark(int sockfd, Fwmark mark, Fwmark mask);
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900242
243 protected:
244 // Can be overridden in tests.
245 virtual int GetSockopt(
246 int sockfd, int level, int optname, void* optval, socklen_t* optlen);
247 virtual int SetSockopt(
248 int sockfd, int level, int optname, const void* optval, socklen_t optlen);
249};
250
Garrick Evans3388a032020-03-24 11:25:55 +0900251} // namespace patchpanel
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900252
Garrick Evans3388a032020-03-24 11:25:55 +0900253#endif // PATCHPANEL_ROUTING_SERVICE_H_