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

selftests/bpf: add test for bpf_tcp_gen_syncookie

Modify the existing bpf_tcp_check_syncookie test to also generate a
SYN cookie, pass the packet to the kernel, and verify that the two
cookies are the same (and both valid). Since cloned SKBs are skipped
during generic XDP, this test does not issue a SYN cookie when run in
XDP mode. We therefore only check that a valid SYN cookie was issued at
the TC hook.

Additionally, verify that the MSS for that SYN cookie is within
expected range.

Signed-off-by: Petar Penkov <ppenkov@google.com>
Reviewed-by: Lorenz Bauer <lmb@cloudflare.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Petar Penkov and committed by
Alexei Starovoitov
91bc3578 637f71c0

+99 -13
+43 -5
tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
··· 19 19 struct bpf_map_def SEC("maps") results = { 20 20 .type = BPF_MAP_TYPE_ARRAY, 21 21 .key_size = sizeof(__u32), 22 - .value_size = sizeof(__u64), 23 - .max_entries = 1, 22 + .value_size = sizeof(__u32), 23 + .max_entries = 3, 24 24 }; 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 + } 25 44 26 45 static __always_inline void check_syncookie(void *ctx, void *data, 27 46 void *data_end) ··· 52 33 struct ipv6hdr *ipv6h; 53 34 struct tcphdr *tcph; 54 35 int ret; 36 + __u32 key_mss = 2; 37 + __u32 key_gen = 1; 55 38 __u32 key = 0; 56 - __u64 value = 1; 39 + __s64 seq_mss; 57 40 58 41 ethh = data; 59 42 if (ethh + 1 > data_end) ··· 87 66 if (sk->state != BPF_TCP_LISTEN) 88 67 goto release; 89 68 69 + seq_mss = gen_syncookie(data_end, sk, ipv4h, sizeof(*ipv4h), 70 + tcph); 71 + 90 72 ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h), 91 73 tcph, sizeof(*tcph)); 92 74 break; ··· 119 95 if (sk->state != BPF_TCP_LISTEN) 120 96 goto release; 121 97 98 + seq_mss = gen_syncookie(data_end, sk, ipv6h, sizeof(*ipv6h), 99 + tcph); 100 + 122 101 ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h), 123 102 tcph, sizeof(*tcph)); 124 103 break; ··· 130 103 return; 131 104 } 132 105 133 - if (ret == 0) 134 - bpf_map_update_elem(&results, &key, &value, 0); 106 + if (seq_mss > 0) { 107 + __u32 cookie = (__u32)seq_mss; 108 + __u32 mss = seq_mss >> 32; 109 + 110 + bpf_map_update_elem(&results, &key_gen, &cookie, 0); 111 + bpf_map_update_elem(&results, &key_mss, &mss, 0); 112 + } 113 + 114 + if (ret == 0) { 115 + __u32 cookie = bpf_ntohl(tcph->ack_seq) - 1; 116 + 117 + bpf_map_update_elem(&results, &key, &cookie, 0); 118 + } 135 119 136 120 release: 137 121 bpf_sk_release(sk);
+3
tools/testing/selftests/bpf/test_tcp_check_syncookie.sh
··· 37 37 ns1_exec ip link set lo up 38 38 39 39 ns1_exec sysctl -w net.ipv4.tcp_syncookies=2 40 + ns1_exec sysctl -w net.ipv4.tcp_window_scaling=0 41 + ns1_exec sysctl -w net.ipv4.tcp_timestamps=0 42 + ns1_exec sysctl -w net.ipv4.tcp_sack=0 40 43 41 44 wait_for_ip 127.0.0.1 42 45 wait_for_ip ::1
+53 -8
tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c
··· 2 2 // Copyright (c) 2018 Facebook 3 3 // Copyright (c) 2019 Cloudflare 4 4 5 + #include <limits.h> 5 6 #include <string.h> 6 7 #include <stdlib.h> 7 8 #include <unistd.h> ··· 78 77 return fd; 79 78 } 80 79 81 - static int get_map_fd_by_prog_id(int prog_id) 80 + static int get_map_fd_by_prog_id(int prog_id, bool *xdp) 82 81 { 83 82 struct bpf_prog_info info = {}; 84 83 __u32 info_len = sizeof(info); ··· 105 104 goto err; 106 105 } 107 106 107 + *xdp = info.type == BPF_PROG_TYPE_XDP; 108 + 108 109 map_fd = bpf_map_get_fd_by_id(map_ids[0]); 109 110 if (map_fd < 0) 110 111 log_err("Failed to get fd by map id %d", map_ids[0]); ··· 116 113 return map_fd; 117 114 } 118 115 119 - static int run_test(int server_fd, int results_fd) 116 + static int run_test(int server_fd, int results_fd, bool xdp) 120 117 { 121 118 int client = -1, srv_client = -1; 122 119 int ret = 0; 123 120 __u32 key = 0; 124 - __u64 value = 0; 121 + __u32 key_gen = 1; 122 + __u32 key_mss = 2; 123 + __u32 value = 0; 124 + __u32 value_gen = 0; 125 + __u32 value_mss = 0; 125 126 126 127 if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) { 128 + log_err("Can't clear results"); 129 + goto err; 130 + } 131 + 132 + if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) { 133 + log_err("Can't clear results"); 134 + goto err; 135 + } 136 + 137 + if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) { 127 138 log_err("Can't clear results"); 128 139 goto err; 129 140 } ··· 157 140 goto err; 158 141 } 159 142 160 - if (value != 1) { 161 - log_err("Didn't match syncookie: %llu", value); 143 + if (value == 0) { 144 + log_err("Didn't match syncookie: %u", value); 145 + goto err; 146 + } 147 + 148 + if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) { 149 + log_err("Can't lookup result"); 150 + goto err; 151 + } 152 + 153 + if (xdp && value_gen == 0) { 154 + // SYN packets do not get passed through generic XDP, skip the 155 + // rest of the test. 156 + printf("Skipping XDP cookie check\n"); 157 + goto out; 158 + } 159 + 160 + if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) { 161 + log_err("Can't lookup result"); 162 + goto err; 163 + } 164 + 165 + if (value != value_gen) { 166 + log_err("BPF generated cookie does not match kernel one"); 167 + goto err; 168 + } 169 + 170 + if (value_mss < 536 || value_mss > USHRT_MAX) { 171 + log_err("Unexpected MSS retrieved"); 162 172 goto err; 163 173 } 164 174 ··· 207 163 int server_v6 = -1; 208 164 int results = -1; 209 165 int err = 0; 166 + bool xdp; 210 167 211 168 if (argc < 2) { 212 169 fprintf(stderr, "Usage: %s prog_id\n", argv[0]); 213 170 exit(1); 214 171 } 215 172 216 - results = get_map_fd_by_prog_id(atoi(argv[1])); 173 + results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp); 217 174 if (results < 0) { 218 175 log_err("Can't get map"); 219 176 goto err; ··· 239 194 if (server_v6 == -1) 240 195 goto err; 241 196 242 - if (run_test(server, results)) 197 + if (run_test(server, results, xdp)) 243 198 goto err; 244 199 245 - if (run_test(server_v6, results)) 200 + if (run_test(server_v6, results, xdp)) 246 201 goto err; 247 202 248 203 printf("ok\n");