263 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test functionality of BPF filters with SO_REUSEPORT. Same test as4 * in reuseport_bpf_cpu, only as one socket per NUMA node.5 */6 7#define _GNU_SOURCE8 9#include <arpa/inet.h>10#include <errno.h>11#include <error.h>12#include <linux/filter.h>13#include <linux/bpf.h>14#include <linux/in.h>15#include <linux/unistd.h>16#include <sched.h>17#include <stdio.h>18#include <stdlib.h>19#include <string.h>20#include <sys/epoll.h>21#include <sys/types.h>22#include <sys/socket.h>23#include <unistd.h>24#include <numa.h>25 26#include "../kselftest.h"27 28static const int PORT = 8888;29 30static void build_rcv_group(int *rcv_fd, size_t len, int family, int proto)31{32 struct sockaddr_storage addr;33 struct sockaddr_in *addr4;34 struct sockaddr_in6 *addr6;35 size_t i;36 int opt;37 38 switch (family) {39 case AF_INET:40 addr4 = (struct sockaddr_in *)&addr;41 addr4->sin_family = AF_INET;42 addr4->sin_addr.s_addr = htonl(INADDR_ANY);43 addr4->sin_port = htons(PORT);44 break;45 case AF_INET6:46 addr6 = (struct sockaddr_in6 *)&addr;47 addr6->sin6_family = AF_INET6;48 addr6->sin6_addr = in6addr_any;49 addr6->sin6_port = htons(PORT);50 break;51 default:52 error(1, 0, "Unsupported family %d", family);53 }54 55 for (i = 0; i < len; ++i) {56 rcv_fd[i] = socket(family, proto, 0);57 if (rcv_fd[i] < 0)58 error(1, errno, "failed to create receive socket");59 60 opt = 1;61 if (setsockopt(rcv_fd[i], SOL_SOCKET, SO_REUSEPORT, &opt,62 sizeof(opt)))63 error(1, errno, "failed to set SO_REUSEPORT");64 65 if (bind(rcv_fd[i], (struct sockaddr *)&addr, sizeof(addr)))66 error(1, errno, "failed to bind receive socket");67 68 if (proto == SOCK_STREAM && listen(rcv_fd[i], len * 10))69 error(1, errno, "failed to listen on receive port");70 }71}72 73static void attach_bpf(int fd)74{75 static char bpf_log_buf[65536];76 static const char bpf_license[] = "";77 78 int bpf_fd;79 const struct bpf_insn prog[] = {80 /* R0 = bpf_get_numa_node_id() */81 { BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_numa_node_id },82 /* return R0 */83 { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }84 };85 union bpf_attr attr;86 87 memset(&attr, 0, sizeof(attr));88 attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;89 attr.insn_cnt = ARRAY_SIZE(prog);90 attr.insns = (unsigned long) &prog;91 attr.license = (unsigned long) &bpf_license;92 attr.log_buf = (unsigned long) &bpf_log_buf;93 attr.log_size = sizeof(bpf_log_buf);94 attr.log_level = 1;95 96 bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));97 if (bpf_fd < 0)98 error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf);99 100 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd,101 sizeof(bpf_fd)))102 error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF");103 104 close(bpf_fd);105}106 107static void send_from_node(int node_id, int family, int proto)108{109 struct sockaddr_storage saddr, daddr;110 struct sockaddr_in *saddr4, *daddr4;111 struct sockaddr_in6 *saddr6, *daddr6;112 int fd;113 114 switch (family) {115 case AF_INET:116 saddr4 = (struct sockaddr_in *)&saddr;117 saddr4->sin_family = AF_INET;118 saddr4->sin_addr.s_addr = htonl(INADDR_ANY);119 saddr4->sin_port = 0;120 121 daddr4 = (struct sockaddr_in *)&daddr;122 daddr4->sin_family = AF_INET;123 daddr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);124 daddr4->sin_port = htons(PORT);125 break;126 case AF_INET6:127 saddr6 = (struct sockaddr_in6 *)&saddr;128 saddr6->sin6_family = AF_INET6;129 saddr6->sin6_addr = in6addr_any;130 saddr6->sin6_port = 0;131 132 daddr6 = (struct sockaddr_in6 *)&daddr;133 daddr6->sin6_family = AF_INET6;134 daddr6->sin6_addr = in6addr_loopback;135 daddr6->sin6_port = htons(PORT);136 break;137 default:138 error(1, 0, "Unsupported family %d", family);139 }140 141 if (numa_run_on_node(node_id) < 0)142 error(1, errno, "failed to pin to node");143 144 fd = socket(family, proto, 0);145 if (fd < 0)146 error(1, errno, "failed to create send socket");147 148 if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)))149 error(1, errno, "failed to bind send socket");150 151 if (connect(fd, (struct sockaddr *)&daddr, sizeof(daddr)))152 error(1, errno, "failed to connect send socket");153 154 if (send(fd, "a", 1, 0) < 0)155 error(1, errno, "failed to send message");156 157 close(fd);158}159 160static161void receive_on_node(int *rcv_fd, int len, int epfd, int node_id, int proto)162{163 struct epoll_event ev;164 int i, fd;165 char buf[8];166 167 i = epoll_wait(epfd, &ev, 1, -1);168 if (i < 0)169 error(1, errno, "epoll_wait failed");170 171 if (proto == SOCK_STREAM) {172 fd = accept(ev.data.fd, NULL, NULL);173 if (fd < 0)174 error(1, errno, "failed to accept");175 i = recv(fd, buf, sizeof(buf), 0);176 close(fd);177 } else {178 i = recv(ev.data.fd, buf, sizeof(buf), 0);179 }180 181 if (i < 0)182 error(1, errno, "failed to recv");183 184 for (i = 0; i < len; ++i)185 if (ev.data.fd == rcv_fd[i])186 break;187 if (i == len)188 error(1, 0, "failed to find socket");189 fprintf(stderr, "send node %d, receive socket %d\n", node_id, i);190 if (node_id != i)191 error(1, 0, "node id/receive socket mismatch");192}193 194static void test(int *rcv_fd, int len, int family, int proto)195{196 struct epoll_event ev;197 int epfd, node;198 199 build_rcv_group(rcv_fd, len, family, proto);200 attach_bpf(rcv_fd[0]);201 202 epfd = epoll_create(1);203 if (epfd < 0)204 error(1, errno, "failed to create epoll");205 for (node = 0; node < len; ++node) {206 ev.events = EPOLLIN;207 ev.data.fd = rcv_fd[node];208 if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fd[node], &ev))209 error(1, errno, "failed to register sock epoll");210 }211 212 /* Forward iterate */213 for (node = 0; node < len; ++node) {214 if (!numa_bitmask_isbitset(numa_nodes_ptr, node))215 continue;216 send_from_node(node, family, proto);217 receive_on_node(rcv_fd, len, epfd, node, proto);218 }219 220 /* Reverse iterate */221 for (node = len - 1; node >= 0; --node) {222 if (!numa_bitmask_isbitset(numa_nodes_ptr, node))223 continue;224 send_from_node(node, family, proto);225 receive_on_node(rcv_fd, len, epfd, node, proto);226 }227 228 close(epfd);229 for (node = 0; node < len; ++node)230 close(rcv_fd[node]);231}232 233int main(void)234{235 int *rcv_fd, nodes;236 237 if (numa_available() < 0)238 ksft_exit_skip("no numa api support\n");239 240 nodes = numa_max_node() + 1;241 242 rcv_fd = calloc(nodes, sizeof(int));243 if (!rcv_fd)244 error(1, 0, "failed to allocate array");245 246 fprintf(stderr, "---- IPv4 UDP ----\n");247 test(rcv_fd, nodes, AF_INET, SOCK_DGRAM);248 249 fprintf(stderr, "---- IPv6 UDP ----\n");250 test(rcv_fd, nodes, AF_INET6, SOCK_DGRAM);251 252 fprintf(stderr, "---- IPv4 TCP ----\n");253 test(rcv_fd, nodes, AF_INET, SOCK_STREAM);254 255 fprintf(stderr, "---- IPv6 TCP ----\n");256 test(rcv_fd, nodes, AF_INET6, SOCK_STREAM);257 258 free(rcv_fd);259 260 fprintf(stderr, "SUCCESS\n");261 return 0;262}263