1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Isovalent */ 3 #include <uapi/linux/if_link.h> 4 #include <test_progs.h> 5 6 #include <netinet/tcp.h> 7 #include <netinet/udp.h> 8 9 #include "network_helpers.h" 10 #include "test_assign_reuse.skel.h" 11 12 #define NS_TEST "assign_reuse" 13 #define LOOPBACK 1 14 #define PORT 4443 15 16 static int attach_reuseport(int sock_fd, int prog_fd) 17 { 18 return setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, 19 &prog_fd, sizeof(prog_fd)); 20 } 21 22 static __u64 cookie(int fd) 23 { 24 __u64 cookie = 0; 25 socklen_t cookie_len = sizeof(cookie); 26 int ret; 27 28 ret = getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len); 29 ASSERT_OK(ret, "cookie"); 30 ASSERT_GT(cookie, 0, "cookie_invalid"); 31 32 return cookie; 33 } 34 35 static int echo_test_udp(int fd_sv) 36 { 37 struct sockaddr_storage addr = {}; 38 socklen_t len = sizeof(addr); 39 char buff[1] = {}; 40 int fd_cl = -1, ret; 41 42 fd_cl = connect_to_fd(fd_sv, 100); 43 ASSERT_GT(fd_cl, 0, "create_client"); 44 ASSERT_EQ(getsockname(fd_cl, (void *)&addr, &len), 0, "getsockname"); 45 46 ASSERT_EQ(send(fd_cl, buff, sizeof(buff), 0), 1, "send_client"); 47 48 ret = recv(fd_sv, buff, sizeof(buff), 0); 49 if (ret < 0) { 50 close(fd_cl); 51 return errno; 52 } 53 54 ASSERT_EQ(ret, 1, "recv_server"); 55 ASSERT_EQ(sendto(fd_sv, buff, sizeof(buff), 0, (void *)&addr, len), 1, "send_server"); 56 ASSERT_EQ(recv(fd_cl, buff, sizeof(buff), 0), 1, "recv_client"); 57 close(fd_cl); 58 return 0; 59 } 60 61 static int echo_test_tcp(int fd_sv) 62 { 63 char buff[1] = {}; 64 int fd_cl = -1, fd_sv_cl = -1; 65 66 fd_cl = connect_to_fd(fd_sv, 100); 67 if (fd_cl < 0) 68 return errno; 69 70 fd_sv_cl = accept(fd_sv, NULL, NULL); 71 ASSERT_GE(fd_sv_cl, 0, "accept_fd"); 72 73 ASSERT_EQ(send(fd_cl, buff, sizeof(buff), 0), 1, "send_client"); 74 ASSERT_EQ(recv(fd_sv_cl, buff, sizeof(buff), 0), 1, "recv_server"); 75 ASSERT_EQ(send(fd_sv_cl, buff, sizeof(buff), 0), 1, "send_server"); 76 ASSERT_EQ(recv(fd_cl, buff, sizeof(buff), 0), 1, "recv_client"); 77 close(fd_sv_cl); 78 close(fd_cl); 79 return 0; 80 } 81 82 void run_assign_reuse(int family, int sotype, const char *ip, __u16 port) 83 { 84 DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, 85 .ifindex = LOOPBACK, 86 .attach_point = BPF_TC_INGRESS, 87 ); 88 DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, 89 .handle = 1, 90 .priority = 1, 91 ); 92 bool hook_created = false, tc_attached = false; 93 int ret, fd_tc, fd_accept, fd_drop, fd_map; 94 int *fd_sv = NULL; 95 __u64 fd_val; 96 struct test_assign_reuse *skel; 97 const int zero = 0; 98 99 skel = test_assign_reuse__open(); 100 if (!ASSERT_OK_PTR(skel, "skel_open")) 101 goto cleanup; 102 103 skel->rodata->dest_port = port; 104 105 ret = test_assign_reuse__load(skel); 106 if (!ASSERT_OK(ret, "skel_load")) 107 goto cleanup; 108 109 ASSERT_EQ(skel->bss->sk_cookie_seen, 0, "cookie_init"); 110 111 fd_tc = bpf_program__fd(skel->progs.tc_main); 112 fd_accept = bpf_program__fd(skel->progs.reuse_accept); 113 fd_drop = bpf_program__fd(skel->progs.reuse_drop); 114 fd_map = bpf_map__fd(skel->maps.sk_map); 115 116 fd_sv = start_reuseport_server(family, sotype, ip, port, 100, 1); 117 if (!ASSERT_NEQ(fd_sv, NULL, "start_reuseport_server")) 118 goto cleanup; 119 120 ret = attach_reuseport(*fd_sv, fd_drop); 121 if (!ASSERT_OK(ret, "attach_reuseport")) 122 goto cleanup; 123 124 fd_val = *fd_sv; 125 ret = bpf_map_update_elem(fd_map, &zero, &fd_val, BPF_NOEXIST); 126 if (!ASSERT_OK(ret, "bpf_sk_map")) 127 goto cleanup; 128 129 ret = bpf_tc_hook_create(&tc_hook); 130 if (ret == 0) 131 hook_created = true; 132 ret = ret == -EEXIST ? 0 : ret; 133 if (!ASSERT_OK(ret, "bpf_tc_hook_create")) 134 goto cleanup; 135 136 tc_opts.prog_fd = fd_tc; 137 ret = bpf_tc_attach(&tc_hook, &tc_opts); 138 if (!ASSERT_OK(ret, "bpf_tc_attach")) 139 goto cleanup; 140 tc_attached = true; 141 142 if (sotype == SOCK_STREAM) 143 ASSERT_EQ(echo_test_tcp(*fd_sv), ECONNREFUSED, "drop_tcp"); 144 else 145 ASSERT_EQ(echo_test_udp(*fd_sv), EAGAIN, "drop_udp"); 146 ASSERT_EQ(skel->bss->reuseport_executed, 1, "program executed once"); 147 148 skel->bss->sk_cookie_seen = 0; 149 skel->bss->reuseport_executed = 0; 150 ASSERT_OK(attach_reuseport(*fd_sv, fd_accept), "attach_reuseport(accept)"); 151 152 if (sotype == SOCK_STREAM) 153 ASSERT_EQ(echo_test_tcp(*fd_sv), 0, "echo_tcp"); 154 else 155 ASSERT_EQ(echo_test_udp(*fd_sv), 0, "echo_udp"); 156 157 ASSERT_EQ(skel->bss->sk_cookie_seen, cookie(*fd_sv), 158 "cookie_mismatch"); 159 ASSERT_EQ(skel->bss->reuseport_executed, 1, "program executed once"); 160 cleanup: 161 if (tc_attached) { 162 tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; 163 ret = bpf_tc_detach(&tc_hook, &tc_opts); 164 ASSERT_OK(ret, "bpf_tc_detach"); 165 } 166 if (hook_created) { 167 tc_hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS; 168 bpf_tc_hook_destroy(&tc_hook); 169 } 170 test_assign_reuse__destroy(skel); 171 free_fds(fd_sv, 1); 172 } 173 174 void test_assign_reuse(void) 175 { 176 struct nstoken *tok = NULL; 177 178 SYS(out, "ip netns add %s", NS_TEST); 179 SYS(cleanup, "ip -net %s link set dev lo up", NS_TEST); 180 181 tok = open_netns(NS_TEST); 182 if (!ASSERT_OK_PTR(tok, "netns token")) 183 return; 184 185 if (test__start_subtest("tcpv4")) 186 run_assign_reuse(AF_INET, SOCK_STREAM, "127.0.0.1", PORT); 187 if (test__start_subtest("tcpv6")) 188 run_assign_reuse(AF_INET6, SOCK_STREAM, "::1", PORT); 189 if (test__start_subtest("udpv4")) 190 run_assign_reuse(AF_INET, SOCK_DGRAM, "127.0.0.1", PORT); 191 if (test__start_subtest("udpv6")) 192 run_assign_reuse(AF_INET6, SOCK_DGRAM, "::1", PORT); 193 194 cleanup: 195 close_netns(tok); 196 SYS_NOFAIL("ip netns delete %s", NS_TEST); 197 out: 198 return; 199 } 200