Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Apr 14-17, 2025
Register
Loading...
Note: File does not exist in v6.2.
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2023 Isovalent */
   3#include <uapi/linux/if_link.h>
   4#include <uapi/linux/pkt_sched.h>
   5#include <net/if.h>
   6#include <test_progs.h>
   7
   8#define loopback 1
   9#define ping_cmd "ping -q -c1 -w1 127.0.0.1 > /dev/null"
  10
  11#include "test_tc_link.skel.h"
  12#include "tc_helpers.h"
  13
  14void serial_test_tc_links_basic(void)
  15{
  16	LIBBPF_OPTS(bpf_prog_query_opts, optq);
  17	LIBBPF_OPTS(bpf_tcx_opts, optl);
  18	__u32 prog_ids[2], link_ids[2];
  19	__u32 pid1, pid2, lid1, lid2;
  20	struct test_tc_link *skel;
  21	struct bpf_link *link;
  22	int err;
  23
  24	skel = test_tc_link__open_and_load();
  25	if (!ASSERT_OK_PTR(skel, "skel_load"))
  26		goto cleanup;
  27
  28	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
  29	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
  30
  31	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
  32
  33	assert_mprog_count(BPF_TCX_INGRESS, 0);
  34	assert_mprog_count(BPF_TCX_EGRESS, 0);
  35
  36	ASSERT_EQ(skel->bss->seen_tc1, false, "seen_tc1");
  37	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
  38
  39	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
  40	if (!ASSERT_OK_PTR(link, "link_attach"))
  41		goto cleanup;
  42
  43	skel->links.tc1 = link;
  44
  45	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
  46
  47	assert_mprog_count(BPF_TCX_INGRESS, 1);
  48	assert_mprog_count(BPF_TCX_EGRESS, 0);
  49
  50	optq.prog_ids = prog_ids;
  51	optq.link_ids = link_ids;
  52
  53	memset(prog_ids, 0, sizeof(prog_ids));
  54	memset(link_ids, 0, sizeof(link_ids));
  55	optq.count = ARRAY_SIZE(prog_ids);
  56
  57	err = bpf_prog_query_opts(loopback, BPF_TCX_INGRESS, &optq);
  58	if (!ASSERT_OK(err, "prog_query"))
  59		goto cleanup;
  60
  61	ASSERT_EQ(optq.count, 1, "count");
  62	ASSERT_EQ(optq.revision, 2, "revision");
  63	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
  64	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
  65	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
  66	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
  67
  68	tc_skel_reset_all_seen(skel);
  69	ASSERT_OK(system(ping_cmd), ping_cmd);
  70
  71	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
  72	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
  73
  74	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
  75	if (!ASSERT_OK_PTR(link, "link_attach"))
  76		goto cleanup;
  77
  78	skel->links.tc2 = link;
  79
  80	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
  81	ASSERT_NEQ(lid1, lid2, "link_ids_1_2");
  82
  83	assert_mprog_count(BPF_TCX_INGRESS, 1);
  84	assert_mprog_count(BPF_TCX_EGRESS, 1);
  85
  86	memset(prog_ids, 0, sizeof(prog_ids));
  87	memset(link_ids, 0, sizeof(link_ids));
  88	optq.count = ARRAY_SIZE(prog_ids);
  89
  90	err = bpf_prog_query_opts(loopback, BPF_TCX_EGRESS, &optq);
  91	if (!ASSERT_OK(err, "prog_query"))
  92		goto cleanup;
  93
  94	ASSERT_EQ(optq.count, 1, "count");
  95	ASSERT_EQ(optq.revision, 2, "revision");
  96	ASSERT_EQ(optq.prog_ids[0], pid2, "prog_ids[0]");
  97	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
  98	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
  99	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
 100
 101	tc_skel_reset_all_seen(skel);
 102	ASSERT_OK(system(ping_cmd), ping_cmd);
 103
 104	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 105	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 106cleanup:
 107	test_tc_link__destroy(skel);
 108
 109	assert_mprog_count(BPF_TCX_INGRESS, 0);
 110	assert_mprog_count(BPF_TCX_EGRESS, 0);
 111}
 112
 113static void test_tc_links_before_target(int target)
 114{
 115	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 116	LIBBPF_OPTS(bpf_tcx_opts, optl);
 117	__u32 prog_ids[5], link_ids[5];
 118	__u32 pid1, pid2, pid3, pid4;
 119	__u32 lid1, lid2, lid3, lid4;
 120	struct test_tc_link *skel;
 121	struct bpf_link *link;
 122	int err;
 123
 124	skel = test_tc_link__open();
 125	if (!ASSERT_OK_PTR(skel, "skel_open"))
 126		goto cleanup;
 127
 128	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 129		  0, "tc1_attach_type");
 130	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 131		  0, "tc2_attach_type");
 132	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
 133		  0, "tc3_attach_type");
 134	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
 135		  0, "tc4_attach_type");
 136
 137	err = test_tc_link__load(skel);
 138	if (!ASSERT_OK(err, "skel_load"))
 139		goto cleanup;
 140
 141	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 142	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 143	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
 144	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
 145
 146	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 147	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
 148	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
 149
 150	assert_mprog_count(target, 0);
 151
 152	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 153	if (!ASSERT_OK_PTR(link, "link_attach"))
 154		goto cleanup;
 155
 156	skel->links.tc1 = link;
 157
 158	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
 159
 160	assert_mprog_count(target, 1);
 161
 162	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 163	if (!ASSERT_OK_PTR(link, "link_attach"))
 164		goto cleanup;
 165
 166	skel->links.tc2 = link;
 167
 168	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
 169
 170	assert_mprog_count(target, 2);
 171
 172	optq.prog_ids = prog_ids;
 173	optq.link_ids = link_ids;
 174
 175	memset(prog_ids, 0, sizeof(prog_ids));
 176	memset(link_ids, 0, sizeof(link_ids));
 177	optq.count = ARRAY_SIZE(prog_ids);
 178
 179	err = bpf_prog_query_opts(loopback, target, &optq);
 180	if (!ASSERT_OK(err, "prog_query"))
 181		goto cleanup;
 182
 183	ASSERT_EQ(optq.count, 2, "count");
 184	ASSERT_EQ(optq.revision, 3, "revision");
 185	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 186	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 187	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
 188	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
 189	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
 190	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
 191
 192	tc_skel_reset_all_seen(skel);
 193	ASSERT_OK(system(ping_cmd), ping_cmd);
 194
 195	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 196	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 197	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
 198	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
 199
 200	LIBBPF_OPTS_RESET(optl,
 201		.flags = BPF_F_BEFORE,
 202		.relative_fd = bpf_program__fd(skel->progs.tc2),
 203	);
 204
 205	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
 206	if (!ASSERT_OK_PTR(link, "link_attach"))
 207		goto cleanup;
 208
 209	skel->links.tc3 = link;
 210
 211	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
 212
 213	LIBBPF_OPTS_RESET(optl,
 214		.flags = BPF_F_BEFORE | BPF_F_LINK,
 215		.relative_id = lid1,
 216	);
 217
 218	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
 219	if (!ASSERT_OK_PTR(link, "link_attach"))
 220		goto cleanup;
 221
 222	skel->links.tc4 = link;
 223
 224	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
 225
 226	assert_mprog_count(target, 4);
 227
 228	memset(prog_ids, 0, sizeof(prog_ids));
 229	memset(link_ids, 0, sizeof(link_ids));
 230	optq.count = ARRAY_SIZE(prog_ids);
 231
 232	err = bpf_prog_query_opts(loopback, target, &optq);
 233	if (!ASSERT_OK(err, "prog_query"))
 234		goto cleanup;
 235
 236	ASSERT_EQ(optq.count, 4, "count");
 237	ASSERT_EQ(optq.revision, 5, "revision");
 238	ASSERT_EQ(optq.prog_ids[0], pid4, "prog_ids[0]");
 239	ASSERT_EQ(optq.link_ids[0], lid4, "link_ids[0]");
 240	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
 241	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
 242	ASSERT_EQ(optq.prog_ids[2], pid3, "prog_ids[2]");
 243	ASSERT_EQ(optq.link_ids[2], lid3, "link_ids[2]");
 244	ASSERT_EQ(optq.prog_ids[3], pid2, "prog_ids[3]");
 245	ASSERT_EQ(optq.link_ids[3], lid2, "link_ids[3]");
 246	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
 247	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
 248
 249	tc_skel_reset_all_seen(skel);
 250	ASSERT_OK(system(ping_cmd), ping_cmd);
 251
 252	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 253	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 254	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
 255	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
 256cleanup:
 257	test_tc_link__destroy(skel);
 258	assert_mprog_count(target, 0);
 259}
 260
 261void serial_test_tc_links_before(void)
 262{
 263	test_tc_links_before_target(BPF_TCX_INGRESS);
 264	test_tc_links_before_target(BPF_TCX_EGRESS);
 265}
 266
 267static void test_tc_links_after_target(int target)
 268{
 269	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 270	LIBBPF_OPTS(bpf_tcx_opts, optl);
 271	__u32 prog_ids[5], link_ids[5];
 272	__u32 pid1, pid2, pid3, pid4;
 273	__u32 lid1, lid2, lid3, lid4;
 274	struct test_tc_link *skel;
 275	struct bpf_link *link;
 276	int err;
 277
 278	skel = test_tc_link__open();
 279	if (!ASSERT_OK_PTR(skel, "skel_open"))
 280		goto cleanup;
 281
 282	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 283		  0, "tc1_attach_type");
 284	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 285		  0, "tc2_attach_type");
 286	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
 287		  0, "tc3_attach_type");
 288	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
 289		  0, "tc4_attach_type");
 290
 291	err = test_tc_link__load(skel);
 292	if (!ASSERT_OK(err, "skel_load"))
 293		goto cleanup;
 294
 295	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 296	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 297	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
 298	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
 299
 300	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 301	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
 302	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
 303
 304	assert_mprog_count(target, 0);
 305
 306	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 307	if (!ASSERT_OK_PTR(link, "link_attach"))
 308		goto cleanup;
 309
 310	skel->links.tc1 = link;
 311
 312	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
 313
 314	assert_mprog_count(target, 1);
 315
 316	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 317	if (!ASSERT_OK_PTR(link, "link_attach"))
 318		goto cleanup;
 319
 320	skel->links.tc2 = link;
 321
 322	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
 323
 324	assert_mprog_count(target, 2);
 325
 326	optq.prog_ids = prog_ids;
 327	optq.link_ids = link_ids;
 328
 329	memset(prog_ids, 0, sizeof(prog_ids));
 330	memset(link_ids, 0, sizeof(link_ids));
 331	optq.count = ARRAY_SIZE(prog_ids);
 332
 333	err = bpf_prog_query_opts(loopback, target, &optq);
 334	if (!ASSERT_OK(err, "prog_query"))
 335		goto cleanup;
 336
 337	ASSERT_EQ(optq.count, 2, "count");
 338	ASSERT_EQ(optq.revision, 3, "revision");
 339	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 340	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 341	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
 342	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
 343	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
 344	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
 345
 346	tc_skel_reset_all_seen(skel);
 347	ASSERT_OK(system(ping_cmd), ping_cmd);
 348
 349	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 350	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 351	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
 352	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
 353
 354	LIBBPF_OPTS_RESET(optl,
 355		.flags = BPF_F_AFTER,
 356		.relative_fd = bpf_program__fd(skel->progs.tc1),
 357	);
 358
 359	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
 360	if (!ASSERT_OK_PTR(link, "link_attach"))
 361		goto cleanup;
 362
 363	skel->links.tc3 = link;
 364
 365	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
 366
 367	LIBBPF_OPTS_RESET(optl,
 368		.flags = BPF_F_AFTER | BPF_F_LINK,
 369		.relative_fd = bpf_link__fd(skel->links.tc2),
 370	);
 371
 372	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
 373	if (!ASSERT_OK_PTR(link, "link_attach"))
 374		goto cleanup;
 375
 376	skel->links.tc4 = link;
 377
 378	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
 379
 380	assert_mprog_count(target, 4);
 381
 382	memset(prog_ids, 0, sizeof(prog_ids));
 383	memset(link_ids, 0, sizeof(link_ids));
 384	optq.count = ARRAY_SIZE(prog_ids);
 385
 386	err = bpf_prog_query_opts(loopback, target, &optq);
 387	if (!ASSERT_OK(err, "prog_query"))
 388		goto cleanup;
 389
 390	ASSERT_EQ(optq.count, 4, "count");
 391	ASSERT_EQ(optq.revision, 5, "revision");
 392	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 393	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 394	ASSERT_EQ(optq.prog_ids[1], pid3, "prog_ids[1]");
 395	ASSERT_EQ(optq.link_ids[1], lid3, "link_ids[1]");
 396	ASSERT_EQ(optq.prog_ids[2], pid2, "prog_ids[2]");
 397	ASSERT_EQ(optq.link_ids[2], lid2, "link_ids[2]");
 398	ASSERT_EQ(optq.prog_ids[3], pid4, "prog_ids[3]");
 399	ASSERT_EQ(optq.link_ids[3], lid4, "link_ids[3]");
 400	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
 401	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
 402
 403	tc_skel_reset_all_seen(skel);
 404	ASSERT_OK(system(ping_cmd), ping_cmd);
 405
 406	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 407	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 408	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
 409	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
 410cleanup:
 411	test_tc_link__destroy(skel);
 412	assert_mprog_count(target, 0);
 413}
 414
 415void serial_test_tc_links_after(void)
 416{
 417	test_tc_links_after_target(BPF_TCX_INGRESS);
 418	test_tc_links_after_target(BPF_TCX_EGRESS);
 419}
 420
 421static void test_tc_links_revision_target(int target)
 422{
 423	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 424	LIBBPF_OPTS(bpf_tcx_opts, optl);
 425	__u32 prog_ids[3], link_ids[3];
 426	__u32 pid1, pid2, lid1, lid2;
 427	struct test_tc_link *skel;
 428	struct bpf_link *link;
 429	int err;
 430
 431	skel = test_tc_link__open();
 432	if (!ASSERT_OK_PTR(skel, "skel_open"))
 433		goto cleanup;
 434
 435	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 436		  0, "tc1_attach_type");
 437	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 438		  0, "tc2_attach_type");
 439
 440	err = test_tc_link__load(skel);
 441	if (!ASSERT_OK(err, "skel_load"))
 442		goto cleanup;
 443
 444	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 445	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 446
 447	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 448
 449	assert_mprog_count(target, 0);
 450
 451	optl.expected_revision = 1;
 452
 453	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 454	if (!ASSERT_OK_PTR(link, "link_attach"))
 455		goto cleanup;
 456
 457	skel->links.tc1 = link;
 458
 459	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
 460
 461	assert_mprog_count(target, 1);
 462
 463	optl.expected_revision = 1;
 464
 465	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 466	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 467		bpf_link__destroy(link);
 468		goto cleanup;
 469	}
 470
 471	assert_mprog_count(target, 1);
 472
 473	optl.expected_revision = 2;
 474
 475	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 476	if (!ASSERT_OK_PTR(link, "link_attach"))
 477		goto cleanup;
 478
 479	skel->links.tc2 = link;
 480
 481	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
 482
 483	assert_mprog_count(target, 2);
 484
 485	optq.prog_ids = prog_ids;
 486	optq.link_ids = link_ids;
 487
 488	memset(prog_ids, 0, sizeof(prog_ids));
 489	memset(link_ids, 0, sizeof(link_ids));
 490	optq.count = ARRAY_SIZE(prog_ids);
 491
 492	err = bpf_prog_query_opts(loopback, target, &optq);
 493	if (!ASSERT_OK(err, "prog_query"))
 494		goto cleanup;
 495
 496	ASSERT_EQ(optq.count, 2, "count");
 497	ASSERT_EQ(optq.revision, 3, "revision");
 498	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 499	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 500	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
 501	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
 502	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
 503	ASSERT_EQ(optq.link_ids[2], 0, "prog_ids[2]");
 504
 505	tc_skel_reset_all_seen(skel);
 506	ASSERT_OK(system(ping_cmd), ping_cmd);
 507
 508	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 509	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 510cleanup:
 511	test_tc_link__destroy(skel);
 512	assert_mprog_count(target, 0);
 513}
 514
 515void serial_test_tc_links_revision(void)
 516{
 517	test_tc_links_revision_target(BPF_TCX_INGRESS);
 518	test_tc_links_revision_target(BPF_TCX_EGRESS);
 519}
 520
 521static void test_tc_chain_classic(int target, bool chain_tc_old)
 522{
 523	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
 524	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
 525	bool hook_created = false, tc_attached = false;
 526	LIBBPF_OPTS(bpf_tcx_opts, optl);
 527	__u32 pid1, pid2, pid3;
 528	struct test_tc_link *skel;
 529	struct bpf_link *link;
 530	int err;
 531
 532	skel = test_tc_link__open();
 533	if (!ASSERT_OK_PTR(skel, "skel_open"))
 534		goto cleanup;
 535
 536	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 537		  0, "tc1_attach_type");
 538	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 539		  0, "tc2_attach_type");
 540
 541	err = test_tc_link__load(skel);
 542	if (!ASSERT_OK(err, "skel_load"))
 543		goto cleanup;
 544
 545	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 546	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 547	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
 548
 549	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 550	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
 551
 552	assert_mprog_count(target, 0);
 553
 554	if (chain_tc_old) {
 555		tc_hook.attach_point = target == BPF_TCX_INGRESS ?
 556				       BPF_TC_INGRESS : BPF_TC_EGRESS;
 557		err = bpf_tc_hook_create(&tc_hook);
 558		if (err == 0)
 559			hook_created = true;
 560		err = err == -EEXIST ? 0 : err;
 561		if (!ASSERT_OK(err, "bpf_tc_hook_create"))
 562			goto cleanup;
 563
 564		tc_opts.prog_fd = bpf_program__fd(skel->progs.tc3);
 565		err = bpf_tc_attach(&tc_hook, &tc_opts);
 566		if (!ASSERT_OK(err, "bpf_tc_attach"))
 567			goto cleanup;
 568		tc_attached = true;
 569	}
 570
 571	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 572	if (!ASSERT_OK_PTR(link, "link_attach"))
 573		goto cleanup;
 574
 575	skel->links.tc1 = link;
 576
 577	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 578	if (!ASSERT_OK_PTR(link, "link_attach"))
 579		goto cleanup;
 580
 581	skel->links.tc2 = link;
 582
 583	assert_mprog_count(target, 2);
 584
 585	tc_skel_reset_all_seen(skel);
 586	ASSERT_OK(system(ping_cmd), ping_cmd);
 587
 588	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 589	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 590	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
 591
 592	err = bpf_link__detach(skel->links.tc2);
 593	if (!ASSERT_OK(err, "prog_detach"))
 594		goto cleanup;
 595
 596	assert_mprog_count(target, 1);
 597
 598	tc_skel_reset_all_seen(skel);
 599	ASSERT_OK(system(ping_cmd), ping_cmd);
 600
 601	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 602	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
 603	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
 604cleanup:
 605	if (tc_attached) {
 606		tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
 607		err = bpf_tc_detach(&tc_hook, &tc_opts);
 608		ASSERT_OK(err, "bpf_tc_detach");
 609	}
 610	if (hook_created) {
 611		tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
 612		bpf_tc_hook_destroy(&tc_hook);
 613	}
 614	assert_mprog_count(target, 1);
 615	test_tc_link__destroy(skel);
 616	assert_mprog_count(target, 0);
 617}
 618
 619void serial_test_tc_links_chain_classic(void)
 620{
 621	test_tc_chain_classic(BPF_TCX_INGRESS, false);
 622	test_tc_chain_classic(BPF_TCX_EGRESS, false);
 623	test_tc_chain_classic(BPF_TCX_INGRESS, true);
 624	test_tc_chain_classic(BPF_TCX_EGRESS, true);
 625}
 626
 627static void test_tc_links_replace_target(int target)
 628{
 629	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 630	LIBBPF_OPTS(bpf_tcx_opts, optl);
 631	__u32 pid1, pid2, pid3, lid1, lid2;
 632	__u32 prog_ids[4], link_ids[4];
 633	struct test_tc_link *skel;
 634	struct bpf_link *link;
 635	int err;
 636
 637	skel = test_tc_link__open();
 638	if (!ASSERT_OK_PTR(skel, "skel_open"))
 639		goto cleanup;
 640
 641	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 642		  0, "tc1_attach_type");
 643	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 644		  0, "tc2_attach_type");
 645	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
 646		  0, "tc3_attach_type");
 647
 648	err = test_tc_link__load(skel);
 649	if (!ASSERT_OK(err, "skel_load"))
 650		goto cleanup;
 651
 652	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 653	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 654	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
 655
 656	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 657	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
 658
 659	assert_mprog_count(target, 0);
 660
 661	optl.expected_revision = 1;
 662
 663	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 664	if (!ASSERT_OK_PTR(link, "link_attach"))
 665		goto cleanup;
 666
 667	skel->links.tc1 = link;
 668
 669	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
 670
 671	assert_mprog_count(target, 1);
 672
 673	LIBBPF_OPTS_RESET(optl,
 674		.flags = BPF_F_BEFORE,
 675		.relative_id = pid1,
 676		.expected_revision = 2,
 677	);
 678
 679	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
 680	if (!ASSERT_OK_PTR(link, "link_attach"))
 681		goto cleanup;
 682
 683	skel->links.tc2 = link;
 684
 685	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
 686
 687	assert_mprog_count(target, 2);
 688
 689	optq.prog_ids = prog_ids;
 690	optq.link_ids = link_ids;
 691
 692	memset(prog_ids, 0, sizeof(prog_ids));
 693	memset(link_ids, 0, sizeof(link_ids));
 694	optq.count = ARRAY_SIZE(prog_ids);
 695
 696	err = bpf_prog_query_opts(loopback, target, &optq);
 697	if (!ASSERT_OK(err, "prog_query"))
 698		goto cleanup;
 699
 700	ASSERT_EQ(optq.count, 2, "count");
 701	ASSERT_EQ(optq.revision, 3, "revision");
 702	ASSERT_EQ(optq.prog_ids[0], pid2, "prog_ids[0]");
 703	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
 704	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
 705	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
 706	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
 707	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
 708
 709	tc_skel_reset_all_seen(skel);
 710	ASSERT_OK(system(ping_cmd), ping_cmd);
 711
 712	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 713	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
 714	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
 715
 716	LIBBPF_OPTS_RESET(optl,
 717		.flags = BPF_F_REPLACE,
 718		.relative_fd = bpf_program__fd(skel->progs.tc2),
 719		.expected_revision = 3,
 720	);
 721
 722	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
 723	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 724		bpf_link__destroy(link);
 725		goto cleanup;
 726	}
 727
 728	assert_mprog_count(target, 2);
 729
 730	LIBBPF_OPTS_RESET(optl,
 731		.flags = BPF_F_REPLACE | BPF_F_LINK,
 732		.relative_fd = bpf_link__fd(skel->links.tc2),
 733		.expected_revision = 3,
 734	);
 735
 736	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
 737	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 738		bpf_link__destroy(link);
 739		goto cleanup;
 740	}
 741
 742	assert_mprog_count(target, 2);
 743
 744	LIBBPF_OPTS_RESET(optl,
 745		.flags = BPF_F_REPLACE | BPF_F_LINK | BPF_F_AFTER,
 746		.relative_id = lid2,
 747	);
 748
 749	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
 750	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 751		bpf_link__destroy(link);
 752		goto cleanup;
 753	}
 754
 755	assert_mprog_count(target, 2);
 756
 757	err = bpf_link__update_program(skel->links.tc2, skel->progs.tc3);
 758	if (!ASSERT_OK(err, "link_update"))
 759		goto cleanup;
 760
 761	assert_mprog_count(target, 2);
 762
 763	memset(prog_ids, 0, sizeof(prog_ids));
 764	memset(link_ids, 0, sizeof(link_ids));
 765	optq.count = ARRAY_SIZE(prog_ids);
 766
 767	err = bpf_prog_query_opts(loopback, target, &optq);
 768	if (!ASSERT_OK(err, "prog_query"))
 769		goto cleanup;
 770
 771	ASSERT_EQ(optq.count, 2, "count");
 772	ASSERT_EQ(optq.revision, 4, "revision");
 773	ASSERT_EQ(optq.prog_ids[0], pid3, "prog_ids[0]");
 774	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
 775	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
 776	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
 777	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
 778	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
 779
 780	tc_skel_reset_all_seen(skel);
 781	ASSERT_OK(system(ping_cmd), ping_cmd);
 782
 783	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 784	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
 785	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
 786
 787	err = bpf_link__detach(skel->links.tc2);
 788	if (!ASSERT_OK(err, "link_detach"))
 789		goto cleanup;
 790
 791	assert_mprog_count(target, 1);
 792
 793	memset(prog_ids, 0, sizeof(prog_ids));
 794	memset(link_ids, 0, sizeof(link_ids));
 795	optq.count = ARRAY_SIZE(prog_ids);
 796
 797	err = bpf_prog_query_opts(loopback, target, &optq);
 798	if (!ASSERT_OK(err, "prog_query"))
 799		goto cleanup;
 800
 801	ASSERT_EQ(optq.count, 1, "count");
 802	ASSERT_EQ(optq.revision, 5, "revision");
 803	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 804	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 805	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
 806	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
 807
 808	tc_skel_reset_all_seen(skel);
 809	ASSERT_OK(system(ping_cmd), ping_cmd);
 810
 811	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 812	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
 813	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
 814
 815	err = bpf_link__update_program(skel->links.tc1, skel->progs.tc1);
 816	if (!ASSERT_OK(err, "link_update_self"))
 817		goto cleanup;
 818
 819	assert_mprog_count(target, 1);
 820
 821	memset(prog_ids, 0, sizeof(prog_ids));
 822	memset(link_ids, 0, sizeof(link_ids));
 823	optq.count = ARRAY_SIZE(prog_ids);
 824
 825	err = bpf_prog_query_opts(loopback, target, &optq);
 826	if (!ASSERT_OK(err, "prog_query"))
 827		goto cleanup;
 828
 829	ASSERT_EQ(optq.count, 1, "count");
 830	ASSERT_EQ(optq.revision, 5, "revision");
 831	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
 832	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
 833	ASSERT_EQ(optq.prog_ids[1], 0, "prog_ids[1]");
 834	ASSERT_EQ(optq.link_ids[1], 0, "link_ids[1]");
 835
 836	tc_skel_reset_all_seen(skel);
 837	ASSERT_OK(system(ping_cmd), ping_cmd);
 838
 839	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
 840	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
 841	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
 842cleanup:
 843	test_tc_link__destroy(skel);
 844	assert_mprog_count(target, 0);
 845}
 846
 847void serial_test_tc_links_replace(void)
 848{
 849	test_tc_links_replace_target(BPF_TCX_INGRESS);
 850	test_tc_links_replace_target(BPF_TCX_EGRESS);
 851}
 852
 853static void test_tc_links_invalid_target(int target)
 854{
 855	LIBBPF_OPTS(bpf_prog_query_opts, optq);
 856	LIBBPF_OPTS(bpf_tcx_opts, optl);
 857	__u32 pid1, pid2, lid1;
 858	struct test_tc_link *skel;
 859	struct bpf_link *link;
 860	int err;
 861
 862	skel = test_tc_link__open();
 863	if (!ASSERT_OK_PTR(skel, "skel_open"))
 864		goto cleanup;
 865
 866	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
 867		  0, "tc1_attach_type");
 868	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
 869		  0, "tc2_attach_type");
 870
 871	err = test_tc_link__load(skel);
 872	if (!ASSERT_OK(err, "skel_load"))
 873		goto cleanup;
 874
 875	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
 876	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
 877
 878	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
 879
 880	assert_mprog_count(target, 0);
 881
 882	optl.flags = BPF_F_BEFORE | BPF_F_AFTER;
 883
 884	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 885	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 886		bpf_link__destroy(link);
 887		goto cleanup;
 888	}
 889
 890	assert_mprog_count(target, 0);
 891
 892	LIBBPF_OPTS_RESET(optl,
 893		.flags = BPF_F_BEFORE | BPF_F_ID,
 894	);
 895
 896	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 897	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 898		bpf_link__destroy(link);
 899		goto cleanup;
 900	}
 901
 902	assert_mprog_count(target, 0);
 903
 904	LIBBPF_OPTS_RESET(optl,
 905		.flags = BPF_F_AFTER | BPF_F_ID,
 906	);
 907
 908	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 909	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 910		bpf_link__destroy(link);
 911		goto cleanup;
 912	}
 913
 914	assert_mprog_count(target, 0);
 915
 916	LIBBPF_OPTS_RESET(optl,
 917		.flags = BPF_F_ID,
 918	);
 919
 920	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 921	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 922		bpf_link__destroy(link);
 923		goto cleanup;
 924	}
 925
 926	assert_mprog_count(target, 0);
 927
 928	LIBBPF_OPTS_RESET(optl,
 929		.flags = BPF_F_LINK,
 930		.relative_fd = bpf_program__fd(skel->progs.tc2),
 931	);
 932
 933	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 934	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 935		bpf_link__destroy(link);
 936		goto cleanup;
 937	}
 938
 939	assert_mprog_count(target, 0);
 940
 941	LIBBPF_OPTS_RESET(optl,
 942		.flags = BPF_F_LINK,
 943	);
 944
 945	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 946	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 947		bpf_link__destroy(link);
 948		goto cleanup;
 949	}
 950
 951	assert_mprog_count(target, 0);
 952
 953	LIBBPF_OPTS_RESET(optl,
 954		.relative_fd = bpf_program__fd(skel->progs.tc2),
 955	);
 956
 957	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 958	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 959		bpf_link__destroy(link);
 960		goto cleanup;
 961	}
 962
 963	assert_mprog_count(target, 0);
 964
 965	LIBBPF_OPTS_RESET(optl,
 966		.flags = BPF_F_BEFORE | BPF_F_AFTER,
 967		.relative_fd = bpf_program__fd(skel->progs.tc2),
 968	);
 969
 970	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 971	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 972		bpf_link__destroy(link);
 973		goto cleanup;
 974	}
 975
 976	assert_mprog_count(target, 0);
 977
 978	LIBBPF_OPTS_RESET(optl,
 979		.flags = BPF_F_BEFORE,
 980		.relative_fd = bpf_program__fd(skel->progs.tc1),
 981	);
 982
 983	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 984	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 985		bpf_link__destroy(link);
 986		goto cleanup;
 987	}
 988
 989	assert_mprog_count(target, 0);
 990
 991	LIBBPF_OPTS_RESET(optl,
 992		.flags = BPF_F_ID,
 993		.relative_id = pid2,
 994	);
 995
 996	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
 997	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
 998		bpf_link__destroy(link);
 999		goto cleanup;
