Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: net: add PF_PACKET TPACKET v1/v2/v3 selftests

This patch adds a simple test case that probes the packet socket's
TPACKET_V1, TPACKET_V2 and TPACKET_V3 behavior regarding mmap(2)'ed
I/O for a small burst of 100 packets. The test currently runs for ...

TPACKET_V1: RX_RING, TX_RING
TPACKET_V2: RX_RING, TX_RING
TPACKET_V3: RX_RING

... and will output on success:

test: TPACKET_V1 with PACKET_RX_RING .................... 100 pkts (9600 bytes)
test: TPACKET_V1 with PACKET_TX_RING .................... 100 pkts (9600 bytes)
test: TPACKET_V2 with PACKET_RX_RING .................... 100 pkts (9600 bytes)
test: TPACKET_V2 with PACKET_TX_RING .................... 100 pkts (9600 bytes)
test: TPACKET_V3 with PACKET_RX_RING .................... 100 pkts (9600 bytes)
OK. All tests passed

Reusable parts of psock_fanout.c have been put into a psock_lib.h
file for common usage. Test case successfully tested on x86_64.

Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Daniel Borkmann and committed by
David S. Miller
23a95442 9dcc71e1

+966 -87
+2 -2
tools/testing/selftests/net/Makefile
··· 1 1 # Makefile for net selftests 2 2 3 3 CC = $(CROSS_COMPILE)gcc 4 - CFLAGS = -Wall 4 + CFLAGS = -Wall -O2 -g 5 5 6 6 CFLAGS += -I../../../../usr/include/ 7 7 8 - NET_PROGS = socket psock_fanout 8 + NET_PROGS = socket psock_fanout psock_tpacket 9 9 10 10 all: $(NET_PROGS) 11 11 %: %.c
+3 -85
tools/testing/selftests/net/psock_fanout.c
··· 61 61 #include <sys/types.h> 62 62 #include <unistd.h> 63 63 64 - #define DATA_LEN 100 65 - #define DATA_CHAR 'a' 64 + #include "psock_lib.h" 65 + 66 66 #define RING_NUM_FRAMES 20 67 - #define PORT_BASE 8000 68 - 69 - static void pair_udp_open(int fds[], uint16_t port) 70 - { 71 - struct sockaddr_in saddr, daddr; 72 - 73 - fds[0] = socket(PF_INET, SOCK_DGRAM, 0); 74 - fds[1] = socket(PF_INET, SOCK_DGRAM, 0); 75 - if (fds[0] == -1 || fds[1] == -1) { 76 - fprintf(stderr, "ERROR: socket dgram\n"); 77 - exit(1); 78 - } 79 - 80 - memset(&saddr, 0, sizeof(saddr)); 81 - saddr.sin_family = AF_INET; 82 - saddr.sin_port = htons(port); 83 - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 84 - 85 - memset(&daddr, 0, sizeof(daddr)); 86 - daddr.sin_family = AF_INET; 87 - daddr.sin_port = htons(port + 1); 88 - daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 89 - 90 - /* must bind both to get consistent hash result */ 91 - if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { 92 - perror("bind"); 93 - exit(1); 94 - } 95 - if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { 96 - perror("bind"); 97 - exit(1); 98 - } 99 - if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { 100 - perror("connect"); 101 - exit(1); 102 - } 103 - } 104 - 105 - static void pair_udp_send(int fds[], int num) 106 - { 107 - char buf[DATA_LEN], rbuf[DATA_LEN]; 108 - 109 - memset(buf, DATA_CHAR, sizeof(buf)); 110 - while (num--) { 111 - /* Should really handle EINTR and EAGAIN */ 112 - if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { 113 - fprintf(stderr, "ERROR: send failed left=%d\n", num); 114 - exit(1); 115 - } 116 - if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { 117 - fprintf(stderr, "ERROR: recv failed left=%d\n", num); 118 - exit(1); 119 - } 120 - if (memcmp(buf, rbuf, sizeof(buf))) { 121 - fprintf(stderr, "ERROR: data failed left=%d\n", num); 122 - exit(1); 123 - } 124 - } 125 - } 126 - 127 - static void sock_fanout_setfilter(int fd) 128 - { 129 - struct sock_filter bpf_filter[] = { 130 - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ 131 - { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ 132 - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ 133 - { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ 134 - { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ 135 - { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ 136 - { 0x6, 0, 0, 0x00000060 }, /* RET match */ 137 - /* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ 138 - }; 139 - struct sock_fprog bpf_prog; 140 - 141 - bpf_prog.filter = bpf_filter; 142 - bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); 143 - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, 144 - sizeof(bpf_prog))) { 145 - perror("setsockopt SO_ATTACH_FILTER"); 146 - exit(1); 147 - } 148 - } 149 67 150 68 /* Open a socket in a given fanout mode. 151 69 * @return -1 if mode is bad, a valid socket otherwise */ ··· 87 169 return -1; 88 170 } 89 171 90 - sock_fanout_setfilter(fd); 172 + pair_udp_setfilter(fd); 91 173 return fd; 92 174 } 93 175
+127
tools/testing/selftests/net/psock_lib.h
··· 1 + /* 2 + * Copyright 2013 Google Inc. 3 + * Author: Willem de Bruijn <willemb@google.com> 4 + * Daniel Borkmann <dborkman@redhat.com> 5 + * 6 + * License (GPLv2): 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms and conditions of the GNU General Public License, 10 + * version 2, as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope it will be useful, but WITHOUT 13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for 15 + * more details. 16 + * 17 + * You should have received a copy of the GNU General Public License along with 18 + * this program; if not, write to the Free Software Foundation, Inc., 19 + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 20 + */ 21 + 22 + #ifndef PSOCK_LIB_H 23 + #define PSOCK_LIB_H 24 + 25 + #include <sys/types.h> 26 + #include <sys/socket.h> 27 + #include <string.h> 28 + #include <arpa/inet.h> 29 + #include <unistd.h> 30 + 31 + #define DATA_LEN 100 32 + #define DATA_CHAR 'a' 33 + 34 + #define PORT_BASE 8000 35 + 36 + #ifndef __maybe_unused 37 + # define __maybe_unused __attribute__ ((__unused__)) 38 + #endif 39 + 40 + static __maybe_unused void pair_udp_setfilter(int fd) 41 + { 42 + struct sock_filter bpf_filter[] = { 43 + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ 44 + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ 45 + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ 46 + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ 47 + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ 48 + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ 49 + { 0x06, 0, 0, 0x00000060 }, /* RET match */ 50 + { 0x06, 0, 0, 0x00000000 }, /* RET no match */ 51 + }; 52 + struct sock_fprog bpf_prog; 53 + 54 + bpf_prog.filter = bpf_filter; 55 + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); 56 + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, 57 + sizeof(bpf_prog))) { 58 + perror("setsockopt SO_ATTACH_FILTER"); 59 + exit(1); 60 + } 61 + } 62 + 63 + static __maybe_unused void pair_udp_open(int fds[], uint16_t port) 64 + { 65 + struct sockaddr_in saddr, daddr; 66 + 67 + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); 68 + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); 69 + if (fds[0] == -1 || fds[1] == -1) { 70 + fprintf(stderr, "ERROR: socket dgram\n"); 71 + exit(1); 72 + } 73 + 74 + memset(&saddr, 0, sizeof(saddr)); 75 + saddr.sin_family = AF_INET; 76 + saddr.sin_port = htons(port); 77 + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 78 + 79 + memset(&daddr, 0, sizeof(daddr)); 80 + daddr.sin_family = AF_INET; 81 + daddr.sin_port = htons(port + 1); 82 + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 83 + 84 + /* must bind both to get consistent hash result */ 85 + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { 86 + perror("bind"); 87 + exit(1); 88 + } 89 + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { 90 + perror("bind"); 91 + exit(1); 92 + } 93 + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { 94 + perror("connect"); 95 + exit(1); 96 + } 97 + } 98 + 99 + static __maybe_unused void pair_udp_send(int fds[], int num) 100 + { 101 + char buf[DATA_LEN], rbuf[DATA_LEN]; 102 + 103 + memset(buf, DATA_CHAR, sizeof(buf)); 104 + while (num--) { 105 + /* Should really handle EINTR and EAGAIN */ 106 + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { 107 + fprintf(stderr, "ERROR: send failed left=%d\n", num); 108 + exit(1); 109 + } 110 + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { 111 + fprintf(stderr, "ERROR: recv failed left=%d\n", num); 112 + exit(1); 113 + } 114 + if (memcmp(buf, rbuf, sizeof(buf))) { 115 + fprintf(stderr, "ERROR: data failed left=%d\n", num); 116 + exit(1); 117 + } 118 + } 119 + } 120 + 121 + static __maybe_unused void pair_udp_close(int fds[]) 122 + { 123 + close(fds[0]); 124 + close(fds[1]); 125 + } 126 + 127 + #endif /* PSOCK_LIB_H */
+824
tools/testing/selftests/net/psock_tpacket.c
··· 1 + /* 2 + * Copyright 2013 Red Hat, Inc. 3 + * Author: Daniel Borkmann <dborkman@redhat.com> 4 + * 5 + * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior. 6 + * 7 + * Control: 8 + * Test the setup of the TPACKET socket with different patterns that are 9 + * known to fail (TODO) resp. succeed (OK). 10 + * 11 + * Datapath: 12 + * Open a pair of packet sockets and send resp. receive an a priori known 13 + * packet pattern accross the sockets and check if it was received resp. 14 + * sent correctly. Fanout in combination with RX_RING is currently not 15 + * tested here. 16 + * 17 + * The test currently runs for 18 + * - TPACKET_V1: RX_RING, TX_RING 19 + * - TPACKET_V2: RX_RING, TX_RING 20 + * - TPACKET_V3: RX_RING 21 + * 22 + * License (GPLv2): 23 + * 24 + * This program is free software; you can redistribute it and/or modify it 25 + * under the terms and conditions of the GNU General Public License, 26 + * version 2, as published by the Free Software Foundation. 27 + * 28 + * This program is distributed in the hope it will be useful, but WITHOUT 29 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 30 + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for 31 + * more details. 32 + * 33 + * You should have received a copy of the GNU General Public License along with 34 + * this program; if not, write to the Free Software Foundation, Inc., 35 + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 36 + */ 37 + 38 + #include <stdio.h> 39 + #include <stdlib.h> 40 + #include <sys/types.h> 41 + #include <sys/stat.h> 42 + #include <sys/socket.h> 43 + #include <sys/mman.h> 44 + #include <linux/if_packet.h> 45 + #include <linux/filter.h> 46 + #include <ctype.h> 47 + #include <fcntl.h> 48 + #include <unistd.h> 49 + #include <bits/wordsize.h> 50 + #include <net/ethernet.h> 51 + #include <netinet/ip.h> 52 + #include <arpa/inet.h> 53 + #include <stdint.h> 54 + #include <string.h> 55 + #include <assert.h> 56 + #include <net/if.h> 57 + #include <inttypes.h> 58 + #include <poll.h> 59 + 60 + #include "psock_lib.h" 61 + 62 + #ifndef bug_on 63 + # define bug_on(cond) assert(!(cond)) 64 + #endif 65 + 66 + #ifndef __aligned_tpacket 67 + # define __aligned_tpacket __attribute__((aligned(TPACKET_ALIGNMENT))) 68 + #endif 69 + 70 + #ifndef __align_tpacket 71 + # define __align_tpacket(x) __attribute__((aligned(TPACKET_ALIGN(x)))) 72 + #endif 73 + 74 + #define BLOCK_STATUS(x) ((x)->h1.block_status) 75 + #define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) 76 + #define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) 77 + #define BLOCK_LEN(x) ((x)->h1.blk_len) 78 + #define BLOCK_SNUM(x) ((x)->h1.seq_num) 79 + #define BLOCK_O2PRIV(x) ((x)->offset_to_priv) 80 + #define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) 81 + #define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) 82 + #define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) 83 + #define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) 84 + 85 + #define NUM_PACKETS 100 86 + 87 + struct ring { 88 + struct iovec *rd; 89 + uint8_t *mm_space; 90 + size_t mm_len, rd_len; 91 + struct sockaddr_ll ll; 92 + void (*walk)(int sock, struct ring *ring); 93 + int type, rd_num, flen, version; 94 + union { 95 + struct tpacket_req req; 96 + struct tpacket_req3 req3; 97 + }; 98 + }; 99 + 100 + struct block_desc { 101 + uint32_t version; 102 + uint32_t offset_to_priv; 103 + struct tpacket_hdr_v1 h1; 104 + }; 105 + 106 + union frame_map { 107 + struct { 108 + struct tpacket_hdr tp_h __aligned_tpacket; 109 + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket_hdr)); 110 + } *v1; 111 + struct { 112 + struct tpacket2_hdr tp_h __aligned_tpacket; 113 + struct sockaddr_ll s_ll __align_tpacket(sizeof(struct tpacket2_hdr)); 114 + } *v2; 115 + void *raw; 116 + }; 117 + 118 + static unsigned int total_packets, total_bytes; 119 + 120 + static int pfsocket(int ver) 121 + { 122 + int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 123 + if (sock == -1) { 124 + perror("socket"); 125 + exit(1); 126 + } 127 + 128 + ret = setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver)); 129 + if (ret == -1) { 130 + perror("setsockopt"); 131 + exit(1); 132 + } 133 + 134 + return sock; 135 + } 136 + 137 + static void status_bar_update(void) 138 + { 139 + if (total_packets % 10 == 0) { 140 + fprintf(stderr, "."); 141 + fflush(stderr); 142 + } 143 + } 144 + 145 + static void test_payload(void *pay, size_t len) 146 + { 147 + struct ethhdr *eth = pay; 148 + 149 + if (len < sizeof(struct ethhdr)) { 150 + fprintf(stderr, "test_payload: packet too " 151 + "small: %zu bytes!\n", len); 152 + exit(1); 153 + } 154 + 155 + if (eth->h_proto != htons(ETH_P_IP)) { 156 + fprintf(stderr, "test_payload: wrong ethernet " 157 + "type: 0x%x!\n", ntohs(eth->h_proto)); 158 + exit(1); 159 + } 160 + } 161 + 162 + static void create_payload(void *pay, size_t *len) 163 + { 164 + int i; 165 + struct ethhdr *eth = pay; 166 + struct iphdr *ip = pay + sizeof(*eth); 167 + 168 + /* Lets create some broken crap, that still passes 169 + * our BPF filter. 170 + */ 171 + 172 + *len = DATA_LEN + 42; 173 + 174 + memset(pay, 0xff, ETH_ALEN * 2); 175 + eth->h_proto = htons(ETH_P_IP); 176 + 177 + for (i = 0; i < sizeof(*ip); ++i) 178 + ((uint8_t *) pay)[i + sizeof(*eth)] = (uint8_t) rand(); 179 + 180 + ip->ihl = 5; 181 + ip->version = 4; 182 + ip->protocol = 0x11; 183 + ip->frag_off = 0; 184 + ip->ttl = 64; 185 + ip->tot_len = htons((uint16_t) *len - sizeof(*eth)); 186 + 187 + ip->saddr = htonl(INADDR_LOOPBACK); 188 + ip->daddr = htonl(INADDR_LOOPBACK); 189 + 190 + memset(pay + sizeof(*eth) + sizeof(*ip), 191 + DATA_CHAR, DATA_LEN); 192 + } 193 + 194 + static inline int __v1_rx_kernel_ready(struct tpacket_hdr *hdr) 195 + { 196 + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); 197 + } 198 + 199 + static inline void __v1_rx_user_ready(struct tpacket_hdr *hdr) 200 + { 201 + hdr->tp_status = TP_STATUS_KERNEL; 202 + __sync_synchronize(); 203 + } 204 + 205 + static inline int __v2_rx_kernel_ready(struct tpacket2_hdr *hdr) 206 + { 207 + return ((hdr->tp_status & TP_STATUS_USER) == TP_STATUS_USER); 208 + } 209 + 210 + static inline void __v2_rx_user_ready(struct tpacket2_hdr *hdr) 211 + { 212 + hdr->tp_status = TP_STATUS_KERNEL; 213 + __sync_synchronize(); 214 + } 215 + 216 + static inline int __v1_v2_rx_kernel_ready(void *base, int version) 217 + { 218 + switch (version) { 219 + case TPACKET_V1: 220 + return __v1_rx_kernel_ready(base); 221 + case TPACKET_V2: 222 + return __v2_rx_kernel_ready(base); 223 + default: 224 + bug_on(1); 225 + return 0; 226 + } 227 + } 228 + 229 + static inline void __v1_v2_rx_user_ready(void *base, int version) 230 + { 231 + switch (version) { 232 + case TPACKET_V1: 233 + __v1_rx_user_ready(base); 234 + break; 235 + case TPACKET_V2: 236 + __v2_rx_user_ready(base); 237 + break; 238 + } 239 + } 240 + 241 + static void walk_v1_v2_rx(int sock, struct ring *ring) 242 + { 243 + struct pollfd pfd; 244 + int udp_sock[2]; 245 + union frame_map ppd; 246 + unsigned int frame_num = 0; 247 + 248 + bug_on(ring->type != PACKET_RX_RING); 249 + 250 + pair_udp_open(udp_sock, PORT_BASE); 251 + pair_udp_setfilter(sock); 252 + 253 + memset(&pfd, 0, sizeof(pfd)); 254 + pfd.fd = sock; 255 + pfd.events = POLLIN | POLLERR; 256 + pfd.revents = 0; 257 + 258 + pair_udp_send(udp_sock, NUM_PACKETS); 259 + 260 + while (total_packets < NUM_PACKETS * 2) { 261 + while (__v1_v2_rx_kernel_ready(ring->rd[frame_num].iov_base, 262 + ring->version)) { 263 + ppd.raw = ring->rd[frame_num].iov_base; 264 + 265 + switch (ring->version) { 266 + case TPACKET_V1: 267 + test_payload((uint8_t *) ppd.raw + ppd.v1->tp_h.tp_mac, 268 + ppd.v1->tp_h.tp_snaplen); 269 + total_bytes += ppd.v1->tp_h.tp_snaplen; 270 + break; 271 + 272 + case TPACKET_V2: 273 + test_payload((uint8_t *) ppd.raw + ppd.v2->tp_h.tp_mac, 274 + ppd.v2->tp_h.tp_snaplen); 275 + total_bytes += ppd.v2->tp_h.tp_snaplen; 276 + break; 277 + } 278 + 279 + status_bar_update(); 280 + total_packets++; 281 + 282 + __v1_v2_rx_user_ready(ppd.raw, ring->version); 283 + 284 + frame_num = (frame_num + 1) % ring->rd_num; 285 + } 286 + 287 + poll(&pfd, 1, 1); 288 + } 289 + 290 + pair_udp_close(udp_sock); 291 + 292 + if (total_packets != 2 * NUM_PACKETS) { 293 + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", 294 + ring->version, total_packets, NUM_PACKETS); 295 + exit(1); 296 + } 297 + 298 + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); 299 + } 300 + 301 + static inline int __v1_tx_kernel_ready(struct tpacket_hdr *hdr) 302 + { 303 + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); 304 + } 305 + 306 + static inline void __v1_tx_user_ready(struct tpacket_hdr *hdr) 307 + { 308 + hdr->tp_status = TP_STATUS_SEND_REQUEST; 309 + __sync_synchronize(); 310 + } 311 + 312 + static inline int __v2_tx_kernel_ready(struct tpacket2_hdr *hdr) 313 + { 314 + return ((hdr->tp_status & TP_STATUS_AVAILABLE) == TP_STATUS_AVAILABLE); 315 + } 316 + 317 + static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) 318 + { 319 + hdr->tp_status = TP_STATUS_SEND_REQUEST; 320 + __sync_synchronize(); 321 + } 322 + 323 + static inline int __v1_v2_tx_kernel_ready(void *base, int version) 324 + { 325 + switch (version) { 326 + case TPACKET_V1: 327 + return __v1_tx_kernel_ready(base); 328 + case TPACKET_V2: 329 + return __v2_tx_kernel_ready(base); 330 + default: 331 + bug_on(1); 332 + return 0; 333 + } 334 + } 335 + 336 + static inline void __v1_v2_tx_user_ready(void *base, int version) 337 + { 338 + switch (version) { 339 + case TPACKET_V1: 340 + __v1_tx_user_ready(base); 341 + break; 342 + case TPACKET_V2: 343 + __v2_tx_user_ready(base); 344 + break; 345 + } 346 + } 347 + 348 + static void __v1_v2_set_packet_loss_discard(int sock) 349 + { 350 + int ret, discard = 1; 351 + 352 + ret = setsockopt(sock, SOL_PACKET, PACKET_LOSS, (void *) &discard, 353 + sizeof(discard)); 354 + if (ret == -1) { 355 + perror("setsockopt"); 356 + exit(1); 357 + } 358 + } 359 + 360 + static void walk_v1_v2_tx(int sock, struct ring *ring) 361 + { 362 + struct pollfd pfd; 363 + int rcv_sock, ret; 364 + size_t packet_len; 365 + union frame_map ppd; 366 + char packet[1024]; 367 + unsigned int frame_num = 0, got = 0; 368 + struct sockaddr_ll ll = { 369 + .sll_family = PF_PACKET, 370 + .sll_halen = ETH_ALEN, 371 + }; 372 + 373 + bug_on(ring->type != PACKET_TX_RING); 374 + bug_on(ring->rd_num < NUM_PACKETS); 375 + 376 + rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 377 + if (rcv_sock == -1) { 378 + perror("socket"); 379 + exit(1); 380 + } 381 + 382 + pair_udp_setfilter(rcv_sock); 383 + 384 + ll.sll_ifindex = if_nametoindex("lo"); 385 + ret = bind(rcv_sock, (struct sockaddr *) &ll, sizeof(ll)); 386 + if (ret == -1) { 387 + perror("bind"); 388 + exit(1); 389 + } 390 + 391 + memset(&pfd, 0, sizeof(pfd)); 392 + pfd.fd = sock; 393 + pfd.events = POLLOUT | POLLERR; 394 + pfd.revents = 0; 395 + 396 + total_packets = NUM_PACKETS; 397 + create_payload(packet, &packet_len); 398 + 399 + while (total_packets > 0) { 400 + while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base, 401 + ring->version) && 402 + total_packets > 0) { 403 + ppd.raw = ring->rd[frame_num].iov_base; 404 + 405 + switch (ring->version) { 406 + case TPACKET_V1: 407 + ppd.v1->tp_h.tp_snaplen = packet_len; 408 + ppd.v1->tp_h.tp_len = packet_len; 409 + 410 + memcpy((uint8_t *) ppd.raw + TPACKET_HDRLEN - 411 + sizeof(struct sockaddr_ll), packet, 412 + packet_len); 413 + total_bytes += ppd.v1->tp_h.tp_snaplen; 414 + break; 415 + 416 + case TPACKET_V2: 417 + ppd.v2->tp_h.tp_snaplen = packet_len; 418 + ppd.v2->tp_h.tp_len = packet_len; 419 + 420 + memcpy((uint8_t *) ppd.raw + TPACKET2_HDRLEN - 421 + sizeof(struct sockaddr_ll), packet, 422 + packet_len); 423 + total_bytes += ppd.v2->tp_h.tp_snaplen; 424 + break; 425 + } 426 + 427 + status_bar_update(); 428 + total_packets--; 429 + 430 + __v1_v2_tx_user_ready(ppd.raw, ring->version); 431 + 432 + frame_num = (frame_num + 1) % ring->rd_num; 433 + } 434 + 435 + poll(&pfd, 1, 1); 436 + } 437 + 438 + bug_on(total_packets != 0); 439 + 440 + ret = sendto(sock, NULL, 0, 0, NULL, 0); 441 + if (ret == -1) { 442 + perror("sendto"); 443 + exit(1); 444 + } 445 + 446 + while ((ret = recvfrom(rcv_sock, packet, sizeof(packet), 447 + 0, NULL, NULL)) > 0 && 448 + total_packets < NUM_PACKETS) { 449 + got += ret; 450 + test_payload(packet, ret); 451 + 452 + status_bar_update(); 453 + total_packets++; 454 + } 455 + 456 + close(rcv_sock); 457 + 458 + if (total_packets != NUM_PACKETS) { 459 + fprintf(stderr, "walk_v%d_rx: received %u out of %u pkts\n", 460 + ring->version, total_packets, NUM_PACKETS); 461 + exit(1); 462 + } 463 + 464 + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, got); 465 + } 466 + 467 + static void walk_v1_v2(int sock, struct ring *ring) 468 + { 469 + if (ring->type == PACKET_RX_RING) 470 + walk_v1_v2_rx(sock, ring); 471 + else 472 + walk_v1_v2_tx(sock, ring); 473 + } 474 + 475 + static uint64_t __v3_prev_block_seq_num = 0; 476 + 477 + void __v3_test_block_seq_num(struct block_desc *pbd) 478 + { 479 + if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) { 480 + fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected " 481 + "seq:%"PRIu64" != actual seq:%"PRIu64"\n", 482 + __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1, 483 + (uint64_t) BLOCK_SNUM(pbd)); 484 + exit(1); 485 + } 486 + 487 + __v3_prev_block_seq_num = BLOCK_SNUM(pbd); 488 + } 489 + 490 + static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) 491 + { 492 + if (BLOCK_NUM_PKTS(pbd)) { 493 + if (bytes != BLOCK_LEN(pbd)) { 494 + fprintf(stderr, "\nblock:%u with %upackets, expected " 495 + "len:%u != actual len:%u\n", block_num, 496 + BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); 497 + exit(1); 498 + } 499 + } else { 500 + if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) { 501 + fprintf(stderr, "\nblock:%u, expected len:%lu != " 502 + "actual len:%u\n", block_num, BLOCK_HDR_LEN, 503 + BLOCK_LEN(pbd)); 504 + exit(1); 505 + } 506 + } 507 + } 508 + 509 + static void __v3_test_block_header(struct block_desc *pbd, const int block_num) 510 + { 511 + uint32_t block_status = BLOCK_STATUS(pbd); 512 + 513 + if ((block_status & TP_STATUS_USER) == 0) { 514 + fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num); 515 + exit(1); 516 + } 517 + 518 + __v3_test_block_seq_num(pbd); 519 + } 520 + 521 + static void __v3_walk_block(struct block_desc *pbd, const int block_num) 522 + { 523 + int num_pkts = BLOCK_NUM_PKTS(pbd), i; 524 + unsigned long bytes = 0; 525 + unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13); 526 + struct tpacket3_hdr *ppd; 527 + 528 + __v3_test_block_header(pbd, block_num); 529 + 530 + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); 531 + for (i = 0; i < num_pkts; ++i) { 532 + bytes += ppd->tp_snaplen; 533 + 534 + if (ppd->tp_next_offset) 535 + bytes_with_padding += ppd->tp_next_offset; 536 + else 537 + bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); 538 + 539 + test_payload((uint8_t *) ppd + ppd->tp_mac, ppd->tp_snaplen); 540 + 541 + status_bar_update(); 542 + total_packets++; 543 + 544 + ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); 545 + __sync_synchronize(); 546 + } 547 + 548 + __v3_test_block_len(pbd, bytes_with_padding, block_num); 549 + total_bytes += bytes; 550 + } 551 + 552 + void __v3_flush_block(struct block_desc *pbd) 553 + { 554 + BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; 555 + __sync_synchronize(); 556 + } 557 + 558 + static void walk_v3_rx(int sock, struct ring *ring) 559 + { 560 + unsigned int block_num = 0; 561 + struct pollfd pfd; 562 + struct block_desc *pbd; 563 + int udp_sock[2]; 564 + 565 + bug_on(ring->type != PACKET_RX_RING); 566 + 567 + pair_udp_open(udp_sock, PORT_BASE); 568 + pair_udp_setfilter(sock); 569 + 570 + memset(&pfd, 0, sizeof(pfd)); 571 + pfd.fd = sock; 572 + pfd.events = POLLIN | POLLERR; 573 + pfd.revents = 0; 574 + 575 + pair_udp_send(udp_sock, NUM_PACKETS); 576 + 577 + while (total_packets < NUM_PACKETS * 2) { 578 + pbd = (struct block_desc *) ring->rd[block_num].iov_base; 579 + 580 + while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) 581 + poll(&pfd, 1, 1); 582 + 583 + __v3_walk_block(pbd, block_num); 584 + __v3_flush_block(pbd); 585 + 586 + block_num = (block_num + 1) % ring->rd_num; 587 + } 588 + 589 + pair_udp_close(udp_sock); 590 + 591 + if (total_packets != 2 * NUM_PACKETS) { 592 + fprintf(stderr, "walk_v3_rx: received %u out of %u pkts\n", 593 + total_packets, NUM_PACKETS); 594 + exit(1); 595 + } 596 + 597 + fprintf(stderr, " %u pkts (%u bytes)", NUM_PACKETS, total_bytes >> 1); 598 + } 599 + 600 + static void walk_v3(int sock, struct ring *ring) 601 + { 602 + if (ring->type == PACKET_RX_RING) 603 + walk_v3_rx(sock, ring); 604 + else 605 + bug_on(1); 606 + } 607 + 608 + static void __v1_v2_fill(struct ring *ring, unsigned int blocks) 609 + { 610 + ring->req.tp_block_size = getpagesize() << 2; 611 + ring->req.tp_frame_size = TPACKET_ALIGNMENT << 7; 612 + ring->req.tp_block_nr = blocks; 613 + 614 + ring->req.tp_frame_nr = ring->req.tp_block_size / 615 + ring->req.tp_frame_size * 616 + ring->req.tp_block_nr; 617 + 618 + ring->mm_len = ring->req.tp_block_size * ring->req.tp_block_nr; 619 + ring->walk = walk_v1_v2; 620 + ring->rd_num = ring->req.tp_frame_nr; 621 + ring->flen = ring->req.tp_frame_size; 622 + } 623 + 624 + static void __v3_fill(struct ring *ring, unsigned int blocks) 625 + { 626 + ring->req3.tp_retire_blk_tov = 64; 627 + ring->req3.tp_sizeof_priv = 13; 628 + ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; 629 + 630 + ring->req3.tp_block_size = getpagesize() << 2; 631 + ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7; 632 + ring->req3.tp_block_nr = blocks; 633 + 634 + ring->req3.tp_frame_nr = ring->req3.tp_block_size / 635 + ring->req3.tp_frame_size * 636 + ring->req3.tp_block_nr; 637 + 638 + ring->mm_len = ring->req3.tp_block_size * ring->req3.tp_block_nr; 639 + ring->walk = walk_v3; 640 + ring->rd_num = ring->req3.tp_block_nr; 641 + ring->flen = ring->req3.tp_block_size; 642 + } 643 + 644 + static void setup_ring(int sock, struct ring *ring, int version, int type) 645 + { 646 + int ret = 0; 647 + unsigned int blocks = 256; 648 + 649 + ring->type = type; 650 + ring->version = version; 651 + 652 + switch (version) { 653 + case TPACKET_V1: 654 + case TPACKET_V2: 655 + if (type == PACKET_TX_RING) 656 + __v1_v2_set_packet_loss_discard(sock); 657 + __v1_v2_fill(ring, blocks); 658 + ret = setsockopt(sock, SOL_PACKET, type, &ring->req, 659 + sizeof(ring->req)); 660 + break; 661 + 662 + case TPACKET_V3: 663 + __v3_fill(ring, blocks); 664 + ret = setsockopt(sock, SOL_PACKET, type, &ring->req3, 665 + sizeof(ring->req3)); 666 + break; 667 + } 668 + 669 + if (ret == -1) { 670 + perror("setsockopt"); 671 + exit(1); 672 + } 673 + 674 + ring->rd_len = ring->rd_num * sizeof(*ring->rd); 675 + ring->rd = malloc(ring->rd_len); 676 + if (ring->rd == NULL) { 677 + perror("malloc"); 678 + exit(1); 679 + } 680 + 681 + total_packets = 0; 682 + total_bytes = 0; 683 + } 684 + 685 + static void mmap_ring(int sock, struct ring *ring) 686 + { 687 + int i; 688 + 689 + ring->mm_space = mmap(0, ring->mm_len, PROT_READ | PROT_WRITE, 690 + MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0); 691 + if (ring->mm_space == MAP_FAILED) { 692 + perror("mmap"); 693 + exit(1); 694 + } 695 + 696 + memset(ring->rd, 0, ring->rd_len); 697 + for (i = 0; i < ring->rd_num; ++i) { 698 + ring->rd[i].iov_base = ring->mm_space + (i * ring->flen); 699 + ring->rd[i].iov_len = ring->flen; 700 + } 701 + } 702 + 703 + static void bind_ring(int sock, struct ring *ring) 704 + { 705 + int ret; 706 + 707 + ring->ll.sll_family = PF_PACKET; 708 + ring->ll.sll_protocol = htons(ETH_P_ALL); 709 + ring->ll.sll_ifindex = if_nametoindex("lo"); 710 + ring->ll.sll_hatype = 0; 711 + ring->ll.sll_pkttype = 0; 712 + ring->ll.sll_halen = 0; 713 + 714 + ret = bind(sock, (struct sockaddr *) &ring->ll, sizeof(ring->ll)); 715 + if (ret == -1) { 716 + perror("bind"); 717 + exit(1); 718 + } 719 + } 720 + 721 + static void walk_ring(int sock, struct ring *ring) 722 + { 723 + ring->walk(sock, ring); 724 + } 725 + 726 + static void unmap_ring(int sock, struct ring *ring) 727 + { 728 + munmap(ring->mm_space, ring->mm_len); 729 + free(ring->rd); 730 + } 731 + 732 + static int test_kernel_bit_width(void) 733 + { 734 + char in[512], *ptr; 735 + int num = 0, fd; 736 + ssize_t ret; 737 + 738 + fd = open("/proc/kallsyms", O_RDONLY); 739 + if (fd == -1) { 740 + perror("open"); 741 + exit(1); 742 + } 743 + 744 + ret = read(fd, in, sizeof(in)); 745 + if (ret <= 0) { 746 + perror("read"); 747 + exit(1); 748 + } 749 + 750 + close(fd); 751 + 752 + ptr = in; 753 + while(!isspace(*ptr)) { 754 + num++; 755 + ptr++; 756 + } 757 + 758 + return num * 4; 759 + } 760 + 761 + static int test_user_bit_width(void) 762 + { 763 + return __WORDSIZE; 764 + } 765 + 766 + static const char *tpacket_str[] = { 767 + [TPACKET_V1] = "TPACKET_V1", 768 + [TPACKET_V2] = "TPACKET_V2", 769 + [TPACKET_V3] = "TPACKET_V3", 770 + }; 771 + 772 + static const char *type_str[] = { 773 + [PACKET_RX_RING] = "PACKET_RX_RING", 774 + [PACKET_TX_RING] = "PACKET_TX_RING", 775 + }; 776 + 777 + static int test_tpacket(int version, int type) 778 + { 779 + int sock; 780 + struct ring ring; 781 + 782 + fprintf(stderr, "test: %s with %s ", tpacket_str[version], 783 + type_str[type]); 784 + fflush(stderr); 785 + 786 + if (version == TPACKET_V1 && 787 + test_kernel_bit_width() != test_user_bit_width()) { 788 + fprintf(stderr, "test: skip %s %s since user and kernel " 789 + "space have different bit width\n", 790 + tpacket_str[version], type_str[type]); 791 + return 0; 792 + } 793 + 794 + sock = pfsocket(version); 795 + memset(&ring, 0, sizeof(ring)); 796 + setup_ring(sock, &ring, version, type); 797 + mmap_ring(sock, &ring); 798 + bind_ring(sock, &ring); 799 + walk_ring(sock, &ring); 800 + unmap_ring(sock, &ring); 801 + close(sock); 802 + 803 + fprintf(stderr, "\n"); 804 + return 0; 805 + } 806 + 807 + int main(void) 808 + { 809 + int ret = 0; 810 + 811 + ret |= test_tpacket(TPACKET_V1, PACKET_RX_RING); 812 + ret |= test_tpacket(TPACKET_V1, PACKET_TX_RING); 813 + 814 + ret |= test_tpacket(TPACKET_V2, PACKET_RX_RING); 815 + ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING); 816 + 817 + ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING); 818 + 819 + if (ret) 820 + return 1; 821 + 822 + printf("OK. All tests passed\n"); 823 + return 0; 824 + }
+10
tools/testing/selftests/net/run_afpackettests
··· 14 14 else 15 15 echo "[PASS]" 16 16 fi 17 + 18 + echo "--------------------" 19 + echo "running psock_tpacket test" 20 + echo "--------------------" 21 + ./psock_tpacket 22 + if [ $? -ne 0 ]; then 23 + echo "[FAIL]" 24 + else 25 + echo "[PASS]" 26 + fi