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_SEND_REQUEST | TP_STATUS_SENDING)); 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_SEND_REQUEST | TP_STATUS_SENDING)); 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 } 825