blob: a2c398232303f812e7ad15b57fe85aaf7cce6bc4 [file] [log] [blame]
Maciej Żenczykowskib70da762019-01-28 15:20:48 -08001/*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080016 * OffloadUtilsTest.cpp - unit tests for OffloadUtils.cpp
Maciej Żenczykowskib70da762019-01-28 15:20:48 -080017 */
18
19#include <gtest/gtest.h>
20
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080021#include "OffloadUtils.h"
Maciej Żenczykowskib70da762019-01-28 15:20:48 -080022
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080023#include <linux/if_arp.h>
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -070024#include <stdlib.h>
25#include <sys/wait.h>
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080026
Maciej Żenczykowski88d28ff2019-03-25 11:54:32 -070027#include "bpf/BpfUtils.h"
28#include "netdbpf/bpf_shared.h"
29
Maciej Żenczykowskib70da762019-01-28 15:20:48 -080030namespace android {
31namespace net {
32
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080033class OffloadUtilsTest : public ::testing::Test {
Maciej Żenczykowskib70da762019-01-28 15:20:48 -080034 public:
35 void SetUp() {}
36};
37
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080038TEST_F(OffloadUtilsTest, HardwareAddressTypeOfNonExistingIf) {
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -070039 ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080040}
41
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080042TEST_F(OffloadUtilsTest, HardwareAddressTypeOfLoopback) {
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -070043 ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080044}
45
46// If wireless 'wlan0' interface exists it should be Ethernet.
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080047TEST_F(OffloadUtilsTest, HardwareAddressTypeOfWireless) {
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080048 int type = hardwareAddressType("wlan0");
49 if (type == -ENODEV) return;
50
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -070051 ASSERT_EQ(ARPHRD_ETHER, type);
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080052}
53
54// If cellular 'rmnet_data0' interface exists it should
55// *probably* not be Ethernet and instead be RawIp.
Maciej Żenczykowskieec72082020-02-04 23:29:41 -080056TEST_F(OffloadUtilsTest, HardwareAddressTypeOfCellular) {
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080057 int type = hardwareAddressType("rmnet_data0");
58 if (type == -ENODEV) return;
59
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -070060 ASSERT_NE(ARPHRD_ETHER, type);
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080061
62 // ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
63 if (type == 530) return;
64
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -070065 ASSERT_EQ(ARPHRD_RAWIP, type);
Maciej Żenczykowski0a7dce82019-01-28 15:31:55 -080066}
67
Hungming Chenec203382020-02-13 19:15:33 +080068TEST_F(OffloadUtilsTest, IsEthernetOfNonExistingIf) {
69 auto res = isEthernet("not_existing_if");
70 ASSERT_FALSE(res.ok());
71 ASSERT_EQ(ENODEV, res.error().code());
72}
73
74TEST_F(OffloadUtilsTest, IsEthernetOfLoopback) {
75 auto res = isEthernet("lo");
76 ASSERT_FALSE(res.ok());
77 ASSERT_EQ(EAFNOSUPPORT, res.error().code());
78}
79
80// If wireless 'wlan0' interface exists it should be Ethernet.
81// See also HardwareAddressTypeOfWireless.
82TEST_F(OffloadUtilsTest, IsEthernetOfWireless) {
83 auto res = isEthernet("wlan0");
84 if (!res.ok() && res.error().code() == ENODEV) return;
85
86 ASSERT_RESULT_OK(res);
87 ASSERT_TRUE(res.value());
88}
89
90// If cellular 'rmnet_data0' interface exists it should
91// *probably* not be Ethernet and instead be RawIp.
92// See also HardwareAddressTypeOfCellular.
93TEST_F(OffloadUtilsTest, IsEthernetOfCellular) {
94 auto res = isEthernet("rmnet_data0");
95 if (!res.ok() && res.error().code() == ENODEV) return;
96
97 ASSERT_RESULT_OK(res);
98 ASSERT_FALSE(res.value());
99}
100
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800101TEST_F(OffloadUtilsTest, GetClatEgressMapFd) {
Maciej Żenczykowski4e36f132019-12-15 13:20:15 -0800102 SKIP_IF_BPF_NOT_SUPPORTED;
103
104 int fd = getClatEgressMapFd();
105 ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
106 close(fd);
107}
108
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800109TEST_F(OffloadUtilsTest, GetClatEgressRawIpProgFd) {
Maciej Żenczykowskie870a872019-12-15 13:56:13 -0800110 SKIP_IF_BPF_NOT_SUPPORTED;
111
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800112 int fd = getClatEgressProgFd(RAWIP);
Maciej Żenczykowskie870a872019-12-15 13:56:13 -0800113 ASSERT_LE(3, fd);
114 close(fd);
115}
116
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800117TEST_F(OffloadUtilsTest, GetClatEgressEtherProgFd) {
Maciej Żenczykowskie870a872019-12-15 13:56:13 -0800118 SKIP_IF_BPF_NOT_SUPPORTED;
119
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800120 int fd = getClatEgressProgFd(ETHER);
Maciej Żenczykowskie870a872019-12-15 13:56:13 -0800121 ASSERT_LE(3, fd);
122 close(fd);
123}
124
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800125TEST_F(OffloadUtilsTest, GetClatIngressMapFd) {
Maciej Żenczykowski88d28ff2019-03-25 11:54:32 -0700126 SKIP_IF_BPF_NOT_SUPPORTED;
127
Maciej Żenczykowski4fe857e2019-03-29 23:29:17 -0700128 int fd = getClatIngressMapFd();
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -0700129 ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
130 close(fd);
Maciej Żenczykowski88d28ff2019-03-25 11:54:32 -0700131}
132
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800133TEST_F(OffloadUtilsTest, GetClatIngressRawIpProgFd) {
Maciej Żenczykowski949d84a2019-01-28 17:22:30 -0800134 SKIP_IF_BPF_NOT_SUPPORTED;
135
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800136 int fd = getClatIngressProgFd(RAWIP);
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -0700137 ASSERT_LE(3, fd);
138 close(fd);
Maciej Żenczykowski949d84a2019-01-28 17:22:30 -0800139}
140
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800141TEST_F(OffloadUtilsTest, GetClatIngressEtherProgFd) {
Maciej Żenczykowski949d84a2019-01-28 17:22:30 -0800142 SKIP_IF_BPF_NOT_SUPPORTED;
143
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800144 int fd = getClatIngressProgFd(ETHER);
Maciej Żenczykowski10a2ed52019-03-27 15:46:26 -0700145 ASSERT_LE(3, fd);
146 close(fd);
Maciej Żenczykowski949d84a2019-01-28 17:22:30 -0800147}
148
Maciej Żenczykowskifdd79292020-02-12 04:28:16 -0800149TEST_F(OffloadUtilsTest, GetTetherIngressMapFd) {
150 SKIP_IF_BPF_NOT_SUPPORTED;
151
152 int fd = getTetherIngressMapFd();
153 ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
154 close(fd);
155}
156
Maciej Żenczykowski4acd4e82020-02-12 04:30:14 -0800157TEST_F(OffloadUtilsTest, GetTetherIngressRawIpProgFd) {
158 // Currently only implementing downstream direction offload.
159 // RX Rawip -> TX Ether requires header adjustments and thus 4.14.
160 SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED;
161
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800162 int fd = getTetherIngressProgFd(RAWIP);
Maciej Żenczykowski4acd4e82020-02-12 04:30:14 -0800163 ASSERT_LE(3, fd);
164 close(fd);
165}
166
167TEST_F(OffloadUtilsTest, GetTetherIngressEtherProgFd) {
168 // Currently only implementing downstream direction offload.
169 // RX Ether -> TX Ether does not require header adjustments
170 SKIP_IF_BPF_NOT_SUPPORTED;
171
Maciej Żenczykowskiad730892020-02-13 18:21:32 -0800172 int fd = getTetherIngressProgFd(ETHER);
Maciej Żenczykowski4acd4e82020-02-12 04:30:14 -0800173 ASSERT_LE(3, fd);
174 close(fd);
175}
176
Maciej Żenczykowski66ce72d2020-02-12 05:05:00 -0800177TEST_F(OffloadUtilsTest, GetTetherStatsMapFd) {
178 SKIP_IF_BPF_NOT_SUPPORTED;
179
180 int fd = getTetherStatsMapFd();
181 ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
182 close(fd);
183}
184
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700185// The SKIP_IF_BPF_NOT_SUPPORTED macro is effectively a check for 4.9+ kernel
186// combined with a launched on P device. Ie. it's a test for 4.9-P or better.
187
188// NET_SCH_INGRESS is only enabled starting with 4.9-Q and as such we need
189// a separate way to test for this...
190int doKernelSupportsNetSchIngress(void) {
Maciej Żenczykowski4e4aaff2019-04-09 02:51:35 -0700191 // NOLINTNEXTLINE(cert-env33-c)
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700192 return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_SCH_INGRESS=[my]$'");
193}
194
195// NET_CLS_BPF is only enabled starting with 4.9-Q...
196int doKernelSupportsNetClsBpf(void) {
Maciej Żenczykowski4e4aaff2019-04-09 02:51:35 -0700197 // NOLINTNEXTLINE(cert-env33-c)
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700198 return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_CLS_BPF=[my]$'");
199}
200
201// Make sure the above functions actually execute correctly rather than failing
202// due to missing binary or execution failure...
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800203TEST_F(OffloadUtilsTest, KernelSupportsNetFuncs) {
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700204 // Make sure the file is present and readable and decompressable.
Maciej Żenczykowski4e4aaff2019-04-09 02:51:35 -0700205 // NOLINTNEXTLINE(cert-env33-c)
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700206 ASSERT_EQ(W_EXITCODE(0, 0), system("zcat /proc/config.gz > /dev/null"));
207
208 int v = doKernelSupportsNetSchIngress();
209 int w = doKernelSupportsNetClsBpf();
210
211 // They should always either return 0 (match) or 1 (no match),
212 // anything else is some sort of exec/environment/etc failure.
213 if (v != W_EXITCODE(1, 0)) ASSERT_EQ(v, W_EXITCODE(0, 0));
214 if (w != W_EXITCODE(1, 0)) ASSERT_EQ(w, W_EXITCODE(0, 0));
215}
216
217// True iff CONFIG_NET_SCH_INGRESS is enabled in /proc/config.gz
218bool kernelSupportsNetSchIngress(void) {
219 return doKernelSupportsNetSchIngress() == W_EXITCODE(0, 0);
220}
221
222// True iff CONFIG_NET_CLS_BPF is enabled in /proc/config.gz
223bool kernelSupportsNetClsBpf(void) {
224 return doKernelSupportsNetClsBpf() == W_EXITCODE(0, 0);
225}
226
Maciej Żenczykowskiff3308d2019-02-12 19:10:55 -0800227// See Linux kernel source in include/net/flow.h
228#define LOOPBACK_IFINDEX 1
229
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800230TEST_F(OffloadUtilsTest, AttachReplaceDetachClsactLo) {
Maciej Żenczykowskiff3308d2019-02-12 19:10:55 -0800231 // Technically does not depend on ebpf, but does depend on clsact,
232 // and we do not really care if it works on pre-4.9-Q anyway.
233 SKIP_IF_BPF_NOT_SUPPORTED;
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700234 if (!kernelSupportsNetSchIngress()) return;
Maciej Żenczykowskiff3308d2019-02-12 19:10:55 -0800235
Maciej Żenczykowskiff3308d2019-02-12 19:10:55 -0800236 // This attaches and detaches a configuration-less and thus no-op clsact
237 // qdisc to loopback interface (and it takes fractions of a second)
Maciej Żenczykowskif89e9112020-02-12 05:22:56 -0800238 EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
239 EXPECT_EQ(0, tcQdiscReplaceDevClsact(LOOPBACK_IFINDEX));
240 EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
241 EXPECT_EQ(-EINVAL, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
Maciej Żenczykowskiff3308d2019-02-12 19:10:55 -0800242}
243
Maciej Żenczykowskif1247382020-02-05 00:44:14 -0800244static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool ethernet) {
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700245 // This test requires kernel 4.9-Q or better
246 SKIP_IF_BPF_NOT_SUPPORTED;
Maciej Żenczykowski18edc522019-04-04 00:59:19 -0700247 if (!kernelSupportsNetSchIngress()) return;
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700248 if (!kernelSupportsNetClsBpf()) return;
249
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800250 const bool extended = (android::bpf::getBpfSupportLevel() >= android::bpf::BpfLevel::EXTENDED);
Maciej Żenczykowski8831f222020-02-05 04:50:47 -0800251 // 4.9 returns EINVAL instead of ENOENT...
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800252 const int errNOENT = extended ? ENOENT : EINVAL;
Maciej Żenczykowski8831f222020-02-05 04:50:47 -0800253
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800254 int clatBpfFd = ingress ? getClatIngressProgFd(ethernet) : getClatEgressProgFd(ethernet);
255 ASSERT_LE(3, clatBpfFd);
256
257 int tetherBpfFd = -1;
258 if (extended && ingress) {
259 tetherBpfFd = getTetherIngressProgFd(ethernet);
260 ASSERT_LE(3, tetherBpfFd);
261 }
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700262
Maciej Żenczykowskif89e9112020-02-12 05:22:56 -0800263 // This attaches and detaches a clsact plus ebpf program to loopback
264 // interface, but it should not affect traffic by virtue of us not
265 // actually populating the ebpf control map.
266 // Furthermore: it only takes fractions of a second.
267 EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
268 EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
269 EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
270 EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
271 EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
272 if (ingress) {
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800273 EXPECT_EQ(0, tcFilterAddDevIngressClatIpv6(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
274 if (extended) {
275 EXPECT_EQ(0, tcFilterAddDevIngressTether(LOOPBACK_IFINDEX, tetherBpfFd, ethernet));
276 EXPECT_EQ(0, tcFilterDelDevIngressTether(LOOPBACK_IFINDEX));
277 }
Maciej Żenczykowskif89e9112020-02-12 05:22:56 -0800278 EXPECT_EQ(0, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
279 } else {
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800280 EXPECT_EQ(0, tcFilterAddDevEgressClatIpv4(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
Maciej Żenczykowskif89e9112020-02-12 05:22:56 -0800281 EXPECT_EQ(0, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700282 }
Maciej Żenczykowskif89e9112020-02-12 05:22:56 -0800283 EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
284 EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
285 EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
286 EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
287 EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700288
Maciej Żenczykowski58efd782020-02-16 17:15:26 -0800289 if (tetherBpfFd != -1) close(tetherBpfFd);
290 close(clatBpfFd);
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700291}
292
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800293TEST_F(OffloadUtilsTest, CheckAttachBpfFilterRawIpClsactEgressLo) {
Maciej Żenczykowski86400032020-02-13 18:39:34 -0800294 checkAttachDetachBpfFilterClsactLo(EGRESS, RAWIP);
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700295}
296
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800297TEST_F(OffloadUtilsTest, CheckAttachBpfFilterEthernetClsactEgressLo) {
Maciej Żenczykowski86400032020-02-13 18:39:34 -0800298 checkAttachDetachBpfFilterClsactLo(EGRESS, ETHER);
Maciej Żenczykowskia06943c2019-12-15 11:57:42 -0800299}
300
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800301TEST_F(OffloadUtilsTest, CheckAttachBpfFilterRawIpClsactIngressLo) {
Maciej Żenczykowski86400032020-02-13 18:39:34 -0800302 checkAttachDetachBpfFilterClsactLo(INGRESS, RAWIP);
Maciej Żenczykowskia06943c2019-12-15 11:57:42 -0800303}
304
Maciej Żenczykowskieec72082020-02-04 23:29:41 -0800305TEST_F(OffloadUtilsTest, CheckAttachBpfFilterEthernetClsactIngressLo) {
Maciej Żenczykowski86400032020-02-13 18:39:34 -0800306 checkAttachDetachBpfFilterClsactLo(INGRESS, ETHER);
Maciej Żenczykowski2f8ff892019-03-25 13:57:20 -0700307}
308
Maciej Żenczykowskib70da762019-01-28 15:20:48 -0800309} // namespace net
310} // namespace android