blob: 0349424a463ebbe70230f5585dffeeac0f357d7d [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#include "patchpanel/routing_service.h"
Hugo Benichi7d9d8db2020-03-30 15:56:56 +09006
7#include <algorithm>
Hugo Benichi08805972020-07-15 22:34:57 +09008#include <memory>
Hugo Benichi7d9d8db2020-03-30 15:56:56 +09009#include <sstream>
10
Hugo Benichi08805972020-07-15 22:34:57 +090011#include <base/strings/stringprintf.h>
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090012#include <gtest/gtest.h>
13
Garrick Evans3388a032020-03-24 11:25:55 +090014namespace patchpanel {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090015namespace {
16
17auto& BYPASS_VPN = patchpanel::SetVpnIntentRequest::BYPASS_VPN;
18auto& DEFAULT_ROUTING = patchpanel::SetVpnIntentRequest::DEFAULT_ROUTING;
19auto& ROUTE_ON_VPN = patchpanel::SetVpnIntentRequest::ROUTE_ON_VPN;
20
21std::string hex(uint32_t val) {
Hugo Benichi08805972020-07-15 22:34:57 +090022 return base::StringPrintf("0x%08x", val);
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090023}
24
25struct sockopt_data {
26 int sockfd;
27 int level;
28 int optname;
29 char optval[256];
30 socklen_t optlen;
31};
32
Hugo Benichi08805972020-07-15 22:34:57 +090033void SetOptval(sockopt_data* sockopt, uint32_t optval) {
34 sockopt->optlen = sizeof(optval);
35 memcpy(sockopt->optval, &optval, sizeof(optval));
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090036}
37
38uint32_t GetOptval(const sockopt_data& sockopt) {
39 uint32_t optval;
40 memcpy(&optval, sockopt.optval, sizeof(optval));
41 return optval;
42}
43
Hugo Benichi08805972020-07-15 22:34:57 +090044Fwmark fwmark(uint32_t fwmark) {
45 return {.fwmark = fwmark};
46}
47
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090048class TestableRoutingService : public RoutingService {
49 public:
50 TestableRoutingService() = default;
51 ~TestableRoutingService() = default;
52
53 int GetSockopt(int sockfd,
54 int level,
55 int optname,
56 void* optval,
57 socklen_t* optlen) override {
58 sockopt.sockfd = sockfd;
59 sockopt.level = level;
60 sockopt.optname = optname;
61 memcpy(optval, sockopt.optval,
62 std::min(*optlen, (socklen_t)sizeof(sockopt.optval)));
63 *optlen = sockopt.optlen;
64 return getsockopt_ret;
65 }
66
67 int SetSockopt(int sockfd,
68 int level,
69 int optname,
70 const void* optval,
71 socklen_t optlen) override {
72 sockopt.sockfd = sockfd;
73 sockopt.level = level;
74 sockopt.optname = optname;
75 sockopt.optlen = optlen;
76 memcpy(sockopt.optval, optval,
77 std::min(optlen, (socklen_t)sizeof(sockopt.optval)));
78 return setsockopt_ret;
79 }
80
81 // Variables used to mock and track interactions with getsockopt and
82 // setsockopt.
83 int getsockopt_ret;
84 int setsockopt_ret;
85 sockopt_data sockopt;
86};
87
88class RoutingServiceTest : public testing::Test {
89 public:
90 RoutingServiceTest() = default;
91
92 protected:
93 void SetUp() override {}
94};
95
96} // namespace
97
Hugo Benichi08805972020-07-15 22:34:57 +090098TEST_F(RoutingServiceTest, FwmarkSize) {
99 EXPECT_EQ(sizeof(uint32_t), sizeof(Fwmark));
100}
101
102TEST_F(RoutingServiceTest, FwmarkOperators) {
103 EXPECT_EQ(fwmark(0x00000000), fwmark(0x00000000) | fwmark(0x00000000));
104 EXPECT_EQ(fwmark(0x00000000), fwmark(0x00000000) & fwmark(0x00000000));
105 EXPECT_EQ(fwmark(0x00110034), fwmark(0x00110034) | fwmark(0x00000000));
106 EXPECT_EQ(fwmark(0x00000000), fwmark(0x00110034) & fwmark(0x00000000));
107 EXPECT_EQ(fwmark(0x1234abcd), fwmark(0x12340000) | fwmark(0x0000abcd));
108 EXPECT_EQ(fwmark(0x00000000), fwmark(0x12340000) & fwmark(0x0000abcd));
109 EXPECT_EQ(fwmark(0x00120000), fwmark(0x00120000) & fwmark(0x00120000));
110 EXPECT_EQ(fwmark(0x12fffbcd), fwmark(0x1234abcd) | fwmark(0x00fff000));
111 EXPECT_EQ(fwmark(0x0034a000), fwmark(0x1234abcd) & fwmark(0x00fff000));
112 EXPECT_EQ(fwmark(0x0000ffff), ~fwmark(0xffff0000));
113 EXPECT_EQ(fwmark(0x12345678), ~~fwmark(0x12345678));
114 EXPECT_EQ(fwmark(0x55443322), ~fwmark(0xaabbccdd));
115}
116
117TEST_F(RoutingServiceTest, FwmarkAndMaskConstants) {
118 EXPECT_EQ("0x00003f00", kFwmarkAllSourcesMask.ToString());
119 EXPECT_EQ("0xffff0000", kFwmarkRoutingMask.ToString());
120 EXPECT_EQ("0x00000001", kFwmarkLegacySNAT.ToString());
121 EXPECT_EQ("0x0000c000", kFwmarkVpnMask.ToString());
122 EXPECT_EQ("0x00008000", kFwmarkRouteOnVpn.ToString());
123 EXPECT_EQ("0x00004000", kFwmarkBypassVpn.ToString());
124 EXPECT_EQ("0x00002000", kFwmarkForwardedSourcesMask.ToString());
125
126 EXPECT_EQ(0x00003f00, kFwmarkAllSourcesMask.Value());
127 EXPECT_EQ(0xffff0000, kFwmarkRoutingMask.Value());
128 EXPECT_EQ(0x00000001, kFwmarkLegacySNAT.Value());
129 EXPECT_EQ(0x0000c000, kFwmarkVpnMask.Value());
130 EXPECT_EQ(0x00008000, kFwmarkRouteOnVpn.Value());
131 EXPECT_EQ(0x00004000, kFwmarkBypassVpn.Value());
132 EXPECT_EQ(0x00002000, kFwmarkForwardedSourcesMask.Value());
133}
134
135TEST_F(RoutingServiceTest, FwmarkSources) {
136 EXPECT_EQ("0x00000000", Fwmark::FromSource(UNKNOWN).ToString());
137 EXPECT_EQ("0x00000100", Fwmark::FromSource(CHROME).ToString());
138 EXPECT_EQ("0x00000200", Fwmark::FromSource(USER).ToString());
139 EXPECT_EQ("0x00000300", Fwmark::FromSource(UPDATE_ENGINE).ToString());
140 EXPECT_EQ("0x00000400", Fwmark::FromSource(SYSTEM).ToString());
141 EXPECT_EQ("0x00000500", Fwmark::FromSource(HOST_VPN).ToString());
142 EXPECT_EQ("0x00002000", Fwmark::FromSource(ARC).ToString());
143 EXPECT_EQ("0x00002100", Fwmark::FromSource(CROSVM).ToString());
144 EXPECT_EQ("0x00002200", Fwmark::FromSource(PLUGINVM).ToString());
145 EXPECT_EQ("0x00002300", Fwmark::FromSource(TETHER_DOWNSTREAM).ToString());
146 EXPECT_EQ("0x00002400", Fwmark::FromSource(ARC_VPN).ToString());
147
148 for (auto ts : kLocalSources) {
149 EXPECT_EQ(
150 "0x00000000",
151 (Fwmark::FromSource(ts) & kFwmarkForwardedSourcesMask).ToString());
152 }
153 for (auto ts : kForwardedSources) {
154 EXPECT_EQ(
155 kFwmarkForwardedSourcesMask.ToString(),
156 (Fwmark::FromSource(ts) & kFwmarkForwardedSourcesMask).ToString());
157 }
158
159 for (auto ts : kLocalSources) {
160 EXPECT_EQ("0x00000000",
161 (Fwmark::FromSource(ts) & ~kFwmarkAllSourcesMask).ToString());
162 }
163 for (auto ts : kForwardedSources) {
164 EXPECT_EQ("0x00000000",
165 (Fwmark::FromSource(ts) & ~kFwmarkAllSourcesMask).ToString());
166 }
167}
168
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900169TEST_F(RoutingServiceTest, SetVpnFwmark) {
170 auto svc = std::make_unique<TestableRoutingService>();
171 svc->getsockopt_ret = 0;
172 svc->setsockopt_ret = 0;
173
174 struct {
175 patchpanel::SetVpnIntentRequest::VpnRoutingPolicy policy;
176 uint32_t initial_fwmark;
177 uint32_t expected_fwmark;
178 } testcases[] = {
Hugo Benichi08805972020-07-15 22:34:57 +0900179 {ROUTE_ON_VPN, 0x0, 0x00008000},
180 {BYPASS_VPN, 0x0, 0x00004000},
181 {ROUTE_ON_VPN, 0x1, 0x00008001},
182 {BYPASS_VPN, 0xabcd00ef, 0xabcd40ef},
183 {ROUTE_ON_VPN, 0x11223344, 0x1122b344},
184 {BYPASS_VPN, 0x11223344, 0x11227344},
185 {ROUTE_ON_VPN, 0x00008000, 0x00008000},
186 {BYPASS_VPN, 0x00004000, 0x00004000},
187 {BYPASS_VPN, 0x00008000, 0x00004000},
188 {ROUTE_ON_VPN, 0x00004000, 0x00008000},
189 {DEFAULT_ROUTING, 0x00008000, 0x00000000},
190 {DEFAULT_ROUTING, 0x00004000, 0x00000000},
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900191 };
192
193 for (const auto& tt : testcases) {
Hugo Benichi08805972020-07-15 22:34:57 +0900194 SetOptval(&svc->sockopt, tt.initial_fwmark);
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900195 EXPECT_TRUE(svc->SetVpnFwmark(4, tt.policy));
196 EXPECT_EQ(4, svc->sockopt.sockfd);
197 EXPECT_EQ(SOL_SOCKET, svc->sockopt.level);
198 EXPECT_EQ(SO_MARK, svc->sockopt.optname);
199 EXPECT_EQ(hex(tt.expected_fwmark), hex(GetOptval(svc->sockopt)));
200 }
201
202 svc->getsockopt_ret = -1;
203 svc->setsockopt_ret = 0;
204 EXPECT_FALSE(svc->SetVpnFwmark(4, ROUTE_ON_VPN));
205
206 svc->getsockopt_ret = 0;
207 svc->setsockopt_ret = -1;
208 EXPECT_FALSE(svc->SetVpnFwmark(4, ROUTE_ON_VPN));
209
210 svc->getsockopt_ret = 0;
211 svc->setsockopt_ret = 0;
212 EXPECT_FALSE(svc->SetVpnFwmark(
213 4, (patchpanel::SetVpnIntentRequest::VpnRoutingPolicy)-1));
214}
215
216TEST_F(RoutingServiceTest, SetFwmark) {
217 auto svc = std::make_unique<TestableRoutingService>();
218 svc->getsockopt_ret = 0;
219 svc->setsockopt_ret = 0;
220
221 struct {
222 uint32_t initial_fwmark;
223 uint32_t fwmark_value;
224 uint32_t fwmark_mask;
225 uint32_t expected_fwmark;
226 } testcases[] = {
227 {0x0, 0x0, 0x0, 0x0},
228 {0x1, 0x0, 0x0, 0x1},
229 {0x1, 0x0, 0x1, 0x0},
230 {0xaabbccdd, 0x11223344, 0xf0f0f0f0, 0x1a2b3c4d},
231 {0xaabbccdd, 0x11223344, 0xffff0000, 0x1122ccdd},
232 {0xaabbccdd, 0x11223344, 0x0000ffff, 0xaabb3344},
233 };
234
235 for (const auto& tt : testcases) {
Hugo Benichi08805972020-07-15 22:34:57 +0900236 SetOptval(&svc->sockopt, tt.initial_fwmark);
237 EXPECT_TRUE(
238 svc->SetFwmark(4, fwmark(tt.fwmark_value), fwmark(tt.fwmark_mask)));
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900239 EXPECT_EQ(4, svc->sockopt.sockfd);
240 EXPECT_EQ(SOL_SOCKET, svc->sockopt.level);
241 EXPECT_EQ(SO_MARK, svc->sockopt.optname);
242 EXPECT_EQ(hex(tt.expected_fwmark), hex(GetOptval(svc->sockopt)));
243 }
244}
245
246TEST_F(RoutingServiceTest, SetFwmark_Failures) {
247 auto svc = std::make_unique<TestableRoutingService>();
248 svc->getsockopt_ret = -1;
249 svc->setsockopt_ret = 0;
Hugo Benichi08805972020-07-15 22:34:57 +0900250 EXPECT_FALSE(svc->SetFwmark(4, fwmark(0x1), fwmark(0x01)));
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900251
252 svc = std::make_unique<TestableRoutingService>();
253 svc->getsockopt_ret = 0;
254 svc->setsockopt_ret = -1;
Hugo Benichi08805972020-07-15 22:34:57 +0900255 EXPECT_FALSE(svc->SetFwmark(5, fwmark(0x1), fwmark(0x01)));
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900256
257 svc = std::make_unique<TestableRoutingService>();
258 svc->getsockopt_ret = 0;
259 svc->setsockopt_ret = 0;
Hugo Benichi08805972020-07-15 22:34:57 +0900260 EXPECT_TRUE(svc->SetFwmark(6, fwmark(0x1), fwmark(0x01)));
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900261}
262
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900263TEST_F(RoutingServiceTest, LocalSourceSpecsPrettyPrinting) {
264 struct {
265 LocalSourceSpecs source;
266 std::string expected_output;
267 } testcases[] = {
268 {{}, "{source: UNKNOWN, uid: , classid: 0, is_on_vpn: false}"},
269 {{TrafficSource::CHROME, kUidChronos, 0, true},
270 "{source: CHROME, uid: chronos, classid: 0, is_on_vpn: true}"},
271 {{TrafficSource::USER, kUidDebugd, 0, true},
272 "{source: USER, uid: debugd, classid: 0, is_on_vpn: true}"},
273 {{TrafficSource::SYSTEM, kUidTlsdate, 0, true},
274 "{source: SYSTEM, uid: tlsdate, classid: 0, is_on_vpn: true}"},
275 {{TrafficSource::USER, kUidPluginvm, 0, true},
276 "{source: USER, uid: pluginvm, classid: 0, is_on_vpn: true}"},
277 {{TrafficSource::UPDATE_ENGINE, "", 1234, false},
278 "{source: UPDATE_ENGINE, uid: , classid: 1234, is_on_vpn: false}"},
279 };
280
281 for (const auto& tt : testcases) {
282 std::ostringstream stream;
283 stream << tt.source;
284 EXPECT_EQ(tt.expected_output, stream.str());
285 }
286}
287
Garrick Evans3388a032020-03-24 11:25:55 +0900288} // namespace patchpanel