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