1 /*
2 * Copyright 2022 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 * TcUtilsTest.cpp - unit tests for TcUtils.cpp
17 */
18
19 #include <gtest/gtest.h>
20
21 #include "bpf/KernelUtils.h"
22 #include <tcutils/tcutils.h>
23
24 #include <BpfSyscallWrappers.h>
25 #include <errno.h>
26 #include <linux/if_ether.h>
27
28 namespace android {
29
TEST(LibTcUtilsTest,IsEthernetOfNonExistingIf)30 TEST(LibTcUtilsTest, IsEthernetOfNonExistingIf) {
31 bool result = false;
32 int error = isEthernet("not_existing_if", result);
33 ASSERT_FALSE(result);
34 ASSERT_EQ(-ENODEV, error);
35 }
36
TEST(LibTcUtilsTest,IsEthernetOfLoopback)37 TEST(LibTcUtilsTest, IsEthernetOfLoopback) {
38 bool result = false;
39 int error = isEthernet("lo", result);
40 ASSERT_FALSE(result);
41 ASSERT_EQ(-EAFNOSUPPORT, error);
42 }
43
44 // If wireless 'wlan0' interface exists it should be Ethernet.
45 // See also HardwareAddressTypeOfWireless.
TEST(LibTcUtilsTest,IsEthernetOfWireless)46 TEST(LibTcUtilsTest, IsEthernetOfWireless) {
47 bool result = false;
48 int error = isEthernet("wlan0", result);
49 if (!result && error == -ENODEV)
50 return;
51
52 ASSERT_EQ(0, error);
53 ASSERT_TRUE(result);
54 }
55
56 // If cellular 'rmnet_data0' interface exists it should
57 // *probably* not be Ethernet and instead be RawIp.
58 // See also HardwareAddressTypeOfCellular.
TEST(LibTcUtilsTest,IsEthernetOfCellular)59 TEST(LibTcUtilsTest, IsEthernetOfCellular) {
60 bool result = false;
61 int error = isEthernet("rmnet_data0", result);
62 if (!result && error == -ENODEV)
63 return;
64
65 ASSERT_EQ(0, error);
66 ASSERT_FALSE(result);
67 }
68
69 // See Linux kernel source in include/net/flow.h
70 static constexpr int LOOPBACK_IFINDEX = 1;
71
TEST(LibTcUtilsTest,AttachReplaceDetachClsactLo)72 TEST(LibTcUtilsTest, AttachReplaceDetachClsactLo) {
73 // This attaches and detaches a configuration-less and thus no-op clsact
74 // qdisc to loopback interface (and it takes fractions of a second)
75 EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
76 EXPECT_EQ(0, tcReplaceQdiscClsact(LOOPBACK_IFINDEX));
77 EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
78 EXPECT_EQ(-EINVAL, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
79 }
80
TEST(LibTcUtilsTest,AddAndDeleteBpfFilter)81 TEST(LibTcUtilsTest, AddAndDeleteBpfFilter) {
82 // TODO: this should likely be in the tethering module, where using netd.h would be ok
83 static constexpr char bpfProgPath[] =
84 "/sys/fs/bpf/tethering/prog_offload_schedcls_tether_downstream6_ether";
85 const int errNOENT = bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
86
87 // static test values
88 static constexpr bool ingress = true;
89 static constexpr uint16_t prio = 17;
90 static constexpr uint16_t proto = ETH_P_ALL;
91
92 // try to delete missing filter from missing qdisc
93 EXPECT_EQ(-EINVAL, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
94 // try to attach bpf filter to missing qdisc
95 EXPECT_EQ(-EINVAL, tcAddBpfFilter(LOOPBACK_IFINDEX, ingress, prio, proto,
96 bpfProgPath));
97 // add the clsact qdisc
98 EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
99 // try to delete missing filter when there is a qdisc attached
100 EXPECT_EQ(-errNOENT, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
101 // add and delete a bpf filter
102 EXPECT_EQ(
103 0, tcAddBpfFilter(LOOPBACK_IFINDEX, ingress, prio, proto, bpfProgPath));
104 EXPECT_EQ(0, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
105 // try to remove the same filter a second time
106 EXPECT_EQ(-errNOENT, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
107 // remove the clsact qdisc
108 EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
109 // once again, try to delete missing filter from missing qdisc
110 EXPECT_EQ(-EINVAL, tcDeleteFilter(LOOPBACK_IFINDEX, ingress, prio, proto));
111 }
112
TEST(LibTcUtilsTest,AddAndDeleteIngressPoliceFilter)113 TEST(LibTcUtilsTest, AddAndDeleteIngressPoliceFilter) {
114 // TODO: this should likely be in the tethering module, where using netd.h would be ok
115 static constexpr char bpfProgPath[] =
116 "/sys/fs/bpf/netd_shared/prog_netd_schedact_ingress_account";
117 int fd = bpf::retrieveProgram(bpfProgPath);
118 ASSERT_LE(3, fd);
119 close(fd);
120
121 const int errNOENT = bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
122
123 // static test values
124 static constexpr unsigned rateInBytesPerSec =
125 1024 * 1024; // 8mbit/s => 1mbyte/s => 1024*1024 bytes/s.
126 static constexpr uint16_t prio = 17;
127 static constexpr uint16_t proto = ETH_P_ALL;
128
129 // try to delete missing filter from missing qdisc
130 EXPECT_EQ(-EINVAL,
131 tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
132 // try to attach bpf filter to missing qdisc
133 EXPECT_EQ(-EINVAL, tcAddIngressPoliceFilter(LOOPBACK_IFINDEX, prio, proto,
134 rateInBytesPerSec, bpfProgPath));
135 // add the clsact qdisc
136 EXPECT_EQ(0, tcAddQdiscClsact(LOOPBACK_IFINDEX));
137 // try to delete missing filter when there is a qdisc attached
138 EXPECT_EQ(-errNOENT,
139 tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
140 // add and delete a bpf filter
141 EXPECT_EQ(0, tcAddIngressPoliceFilter(LOOPBACK_IFINDEX, prio, proto,
142 rateInBytesPerSec, bpfProgPath));
143 EXPECT_EQ(0, tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
144 // try to remove the same filter a second time
145 EXPECT_EQ(-errNOENT,
146 tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
147 // remove the clsact qdisc
148 EXPECT_EQ(0, tcDeleteQdiscClsact(LOOPBACK_IFINDEX));
149 // once again, try to delete missing filter from missing qdisc
150 EXPECT_EQ(-EINVAL,
151 tcDeleteFilter(LOOPBACK_IFINDEX, true /*ingress*/, prio, proto));
152 }
153
154 } // namespace android
155