1000	}
1001
1002	assert_mprog_count(target, 0);
1003
1004	LIBBPF_OPTS_RESET(optl,
1005		.flags = BPF_F_ID,
1006		.relative_id = 42,
1007	);
1008
1009	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1010	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1011		bpf_link__destroy(link);
1012		goto cleanup;
1013	}
1014
1015	assert_mprog_count(target, 0);
1016
1017	LIBBPF_OPTS_RESET(optl,
1018		.flags = BPF_F_BEFORE,
1019		.relative_fd = bpf_program__fd(skel->progs.tc1),
1020	);
1021
1022	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1023	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1024		bpf_link__destroy(link);
1025		goto cleanup;
1026	}
1027
1028	assert_mprog_count(target, 0);
1029
1030	LIBBPF_OPTS_RESET(optl,
1031		.flags = BPF_F_BEFORE | BPF_F_LINK,
1032		.relative_fd = bpf_program__fd(skel->progs.tc1),
1033	);
1034
1035	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1036	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1037		bpf_link__destroy(link);
1038		goto cleanup;
1039	}
1040
1041	assert_mprog_count(target, 0);
1042
1043	LIBBPF_OPTS_RESET(optl,
1044		.flags = BPF_F_AFTER,
1045		.relative_fd = bpf_program__fd(skel->progs.tc1),
1046	);
1047
1048	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1049	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1050		bpf_link__destroy(link);
1051		goto cleanup;
1052	}
1053
1054	assert_mprog_count(target, 0);
1055
1056	LIBBPF_OPTS_RESET(optl);
1057
1058	link = bpf_program__attach_tcx(skel->progs.tc1, 0, &optl);
1059	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1060		bpf_link__destroy(link);
1061		goto cleanup;
1062	}
1063
1064	assert_mprog_count(target, 0);
1065
1066	LIBBPF_OPTS_RESET(optl,
1067		.flags = BPF_F_AFTER | BPF_F_LINK,
1068		.relative_fd = bpf_program__fd(skel->progs.tc1),
1069	);
1070
1071	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1072	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1073		bpf_link__destroy(link);
1074		goto cleanup;
1075	}
1076
1077	assert_mprog_count(target, 0);
1078
1079	LIBBPF_OPTS_RESET(optl);
1080
1081	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1082	if (!ASSERT_OK_PTR(link, "link_attach"))
1083		goto cleanup;
1084
1085	skel->links.tc1 = link;
1086
1087	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1088
1089	assert_mprog_count(target, 1);
1090
1091	LIBBPF_OPTS_RESET(optl,
1092		.flags = BPF_F_AFTER | BPF_F_LINK,
1093		.relative_fd = bpf_program__fd(skel->progs.tc1),
1094	);
1095
1096	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1097	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1098		bpf_link__destroy(link);
1099		goto cleanup;
1100	}
1101
1102	assert_mprog_count(target, 1);
1103
1104	LIBBPF_OPTS_RESET(optl,
1105		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1106		.relative_id = ~0,
1107	);
1108
1109	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1110	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1111		bpf_link__destroy(link);
1112		goto cleanup;
1113	}
1114
1115	assert_mprog_count(target, 1);
1116
1117	LIBBPF_OPTS_RESET(optl,
1118		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1119		.relative_id = lid1,
1120	);
1121
1122	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1123	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1124		bpf_link__destroy(link);
1125		goto cleanup;
1126	}
1127
1128	assert_mprog_count(target, 1);
1129
1130	LIBBPF_OPTS_RESET(optl,
1131		.flags = BPF_F_BEFORE | BPF_F_ID,
1132		.relative_id = pid1,
1133	);
1134
1135	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1136	if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
1137		bpf_link__destroy(link);
1138		goto cleanup;
1139	}
1140	assert_mprog_count(target, 1);
1141
1142	LIBBPF_OPTS_RESET(optl,
1143		.flags = BPF_F_BEFORE | BPF_F_LINK | BPF_F_ID,
1144		.relative_id = lid1,
1145	);
1146
1147	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1148	if (!ASSERT_OK_PTR(link, "link_attach"))
1149		goto cleanup;
1150
1151	skel->links.tc2 = link;
1152
1153	assert_mprog_count(target, 2);
1154cleanup:
1155	test_tc_link__destroy(skel);
1156	assert_mprog_count(target, 0);
1157}
1158
1159void serial_test_tc_links_invalid(void)
1160{
1161	test_tc_links_invalid_target(BPF_TCX_INGRESS);
1162	test_tc_links_invalid_target(BPF_TCX_EGRESS);
1163}
1164
1165static void test_tc_links_prepend_target(int target)
1166{
1167	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1168	LIBBPF_OPTS(bpf_tcx_opts, optl);
1169	__u32 prog_ids[5], link_ids[5];
1170	__u32 pid1, pid2, pid3, pid4;
1171	__u32 lid1, lid2, lid3, lid4;
1172	struct test_tc_link *skel;
1173	struct bpf_link *link;
1174	int err;
1175
1176	skel = test_tc_link__open();
1177	if (!ASSERT_OK_PTR(skel, "skel_open"))
1178		goto cleanup;
1179
1180	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1181		  0, "tc1_attach_type");
1182	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1183		  0, "tc2_attach_type");
1184	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1185		  0, "tc3_attach_type");
1186	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1187		  0, "tc4_attach_type");
1188
1189	err = test_tc_link__load(skel);
1190	if (!ASSERT_OK(err, "skel_load"))
1191		goto cleanup;
1192
1193	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1194	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1195	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1196	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1197
1198	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1199	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1200	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1201
1202	assert_mprog_count(target, 0);
1203
1204	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1205	if (!ASSERT_OK_PTR(link, "link_attach"))
1206		goto cleanup;
1207
1208	skel->links.tc1 = link;
1209
1210	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1211
1212	assert_mprog_count(target, 1);
1213
1214	LIBBPF_OPTS_RESET(optl,
1215		.flags = BPF_F_BEFORE,
1216	);
1217
1218	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1219	if (!ASSERT_OK_PTR(link, "link_attach"))
1220		goto cleanup;
1221
1222	skel->links.tc2 = link;
1223
1224	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1225
1226	assert_mprog_count(target, 2);
1227
1228	optq.prog_ids = prog_ids;
1229	optq.link_ids = link_ids;
1230
1231	memset(prog_ids, 0, sizeof(prog_ids));
1232	memset(link_ids, 0, sizeof(link_ids));
1233	optq.count = ARRAY_SIZE(prog_ids);
1234
1235	err = bpf_prog_query_opts(loopback, target, &optq);
1236	if (!ASSERT_OK(err, "prog_query"))
1237		goto cleanup;
1238
1239	ASSERT_EQ(optq.count, 2, "count");
1240	ASSERT_EQ(optq.revision, 3, "revision");
1241	ASSERT_EQ(optq.prog_ids[0], pid2, "prog_ids[0]");
1242	ASSERT_EQ(optq.link_ids[0], lid2, "link_ids[0]");
1243	ASSERT_EQ(optq.prog_ids[1], pid1, "prog_ids[1]");
1244	ASSERT_EQ(optq.link_ids[1], lid1, "link_ids[1]");
1245	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1246	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
1247
1248	tc_skel_reset_all_seen(skel);
1249	ASSERT_OK(system(ping_cmd), ping_cmd);
1250
1251	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1252	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1253	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1254	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1255
1256	LIBBPF_OPTS_RESET(optl,
1257		.flags = BPF_F_BEFORE,
1258	);
1259
1260	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
1261	if (!ASSERT_OK_PTR(link, "link_attach"))
1262		goto cleanup;
1263
1264	skel->links.tc3 = link;
1265
1266	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
1267
1268	LIBBPF_OPTS_RESET(optl,
1269		.flags = BPF_F_BEFORE,
1270	);
1271
1272	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1273	if (!ASSERT_OK_PTR(link, "link_attach"))
1274		goto cleanup;
1275
1276	skel->links.tc4 = link;
1277
1278	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1279
1280	assert_mprog_count(target, 4);
1281
1282	memset(prog_ids, 0, sizeof(prog_ids));
1283	memset(link_ids, 0, sizeof(link_ids));
1284	optq.count = ARRAY_SIZE(prog_ids);
1285
1286	err = bpf_prog_query_opts(loopback, target, &optq);
1287	if (!ASSERT_OK(err, "prog_query"))
1288		goto cleanup;
1289
1290	ASSERT_EQ(optq.count, 4, "count");
1291	ASSERT_EQ(optq.revision, 5, "revision");
1292	ASSERT_EQ(optq.prog_ids[0], pid4, "prog_ids[0]");
1293	ASSERT_EQ(optq.link_ids[0], lid4, "link_ids[0]");
1294	ASSERT_EQ(optq.prog_ids[1], pid3, "prog_ids[1]");
1295	ASSERT_EQ(optq.link_ids[1], lid3, "link_ids[1]");
1296	ASSERT_EQ(optq.prog_ids[2], pid2, "prog_ids[2]");
1297	ASSERT_EQ(optq.link_ids[2], lid2, "link_ids[2]");
1298	ASSERT_EQ(optq.prog_ids[3], pid1, "prog_ids[3]");
1299	ASSERT_EQ(optq.link_ids[3], lid1, "link_ids[3]");
1300	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1301	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
1302
1303	tc_skel_reset_all_seen(skel);
1304	ASSERT_OK(system(ping_cmd), ping_cmd);
1305
1306	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1307	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1308	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1309	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1310cleanup:
1311	test_tc_link__destroy(skel);
1312	assert_mprog_count(target, 0);
1313}
1314
1315void serial_test_tc_links_prepend(void)
1316{
1317	test_tc_links_prepend_target(BPF_TCX_INGRESS);
1318	test_tc_links_prepend_target(BPF_TCX_EGRESS);
1319}
1320
1321static void test_tc_links_append_target(int target)
1322{
1323	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1324	LIBBPF_OPTS(bpf_tcx_opts, optl);
1325	__u32 prog_ids[5], link_ids[5];
1326	__u32 pid1, pid2, pid3, pid4;
1327	__u32 lid1, lid2, lid3, lid4;
1328	struct test_tc_link *skel;
1329	struct bpf_link *link;
1330	int err;
1331
1332	skel = test_tc_link__open();
1333	if (!ASSERT_OK_PTR(skel, "skel_open"))
1334		goto cleanup;
1335
1336	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1337		  0, "tc1_attach_type");
1338	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1339		  0, "tc2_attach_type");
1340	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1341		  0, "tc3_attach_type");
1342	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1343		  0, "tc4_attach_type");
1344
1345	err = test_tc_link__load(skel);
1346	if (!ASSERT_OK(err, "skel_load"))
1347		goto cleanup;
1348
1349	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1350	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1351	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1352	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1353
1354	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1355	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1356	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1357
1358	assert_mprog_count(target, 0);
1359
1360	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1361	if (!ASSERT_OK_PTR(link, "link_attach"))
1362		goto cleanup;
1363
1364	skel->links.tc1 = link;
1365
1366	lid1 = id_from_link_fd(bpf_link__fd(skel->links.tc1));
1367
1368	assert_mprog_count(target, 1);
1369
1370	LIBBPF_OPTS_RESET(optl,
1371		.flags = BPF_F_AFTER,
1372	);
1373
1374	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1375	if (!ASSERT_OK_PTR(link, "link_attach"))
1376		goto cleanup;
1377
1378	skel->links.tc2 = link;
1379
1380	lid2 = id_from_link_fd(bpf_link__fd(skel->links.tc2));
1381
1382	assert_mprog_count(target, 2);
1383
1384	optq.prog_ids = prog_ids;
1385	optq.link_ids = link_ids;
1386
1387	memset(prog_ids, 0, sizeof(prog_ids));
1388	memset(link_ids, 0, sizeof(link_ids));
1389	optq.count = ARRAY_SIZE(prog_ids);
1390
1391	err = bpf_prog_query_opts(loopback, target, &optq);
1392	if (!ASSERT_OK(err, "prog_query"))
1393		goto cleanup;
1394
1395	ASSERT_EQ(optq.count, 2, "count");
1396	ASSERT_EQ(optq.revision, 3, "revision");
1397	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
1398	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
1399	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1400	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1401	ASSERT_EQ(optq.prog_ids[2], 0, "prog_ids[2]");
1402	ASSERT_EQ(optq.link_ids[2], 0, "link_ids[2]");
1403
1404	tc_skel_reset_all_seen(skel);
1405	ASSERT_OK(system(ping_cmd), ping_cmd);
1406
1407	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1408	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1409	ASSERT_EQ(skel->bss->seen_tc3, false, "seen_tc3");
1410	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1411
1412	LIBBPF_OPTS_RESET(optl,
1413		.flags = BPF_F_AFTER,
1414	);
1415
1416	link = bpf_program__attach_tcx(skel->progs.tc3, loopback, &optl);
1417	if (!ASSERT_OK_PTR(link, "link_attach"))
1418		goto cleanup;
1419
1420	skel->links.tc3 = link;
1421
1422	lid3 = id_from_link_fd(bpf_link__fd(skel->links.tc3));
1423
1424	LIBBPF_OPTS_RESET(optl,
1425		.flags = BPF_F_AFTER,
1426	);
1427
1428	link = bpf_program__attach_tcx(skel->progs.tc4, loopback, &optl);
1429	if (!ASSERT_OK_PTR(link, "link_attach"))
1430		goto cleanup;
1431
1432	skel->links.tc4 = link;
1433
1434	lid4 = id_from_link_fd(bpf_link__fd(skel->links.tc4));
1435
1436	assert_mprog_count(target, 4);
1437
1438	memset(prog_ids, 0, sizeof(prog_ids));
1439	memset(link_ids, 0, sizeof(link_ids));
1440	optq.count = ARRAY_SIZE(prog_ids);
1441
1442	err = bpf_prog_query_opts(loopback, target, &optq);
1443	if (!ASSERT_OK(err, "prog_query"))
1444		goto cleanup;
1445
1446	ASSERT_EQ(optq.count, 4, "count");
1447	ASSERT_EQ(optq.revision, 5, "revision");
1448	ASSERT_EQ(optq.prog_ids[0], pid1, "prog_ids[0]");
1449	ASSERT_EQ(optq.link_ids[0], lid1, "link_ids[0]");
1450	ASSERT_EQ(optq.prog_ids[1], pid2, "prog_ids[1]");
1451	ASSERT_EQ(optq.link_ids[1], lid2, "link_ids[1]");
1452	ASSERT_EQ(optq.prog_ids[2], pid3, "prog_ids[2]");
1453	ASSERT_EQ(optq.link_ids[2], lid3, "link_ids[2]");
1454	ASSERT_EQ(optq.prog_ids[3], pid4, "prog_ids[3]");
1455	ASSERT_EQ(optq.link_ids[3], lid4, "link_ids[3]");
1456	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
1457	ASSERT_EQ(optq.link_ids[4], 0, "link_ids[4]");
1458
1459	tc_skel_reset_all_seen(skel);
1460	ASSERT_OK(system(ping_cmd), ping_cmd);
1461
1462	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1463	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1464	ASSERT_EQ(skel->bss->seen_tc3, true, "seen_tc3");
1465	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1466cleanup:
1467	test_tc_link__destroy(skel);
1468	assert_mprog_count(target, 0);
1469}
1470
1471void serial_test_tc_links_append(void)
1472{
1473	test_tc_links_append_target(BPF_TCX_INGRESS);
1474	test_tc_links_append_target(BPF_TCX_EGRESS);
1475}
1476
1477static void test_tc_links_dev_cleanup_target(int target)
1478{
1479	LIBBPF_OPTS(bpf_tcx_opts, optl);
1480	LIBBPF_OPTS(bpf_prog_query_opts, optq);
1481	__u32 pid1, pid2, pid3, pid4;
1482	struct test_tc_link *skel;
1483	struct bpf_link *link;
1484	int err, ifindex;
1485
1486	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
1487	ifindex = if_nametoindex("tcx_opts1");
1488	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
1489
1490	skel = test_tc_link__open();
1491	if (!ASSERT_OK_PTR(skel, "skel_open"))
1492		goto cleanup;
1493
1494	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1495		  0, "tc1_attach_type");
1496	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1497		  0, "tc2_attach_type");
1498	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1499		  0, "tc3_attach_type");
1500	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1501		  0, "tc4_attach_type");
1502
1503	err = test_tc_link__load(skel);
1504	if (!ASSERT_OK(err, "skel_load"))
1505		goto cleanup;
1506
1507	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1508	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1509	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1510	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1511
1512	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1513	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1514	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1515
1516	assert_mprog_count(target, 0);
1517
1518	link = bpf_program__attach_tcx(skel->progs.tc1, ifindex, &optl);
1519	if (!ASSERT_OK_PTR(link, "link_attach"))
1520		goto cleanup;
1521
1522	skel->links.tc1 = link;
1523
1524	assert_mprog_count_ifindex(ifindex, target, 1);
1525
1526	link = bpf_program__attach_tcx(skel->progs.tc2, ifindex, &optl);
1527	if (!ASSERT_OK_PTR(link, "link_attach"))
1528		goto cleanup;
1529
1530	skel->links.tc2 = link;
1531
1532	assert_mprog_count_ifindex(ifindex, target, 2);
1533
1534	link = bpf_program__attach_tcx(skel->progs.tc3, ifindex, &optl);
1535	if (!ASSERT_OK_PTR(link, "link_attach"))
1536		goto cleanup;
1537
1538	skel->links.tc3 = link;
1539
1540	assert_mprog_count_ifindex(ifindex, target, 3);
1541
1542	link = bpf_program__attach_tcx(skel->progs.tc4, ifindex, &optl);
1543	if (!ASSERT_OK_PTR(link, "link_attach"))
1544		goto cleanup;
1545
1546	skel->links.tc4 = link;
1547
1548	assert_mprog_count_ifindex(ifindex, target, 4);
1549
1550	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1551	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1552	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1553
1554	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc1)), 0, "tc1_ifindex");
1555	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc2)), 0, "tc2_ifindex");
1556	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc3)), 0, "tc3_ifindex");
1557	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc4)), 0, "tc4_ifindex");
1558
1559	test_tc_link__destroy(skel);
1560	return;
1561cleanup:
1562	test_tc_link__destroy(skel);
1563
1564	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1565	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1566	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1567}
1568
1569void serial_test_tc_links_dev_cleanup(void)
1570{
1571	test_tc_links_dev_cleanup_target(BPF_TCX_INGRESS);
1572	test_tc_links_dev_cleanup_target(BPF_TCX_EGRESS);
1573}
1574
1575static void test_tc_chain_mixed(int target)
1576{
1577	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
1578	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = loopback);
1579	LIBBPF_OPTS(bpf_tcx_opts, optl);
1580	struct test_tc_link *skel;
1581	struct bpf_link *link;
1582	__u32 pid1, pid2, pid3;
1583	int err;
1584
1585	skel = test_tc_link__open();
1586	if (!ASSERT_OK_PTR(skel, "skel_open"))
1587		goto cleanup;
1588
1589	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1590		  0, "tc4_attach_type");
1591	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc5, target),
1592		  0, "tc5_attach_type");
1593	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc6, target),
1594		  0, "tc6_attach_type");
1595
1596	err = test_tc_link__load(skel);
1597	if (!ASSERT_OK(err, "skel_load"))
1598		goto cleanup;
1599
1600	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1601	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc5));
1602	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc6));
1603
1604	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1605	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1606
1607	assert_mprog_count(target, 0);
1608
1609	tc_hook.attach_point = target == BPF_TCX_INGRESS ?
1610			       BPF_TC_INGRESS : BPF_TC_EGRESS;
1611	err = bpf_tc_hook_create(&tc_hook);
1612	err = err == -EEXIST ? 0 : err;
1613	if (!ASSERT_OK(err, "bpf_tc_hook_create"))
1614		goto cleanup;
1615
1616	tc_opts.prog_fd = bpf_program__fd(skel->progs.tc5);
1617	err = bpf_tc_attach(&tc_hook, &tc_opts);
1618	if (!ASSERT_OK(err, "bpf_tc_attach"))
1619		goto cleanup;
1620
1621	link = bpf_program__attach_tcx(skel->progs.tc6, loopback, &optl);
1622	if (!ASSERT_OK_PTR(link, "link_attach"))
1623		goto cleanup;
1624
1625	skel->links.tc6 = link;
1626
1627	assert_mprog_count(target, 1);
1628
1629	tc_skel_reset_all_seen(skel);
1630	ASSERT_OK(system(ping_cmd), ping_cmd);
1631
1632	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1633	ASSERT_EQ(skel->bss->seen_tc5, false, "seen_tc5");
1634	ASSERT_EQ(skel->bss->seen_tc6, true, "seen_tc6");
1635
1636	err = bpf_link__update_program(skel->links.tc6, skel->progs.tc4);
1637	if (!ASSERT_OK(err, "link_update"))
1638		goto cleanup;
1639
1640	assert_mprog_count(target, 1);
1641
1642	tc_skel_reset_all_seen(skel);
1643	ASSERT_OK(system(ping_cmd), ping_cmd);
1644
1645	ASSERT_EQ(skel->bss->seen_tc4, true, "seen_tc4");
1646	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
1647	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
1648
1649	err = bpf_link__detach(skel->links.tc6);
1650	if (!ASSERT_OK(err, "prog_detach"))
1651		goto cleanup;
1652
1653	assert_mprog_count(target, 0);
1654
1655	tc_skel_reset_all_seen(skel);
1656	ASSERT_OK(system(ping_cmd), ping_cmd);
1657
1658	ASSERT_EQ(skel->bss->seen_tc4, false, "seen_tc4");
1659	ASSERT_EQ(skel->bss->seen_tc5, true, "seen_tc5");
1660	ASSERT_EQ(skel->bss->seen_tc6, false, "seen_tc6");
1661
1662cleanup:
1663	tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
1664	err = bpf_tc_detach(&tc_hook, &tc_opts);
1665	ASSERT_OK(err, "bpf_tc_detach");
1666
1667	tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
1668	bpf_tc_hook_destroy(&tc_hook);
1669
1670	test_tc_link__destroy(skel);
1671}
1672
1673void serial_test_tc_links_chain_mixed(void)
1674{
1675	test_tc_chain_mixed(BPF_TCX_INGRESS);
1676	test_tc_chain_mixed(BPF_TCX_EGRESS);
1677}
1678
1679static void test_tc_links_ingress(int target, bool chain_tc_old,
1680				  bool tcx_teardown_first)
1681{
1682	LIBBPF_OPTS(bpf_tc_opts, tc_opts,
1683		.handle		= 1,
1684		.priority	= 1,
1685	);
1686	LIBBPF_OPTS(bpf_tc_hook, tc_hook,
1687		.ifindex	= loopback,
1688		.attach_point	= BPF_TC_CUSTOM,
1689		.parent		= TC_H_INGRESS,
1690	);
1691	bool hook_created = false, tc_attached = false;
1692	LIBBPF_OPTS(bpf_tcx_opts, optl);
1693	__u32 pid1, pid2, pid3;
1694	struct test_tc_link *skel;
1695	struct bpf_link *link;
1696	int err;
1697
1698	skel = test_tc_link__open();
1699	if (!ASSERT_OK_PTR(skel, "skel_open"))
1700		goto cleanup;
1701
1702	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1703		  0, "tc1_attach_type");
1704	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1705		  0, "tc2_attach_type");
1706
1707	err = test_tc_link__load(skel);
1708	if (!ASSERT_OK(err, "skel_load"))
1709		goto cleanup;
1710
1711	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1712	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1713	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1714
1715	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1716	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1717
1718	assert_mprog_count(target, 0);
1719
1720	if (chain_tc_old) {
1721		ASSERT_OK(system("tc qdisc add dev lo ingress"), "add_ingress");
1722		hook_created = true;
1723
1724		tc_opts.prog_fd = bpf_program__fd(skel->progs.tc3);
1725		err = bpf_tc_attach(&tc_hook, &tc_opts);
1726		if (!ASSERT_OK(err, "bpf_tc_attach"))
1727			goto cleanup;
1728		tc_attached = true;
1729	}
1730
1731	link = bpf_program__attach_tcx(skel->progs.tc1, loopback, &optl);
1732	if (!ASSERT_OK_PTR(link, "link_attach"))
1733		goto cleanup;
1734
1735	skel->links.tc1 = link;
1736
1737	link = bpf_program__attach_tcx(skel->progs.tc2, loopback, &optl);
1738	if (!ASSERT_OK_PTR(link, "link_attach"))
1739		goto cleanup;
1740
1741	skel->links.tc2 = link;
1742
1743	assert_mprog_count(target, 2);
1744
1745	tc_skel_reset_all_seen(skel);
1746	ASSERT_OK(system(ping_cmd), ping_cmd);
1747
1748	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1749	ASSERT_EQ(skel->bss->seen_tc2, true, "seen_tc2");
1750	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
1751
1752	err = bpf_link__detach(skel->links.tc2);
1753	if (!ASSERT_OK(err, "prog_detach"))
1754		goto cleanup;
1755
1756	assert_mprog_count(target, 1);
1757
1758	tc_skel_reset_all_seen(skel);
1759	ASSERT_OK(system(ping_cmd), ping_cmd);
1760
1761	ASSERT_EQ(skel->bss->seen_tc1, true, "seen_tc1");
1762	ASSERT_EQ(skel->bss->seen_tc2, false, "seen_tc2");
1763	ASSERT_EQ(skel->bss->seen_tc3, chain_tc_old, "seen_tc3");
1764cleanup:
1765	if (tc_attached) {
1766		tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
1767		err = bpf_tc_detach(&tc_hook, &tc_opts);
1768		ASSERT_OK(err, "bpf_tc_detach");
1769	}
1770	ASSERT_OK(system(ping_cmd), ping_cmd);
1771	assert_mprog_count(target, 1);
1772	if (hook_created && tcx_teardown_first)
1773		ASSERT_OK(system("tc qdisc del dev lo ingress"), "del_ingress");
1774	ASSERT_OK(system(ping_cmd), ping_cmd);
1775	test_tc_link__destroy(skel);
1776	ASSERT_OK(system(ping_cmd), ping_cmd);
1777	if (hook_created && !tcx_teardown_first)
1778		ASSERT_OK(system("tc qdisc del dev lo ingress"), "del_ingress");
1779	ASSERT_OK(system(ping_cmd), ping_cmd);
1780	assert_mprog_count(target, 0);
1781}
1782
1783void serial_test_tc_links_ingress(void)
1784{
1785	test_tc_links_ingress(BPF_TCX_INGRESS, true, true);
1786	test_tc_links_ingress(BPF_TCX_INGRESS, true, false);
1787	test_tc_links_ingress(BPF_TCX_INGRESS, false, false);
1788}
1789
1790static void test_tc_links_dev_mixed(int target)
1791{
1792	LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
1793	LIBBPF_OPTS(bpf_tc_hook, tc_hook);
1794	LIBBPF_OPTS(bpf_tcx_opts, optl);
1795	__u32 pid1, pid2, pid3, pid4;
1796	struct test_tc_link *skel;
1797	struct bpf_link *link;
1798	int err, ifindex;
1799
1800	ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth");
1801	ifindex = if_nametoindex("tcx_opts1");
1802	ASSERT_NEQ(ifindex, 0, "non_zero_ifindex");
1803
1804	skel = test_tc_link__open();
1805	if (!ASSERT_OK_PTR(skel, "skel_open"))
1806		goto cleanup;
1807
1808	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc1, target),
1809		  0, "tc1_attach_type");
1810	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc2, target),
1811		  0, "tc2_attach_type");
1812	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc3, target),
1813		  0, "tc3_attach_type");
1814	ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc4, target),
1815		  0, "tc4_attach_type");
1816
1817	err = test_tc_link__load(skel);
1818	if (!ASSERT_OK(err, "skel_load"))
1819		goto cleanup;
1820
1821	pid1 = id_from_prog_fd(bpf_program__fd(skel->progs.tc1));
1822	pid2 = id_from_prog_fd(bpf_program__fd(skel->progs.tc2));
1823	pid3 = id_from_prog_fd(bpf_program__fd(skel->progs.tc3));
1824	pid4 = id_from_prog_fd(bpf_program__fd(skel->progs.tc4));
1825
1826	ASSERT_NEQ(pid1, pid2, "prog_ids_1_2");
1827	ASSERT_NEQ(pid3, pid4, "prog_ids_3_4");
1828	ASSERT_NEQ(pid2, pid3, "prog_ids_2_3");
1829
1830	assert_mprog_count(target, 0);
1831
1832	link = bpf_program__attach_tcx(skel->progs.tc1, ifindex, &optl);
1833	if (!ASSERT_OK_PTR(link, "link_attach"))
1834		goto cleanup;
1835
1836	skel->links.tc1 = link;
1837
1838	assert_mprog_count_ifindex(ifindex, target, 1);
1839
1840	link = bpf_program__attach_tcx(skel->progs.tc2, ifindex, &optl);
1841	if (!ASSERT_OK_PTR(link, "link_attach"))
1842		goto cleanup;
1843
1844	skel->links.tc2 = link;
1845
1846	assert_mprog_count_ifindex(ifindex, target, 2);
1847
1848	link = bpf_program__attach_tcx(skel->progs.tc3, ifindex, &optl);
1849	if (!ASSERT_OK_PTR(link, "link_attach"))
1850		goto cleanup;
1851
1852	skel->links.tc3 = link;
1853
1854	assert_mprog_count_ifindex(ifindex, target, 3);
1855
1856	link = bpf_program__attach_tcx(skel->progs.tc4, ifindex, &optl);
1857	if (!ASSERT_OK_PTR(link, "link_attach"))
1858		goto cleanup;
1859
1860	skel->links.tc4 = link;
1861
1862	assert_mprog_count_ifindex(ifindex, target, 4);
1863
1864	tc_hook.ifindex = ifindex;
1865	tc_hook.attach_point = target == BPF_TCX_INGRESS ?
1866			       BPF_TC_INGRESS : BPF_TC_EGRESS;
1867
1868	err = bpf_tc_hook_create(&tc_hook);
1869	err = err == -EEXIST ? 0 : err;
1870	if (!ASSERT_OK(err, "bpf_tc_hook_create"))
1871		goto cleanup;
1872
1873	tc_opts.prog_fd = bpf_program__fd(skel->progs.tc5);
1874	err = bpf_tc_attach(&tc_hook, &tc_opts);
1875	if (!ASSERT_OK(err, "bpf_tc_attach"))
1876		goto cleanup;
1877
1878	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1879	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1880	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1881
1882	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc1)), 0, "tc1_ifindex");
1883	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc2)), 0, "tc2_ifindex");
1884	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc3)), 0, "tc3_ifindex");
1885	ASSERT_EQ(ifindex_from_link_fd(bpf_link__fd(skel->links.tc4)), 0, "tc4_ifindex");
1886
1887	test_tc_link__destroy(skel);
1888	return;
1889cleanup:
1890	test_tc_link__destroy(skel);
1891
1892	ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth");
1893	ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed");
1894	ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
1895}
1896
1897void serial_test_tc_links_dev_mixed(void)
1898{
1899	test_tc_links_dev_mixed(BPF_TCX_INGRESS);
1900	test_tc_links_dev_mixed(BPF_TCX_EGRESS);
1901}