1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2020 Facebook */ 3 #include <test_progs.h> 4 #include "bpf_iter_ipv6_route.skel.h" 5 #include "bpf_iter_netlink.skel.h" 6 #include "bpf_iter_bpf_map.skel.h" 7 #include "bpf_iter_task.skel.h" 8 #include "bpf_iter_task_stack.skel.h" 9 #include "bpf_iter_task_file.skel.h" 10 #include "bpf_iter_tcp4.skel.h" 11 #include "bpf_iter_tcp6.skel.h" 12 #include "bpf_iter_udp4.skel.h" 13 #include "bpf_iter_udp6.skel.h" 14 #include "bpf_iter_test_kern1.skel.h" 15 #include "bpf_iter_test_kern2.skel.h" 16 #include "bpf_iter_test_kern3.skel.h" 17 #include "bpf_iter_test_kern4.skel.h" 18 19 static int duration; 20 21 static void test_btf_id_or_null(void) 22 { 23 struct bpf_iter_test_kern3 *skel; 24 25 skel = bpf_iter_test_kern3__open_and_load(); 26 if (CHECK(skel, "bpf_iter_test_kern3__open_and_load", 27 "skeleton open_and_load unexpectedly succeeded\n")) { 28 bpf_iter_test_kern3__destroy(skel); 29 return; 30 } 31 } 32 33 static void do_dummy_read(struct bpf_program *prog) 34 { 35 struct bpf_link *link; 36 char buf[16] = {}; 37 int iter_fd, len; 38 39 link = bpf_program__attach_iter(prog, NULL); 40 if (CHECK(IS_ERR(link), "attach_iter", "attach_iter failed\n")) 41 return; 42 43 iter_fd = bpf_iter_create(bpf_link__fd(link)); 44 if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n")) 45 goto free_link; 46 47 /* not check contents, but ensure read() ends without error */ 48 while ((len = read(iter_fd, buf, sizeof(buf))) > 0) 49 ; 50 CHECK(len < 0, "read", "read failed: %s\n", strerror(errno)); 51 52 close(iter_fd); 53 54 free_link: 55 bpf_link__destroy(link); 56 } 57 58 static void test_ipv6_route(void) 59 { 60 struct bpf_iter_ipv6_route *skel; 61 62 skel = bpf_iter_ipv6_route__open_and_load(); 63 if (CHECK(!skel, "bpf_iter_ipv6_route__open_and_load", 64 "skeleton open_and_load failed\n")) 65 return; 66 67 do_dummy_read(skel->progs.dump_ipv6_route); 68 69 bpf_iter_ipv6_route__destroy(skel); 70 } 71 72 static void test_netlink(void) 73 { 74 struct bpf_iter_netlink *skel; 75 76 skel = bpf_iter_netlink__open_and_load(); 77 if (CHECK(!skel, "bpf_iter_netlink__open_and_load", 78 "skeleton open_and_load failed\n")) 79 return; 80 81 do_dummy_read(skel->progs.dump_netlink); 82 83 bpf_iter_netlink__destroy(skel); 84 } 85 86 static void test_bpf_map(void) 87 { 88 struct bpf_iter_bpf_map *skel; 89 90 skel = bpf_iter_bpf_map__open_and_load(); 91 if (CHECK(!skel, "bpf_iter_bpf_map__open_and_load", 92 "skeleton open_and_load failed\n")) 93 return; 94 95 do_dummy_read(skel->progs.dump_bpf_map); 96 97 bpf_iter_bpf_map__destroy(skel); 98 } 99 100 static void test_task(void) 101 { 102 struct bpf_iter_task *skel; 103 104 skel = bpf_iter_task__open_and_load(); 105 if (CHECK(!skel, "bpf_iter_task__open_and_load", 106 "skeleton open_and_load failed\n")) 107 return; 108 109 do_dummy_read(skel->progs.dump_task); 110 111 bpf_iter_task__destroy(skel); 112 } 113 114 static void test_task_stack(void) 115 { 116 struct bpf_iter_task_stack *skel; 117 118 skel = bpf_iter_task_stack__open_and_load(); 119 if (CHECK(!skel, "bpf_iter_task_stack__open_and_load", 120 "skeleton open_and_load failed\n")) 121 return; 122 123 do_dummy_read(skel->progs.dump_task_stack); 124 125 bpf_iter_task_stack__destroy(skel); 126 } 127 128 static void test_task_file(void) 129 { 130 struct bpf_iter_task_file *skel; 131 132 skel = bpf_iter_task_file__open_and_load(); 133 if (CHECK(!skel, "bpf_iter_task_file__open_and_load", 134 "skeleton open_and_load failed\n")) 135 return; 136 137 do_dummy_read(skel->progs.dump_task_file); 138 139 bpf_iter_task_file__destroy(skel); 140 } 141 142 static void test_tcp4(void) 143 { 144 struct bpf_iter_tcp4 *skel; 145 146 skel = bpf_iter_tcp4__open_and_load(); 147 if (CHECK(!skel, "bpf_iter_tcp4__open_and_load", 148 "skeleton open_and_load failed\n")) 149 return; 150 151 do_dummy_read(skel->progs.dump_tcp4); 152 153 bpf_iter_tcp4__destroy(skel); 154 } 155 156 static void test_tcp6(void) 157 { 158 struct bpf_iter_tcp6 *skel; 159 160 skel = bpf_iter_tcp6__open_and_load(); 161 if (CHECK(!skel, "bpf_iter_tcp6__open_and_load", 162 "skeleton open_and_load failed\n")) 163 return; 164 165 do_dummy_read(skel->progs.dump_tcp6); 166 167 bpf_iter_tcp6__destroy(skel); 168 } 169 170 static void test_udp4(void) 171 { 172 struct bpf_iter_udp4 *skel; 173 174 skel = bpf_iter_udp4__open_and_load(); 175 if (CHECK(!skel, "bpf_iter_udp4__open_and_load", 176 "skeleton open_and_load failed\n")) 177 return; 178 179 do_dummy_read(skel->progs.dump_udp4); 180 181 bpf_iter_udp4__destroy(skel); 182 } 183 184 static void test_udp6(void) 185 { 186 struct bpf_iter_udp6 *skel; 187 188 skel = bpf_iter_udp6__open_and_load(); 189 if (CHECK(!skel, "bpf_iter_udp6__open_and_load", 190 "skeleton open_and_load failed\n")) 191 return; 192 193 do_dummy_read(skel->progs.dump_udp6); 194 195 bpf_iter_udp6__destroy(skel); 196 } 197 198 /* The expected string is less than 16 bytes */ 199 static int do_read_with_fd(int iter_fd, const char *expected, 200 bool read_one_char) 201 { 202 int err = -1, len, read_buf_len, start; 203 char buf[16] = {}; 204 205 read_buf_len = read_one_char ? 1 : 16; 206 start = 0; 207 while ((len = read(iter_fd, buf + start, read_buf_len)) > 0) { 208 start += len; 209 if (CHECK(start >= 16, "read", "read len %d\n", len)) 210 return -1; 211 read_buf_len = read_one_char ? 1 : 16 - start; 212 } 213 if (CHECK(len < 0, "read", "read failed: %s\n", strerror(errno))) 214 return -1; 215 216 err = strcmp(buf, expected); 217 if (CHECK(err, "read", "incorrect read result: buf %s, expected %s\n", 218 buf, expected)) 219 return -1; 220 221 return 0; 222 } 223 224 static void test_anon_iter(bool read_one_char) 225 { 226 struct bpf_iter_test_kern1 *skel; 227 struct bpf_link *link; 228 int iter_fd, err; 229 230 skel = bpf_iter_test_kern1__open_and_load(); 231 if (CHECK(!skel, "bpf_iter_test_kern1__open_and_load", 232 "skeleton open_and_load failed\n")) 233 return; 234 235 err = bpf_iter_test_kern1__attach(skel); 236 if (CHECK(err, "bpf_iter_test_kern1__attach", 237 "skeleton attach failed\n")) { 238 goto out; 239 } 240 241 link = skel->links.dump_task; 242 iter_fd = bpf_iter_create(bpf_link__fd(link)); 243 if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n")) 244 goto out; 245 246 do_read_with_fd(iter_fd, "abcd", read_one_char); 247 close(iter_fd); 248 249 out: 250 bpf_iter_test_kern1__destroy(skel); 251 } 252 253 static int do_read(const char *path, const char *expected) 254 { 255 int err, iter_fd; 256 257 iter_fd = open(path, O_RDONLY); 258 if (CHECK(iter_fd < 0, "open", "open %s failed: %s\n", 259 path, strerror(errno))) 260 return -1; 261 262 err = do_read_with_fd(iter_fd, expected, false); 263 close(iter_fd); 264 return err; 265 } 266 267 static void test_file_iter(void) 268 { 269 const char *path = "/sys/fs/bpf/bpf_iter_test1"; 270 struct bpf_iter_test_kern1 *skel1; 271 struct bpf_iter_test_kern2 *skel2; 272 struct bpf_link *link; 273 int err; 274 275 skel1 = bpf_iter_test_kern1__open_and_load(); 276 if (CHECK(!skel1, "bpf_iter_test_kern1__open_and_load", 277 "skeleton open_and_load failed\n")) 278 return; 279 280 link = bpf_program__attach_iter(skel1->progs.dump_task, NULL); 281 if (CHECK(IS_ERR(link), "attach_iter", "attach_iter failed\n")) 282 goto out; 283 284 /* unlink this path if it exists. */ 285 unlink(path); 286 287 err = bpf_link__pin(link, path); 288 if (CHECK(err, "pin_iter", "pin_iter to %s failed: %d\n", path, err)) 289 goto free_link; 290 291 err = do_read(path, "abcd"); 292 if (err) 293 goto unlink_path; 294 295 /* file based iterator seems working fine. Let us a link update 296 * of the underlying link and `cat` the iterator again, its content 297 * should change. 298 */ 299 skel2 = bpf_iter_test_kern2__open_and_load(); 300 if (CHECK(!skel2, "bpf_iter_test_kern2__open_and_load", 301 "skeleton open_and_load failed\n")) 302 goto unlink_path; 303 304 err = bpf_link__update_program(link, skel2->progs.dump_task); 305 if (CHECK(err, "update_prog", "update_prog failed\n")) 306 goto destroy_skel2; 307 308 do_read(path, "ABCD"); 309 310 destroy_skel2: 311 bpf_iter_test_kern2__destroy(skel2); 312 unlink_path: 313 unlink(path); 314 free_link: 315 bpf_link__destroy(link); 316 out: 317 bpf_iter_test_kern1__destroy(skel1); 318 } 319 320 static void test_overflow(bool test_e2big_overflow, bool ret1) 321 { 322 __u32 map_info_len, total_read_len, expected_read_len; 323 int err, iter_fd, map1_fd, map2_fd, len; 324 struct bpf_map_info map_info = {}; 325 struct bpf_iter_test_kern4 *skel; 326 struct bpf_link *link; 327 __u32 page_size; 328 char *buf; 329 330 skel = bpf_iter_test_kern4__open(); 331 if (CHECK(!skel, "bpf_iter_test_kern4__open", 332 "skeleton open failed\n")) 333 return; 334 335 /* create two maps: bpf program will only do bpf_seq_write 336 * for these two maps. The goal is one map output almost 337 * fills seq_file buffer and then the other will trigger 338 * overflow and needs restart. 339 */ 340 map1_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); 341 if (CHECK(map1_fd < 0, "bpf_create_map", 342 "map_creation failed: %s\n", strerror(errno))) 343 goto out; 344 map2_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); 345 if (CHECK(map2_fd < 0, "bpf_create_map", 346 "map_creation failed: %s\n", strerror(errno))) 347 goto free_map1; 348 349 /* bpf_seq_printf kernel buffer is one page, so one map 350 * bpf_seq_write will mostly fill it, and the other map 351 * will partially fill and then trigger overflow and need 352 * bpf_seq_read restart. 353 */ 354 page_size = sysconf(_SC_PAGE_SIZE); 355 356 if (test_e2big_overflow) { 357 skel->rodata->print_len = (page_size + 8) / 8; 358 expected_read_len = 2 * (page_size + 8); 359 } else if (!ret1) { 360 skel->rodata->print_len = (page_size - 8) / 8; 361 expected_read_len = 2 * (page_size - 8); 362 } else { 363 skel->rodata->print_len = 1; 364 expected_read_len = 2 * 8; 365 } 366 skel->rodata->ret1 = ret1; 367 368 if (CHECK(bpf_iter_test_kern4__load(skel), 369 "bpf_iter_test_kern4__load", "skeleton load failed\n")) 370 goto free_map2; 371 372 /* setup filtering map_id in bpf program */ 373 map_info_len = sizeof(map_info); 374 err = bpf_obj_get_info_by_fd(map1_fd, &map_info, &map_info_len); 375 if (CHECK(err, "get_map_info", "get map info failed: %s\n", 376 strerror(errno))) 377 goto free_map2; 378 skel->bss->map1_id = map_info.id; 379 380 err = bpf_obj_get_info_by_fd(map2_fd, &map_info, &map_info_len); 381 if (CHECK(err, "get_map_info", "get map info failed: %s\n", 382 strerror(errno))) 383 goto free_map2; 384 skel->bss->map2_id = map_info.id; 385 386 link = bpf_program__attach_iter(skel->progs.dump_bpf_map, NULL); 387 if (CHECK(IS_ERR(link), "attach_iter", "attach_iter failed\n")) 388 goto free_map2; 389 390 iter_fd = bpf_iter_create(bpf_link__fd(link)); 391 if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n")) 392 goto free_link; 393 394 buf = malloc(expected_read_len); 395 if (!buf) 396 goto close_iter; 397 398 /* do read */ 399 total_read_len = 0; 400 if (test_e2big_overflow) { 401 while ((len = read(iter_fd, buf, expected_read_len)) > 0) 402 total_read_len += len; 403 404 CHECK(len != -1 || errno != E2BIG, "read", 405 "expected ret -1, errno E2BIG, but get ret %d, error %s\n", 406 len, strerror(errno)); 407 goto free_buf; 408 } else if (!ret1) { 409 while ((len = read(iter_fd, buf, expected_read_len)) > 0) 410 total_read_len += len; 411 412 if (CHECK(len < 0, "read", "read failed: %s\n", 413 strerror(errno))) 414 goto free_buf; 415 } else { 416 do { 417 len = read(iter_fd, buf, expected_read_len); 418 if (len > 0) 419 total_read_len += len; 420 } while (len > 0 || len == -EAGAIN); 421 422 if (CHECK(len < 0, "read", "read failed: %s\n", 423 strerror(errno))) 424 goto free_buf; 425 } 426 427 if (CHECK(total_read_len != expected_read_len, "read", 428 "total len %u, expected len %u\n", total_read_len, 429 expected_read_len)) 430 goto free_buf; 431 432 if (CHECK(skel->bss->map1_accessed != 1, "map1_accessed", 433 "expected 1 actual %d\n", skel->bss->map1_accessed)) 434 goto free_buf; 435 436 if (CHECK(skel->bss->map2_accessed != 2, "map2_accessed", 437 "expected 2 actual %d\n", skel->bss->map2_accessed)) 438 goto free_buf; 439 440 CHECK(skel->bss->map2_seqnum1 != skel->bss->map2_seqnum2, 441 "map2_seqnum", "two different seqnum %lld %lld\n", 442 skel->bss->map2_seqnum1, skel->bss->map2_seqnum2); 443 444 free_buf: 445 free(buf); 446 close_iter: 447 close(iter_fd); 448 free_link: 449 bpf_link__destroy(link); 450 free_map2: 451 close(map2_fd); 452 free_map1: 453 close(map1_fd); 454 out: 455 bpf_iter_test_kern4__destroy(skel); 456 } 457 458 void test_bpf_iter(void) 459 { 460 if (test__start_subtest("btf_id_or_null")) 461 test_btf_id_or_null(); 462 if (test__start_subtest("ipv6_route")) 463 test_ipv6_route(); 464 if (test__start_subtest("netlink")) 465 test_netlink(); 466 if (test__start_subtest("bpf_map")) 467 test_bpf_map(); 468 if (test__start_subtest("task")) 469 test_task(); 470 if (test__start_subtest("task_stack")) 471 test_task_stack(); 472 if (test__start_subtest("task_file")) 473 test_task_file(); 474 if (test__start_subtest("tcp4")) 475 test_tcp4(); 476 if (test__start_subtest("tcp6")) 477 test_tcp6(); 478 if (test__start_subtest("udp4")) 479 test_udp4(); 480 if (test__start_subtest("udp6")) 481 test_udp6(); 482 if (test__start_subtest("anon")) 483 test_anon_iter(false); 484 if (test__start_subtest("anon-read-one-char")) 485 test_anon_iter(true); 486 if (test__start_subtest("file")) 487 test_file_iter(); 488 if (test__start_subtest("overflow")) 489 test_overflow(false, false); 490 if (test__start_subtest("overflow-e2big")) 491 test_overflow(true, false); 492 if (test__start_subtest("prog-ret-1")) 493 test_overflow(false, true); 494 } 495