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

Merge branch 'selftests/bpf: integrate test_tcp_check_syncookie.sh into test_progs'

Alexis Lothoré (eBPF Foundation) says:

====================
this series aims to bring test_tcp_check_syncookie.sh scope into
test_progs to make sure that the corresponding tests are also run
automatically in CI. This script tests for bpf_tcp_{gen,check}_syncookie
and bpf_skc_lookup_tcp, in different contexts (ipv4, v6 or dual, and
with tc and xdp programs).
Some other tests like btf_skc_cls_ingress have some overlapping tests with
test_tcp_check_syncookie.sh, so this series moves the missing bits from
test_tcp_check_syncookie.sh into btf_skc_cls_ingress, which is already
integrated into test_progs.
- the first three commits bring some minor improvements to
btf_skc_cls_ingress without changing its testing scope
- fourth and fifth commits bring test_tcp_check_syncookie.sh features
into btf_skc_cls_ingress
- last commit removes test_tcp_check_syncookie.sh

The only topic for which I am not sure for this integration is the
necessity or not to run the tests with different program types:
test_tcp_check_syncookie.sh runs tests with both tc and xdp programs, but
btf_skc_cls_ingress currently tests those helpers only with a tc
program. Would it make sense to also make sure that btf_skc_cls_ingress
is tested with all the programs types supported by those helpers ?

The series has been tested both in CI and in a local x86_64 qemu
environment:
# ./test_progs -a btf_skc_cls_ingress
#38/1 btf_skc_cls_ingress/conn_ipv4:OK
#38/2 btf_skc_cls_ingress/conn_ipv6:OK
#38/3 btf_skc_cls_ingress/conn_dual:OK
#38/4 btf_skc_cls_ingress/syncookie_ipv4:OK
#38/5 btf_skc_cls_ingress/syncookie_ipv6:OK
#38/6 btf_skc_cls_ingress/syncookie_dual:OK
#38 btf_skc_cls_ingress:OK
Summary: 1/6 PASSED, 0 SKIPPED, 0 FAILED

---
Changes in v2:
- fix initial test author mail in Cc
- Fix default cases in switches: indent, action
- remove unneeded initializer
- remove duplicate interface bring-up
- remove unnecessary check and return in bpf program
- Link to v1: https://lore.kernel.org/r/20241016-syncookie-v1-0-3b7a0de12153@bootlin.com
====================

Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>

