1 /*
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 *
16 * ClatUtilsTest.cpp - unit tests for ClatUtils.cpp
17 */
18
19 #include <gtest/gtest.h>
20
21 #include "ClatUtils.h"
22
23 #include <linux/if_arp.h>
24 #include <stdlib.h>
25 #include <sys/wait.h>
26
27 #include "bpf/BpfUtils.h"
28 #include "netdbpf/bpf_shared.h"
29
30 namespace android {
31 namespace net {
32
33 class ClatUtilsTest : public ::testing::Test {
34 public:
SetUp()35 void SetUp() {}
36 };
37
TEST_F(ClatUtilsTest,HardwareAddressTypeOfNonExistingIf)38 TEST_F(ClatUtilsTest, HardwareAddressTypeOfNonExistingIf) {
39 ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
40 }
41
TEST_F(ClatUtilsTest,HardwareAddressTypeOfLoopback)42 TEST_F(ClatUtilsTest, HardwareAddressTypeOfLoopback) {
43 ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
44 }
45
46 // If wireless 'wlan0' interface exists it should be Ethernet.
TEST_F(ClatUtilsTest,HardwareAddressTypeOfWireless)47 TEST_F(ClatUtilsTest, HardwareAddressTypeOfWireless) {
48 int type = hardwareAddressType("wlan0");
49 if (type == -ENODEV) return;
50
51 ASSERT_EQ(ARPHRD_ETHER, type);
52 }
53
54 // If cellular 'rmnet_data0' interface exists it should
55 // *probably* not be Ethernet and instead be RawIp.
TEST_F(ClatUtilsTest,HardwareAddressTypeOfCellular)56 TEST_F(ClatUtilsTest, HardwareAddressTypeOfCellular) {
57 int type = hardwareAddressType("rmnet_data0");
58 if (type == -ENODEV) return;
59
60 ASSERT_NE(ARPHRD_ETHER, type);
61
62 // ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
63 if (type == 530) return;
64
65 ASSERT_EQ(ARPHRD_RAWIP, type);
66 }
67
TEST_F(ClatUtilsTest,GetClatMapFd)68 TEST_F(ClatUtilsTest, GetClatMapFd) {
69 SKIP_IF_BPF_NOT_SUPPORTED;
70
71 int fd = getClatIngressMapFd();
72 ASSERT_LE(3, fd); // 0,1,2 - stdin/out/err, thus 3 <= fd
73 close(fd);
74 }
75
TEST_F(ClatUtilsTest,GetClatRawIpProgFd)76 TEST_F(ClatUtilsTest, GetClatRawIpProgFd) {
77 SKIP_IF_BPF_NOT_SUPPORTED;
78
79 int fd = getClatIngressProgFd(false);
80 ASSERT_LE(3, fd);
81 close(fd);
82 }
83
TEST_F(ClatUtilsTest,GetClatEtherProgFd)84 TEST_F(ClatUtilsTest, GetClatEtherProgFd) {
85 SKIP_IF_BPF_NOT_SUPPORTED;
86
87 int fd = getClatIngressProgFd(true);
88 ASSERT_LE(3, fd);
89 close(fd);
90 }
91
TEST_F(ClatUtilsTest,TryOpeningNetlinkSocket)92 TEST_F(ClatUtilsTest, TryOpeningNetlinkSocket) {
93 int fd = openNetlinkSocket();
94 ASSERT_LE(3, fd);
95 close(fd);
96 }
97
98 // The SKIP_IF_BPF_NOT_SUPPORTED macro is effectively a check for 4.9+ kernel
99 // combined with a launched on P device. Ie. it's a test for 4.9-P or better.
100
101 // NET_SCH_INGRESS is only enabled starting with 4.9-Q and as such we need
102 // a separate way to test for this...
doKernelSupportsNetSchIngress(void)103 int doKernelSupportsNetSchIngress(void) {
104 // NOLINTNEXTLINE(cert-env33-c)
105 return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_SCH_INGRESS=[my]$'");
106 }
107
108 // NET_CLS_BPF is only enabled starting with 4.9-Q...
doKernelSupportsNetClsBpf(void)109 int doKernelSupportsNetClsBpf(void) {
110 // NOLINTNEXTLINE(cert-env33-c)
111 return system("zcat /proc/config.gz | egrep -q '^CONFIG_NET_CLS_BPF=[my]$'");
112 }
113
114 // Make sure the above functions actually execute correctly rather than failing
115 // due to missing binary or execution failure...
TEST_F(ClatUtilsTest,KernelSupportsNetFuncs)116 TEST_F(ClatUtilsTest, KernelSupportsNetFuncs) {
117 // Make sure the file is present and readable and decompressable.
118 // NOLINTNEXTLINE(cert-env33-c)
119 ASSERT_EQ(W_EXITCODE(0, 0), system("zcat /proc/config.gz > /dev/null"));
120
121 int v = doKernelSupportsNetSchIngress();
122 int w = doKernelSupportsNetClsBpf();
123
124 // They should always either return 0 (match) or 1 (no match),
125 // anything else is some sort of exec/environment/etc failure.
126 if (v != W_EXITCODE(1, 0)) ASSERT_EQ(v, W_EXITCODE(0, 0));
127 if (w != W_EXITCODE(1, 0)) ASSERT_EQ(w, W_EXITCODE(0, 0));
128 }
129
130 // True iff CONFIG_NET_SCH_INGRESS is enabled in /proc/config.gz
kernelSupportsNetSchIngress(void)131 bool kernelSupportsNetSchIngress(void) {
132 return doKernelSupportsNetSchIngress() == W_EXITCODE(0, 0);
133 }
134
135 // True iff CONFIG_NET_CLS_BPF is enabled in /proc/config.gz
kernelSupportsNetClsBpf(void)136 bool kernelSupportsNetClsBpf(void) {
137 return doKernelSupportsNetClsBpf() == W_EXITCODE(0, 0);
138 }
139
140 // See Linux kernel source in include/net/flow.h
141 #define LOOPBACK_IFINDEX 1
142
TEST_F(ClatUtilsTest,AttachReplaceDetachClsactLo)143 TEST_F(ClatUtilsTest, AttachReplaceDetachClsactLo) {
144 // Technically does not depend on ebpf, but does depend on clsact,
145 // and we do not really care if it works on pre-4.9-Q anyway.
146 SKIP_IF_BPF_NOT_SUPPORTED;
147 if (!kernelSupportsNetSchIngress()) return;
148
149 int fd = openNetlinkSocket();
150 ASSERT_LE(3, fd);
151
152 // This attaches and detaches a configuration-less and thus no-op clsact
153 // qdisc to loopback interface (and it takes fractions of a second)
154 EXPECT_EQ(0, tcQdiscAddDevClsact(fd, LOOPBACK_IFINDEX));
155 EXPECT_EQ(0, tcQdiscReplaceDevClsact(fd, LOOPBACK_IFINDEX));
156 EXPECT_EQ(0, tcQdiscDelDevClsact(fd, LOOPBACK_IFINDEX));
157 close(fd);
158 }
159
checkAttachBpfFilterClsactLo(const bool ethernet)160 void checkAttachBpfFilterClsactLo(const bool ethernet) {
161 // This test requires kernel 4.9-Q or better
162 SKIP_IF_BPF_NOT_SUPPORTED;
163 if (!kernelSupportsNetSchIngress()) return;
164 if (!kernelSupportsNetClsBpf()) return;
165
166 int bpf_fd = getClatIngressProgFd(false);
167 ASSERT_LE(3, bpf_fd);
168
169 int fd = openNetlinkSocket();
170 EXPECT_LE(3, fd);
171 if (fd >= 0) {
172 // This attaches and detaches a clsact plus ebpf program to loopback
173 // interface, but it should not affect traffic by virtue of us not
174 // actually populating the ebpf control map.
175 // Furthermore: it only takes fractions of a second.
176 EXPECT_EQ(0, tcQdiscAddDevClsact(fd, LOOPBACK_IFINDEX));
177 EXPECT_EQ(0, tcFilterAddDevBpf(fd, LOOPBACK_IFINDEX, bpf_fd, ethernet));
178 EXPECT_EQ(0, tcQdiscDelDevClsact(fd, LOOPBACK_IFINDEX));
179 close(fd);
180 }
181
182 close(bpf_fd);
183 }
184
TEST_F(ClatUtilsTest,CheckAttachBpfFilterRawIpClsactLo)185 TEST_F(ClatUtilsTest, CheckAttachBpfFilterRawIpClsactLo) {
186 checkAttachBpfFilterClsactLo(false);
187 }
188
TEST_F(ClatUtilsTest,CheckAttachBpfFilterEthernetClsactLo)189 TEST_F(ClatUtilsTest, CheckAttachBpfFilterEthernetClsactLo) {
190 checkAttachBpfFilterClsactLo(true);
191 }
192
193 } // namespace net
194 } // namespace android
195