summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/net
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/net')
-rw-r--r--tools/testing/selftests/net/Makefile20
-rwxr-xr-xtools/testing/selftests/net/netdevice.sh200
-rw-r--r--tools/testing/selftests/net/psock_fanout.c115
-rw-r--r--tools/testing/selftests/net/psock_lib.h52
-rw-r--r--tools/testing/selftests/net/psock_tpacket.c97
5 files changed, 421 insertions, 63 deletions
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index e24e4c82542e..f6c9dbf478f8 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -3,20 +3,12 @@
CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
CFLAGS += -I../../../../usr/include/
-NET_PROGS = socket
-NET_PROGS += psock_fanout psock_tpacket
-NET_PROGS += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
-NET_PROGS += reuseport_dualstack
-
-all: $(NET_PROGS)
-reuseport_bpf_numa: LDFLAGS += -lnuma
-%: %.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
-
-TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh
-TEST_FILES := $(NET_PROGS)
+TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh
+TEST_GEN_FILES = socket
+TEST_GEN_FILES += psock_fanout psock_tpacket
+TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
+TEST_GEN_FILES += reuseport_dualstack
include ../lib.mk
-clean:
- $(RM) $(NET_PROGS)
+$(OUTPUT)/reuseport_bpf_numa: LDFLAGS += -lnuma
diff --git a/tools/testing/selftests/net/netdevice.sh b/tools/testing/selftests/net/netdevice.sh
new file mode 100755
index 000000000000..4e00568d70c2
--- /dev/null
+++ b/tools/testing/selftests/net/netdevice.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+#
+# This test is for checking network interface
+# For the moment it tests only ethernet interface (but wifi could be easily added)
+#
+# We assume that all network driver are loaded
+# if not they probably have failed earlier in the boot process and their logged error will be catched by another test
+#
+
+# this function will try to up the interface
+# if already up, nothing done
+# arg1: network interface name
+kci_net_start()
+{
+ netdev=$1
+
+ ip link show "$netdev" |grep -q UP
+ if [ $? -eq 0 ];then
+ echo "SKIP: $netdev: interface already up"
+ return 0
+ fi
+
+ ip link set "$netdev" up
+ if [ $? -ne 0 ];then
+ echo "FAIL: $netdev: Fail to up interface"
+ return 1
+ else
+ echo "PASS: $netdev: set interface up"
+ NETDEV_STARTED=1
+ fi
+ return 0
+}
+
+# this function will try to setup an IP and MAC address on a network interface
+# Doing nothing if the interface was already up
+# arg1: network interface name
+kci_net_setup()
+{
+ netdev=$1
+
+ # do nothing if the interface was already up
+ if [ $NETDEV_STARTED -eq 0 ];then
+ return 0
+ fi
+
+ MACADDR='02:03:04:05:06:07'
+ ip link set dev $netdev address "$MACADDR"
+ if [ $? -ne 0 ];then
+ echo "FAIL: $netdev: Cannot set MAC address"
+ else
+ ip link show $netdev |grep -q "$MACADDR"
+ if [ $? -eq 0 ];then
+ echo "PASS: $netdev: set MAC address"
+ else
+ echo "FAIL: $netdev: Cannot set MAC address"
+ fi
+ fi
+
+ #check that the interface did not already have an IP
+ ip address show "$netdev" |grep '^[[:space:]]*inet'
+ if [ $? -eq 0 ];then
+ echo "SKIP: $netdev: already have an IP"
+ return 0
+ fi
+
+ # TODO what ipaddr to set ? DHCP ?
+ echo "SKIP: $netdev: set IP address"
+ return 0
+}
+
+# test an ethtool command
+# arg1: return code for not supported (see ethtool code source)
+# arg2: summary of the command
+# arg3: command to execute
+kci_netdev_ethtool_test()
+{
+ if [ $# -le 2 ];then
+ echo "SKIP: $netdev: ethtool: invalid number of arguments"
+ return 1
+ fi
+ $3 >/dev/null
+ ret=$?
+ if [ $ret -ne 0 ];then
+ if [ $ret -eq "$1" ];then
+ echo "SKIP: $netdev: ethtool $2 not supported"
+ else
+ echo "FAIL: $netdev: ethtool $2"
+ return 1
+ fi
+ else
+ echo "PASS: $netdev: ethtool $2"
+ fi
+ return 0
+}
+
+# test ethtool commands
+# arg1: network interface name
+kci_netdev_ethtool()
+{
+ netdev=$1
+
+ #check presence of ethtool
+ ethtool --version 2>/dev/null >/dev/null
+ if [ $? -ne 0 ];then
+ echo "SKIP: ethtool not present"
+ return 1
+ fi
+
+ TMP_ETHTOOL_FEATURES="$(mktemp)"
+ if [ ! -e "$TMP_ETHTOOL_FEATURES" ];then
+ echo "SKIP: Cannot create a tmp file"
+ return 1
+ fi
+
+ ethtool -k "$netdev" > "$TMP_ETHTOOL_FEATURES"
+ if [ $? -ne 0 ];then
+ echo "FAIL: $netdev: ethtool list features"
+ rm "$TMP_ETHTOOL_FEATURES"
+ return 1
+ fi
+ echo "PASS: $netdev: ethtool list features"
+ #TODO for each non fixed features, try to turn them on/off
+ rm "$TMP_ETHTOOL_FEATURES"
+
+ kci_netdev_ethtool_test 74 'dump' "ethtool -d $netdev"
+ kci_netdev_ethtool_test 94 'stats' "ethtool -S $netdev"
+ return 0
+}
+
+# stop a netdev
+# arg1: network interface name
+kci_netdev_stop()
+{
+ netdev=$1
+
+ if [ $NETDEV_STARTED -eq 0 ];then
+ echo "SKIP: $netdev: interface kept up"
+ return 0
+ fi
+
+ ip link set "$netdev" down
+ if [ $? -ne 0 ];then
+ echo "FAIL: $netdev: stop interface"
+ return 1
+ fi
+ echo "PASS: $netdev: stop interface"
+ return 0
+}
+
+# run all test on a netdev
+# arg1: network interface name
+kci_test_netdev()
+{
+ NETDEV_STARTED=0
+ IFACE_TO_UPDOWN="$1"
+ IFACE_TO_TEST="$1"
+ #check for VLAN interface
+ MASTER_IFACE="$(echo $1 | cut -d@ -f2)"
+ if [ ! -z "$MASTER_IFACE" ];then
+ IFACE_TO_UPDOWN="$MASTER_IFACE"
+ IFACE_TO_TEST="$(echo $1 | cut -d@ -f1)"
+ fi
+
+ NETDEV_STARTED=0
+ kci_net_start "$IFACE_TO_UPDOWN"
+
+ kci_net_setup "$IFACE_TO_TEST"
+
+ kci_netdev_ethtool "$IFACE_TO_TEST"
+
+ kci_netdev_stop "$IFACE_TO_UPDOWN"
+ return 0
+}
+
+#check for needed privileges
+if [ "$(id -u)" -ne 0 ];then
+ echo "SKIP: Need root privileges"
+ exit 0
+fi
+
+ip -Version 2>/dev/null >/dev/null
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without the ip tool"
+ exit 0
+fi
+
+TMP_LIST_NETDEV="$(mktemp)"
+if [ ! -e "$TMP_LIST_NETDEV" ];then
+ echo "FAIL: Cannot create a tmp file"
+ exit 1
+fi
+
+ip link show |grep '^[0-9]' | grep -oE '[[:space:]].*eth[0-9]*:|[[:space:]].*enp[0-9]s[0-9]:' | cut -d\ -f2 | cut -d: -f1> "$TMP_LIST_NETDEV"
+while read netdev
+do
+ kci_test_netdev "$netdev"
+done < "$TMP_LIST_NETDEV"
+
+rm "$TMP_LIST_NETDEV"
+exit 0
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index 412459369686..989f917068d1 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -71,18 +71,17 @@
/* 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)
+static int sock_fanout_open(uint16_t typeflags, uint16_t group_id)
{
int fd, val;
- fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ fd = socket(PF_PACKET, SOCK_RAW, 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;
+ val = (((int) typeflags) << 16) | group_id;
if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) {
if (close(fd)) {
perror("close packet");
@@ -95,6 +94,38 @@ static int sock_fanout_open(uint16_t typeflags, int num_packets)
return fd;
}
+static void sock_fanout_set_cbpf(int fd)
+{
+ struct sock_filter bpf_filter[] = {
+ BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 80), /* ldb [80] */
+ BPF_STMT(BPF_RET+BPF_A, 0), /* ret A */
+ };
+ struct sock_fprog bpf_prog;
+
+ bpf_prog.filter = bpf_filter;
+ bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter);
+
+ if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT_DATA, &bpf_prog,
+ sizeof(bpf_prog))) {
+ perror("fanout data cbpf");
+ exit(1);
+ }
+}
+
+static void sock_fanout_getopts(int fd, uint16_t *typeflags, uint16_t *group_id)
+{
+ int sockopt;
+ socklen_t sockopt_len = sizeof(sockopt);
+
+ if (getsockopt(fd, SOL_PACKET, PACKET_FANOUT,
+ &sockopt, &sockopt_len)) {
+ perror("failed to getsockopt");
+ exit(1);
+ }
+ *typeflags = sockopt >> 16;
+ *group_id = sockopt & 0xfffff;
+}
+
static void sock_fanout_set_ebpf(int fd)
{
const int len_off = __builtin_offsetof(struct __sk_buff, len);
@@ -223,26 +254,26 @@ static void test_control_group(void)
fprintf(stderr, "test: control multiple sockets\n");
- fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
+ fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 0);
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) {
+ PACKET_FANOUT_FLAG_DEFRAG, 0) != -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) {
+ PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) {
fprintf(stderr, "ERROR: joined group with wrong flag ro\n");
exit(1);
}
- if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) {
+ if (sock_fanout_open(PACKET_FANOUT_CPU, 0) != -1) {
fprintf(stderr, "ERROR: joined group with wrong mode\n");
exit(1);
}
- fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20);
+ fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 0);
if (fds[1] == -1) {
fprintf(stderr, "ERROR: failed to join group\n");
exit(1);
@@ -253,6 +284,61 @@ static void test_control_group(void)
}
}
+/* Test creating a unique fanout group ids */
+static void test_unique_fanout_group_ids(void)
+{
+ int fds[3];
+ uint16_t typeflags, first_group_id, second_group_id;
+
+ fprintf(stderr, "test: unique ids\n");
+
+ fds[0] = sock_fanout_open(PACKET_FANOUT_HASH |
+ PACKET_FANOUT_FLAG_UNIQUEID, 0);
+ if (fds[0] == -1) {
+ fprintf(stderr, "ERROR: failed to create a unique id group.\n");
+ exit(1);
+ }
+
+ sock_fanout_getopts(fds[0], &typeflags, &first_group_id);
+ if (typeflags != PACKET_FANOUT_HASH) {
+ fprintf(stderr, "ERROR: unexpected typeflags %x\n", typeflags);
+ exit(1);
+ }
+
+ if (sock_fanout_open(PACKET_FANOUT_CPU, first_group_id) != -1) {
+ fprintf(stderr, "ERROR: joined group with wrong type.\n");
+ exit(1);
+ }
+
+ fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, first_group_id);
+ if (fds[1] == -1) {
+ fprintf(stderr,
+ "ERROR: failed to join previously created group.\n");
+ exit(1);
+ }
+
+ fds[2] = sock_fanout_open(PACKET_FANOUT_HASH |
+ PACKET_FANOUT_FLAG_UNIQUEID, 0);
+ if (fds[2] == -1) {
+ fprintf(stderr,
+ "ERROR: failed to create a second unique id group.\n");
+ exit(1);
+ }
+
+ sock_fanout_getopts(fds[2], &typeflags, &second_group_id);
+ if (sock_fanout_open(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_UNIQUEID,
+ second_group_id) != -1) {
+ fprintf(stderr,
+ "ERROR: specified a group id when requesting unique id\n");
+ exit(1);
+ }
+
+ if (close(fds[0]) || close(fds[1]) || close(fds[2])) {
+ fprintf(stderr, "ERROR: closing sockets\n");
+ exit(1);
+ }
+}
+
static int test_datapath(uint16_t typeflags, int port_off,
const int expect1[], const int expect2[])
{
@@ -263,14 +349,14 @@ static int test_datapath(uint16_t typeflags, int port_off,
fprintf(stderr, "test: datapath 0x%hx\n", typeflags);
- fds[0] = sock_fanout_open(typeflags, 20);
- fds[1] = sock_fanout_open(typeflags, 20);
+ fds[0] = sock_fanout_open(typeflags, 0);
+ fds[1] = sock_fanout_open(typeflags, 0);
if (fds[0] == -1 || fds[1] == -1) {
fprintf(stderr, "ERROR: failed open\n");
exit(1);
}
if (type == PACKET_FANOUT_CBPF)
- sock_setfilter(fds[0], SOL_PACKET, PACKET_FANOUT_DATA);
+ sock_fanout_set_cbpf(fds[0]);
else if (type == PACKET_FANOUT_EBPF)
sock_fanout_set_ebpf(fds[0]);
@@ -331,10 +417,12 @@ int main(int argc, char **argv)
const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } };
const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } };
const int expect_bpf[2][2] = { { 15, 5 }, { 15, 20 } };
+ const int expect_uniqueid[2][2] = { { 20, 20}, { 20, 20 } };
int port_off = 2, tries = 5, ret;
test_control_single();
test_control_group();
+ test_unique_fanout_group_ids();
/* find a set of ports that do not collide onto the same socket */
ret = test_datapath(PACKET_FANOUT_HASH, port_off,
@@ -365,6 +453,9 @@ int main(int argc, char **argv)
ret |= test_datapath(PACKET_FANOUT_CPU, port_off,
expect_cpu1[0], expect_cpu1[1]);
+ ret |= test_datapath(PACKET_FANOUT_FLAG_UNIQUEID, port_off,
+ expect_uniqueid[0], expect_uniqueid[1]);
+
if (ret)
return 1;
diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h
index 24bc7ec1be7d..7d990d6c861b 100644
--- a/tools/testing/selftests/net/psock_lib.h
+++ b/tools/testing/selftests/net/psock_lib.h
@@ -38,36 +38,54 @@
# define __maybe_unused __attribute__ ((__unused__))
#endif
-static __maybe_unused void sock_setfilter(int fd, int lvl, int optnum)
+static __maybe_unused void pair_udp_setfilter(int fd)
{
+ /* the filter below checks for all of the following conditions that
+ * are based on the contents of create_payload()
+ * ether type 0x800 and
+ * ip proto udp and
+ * skb->len == DATA_LEN and
+ * udp[38] == 'a' or udp[38] == 'b'
+ * It can be generated from the following bpf_asm input:
+ * ldh [12]
+ * jne #0x800, drop ; ETH_P_IP
+ * ldb [23]
+ * jneq #17, drop ; IPPROTO_UDP
+ * ld len ; ld skb->len
+ * jlt #100, drop ; DATA_LEN
+ * ldb [80]
+ * jeq #97, pass ; DATA_CHAR
+ * jne #98, drop ; DATA_CHAR_1
+ * pass:
+ * ret #-1
+ * drop:
+ * ret #0
+ */
struct sock_filter bpf_filter[] = {
- { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */
- { 0x35, 0, 4, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/
- { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */
- { 0x15, 1, 0, DATA_CHAR }, /* JEQ DATA_CHAR [t goto match]*/
- { 0x15, 0, 1, DATA_CHAR_1}, /* JEQ DATA_CHAR_1 [t goto match]*/
- { 0x06, 0, 0, 0x00000060 }, /* RET match */
- { 0x06, 0, 0, 0x00000000 }, /* RET no match */
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 8, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 6, 0x00000011 },
+ { 0x80, 0, 0, 0000000000 },
+ { 0x35, 0, 4, 0x00000064 },
+ { 0x30, 0, 0, 0x00000050 },
+ { 0x15, 1, 0, 0x00000061 },
+ { 0x15, 0, 1, 0x00000062 },
+ { 0x06, 0, 0, 0xffffffff },
+ { 0x06, 0, 0, 0000000000 },
};
struct sock_fprog bpf_prog;
- if (lvl == SOL_PACKET && optnum == PACKET_FANOUT_DATA)
- bpf_filter[5].code = 0x16; /* RET A */
-
bpf_prog.filter = bpf_filter;
bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter);
- if (setsockopt(fd, lvl, optnum, &bpf_prog,
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog,
sizeof(bpf_prog))) {
perror("setsockopt SO_ATTACH_FILTER");
exit(1);
}
}
-static __maybe_unused void pair_udp_setfilter(int fd)
-{
- sock_setfilter(fd, SOL_SOCKET, SO_ATTACH_FILTER);
-}
-
static __maybe_unused void pair_udp_open(int fds[], uint16_t port)
{
struct sockaddr_in saddr, daddr;
diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c
index 24adf709bd9d..7f6cd9fdacf3 100644
--- a/tools/testing/selftests/net/psock_tpacket.c
+++ b/tools/testing/selftests/net/psock_tpacket.c
@@ -110,7 +110,7 @@ static unsigned int total_packets, total_bytes;
static int pfsocket(int ver)
{
- int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ int ret, sock = socket(PF_PACKET, SOCK_RAW, 0);
if (sock == -1) {
perror("socket");
exit(1);
@@ -239,7 +239,6 @@ static void walk_v1_v2_rx(int sock, struct ring *ring)
bug_on(ring->type != PACKET_RX_RING);
pair_udp_open(udp_sock, PORT_BASE);
- pair_udp_setfilter(sock);
memset(&pfd, 0, sizeof(pfd));
pfd.fd = sock;
@@ -311,20 +310,33 @@ static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr)
__sync_synchronize();
}
-static inline int __v1_v2_tx_kernel_ready(void *base, int version)
+static inline int __v3_tx_kernel_ready(struct tpacket3_hdr *hdr)
+{
+ return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING));
+}
+
+static inline void __v3_tx_user_ready(struct tpacket3_hdr *hdr)
+{
+ hdr->tp_status = TP_STATUS_SEND_REQUEST;
+ __sync_synchronize();
+}
+
+static inline int __tx_kernel_ready(void *base, int version)
{
switch (version) {
case TPACKET_V1:
return __v1_tx_kernel_ready(base);
case TPACKET_V2:
return __v2_tx_kernel_ready(base);
+ case TPACKET_V3:
+ return __v3_tx_kernel_ready(base);
default:
bug_on(1);
return 0;
}
}
-static inline void __v1_v2_tx_user_ready(void *base, int version)
+static inline void __tx_user_ready(void *base, int version)
{
switch (version) {
case TPACKET_V1:
@@ -333,6 +345,9 @@ static inline void __v1_v2_tx_user_ready(void *base, int version)
case TPACKET_V2:
__v2_tx_user_ready(base);
break;
+ case TPACKET_V3:
+ __v3_tx_user_ready(base);
+ break;
}
}
@@ -348,7 +363,22 @@ static void __v1_v2_set_packet_loss_discard(int sock)
}
}
-static void walk_v1_v2_tx(int sock, struct ring *ring)
+static inline void *get_next_frame(struct ring *ring, int n)
+{
+ uint8_t *f0 = ring->rd[0].iov_base;
+
+ switch (ring->version) {
+ case TPACKET_V1:
+ case TPACKET_V2:
+ return ring->rd[n].iov_base;
+ case TPACKET_V3:
+ return f0 + (n * ring->req3.tp_frame_size);
+ default:
+ bug_on(1);
+ }
+}
+
+static void walk_tx(int sock, struct ring *ring)
{
struct pollfd pfd;
int rcv_sock, ret;
@@ -360,9 +390,19 @@ static void walk_v1_v2_tx(int sock, struct ring *ring)
.sll_family = PF_PACKET,
.sll_halen = ETH_ALEN,
};
+ int nframes;
+
+ /* TPACKET_V{1,2} sets up the ring->rd* related variables based
+ * on frames (e.g., rd_num is tp_frame_nr) whereas V3 sets these
+ * up based on blocks (e.g, rd_num is tp_block_nr)
+ */
+ if (ring->version <= TPACKET_V2)
+ nframes = ring->rd_num;
+ else
+ nframes = ring->req3.tp_frame_nr;
bug_on(ring->type != PACKET_TX_RING);
- bug_on(ring->rd_num < NUM_PACKETS);
+ bug_on(nframes < NUM_PACKETS);
rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (rcv_sock == -1) {
@@ -388,10 +428,11 @@ static void walk_v1_v2_tx(int sock, struct ring *ring)
create_payload(packet, &packet_len);
while (total_packets > 0) {
- while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base,
- ring->version) &&
+ void *next = get_next_frame(ring, frame_num);
+
+ while (__tx_kernel_ready(next, ring->version) &&
total_packets > 0) {
- ppd.raw = ring->rd[frame_num].iov_base;
+ ppd.raw = next;
switch (ring->version) {
case TPACKET_V1:
@@ -413,14 +454,27 @@ static void walk_v1_v2_tx(int sock, struct ring *ring)
packet_len);
total_bytes += ppd.v2->tp_h.tp_snaplen;
break;
+ case TPACKET_V3: {
+ struct tpacket3_hdr *tx = next;
+
+ tx->tp_snaplen = packet_len;
+ tx->tp_len = packet_len;
+ tx->tp_next_offset = 0;
+
+ memcpy((uint8_t *)tx + TPACKET3_HDRLEN -
+ sizeof(struct sockaddr_ll), packet,
+ packet_len);
+ total_bytes += tx->tp_snaplen;
+ break;
+ }
}
status_bar_update();
total_packets--;
- __v1_v2_tx_user_ready(ppd.raw, ring->version);
+ __tx_user_ready(next, ring->version);
- frame_num = (frame_num + 1) % ring->rd_num;
+ frame_num = (frame_num + 1) % nframes;
}
poll(&pfd, 1, 1);
@@ -460,7 +514,7 @@ static void walk_v1_v2(int sock, struct ring *ring)
if (ring->type == PACKET_RX_RING)
walk_v1_v2_rx(sock, ring);
else
- walk_v1_v2_tx(sock, ring);
+ walk_tx(sock, ring);
}
static uint64_t __v3_prev_block_seq_num = 0;
@@ -546,7 +600,6 @@ static void walk_v3_rx(int sock, struct ring *ring)
bug_on(ring->type != PACKET_RX_RING);
pair_udp_open(udp_sock, PORT_BASE);
- pair_udp_setfilter(sock);
memset(&pfd, 0, sizeof(pfd));
pfd.fd = sock;
@@ -583,7 +636,7 @@ static void walk_v3(int sock, struct ring *ring)
if (ring->type == PACKET_RX_RING)
walk_v3_rx(sock, ring);
else
- bug_on(1);
+ walk_tx(sock, ring);
}
static void __v1_v2_fill(struct ring *ring, unsigned int blocks)
@@ -602,12 +655,13 @@ static void __v1_v2_fill(struct ring *ring, unsigned int blocks)
ring->flen = ring->req.tp_frame_size;
}
-static void __v3_fill(struct ring *ring, unsigned int blocks)
+static void __v3_fill(struct ring *ring, unsigned int blocks, int type)
{
- ring->req3.tp_retire_blk_tov = 64;
- ring->req3.tp_sizeof_priv = 0;
- ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
-
+ if (type == PACKET_RX_RING) {
+ ring->req3.tp_retire_blk_tov = 64;
+ ring->req3.tp_sizeof_priv = 0;
+ ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
+ }
ring->req3.tp_block_size = getpagesize() << 2;
ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7;
ring->req3.tp_block_nr = blocks;
@@ -641,7 +695,7 @@ static void setup_ring(int sock, struct ring *ring, int version, int type)
break;
case TPACKET_V3:
- __v3_fill(ring, blocks);
+ __v3_fill(ring, blocks, type);
ret = setsockopt(sock, SOL_PACKET, type, &ring->req3,
sizeof(ring->req3));
break;
@@ -685,6 +739,8 @@ static void bind_ring(int sock, struct ring *ring)
{
int ret;
+ pair_udp_setfilter(sock);
+
ring->ll.sll_family = PF_PACKET;
ring->ll.sll_protocol = htons(ETH_P_ALL);
ring->ll.sll_ifindex = if_nametoindex("lo");
@@ -796,6 +852,7 @@ int main(void)
ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING);
ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING);
+ ret |= test_tpacket(TPACKET_V3, PACKET_TX_RING);
if (ret)
return 1;
OpenPOWER on IntegriCloud