blob: 70e450401e227c7475b9ed0b57304632b0c4a030 [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>
8#include <sstream>
9
10#include <gtest/gtest.h>
11
Garrick Evans3388a032020-03-24 11:25:55 +090012namespace patchpanel {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +090013namespace {
14
15auto& BYPASS_VPN = patchpanel::SetVpnIntentRequest::BYPASS_VPN;
16auto& DEFAULT_ROUTING = patchpanel::SetVpnIntentRequest::DEFAULT_ROUTING;
17auto& ROUTE_ON_VPN = patchpanel::SetVpnIntentRequest::ROUTE_ON_VPN;
18
19std::string hex(uint32_t val) {
20 std::stringstream ss;
21 ss << "0x" << std::hex << val;
22 return ss.str();
23}
24
25struct sockopt_data {
26 int sockfd;
27 int level;
28 int optname;
29 char optval[256];
30 socklen_t optlen;
31};
32
33void SetOptval(sockopt_data& sockopt, uint32_t optval) {
34 sockopt.optlen = sizeof(optval);
35 memcpy(sockopt.optval, &optval, sizeof(optval));
36}
37
38uint32_t GetOptval(const sockopt_data& sockopt) {
39 uint32_t optval;
40 memcpy(&optval, sockopt.optval, sizeof(optval));
41 return optval;
42}
43
44class TestableRoutingService : public RoutingService {
45 public:
46 TestableRoutingService() = default;
47 ~TestableRoutingService() = default;
48
49 int GetSockopt(int sockfd,
50 int level,
51 int optname,
52 void* optval,
53 socklen_t* optlen) override {
54 sockopt.sockfd = sockfd;
55 sockopt.level = level;
56 sockopt.optname = optname;
57 memcpy(optval, sockopt.optval,
58 std::min(*optlen, (socklen_t)sizeof(sockopt.optval)));
59 *optlen = sockopt.optlen;
60 return getsockopt_ret;
61 }
62
63 int SetSockopt(int sockfd,
64 int level,
65 int optname,
66 const void* optval,
67 socklen_t optlen) override {
68 sockopt.sockfd = sockfd;
69 sockopt.level = level;
70 sockopt.optname = optname;
71 sockopt.optlen = optlen;
72 memcpy(sockopt.optval, optval,
73 std::min(optlen, (socklen_t)sizeof(sockopt.optval)));
74 return setsockopt_ret;
75 }
76
77 // Variables used to mock and track interactions with getsockopt and
78 // setsockopt.
79 int getsockopt_ret;
80 int setsockopt_ret;
81 sockopt_data sockopt;
82};
83
84class RoutingServiceTest : public testing::Test {
85 public:
86 RoutingServiceTest() = default;
87
88 protected:
89 void SetUp() override {}
90};
91
92} // namespace
93
94TEST_F(RoutingServiceTest, SetVpnFwmark) {
95 auto svc = std::make_unique<TestableRoutingService>();
96 svc->getsockopt_ret = 0;
97 svc->setsockopt_ret = 0;
98
99 struct {
100 patchpanel::SetVpnIntentRequest::VpnRoutingPolicy policy;
101 uint32_t initial_fwmark;
102 uint32_t expected_fwmark;
103 } testcases[] = {
104 {ROUTE_ON_VPN, 0x0, 0x80000000},
105 {BYPASS_VPN, 0x0, 0x40000000},
106 {ROUTE_ON_VPN, 0x1, 0x80000001},
107 {BYPASS_VPN, 0x00abcdef, 0x40abcdef},
108 {ROUTE_ON_VPN, 0x11223344, 0x91223344},
109 {BYPASS_VPN, 0x11223344, 0x51223344},
110 {ROUTE_ON_VPN, 0x80000000, 0x80000000},
111 {BYPASS_VPN, 0x40000000, 0x40000000},
112 {BYPASS_VPN, 0x80000000, 0x40000000},
113 {ROUTE_ON_VPN, 0x40000000, 0x80000000},
114 {DEFAULT_ROUTING, 0x80000000, 0x00000000},
115 {DEFAULT_ROUTING, 0x40000000, 0x00000000},
116 };
117
118 for (const auto& tt : testcases) {
119 SetOptval(svc->sockopt, tt.initial_fwmark);
120 EXPECT_TRUE(svc->SetVpnFwmark(4, tt.policy));
121 EXPECT_EQ(4, svc->sockopt.sockfd);
122 EXPECT_EQ(SOL_SOCKET, svc->sockopt.level);
123 EXPECT_EQ(SO_MARK, svc->sockopt.optname);
124 EXPECT_EQ(hex(tt.expected_fwmark), hex(GetOptval(svc->sockopt)));
125 }
126
127 svc->getsockopt_ret = -1;
128 svc->setsockopt_ret = 0;
129 EXPECT_FALSE(svc->SetVpnFwmark(4, ROUTE_ON_VPN));
130
131 svc->getsockopt_ret = 0;
132 svc->setsockopt_ret = -1;
133 EXPECT_FALSE(svc->SetVpnFwmark(4, ROUTE_ON_VPN));
134
135 svc->getsockopt_ret = 0;
136 svc->setsockopt_ret = 0;
137 EXPECT_FALSE(svc->SetVpnFwmark(
138 4, (patchpanel::SetVpnIntentRequest::VpnRoutingPolicy)-1));
139}
140
141TEST_F(RoutingServiceTest, SetFwmark) {
142 auto svc = std::make_unique<TestableRoutingService>();
143 svc->getsockopt_ret = 0;
144 svc->setsockopt_ret = 0;
145
146 struct {
147 uint32_t initial_fwmark;
148 uint32_t fwmark_value;
149 uint32_t fwmark_mask;
150 uint32_t expected_fwmark;
151 } testcases[] = {
152 {0x0, 0x0, 0x0, 0x0},
153 {0x1, 0x0, 0x0, 0x1},
154 {0x1, 0x0, 0x1, 0x0},
155 {0xaabbccdd, 0x11223344, 0xf0f0f0f0, 0x1a2b3c4d},
156 {0xaabbccdd, 0x11223344, 0xffff0000, 0x1122ccdd},
157 {0xaabbccdd, 0x11223344, 0x0000ffff, 0xaabb3344},
158 };
159
160 for (const auto& tt : testcases) {
161 SetOptval(svc->sockopt, tt.initial_fwmark);
162 EXPECT_TRUE(svc->SetFwmark(4, tt.fwmark_value, tt.fwmark_mask));
163 EXPECT_EQ(4, svc->sockopt.sockfd);
164 EXPECT_EQ(SOL_SOCKET, svc->sockopt.level);
165 EXPECT_EQ(SO_MARK, svc->sockopt.optname);
166 EXPECT_EQ(hex(tt.expected_fwmark), hex(GetOptval(svc->sockopt)));
167 }
168}
169
170TEST_F(RoutingServiceTest, SetFwmark_Failures) {
171 auto svc = std::make_unique<TestableRoutingService>();
172 svc->getsockopt_ret = -1;
173 svc->setsockopt_ret = 0;
174 EXPECT_FALSE(svc->SetFwmark(4, 0x1, 0x1));
175
176 svc = std::make_unique<TestableRoutingService>();
177 svc->getsockopt_ret = 0;
178 svc->setsockopt_ret = -1;
179 EXPECT_FALSE(svc->SetFwmark(5, 0x1, 0x1));
180
181 svc = std::make_unique<TestableRoutingService>();
182 svc->getsockopt_ret = 0;
183 svc->setsockopt_ret = 0;
184 EXPECT_TRUE(svc->SetFwmark(6, 0x1, 0x1));
185}
186
Garrick Evans3388a032020-03-24 11:25:55 +0900187} // namespace patchpanel