1 // SPDX-License-Identifier: GPL-2.0
3 /* Test hardware checksum offload: Rx + Tx, IPv4 + IPv6, TCP + UDP.
5 * The test runs on two machines to exercise the NIC. For this reason it
6 * is not integrated in kselftests.
8 * CMD=$((./csum -[46] -[tu] -S $SADDR -D $DADDR -[RT] -r 1 $EXTRA_ARGS))
12 * The sender sends packets with a known checksum field using PF_INET(6)
15 * good packet: $CMD [-t]
16 * bad packet: $CMD [-t] -E
18 * The receiver reads UDP packets with a UDP socket. This is not an
19 * option for TCP packets ('-t'). Optionally insert an iptables filter
20 * to avoid these entering the real protocol stack.
22 * The receiver also reads all packets with a PF_PACKET socket, to
23 * observe whether both good and bad packets arrive on the host. And to
24 * read the optional TP_STATUS_CSUM_VALID bit. This requires setting
25 * option PACKET_AUXDATA, and works only for CHECKSUM_UNNECESSARY.
29 * The sender needs to build CHECKSUM_PARTIAL packets to exercise tx
32 * The sender can sends packets with a UDP socket.
34 * Optionally crafts a packet that sums up to zero to verify that the
35 * device writes negative zero 0xFFFF in this case to distinguish from
36 * 0x0000 (checksum disabled), as required by RFC 768. Hit this case
37 * by choosing a specific source port.
39 * good packet: $CMD -U
40 * zero csum: $CMD -U -Z
42 * The sender can also build packets with PF_PACKET with PACKET_VNET_HDR,
43 * to cover more protocols. PF_PACKET requires passing src and dst mac
46 * good packet: $CMD -s $smac -d $dmac -p [-t]
48 * Argument '-z' sends UDP packets with a 0x000 checksum disabled field,
49 * to verify that the NIC passes these packets unmodified.
51 * Argument '-e' adds a transport mode encapsulation header between
52 * network and transport header. This will fail for devices that parse
53 * headers. Should work on devices that implement protocol agnostic tx
54 * checksum offload (NETIF_F_HW_CSUM).
56 * Argument '-r $SEED' optionally randomizes header, payload and length
57 * to increase coverage between packets sent. SEED 1 further chooses a
58 * different seed for each run (and logs this for reproducibility). It
59 * is advised to enable this for extra coverage in continuous testing.
64 #include <arpa/inet.h>
65 #include <asm/byteorder.h>
68 #include <linux/filter.h>
69 #include <linux/if_packet.h>
70 #include <linux/ipv6.h>
71 #include <linux/virtio_net.h>
72 #include <net/ethernet.h>
74 #include <netinet/if_ether.h>
75 #include <netinet/in.h>
76 #include <netinet/ip.h>
77 #include <netinet/ip6.h>
78 #include <netinet/tcp.h>
79 #include <netinet/udp.h>
88 #include <sys/socket.h>
91 #include <sys/types.h>
94 #include "kselftest.h"
96 static bool cfg_bad_csum
;
97 static int cfg_family
= PF_INET6
;
98 static int cfg_num_pkt
= 4;
99 static bool cfg_do_rx
= true;
100 static bool cfg_do_tx
= true;
101 static bool cfg_encap
;
102 static char *cfg_ifname
= "eth0";
103 static char *cfg_mac_dst
;
104 static char *cfg_mac_src
;
105 static int cfg_proto
= IPPROTO_UDP
;
106 static int cfg_payload_char
= 'a';
107 static int cfg_payload_len
= 100;
108 static uint16_t cfg_port_dst
= 34000;
109 static uint16_t cfg_port_src
= 33000;
110 static uint16_t cfg_port_src_encap
= 33001;
111 static unsigned int cfg_random_seed
;
112 static int cfg_rcvbuf
= 1 << 22; /* be able to queue large cfg_num_pkt */
113 static bool cfg_send_pfpacket
;
114 static bool cfg_send_udp
;
115 static int cfg_timeout_ms
= 2000;
116 static bool cfg_zero_disable
; /* skip checksum: set to zero (udp only) */
117 static bool cfg_zero_sum
; /* create packet that adds up to zero */
119 static struct sockaddr_in cfg_daddr4
= {.sin_family
= AF_INET
};
120 static struct sockaddr_in cfg_saddr4
= {.sin_family
= AF_INET
};
121 static struct sockaddr_in6 cfg_daddr6
= {.sin6_family
= AF_INET6
};
122 static struct sockaddr_in6 cfg_saddr6
= {.sin6_family
= AF_INET6
};
124 #define ENC_HEADER_LEN (sizeof(struct udphdr) + sizeof(struct udp_encap_hdr))
125 #define MAX_HEADER_LEN (sizeof(struct ipv6hdr) + ENC_HEADER_LEN + sizeof(struct tcphdr))
126 #define MAX_PAYLOAD_LEN 1024
128 /* Trivial demo encap. Stand-in for transport layer protocols like ESP or PSP */
129 struct udp_encap_hdr
{
134 /* Ipaddrs, for pseudo csum. Global var is ugly, pass through funcs was worse */
135 static void *iph_addr_p
;
137 static unsigned long gettimeofday_ms(void)
141 gettimeofday(&tv
, NULL
);
142 return (tv
.tv_sec
* 1000UL) + (tv
.tv_usec
/ 1000UL);
145 static uint32_t checksum_nofold(char *data
, size_t len
, uint32_t sum
)
147 uint16_t *words
= (uint16_t *)data
;
150 for (i
= 0; i
< len
/ 2; i
++)
154 sum
+= ((unsigned char *)data
)[len
- 1];
159 static uint16_t checksum_fold(void *data
, size_t len
, uint32_t sum
)
161 sum
= checksum_nofold(data
, len
, sum
);
164 sum
= (sum
& 0xFFFF) + (sum
>> 16);
169 static uint16_t checksum(void *th
, uint16_t proto
, size_t len
)
174 alen
= cfg_family
== PF_INET6
? 32 : 8;
176 sum
= checksum_nofold(iph_addr_p
, alen
, 0);
180 /* With CHECKSUM_PARTIAL kernel expects non-inverted pseudo csum */
181 if (cfg_do_tx
&& cfg_send_pfpacket
)
182 return ~checksum_fold(NULL
, 0, sum
);
184 return checksum_fold(th
, len
, sum
);
187 static void *build_packet_ipv4(void *_iph
, uint8_t proto
, unsigned int len
)
189 struct iphdr
*iph
= _iph
;
191 memset(iph
, 0, sizeof(*iph
));
196 iph
->protocol
= proto
;
197 iph
->saddr
= cfg_saddr4
.sin_addr
.s_addr
;
198 iph
->daddr
= cfg_daddr4
.sin_addr
.s_addr
;
199 iph
->tot_len
= htons(sizeof(*iph
) + len
);
200 iph
->check
= checksum_fold(iph
, sizeof(*iph
), 0);
202 iph_addr_p
= &iph
->saddr
;
207 static void *build_packet_ipv6(void *_ip6h
, uint8_t proto
, unsigned int len
)
209 struct ipv6hdr
*ip6h
= _ip6h
;
211 memset(ip6h
, 0, sizeof(*ip6h
));
214 ip6h
->payload_len
= htons(len
);
215 ip6h
->nexthdr
= proto
;
216 ip6h
->hop_limit
= 64;
217 ip6h
->saddr
= cfg_saddr6
.sin6_addr
;
218 ip6h
->daddr
= cfg_daddr6
.sin6_addr
;
220 iph_addr_p
= &ip6h
->saddr
;
225 static void *build_packet_udp(void *_uh
)
227 struct udphdr
*uh
= _uh
;
229 uh
->source
= htons(cfg_port_src
);
230 uh
->dest
= htons(cfg_port_dst
);
231 uh
->len
= htons(sizeof(*uh
) + cfg_payload_len
);
234 /* choose source port so that uh->check adds up to zero */
237 uh
->source
= checksum(uh
, IPPROTO_UDP
, sizeof(*uh
) + cfg_payload_len
);
239 fprintf(stderr
, "tx: changing sport: %hu -> %hu\n",
240 cfg_port_src
, ntohs(uh
->source
));
241 cfg_port_src
= ntohs(uh
->source
);
244 if (cfg_zero_disable
)
247 uh
->check
= checksum(uh
, IPPROTO_UDP
, sizeof(*uh
) + cfg_payload_len
);
250 uh
->check
= ~uh
->check
;
252 fprintf(stderr
, "tx: sending checksum: 0x%x\n", uh
->check
);
256 static void *build_packet_tcp(void *_th
)
258 struct tcphdr
*th
= _th
;
260 th
->source
= htons(cfg_port_src
);
261 th
->dest
= htons(cfg_port_dst
);
265 th
->check
= checksum(th
, IPPROTO_TCP
, sizeof(*th
) + cfg_payload_len
);
268 th
->check
= ~th
->check
;
270 fprintf(stderr
, "tx: sending checksum: 0x%x\n", th
->check
);
274 static char *build_packet_udp_encap(void *_uh
)
276 struct udphdr
*uh
= _uh
;
277 struct udp_encap_hdr
*eh
= _uh
+ sizeof(*uh
);
279 /* outer dst == inner dst, to simplify BPF filter
280 * outer src != inner src, to demultiplex on recv
282 uh
->dest
= htons(cfg_port_dst
);
283 uh
->source
= htons(cfg_port_src_encap
);
285 uh
->len
= htons(sizeof(*uh
) +
287 sizeof(struct tcphdr
) +
290 eh
->nexthdr
= IPPROTO_TCP
;
292 return build_packet_tcp(eh
+ 1);
295 static char *build_packet(char *buf
, int max_len
, int *len
)
301 if (cfg_random_seed
) {
302 int *buf32
= (void *)buf
;
305 for (i
= 0; i
< (max_len
/ sizeof(int)); i
++)
308 memset(buf
, cfg_payload_char
, max_len
);
311 if (cfg_proto
== IPPROTO_UDP
)
312 tlen
= sizeof(struct udphdr
) + cfg_payload_len
;
314 tlen
= sizeof(struct tcphdr
) + cfg_payload_len
;
318 tlen
+= ENC_HEADER_LEN
;
323 if (cfg_family
== PF_INET
)
324 off
= build_packet_ipv4(buf
, proto
, tlen
);
326 off
= build_packet_ipv6(buf
, proto
, tlen
);
329 off
= build_packet_udp_encap(off
);
330 else if (cfg_proto
== IPPROTO_UDP
)
331 off
= build_packet_udp(off
);
333 off
= build_packet_tcp(off
);
335 /* only pass the payload, but still compute headers for cfg_zero_sum */
337 *len
= cfg_payload_len
;
341 *len
= off
- buf
+ cfg_payload_len
;
345 static int open_inet(int ipproto
, int protocol
)
349 fd
= socket(cfg_family
, ipproto
, protocol
);
351 error(1, errno
, "socket inet");
353 if (cfg_family
== PF_INET6
) {
354 /* may have been updated by cfg_zero_sum */
355 cfg_saddr6
.sin6_port
= htons(cfg_port_src
);
357 if (bind(fd
, (void *)&cfg_saddr6
, sizeof(cfg_saddr6
)))
358 error(1, errno
, "bind dgram 6");
359 if (connect(fd
, (void *)&cfg_daddr6
, sizeof(cfg_daddr6
)))
360 error(1, errno
, "connect dgram 6");
362 /* may have been updated by cfg_zero_sum */
363 cfg_saddr4
.sin_port
= htons(cfg_port_src
);
365 if (bind(fd
, (void *)&cfg_saddr4
, sizeof(cfg_saddr4
)))
366 error(1, errno
, "bind dgram 4");
367 if (connect(fd
, (void *)&cfg_daddr4
, sizeof(cfg_daddr4
)))
368 error(1, errno
, "connect dgram 4");
374 static int open_packet(void)
378 fd
= socket(PF_PACKET
, SOCK_RAW
, 0);
380 error(1, errno
, "socket packet");
382 if (setsockopt(fd
, SOL_PACKET
, PACKET_VNET_HDR
, &one
, sizeof(one
)))
383 error(1, errno
, "setsockopt packet_vnet_ndr");
388 static void send_inet(int fd
, const char *buf
, int len
)
392 ret
= write(fd
, buf
, len
);
394 error(1, errno
, "write");
396 error(1, 0, "write: %d", ret
);
399 static void eth_str_to_addr(const char *str
, unsigned char *eth
)
401 if (sscanf(str
, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
402 ð
[0], ð
[1], ð
[2], ð
[3], ð
[4], ð
[5]) != 6)
403 error(1, 0, "cannot parse mac addr %s", str
);
406 static void send_packet(int fd
, const char *buf
, int len
)
408 struct virtio_net_hdr vh
= {0};
409 struct sockaddr_ll addr
= {0};
410 struct msghdr msg
= {0};
415 addr
.sll_family
= AF_PACKET
;
416 addr
.sll_halen
= ETH_ALEN
;
417 addr
.sll_ifindex
= if_nametoindex(cfg_ifname
);
418 if (!addr
.sll_ifindex
)
419 error(1, errno
, "if_nametoindex %s", cfg_ifname
);
421 vh
.flags
= VIRTIO_NET_HDR_F_NEEDS_CSUM
;
422 if (cfg_family
== PF_INET6
) {
423 vh
.csum_start
= sizeof(struct ethhdr
) + sizeof(struct ipv6hdr
);
424 addr
.sll_protocol
= htons(ETH_P_IPV6
);
426 vh
.csum_start
= sizeof(struct ethhdr
) + sizeof(struct iphdr
);
427 addr
.sll_protocol
= htons(ETH_P_IP
);
431 vh
.csum_start
+= ENC_HEADER_LEN
;
433 if (cfg_proto
== IPPROTO_TCP
) {
434 vh
.csum_offset
= __builtin_offsetof(struct tcphdr
, check
);
435 vh
.hdr_len
= vh
.csum_start
+ sizeof(struct tcphdr
);
437 vh
.csum_offset
= __builtin_offsetof(struct udphdr
, check
);
438 vh
.hdr_len
= vh
.csum_start
+ sizeof(struct udphdr
);
441 eth_str_to_addr(cfg_mac_src
, eth
.h_source
);
442 eth_str_to_addr(cfg_mac_dst
, eth
.h_dest
);
443 eth
.h_proto
= addr
.sll_protocol
;
445 iov
[0].iov_base
= &vh
;
446 iov
[0].iov_len
= sizeof(vh
);
448 iov
[1].iov_base
= ð
;
449 iov
[1].iov_len
= sizeof(eth
);
451 iov
[2].iov_base
= (void *)buf
;
452 iov
[2].iov_len
= len
;
455 msg
.msg_iovlen
= ARRAY_SIZE(iov
);
457 msg
.msg_name
= &addr
;
458 msg
.msg_namelen
= sizeof(addr
);
460 ret
= sendmsg(fd
, &msg
, 0);
462 error(1, errno
, "sendmsg packet");
463 if (ret
!= sizeof(vh
) + sizeof(eth
) + len
)
464 error(1, errno
, "sendmsg packet: %u", ret
);
467 static int recv_prepare_udp(void)
471 fd
= socket(cfg_family
, SOCK_DGRAM
, 0);
473 error(1, errno
, "socket r");
475 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
,
476 &cfg_rcvbuf
, sizeof(cfg_rcvbuf
)))
477 error(1, errno
, "setsockopt SO_RCVBUF r");
479 if (cfg_family
== PF_INET6
) {
480 if (bind(fd
, (void *)&cfg_daddr6
, sizeof(cfg_daddr6
)))
481 error(1, errno
, "bind r");
483 if (bind(fd
, (void *)&cfg_daddr4
, sizeof(cfg_daddr4
)))
484 error(1, errno
, "bind r");
490 /* Filter out all traffic that is not cfg_proto with our destination port.
492 * Otherwise background noise may cause PF_PACKET receive queue overflow,
493 * dropping the expected packets and failing the test.
495 static void __recv_prepare_packet_filter(int fd
, int off_nexthdr
, int off_dport
)
497 struct sock_filter filter
[] = {
498 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, SKF_AD_OFF
+ SKF_AD_PKTTYPE
),
499 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, PACKET_HOST
, 0, 4),
500 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, off_nexthdr
),
501 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, cfg_encap
? IPPROTO_UDP
: cfg_proto
, 0, 2),
502 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, off_dport
),
503 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, cfg_port_dst
, 1, 0),
504 BPF_STMT(BPF_RET
+ BPF_K
, 0),
505 BPF_STMT(BPF_RET
+ BPF_K
, 0xFFFF),
507 struct sock_fprog prog
= {};
509 prog
.filter
= filter
;
510 prog
.len
= ARRAY_SIZE(filter
);
511 if (setsockopt(fd
, SOL_SOCKET
, SO_ATTACH_FILTER
, &prog
, sizeof(prog
)))
512 error(1, errno
, "setsockopt filter");
515 static void recv_prepare_packet_filter(int fd
)
517 const int off_dport
= offsetof(struct tcphdr
, dest
); /* same for udp */
519 if (cfg_family
== AF_INET
)
520 __recv_prepare_packet_filter(fd
, offsetof(struct iphdr
, protocol
),
521 sizeof(struct iphdr
) + off_dport
);
523 __recv_prepare_packet_filter(fd
, offsetof(struct ipv6hdr
, nexthdr
),
524 sizeof(struct ipv6hdr
) + off_dport
);
527 static void recv_prepare_packet_bind(int fd
)
529 struct sockaddr_ll laddr
= {0};
531 laddr
.sll_family
= AF_PACKET
;
533 if (cfg_family
== PF_INET
)
534 laddr
.sll_protocol
= htons(ETH_P_IP
);
536 laddr
.sll_protocol
= htons(ETH_P_IPV6
);
538 laddr
.sll_ifindex
= if_nametoindex(cfg_ifname
);
539 if (!laddr
.sll_ifindex
)
540 error(1, 0, "if_nametoindex %s", cfg_ifname
);
542 if (bind(fd
, (void *)&laddr
, sizeof(laddr
)))
543 error(1, errno
, "bind pf_packet");
546 static int recv_prepare_packet(void)
550 fd
= socket(PF_PACKET
, SOCK_DGRAM
, 0);
552 error(1, errno
, "socket p");
554 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
,
555 &cfg_rcvbuf
, sizeof(cfg_rcvbuf
)))
556 error(1, errno
, "setsockopt SO_RCVBUF p");
558 /* enable auxdata to recv checksum status (valid vs unknown) */
559 if (setsockopt(fd
, SOL_PACKET
, PACKET_AUXDATA
, &one
, sizeof(one
)))
560 error(1, errno
, "setsockopt auxdata");
562 /* install filter to restrict packet flow to match */
563 recv_prepare_packet_filter(fd
);
565 /* bind to address family to start packet flow */
566 recv_prepare_packet_bind(fd
);
571 static int recv_udp(int fd
)
573 static char buf
[MAX_PAYLOAD_LEN
];
577 ret
= recv(fd
, buf
, sizeof(buf
), MSG_DONTWAIT
);
578 if (ret
== -1 && errno
== EAGAIN
)
581 error(1, errno
, "recv r");
583 fprintf(stderr
, "rx: udp: len=%u\n", ret
);
590 static int recv_verify_csum(void *th
, int len
, uint16_t sport
, uint16_t csum_field
)
594 csum
= checksum(th
, cfg_proto
, len
);
596 fprintf(stderr
, "rx: pkt: sport=%hu len=%u csum=0x%hx verify=0x%hx\n",
597 sport
, len
, csum_field
, csum
);
599 /* csum must be zero unless cfg_bad_csum indicates bad csum */
600 if (csum
&& !cfg_bad_csum
) {
601 fprintf(stderr
, "pkt: bad csum\n");
603 } else if (cfg_bad_csum
&& !csum
) {
604 fprintf(stderr
, "pkt: good csum, while bad expected\n");
608 if (cfg_zero_sum
&& csum_field
!= 0xFFFF) {
609 fprintf(stderr
, "pkt: zero csum: field should be 0xFFFF, is 0x%hx\n", csum_field
);
616 static int recv_verify_packet_tcp(void *th
, int len
)
618 struct tcphdr
*tcph
= th
;
620 if (len
< sizeof(*tcph
) || tcph
->dest
!= htons(cfg_port_dst
))
623 return recv_verify_csum(th
, len
, ntohs(tcph
->source
), tcph
->check
);
626 static int recv_verify_packet_udp_encap(void *th
, int len
)
628 struct udp_encap_hdr
*eh
= th
;
630 if (len
< sizeof(*eh
) || eh
->nexthdr
!= IPPROTO_TCP
)
633 return recv_verify_packet_tcp(eh
+ 1, len
- sizeof(*eh
));
636 static int recv_verify_packet_udp(void *th
, int len
)
638 struct udphdr
*udph
= th
;
640 if (len
< sizeof(*udph
))
643 if (udph
->dest
!= htons(cfg_port_dst
))
646 if (udph
->source
== htons(cfg_port_src_encap
))
647 return recv_verify_packet_udp_encap(udph
+ 1,
648 len
- sizeof(*udph
));
650 return recv_verify_csum(th
, len
, ntohs(udph
->source
), udph
->check
);
653 static int recv_verify_packet_ipv4(void *nh
, int len
)
655 struct iphdr
*iph
= nh
;
656 uint16_t proto
= cfg_encap
? IPPROTO_UDP
: cfg_proto
;
659 if (len
< sizeof(*iph
) || iph
->protocol
!= proto
)
662 ip_len
= ntohs(iph
->tot_len
);
663 if (ip_len
> len
|| ip_len
< sizeof(*iph
))
667 iph_addr_p
= &iph
->saddr
;
668 if (proto
== IPPROTO_TCP
)
669 return recv_verify_packet_tcp(iph
+ 1, len
- sizeof(*iph
));
671 return recv_verify_packet_udp(iph
+ 1, len
- sizeof(*iph
));
674 static int recv_verify_packet_ipv6(void *nh
, int len
)
676 struct ipv6hdr
*ip6h
= nh
;
677 uint16_t proto
= cfg_encap
? IPPROTO_UDP
: cfg_proto
;
678 uint16_t payload_len
;
680 if (len
< sizeof(*ip6h
) || ip6h
->nexthdr
!= proto
)
683 payload_len
= ntohs(ip6h
->payload_len
);
684 if (payload_len
> len
- sizeof(*ip6h
))
687 iph_addr_p
= &ip6h
->saddr
;
688 if (proto
== IPPROTO_TCP
)
689 return recv_verify_packet_tcp(ip6h
+ 1, payload_len
);
691 return recv_verify_packet_udp(ip6h
+ 1, payload_len
);
694 /* return whether auxdata includes TP_STATUS_CSUM_VALID */
695 static uint32_t recv_get_packet_csum_status(struct msghdr
*msg
)
697 struct tpacket_auxdata
*aux
= NULL
;
700 if (msg
->msg_flags
& MSG_CTRUNC
)
701 error(1, 0, "cmsg: truncated");
703 for (cm
= CMSG_FIRSTHDR(msg
); cm
; cm
= CMSG_NXTHDR(msg
, cm
)) {
704 if (cm
->cmsg_level
!= SOL_PACKET
||
705 cm
->cmsg_type
!= PACKET_AUXDATA
)
706 error(1, 0, "cmsg: level=%d type=%d\n",
707 cm
->cmsg_level
, cm
->cmsg_type
);
709 if (cm
->cmsg_len
!= CMSG_LEN(sizeof(struct tpacket_auxdata
)))
710 error(1, 0, "cmsg: len=%lu expected=%lu",
711 cm
->cmsg_len
, CMSG_LEN(sizeof(struct tpacket_auxdata
)));
713 aux
= (void *)CMSG_DATA(cm
);
717 error(1, 0, "cmsg: no auxdata");
719 return aux
->tp_status
;
722 static int recv_packet(int fd
)
724 static char _buf
[MAX_HEADER_LEN
+ MAX_PAYLOAD_LEN
];
725 unsigned long total
= 0, bad_csums
= 0, bad_validations
= 0;
726 char ctrl
[CMSG_SPACE(sizeof(struct tpacket_auxdata
))];
727 struct pkt
*buf
= (void *)_buf
;
728 struct msghdr msg
= {0};
734 iov
.iov_len
= sizeof(_buf
);
739 msg
.msg_control
= ctrl
;
740 msg
.msg_controllen
= sizeof(ctrl
);
745 len
= recvmsg(fd
, &msg
, MSG_DONTWAIT
);
746 if (len
== -1 && errno
== EAGAIN
)
749 error(1, errno
, "recv p");
751 tp_status
= recv_get_packet_csum_status(&msg
);
753 /* GRO might coalesce randomized packets. Such GSO packets are
754 * then reinitialized for csum offload (CHECKSUM_PARTIAL), with
755 * a pseudo csum. Do not try to validate these checksums.
757 if (tp_status
& TP_STATUS_CSUMNOTREADY
) {
758 fprintf(stderr
, "cmsg: GSO packet has partial csum: skip\n");
762 if (cfg_family
== PF_INET6
)
763 ret
= recv_verify_packet_ipv6(buf
, len
);
765 ret
= recv_verify_packet_ipv4(buf
, len
);
767 if (ret
== -1 /* skip: non-matching */)
774 /* Fail if kernel returns valid for known bad csum.
775 * Do not fail if kernel does not validate a good csum:
776 * Absence of validation does not imply invalid.
778 if (tp_status
& TP_STATUS_CSUM_VALID
&& cfg_bad_csum
) {
779 fprintf(stderr
, "cmsg: expected bad csum, pf_packet returns valid\n");
784 if (bad_csums
|| bad_validations
)
785 error(1, 0, "rx: errors at pf_packet: total=%lu bad_csums=%lu bad_valids=%lu\n",
786 total
, bad_csums
, bad_validations
);
791 static void parse_args(int argc
, char *const argv
[])
793 const char *daddr
= NULL
, *saddr
= NULL
;
796 while ((c
= getopt(argc
, argv
, "46d:D:eEi:l:L:n:r:PRs:S:tTuUzZ")) != -1) {
799 cfg_family
= PF_INET
;
802 cfg_family
= PF_INET6
;
805 cfg_mac_dst
= optarg
;
820 cfg_payload_len
= strtol(optarg
, NULL
, 0);
823 cfg_timeout_ms
= strtol(optarg
, NULL
, 0) * 1000;
826 cfg_num_pkt
= strtol(optarg
, NULL
, 0);
829 cfg_random_seed
= strtol(optarg
, NULL
, 0);
832 cfg_send_pfpacket
= true;
835 /* only Rx: used with two machine tests */
839 cfg_mac_src
= optarg
;
845 cfg_proto
= IPPROTO_TCP
;
848 /* only Tx: used with two machine tests */
852 cfg_proto
= IPPROTO_UDP
;
855 /* send using real udp socket,
856 * to exercise tx checksum offload
861 cfg_zero_disable
= true;
867 error(1, 0, "unknown arg %c", c
);
871 if (!daddr
|| !saddr
)
872 error(1, 0, "Must pass -D <daddr> and -S <saddr>");
874 if (cfg_do_tx
&& cfg_send_pfpacket
&& (!cfg_mac_src
|| !cfg_mac_dst
))
875 error(1, 0, "Transmit with pf_packet requires mac addresses");
877 if (cfg_payload_len
> MAX_PAYLOAD_LEN
)
878 error(1, 0, "Payload length exceeds max");
880 if (cfg_proto
!= IPPROTO_UDP
&& (cfg_zero_sum
|| cfg_zero_disable
))
881 error(1, 0, "Only UDP supports zero csum");
883 if (cfg_zero_sum
&& !cfg_send_udp
)
884 error(1, 0, "Zero checksum conversion requires -U for tx csum offload");
885 if (cfg_zero_sum
&& cfg_bad_csum
)
886 error(1, 0, "Cannot combine zero checksum conversion and invalid checksum");
887 if (cfg_zero_sum
&& cfg_random_seed
)
888 error(1, 0, "Cannot combine zero checksum conversion with randomization");
890 if (cfg_family
== PF_INET6
) {
891 cfg_saddr6
.sin6_port
= htons(cfg_port_src
);
892 cfg_daddr6
.sin6_port
= htons(cfg_port_dst
);
894 if (inet_pton(cfg_family
, daddr
, &cfg_daddr6
.sin6_addr
) != 1)
895 error(1, errno
, "Cannot parse ipv6 -D");
896 if (inet_pton(cfg_family
, saddr
, &cfg_saddr6
.sin6_addr
) != 1)
897 error(1, errno
, "Cannot parse ipv6 -S");
899 cfg_saddr4
.sin_port
= htons(cfg_port_src
);
900 cfg_daddr4
.sin_port
= htons(cfg_port_dst
);
902 if (inet_pton(cfg_family
, daddr
, &cfg_daddr4
.sin_addr
) != 1)
903 error(1, errno
, "Cannot parse ipv4 -D");
904 if (inet_pton(cfg_family
, saddr
, &cfg_saddr4
.sin_addr
) != 1)
905 error(1, errno
, "Cannot parse ipv4 -S");
908 if (cfg_do_tx
&& cfg_random_seed
) {
909 /* special case: time-based seed */
910 if (cfg_random_seed
== 1)
911 cfg_random_seed
= (unsigned int)gettimeofday_ms();
912 srand(cfg_random_seed
);
913 fprintf(stderr
, "randomization seed: %u\n", cfg_random_seed
);
917 static void do_tx(void)
919 static char _buf
[MAX_HEADER_LEN
+ MAX_PAYLOAD_LEN
];
923 buf
= build_packet(_buf
, sizeof(_buf
), &len
);
925 if (cfg_send_pfpacket
)
927 else if (cfg_send_udp
)
928 fd
= open_inet(SOCK_DGRAM
, 0);
930 fd
= open_inet(SOCK_RAW
, IPPROTO_RAW
);
932 for (i
= 0; i
< cfg_num_pkt
; i
++) {
933 if (cfg_send_pfpacket
)
934 send_packet(fd
, buf
, len
);
936 send_inet(fd
, buf
, len
);
938 /* randomize each packet individually to increase coverage */
939 if (cfg_random_seed
) {
940 cfg_payload_len
= rand() % MAX_PAYLOAD_LEN
;
941 buf
= build_packet(_buf
, sizeof(_buf
), &len
);
946 error(1, errno
, "close tx");
949 static void do_rx(int fdp
, int fdr
)
951 unsigned long count_udp
= 0, count_pkt
= 0;
955 tstop
= gettimeofday_ms() + cfg_timeout_ms
;
956 tleft
= cfg_timeout_ms
;
961 if (poll(&pfd
, 1, tleft
) == -1)
962 error(1, errno
, "poll");
964 if (pfd
.revents
& POLLIN
)
965 count_pkt
+= recv_packet(fdp
);
967 if (cfg_proto
== IPPROTO_UDP
)
968 count_udp
+= recv_udp(fdr
);
970 tleft
= tstop
- gettimeofday_ms();
974 error(1, errno
, "close r");
976 error(1, errno
, "close p");
978 if (count_pkt
< cfg_num_pkt
)
979 error(1, 0, "rx: missing packets at pf_packet: %lu < %u",
980 count_pkt
, cfg_num_pkt
);
982 if (cfg_proto
== IPPROTO_UDP
) {
983 if (cfg_bad_csum
&& count_udp
)
984 error(1, 0, "rx: unexpected packets at udp");
985 if (!cfg_bad_csum
&& !count_udp
)
986 error(1, 0, "rx: missing packets at udp");
990 int main(int argc
, char *const argv
[])
992 int fdp
= -1, fdr
= -1; /* -1 to silence -Wmaybe-uninitialized */
994 parse_args(argc
, argv
);
996 /* open receive sockets before transmitting */
998 fdp
= recv_prepare_packet();
999 fdr
= recv_prepare_udp();
1008 fprintf(stderr
, "OK\n");