1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2020 Cloudflare 3 #include <error.h> 4 #include <netinet/tcp.h> 5 6 #include "test_progs.h" 7 #include "test_skmsg_load_helpers.skel.h" 8 #include "test_sockmap_update.skel.h" 9 #include "test_sockmap_invalid_update.skel.h" 10 #include "test_sockmap_skb_verdict_attach.skel.h" 11 #include "test_sockmap_progs_query.skel.h" 12 #include "bpf_iter_sockmap.skel.h" 13 14 #define TCP_REPAIR 19 /* TCP sock is under repair right now */ 15 16 #define TCP_REPAIR_ON 1 17 #define TCP_REPAIR_OFF_NO_WP -1 /* Turn off without window probes */ 18 19 static int connected_socket_v4(void) 20 { 21 struct sockaddr_in addr = { 22 .sin_family = AF_INET, 23 .sin_port = htons(80), 24 .sin_addr = { inet_addr("127.0.0.1") }, 25 }; 26 socklen_t len = sizeof(addr); 27 int s, repair, err; 28 29 s = socket(AF_INET, SOCK_STREAM, 0); 30 if (CHECK_FAIL(s == -1)) 31 goto error; 32 33 repair = TCP_REPAIR_ON; 34 err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); 35 if (CHECK_FAIL(err)) 36 goto error; 37 38 err = connect(s, (struct sockaddr *)&addr, len); 39 if (CHECK_FAIL(err)) 40 goto error; 41 42 repair = TCP_REPAIR_OFF_NO_WP; 43 err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); 44 if (CHECK_FAIL(err)) 45 goto error; 46 47 return s; 48 error: 49 perror(__func__); 50 close(s); 51 return -1; 52 } 53 54 static void compare_cookies(struct bpf_map *src, struct bpf_map *dst) 55 { 56 __u32 i, max_entries = bpf_map__max_entries(src); 57 int err, duration = 0, src_fd, dst_fd; 58 59 src_fd = bpf_map__fd(src); 60 dst_fd = bpf_map__fd(dst); 61 62 for (i = 0; i < max_entries; i++) { 63 __u64 src_cookie, dst_cookie; 64 65 err = bpf_map_lookup_elem(src_fd, &i, &src_cookie); 66 if (err && errno == ENOENT) { 67 err = bpf_map_lookup_elem(dst_fd, &i, &dst_cookie); 68 CHECK(!err, "map_lookup_elem(dst)", "element %u not deleted\n", i); 69 CHECK(err && errno != ENOENT, "map_lookup_elem(dst)", "%s\n", 70 strerror(errno)); 71 continue; 72 } 73 if (CHECK(err, "lookup_elem(src)", "%s\n", strerror(errno))) 74 continue; 75 76 err = bpf_map_lookup_elem(dst_fd, &i, &dst_cookie); 77 if (CHECK(err, "lookup_elem(dst)", "%s\n", strerror(errno))) 78 continue; 79 80 CHECK(dst_cookie != src_cookie, "cookie mismatch", 81 "%llu != %llu (pos %u)\n", dst_cookie, src_cookie, i); 82 } 83 } 84 85 /* Create a map, populate it with one socket, and free the map. */ 86 static void test_sockmap_create_update_free(enum bpf_map_type map_type) 87 { 88 const int zero = 0; 89 int s, map, err; 90 91 s = connected_socket_v4(); 92 if (CHECK_FAIL(s < 0)) 93 return; 94 95 map = bpf_map_create(map_type, NULL, sizeof(int), sizeof(int), 1, NULL); 96 if (CHECK_FAIL(map < 0)) { 97 perror("bpf_cmap_create"); 98 goto out; 99 } 100 101 err = bpf_map_update_elem(map, &zero, &s, BPF_NOEXIST); 102 if (CHECK_FAIL(err)) { 103 perror("bpf_map_update"); 104 goto out; 105 } 106 107 out: 108 close(map); 109 close(s); 110 } 111 112 static void test_skmsg_helpers(enum bpf_map_type map_type) 113 { 114 struct test_skmsg_load_helpers *skel; 115 int err, map, verdict; 116 117 skel = test_skmsg_load_helpers__open_and_load(); 118 if (CHECK_FAIL(!skel)) { 119 perror("test_skmsg_load_helpers__open_and_load"); 120 return; 121 } 122 123 verdict = bpf_program__fd(skel->progs.prog_msg_verdict); 124 map = bpf_map__fd(skel->maps.sock_map); 125 126 err = bpf_prog_attach(verdict, map, BPF_SK_MSG_VERDICT, 0); 127 if (CHECK_FAIL(err)) { 128 perror("bpf_prog_attach"); 129 goto out; 130 } 131 132 err = bpf_prog_detach2(verdict, map, BPF_SK_MSG_VERDICT); 133 if (CHECK_FAIL(err)) { 134 perror("bpf_prog_detach2"); 135 goto out; 136 } 137 out: 138 test_skmsg_load_helpers__destroy(skel); 139 } 140 141 static void test_sockmap_update(enum bpf_map_type map_type) 142 { 143 int err, prog, src, duration = 0; 144 struct test_sockmap_update *skel; 145 struct bpf_map *dst_map; 146 const __u32 zero = 0; 147 char dummy[14] = {0}; 148 LIBBPF_OPTS(bpf_test_run_opts, topts, 149 .data_in = dummy, 150 .data_size_in = sizeof(dummy), 151 .repeat = 1, 152 ); 153 __s64 sk; 154 155 sk = connected_socket_v4(); 156 if (CHECK(sk == -1, "connected_socket_v4", "cannot connect\n")) 157 return; 158 159 skel = test_sockmap_update__open_and_load(); 160 if (CHECK(!skel, "open_and_load", "cannot load skeleton\n")) 161 goto close_sk; 162 163 prog = bpf_program__fd(skel->progs.copy_sock_map); 164 src = bpf_map__fd(skel->maps.src); 165 if (map_type == BPF_MAP_TYPE_SOCKMAP) 166 dst_map = skel->maps.dst_sock_map; 167 else 168 dst_map = skel->maps.dst_sock_hash; 169 170 err = bpf_map_update_elem(src, &zero, &sk, BPF_NOEXIST); 171 if (CHECK(err, "update_elem(src)", "errno=%u\n", errno)) 172 goto out; 173 174 err = bpf_prog_test_run_opts(prog, &topts); 175 if (!ASSERT_OK(err, "test_run")) 176 goto out; 177 if (!ASSERT_NEQ(topts.retval, 0, "test_run retval")) 178 goto out; 179 180 compare_cookies(skel->maps.src, dst_map); 181 182 out: 183 test_sockmap_update__destroy(skel); 184 close_sk: 185 close(sk); 186 } 187 188 static void test_sockmap_invalid_update(void) 189 { 190 struct test_sockmap_invalid_update *skel; 191 int duration = 0; 192 193 skel = test_sockmap_invalid_update__open_and_load(); 194 if (CHECK(skel, "open_and_load", "verifier accepted map_update\n")) 195 test_sockmap_invalid_update__destroy(skel); 196 } 197 198 static void test_sockmap_copy(enum bpf_map_type map_type) 199 { 200 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 201 int err, len, src_fd, iter_fd, duration = 0; 202 union bpf_iter_link_info linfo = {}; 203 __u32 i, num_sockets, num_elems; 204 struct bpf_iter_sockmap *skel; 205 __s64 *sock_fd = NULL; 206 struct bpf_link *link; 207 struct bpf_map *src; 208 char buf[64]; 209 210 skel = bpf_iter_sockmap__open_and_load(); 211 if (CHECK(!skel, "bpf_iter_sockmap__open_and_load", "skeleton open_and_load failed\n")) 212 return; 213 214 if (map_type == BPF_MAP_TYPE_SOCKMAP) { 215 src = skel->maps.sockmap; 216 num_elems = bpf_map__max_entries(src); 217 num_sockets = num_elems - 1; 218 } else { 219 src = skel->maps.sockhash; 220 num_elems = bpf_map__max_entries(src) - 1; 221 num_sockets = num_elems; 222 } 223 224 sock_fd = calloc(num_sockets, sizeof(*sock_fd)); 225 if (CHECK(!sock_fd, "calloc(sock_fd)", "failed to allocate\n")) 226 goto out; 227 228 for (i = 0; i < num_sockets; i++) 229 sock_fd[i] = -1; 230 231 src_fd = bpf_map__fd(src); 232 233 for (i = 0; i < num_sockets; i++) { 234 sock_fd[i] = connected_socket_v4(); 235 if (CHECK(sock_fd[i] == -1, "connected_socket_v4", "cannot connect\n")) 236 goto out; 237 238 err = bpf_map_update_elem(src_fd, &i, &sock_fd[i], BPF_NOEXIST); 239 if (CHECK(err, "map_update", "failed: %s\n", strerror(errno))) 240 goto out; 241 } 242 243 linfo.map.map_fd = src_fd; 244 opts.link_info = &linfo; 245 opts.link_info_len = sizeof(linfo); 246 link = bpf_program__attach_iter(skel->progs.copy, &opts); 247 if (!ASSERT_OK_PTR(link, "attach_iter")) 248 goto out; 249 250 iter_fd = bpf_iter_create(bpf_link__fd(link)); 251 if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n")) 252 goto free_link; 253 254 /* do some tests */ 255 while ((len = read(iter_fd, buf, sizeof(buf))) > 0) 256 ; 257 if (CHECK(len < 0, "read", "failed: %s\n", strerror(errno))) 258 goto close_iter; 259 260 /* test results */ 261 if (CHECK(skel->bss->elems != num_elems, "elems", "got %u expected %u\n", 262 skel->bss->elems, num_elems)) 263 goto close_iter; 264 265 if (CHECK(skel->bss->socks != num_sockets, "socks", "got %u expected %u\n", 266 skel->bss->socks, num_sockets)) 267 goto close_iter; 268 269 compare_cookies(src, skel->maps.dst); 270 271 close_iter: 272 close(iter_fd); 273 free_link: 274 bpf_link__destroy(link); 275 out: 276 for (i = 0; sock_fd && i < num_sockets; i++) 277 if (sock_fd[i] >= 0) 278 close(sock_fd[i]); 279 if (sock_fd) 280 free(sock_fd); 281 bpf_iter_sockmap__destroy(skel); 282 } 283 284 static void test_sockmap_skb_verdict_attach(enum bpf_attach_type first, 285 enum bpf_attach_type second) 286 { 287 struct test_sockmap_skb_verdict_attach *skel; 288 int err, map, verdict; 289 290 skel = test_sockmap_skb_verdict_attach__open_and_load(); 291 if (CHECK_FAIL(!skel)) { 292 perror("test_sockmap_skb_verdict_attach__open_and_load"); 293 return; 294 } 295 296 verdict = bpf_program__fd(skel->progs.prog_skb_verdict); 297 map = bpf_map__fd(skel->maps.sock_map); 298 299 err = bpf_prog_attach(verdict, map, first, 0); 300 if (CHECK_FAIL(err)) { 301 perror("bpf_prog_attach"); 302 goto out; 303 } 304 305 err = bpf_prog_attach(verdict, map, second, 0); 306 ASSERT_EQ(err, -EBUSY, "prog_attach_fail"); 307 308 err = bpf_prog_detach2(verdict, map, first); 309 if (CHECK_FAIL(err)) { 310 perror("bpf_prog_detach2"); 311 goto out; 312 } 313 out: 314 test_sockmap_skb_verdict_attach__destroy(skel); 315 } 316 317 static __u32 query_prog_id(int prog_fd) 318 { 319 struct bpf_prog_info info = {}; 320 __u32 info_len = sizeof(info); 321 int err; 322 323 err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); 324 if (!ASSERT_OK(err, "bpf_obj_get_info_by_fd") || 325 !ASSERT_EQ(info_len, sizeof(info), "bpf_obj_get_info_by_fd")) 326 return 0; 327 328 return info.id; 329 } 330 331 static void test_sockmap_progs_query(enum bpf_attach_type attach_type) 332 { 333 struct test_sockmap_progs_query *skel; 334 int err, map_fd, verdict_fd; 335 __u32 attach_flags = 0; 336 __u32 prog_ids[3] = {}; 337 __u32 prog_cnt = 3; 338 339 skel = test_sockmap_progs_query__open_and_load(); 340 if (!ASSERT_OK_PTR(skel, "test_sockmap_progs_query__open_and_load")) 341 return; 342 343 map_fd = bpf_map__fd(skel->maps.sock_map); 344 345 if (attach_type == BPF_SK_MSG_VERDICT) 346 verdict_fd = bpf_program__fd(skel->progs.prog_skmsg_verdict); 347 else 348 verdict_fd = bpf_program__fd(skel->progs.prog_skb_verdict); 349 350 err = bpf_prog_query(map_fd, attach_type, 0 /* query flags */, 351 &attach_flags, prog_ids, &prog_cnt); 352 ASSERT_OK(err, "bpf_prog_query failed"); 353 ASSERT_EQ(attach_flags, 0, "wrong attach_flags on query"); 354 ASSERT_EQ(prog_cnt, 0, "wrong program count on query"); 355 356 err = bpf_prog_attach(verdict_fd, map_fd, attach_type, 0); 357 if (!ASSERT_OK(err, "bpf_prog_attach failed")) 358 goto out; 359 360 prog_cnt = 1; 361 err = bpf_prog_query(map_fd, attach_type, 0 /* query flags */, 362 &attach_flags, prog_ids, &prog_cnt); 363 ASSERT_OK(err, "bpf_prog_query failed"); 364 ASSERT_EQ(attach_flags, 0, "wrong attach_flags on query"); 365 ASSERT_EQ(prog_cnt, 1, "wrong program count on query"); 366 ASSERT_EQ(prog_ids[0], query_prog_id(verdict_fd), 367 "wrong prog_ids on query"); 368 369 bpf_prog_detach2(verdict_fd, map_fd, attach_type); 370 out: 371 test_sockmap_progs_query__destroy(skel); 372 } 373 374 void test_sockmap_basic(void) 375 { 376 if (test__start_subtest("sockmap create_update_free")) 377 test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKMAP); 378 if (test__start_subtest("sockhash create_update_free")) 379 test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKHASH); 380 if (test__start_subtest("sockmap sk_msg load helpers")) 381 test_skmsg_helpers(BPF_MAP_TYPE_SOCKMAP); 382 if (test__start_subtest("sockhash sk_msg load helpers")) 383 test_skmsg_helpers(BPF_MAP_TYPE_SOCKHASH); 384 if (test__start_subtest("sockmap update")) 385 test_sockmap_update(BPF_MAP_TYPE_SOCKMAP); 386 if (test__start_subtest("sockhash update")) 387 test_sockmap_update(BPF_MAP_TYPE_SOCKHASH); 388 if (test__start_subtest("sockmap update in unsafe context")) 389 test_sockmap_invalid_update(); 390 if (test__start_subtest("sockmap copy")) 391 test_sockmap_copy(BPF_MAP_TYPE_SOCKMAP); 392 if (test__start_subtest("sockhash copy")) 393 test_sockmap_copy(BPF_MAP_TYPE_SOCKHASH); 394 if (test__start_subtest("sockmap skb_verdict attach")) { 395 test_sockmap_skb_verdict_attach(BPF_SK_SKB_VERDICT, 396 BPF_SK_SKB_STREAM_VERDICT); 397 test_sockmap_skb_verdict_attach(BPF_SK_SKB_STREAM_VERDICT, 398 BPF_SK_SKB_VERDICT); 399 } 400 if (test__start_subtest("sockmap msg_verdict progs query")) 401 test_sockmap_progs_query(BPF_SK_MSG_VERDICT); 402 if (test__start_subtest("sockmap stream_parser progs query")) 403 test_sockmap_progs_query(BPF_SK_SKB_STREAM_PARSER); 404 if (test__start_subtest("sockmap stream_verdict progs query")) 405 test_sockmap_progs_query(BPF_SK_SKB_STREAM_VERDICT); 406 if (test__start_subtest("sockmap skb_verdict progs query")) 407 test_sockmap_progs_query(BPF_SK_SKB_VERDICT); 408 } 409