+217 -604
-1
tools/testing/selftests/bpf/.gitignore
··· 24 24 flow_dissector_load 25 25 test_tcpnotify_user 26 26 test_libbpf 27 - test_tcp_check_syncookie_user 28 27 test_sysctl 29 28 xdping 30 29 test_cpp
+3 -6
tools/testing/selftests/bpf/Makefile
··· 137 137 test_xdp_vlan_mode_generic.sh \ 138 138 test_xdp_vlan_mode_native.sh \ 139 139 test_lwt_ip_encap.sh \ 140 - test_tcp_check_syncookie.sh \ 141 140 test_tc_tunnel.sh \ 142 141 test_tc_edt.sh \ 143 142 test_xdping.sh \ ··· 153 154 154 155 # Compile but not part of 'make run_tests' 155 156 TEST_GEN_PROGS_EXTENDED = \ 156 - flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \ 157 - test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \ 158 - xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \ 159 - xdp_features bpf_test_no_cfi.ko 157 + flow_dissector_load test_flow_dissector test_lirc_mode2_user xdping \ 158 + test_cpp runqslower bench bpf_testmod.ko xskxceiver xdp_redirect_multi \ 159 + xdp_synproxy veristat xdp_hw_metadata xdp_features bpf_test_no_cfi.ko 160 160 161 161 TEST_GEN_FILES += liburandom_read.so urandom_read sign-file uprobe_multi 162 162 ··· 345 347 $(OUTPUT)/test_maps: $(TESTING_HELPERS) 346 348 $(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS) 347 349 $(OUTPUT)/xsk.o: $(BPFOBJ) 348 - $(OUTPUT)/test_tcp_check_syncookie_user: $(NETWORK_HELPERS) 349 350 350 351 BPFTOOL ?= $(DEFAULT_BPFTOOL) 351 352 $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \
+159 -105
tools/testing/selftests/bpf/prog_tests/btf_skc_cls_ingress.c
··· 17 17 #include "test_progs.h" 18 18 #include "test_btf_skc_cls_ingress.skel.h" 19 19 20 - static struct test_btf_skc_cls_ingress *skel; 21 - static struct sockaddr_in6 srv_sa6; 22 - static __u32 duration; 20 + #define TEST_NS "skc_cls_ingress" 23 21 24 - static int prepare_netns(void) 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 + /* RFC791, 576 for minimal IPv4 datagram, minus 40 bytes of TCP header */ 31 + #define MIN_IPV4_MSS 536 32 + 33 + static struct netns_obj *prepare_netns(struct test_btf_skc_cls_ingress *skel) 25 34 { 26 35 LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS); 27 36 LIBBPF_OPTS(bpf_tc_opts, tc_attach, 28 37 .prog_fd = bpf_program__fd(skel->progs.cls_ingress)); 38 + struct netns_obj *ns = NULL; 29 39 30 - if (CHECK(unshare(CLONE_NEWNET), "create netns", 31 - "unshare(CLONE_NEWNET): %s (%d)", 32 - strerror(errno), errno)) 33 - return -1; 34 - 35 - if (CHECK(system("ip link set dev lo up"), 36 - "ip link set dev lo up", "failed\n")) 37 - return -1; 40 + ns = netns_new(TEST_NS, true); 41 + if (!ASSERT_OK_PTR(ns, "create and join netns")) 42 + return ns; 38 43 39 44 qdisc_lo.ifindex = if_nametoindex("lo"); 40 45 if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact")) 41 - return -1; 46 + goto free_ns; 42 47 43 48 if (!ASSERT_OK(bpf_tc_attach(&qdisc_lo, &tc_attach), 44 49 "filter add dev lo ingress")) 45 - return -1; 50 + goto free_ns; 46 51 47 52 /* Ensure 20 bytes options (i.e. in total 40 bytes tcp header) for the 48 53 * bpf_tcp_gen_syncookie() helper. ··· 55 50 if (write_sysctl("/proc/sys/net/ipv4/tcp_window_scaling", "1") || 56 51 write_sysctl("/proc/sys/net/ipv4/tcp_timestamps", "1") || 57 52 write_sysctl("/proc/sys/net/ipv4/tcp_sack", "1")) 58 - return -1; 53 + goto free_ns; 59 54 60 - return 0; 55 + return ns; 56 + 57 + free_ns: 58 + netns_free(ns); 59 + return NULL; 61 60 } 62 61 63 - static void reset_test(void) 62 + static void reset_test(struct test_btf_skc_cls_ingress *skel) 64 63 { 64 + memset(&skel->bss->srv_sa4, 0, sizeof(skel->bss->srv_sa4)); 65 65 memset(&skel->bss->srv_sa6, 0, sizeof(skel->bss->srv_sa6)); 66 66 skel->bss->listen_tp_sport = 0; 67 67 skel->bss->req_sk_sport = 0; 68 68 skel->bss->recv_cookie = 0; 69 69 skel->bss->gen_cookie = 0; 70 70 skel->bss->linum = 0; 71 + skel->bss->mss = 0; 71 72 } 72 73 73 - static void print_err_line(void) 74 + static void print_err_line(struct test_btf_skc_cls_ingress *skel) 74 75 { 75 76 if (skel->bss->linum) 76 77 printf("bpf prog error at line %u\n", skel->bss->linum); 77 78 } 78 79 79 - static void test_conn(void) 80 + static int v6only_true(int fd, void *opts) 80 81 { 82 + int mode = true; 83 + 84 + return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 85 + } 86 + 87 + static int v6only_false(int fd, void *opts) 88 + { 89 + int mode = false; 90 + 91 + return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 92 + } 93 + 94 + static void run_test(struct test_btf_skc_cls_ingress *skel, bool gen_cookies, 95 + int ip_mode) 96 + { 97 + const char *tcp_syncookies = gen_cookies ? "2" : "1"; 81 98 int listen_fd = -1, cli_fd = -1, srv_fd = -1, err; 82 - socklen_t addrlen = sizeof(srv_sa6); 99 + struct network_helper_opts opts = { 0 }; 100 + struct sockaddr_storage *addr; 101 + struct sockaddr_in6 srv_sa6; 102 + struct sockaddr_in srv_sa4; 103 + socklen_t addr_len; 104 + int sock_family; 105 + char *srv_addr; 83 106 int srv_port; 84 107 85 - if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 108 + switch (ip_mode) { 109 + case TEST_MODE_IPV4: 110 + sock_family = AF_INET; 111 + srv_addr = SERVER_ADDR_IPV4; 112 + addr = (struct sockaddr_storage *)&srv_sa4; 113 + addr_len = sizeof(srv_sa4); 114 + break; 115 + case TEST_MODE_IPV6: 116 + opts.post_socket_cb = v6only_true; 117 + sock_family = AF_INET6; 118 + srv_addr = SERVER_ADDR_IPV6; 119 + addr = (struct sockaddr_storage *)&srv_sa6; 120 + addr_len = sizeof(srv_sa6); 121 + break; 122 + case TEST_MODE_DUAL: 123 + opts.post_socket_cb = v6only_false; 124 + sock_family = AF_INET6; 125 + srv_addr = SERVER_ADDR_DUAL; 126 + addr = (struct sockaddr_storage *)&srv_sa6; 127 + addr_len = sizeof(srv_sa6); 128 + break; 129 + default: 130 + PRINT_FAIL("Unknown IP mode %d", ip_mode); 131 + return; 132 + } 133 + 134 + if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", tcp_syncookies)) 86 135 return; 87 136 88 - listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 89 - if (CHECK_FAIL(listen_fd == -1)) 137 + listen_fd = start_server_str(sock_family, SOCK_STREAM, srv_addr, 0, 138 + &opts); 139 + if (!ASSERT_OK_FD(listen_fd, "start server")) 90 140 return; 91 141 92 - err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen); 93 - if (CHECK(err, "getsockname(listen_fd)", "err:%d errno:%d\n", err, 94 - errno)) 142 + err = getsockname(listen_fd, (struct sockaddr *)addr, &addr_len); 143 + if (!ASSERT_OK(err, "getsockname(listen_fd)")) 95 144 goto done; 96 - memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); 97 - srv_port = ntohs(srv_sa6.sin6_port); 145 + 146 + switch (ip_mode) { 147 + case TEST_MODE_IPV4: 148 + memcpy(&skel->bss->srv_sa4, &srv_sa4, sizeof(srv_sa4)); 149 + srv_port = ntohs(srv_sa4.sin_port); 150 + break; 151 + case TEST_MODE_IPV6: 152 + case TEST_MODE_DUAL: 153 + memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); 154 + srv_port = ntohs(srv_sa6.sin6_port); 155 + break; 156 + default: 157 + goto done; 158 + } 98 159 99 160 cli_fd = connect_to_fd(listen_fd, 0); 100 - if (CHECK_FAIL(cli_fd == -1)) 161 + if (!ASSERT_OK_FD(cli_fd, "connect client")) 101 162 goto done; 102 163 103 164 srv_fd = accept(listen_fd, NULL, NULL); 104 - if (CHECK_FAIL(srv_fd == -1)) 165 + if (!ASSERT_OK_FD(srv_fd, "accept connection")) 105 166 goto done; 106 167 107 - if (CHECK(skel->bss->listen_tp_sport != srv_port || 108 - skel->bss->req_sk_sport != srv_port, 109 - "Unexpected sk src port", 110 - "listen_tp_sport:%u req_sk_sport:%u expected:%u\n", 111 - skel->bss->listen_tp_sport, skel->bss->req_sk_sport, 112 - srv_port)) 113 - goto done; 168 + ASSERT_EQ(skel->bss->listen_tp_sport, srv_port, "listen tp src port"); 114 169 115 - if (CHECK(skel->bss->gen_cookie || skel->bss->recv_cookie, 116 - "Unexpected syncookie states", 117 - "gen_cookie:%u recv_cookie:%u\n", 118 - skel->bss->gen_cookie, skel->bss->recv_cookie)) 119 - goto done; 120 - 121 - CHECK(skel->bss->linum, "bpf prog detected error", "at line %u\n", 122 - skel->bss->linum); 170 + if (!gen_cookies) { 171 + ASSERT_EQ(skel->bss->req_sk_sport, srv_port, 172 + "request socket source port with syncookies disabled"); 173 + ASSERT_EQ(skel->bss->gen_cookie, 0, 174 + "generated syncookie with syncookies disabled"); 175 + ASSERT_EQ(skel->bss->recv_cookie, 0, 176 + "received syncookie with syncookies disabled"); 177 + } else { 178 + ASSERT_EQ(skel->bss->req_sk_sport, 0, 179 + "request socket source port with syncookies enabled"); 180 + ASSERT_NEQ(skel->bss->gen_cookie, 0, 181 + "syncookie properly generated"); 182 + ASSERT_EQ(skel->bss->gen_cookie, skel->bss->recv_cookie, 183 + "matching syncookies on client and server"); 184 + ASSERT_GT(skel->bss->mss, MIN_IPV4_MSS, 185 + "MSS in cookie min value"); 186 + ASSERT_LT(skel->bss->mss, USHRT_MAX, 187 + "MSS in cookie max value"); 188 + } 123 189 124 190 done: 125 191 if (listen_fd != -1) ··· 201 125 close(srv_fd); 202 126 } 203 127 204 - static void test_syncookie(void) 128 + static void test_conn_ipv4(struct test_btf_skc_cls_ingress *skel) 205 129 { 206 - int listen_fd = -1, cli_fd = -1, srv_fd = -1, err; 207 - socklen_t addrlen = sizeof(srv_sa6); 208 - int srv_port; 130 + run_test(skel, false, TEST_MODE_IPV4); 131 + } 209 132 210 - /* Enforce syncookie mode */ 211 - if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2")) 212 - return; 133 + static void test_conn_ipv6(struct test_btf_skc_cls_ingress *skel) 134 + { 135 + run_test(skel, false, TEST_MODE_IPV6); 136 + } 213 137 214 - listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 215 - if (CHECK_FAIL(listen_fd == -1)) 216 - return; 138 + static void test_conn_dual(struct test_btf_skc_cls_ingress *skel) 139 + { 140 + run_test(skel, false, TEST_MODE_DUAL); 141 + } 217 142 218 - err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen); 219 - if (CHECK(err, "getsockname(listen_fd)", "err:%d errno:%d\n", err, 220 - errno)) 221 - goto done; 222 - memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); 223 - srv_port = ntohs(srv_sa6.sin6_port); 143 + static void test_syncookie_ipv4(struct test_btf_skc_cls_ingress *skel) 144 + { 145 + run_test(skel, true, TEST_MODE_IPV4); 146 + } 224 147 225 - cli_fd = connect_to_fd(listen_fd, 0); 226 - if (CHECK_FAIL(cli_fd == -1)) 227 - goto done; 148 + static void test_syncookie_ipv6(struct test_btf_skc_cls_ingress *skel) 149 + { 150 + run_test(skel, true, TEST_MODE_IPV6); 151 + } 228 152 229 - srv_fd = accept(listen_fd, NULL, NULL); 230 - if (CHECK_FAIL(srv_fd == -1)) 231 - goto done; 232 - 233 - if (CHECK(skel->bss->listen_tp_sport != srv_port, 234 - "Unexpected tp src port", 235 - "listen_tp_sport:%u expected:%u\n", 236 - skel->bss->listen_tp_sport, srv_port)) 237 - goto done; 238 - 239 - if (CHECK(skel->bss->req_sk_sport, 240 - "Unexpected req_sk src port", 241 - "req_sk_sport:%u expected:0\n", 242 - skel->bss->req_sk_sport)) 243 - goto done; 244 - 245 - if (CHECK(!skel->bss->gen_cookie || 246 - skel->bss->gen_cookie != skel->bss->recv_cookie, 247 - "Unexpected syncookie states", 248 - "gen_cookie:%u recv_cookie:%u\n", 249 - skel->bss->gen_cookie, skel->bss->recv_cookie)) 250 - goto done; 251 - 252 - CHECK(skel->bss->linum, "bpf prog detected error", "at line %u\n", 253 - skel->bss->linum); 254 - 255 - done: 256 - if (listen_fd != -1) 257 - close(listen_fd); 258 - if (cli_fd != -1) 259 - close(cli_fd); 260 - if (srv_fd != -1) 261 - close(srv_fd); 153 + static void test_syncookie_dual(struct test_btf_skc_cls_ingress *skel) 154 + { 155 + run_test(skel, true, TEST_MODE_DUAL); 262 156 } 263 157 264 158 struct test { 265 159 const char *desc; 266 - void (*run)(void); 160 + void (*run)(struct test_btf_skc_cls_ingress *skel); 267 161 }; 268 162 269 163 #define DEF_TEST(name) { #name, test_##name } 270 164 static struct test tests[] = { 271 - DEF_TEST(conn), 272 - DEF_TEST(syncookie), 165 + DEF_TEST(conn_ipv4), 166 + DEF_TEST(conn_ipv6), 167 + DEF_TEST(conn_dual), 168 + DEF_TEST(syncookie_ipv4), 169 + DEF_TEST(syncookie_ipv6), 170 + DEF_TEST(syncookie_dual), 273 171 }; 274 172 275 173 void test_btf_skc_cls_ingress(void) 276 174 { 175 + struct test_btf_skc_cls_ingress *skel; 176 + struct netns_obj *ns; 277 177 int i; 278 178 279 179 skel = test_btf_skc_cls_ingress__open_and_load(); 280 - if (CHECK(!skel, "test_btf_skc_cls_ingress__open_and_load", "failed\n")) 180 + if (!ASSERT_OK_PTR(skel, "test_btf_skc_cls_ingress__open_and_load")) 281 181 return; 282 182 283 183 for (i = 0; i < ARRAY_SIZE(tests); i++) { 284 184 if (!test__start_subtest(tests[i].desc)) 285 185 continue; 286 186 287 - if (prepare_netns()) 187 + ns = prepare_netns(skel); 188 + if (!ns) 288 189 break; 289 190 290 - tests[i].run(); 191 + tests[i].run(skel); 291 192 292 - print_err_line(); 293 - reset_test(); 193 + print_err_line(skel); 194 + reset_test(skel); 195 + netns_free(ns); 294 196 } 295 197 296 198 test_btf_skc_cls_ingress__destroy(skel);
+55 -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; 16 17 __u32 gen_cookie = 0; 18 + __u32 mss = 0; 17 19 __u32 linum = 0; 18 20 19 21 #define LOG() ({ if (!linum) linum = __LINE__; }) 20 22 21 - static void test_syncookie_helper(struct ipv6hdr *ip6h, struct tcphdr *th, 22 - struct tcp_sock *tp, 23 + static void test_syncookie_helper(void *iphdr, int iphdr_size, 24 + struct tcphdr *th, struct tcp_sock *tp, 23 25 struct __sk_buff *skb) 24 26 { 25 27 if (th->syn) { ··· 40 38 return; 41 39 } 42 40 43 - mss_cookie = bpf_tcp_gen_syncookie(tp, ip6h, sizeof(*ip6h), 41 + mss_cookie = bpf_tcp_gen_syncookie(tp, iphdr, iphdr_size, 44 42 th, 40); 45 43 if (mss_cookie < 0) { 46 44 if (mss_cookie != -ENOENT) 47 45 LOG(); 48 46 } else { 49 47 gen_cookie = (__u32)mss_cookie; 48 + mss = mss_cookie >> 32; 50 49 } 51 50 } else if (gen_cookie) { 52 51 /* It was in cookie mode */ 53 - int ret = bpf_tcp_check_syncookie(tp, ip6h, sizeof(*ip6h), 52 + int ret = bpf_tcp_check_syncookie(tp, iphdr, iphdr_size, 54 53 th, sizeof(*th)); 55 54 56 55 if (ret < 0) { ··· 63 60 } 64 61 } 65 62 66 - 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) 67 64 { 68 - struct bpf_sock_tuple *tuple; 65 + struct bpf_sock_tuple *tuple = NULL; 66 + unsigned int tuple_len = 0; 69 67 struct bpf_sock *bpf_skc; 70 - unsigned int tuple_len; 68 + void *data_end, *iphdr; 69 + struct ipv6hdr *ip6h; 70 + struct iphdr *ip4h; 71 71 struct tcphdr *th; 72 - void *data_end; 72 + int iphdr_size; 73 73 74 74 data_end = (void *)(long)(skb->data_end); 75 75 76 - th = (struct tcphdr *)(ip6h + 1); 77 - 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: 78 112 return TC_ACT_OK; 113 + } 79 114 80 - /* Is it the testing traffic? */ 81 - if (th->dest != srv_sa6.sin6_port) 82 - return TC_ACT_OK; 83 - 84 - tuple_len = sizeof(tuple->ipv6); 85 - tuple = (struct bpf_sock_tuple *)&ip6h->saddr; 86 115 if ((void *)tuple + tuple_len > data_end) { 87 116 LOG(); 88 117 return TC_ACT_OK; ··· 161 126 162 127 listen_tp_sport = tp->inet_conn.icsk_inet.sk.__sk_common.skc_num; 163 128 164 - test_syncookie_helper(ip6h, th, tp, skb); 129 + test_syncookie_helper(iphdr, iphdr_size, th, tp, skb); 165 130 bpf_sk_release(tp); 166 131 return TC_ACT_OK; 167 132 } ··· 177 142 SEC("tc") 178 143 int cls_ingress(struct __sk_buff *skb) 179 144 { 180 - struct ipv6hdr *ip6h; 181 145 struct ethhdr *eth; 182 146 void *data_end; 183 147 ··· 186 152 if (eth + 1 > data_end) 187 153 return TC_ACT_OK; 188 154 189 - 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)) 190 157 return TC_ACT_OK; 191 158 192 - ip6h = (struct ipv6hdr *)(eth + 1); 193 - if (ip6h + 1 > data_end) 194 - return TC_ACT_OK; 195 - 196 - if (ip6h->nexthdr == IPPROTO_TCP) 197 - return handle_ip6_tcp(ip6h, skb); 198 - 199 - return TC_ACT_OK; 159 + return handle_ip_tcp(eth, skb); 200 160 } 201 161 202 162 char _license[] SEC("license") = "GPL";
-167
tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - // Copyright (c) 2018 Facebook 3 - // Copyright (c) 2019 Cloudflare 4 - 5 - #include <string.h> 6 - 7 - #include <linux/bpf.h> 8 - #include <linux/pkt_cls.h> 9 - #include <linux/if_ether.h> 10 - #include <linux/in.h> 11 - #include <linux/ip.h> 12 - #include <linux/ipv6.h> 13 - #include <sys/socket.h> 14 - #include <linux/tcp.h> 15 - 16 - #include <bpf/bpf_helpers.h> 17 - #include <bpf/bpf_endian.h> 18 - 19 - struct { 20 - __uint(type, BPF_MAP_TYPE_ARRAY); 21 - __type(key, __u32); 22 - __type(value, __u32); 23 - __uint(max_entries, 3); 24 - } results SEC(".maps"); 25 - 26 - static __always_inline __s64 gen_syncookie(void *data_end, struct bpf_sock *sk, 27 - void *iph, __u32 ip_size, 28 - struct tcphdr *tcph) 29 - { 30 - __u32 thlen = tcph->doff * 4; 31 - 32 - if (tcph->syn && !tcph->ack) { 33 - // packet should only have an MSS option 34 - if (thlen != 24) 35 - return 0; 36 - 37 - if ((void *)tcph + thlen > data_end) 38 - return 0; 39 - 40 - return bpf_tcp_gen_syncookie(sk, iph, ip_size, tcph, thlen); 41 - } 42 - return 0; 43 - } 44 - 45 - static __always_inline void check_syncookie(void *ctx, void *data, 46 - void *data_end) 47 - { 48 - struct bpf_sock_tuple tup; 49 - struct bpf_sock *sk; 50 - struct ethhdr *ethh; 51 - struct iphdr *ipv4h; 52 - struct ipv6hdr *ipv6h; 53 - struct tcphdr *tcph; 54 - int ret; 55 - __u32 key_mss = 2; 56 - __u32 key_gen = 1; 57 - __u32 key = 0; 58 - __s64 seq_mss; 59 - 60 - ethh = data; 61 - if (ethh + 1 > data_end) 62 - return; 63 - 64 - switch (bpf_ntohs(ethh->h_proto)) { 65 - case ETH_P_IP: 66 - ipv4h = data + sizeof(struct ethhdr); 67 - if (ipv4h + 1 > data_end) 68 - return; 69 - 70 - if (ipv4h->ihl != 5) 71 - return; 72 - 73 - tcph = data + sizeof(struct ethhdr) + sizeof(struct iphdr); 74 - if (tcph + 1 > data_end) 75 - return; 76 - 77 - tup.ipv4.saddr = ipv4h->saddr; 78 - tup.ipv4.daddr = ipv4h->daddr; 79 - tup.ipv4.sport = tcph->source; 80 - tup.ipv4.dport = tcph->dest; 81 - 82 - sk = bpf_skc_lookup_tcp(ctx, &tup, sizeof(tup.ipv4), 83 - BPF_F_CURRENT_NETNS, 0); 84 - if (!sk) 85 - return; 86 - 87 - if (sk->state != BPF_TCP_LISTEN) 88 - goto release; 89 - 90 - seq_mss = gen_syncookie(data_end, sk, ipv4h, sizeof(*ipv4h), 91 - tcph); 92 - 93 - ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h), 94 - tcph, sizeof(*tcph)); 95 - break; 96 - 97 - case ETH_P_IPV6: 98 - ipv6h = data + sizeof(struct ethhdr); 99 - if (ipv6h + 1 > data_end) 100 - return; 101 - 102 - if (ipv6h->nexthdr != IPPROTO_TCP) 103 - return; 104 - 105 - tcph = data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr); 106 - if (tcph + 1 > data_end) 107 - return; 108 - 109 - memcpy(tup.ipv6.saddr, &ipv6h->saddr, sizeof(tup.ipv6.saddr)); 110 - memcpy(tup.ipv6.daddr, &ipv6h->daddr, sizeof(tup.ipv6.daddr)); 111 - tup.ipv6.sport = tcph->source; 112 - tup.ipv6.dport = tcph->dest; 113 - 114 - sk = bpf_skc_lookup_tcp(ctx, &tup, sizeof(tup.ipv6), 115 - BPF_F_CURRENT_NETNS, 0); 116 - if (!sk) 117 - return; 118 - 119 - if (sk->state != BPF_TCP_LISTEN) 120 - goto release; 121 - 122 - seq_mss = gen_syncookie(data_end, sk, ipv6h, sizeof(*ipv6h), 123 - tcph); 124 - 125 - ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h), 126 - tcph, sizeof(*tcph)); 127 - break; 128 - 129 - default: 130 - return; 131 - } 132 - 133 - if (seq_mss > 0) { 134 - __u32 cookie = (__u32)seq_mss; 135 - __u32 mss = seq_mss >> 32; 136 - 137 - bpf_map_update_elem(&results, &key_gen, &cookie, 0); 138 - bpf_map_update_elem(&results, &key_mss, &mss, 0); 139 - } 140 - 141 - if (ret == 0) { 142 - __u32 cookie = bpf_ntohl(tcph->ack_seq) - 1; 143 - 144 - bpf_map_update_elem(&results, &key, &cookie, 0); 145 - } 146 - 147 - release: 148 - bpf_sk_release(sk); 149 - } 150 - 151 - SEC("tc") 152 - int check_syncookie_clsact(struct __sk_buff *skb) 153 - { 154 - check_syncookie(skb, (void *)(long)skb->data, 155 - (void *)(long)skb->data_end); 156 - return TC_ACT_OK; 157 - } 158 - 159 - SEC("xdp") 160 - int check_syncookie_xdp(struct xdp_md *ctx) 161 - { 162 - check_syncookie(ctx, (void *)(long)ctx->data, 163 - (void *)(long)ctx->data_end); 164 - return XDP_PASS; 165 - } 166 - 167 - char _license[] SEC("license") = "GPL";
-85
tools/testing/selftests/bpf/test_tcp_check_syncookie.sh
··· 1 - #!/bin/sh 2 - # SPDX-License-Identifier: GPL-2.0 3 - # Copyright (c) 2018 Facebook 4 - # Copyright (c) 2019 Cloudflare 5 - 6 - set -eu 7 - readonly NS1="ns1-$(mktemp -u XXXXXX)" 8 - 9 - wait_for_ip() 10 - { 11 - local _i 12 - printf "Wait for IP %s to become available " "$1" 13 - for _i in $(seq ${MAX_PING_TRIES}); do 14 - printf "." 15 - if ns1_exec ping -c 1 -W 1 "$1" >/dev/null 2>&1; then 16 - echo " OK" 17 - return 18 - fi 19 - sleep 1 20 - done 21 - echo 1>&2 "ERROR: Timeout waiting for test IP to become available." 22 - exit 1 23 - } 24 - 25 - get_prog_id() 26 - { 27 - awk '/ id / {sub(/.* id /, "", $0); print($1)}' 28 - } 29 - 30 - ns1_exec() 31 - { 32 - ip netns exec ${NS1} "$@" 33 - } 34 - 35 - setup() 36 - { 37 - ip netns add ${NS1} 38 - ns1_exec ip link set lo up 39 - 40 - ns1_exec sysctl -w net.ipv4.tcp_syncookies=2 41 - ns1_exec sysctl -w net.ipv4.tcp_window_scaling=0 42 - ns1_exec sysctl -w net.ipv4.tcp_timestamps=0 43 - ns1_exec sysctl -w net.ipv4.tcp_sack=0 44 - 45 - wait_for_ip 127.0.0.1 46 - wait_for_ip ::1 47 - } 48 - 49 - cleanup() 50 - { 51 - ip netns del ns1 2>/dev/null || : 52 - } 53 - 54 - main() 55 - { 56 - trap cleanup EXIT 2 3 6 15 57 - setup 58 - 59 - printf "Testing clsact..." 60 - ns1_exec tc qdisc add dev "${TEST_IF}" clsact 61 - ns1_exec tc filter add dev "${TEST_IF}" ingress \ 62 - bpf obj "${BPF_PROG_OBJ}" sec "${CLSACT_SECTION}" da 63 - 64 - BPF_PROG_ID=$(ns1_exec tc filter show dev "${TEST_IF}" ingress | \ 65 - get_prog_id) 66 - ns1_exec "${PROG}" "${BPF_PROG_ID}" 67 - ns1_exec tc qdisc del dev "${TEST_IF}" clsact 68 - 69 - printf "Testing XDP..." 70 - ns1_exec ip link set "${TEST_IF}" xdp \ 71 - object "${BPF_PROG_OBJ}" section "${XDP_SECTION}" 72 - BPF_PROG_ID=$(ns1_exec ip link show "${TEST_IF}" | get_prog_id) 73 - ns1_exec "${PROG}" "${BPF_PROG_ID}" 74 - } 75 - 76 - DIR=$(dirname $0) 77 - TEST_IF=lo 78 - MAX_PING_TRIES=5 79 - BPF_PROG_OBJ="${DIR}/test_tcp_check_syncookie_kern.bpf.o" 80 - CLSACT_SECTION="tc" 81 - XDP_SECTION="xdp" 82 - BPF_PROG_ID=0 83 - PROG="${DIR}/test_tcp_check_syncookie_user" 84 - 85 - main
-213
tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - // Copyright (c) 2018 Facebook 3 - // Copyright (c) 2019 Cloudflare 4 - 5 - #include <limits.h> 6 - #include <string.h> 7 - #include <stdlib.h> 8 - #include <unistd.h> 9 - 10 - #include <arpa/inet.h> 11 - #include <netinet/in.h> 12 - #include <sys/types.h> 13 - #include <sys/socket.h> 14 - 15 - #include <bpf/bpf.h> 16 - #include <bpf/libbpf.h> 17 - 18 - #include "cgroup_helpers.h" 19 - #include "network_helpers.h" 20 - 21 - static int get_map_fd_by_prog_id(int prog_id, bool *xdp) 22 - { 23 - struct bpf_prog_info info = {}; 24 - __u32 info_len = sizeof(info); 25 - __u32 map_ids[1]; 26 - int prog_fd = -1; 27 - int map_fd = -1; 28 - 29 - prog_fd = bpf_prog_get_fd_by_id(prog_id); 30 - if (prog_fd < 0) { 31 - log_err("Failed to get fd by prog id %d", prog_id); 32 - goto err; 33 - } 34 - 35 - info.nr_map_ids = 1; 36 - info.map_ids = (__u64)(unsigned long)map_ids; 37 - 38 - if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) { 39 - log_err("Failed to get info by prog fd %d", prog_fd); 40 - goto err; 41 - } 42 - 43 - if (!info.nr_map_ids) { 44 - log_err("No maps found for prog fd %d", prog_fd); 45 - goto err; 46 - } 47 - 48 - *xdp = info.type == BPF_PROG_TYPE_XDP; 49 - 50 - map_fd = bpf_map_get_fd_by_id(map_ids[0]); 51 - if (map_fd < 0) 52 - log_err("Failed to get fd by map id %d", map_ids[0]); 53 - err: 54 - if (prog_fd >= 0) 55 - close(prog_fd); 56 - return map_fd; 57 - } 58 - 59 - static int run_test(int server_fd, int results_fd, bool xdp) 60 - { 61 - int client = -1, srv_client = -1; 62 - int ret = 0; 63 - __u32 key = 0; 64 - __u32 key_gen = 1; 65 - __u32 key_mss = 2; 66 - __u32 value = 0; 67 - __u32 value_gen = 0; 68 - __u32 value_mss = 0; 69 - 70 - if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) { 71 - log_err("Can't clear results"); 72 - goto err; 73 - } 74 - 75 - if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) { 76 - log_err("Can't clear results"); 77 - goto err; 78 - } 79 - 80 - if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) { 81 - log_err("Can't clear results"); 82 - goto err; 83 - } 84 - 85 - client = connect_to_fd(server_fd, 0); 86 - if (client == -1) 87 - goto err; 88 - 89 - srv_client = accept(server_fd, NULL, 0); 90 - if (srv_client == -1) { 91 - log_err("Can't accept connection"); 92 - goto err; 93 - } 94 - 95 - if (bpf_map_lookup_elem(results_fd, &key, &value) < 0) { 96 - log_err("Can't lookup result"); 97 - goto err; 98 - } 99 - 100 - if (value == 0) { 101 - log_err("Didn't match syncookie: %u", value); 102 - goto err; 103 - } 104 - 105 - if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) { 106 - log_err("Can't lookup result"); 107 - goto err; 108 - } 109 - 110 - if (xdp && value_gen == 0) { 111 - // SYN packets do not get passed through generic XDP, skip the 112 - // rest of the test. 113 - printf("Skipping XDP cookie check\n"); 114 - goto out; 115 - } 116 - 117 - if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) { 118 - log_err("Can't lookup result"); 119 - goto err; 120 - } 121 - 122 - if (value != value_gen) { 123 - log_err("BPF generated cookie does not match kernel one"); 124 - goto err; 125 - } 126 - 127 - if (value_mss < 536 || value_mss > USHRT_MAX) { 128 - log_err("Unexpected MSS retrieved"); 129 - goto err; 130 - } 131 - 132 - goto out; 133 - 134 - err: 135 - ret = 1; 136 - out: 137 - close(client); 138 - close(srv_client); 139 - return ret; 140 - } 141 - 142 - static int v6only_true(int fd, void *opts) 143 - { 144 - int mode = true; 145 - 146 - return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 147 - } 148 - 149 - static int v6only_false(int fd, void *opts) 150 - { 151 - int mode = false; 152 - 153 - return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode)); 154 - } 155 - 156 - int main(int argc, char **argv) 157 - { 158 - struct network_helper_opts opts = { 0 }; 159 - int server = -1; 160 - int server_v6 = -1; 161 - int server_dual = -1; 162 - int results = -1; 163 - int err = 0; 164 - bool xdp; 165 - 166 - if (argc < 2) { 167 - fprintf(stderr, "Usage: %s prog_id\n", argv[0]); 168 - exit(1); 169 - } 170 - 171 - /* Use libbpf 1.0 API mode */ 172 - libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 173 - 174 - results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp); 175 - if (results < 0) { 176 - log_err("Can't get map"); 177 - goto err; 178 - } 179 - 180 - server = start_server_str(AF_INET, SOCK_STREAM, "127.0.0.1", 0, NULL); 181 - if (server == -1) 182 - goto err; 183 - 184 - opts.post_socket_cb = v6only_true; 185 - server_v6 = start_server_str(AF_INET6, SOCK_STREAM, "::1", 0, &opts); 186 - if (server_v6 == -1) 187 - goto err; 188 - 189 - opts.post_socket_cb = v6only_false; 190 - server_dual = start_server_str(AF_INET6, SOCK_STREAM, "::0", 0, &opts); 191 - if (server_dual == -1) 192 - goto err; 193 - 194 - if (run_test(server, results, xdp)) 195 - goto err; 196 - 197 - if (run_test(server_v6, results, xdp)) 198 - goto err; 199 - 200 - if (run_test(server_dual, results, xdp)) 201 - goto err; 202 - 203 - printf("ok\n"); 204 - goto out; 205 - err: 206 - err = 1; 207 - out: 208 - close(server); 209 - close(server_v6); 210 - close(server_dual); 211 - close(results); 212 - return err; 213 - }