blob: e80fcdeaf10e899b823d81e2e29686db0a2a6823 [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>
13#include <string>
14
15#include <base/strings/stringprintf.h>
16
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090017#include <patchpanel/proto_bindings/patchpanel_service.pb.h>
18
Garrick Evans3388a032020-03-24 11:25:55 +090019namespace patchpanel {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090020
Hugo Benichi08805972020-07-15 22:34:57 +090021// The list of all sources of traffic that need to be distinguished
22// for routing or traffic accounting. Currently 6 bits are used for encoding
23// the TrafficSource enum in a fwmark. The enum is split into two groups:local
24// sources and forwarded sources. The enum values of forwarded sources are
25// offset by 0x20 so that their most significant bit is always set and can be
26// easily matched separately from local sources.
27enum TrafficSource {
28 UNKNOWN = 0,
29
30 // Local sources:
31 // Traffic corresponding to uid "chronos".
32 CHROME = 1,
33 // Other uids classified as "user" for traffic purposes: debugd, cups,
34 // tlsdate, pluginvm, etc.
35 USER = 2,
36 // Traffic from Update engine.
37 UPDATE_ENGINE = 3,
38 // Other system traffic.
39 SYSTEM = 4,
40 // Traffic emitted on an underlying physical network by the built-in OpenVPN
41 // and L2TP clients, or Chrome 3rd party VPN Apps. This traffic constitutes
42 // the VPN tunnel.
43 HOST_VPN = 5,
44
45 // Forwarded sources:
46 // ARC++ and ARCVM.
47 ARC = 0x20,
48 // Crostini VMs and lxc containers.
49 CROSVM = 0x21,
50 // Other plugin VMs.
51 PLUGINVM = 0x22,
52 // A tethered downstream network. Currently reserved for future use.
53 TETHER_DOWNSTREAM = 0x23,
54 // Traffic emitted by Android VPNs for their tunnelled connections.
55 ARC_VPN = 0x24,
56};
57
58// A representation of how fwmark bits are split and used for tagging and
59// routing traffic. The 32 bits of the fwmark are currently organized as such:
60// 0 1 2 3
61// 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
62// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63// | routing table id |VPN|source enum| reserved |*|
64// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65//
66// routing table id (16bits): the routing table id of a physical device managed
67// by shill or of a virtual private network.
68// VPN (2bits): policy bits controlled by host application to force VPN routing
69// or bypass VPN routing.
70// source enum(6bits): policy bits controlled by patchpanel for grouping
71// originated traffic by domain.
72// reserved(7bits): no usage at the moment.
73// legacy SNAT(1bit): legacy bit used for setting up SNAT for ARC, Crostini, and
74// PluginVMs with iptables MASQUERADE.
75//
76// Note that bitfields are not a portable way to define a
77// stable Fwmark. Also note that the in-memory representation of values of
78// this union changes depending on endianness, so care must be taken when
79// serializing or deserializing Fwmark values, or when aliasing with raw bytes
80// through pointers. In practice client code should not rely on a specific
81// memory representation and should instead use ToString() and Value().
82union Fwmark {
83 struct {
84 // The LSB is currently only used for applying IPv4 SNAT to egress traffic
85 // from ARC and other VMs; indicated by a value of 1.
86 uint8_t legacy;
87 // The 3rd byte is used to store the intent and policy to be applied to the
88 // traffic. The first 2 bits are used for host processes to select a VPN
89 // routing intent via patchpanel SetVpnIntent API. The next 6 bits of are
90 // used for tagging the traffic with a source.
91 uint8_t policy;
92 // The 2 upper bytes corresponds to the routing table id associated with
93 // a shill device or a VPN.
94 uint16_t rt_table_id;
95 };
96 // The raw memory representation of this fwmark as a uint32_t.
97 uint32_t fwmark;
98
99 // Returns a String representation of this Fwmark. This should
100 std::string ToString() const {
101 return base::StringPrintf("0x%04x%02x%02x", rt_table_id, policy, legacy);
102 }
103
104 // Returns the logical uint32_t value of this Fwmark.
105 uint32_t Value() const { return rt_table_id << 16 | policy << 8 | legacy; }
106
107 constexpr TrafficSource Source() {
108 return static_cast<TrafficSource>(policy & 0x3f);
109 }
110
111 constexpr bool operator==(Fwmark that) const { return fwmark == that.fwmark; }
112
113 constexpr Fwmark operator|(Fwmark that) const {
114 return {.fwmark = fwmark | that.fwmark};
115 }
116
117 constexpr Fwmark operator&(Fwmark that) const {
118 return {.fwmark = fwmark & that.fwmark};
119 }
120
121 constexpr Fwmark operator~() const { return {.fwmark = ~fwmark}; }
122
123 static Fwmark FromSource(TrafficSource source) {
124 return {
125 .policy = static_cast<uint8_t>(source), .legacy = 0, .rt_table_id = 0};
126 }
127};
128
129// All local sources
130constexpr std::array<TrafficSource, 5> kLocalSources{
131 {CHROME, USER, UPDATE_ENGINE, SYSTEM, HOST_VPN}};
132
133// All forwarded sources
134constexpr std::array<TrafficSource, 5> kForwardedSources{
135 {ARC, CROSVM, PLUGINVM, TETHER_DOWNSTREAM, ARC_VPN}};
136
137// Constant fwmark value for tagging traffic with the "route-on-vpn" intent.
138constexpr const Fwmark kFwmarkRouteOnVpn = {.policy = 0x80};
139// Constant fwmark value for tagging traffic with the "bypass-vpn" intent.
140constexpr const Fwmark kFwmarkBypassVpn = {.policy = 0x40};
141// constexpr const Fwmark kFwmarkVpnMask = kFwmarkRouteOnVpn | kFwmarkBypassVpn;
142constexpr const Fwmark kFwmarkVpnMask = {.policy = 0xc0};
143// A mask for matching fwmarks on the routing table id.
144constexpr const Fwmark kFwmarkRoutingMask = {.rt_table_id = 0xffff};
145// A mask for matching fwmarks on the source.
146constexpr const Fwmark kFwmarkAllSourcesMask = {.policy = 0x3f};
147// A mast for matching fwmarks of forwarded sources.
148constexpr const Fwmark kFwmarkForwardedSourcesMask = {.policy = 0x20};
149// Both the mask and fwmark values for legacy SNAT rules used for ARC and other
150// containers.
151constexpr const Fwmark kFwmarkLegacySNAT = {.legacy = 0x1};
152
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900153// Service implementing routing features of patchpanel.
154// TODO(hugobenichi) Explain how this coordinates with shill's RoutingTable.
155class RoutingService {
156 public:
157 RoutingService();
158 RoutingService(const RoutingService&) = delete;
159 RoutingService& operator=(const RoutingService&) = delete;
160 virtual ~RoutingService() = default;
161
162 // Sets the VPN bits of the fwmark for the given socket according to the
163 // given policy. Preserves any other bits of the fwmark already set.
164 bool SetVpnFwmark(int sockfd,
165 patchpanel::SetVpnIntentRequest::VpnRoutingPolicy policy);
166
167 // Sets the fwmark on the given socket with the given mask.
168 // Preserves any other bits of the fwmark already set.
Hugo Benichi08805972020-07-15 22:34:57 +0900169 bool SetFwmark(int sockfd, Fwmark mark, Fwmark mask);
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900170
171 protected:
172 // Can be overridden in tests.
173 virtual int GetSockopt(
174 int sockfd, int level, int optname, void* optval, socklen_t* optlen);
175 virtual int SetSockopt(
176 int sockfd, int level, int optname, const void* optval, socklen_t optlen);
177};
178
Garrick Evans3388a032020-03-24 11:25:55 +0900179} // namespace patchpanel
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900180
Garrick Evans3388a032020-03-24 11:25:55 +0900181#endif // PATCHPANEL_ROUTING_SERVICE_H_