linux/tools/testing/selftests/net/reuseport_bpf_numa.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Test functionality of BPF filters with SO_REUSEPORT. Same test as
   4 * in reuseport_bpf_cpu, only as one socket per NUMA node.
   5 */
   6
   7#define _GNU_SOURCE
   8
   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 = sizeof(prog) / sizeof(prog[0]);
  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
 160static
 161void 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                send_from_node(node, family, proto);
 215                receive_on_node(rcv_fd, len, epfd, node, proto);
 216        }
 217
 218        /* Reverse iterate */
 219        for (node = len - 1; node >= 0; --node) {
 220                send_from_node(node, family, proto);
 221                receive_on_node(rcv_fd, len, epfd, node, proto);
 222        }
 223
 224        close(epfd);
 225        for (node = 0; node < len; ++node)
 226                close(rcv_fd[node]);
 227}
 228
 229int main(void)
 230{
 231        int *rcv_fd, nodes;
 232
 233        if (numa_available() < 0)
 234                ksft_exit_skip("no numa api support\n");
 235
 236        nodes = numa_max_node() + 1;
 237
 238        rcv_fd = calloc(nodes, sizeof(int));
 239        if (!rcv_fd)
 240                error(1, 0, "failed to allocate array");
 241
 242        fprintf(stderr, "---- IPv4 UDP ----\n");
 243        test(rcv_fd, nodes, AF_INET, SOCK_DGRAM);
 244
 245        fprintf(stderr, "---- IPv6 UDP ----\n");
 246        test(rcv_fd, nodes, AF_INET6, SOCK_DGRAM);
 247
 248        fprintf(stderr, "---- IPv4 TCP ----\n");
 249        test(rcv_fd, nodes, AF_INET, SOCK_STREAM);
 250
 251        fprintf(stderr, "---- IPv6 TCP ----\n");
 252        test(rcv_fd, nodes, AF_INET6, SOCK_STREAM);
 253
 254        free(rcv_fd);
 255
 256        fprintf(stderr, "SUCCESS\n");
 257        return 0;
 258}
 259