200 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 114#define PORT 444315 16static 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 22static __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 35static 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 61static 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 82void 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 else145 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 else155 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");160cleanup: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 174void 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 194cleanup:195 close_netns(tok);196 SYS_NOFAIL("ip netns delete %s", NS_TEST);197out:198 return;199}200