@@ -2378,3 +2378,87 @@ void serial_test_tc_opts_chain_mixed(void)
23782378 test_tc_chain_mixed (BPF_TCX_INGRESS );
23792379 test_tc_chain_mixed (BPF_TCX_EGRESS );
23802380}
2381+
2382+ static int generate_dummy_prog (void )
2383+ {
2384+ const struct bpf_insn prog_insns [] = {
2385+ BPF_MOV64_IMM (BPF_REG_0 , 0 ),
2386+ BPF_EXIT_INSN (),
2387+ };
2388+ const size_t prog_insn_cnt = sizeof (prog_insns ) / sizeof (struct bpf_insn );
2389+ LIBBPF_OPTS (bpf_prog_load_opts , opts );
2390+ const size_t log_buf_sz = 256 ;
2391+ char * log_buf ;
2392+ int fd = -1 ;
2393+
2394+ log_buf = malloc (log_buf_sz );
2395+ if (!ASSERT_OK_PTR (log_buf , "log_buf_alloc" ))
2396+ return fd ;
2397+ opts .log_buf = log_buf ;
2398+ opts .log_size = log_buf_sz ;
2399+
2400+ log_buf [0 ] = '\0' ;
2401+ opts .log_level = 0 ;
2402+ fd = bpf_prog_load (BPF_PROG_TYPE_SCHED_CLS , "tcx_prog" , "GPL" ,
2403+ prog_insns , prog_insn_cnt , & opts );
2404+ ASSERT_STREQ (log_buf , "" , "log_0" );
2405+ ASSERT_GE (fd , 0 , "prog_fd" );
2406+ free (log_buf );
2407+ return fd ;
2408+ }
2409+
2410+ static void test_tc_opts_max_target (int target , int flags , bool relative )
2411+ {
2412+ int err , ifindex , i , prog_fd , last_fd = -1 ;
2413+ LIBBPF_OPTS (bpf_prog_attach_opts , opta );
2414+ const int max_progs = 63 ;
2415+
2416+ ASSERT_OK (system ("ip link add dev tcx_opts1 type veth peer name tcx_opts2" ), "add veth" );
2417+ ifindex = if_nametoindex ("tcx_opts1" );
2418+ ASSERT_NEQ (ifindex , 0 , "non_zero_ifindex" );
2419+
2420+ assert_mprog_count_ifindex (ifindex , target , 0 );
2421+
2422+ for (i = 0 ; i < max_progs ; i ++ ) {
2423+ prog_fd = generate_dummy_prog ();
2424+ if (!ASSERT_GE (prog_fd , 0 , "dummy_prog" ))
2425+ goto cleanup ;
2426+ err = bpf_prog_attach_opts (prog_fd , ifindex , target , & opta );
2427+ if (!ASSERT_EQ (err , 0 , "prog_attach" ))
2428+ goto cleanup ;
2429+ assert_mprog_count_ifindex (ifindex , target , i + 1 );
2430+ if (i == max_progs - 1 && relative )
2431+ last_fd = prog_fd ;
2432+ else
2433+ close (prog_fd );
2434+ }
2435+
2436+ prog_fd = generate_dummy_prog ();
2437+ if (!ASSERT_GE (prog_fd , 0 , "dummy_prog" ))
2438+ goto cleanup ;
2439+ opta .flags = flags ;
2440+ if (last_fd > 0 )
2441+ opta .relative_fd = last_fd ;
2442+ err = bpf_prog_attach_opts (prog_fd , ifindex , target , & opta );
2443+ ASSERT_EQ (err , - ERANGE , "prog_64_attach" );
2444+ assert_mprog_count_ifindex (ifindex , target , max_progs );
2445+ close (prog_fd );
2446+ cleanup :
2447+ if (last_fd > 0 )
2448+ close (last_fd );
2449+ ASSERT_OK (system ("ip link del dev tcx_opts1" ), "del veth" );
2450+ ASSERT_EQ (if_nametoindex ("tcx_opts1" ), 0 , "dev1_removed" );
2451+ ASSERT_EQ (if_nametoindex ("tcx_opts2" ), 0 , "dev2_removed" );
2452+ }
2453+
2454+ void serial_test_tc_opts_max (void )
2455+ {
2456+ test_tc_opts_max_target (BPF_TCX_INGRESS , 0 , false);
2457+ test_tc_opts_max_target (BPF_TCX_EGRESS , 0 , false);
2458+
2459+ test_tc_opts_max_target (BPF_TCX_INGRESS , BPF_F_BEFORE , false);
2460+ test_tc_opts_max_target (BPF_TCX_EGRESS , BPF_F_BEFORE , true);
2461+
2462+ test_tc_opts_max_target (BPF_TCX_INGRESS , BPF_F_AFTER , true);
2463+ test_tc_opts_max_target (BPF_TCX_EGRESS , BPF_F_AFTER , false);
2464+ }
0 commit comments