From b0aa73bf081da6810dacd750b9f8186640e172db Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 14:49:44 -0400 Subject: net: Add socket() system call self test. Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-socket/Makefile | 16 ++++ .../testing/selftests/net-socket/run_netsocktests | 12 +++ tools/testing/selftests/net-socket/socket.c | 92 ++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 tools/testing/selftests/net-socket/Makefile create mode 100644 tools/testing/selftests/net-socket/run_netsocktests create mode 100644 tools/testing/selftests/net-socket/socket.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3cc0ad7ae863..7c6280f1cf4d 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,6 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs +TARGETS += net-socket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile new file mode 100644 index 000000000000..f27ee10da530 --- /dev/null +++ b/tools/testing/selftests/net-socket/Makefile @@ -0,0 +1,16 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +NET_SOCK_PROGS = socket + +all: $(NET_SOCK_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + +clean: + $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests new file mode 100644 index 000000000000..c09a682df56a --- /dev/null +++ b/tools/testing/selftests/net-socket/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c new file mode 100644 index 000000000000..0f227f2f9be9 --- /dev/null +++ b/tools/testing/selftests/net-socket/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3 From 77f65ebdca506870d99bfabe52bde222511022ec Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 10:18:11 +0000 Subject: packet: packet fanout rollover during socket overload Changes: v3->v2: rebase (no other changes) passes selftest v2->v1: read f->num_members only once fix bug: test rollover mode + flag Minimize packet drop in a fanout group. If one socket is full, roll over packets to another from the group. Maintain flow affinity during normal load using an rxhash fanout policy, while dispersing unexpected traffic storms that hit a single cpu, such as spoofed-source DoS flows. Rollover breaks affinity for flows arriving at saturated sockets during those conditions. The patch adds a fanout policy ROLLOVER that rotates between sockets, filling each socket before moving to the next. It also adds a fanout flag ROLLOVER. If passed along with any other fanout policy, the primary policy is applied until the chosen socket is full. Then, rollover selects another socket, to delay packet drop until the entire system is saturated. Probing sockets is not free. Selecting the last used socket, as rollover does, is a greedy approach that maximizes chance of success, at the cost of extreme load imbalance. In practice, with sufficiently long queues to absorb bursts, sockets are drained in parallel and load balance looks uniform in `top`. To avoid contention, scales counters with number of sockets and accesses them lockfree. Values are bounds checked to ensure correctness. Tested using an application with 9 threads pinned to CPUs, one socket per thread and sufficient busywork per packet operation to limits each thread to handling 32 Kpps. When sent 500 Kpps single UDP stream packets, a FANOUT_CPU setup processes 32 Kpps in total without this patch, 270 Kpps with the patch. Tested with read() and with a packet ring (V1). Also, passes psock_fanout.c unit test added to selftests. Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-afpacket/Makefile | 18 ++ .../testing/selftests/net-afpacket/psock_fanout.c | 326 +++++++++++++++++++++ .../selftests/net-afpacket/run_afpackettests | 16 + 4 files changed, 361 insertions(+) create mode 100644 tools/testing/selftests/net-afpacket/Makefile create mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c create mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7c6280f1cf4d..7f50078d0e8c 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs TARGETS += net-socket +TARGETS += net-afpacket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile new file mode 100644 index 000000000000..45f2ffb7fda7 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/Makefile @@ -0,0 +1,18 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +AF_PACKET_PROGS = psock_fanout + +all: $(AF_PACKET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c new file mode 100644 index 000000000000..09dbf93c53d4 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -0,0 +1,326 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - datapath: PACKET_FANOUT_LB + * - datapath: PACKET_FANOUT_CPU + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hack: build even if local includes are old */ +#ifndef PACKET_FANOUT +#define PACKET_FANOUT 18 +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +#ifndef PACKET_FANOUT_ROLLOVER +#define PACKET_FANOUT_ROLLOVER 3 +#endif + +#ifndef PACKET_FANOUT_FLAG_ROLLOVER +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#endif + +#endif + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; + val *= num_packets; + /* hack: apparently, the above calculation is too small (TODO: fix) */ + val *= 3; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { + perror("setsockopt SO_RCVBUF"); + exit(1); + } + + sock_fanout_setfilter(fd); + return fd; +} + +static void sock_fanout_read(int fds[], const int expect[]) +{ + struct tpacket_stats stats; + socklen_t ssize; + int ret[2]; + + ssize = sizeof(stats); + if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 0"); + exit(1); + } + ret[0] = stats.tp_packets - stats.tp_drops; + ssize = sizeof(stats); + if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 1"); + exit(1); + } + ret[1] = stats.tp_packets - stats.tp_drops; + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + exit(1); + } +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static void test_datapath(uint16_t typeflags, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + int fds[2], fds_udp[2][2]; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + pair_udp_open(fds_udp[0], 8000); + pair_udp_open(fds_udp[1], 8002); + sock_fanout_read(fds, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + sock_fanout_read(fds, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + sock_fanout_read(fds, expect2); + + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + + test_control_single(); + test_control_group(); + + test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); + test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + expect_hash_rb[0], expect_hash_rb[1]); + test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests new file mode 100644 index 000000000000..7907824c6355 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3 From 947124460d0c47d465b5846946d3f2b5cee6026e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 17:05:50 -0400 Subject: net: Fix failure string in net-socket selftests Makefile. Signed-off-by: David S. Miller --- tools/testing/selftests/net-socket/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile index f27ee10da530..2450fd8bb10a 100644 --- a/tools/testing/selftests/net-socket/Makefile +++ b/tools/testing/selftests/net-socket/Makefile @@ -10,7 +10,7 @@ all: $(NET_SOCK_PROGS) $(CC) $(CFLAGS) -o $@ $^ run_tests: all - @/bin/sh ./run_netsocktests || echo "vmtests: [FAIL]" + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" clean: $(RM) $(NET_SOCK_PROGS) -- cgit v1.2.3 From b44540ea024b9280c9be9d055f084e0956bcfa44 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Mar 2013 18:08:45 -0400 Subject: net: Get rid of compat defines in psock_fanout.c selftest. Reported-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index 09dbf93c53d4..af9b0196b0be 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -56,24 +56,6 @@ #include #include -/* Hack: build even if local includes are old */ -#ifndef PACKET_FANOUT -#define PACKET_FANOUT 18 -#define PACKET_FANOUT_HASH 0 -#define PACKET_FANOUT_LB 1 -#define PACKET_FANOUT_CPU 2 -#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 - -#ifndef PACKET_FANOUT_ROLLOVER -#define PACKET_FANOUT_ROLLOVER 3 -#endif - -#ifndef PACKET_FANOUT_FLAG_ROLLOVER -#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 -#endif - -#endif - #define DATA_LEN 100 #define DATA_CHAR 'a' -- cgit v1.2.3 From 23a9072e3af0d9538e25837fb2b56bb94e4a8e67 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 20:42:44 +0000 Subject: net: fix psock_fanout selftest hash collision Fix flaky results with PACKET_FANOUT_HASH depending on whether the two flows hash into the same packet socket or not. Also adds tests for PACKET_FANOUT_LB and PACKET_FANOUT_CPU and replaces the counting method with a packet ring. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- .../testing/selftests/net-afpacket/psock_fanout.c | 160 +++++++++++++++------ 1 file changed, 120 insertions(+), 40 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index af9b0196b0be..f765f09931bd 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -16,11 +16,11 @@ * The test currently runs for * - PACKET_FANOUT_HASH * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU * - PACKET_FANOUT_ROLLOVER * * Todo: - * - datapath: PACKET_FANOUT_LB - * - datapath: PACKET_FANOUT_CPU * - functionality: PACKET_FANOUT_FLAG_DEFRAG * * License (GPLv2): @@ -39,18 +39,23 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE /* for sched_setaffinity */ + #include #include +#include #include #include #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -58,6 +63,8 @@ #define DATA_LEN 100 #define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 static void pair_udp_open(int fds[], uint16_t port) { @@ -162,37 +169,55 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets) return -1; } - val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; - val *= num_packets; - /* hack: apparently, the above calculation is too small (TODO: fix) */ - val *= 3; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { - perror("setsockopt SO_RCVBUF"); - exit(1); - } - sock_fanout_setfilter(fd); return fd; } -static void sock_fanout_read(int fds[], const int expect[]) +static char *sock_fanout_open_ring(int fd) { - struct tpacket_stats stats; - socklen_t ssize; - int ret[2]; + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; - ssize = sizeof(stats); - if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 0"); + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); exit(1); } - ret[0] = stats.tp_packets - stats.tp_drops; - ssize = sizeof(stats); - if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { - perror("getsockopt statistics 1"); + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); exit(1); } - ret[1] = stats.tp_packets - stats.tp_drops; + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", ret[0], ret[1], expect[0], expect[1]); @@ -200,8 +225,10 @@ static void sock_fanout_read(int fds[], const int expect[]) if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && (!(ret[0] == expect[1] && ret[1] == expect[0]))) { fprintf(stderr, "ERROR: incorrect queue lengths\n"); - exit(1); + return 1; } + + return 0; } /* Test illegal mode + flag combination */ @@ -253,11 +280,12 @@ static void test_control_group(void) } } -static void test_datapath(uint16_t typeflags, - const int expect1[], const int expect2[]) +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) { const int expect0[] = { 0, 0 }; - int fds[2], fds_udp[2][2]; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; fprintf(stderr, "test: datapath 0x%hx\n", typeflags); @@ -267,41 +295,93 @@ static void test_datapath(uint16_t typeflags, fprintf(stderr, "ERROR: failed open\n"); exit(1); } - pair_udp_open(fds_udp[0], 8000); - pair_udp_open(fds_udp[1], 8002); - sock_fanout_read(fds, expect0); + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); /* Send data, but not enough to overflow a queue */ pair_udp_send(fds_udp[0], 15); pair_udp_send(fds_udp[1], 5); - sock_fanout_read(fds, expect1); + ret = sock_fanout_read(fds, rings, expect1); /* Send more data, overflow the queue */ pair_udp_send(fds_udp[0], 15); /* TODO: ensure consistent order between expect1 and expect2 */ - sock_fanout_read(fds, expect2); + ret |= sock_fanout_read(fds, rings, expect2); + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || close(fds_udp[0][1]) || close(fds_udp[0][0]) || close(fds[1]) || close(fds[0])) { fprintf(stderr, "close datapath\n"); exit(1); } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; } int main(int argc, char **argv) { - const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; test_control_single(); test_control_group(); - test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); - test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - expect_hash_rb[0], expect_hash_rb[1]); - test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; printf("OK. All tests passed\n"); return 0; -- cgit v1.2.3 From 4c1d8d0617a39c8325a7c2fd80ac14bf40fd8cc6 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Wed, 20 Mar 2013 19:28:56 +0200 Subject: net: fix psock_fanout selftest bind error message Signed-off-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/net-afpacket/psock_fanout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c index f765f09931bd..226e5e33105a 100644 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -97,7 +97,7 @@ static void pair_udp_open(int fds[], uint16_t port) exit(1); } if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("bind"); + perror("connect"); exit(1); } } -- cgit v1.2.3 From a6f68034de8a5784dfeabb337506254c80b4c8c6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 20 Mar 2013 15:07:56 -0400 Subject: net: Move selftests to common net/ subdirectory. Suggested-by: Daniel Baluta Signed-off-by: David S. Miller --- tools/testing/selftests/Makefile | 3 +- tools/testing/selftests/net-afpacket/Makefile | 18 - .../testing/selftests/net-afpacket/psock_fanout.c | 388 --------------------- .../selftests/net-afpacket/run_afpackettests | 16 - tools/testing/selftests/net-socket/Makefile | 16 - .../testing/selftests/net-socket/run_netsocktests | 12 - tools/testing/selftests/net-socket/socket.c | 92 ----- tools/testing/selftests/net/Makefile | 19 + tools/testing/selftests/net/psock_fanout.c | 388 +++++++++++++++++++++ tools/testing/selftests/net/run_afpackettests | 16 + tools/testing/selftests/net/run_netsocktests | 12 + tools/testing/selftests/net/socket.c | 92 +++++ 12 files changed, 528 insertions(+), 544 deletions(-) delete mode 100644 tools/testing/selftests/net-afpacket/Makefile delete mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c delete mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests delete mode 100644 tools/testing/selftests/net-socket/Makefile delete mode 100644 tools/testing/selftests/net-socket/run_netsocktests delete mode 100644 tools/testing/selftests/net-socket/socket.c create mode 100644 tools/testing/selftests/net/Makefile create mode 100644 tools/testing/selftests/net/psock_fanout.c create mode 100644 tools/testing/selftests/net/run_afpackettests create mode 100644 tools/testing/selftests/net/run_netsocktests create mode 100644 tools/testing/selftests/net/socket.c (limited to 'tools') diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7f50078d0e8c..a4805932972b 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -5,8 +5,7 @@ TARGETS += vm TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs -TARGETS += net-socket -TARGETS += net-afpacket +TARGETS += net all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile deleted file mode 100644 index 45f2ffb7fda7..000000000000 --- a/tools/testing/selftests/net-afpacket/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -CFLAGS += -I../../../../usr/include/ - -AF_PACKET_PROGS = psock_fanout - -all: $(AF_PACKET_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" - -clean: - $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c deleted file mode 100644 index 226e5e33105a..000000000000 --- a/tools/testing/selftests/net-afpacket/psock_fanout.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Author: Willem de Bruijn (willemb@google.com) - * - * A basic test of packet socket fanout behavior. - * - * Control: - * - create fanout fails as expected with illegal flag combinations - * - join fanout fails as expected with diverging types or flags - * - * Datapath: - * Open a pair of packet sockets and a pair of INET sockets, send a known - * number of packets across the two INET sockets and count the number of - * packets enqueued onto the two packet sockets. - * - * The test currently runs for - * - PACKET_FANOUT_HASH - * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER - * - PACKET_FANOUT_LB - * - PACKET_FANOUT_CPU - * - PACKET_FANOUT_ROLLOVER - * - * Todo: - * - functionality: PACKET_FANOUT_FLAG_DEFRAG - * - * License (GPLv2): - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE /* for sched_setaffinity */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DATA_LEN 100 -#define DATA_CHAR 'a' -#define RING_NUM_FRAMES 20 -#define PORT_BASE 8000 - -static void pair_udp_open(int fds[], uint16_t port) -{ - struct sockaddr_in saddr, daddr; - - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: socket dgram\n"); - exit(1); - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&daddr, 0, sizeof(daddr)); - daddr.sin_family = AF_INET; - daddr.sin_port = htons(port + 1); - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - /* must bind both to get consistent hash result */ - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { - perror("bind"); - exit(1); - } - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { - perror("bind"); - exit(1); - } - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { - perror("connect"); - exit(1); - } -} - -static void pair_udp_send(int fds[], int num) -{ - char buf[DATA_LEN], rbuf[DATA_LEN]; - - memset(buf, DATA_CHAR, sizeof(buf)); - while (num--) { - /* Should really handle EINTR and EAGAIN */ - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { - fprintf(stderr, "ERROR: send failed left=%d\n", num); - exit(1); - } - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { - fprintf(stderr, "ERROR: recv failed left=%d\n", num); - exit(1); - } - if (memcmp(buf, rbuf, sizeof(buf))) { - fprintf(stderr, "ERROR: data failed left=%d\n", num); - exit(1); - } - } -} - -static void sock_fanout_setfilter(int fd) -{ - struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ - { 0x6, 0, 0, 0x00000060 }, /* RET match */ -/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ - }; - struct sock_fprog bpf_prog; - - bpf_prog.filter = bpf_filter; - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, - sizeof(bpf_prog))) { - perror("setsockopt SO_ATTACH_FILTER"); - exit(1); - } -} - -/* Open a socket in a given fanout mode. - * @return -1 if mode is bad, a valid socket otherwise */ -static int sock_fanout_open(uint16_t typeflags, int num_packets) -{ - int fd, val; - - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); - if (fd < 0) { - perror("socket packet"); - exit(1); - } - - /* fanout group ID is always 0: tests whether old groups are deleted */ - val = ((int) typeflags) << 16; - if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { - if (close(fd)) { - perror("close packet"); - exit(1); - } - return -1; - } - - sock_fanout_setfilter(fd); - return fd; -} - -static char *sock_fanout_open_ring(int fd) -{ - struct tpacket_req req = { - .tp_block_size = getpagesize(), - .tp_frame_size = getpagesize(), - .tp_block_nr = RING_NUM_FRAMES, - .tp_frame_nr = RING_NUM_FRAMES, - }; - char *ring; - - if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, - sizeof(req))) { - perror("packetsock ring setsockopt"); - exit(1); - } - - ring = mmap(0, req.tp_block_size * req.tp_block_nr, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (!ring) { - fprintf(stderr, "packetsock ring mmap\n"); - exit(1); - } - - return ring; -} - -static int sock_fanout_read_ring(int fd, void *ring) -{ - struct tpacket_hdr *header = ring; - int count = 0; - - while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { - count++; - header = ring + (count * getpagesize()); - } - - return count; -} - -static int sock_fanout_read(int fds[], char *rings[], const int expect[]) -{ - int ret[2]; - - ret[0] = sock_fanout_read_ring(fds[0], rings[0]); - ret[1] = sock_fanout_read_ring(fds[1], rings[1]); - - fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", - ret[0], ret[1], expect[0], expect[1]); - - if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && - (!(ret[0] == expect[1] && ret[1] == expect[0]))) { - fprintf(stderr, "ERROR: incorrect queue lengths\n"); - return 1; - } - - return 0; -} - -/* Test illegal mode + flag combination */ -static void test_control_single(void) -{ - fprintf(stderr, "test: control single socket\n"); - - if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | - PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { - fprintf(stderr, "ERROR: opened socket with dual rollover\n"); - exit(1); - } -} - -/* Test illegal group with different modes or flags */ -static void test_control_group(void) -{ - int fds[2]; - - fprintf(stderr, "test: control multiple sockets\n"); - - fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[0] == -1) { - fprintf(stderr, "ERROR: failed to open HASH socket\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); - exit(1); - } - if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { - fprintf(stderr, "ERROR: joined group with wrong mode\n"); - exit(1); - } - fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); - if (fds[1] == -1) { - fprintf(stderr, "ERROR: failed to join group\n"); - exit(1); - } - if (close(fds[1]) || close(fds[0])) { - fprintf(stderr, "ERROR: closing sockets\n"); - exit(1); - } -} - -static int test_datapath(uint16_t typeflags, int port_off, - const int expect1[], const int expect2[]) -{ - const int expect0[] = { 0, 0 }; - char *rings[2]; - int fds[2], fds_udp[2][2], ret; - - fprintf(stderr, "test: datapath 0x%hx\n", typeflags); - - fds[0] = sock_fanout_open(typeflags, 20); - fds[1] = sock_fanout_open(typeflags, 20); - if (fds[0] == -1 || fds[1] == -1) { - fprintf(stderr, "ERROR: failed open\n"); - exit(1); - } - rings[0] = sock_fanout_open_ring(fds[0]); - rings[1] = sock_fanout_open_ring(fds[1]); - pair_udp_open(fds_udp[0], PORT_BASE); - pair_udp_open(fds_udp[1], PORT_BASE + port_off); - sock_fanout_read(fds, rings, expect0); - - /* Send data, but not enough to overflow a queue */ - pair_udp_send(fds_udp[0], 15); - pair_udp_send(fds_udp[1], 5); - ret = sock_fanout_read(fds, rings, expect1); - - /* Send more data, overflow the queue */ - pair_udp_send(fds_udp[0], 15); - /* TODO: ensure consistent order between expect1 and expect2 */ - ret |= sock_fanout_read(fds, rings, expect2); - - if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || - munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { - fprintf(stderr, "close rings\n"); - exit(1); - } - if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || - close(fds_udp[0][1]) || close(fds_udp[0][0]) || - close(fds[1]) || close(fds[0])) { - fprintf(stderr, "close datapath\n"); - exit(1); - } - - return ret; -} - -static int set_cpuaffinity(int cpuid) -{ - cpu_set_t mask; - - CPU_ZERO(&mask); - CPU_SET(cpuid, &mask); - if (sched_setaffinity(0, sizeof(mask), &mask)) { - if (errno != EINVAL) { - fprintf(stderr, "setaffinity %d\n", cpuid); - exit(1); - } - return 1; - } - - return 0; -} - -int main(int argc, char **argv) -{ - const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; - const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; - const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; - const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; - const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; - const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; - int port_off = 2, tries = 5, ret; - - test_control_single(); - test_control_group(); - - /* find a set of ports that do not collide onto the same socket */ - ret = test_datapath(PACKET_FANOUT_HASH, port_off, - expect_hash[0], expect_hash[1]); - while (ret && tries--) { - fprintf(stderr, "info: trying alternate ports (%d)\n", tries); - ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, - expect_hash[0], expect_hash[1]); - } - - ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, - port_off, expect_hash_rb[0], expect_hash_rb[1]); - ret |= test_datapath(PACKET_FANOUT_LB, - port_off, expect_lb[0], expect_lb[1]); - ret |= test_datapath(PACKET_FANOUT_ROLLOVER, - port_off, expect_rb[0], expect_rb[1]); - - set_cpuaffinity(0); - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu0[0], expect_cpu0[1]); - if (!set_cpuaffinity(1)) - /* TODO: test that choice alternates with previous */ - ret |= test_datapath(PACKET_FANOUT_CPU, port_off, - expect_cpu1[0], expect_cpu1[1]); - - if (ret) - return 1; - - printf("OK. All tests passed\n"); - return 0; -} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests deleted file mode 100644 index 7907824c6355..000000000000 --- a/tools/testing/selftests/net-afpacket/run_afpackettests +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -if [ $(id -u) != 0 ]; then - echo $msg must be run as root >&2 - exit 0 -fi - -echo "--------------------" -echo "running psock_fanout test" -echo "--------------------" -./psock_fanout -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi diff --git a/tools/testing/selftests/net-socket/Makefile b/tools/testing/selftests/net-socket/Makefile deleted file mode 100644 index 2450fd8bb10a..000000000000 --- a/tools/testing/selftests/net-socket/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Makefile for net-socket selftests - -CC = $(CROSS_COMPILE)gcc -CFLAGS = -Wall - -NET_SOCK_PROGS = socket - -all: $(NET_SOCK_PROGS) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -run_tests: all - @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" - -clean: - $(RM) $(NET_SOCK_PROGS) diff --git a/tools/testing/selftests/net-socket/run_netsocktests b/tools/testing/selftests/net-socket/run_netsocktests deleted file mode 100644 index c09a682df56a..000000000000 --- a/tools/testing/selftests/net-socket/run_netsocktests +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -echo "--------------------" -echo "running socket test" -echo "--------------------" -./socket -if [ $? -ne 0 ]; then - echo "[FAIL]" -else - echo "[PASS]" -fi - diff --git a/tools/testing/selftests/net-socket/socket.c b/tools/testing/selftests/net-socket/socket.c deleted file mode 100644 index 0f227f2f9be9..000000000000 --- a/tools/testing/selftests/net-socket/socket.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -struct socket_testcase { - int domain; - int type; - int protocol; - - /* 0 = valid file descriptor - * -foo = error foo - */ - int expect; - - /* If non-zero, accept EAFNOSUPPORT to handle the case - * of the protocol not being configured into the kernel. - */ - int nosupport_ok; -}; - -static struct socket_testcase tests[] = { - { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, - { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, - { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, - { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, -}; - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#define ERR_STRING_SZ 64 - -static int run_tests(void) -{ - char err_string1[ERR_STRING_SZ]; - char err_string2[ERR_STRING_SZ]; - int i, err; - - err = 0; - for (i = 0; i < ARRAY_SIZE(tests); i++) { - struct socket_testcase *s = &tests[i]; - int fd; - - fd = socket(s->domain, s->type, s->protocol); - if (fd < 0) { - if (s->nosupport_ok && - errno == EAFNOSUPPORT) - continue; - - if (s->expect < 0 && - errno == -s->expect) - continue; - - strerror_r(-s->expect, err_string1, ERR_STRING_SZ); - strerror_r(errno, err_string2, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "err (%s) got (%s)\n", - s->domain, s->type, s->protocol, - err_string1, err_string2); - - err = -1; - break; - } else { - close(fd); - - if (s->expect < 0) { - strerror_r(errno, err_string1, ERR_STRING_SZ); - - fprintf(stderr, "socket(%d, %d, %d) expected " - "success got err (%s)\n", - s->domain, s->type, s->protocol, - err_string1); - - err = -1; - break; - } - } - } - - return err; -} - -int main(void) -{ - int err = run_tests(); - - return err; -} diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile new file mode 100644 index 000000000000..bd6e272bab87 --- /dev/null +++ b/tools/testing/selftests/net/Makefile @@ -0,0 +1,19 @@ +# Makefile for net selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +NET_PROGS = socket psock_fanout + +all: $(NET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]" + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(NET_PROGS) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c new file mode 100644 index 000000000000..226e5e33105a --- /dev/null +++ b/tools/testing/selftests/net/psock_fanout.c @@ -0,0 +1,388 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_LB + * - PACKET_FANOUT_CPU + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE /* for sched_setaffinity */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_LEN 100 +#define DATA_CHAR 'a' +#define RING_NUM_FRAMES 20 +#define PORT_BASE 8000 + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("connect"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + sock_fanout_setfilter(fd); + return fd; +} + +static char *sock_fanout_open_ring(int fd) +{ + struct tpacket_req req = { + .tp_block_size = getpagesize(), + .tp_frame_size = getpagesize(), + .tp_block_nr = RING_NUM_FRAMES, + .tp_frame_nr = RING_NUM_FRAMES, + }; + char *ring; + + if (setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, + sizeof(req))) { + perror("packetsock ring setsockopt"); + exit(1); + } + + ring = mmap(0, req.tp_block_size * req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (!ring) { + fprintf(stderr, "packetsock ring mmap\n"); + exit(1); + } + + return ring; +} + +static int sock_fanout_read_ring(int fd, void *ring) +{ + struct tpacket_hdr *header = ring; + int count = 0; + + while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { + count++; + header = ring + (count * getpagesize()); + } + + return count; +} + +static int sock_fanout_read(int fds[], char *rings[], const int expect[]) +{ + int ret[2]; + + ret[0] = sock_fanout_read_ring(fds[0], rings[0]); + ret[1] = sock_fanout_read_ring(fds[1], rings[1]); + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + return 1; + } + + return 0; +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static int test_datapath(uint16_t typeflags, int port_off, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + char *rings[2]; + int fds[2], fds_udp[2][2], ret; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + rings[0] = sock_fanout_open_ring(fds[0]); + rings[1] = sock_fanout_open_ring(fds[1]); + pair_udp_open(fds_udp[0], PORT_BASE); + pair_udp_open(fds_udp[1], PORT_BASE + port_off); + sock_fanout_read(fds, rings, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + ret = sock_fanout_read(fds, rings, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + ret |= sock_fanout_read(fds, rings, expect2); + + if (munmap(rings[1], RING_NUM_FRAMES * getpagesize()) || + munmap(rings[0], RING_NUM_FRAMES * getpagesize())) { + fprintf(stderr, "close rings\n"); + exit(1); + } + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } + + return ret; +} + +static int set_cpuaffinity(int cpuid) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpuid, &mask); + if (sched_setaffinity(0, sizeof(mask), &mask)) { + if (errno != EINVAL) { + fprintf(stderr, "setaffinity %d\n", cpuid); + exit(1); + } + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 20, 5 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 20, 15 } }; + const int expect_lb[2][2] = { { 10, 10 }, { 18, 17 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 20, 15 } }; + const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; + const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; + int port_off = 2, tries = 5, ret; + + test_control_single(); + test_control_group(); + + /* find a set of ports that do not collide onto the same socket */ + ret = test_datapath(PACKET_FANOUT_HASH, port_off, + expect_hash[0], expect_hash[1]); + while (ret && tries--) { + fprintf(stderr, "info: trying alternate ports (%d)\n", tries); + ret = test_datapath(PACKET_FANOUT_HASH, ++port_off, + expect_hash[0], expect_hash[1]); + } + + ret |= test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + port_off, expect_hash_rb[0], expect_hash_rb[1]); + ret |= test_datapath(PACKET_FANOUT_LB, + port_off, expect_lb[0], expect_lb[1]); + ret |= test_datapath(PACKET_FANOUT_ROLLOVER, + port_off, expect_rb[0], expect_rb[1]); + + set_cpuaffinity(0); + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu0[0], expect_cpu0[1]); + if (!set_cpuaffinity(1)) + /* TODO: test that choice alternates with previous */ + ret |= test_datapath(PACKET_FANOUT_CPU, port_off, + expect_cpu1[0], expect_cpu1[1]); + + if (ret) + return 1; + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests new file mode 100644 index 000000000000..7907824c6355 --- /dev/null +++ b/tools/testing/selftests/net/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests new file mode 100644 index 000000000000..c09a682df56a --- /dev/null +++ b/tools/testing/selftests/net/run_netsocktests @@ -0,0 +1,12 @@ +#!/bin/bash + +echo "--------------------" +echo "running socket test" +echo "--------------------" +./socket +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi + diff --git a/tools/testing/selftests/net/socket.c b/tools/testing/selftests/net/socket.c new file mode 100644 index 000000000000..0f227f2f9be9 --- /dev/null +++ b/tools/testing/selftests/net/socket.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +struct socket_testcase { + int domain; + int type; + int protocol; + + /* 0 = valid file descriptor + * -foo = error foo + */ + int expect; + + /* If non-zero, accept EAFNOSUPPORT to handle the case + * of the protocol not being configured into the kernel. + */ + int nosupport_ok; +}; + +static struct socket_testcase tests[] = { + { AF_MAX, 0, 0, -EAFNOSUPPORT, 0 }, + { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_TCP, -EPROTONOSUPPORT, 1 }, + { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, 1 }, + { AF_INET, SOCK_STREAM, IPPROTO_UDP, -EPROTONOSUPPORT, 1 }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define ERR_STRING_SZ 64 + +static int run_tests(void) +{ + char err_string1[ERR_STRING_SZ]; + char err_string2[ERR_STRING_SZ]; + int i, err; + + err = 0; + for (i = 0; i < ARRAY_SIZE(tests); i++) { + struct socket_testcase *s = &tests[i]; + int fd; + + fd = socket(s->domain, s->type, s->protocol); + if (fd < 0) { + if (s->nosupport_ok && + errno == EAFNOSUPPORT) + continue; + + if (s->expect < 0 && + errno == -s->expect) + continue; + + strerror_r(-s->expect, err_string1, ERR_STRING_SZ); + strerror_r(errno, err_string2, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "err (%s) got (%s)\n", + s->domain, s->type, s->protocol, + err_string1, err_string2); + + err = -1; + break; + } else { + close(fd); + + if (s->expect < 0) { + strerror_r(errno, err_string1, ERR_STRING_SZ); + + fprintf(stderr, "socket(%d, %d, %d) expected " + "success got err (%s)\n", + s->domain, s->type, s->protocol, + err_string1); + + err = -1; + break; + } + } + } + + return err; +} + +int main(void) +{ + int err = run_tests(); + + return err; +} -- cgit v1.2.3 From e306e2c13b8c214618af0c61acf62a6e42d486de Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 20 Mar 2013 12:11:47 +0000 Subject: filter: add minimal BPF JIT image disassembler This is a minimal stand-alone user space helper, that allows for debugging or verification of emitted BPF JIT images. This is in particular useful for emitted opcode debugging, since minor bugs in the JIT compiler can be fatal. The disassembler is architecture generic and uses libopcodes and libbfd. How to get to the disassembly, example: 1) `echo 2 > /proc/sys/net/core/bpf_jit_enable` 2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`) 3) Run e.g. `bpf_jit_disasm -o` to disassemble the most recent JIT code output `bpf_jit_disasm -o` will display the related opcodes to a particular instruction as well. Example for x86_64: $ ./bpf_jit_disasm 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + : 0: push %rbp 1: mov %rsp,%rbp 4: sub $0x60,%rsp 8: mov %rbx,-0x8(%rbp) c: mov 0x68(%rdi),%r9d 10: sub 0x6c(%rdi),%r9d 14: mov 0xe0(%rdi),%r8 1b: mov $0xc,%esi 20: callq 0xffffffffe0d01b71 25: cmp $0x86dd,%eax 2a: jne 0x000000000000003d 2c: mov $0x14,%esi 31: callq 0xffffffffe0d01b8d 36: cmp $0x6,%eax [...] 5c: leaveq 5d: retq $ ./bpf_jit_disasm -o 94 bytes emitted from JIT compiler (pass:3, flen:9) ffffffffa0356000 + : 0: push %rbp 55 1: mov %rsp,%rbp 48 89 e5 4: sub $0x60,%rsp 48 83 ec 60 8: mov %rbx,-0x8(%rbp) 48 89 5d f8 c: mov 0x68(%rdi),%r9d 44 8b 4f 68 10: sub 0x6c(%rdi),%r9d 44 2b 4f 6c [...] 5c: leaveq c9 5d: retq c3 Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/Makefile | 11 +-- tools/net/Makefile | 15 ++++ tools/net/bpf_jit_disasm.c | 199 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 tools/net/Makefile create mode 100644 tools/net/bpf_jit_disasm.c (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index fa36