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