1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3 4 #include "vmlinux.h" 5 #include "bpf_tracing_net.h" 6 #include <bpf/bpf_core_read.h> 7 #include <bpf/bpf_helpers.h> 8 #include <bpf/bpf_tracing.h> 9 #include "bpf_misc.h" 10 11 extern unsigned long CONFIG_HZ __kconfig; 12 13 const volatile char veth[IFNAMSIZ]; 14 const volatile int veth_ifindex; 15 16 int nr_listen; 17 int nr_passive; 18 int nr_active; 19 int nr_connect; 20 int nr_binddev; 21 int nr_socket_post_create; 22 int nr_fin_wait1; 23 24 struct sockopt_test { 25 int opt; 26 int new; 27 int restore; 28 int expected; 29 int tcp_expected; 30 unsigned int flip:1; 31 }; 32 33 static const char not_exist_cc[] = "not_exist"; 34 static const char cubic_cc[] = "cubic"; 35 static const char reno_cc[] = "reno"; 36 37 static const struct sockopt_test sol_socket_tests[] = { 38 { .opt = SO_REUSEADDR, .flip = 1, }, 39 { .opt = SO_SNDBUF, .new = 8123, .expected = 8123 * 2, }, 40 { .opt = SO_RCVBUF, .new = 8123, .expected = 8123 * 2, }, 41 { .opt = SO_KEEPALIVE, .flip = 1, }, 42 { .opt = SO_PRIORITY, .new = 0xeb9f, .expected = 0xeb9f, }, 43 { .opt = SO_REUSEPORT, .flip = 1, }, 44 { .opt = SO_RCVLOWAT, .new = 8123, .expected = 8123, }, 45 { .opt = SO_MARK, .new = 0xeb9f, .expected = 0xeb9f, }, 46 { .opt = SO_MAX_PACING_RATE, .new = 0xeb9f, .expected = 0xeb9f, }, 47 { .opt = SO_TXREHASH, .flip = 1, }, 48 { .opt = 0, }, 49 }; 50 51 static const struct sockopt_test sol_tcp_tests[] = { 52 { .opt = TCP_NODELAY, .flip = 1, }, 53 { .opt = TCP_KEEPIDLE, .new = 123, .expected = 123, .restore = 321, }, 54 { .opt = TCP_KEEPINTVL, .new = 123, .expected = 123, .restore = 321, }, 55 { .opt = TCP_KEEPCNT, .new = 123, .expected = 123, .restore = 124, }, 56 { .opt = TCP_SYNCNT, .new = 123, .expected = 123, .restore = 124, }, 57 { .opt = TCP_WINDOW_CLAMP, .new = 8123, .expected = 8123, .restore = 8124, }, 58 { .opt = TCP_CONGESTION, }, 59 { .opt = TCP_THIN_LINEAR_TIMEOUTS, .flip = 1, }, 60 { .opt = TCP_USER_TIMEOUT, .new = 123400, .expected = 123400, }, 61 { .opt = TCP_NOTSENT_LOWAT, .new = 1314, .expected = 1314, }, 62 { .opt = 0, }, 63 }; 64 65 static const struct sockopt_test sol_ip_tests[] = { 66 { .opt = IP_TOS, .new = 0xe1, .expected = 0xe1, .tcp_expected = 0xe0, }, 67 { .opt = 0, }, 68 }; 69 70 static const struct sockopt_test sol_ipv6_tests[] = { 71 { .opt = IPV6_TCLASS, .new = 0xe1, .expected = 0xe1, .tcp_expected = 0xe0, }, 72 { .opt = IPV6_AUTOFLOWLABEL, .flip = 1, }, 73 { .opt = 0, }, 74 }; 75 76 struct loop_ctx { 77 void *ctx; 78 struct sock *sk; 79 }; 80 81 static int bpf_test_sockopt_flip(void *ctx, struct sock *sk, 82 const struct sockopt_test *t, 83 int level) 84 { 85 int old, tmp, new, opt = t->opt; 86 87 opt = t->opt; 88 89 if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old))) 90 return 1; 91 /* kernel initialized txrehash to 255 */ 92 if (level == SOL_SOCKET && opt == SO_TXREHASH && old != 0 && old != 1) 93 old = 1; 94 95 new = !old; 96 if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new))) 97 return 1; 98 if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) || 99 tmp != new) 100 return 1; 101 102 if (bpf_setsockopt(ctx, level, opt, &old, sizeof(old))) 103 return 1; 104 105 return 0; 106 } 107 108 static int bpf_test_sockopt_int(void *ctx, struct sock *sk, 109 const struct sockopt_test *t, 110 int level) 111 { 112 int old, tmp, new, expected, opt; 113 114 opt = t->opt; 115 new = t->new; 116 if (sk->sk_type == SOCK_STREAM && t->tcp_expected) 117 expected = t->tcp_expected; 118 else 119 expected = t->expected; 120 121 if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)) || 122 old == new) 123 return 1; 124 125 if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new))) 126 return 1; 127 if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) || 128 tmp != expected) 129 return 1; 130 131 if (t->restore) 132 old = t->restore; 133 if (bpf_setsockopt(ctx, level, opt, &old, sizeof(old))) 134 return 1; 135 136 return 0; 137 } 138 139 static int bpf_test_socket_sockopt(__u32 i, struct loop_ctx *lc) 140 { 141 const struct sockopt_test *t; 142 143 if (i >= ARRAY_SIZE(sol_socket_tests)) 144 return 1; 145 146 t = &sol_socket_tests[i]; 147 if (!t->opt) 148 return 1; 149 150 if (t->flip) 151 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, SOL_SOCKET); 152 153 return bpf_test_sockopt_int(lc->ctx, lc->sk, t, SOL_SOCKET); 154 } 155 156 static int bpf_test_ip_sockopt(__u32 i, struct loop_ctx *lc) 157 { 158 const struct sockopt_test *t; 159 160 if (i >= ARRAY_SIZE(sol_ip_tests)) 161 return 1; 162 163 t = &sol_ip_tests[i]; 164 if (!t->opt) 165 return 1; 166 167 if (t->flip) 168 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, IPPROTO_IP); 169 170 return bpf_test_sockopt_int(lc->ctx, lc->sk, t, IPPROTO_IP); 171 } 172 173 static int bpf_test_ipv6_sockopt(__u32 i, struct loop_ctx *lc) 174 { 175 const struct sockopt_test *t; 176 177 if (i >= ARRAY_SIZE(sol_ipv6_tests)) 178 return 1; 179 180 t = &sol_ipv6_tests[i]; 181 if (!t->opt) 182 return 1; 183 184 if (t->flip) 185 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, IPPROTO_IPV6); 186 187 return bpf_test_sockopt_int(lc->ctx, lc->sk, t, IPPROTO_IPV6); 188 } 189 190 static int bpf_test_tcp_sockopt(__u32 i, struct loop_ctx *lc) 191 { 192 const struct sockopt_test *t; 193 struct sock *sk; 194 void *ctx; 195 196 if (i >= ARRAY_SIZE(sol_tcp_tests)) 197 return 1; 198 199 t = &sol_tcp_tests[i]; 200 if (!t->opt) 201 return 1; 202 203 ctx = lc->ctx; 204 sk = lc->sk; 205 206 if (t->opt == TCP_CONGESTION) { 207 char old_cc[16], tmp_cc[16]; 208 const char *new_cc; 209 int new_cc_len; 210 211 if (!bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, 212 (void *)not_exist_cc, sizeof(not_exist_cc))) 213 return 1; 214 if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, old_cc, sizeof(old_cc))) 215 return 1; 216 if (!bpf_strncmp(old_cc, sizeof(old_cc), cubic_cc)) { 217 new_cc = reno_cc; 218 new_cc_len = sizeof(reno_cc); 219 } else { 220 new_cc = cubic_cc; 221 new_cc_len = sizeof(cubic_cc); 222 } 223 if (bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, (void *)new_cc, 224 new_cc_len)) 225 return 1; 226 if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, tmp_cc, sizeof(tmp_cc))) 227 return 1; 228 if (bpf_strncmp(tmp_cc, sizeof(tmp_cc), new_cc)) 229 return 1; 230 if (bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, old_cc, sizeof(old_cc))) 231 return 1; 232 return 0; 233 } 234 235 if (t->flip) 236 return bpf_test_sockopt_flip(ctx, sk, t, IPPROTO_TCP); 237 238 return bpf_test_sockopt_int(ctx, sk, t, IPPROTO_TCP); 239 } 240 241 static int bpf_test_sockopt(void *ctx, struct sock *sk) 242 { 243 struct loop_ctx lc = { .ctx = ctx, .sk = sk, }; 244 __u16 family, proto; 245 int n; 246 247 family = sk->sk_family; 248 proto = sk->sk_protocol; 249 250 n = bpf_loop(ARRAY_SIZE(sol_socket_tests), bpf_test_socket_sockopt, &lc, 0); 251 if (n != ARRAY_SIZE(sol_socket_tests)) 252 return -1; 253 254 if (proto == IPPROTO_TCP) { 255 n = bpf_loop(ARRAY_SIZE(sol_tcp_tests), bpf_test_tcp_sockopt, &lc, 0); 256 if (n != ARRAY_SIZE(sol_tcp_tests)) 257 return -1; 258 } 259 260 if (family == AF_INET) { 261 n = bpf_loop(ARRAY_SIZE(sol_ip_tests), bpf_test_ip_sockopt, &lc, 0); 262 if (n != ARRAY_SIZE(sol_ip_tests)) 263 return -1; 264 } else { 265 n = bpf_loop(ARRAY_SIZE(sol_ipv6_tests), bpf_test_ipv6_sockopt, &lc, 0); 266 if (n != ARRAY_SIZE(sol_ipv6_tests)) 267 return -1; 268 } 269 270 return 0; 271 } 272 273 static int binddev_test(void *ctx) 274 { 275 const char empty_ifname[] = ""; 276 int ifindex, zero = 0; 277 278 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 279 (void *)veth, sizeof(veth))) 280 return -1; 281 if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 282 &ifindex, sizeof(int)) || 283 ifindex != veth_ifindex) 284 return -1; 285 286 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE, 287 (void *)empty_ifname, sizeof(empty_ifname))) 288 return -1; 289 if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 290 &ifindex, sizeof(int)) || 291 ifindex != 0) 292 return -1; 293 294 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 295 (void *)&veth_ifindex, sizeof(int))) 296 return -1; 297 if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 298 &ifindex, sizeof(int)) || 299 ifindex != veth_ifindex) 300 return -1; 301 302 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 303 &zero, sizeof(int))) 304 return -1; 305 if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, 306 &ifindex, sizeof(int)) || 307 ifindex != 0) 308 return -1; 309 310 return 0; 311 } 312 313 static int test_tcp_maxseg(void *ctx, struct sock *sk) 314 { 315 int val = 1314, tmp; 316 317 if (sk->sk_state != TCP_ESTABLISHED) 318 return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG, 319 &val, sizeof(val)); 320 321 if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG, &tmp, sizeof(tmp)) || 322 tmp > val) 323 return -1; 324 325 return 0; 326 } 327 328 static int test_tcp_saved_syn(void *ctx, struct sock *sk) 329 { 330 __u8 saved_syn[20]; 331 int one = 1; 332 333 if (sk->sk_state == TCP_LISTEN) 334 return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_SAVE_SYN, 335 &one, sizeof(one)); 336 337 return bpf_getsockopt(ctx, IPPROTO_TCP, TCP_SAVED_SYN, 338 saved_syn, sizeof(saved_syn)); 339 } 340 341 SEC("lsm_cgroup/socket_post_create") 342 int BPF_PROG(socket_post_create, struct socket *sock, int family, 343 int type, int protocol, int kern) 344 { 345 struct sock *sk = sock->sk; 346 347 if (!sk) 348 return 1; 349 350 nr_socket_post_create += !bpf_test_sockopt(sk, sk); 351 nr_binddev += !binddev_test(sk); 352 353 return 1; 354 } 355 356 SEC("sockops") 357 int skops_sockopt(struct bpf_sock_ops *skops) 358 { 359 struct bpf_sock *bpf_sk = skops->sk; 360 struct sock *sk; 361 362 if (!bpf_sk) 363 return 1; 364 365 sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk); 366 if (!sk) 367 return 1; 368 369 switch (skops->op) { 370 case BPF_SOCK_OPS_TCP_LISTEN_CB: 371 nr_listen += !(bpf_test_sockopt(skops, sk) || 372 test_tcp_maxseg(skops, sk) || 373 test_tcp_saved_syn(skops, sk)); 374 break; 375 case BPF_SOCK_OPS_TCP_CONNECT_CB: 376 nr_connect += !(bpf_test_sockopt(skops, sk) || 377 test_tcp_maxseg(skops, sk)); 378 break; 379 case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: 380 nr_active += !(bpf_test_sockopt(skops, sk) || 381 test_tcp_maxseg(skops, sk)); 382 break; 383 case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: 384 nr_passive += !(bpf_test_sockopt(skops, sk) || 385 test_tcp_maxseg(skops, sk) || 386 test_tcp_saved_syn(skops, sk)); 387 bpf_sock_ops_cb_flags_set(skops, 388 skops->bpf_sock_ops_cb_flags | 389 BPF_SOCK_OPS_STATE_CB_FLAG); 390 break; 391 case BPF_SOCK_OPS_STATE_CB: 392 if (skops->args[1] == BPF_TCP_CLOSE_WAIT) 393 nr_fin_wait1 += !bpf_test_sockopt(skops, sk); 394 break; 395 } 396 397 return 1; 398 } 399 400 char _license[] SEC("license") = "GPL"; 401
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.