Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: add ipv4 and dual ipv4/ipv6 support in btf_skc_cls_ingress

btf_skc_cls_ingress test currently checks that syncookie and
bpf_sk_assign/release helpers behave correctly in multiple scenarios,
but only with ipv6 socket.

Increase those helpers coverage by adding testing support for IPv4
sockets and IPv4/IPv6 sockets. The rework is mostly based on features
brought earlier in test_tcp_check_syncookie.sh to cover some fixes
performed on those helpers, but test_tcp_check_syncookie.sh is not
integrated in test_progs. The most notable changes linked to this are:
- some rework in the corresponding eBPF program to support both types of
traffic
- the switch from start_server to start_server_str to allow to check
some socket options
- the introduction of new subtests for ipv4 and ipv4/ipv6

Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
Link: https://lore.kernel.org/r/20241020-syncookie-v2-4-2db240225fed@bootlin.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>

authored by

Alexis Lothoré (eBPF Foundation) and committed by
Martin KaFai Lau
8a5cd986 0da0a75c

+158 -39
+105 -12
tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c
··· 19 19 20 20 #define TEST_NS "skc_cls_ingress" 21 21 22 + #define BIT(n) (1 << (n)) 23 + #define TEST_MODE_IPV4 BIT(0) 24 + #define TEST_MODE_IPV6 BIT(1) 25 + #define TEST_MODE_DUAL (TEST_MODE_IPV4 | TEST_MODE_IPV6) 26 + 27 + #define SERVER_ADDR_IPV4 "127.0.0.1" 28 + #define SERVER_ADDR_IPV6 "::1" 29 + #define SERVER_ADDR_DUAL "::0" 30 + 22 31 static struct netns_obj *prepare_netns(struct test_btf_skc_cls_ingress *skel) 23 32 { 24 33 LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS); ··· 64 55 65 56 static void reset_test(struct test_btf_skc_cls_ingress *skel) 66 57 { 58 + memset(&skel->bss->srv_sa4, 0, sizeof(skel->bss->srv_sa4)); 67 59 memset(&skel->bss->srv_sa6, 0, sizeof(skel->bss->srv_sa6)); 68 60 skel->bss->listen_tp_sport = 0; 69 61 skel->bss->req_sk_sport = 0; ··· 79 69 printf("bpf prog error at line %u\n", skel->bss->linum); 80 70 } 81 71 82 - static void run_test(struct test_btf_skc_cls_ingress *skel, bool gen_cookies) 72 + static int v6only_true(int fd, void *opts) 73 + { 74 + int mode = true; 75 + 76 + return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 77 + } 78 + 79 + static int v6only_false(int fd, void *opts) 80 + { 81 + int mode = false; 82 + 83 + return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 84 + } 85 + 86 + static void run_test(struct test_btf_skc_cls_ingress *skel, bool gen_cookies, 87 + int ip_mode) 83 88 { 84 89 const char *tcp_syncookies = gen_cookies ? "2" : "1"; 85 90 int listen_fd = -1, cli_fd = -1, srv_fd = -1, err; 91 + struct network_helper_opts opts = { 0 }; 92 + struct sockaddr_storage *addr; 86 93 struct sockaddr_in6 srv_sa6; 87 - socklen_t addrlen = sizeof(srv_sa6); 94 + struct sockaddr_in srv_sa4; 95 + socklen_t addr_len; 96 + int sock_family; 97 + char *srv_addr; 88 98 int srv_port; 99 + 100 + switch (ip_mode) { 101 + case TEST_MODE_IPV4: 102 + sock_family = AF_INET; 103 + srv_addr = SERVER_ADDR_IPV4; 104 + addr = (struct sockaddr_storage *)&srv_sa4; 105 + addr_len = sizeof(srv_sa4); 106 + break; 107 + case TEST_MODE_IPV6: 108 + opts.post_socket_cb = v6only_true; 109 + sock_family = AF_INET6; 110 + srv_addr = SERVER_ADDR_IPV6; 111 + addr = (struct sockaddr_storage *)&srv_sa6; 112 + addr_len = sizeof(srv_sa6); 113 + break; 114 + case TEST_MODE_DUAL: 115 + opts.post_socket_cb = v6only_false; 116 + sock_family = AF_INET6; 117 + srv_addr = SERVER_ADDR_DUAL; 118 + addr = (struct sockaddr_storage *)&srv_sa6; 119 + addr_len = sizeof(srv_sa6); 120 + break; 121 + default: 122 + PRINT_FAIL("Unknown IP mode %d", ip_mode); 123 + return; 124 + } 89 125 90 126 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", tcp_syncookies)) 91 127 return; 92 128 93 - listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 129 + listen_fd = start_server_str(sock_family, SOCK_STREAM, srv_addr, 0, 130 + &opts); 94 131 if (!ASSERT_OK_FD(listen_fd, "start server")) 95 132 return; 96 133 97 - err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen); 134 + err = getsockname(listen_fd, (struct sockaddr *)addr, &addr_len); 98 135 if (!ASSERT_OK(err, "getsockname(listen_fd)")) 99 136 goto done; 100 - memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); 101 - srv_port = ntohs(srv_sa6.sin6_port); 137 + 138 + switch (ip_mode) { 139 + case TEST_MODE_IPV4: 140 + memcpy(&skel->bss->srv_sa4, &srv_sa4, sizeof(srv_sa4)); 141 + srv_port = ntohs(srv_sa4.sin_port); 142 + break; 143 + case TEST_MODE_IPV6: 144 + case TEST_MODE_DUAL: 145 + memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); 146 + srv_port = ntohs(srv_sa6.sin6_port); 147 + break; 148 + default: 149 + goto done; 150 + } 102 151 103 152 cli_fd = connect_to_fd(listen_fd, 0); 104 153 if (!ASSERT_OK_FD(cli_fd, "connect client")) ··· 194 125 close(srv_fd); 195 126 } 196 127 197 - static void test_conn(struct test_btf_skc_cls_ingress *skel) 128 + static void test_conn_ipv4(struct test_btf_skc_cls_ingress *skel) 198 129 { 199 - run_test(skel, false); 130 + run_test(skel, false, TEST_MODE_IPV4); 200 131 } 201 132 202 - static void test_syncookie(struct test_btf_skc_cls_ingress *skel) 133 + static void test_conn_ipv6(struct test_btf_skc_cls_ingress *skel) 203 134 { 204 - run_test(skel, true); 135 + run_test(skel, false, TEST_MODE_IPV6); 136 + } 137 + 138 + static void test_conn_dual(struct test_btf_skc_cls_ingress *skel) 139 + { 140 + run_test(skel, false, TEST_MODE_DUAL); 141 + } 142 + 143 + static void test_syncookie_ipv4(struct test_btf_skc_cls_ingress *skel) 144 + { 145 + run_test(skel, true, TEST_MODE_IPV4); 146 + } 147 + 148 + static void test_syncookie_ipv6(struct test_btf_skc_cls_ingress *skel) 149 + { 150 + run_test(skel, true, TEST_MODE_IPV6); 151 + } 152 + 153 + static void test_syncookie_dual(struct test_btf_skc_cls_ingress *skel) 154 + { 155 + run_test(skel, true, TEST_MODE_DUAL); 205 156 } 206 157 207 158 struct test { ··· 231 142 232 143 #define DEF_TEST(name) { #name, test_##name } 233 144 static struct test tests[] = { 234 - DEF_TEST(conn), 235 - DEF_TEST(syncookie), 145 + DEF_TEST(conn_ipv4), 146 + DEF_TEST(conn_ipv6), 147 + DEF_TEST(conn_dual), 148 + DEF_TEST(syncookie_ipv4), 149 + DEF_TEST(syncookie_ipv6), 150 + DEF_TEST(syncookie_dual), 236 151 }; 237 152 238 153 void test_btf_skc_cls_ingress(void)
+53 -27
tools/testing/selftests/bpf/progs/test_btf_skc_cls_ingress.c
··· 10 10 #endif 11 11 12 12 struct sockaddr_in6 srv_sa6 = {}; 13 + struct sockaddr_in srv_sa4 = {}; 13 14 __u16 listen_tp_sport = 0; 14 15 __u16 req_sk_sport = 0; 15 16 __u32 recv_cookie = 0; ··· 19 18 20 19 #define LOG() ({ if (!linum) linum = __LINE__; }) 21 20 22 - static void test_syncookie_helper(struct ipv6hdr *ip6h, struct tcphdr *th, 23 - struct tcp_sock *tp, 21 + static void test_syncookie_helper(void *iphdr, int iphdr_size, 22 + struct tcphdr *th, struct tcp_sock *tp, 24 23 struct __sk_buff *skb) 25 24 { 26 25 if (th->syn) { ··· 39 38 return; 40 39 } 41 40 42 - mss_cookie = bpf_tcp_gen_syncookie(tp, ip6h, sizeof(*ip6h), 41 + mss_cookie = bpf_tcp_gen_syncookie(tp, iphdr, iphdr_size, 43 42 th, 40); 44 43 if (mss_cookie < 0) { 45 44 if (mss_cookie != -ENOENT) ··· 49 48 } 50 49 } else if (gen_cookie) { 51 50 /* It was in cookie mode */ 52 - int ret = bpf_tcp_check_syncookie(tp, ip6h, sizeof(*ip6h), 51 + int ret = bpf_tcp_check_syncookie(tp, iphdr, iphdr_size, 53 52 th, sizeof(*th)); 54 53 55 54 if (ret < 0) { ··· 61 60 } 62 61 } 63 62 64 - static int handle_ip6_tcp(struct ipv6hdr *ip6h, struct __sk_buff *skb) 63 + static int handle_ip_tcp(struct ethhdr *eth, struct __sk_buff *skb) 65 64 { 66 - struct bpf_sock_tuple *tuple; 65 + struct bpf_sock_tuple *tuple = NULL; 66 + unsigned int tuple_len = 0; 67 67 struct bpf_sock *bpf_skc; 68 - unsigned int tuple_len; 68 + void *data_end, *iphdr; 69 + struct ipv6hdr *ip6h; 70 + struct iphdr *ip4h; 69 71 struct tcphdr *th; 70 - void *data_end; 72 + int iphdr_size; 71 73 72 74 data_end = (void *)(long)(skb->data_end); 73 75 74 - th = (struct tcphdr *)(ip6h + 1); 75 - if (th + 1 > data_end) 76 + switch (eth->h_proto) { 77 + case bpf_htons(ETH_P_IP): 78 + ip4h = (struct iphdr *)(eth + 1); 79 + if (ip4h + 1 > data_end) 80 + return TC_ACT_OK; 81 + if (ip4h->protocol != IPPROTO_TCP) 82 + return TC_ACT_OK; 83 + th = (struct tcphdr *)(ip4h + 1); 84 + if (th + 1 > data_end) 85 + return TC_ACT_OK; 86 + /* Is it the testing traffic? */ 87 + if (th->dest != srv_sa4.sin_port) 88 + return TC_ACT_OK; 89 + tuple_len = sizeof(tuple->ipv4); 90 + tuple = (struct bpf_sock_tuple *)&ip4h->saddr; 91 + iphdr = ip4h; 92 + iphdr_size = sizeof(*ip4h); 93 + break; 94 + case bpf_htons(ETH_P_IPV6): 95 + ip6h = (struct ipv6hdr *)(eth + 1); 96 + if (ip6h + 1 > data_end) 97 + return TC_ACT_OK; 98 + if (ip6h->nexthdr != IPPROTO_TCP) 99 + return TC_ACT_OK; 100 + th = (struct tcphdr *)(ip6h + 1); 101 + if (th + 1 > data_end) 102 + return TC_ACT_OK; 103 + /* Is it the testing traffic? */ 104 + if (th->dest != srv_sa6.sin6_port) 105 + return TC_ACT_OK; 106 + tuple_len = sizeof(tuple->ipv6); 107 + tuple = (struct bpf_sock_tuple *)&ip6h->saddr; 108 + iphdr = ip6h; 109 + iphdr_size = sizeof(*ip6h); 110 + break; 111 + default: 76 112 return TC_ACT_OK; 113 + } 77 114 78 - /* Is it the testing traffic? */ 79 - if (th->dest != srv_sa6.sin6_port) 80 - return TC_ACT_OK; 81 - 82 - tuple_len = sizeof(tuple->ipv6); 83 - tuple = (struct bpf_sock_tuple *)&ip6h->saddr; 84 115 if ((void *)tuple + tuple_len > data_end) { 85 116 LOG(); 86 117 return TC_ACT_OK; ··· 159 126 160 127 listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num; 161 128 162 - test_syncookie_helper(ip6h, th, tp, skb); 129 + test_syncookie_helper(iphdr, iphdr_size, th, tp, skb); 163 130 bpf_sk_release(tp); 164 131 return TC_ACT_OK; 165 132 } ··· 175 142 SEC("tc") 176 143 int cls_ingress(struct __sk_buff *skb) 177 144 { 178 - struct ipv6hdr *ip6h; 179 145 struct ethhdr *eth; 180 146 void *data_end; 181 147 ··· 184 152 if (eth + 1 > data_end) 185 153 return TC_ACT_OK; 186 154 187 - if (eth->h_proto != bpf_htons(ETH_P_IPV6)) 155 + if (eth->h_proto != bpf_htons(ETH_P_IP) && 156 + eth->h_proto != bpf_htons(ETH_P_IPV6)) 188 157 return TC_ACT_OK; 189 158 190 - ip6h = (struct ipv6hdr *)(eth + 1); 191 - if (ip6h + 1 > data_end) 192 - return TC_ACT_OK; 193 - 194 - if (ip6h->nexthdr == IPPROTO_TCP) 195 - return handle_ip6_tcp(ip6h, skb); 196 - 197 - return TC_ACT_OK; 159 + return handle_ip_tcp(eth, skb); 198 160 } 199 161 200 162 char _license[] SEC("license") = "GPL";