1 /*
2 * Copyright (C) 2018 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 requied 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 */
17
18 #define LOG_TAG "BpfTest"
19
20 #include <arpa/inet.h>
21 #include <assert.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <linux/pfkeyv2.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29
30 #include <thread>
31
32 #include <android-base/file.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/unique_fd.h>
35 #include <gtest/gtest.h>
36 #include <utils/Log.h>
37
38 #include "bpf/BpfMap.h"
39 #include "bpf/BpfUtils.h"
40 #include "kern.h"
41 #include "libbpf_android.h"
42
43 using android::base::unique_fd;
44 using namespace android::bpf;
45
46 namespace android {
47
TEST(BpfTest,bpfMapPinTest)48 TEST(BpfTest, bpfMapPinTest) {
49 SKIP_IF_BPF_NOT_SUPPORTED;
50
51 const char* bpfMapPath = "/sys/fs/bpf/testMap";
52 int ret = access(bpfMapPath, F_OK);
53 if (!ret) {
54 ASSERT_EQ(0, remove(bpfMapPath));
55 } else {
56 ASSERT_EQ(errno, ENOENT);
57 }
58
59 android::base::unique_fd mapfd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t),
60 sizeof(uint32_t), 10,
61 BPF_F_NO_PREALLOC));
62 ASSERT_LT(0, mapfd) << "create map failed with error: " << strerror(errno);
63 ASSERT_EQ(0, bpfFdPin(mapfd, bpfMapPath))
64 << "pin map failed with error: " << strerror(errno);
65 ASSERT_EQ(0, access(bpfMapPath, F_OK));
66 ASSERT_EQ(0, remove(bpfMapPath));
67 }
68
69 #define BPF_SRC_PATH "/data/local/tmp"
70
71 #if defined(__aarch64__) || defined(__x86_64__)
72 #define BPF_SRC_NAME "/64/kern.o"
73 #else
74 #define BPF_SRC_NAME "/32/kern.o"
75 #endif
76
77 #define BPF_PATH "/sys/fs/bpf"
78 #define TEST_PROG_PATH BPF_PATH "/prog_kern_skfilter_test"
79 #define TEST_STATS_MAP_A_PATH BPF_PATH "/map_kern_test_stats_map_A"
80 #define TEST_STATS_MAP_B_PATH BPF_PATH "/map_kern_test_stats_map_B"
81 #define TEST_CONFIGURATION_MAP_PATH BPF_PATH "/map_kern_test_configuration_map"
82
83 constexpr int ACTIVE_MAP_KEY = 1;
84
85 class BpfRaceTest : public ::testing::Test {
86 protected:
BpfRaceTest()87 BpfRaceTest() {}
88 BpfMap<uint64_t, stats_value> cookieStatsMap[2];
89 BpfMap<uint32_t, uint32_t> configurationMap;
90 BpfProgInfo program;
91 bool stop;
92 std::thread tds[NUM_SOCKETS];
93
workerThread(int prog_fd,bool * stop)94 static void workerThread(int prog_fd, bool *stop) {
95 struct sockaddr_in6 remote = {.sin6_family = AF_INET6};
96 struct sockaddr_in6 local;
97 uint64_t j = 0;
98 int recvSock, sendSock, recv_len;
99 char buf[strlen("msg: 18446744073709551615")];
100 int res;
101 socklen_t slen = sizeof(remote);
102
103 recvSock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
104 EXPECT_NE(-1, recvSock);
105 std::string address = android::base::StringPrintf("::1");
106 EXPECT_NE(0, inet_pton(AF_INET6, address.c_str(), &remote.sin6_addr));
107 EXPECT_NE(-1, bind(recvSock, (struct sockaddr *)&remote, sizeof(remote)));
108 EXPECT_EQ(0, getsockname(recvSock, (struct sockaddr *)&remote, &slen));
109 sendSock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
110 EXPECT_NE(-1, sendSock) << "send socket create failed!\n";
111 EXPECT_NE(-1, setsockopt(recvSock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
112 sizeof(prog_fd)))
113 << "attach bpf program failed"
114 << android::base::StringPrintf("%s\n", strerror(errno));
115
116 // Keep sending and receiving packet until test end.
117 while (!*stop) {
118 std::string id = android::base::StringPrintf("msg: %" PRIu64 "\n", j);
119 res = sendto(sendSock, &id, id.length(), 0, (struct sockaddr *)&remote,
120 slen);
121 EXPECT_EQ(id.size(), res);
122 recv_len = recvfrom(recvSock, &buf, sizeof(buf), 0,
123 (struct sockaddr *)&local, &slen);
124 EXPECT_EQ(id.size(), recv_len);
125 }
126 }
127
SetUp()128 void SetUp() {
129 SKIP_IF_BPF_NOT_SUPPORTED;
130 int ret = access(TEST_PROG_PATH, R_OK);
131 // Always create a new program and remove the pinned program after program
132 // loading is done.
133 if (ret == 0) {
134 remove(TEST_PROG_PATH);
135 }
136 std::string progSrcPath = BPF_SRC_PATH BPF_SRC_NAME;
137 ASSERT_EQ(0, android::bpf::loadProg(progSrcPath.c_str()));
138
139 EXPECT_TRUE(isOk(cookieStatsMap[0].init(TEST_STATS_MAP_A_PATH)));
140 EXPECT_TRUE(isOk(cookieStatsMap[1].init(TEST_STATS_MAP_B_PATH)));
141 EXPECT_TRUE(isOk(configurationMap.init(TEST_CONFIGURATION_MAP_PATH)));
142 EXPECT_TRUE(cookieStatsMap[0].isValid());
143 EXPECT_TRUE(cookieStatsMap[1].isValid());
144 EXPECT_TRUE(configurationMap.isValid());
145 // Start several threads to send and receive packets with an eBPF program
146 // attached to the socket.
147 stop = false;
148 int prog_fd = bpfFdGet(TEST_PROG_PATH, 0);
149 EXPECT_OK(configurationMap.writeValue(ACTIVE_MAP_KEY, 0, BPF_ANY));
150
151 for (int i = 0; i < NUM_SOCKETS; i++) {
152 tds[i] = std::thread(workerThread, prog_fd, &stop);
153 }
154 }
155
TearDown()156 void TearDown() {
157 SKIP_IF_BPF_NOT_SUPPORTED;
158
159 // Stop the threads and clean up the program.
160 stop = true;
161 for (int i = 0; i < NUM_SOCKETS; i++) {
162 tds[i].join();
163 }
164 remove(TEST_PROG_PATH);
165 remove(TEST_STATS_MAP_A_PATH);
166 remove(TEST_STATS_MAP_B_PATH);
167 remove(TEST_CONFIGURATION_MAP_PATH);
168 }
169
swapAndCleanStatsMap(bool expectSynchronized,int seconds)170 void swapAndCleanStatsMap(bool expectSynchronized, int seconds) {
171 uint64_t i = 0;
172 auto start = std::clock();
173 while (((double)(std::clock() - start) / CLOCKS_PER_SEC) < seconds) {
174 // Check if the vacant map is empty based on the current configuration.
175 auto isEmpty = cookieStatsMap[i].isEmpty();
176 EXPECT_TRUE(isOk(isEmpty));
177 if (expectSynchronized) {
178 // The map should always be empty because synchronizeKernelRCU should
179 // ensure that the BPF programs running on all cores have seen the write
180 // to the configuration map that tells them to write to the other map.
181 // If it's not empty, fail.
182 ASSERT_TRUE(isEmpty.value())
183 << "Race problem between stats clean and updates";
184 } else if (!isEmpty.value()) {
185 // We found a race condition, which is expected (eventually) because
186 // we're not calling synchronizeKernelRCU. Pass the test.
187 break;
188 }
189
190 // Change the configuration and wait for rcu grace period.
191 i ^= 1;
192 EXPECT_OK(configurationMap.writeValue(ACTIVE_MAP_KEY, i, BPF_ANY));
193 if (expectSynchronized) {
194 EXPECT_EQ(0, synchronizeKernelRCU());
195 }
196
197 // Clean up the previous map after map swap.
198 EXPECT_OK(cookieStatsMap[i].clear());
199 }
200 if (!expectSynchronized) {
201 EXPECT_GE(seconds, (double)(std::clock() - start) / CLOCKS_PER_SEC)
202 << "Race problem didn't happen before time out";
203 }
204 }
205 };
206
207 // Verify the race problem disappear when the kernel call synchronize_rcu
208 // after changing the active map.
TEST_F(BpfRaceTest,testRaceWithBarrier)209 TEST_F(BpfRaceTest, testRaceWithBarrier) {
210 SKIP_IF_BPF_NOT_SUPPORTED;
211
212 swapAndCleanStatsMap(true, 60);
213 }
214
215 // Confirm the race problem exists when the kernel doesn't call synchronize_rcu
216 // after changing the active map.
TEST_F(BpfRaceTest,testRaceWithoutBarrier)217 TEST_F(BpfRaceTest, testRaceWithoutBarrier) {
218 SKIP_IF_BPF_NOT_SUPPORTED;
219
220 swapAndCleanStatsMap(false, 20);
221 }
222
223 } // namespace android
224