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