/* * Test functionality of BPF filters for SO_REUSEPORT. The tests below will use * a BPF program (both classic and extended) to read the first word from an * incoming packet (expected to be in network byte-order), calculate a modulus * of that number, and then dispatch the packet to the Nth socket using the * result. These tests are run for each supported address family and protocol. * Additionally, a few edge cases in the implementation are tested.
*/
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, sizeof(bpf_fd)))
error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF");
close(bpf_fd);
}
staticvoid attach_cbpf(int fd, uint16_t mod)
{ struct sock_filter code[] = { /* A = (uint32_t)skb[0] */
{ BPF_LD | BPF_W | BPF_ABS, 0, 0, 0 }, /* A = A % mod */
{ BPF_ALU | BPF_MOD, 0, 0, mod }, /* return A */
{ BPF_RET | BPF_A, 0, 0, 0 },
}; struct sock_fprog p = {
.len = ARRAY_SIZE(code),
.filter = code,
};
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p)))
error(1, errno, "failed to set SO_ATTACH_REUSEPORT_CBPF");
}
staticvoid build_recv_group(conststruct test_params p, int fd[], uint16_t mod, void (*attach_bpf)(int, uint16_t))
{ struct sockaddr * const addr =
new_any_sockaddr(p.recv_family, p.recv_port); int i, opt;
for (i = 0; i < p.recv_socks; ++i) {
fd[i] = socket(p.recv_family, p.protocol, 0); if (fd[i] < 0)
error(1, errno, "failed to create recv %d", i);
opt = 1; if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT on %d", i);
if (i == 0)
attach_bpf(fd[i], mod);
if (bind(fd[i], addr, sockaddr_size()))
error(1, errno, "failed to bind recv socket %d", i);
if (p.protocol == SOCK_STREAM) {
opt = 4; if (setsockopt(fd[i], SOL_TCP, TCP_FASTOPEN, &opt, sizeof(opt)))
error(1, errno, "failed to set TCP_FASTOPEN on %d", i); if (listen(fd[i], p.recv_socks * 10))
error(1, errno, "failed to listen on socket");
}
}
free(addr);
}
for (data = 0; data < p.recv_socks * 2; ++data) {
sport = p.send_port_min + data;
ndata = htonl(data);
memcpy(send_buf, &ndata, sizeof(ndata));
send_from(p, sport, send_buf, sizeof(ndata));
i = epoll_wait(epfd, &ev, 1, -1); if (i < 0)
error(1, errno, "epoll wait failed");
if (p.protocol == SOCK_STREAM) {
conn = accept(ev.data.fd, NULL, NULL); if (conn < 0)
error(1, errno, "error accepting");
i = recvmsg(conn, &msg, 0);
close(conn);
} else {
i = recvmsg(ev.data.fd, &msg, 0);
} if (i < 0)
error(1, errno, "recvmsg error"); if (i != sizeof(ndata))
error(1, 0, "expected size %zd got %d", sizeof(ndata), i);
for (i = 0; i < p.recv_socks; ++i) if (ev.data.fd == fd[i]) break;
memcpy(&ndata, recv_buf, sizeof(ndata));
fprintf(stderr, "Socket %d: %d\n", i, ntohl(ndata));
expected = (sport % mod); if (i != expected)
error(1, 0, "expected socket %d", expected);
}
}
staticvoid test_reuseport_ebpf(struct test_params p)
{ int i, fd[p.recv_socks];
fprintf(stderr, "Testing too many filters...\n");
fd1 = socket(p.recv_family, p.protocol, 0); if (fd1 < 0)
error(1, errno, "failed to create socket 1");
fd2 = socket(p.recv_family, p.protocol, 0); if (fd2 < 0)
error(1, errno, "failed to create socket 2");
opt = 1; if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT on socket 1"); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT on socket 2");
attach_ebpf(fd1, 10);
attach_ebpf(fd2, 10);
if (bind(fd1, addr, sockaddr_size()))
error(1, errno, "failed to bind recv socket 1");
if (!bind(fd2, addr, sockaddr_size()) || errno != EADDRINUSE)
error(1, errno, "bind socket 2 should fail with EADDRINUSE");
bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &eprog, sizeof(eprog)); if (bpf_fd < 0)
error(1, errno, "ebpf error");
fd = socket(p.recv_family, p.protocol, 0); if (fd < 0)
error(1, errno, "failed to create socket 1");
if (bind(fd, addr, sockaddr_size()))
error(1, errno, "failed to bind recv socket 1");
errno = 0; if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd, sizeof(bpf_fd)) || errno != EINVAL)
error(1, errno, "setsockopt should have returned EINVAL");
errno = 0; if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &cprog, sizeof(cprog)) || errno != EINVAL)
error(1, errno, "setsockopt should have returned EINVAL");
free(addr);
}
staticvoid test_filter_without_bind(void)
{ int fd1, fd2, opt = 1;
fprintf(stderr, "Testing filter add without bind...\n");
fd1 = socket(AF_INET, SOCK_DGRAM, 0); if (fd1 < 0)
error(1, errno, "failed to create socket 1");
fd2 = socket(AF_INET, SOCK_DGRAM, 0); if (fd2 < 0)
error(1, errno, "failed to create socket 2"); if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT on socket 1"); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT on socket 2");
attach_ebpf(fd1, 10);
attach_cbpf(fd2, 10);
close(fd1);
close(fd2);
}
void enable_fastopen(void)
{ int fd = open("/proc/sys/net/ipv4/tcp_fastopen", 0); int rw_mask = 3; /* bit 1: client side; bit-2 server side */ int val, size; char buf[16];
if (fd < 0)
error(1, errno, "Unable to open tcp_fastopen sysctl"); if (read(fd, buf, sizeof(buf)) <= 0)
error(1, errno, "Unable to read tcp_fastopen sysctl");
val = atoi(buf);
close(fd);
if ((val & rw_mask) != rw_mask) {
fd = open("/proc/sys/net/ipv4/tcp_fastopen", O_RDWR); if (fd < 0)
error(1, errno, "Unable to open tcp_fastopen sysctl for writing");
val |= rw_mask;
size = snprintf(buf, 16, "%d", val); if (write(fd, buf, size) <= 0)
error(1, errno, "Unable to write tcp_fastopen sysctl");
close(fd);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.