• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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