xref: /openbmc/linux/tools/testing/selftests/net/csum.c (revision 033a71ef)
191a7de85SWillem de Bruijn // SPDX-License-Identifier: GPL-2.0
291a7de85SWillem de Bruijn 
391a7de85SWillem de Bruijn /* Test hardware checksum offload: Rx + Tx, IPv4 + IPv6, TCP + UDP.
491a7de85SWillem de Bruijn  *
591a7de85SWillem de Bruijn  * The test runs on two machines to exercise the NIC. For this reason it
691a7de85SWillem de Bruijn  * is not integrated in kselftests.
791a7de85SWillem de Bruijn  *
891a7de85SWillem de Bruijn  *     CMD=$((./csum -[46] -[tu] -S $SADDR -D $DADDR -[RT] -r 1 $EXTRA_ARGS))
991a7de85SWillem de Bruijn  *
1091a7de85SWillem de Bruijn  * Rx:
1191a7de85SWillem de Bruijn  *
1291a7de85SWillem de Bruijn  * The sender sends packets with a known checksum field using PF_INET(6)
1391a7de85SWillem de Bruijn  * SOCK_RAW sockets.
1491a7de85SWillem de Bruijn  *
1591a7de85SWillem de Bruijn  * good packet: $CMD [-t]
1691a7de85SWillem de Bruijn  * bad packet:  $CMD [-t] -E
1791a7de85SWillem de Bruijn  *
1891a7de85SWillem de Bruijn  * The receiver reads UDP packets with a UDP socket. This is not an
1991a7de85SWillem de Bruijn  * option for TCP packets ('-t'). Optionally insert an iptables filter
2091a7de85SWillem de Bruijn  * to avoid these entering the real protocol stack.
2191a7de85SWillem de Bruijn  *
2291a7de85SWillem de Bruijn  * The receiver also reads all packets with a PF_PACKET socket, to
2391a7de85SWillem de Bruijn  * observe whether both good and bad packets arrive on the host. And to
2491a7de85SWillem de Bruijn  * read the optional TP_STATUS_CSUM_VALID bit. This requires setting
2591a7de85SWillem de Bruijn  * option PACKET_AUXDATA, and works only for CHECKSUM_UNNECESSARY.
2691a7de85SWillem de Bruijn  *
2791a7de85SWillem de Bruijn  * Tx:
2891a7de85SWillem de Bruijn  *
2991a7de85SWillem de Bruijn  * The sender needs to build CHECKSUM_PARTIAL packets to exercise tx
3091a7de85SWillem de Bruijn  * checksum offload.
3191a7de85SWillem de Bruijn  *
3291a7de85SWillem de Bruijn  * The sender can sends packets with a UDP socket.
3391a7de85SWillem de Bruijn  *
3491a7de85SWillem de Bruijn  * Optionally crafts a packet that sums up to zero to verify that the
3591a7de85SWillem de Bruijn  * device writes negative zero 0xFFFF in this case to distinguish from
3691a7de85SWillem de Bruijn  * 0x0000 (checksum disabled), as required by RFC 768. Hit this case
3791a7de85SWillem de Bruijn  * by choosing a specific source port.
3891a7de85SWillem de Bruijn  *
3991a7de85SWillem de Bruijn  * good packet: $CMD -U
4091a7de85SWillem de Bruijn  * zero csum:   $CMD -U -Z
4191a7de85SWillem de Bruijn  *
4291a7de85SWillem de Bruijn  * The sender can also build packets with PF_PACKET with PACKET_VNET_HDR,
4391a7de85SWillem de Bruijn  * to cover more protocols. PF_PACKET requires passing src and dst mac
4491a7de85SWillem de Bruijn  * addresses.
4591a7de85SWillem de Bruijn  *
4691a7de85SWillem de Bruijn  * good packet: $CMD -s $smac -d $dmac -p [-t]
4791a7de85SWillem de Bruijn  *
4891a7de85SWillem de Bruijn  * Argument '-z' sends UDP packets with a 0x000 checksum disabled field,
4991a7de85SWillem de Bruijn  * to verify that the NIC passes these packets unmodified.
5091a7de85SWillem de Bruijn  *
5191a7de85SWillem de Bruijn  * Argument '-e' adds a transport mode encapsulation header between
5291a7de85SWillem de Bruijn  * network and transport header. This will fail for devices that parse
5391a7de85SWillem de Bruijn  *  headers. Should work on devices that implement protocol agnostic tx
5491a7de85SWillem de Bruijn  * checksum offload (NETIF_F_HW_CSUM).
5591a7de85SWillem de Bruijn  *
5691a7de85SWillem de Bruijn  * Argument '-r $SEED' optionally randomizes header, payload and length
5791a7de85SWillem de Bruijn  * to increase coverage between packets sent. SEED 1 further chooses a
5891a7de85SWillem de Bruijn  * different seed for each run (and logs this for reproducibility). It
5991a7de85SWillem de Bruijn  * is advised to enable this for extra coverage in continuous testing.
6091a7de85SWillem de Bruijn  */
6191a7de85SWillem de Bruijn 
6291a7de85SWillem de Bruijn #define _GNU_SOURCE
6391a7de85SWillem de Bruijn 
6491a7de85SWillem de Bruijn #include <arpa/inet.h>
6591a7de85SWillem de Bruijn #include <asm/byteorder.h>
6691a7de85SWillem de Bruijn #include <errno.h>
6791a7de85SWillem de Bruijn #include <error.h>
6891a7de85SWillem de Bruijn #include <linux/filter.h>
6991a7de85SWillem de Bruijn #include <linux/if_packet.h>
7091a7de85SWillem de Bruijn #include <linux/ipv6.h>
7191a7de85SWillem de Bruijn #include <linux/virtio_net.h>
7291a7de85SWillem de Bruijn #include <net/ethernet.h>
7391a7de85SWillem de Bruijn #include <net/if.h>
7491a7de85SWillem de Bruijn #include <netinet/if_ether.h>
7591a7de85SWillem de Bruijn #include <netinet/in.h>
7691a7de85SWillem de Bruijn #include <netinet/ip.h>
7791a7de85SWillem de Bruijn #include <netinet/ip6.h>
7891a7de85SWillem de Bruijn #include <netinet/tcp.h>
7991a7de85SWillem de Bruijn #include <netinet/udp.h>
8091a7de85SWillem de Bruijn #include <poll.h>
8191a7de85SWillem de Bruijn #include <sched.h>
8291a7de85SWillem de Bruijn #include <stdbool.h>
8391a7de85SWillem de Bruijn #include <stddef.h>
8491a7de85SWillem de Bruijn #include <stdint.h>
8591a7de85SWillem de Bruijn #include <stdio.h>
8691a7de85SWillem de Bruijn #include <stdlib.h>
8791a7de85SWillem de Bruijn #include <string.h>
8891a7de85SWillem de Bruijn #include <sys/socket.h>
8991a7de85SWillem de Bruijn #include <sys/stat.h>
9091a7de85SWillem de Bruijn #include <sys/time.h>
9191a7de85SWillem de Bruijn #include <sys/types.h>
9291a7de85SWillem de Bruijn #include <unistd.h>
9391a7de85SWillem de Bruijn 
943645c71bSMahmoud Maatuq #include "kselftest.h"
953645c71bSMahmoud Maatuq 
9691a7de85SWillem de Bruijn static bool cfg_bad_csum;
9791a7de85SWillem de Bruijn static int cfg_family = PF_INET6;
9891a7de85SWillem de Bruijn static int cfg_num_pkt = 4;
9991a7de85SWillem de Bruijn static bool cfg_do_rx = true;
10091a7de85SWillem de Bruijn static bool cfg_do_tx = true;
10191a7de85SWillem de Bruijn static bool cfg_encap;
10291a7de85SWillem de Bruijn static char *cfg_ifname = "eth0";
10391a7de85SWillem de Bruijn static char *cfg_mac_dst;
10491a7de85SWillem de Bruijn static char *cfg_mac_src;
10591a7de85SWillem de Bruijn static int cfg_proto = IPPROTO_UDP;
10691a7de85SWillem de Bruijn static int cfg_payload_char = 'a';
10791a7de85SWillem de Bruijn static int cfg_payload_len = 100;
10891a7de85SWillem de Bruijn static uint16_t cfg_port_dst = 34000;
10991a7de85SWillem de Bruijn static uint16_t cfg_port_src = 33000;
11091a7de85SWillem de Bruijn static uint16_t cfg_port_src_encap = 33001;
11191a7de85SWillem de Bruijn static unsigned int cfg_random_seed;
11291a7de85SWillem de Bruijn static int cfg_rcvbuf = 1 << 22;	/* be able to queue large cfg_num_pkt */
11391a7de85SWillem de Bruijn static bool cfg_send_pfpacket;
11491a7de85SWillem de Bruijn static bool cfg_send_udp;
11591a7de85SWillem de Bruijn static int cfg_timeout_ms = 2000;
11691a7de85SWillem de Bruijn static bool cfg_zero_disable; /* skip checksum: set to zero (udp only) */
11791a7de85SWillem de Bruijn static bool cfg_zero_sum;     /* create packet that adds up to zero */
11891a7de85SWillem de Bruijn 
11991a7de85SWillem de Bruijn static struct sockaddr_in cfg_daddr4 = {.sin_family = AF_INET};
12091a7de85SWillem de Bruijn static struct sockaddr_in cfg_saddr4 = {.sin_family = AF_INET};
12191a7de85SWillem de Bruijn static struct sockaddr_in6 cfg_daddr6 = {.sin6_family = AF_INET6};
12291a7de85SWillem de Bruijn static struct sockaddr_in6 cfg_saddr6 = {.sin6_family = AF_INET6};
12391a7de85SWillem de Bruijn 
12491a7de85SWillem de Bruijn #define ENC_HEADER_LEN	(sizeof(struct udphdr) + sizeof(struct udp_encap_hdr))
12591a7de85SWillem de Bruijn #define MAX_HEADER_LEN	(sizeof(struct ipv6hdr) + ENC_HEADER_LEN + sizeof(struct tcphdr))
12691a7de85SWillem de Bruijn #define MAX_PAYLOAD_LEN 1024
12791a7de85SWillem de Bruijn 
12891a7de85SWillem de Bruijn /* Trivial demo encap. Stand-in for transport layer protocols like ESP or PSP */
12991a7de85SWillem de Bruijn struct udp_encap_hdr {
13091a7de85SWillem de Bruijn 	uint8_t nexthdr;
13191a7de85SWillem de Bruijn 	uint8_t padding[3];
13291a7de85SWillem de Bruijn };
13391a7de85SWillem de Bruijn 
13491a7de85SWillem de Bruijn /* Ipaddrs, for pseudo csum. Global var is ugly, pass through funcs was worse */
13591a7de85SWillem de Bruijn static void *iph_addr_p;
13691a7de85SWillem de Bruijn 
gettimeofday_ms(void)13791a7de85SWillem de Bruijn static unsigned long gettimeofday_ms(void)
13891a7de85SWillem de Bruijn {
13991a7de85SWillem de Bruijn 	struct timeval tv;
14091a7de85SWillem de Bruijn 
14191a7de85SWillem de Bruijn 	gettimeofday(&tv, NULL);
14291a7de85SWillem de Bruijn 	return (tv.tv_sec * 1000UL) + (tv.tv_usec / 1000UL);
14391a7de85SWillem de Bruijn }
14491a7de85SWillem de Bruijn 
checksum_nofold(char * data,size_t len,uint32_t sum)14591a7de85SWillem de Bruijn static uint32_t checksum_nofold(char *data, size_t len, uint32_t sum)
14691a7de85SWillem de Bruijn {
14791a7de85SWillem de Bruijn 	uint16_t *words = (uint16_t *)data;
14891a7de85SWillem de Bruijn 	int i;
14991a7de85SWillem de Bruijn 
15091a7de85SWillem de Bruijn 	for (i = 0; i < len / 2; i++)
15191a7de85SWillem de Bruijn 		sum += words[i];
15291a7de85SWillem de Bruijn 
15391a7de85SWillem de Bruijn 	if (len & 1)
15491a7de85SWillem de Bruijn 		sum += ((unsigned char *)data)[len - 1];
15591a7de85SWillem de Bruijn 
15691a7de85SWillem de Bruijn 	return sum;
15791a7de85SWillem de Bruijn }
15891a7de85SWillem de Bruijn 
checksum_fold(void * data,size_t len,uint32_t sum)15991a7de85SWillem de Bruijn static uint16_t checksum_fold(void *data, size_t len, uint32_t sum)
16091a7de85SWillem de Bruijn {
16191a7de85SWillem de Bruijn 	sum = checksum_nofold(data, len, sum);
16291a7de85SWillem de Bruijn 
16391a7de85SWillem de Bruijn 	while (sum > 0xFFFF)
16491a7de85SWillem de Bruijn 		sum = (sum & 0xFFFF) + (sum >> 16);
16591a7de85SWillem de Bruijn 
16691a7de85SWillem de Bruijn 	return ~sum;
16791a7de85SWillem de Bruijn }
16891a7de85SWillem de Bruijn 
checksum(void * th,uint16_t proto,size_t len)16991a7de85SWillem de Bruijn static uint16_t checksum(void *th, uint16_t proto, size_t len)
17091a7de85SWillem de Bruijn {
17191a7de85SWillem de Bruijn 	uint32_t sum;
17291a7de85SWillem de Bruijn 	int alen;
17391a7de85SWillem de Bruijn 
17491a7de85SWillem de Bruijn 	alen = cfg_family == PF_INET6 ? 32 : 8;
17591a7de85SWillem de Bruijn 
17691a7de85SWillem de Bruijn 	sum = checksum_nofold(iph_addr_p, alen, 0);
17791a7de85SWillem de Bruijn 	sum += htons(proto);
17891a7de85SWillem de Bruijn 	sum += htons(len);
17991a7de85SWillem de Bruijn 
18091a7de85SWillem de Bruijn 	/* With CHECKSUM_PARTIAL kernel expects non-inverted pseudo csum */
18191a7de85SWillem de Bruijn 	if (cfg_do_tx && cfg_send_pfpacket)
18291a7de85SWillem de Bruijn 		return ~checksum_fold(NULL, 0, sum);
18391a7de85SWillem de Bruijn 	else
18491a7de85SWillem de Bruijn 		return checksum_fold(th, len, sum);
18591a7de85SWillem de Bruijn }
18691a7de85SWillem de Bruijn 
build_packet_ipv4(void * _iph,uint8_t proto,unsigned int len)18791a7de85SWillem de Bruijn static void *build_packet_ipv4(void *_iph, uint8_t proto, unsigned int len)
18891a7de85SWillem de Bruijn {
18991a7de85SWillem de Bruijn 	struct iphdr *iph = _iph;
19091a7de85SWillem de Bruijn 
19191a7de85SWillem de Bruijn 	memset(iph, 0, sizeof(*iph));
19291a7de85SWillem de Bruijn 
19391a7de85SWillem de Bruijn 	iph->version = 4;
19491a7de85SWillem de Bruijn 	iph->ihl = 5;
19591a7de85SWillem de Bruijn 	iph->ttl = 8;
19691a7de85SWillem de Bruijn 	iph->protocol = proto;
19791a7de85SWillem de Bruijn 	iph->saddr = cfg_saddr4.sin_addr.s_addr;
19891a7de85SWillem de Bruijn 	iph->daddr = cfg_daddr4.sin_addr.s_addr;
19991a7de85SWillem de Bruijn 	iph->tot_len = htons(sizeof(*iph) + len);
20091a7de85SWillem de Bruijn 	iph->check = checksum_fold(iph, sizeof(*iph), 0);
20191a7de85SWillem de Bruijn 
20291a7de85SWillem de Bruijn 	iph_addr_p = &iph->saddr;
20391a7de85SWillem de Bruijn 
20491a7de85SWillem de Bruijn 	return iph + 1;
20591a7de85SWillem de Bruijn }
20691a7de85SWillem de Bruijn 
build_packet_ipv6(void * _ip6h,uint8_t proto,unsigned int len)20791a7de85SWillem de Bruijn static void *build_packet_ipv6(void *_ip6h, uint8_t proto, unsigned int len)
20891a7de85SWillem de Bruijn {
20991a7de85SWillem de Bruijn 	struct ipv6hdr *ip6h = _ip6h;
21091a7de85SWillem de Bruijn 
21191a7de85SWillem de Bruijn 	memset(ip6h, 0, sizeof(*ip6h));
21291a7de85SWillem de Bruijn 
21391a7de85SWillem de Bruijn 	ip6h->version = 6;
21491a7de85SWillem de Bruijn 	ip6h->payload_len = htons(len);
21591a7de85SWillem de Bruijn 	ip6h->nexthdr = proto;
21691a7de85SWillem de Bruijn 	ip6h->hop_limit = 64;
21791a7de85SWillem de Bruijn 	ip6h->saddr = cfg_saddr6.sin6_addr;
21891a7de85SWillem de Bruijn 	ip6h->daddr = cfg_daddr6.sin6_addr;
21991a7de85SWillem de Bruijn 
22091a7de85SWillem de Bruijn 	iph_addr_p = &ip6h->saddr;
22191a7de85SWillem de Bruijn 
22291a7de85SWillem de Bruijn 	return ip6h + 1;
22391a7de85SWillem de Bruijn }
22491a7de85SWillem de Bruijn 
build_packet_udp(void * _uh)22591a7de85SWillem de Bruijn static void *build_packet_udp(void *_uh)
22691a7de85SWillem de Bruijn {
22791a7de85SWillem de Bruijn 	struct udphdr *uh = _uh;
22891a7de85SWillem de Bruijn 
22991a7de85SWillem de Bruijn 	uh->source = htons(cfg_port_src);
23091a7de85SWillem de Bruijn 	uh->dest = htons(cfg_port_dst);
23191a7de85SWillem de Bruijn 	uh->len = htons(sizeof(*uh) + cfg_payload_len);
23291a7de85SWillem de Bruijn 	uh->check = 0;
23391a7de85SWillem de Bruijn 
23491a7de85SWillem de Bruijn 	/* choose source port so that uh->check adds up to zero */
23591a7de85SWillem de Bruijn 	if (cfg_zero_sum) {
23691a7de85SWillem de Bruijn 		uh->source = 0;
23791a7de85SWillem de Bruijn 		uh->source = checksum(uh, IPPROTO_UDP, sizeof(*uh) + cfg_payload_len);
23891a7de85SWillem de Bruijn 
23991a7de85SWillem de Bruijn 		fprintf(stderr, "tx: changing sport: %hu -> %hu\n",
24091a7de85SWillem de Bruijn 			cfg_port_src, ntohs(uh->source));
24191a7de85SWillem de Bruijn 		cfg_port_src = ntohs(uh->source);
24291a7de85SWillem de Bruijn 	}
24391a7de85SWillem de Bruijn 
24491a7de85SWillem de Bruijn 	if (cfg_zero_disable)
24591a7de85SWillem de Bruijn 		uh->check = 0;
24691a7de85SWillem de Bruijn 	else
24791a7de85SWillem de Bruijn 		uh->check = checksum(uh, IPPROTO_UDP, sizeof(*uh) + cfg_payload_len);
24891a7de85SWillem de Bruijn 
24991a7de85SWillem de Bruijn 	if (cfg_bad_csum)
25091a7de85SWillem de Bruijn 		uh->check = ~uh->check;
25191a7de85SWillem de Bruijn 
25291a7de85SWillem de Bruijn 	fprintf(stderr, "tx: sending checksum: 0x%x\n", uh->check);
25391a7de85SWillem de Bruijn 	return uh + 1;
25491a7de85SWillem de Bruijn }
25591a7de85SWillem de Bruijn 
build_packet_tcp(void * _th)25691a7de85SWillem de Bruijn static void *build_packet_tcp(void *_th)
25791a7de85SWillem de Bruijn {
25891a7de85SWillem de Bruijn 	struct tcphdr *th = _th;
25991a7de85SWillem de Bruijn 
26091a7de85SWillem de Bruijn 	th->source = htons(cfg_port_src);
26191a7de85SWillem de Bruijn 	th->dest = htons(cfg_port_dst);
26291a7de85SWillem de Bruijn 	th->doff = 5;
26391a7de85SWillem de Bruijn 	th->check = 0;
26491a7de85SWillem de Bruijn 
26591a7de85SWillem de Bruijn 	th->check = checksum(th, IPPROTO_TCP, sizeof(*th) + cfg_payload_len);
26691a7de85SWillem de Bruijn 
26791a7de85SWillem de Bruijn 	if (cfg_bad_csum)
26891a7de85SWillem de Bruijn 		th->check = ~th->check;
26991a7de85SWillem de Bruijn 
27091a7de85SWillem de Bruijn 	fprintf(stderr, "tx: sending checksum: 0x%x\n", th->check);
27191a7de85SWillem de Bruijn 	return th + 1;
27291a7de85SWillem de Bruijn }
27391a7de85SWillem de Bruijn 
build_packet_udp_encap(void * _uh)27491a7de85SWillem de Bruijn static char *build_packet_udp_encap(void *_uh)
27591a7de85SWillem de Bruijn {
27691a7de85SWillem de Bruijn 	struct udphdr *uh = _uh;
27791a7de85SWillem de Bruijn 	struct udp_encap_hdr *eh = _uh + sizeof(*uh);
27891a7de85SWillem de Bruijn 
27991a7de85SWillem de Bruijn 	/* outer dst == inner dst, to simplify BPF filter
28091a7de85SWillem de Bruijn 	 * outer src != inner src, to demultiplex on recv
28191a7de85SWillem de Bruijn 	 */
28291a7de85SWillem de Bruijn 	uh->dest = htons(cfg_port_dst);
28391a7de85SWillem de Bruijn 	uh->source = htons(cfg_port_src_encap);
28491a7de85SWillem de Bruijn 	uh->check = 0;
28591a7de85SWillem de Bruijn 	uh->len = htons(sizeof(*uh) +
28691a7de85SWillem de Bruijn 			sizeof(*eh) +
28791a7de85SWillem de Bruijn 			sizeof(struct tcphdr) +
28891a7de85SWillem de Bruijn 			cfg_payload_len);
28991a7de85SWillem de Bruijn 
29091a7de85SWillem de Bruijn 	eh->nexthdr = IPPROTO_TCP;
29191a7de85SWillem de Bruijn 
29291a7de85SWillem de Bruijn 	return build_packet_tcp(eh + 1);
29391a7de85SWillem de Bruijn }
29491a7de85SWillem de Bruijn 
build_packet(char * buf,int max_len,int * len)29591a7de85SWillem de Bruijn static char *build_packet(char *buf, int max_len, int *len)
29691a7de85SWillem de Bruijn {
29791a7de85SWillem de Bruijn 	uint8_t proto;
29891a7de85SWillem de Bruijn 	char *off;
29991a7de85SWillem de Bruijn 	int tlen;
30091a7de85SWillem de Bruijn 
30191a7de85SWillem de Bruijn 	if (cfg_random_seed) {
30291a7de85SWillem de Bruijn 		int *buf32 = (void *)buf;
30391a7de85SWillem de Bruijn 		int i;
30491a7de85SWillem de Bruijn 
30591a7de85SWillem de Bruijn 		for (i = 0; i < (max_len / sizeof(int)); i++)
30691a7de85SWillem de Bruijn 			buf32[i] = rand();
30791a7de85SWillem de Bruijn 	} else {
30891a7de85SWillem de Bruijn 		memset(buf, cfg_payload_char, max_len);
30991a7de85SWillem de Bruijn 	}
31091a7de85SWillem de Bruijn 
31191a7de85SWillem de Bruijn 	if (cfg_proto == IPPROTO_UDP)
31291a7de85SWillem de Bruijn 		tlen = sizeof(struct udphdr) + cfg_payload_len;
31391a7de85SWillem de Bruijn 	else
31491a7de85SWillem de Bruijn 		tlen = sizeof(struct tcphdr) + cfg_payload_len;
31591a7de85SWillem de Bruijn 
31691a7de85SWillem de Bruijn 	if (cfg_encap) {
31791a7de85SWillem de Bruijn 		proto = IPPROTO_UDP;
31891a7de85SWillem de Bruijn 		tlen += ENC_HEADER_LEN;
31991a7de85SWillem de Bruijn 	} else {
32091a7de85SWillem de Bruijn 		proto = cfg_proto;
32191a7de85SWillem de Bruijn 	}
32291a7de85SWillem de Bruijn 
32391a7de85SWillem de Bruijn 	if (cfg_family == PF_INET)
32491a7de85SWillem de Bruijn 		off = build_packet_ipv4(buf, proto, tlen);
32591a7de85SWillem de Bruijn 	else
32691a7de85SWillem de Bruijn 		off = build_packet_ipv6(buf, proto, tlen);
32791a7de85SWillem de Bruijn 
32891a7de85SWillem de Bruijn 	if (cfg_encap)
32991a7de85SWillem de Bruijn 		off = build_packet_udp_encap(off);
33091a7de85SWillem de Bruijn 	else if (cfg_proto == IPPROTO_UDP)
33191a7de85SWillem de Bruijn 		off = build_packet_udp(off);
33291a7de85SWillem de Bruijn 	else
33391a7de85SWillem de Bruijn 		off = build_packet_tcp(off);
33491a7de85SWillem de Bruijn 
33591a7de85SWillem de Bruijn 	/* only pass the payload, but still compute headers for cfg_zero_sum */
33691a7de85SWillem de Bruijn 	if (cfg_send_udp) {
33791a7de85SWillem de Bruijn 		*len = cfg_payload_len;
33891a7de85SWillem de Bruijn 		return off;
33991a7de85SWillem de Bruijn 	}
34091a7de85SWillem de Bruijn 
34191a7de85SWillem de Bruijn 	*len = off - buf + cfg_payload_len;
34291a7de85SWillem de Bruijn 	return buf;
34391a7de85SWillem de Bruijn }
34491a7de85SWillem de Bruijn 
open_inet(int ipproto,int protocol)34591a7de85SWillem de Bruijn static int open_inet(int ipproto, int protocol)
34691a7de85SWillem de Bruijn {
34791a7de85SWillem de Bruijn 	int fd;
34891a7de85SWillem de Bruijn 
34991a7de85SWillem de Bruijn 	fd = socket(cfg_family, ipproto, protocol);
35091a7de85SWillem de Bruijn 	if (fd == -1)
35191a7de85SWillem de Bruijn 		error(1, errno, "socket inet");
35291a7de85SWillem de Bruijn 
35391a7de85SWillem de Bruijn 	if (cfg_family == PF_INET6) {
35491a7de85SWillem de Bruijn 		/* may have been updated by cfg_zero_sum */
35591a7de85SWillem de Bruijn 		cfg_saddr6.sin6_port = htons(cfg_port_src);
35691a7de85SWillem de Bruijn 
35791a7de85SWillem de Bruijn 		if (bind(fd, (void *)&cfg_saddr6, sizeof(cfg_saddr6)))
35891a7de85SWillem de Bruijn 			error(1, errno, "bind dgram 6");
35991a7de85SWillem de Bruijn 		if (connect(fd, (void *)&cfg_daddr6, sizeof(cfg_daddr6)))
36091a7de85SWillem de Bruijn 			error(1, errno, "connect dgram 6");
36191a7de85SWillem de Bruijn 	} else {
36291a7de85SWillem de Bruijn 		/* may have been updated by cfg_zero_sum */
36391a7de85SWillem de Bruijn 		cfg_saddr4.sin_port = htons(cfg_port_src);
36491a7de85SWillem de Bruijn 
36591a7de85SWillem de Bruijn 		if (bind(fd, (void *)&cfg_saddr4, sizeof(cfg_saddr4)))
36691a7de85SWillem de Bruijn 			error(1, errno, "bind dgram 4");
36791a7de85SWillem de Bruijn 		if (connect(fd, (void *)&cfg_daddr4, sizeof(cfg_daddr4)))
36891a7de85SWillem de Bruijn 			error(1, errno, "connect dgram 4");
36991a7de85SWillem de Bruijn 	}
37091a7de85SWillem de Bruijn 
37191a7de85SWillem de Bruijn 	return fd;
37291a7de85SWillem de Bruijn }
37391a7de85SWillem de Bruijn 
open_packet(void)37491a7de85SWillem de Bruijn static int open_packet(void)
37591a7de85SWillem de Bruijn {
37691a7de85SWillem de Bruijn 	int fd, one = 1;
37791a7de85SWillem de Bruijn 
37891a7de85SWillem de Bruijn 	fd = socket(PF_PACKET, SOCK_RAW, 0);
37991a7de85SWillem de Bruijn 	if (fd == -1)
38091a7de85SWillem de Bruijn 		error(1, errno, "socket packet");
38191a7de85SWillem de Bruijn 
38291a7de85SWillem de Bruijn 	if (setsockopt(fd, SOL_PACKET, PACKET_VNET_HDR, &one, sizeof(one)))
38391a7de85SWillem de Bruijn 		error(1, errno, "setsockopt packet_vnet_ndr");
38491a7de85SWillem de Bruijn 
38591a7de85SWillem de Bruijn 	return fd;
38691a7de85SWillem de Bruijn }
38791a7de85SWillem de Bruijn 
send_inet(int fd,const char * buf,int len)38891a7de85SWillem de Bruijn static void send_inet(int fd, const char *buf, int len)
38991a7de85SWillem de Bruijn {
39091a7de85SWillem de Bruijn 	int ret;
39191a7de85SWillem de Bruijn 
39291a7de85SWillem de Bruijn 	ret = write(fd, buf, len);
39391a7de85SWillem de Bruijn 	if (ret == -1)
39491a7de85SWillem de Bruijn 		error(1, errno, "write");
39591a7de85SWillem de Bruijn 	if (ret != len)
39691a7de85SWillem de Bruijn 		error(1, 0, "write: %d", ret);
39791a7de85SWillem de Bruijn }
39891a7de85SWillem de Bruijn 
eth_str_to_addr(const char * str,unsigned char * eth)39991a7de85SWillem de Bruijn static void eth_str_to_addr(const char *str, unsigned char *eth)
40091a7de85SWillem de Bruijn {
40191a7de85SWillem de Bruijn 	if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
40291a7de85SWillem de Bruijn 		   &eth[0], &eth[1], &eth[2], &eth[3], &eth[4], &eth[5]) != 6)
40391a7de85SWillem de Bruijn 		error(1, 0, "cannot parse mac addr %s", str);
40491a7de85SWillem de Bruijn }
40591a7de85SWillem de Bruijn 
send_packet(int fd,const char * buf,int len)40691a7de85SWillem de Bruijn static void send_packet(int fd, const char *buf, int len)
40791a7de85SWillem de Bruijn {
40891a7de85SWillem de Bruijn 	struct virtio_net_hdr vh = {0};
40991a7de85SWillem de Bruijn 	struct sockaddr_ll addr = {0};
41091a7de85SWillem de Bruijn 	struct msghdr msg = {0};
41191a7de85SWillem de Bruijn 	struct ethhdr eth;
41291a7de85SWillem de Bruijn 	struct iovec iov[3];
41391a7de85SWillem de Bruijn 	int ret;
41491a7de85SWillem de Bruijn 
41591a7de85SWillem de Bruijn 	addr.sll_family = AF_PACKET;
41691a7de85SWillem de Bruijn 	addr.sll_halen = ETH_ALEN;
41791a7de85SWillem de Bruijn 	addr.sll_ifindex = if_nametoindex(cfg_ifname);
41891a7de85SWillem de Bruijn 	if (!addr.sll_ifindex)
41991a7de85SWillem de Bruijn 		error(1, errno, "if_nametoindex %s", cfg_ifname);
42091a7de85SWillem de Bruijn 
42191a7de85SWillem de Bruijn 	vh.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
42291a7de85SWillem de Bruijn 	if (cfg_family == PF_INET6) {
42391a7de85SWillem de Bruijn 		vh.csum_start = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
42491a7de85SWillem de Bruijn 		addr.sll_protocol = htons(ETH_P_IPV6);
42591a7de85SWillem de Bruijn 	} else {
42691a7de85SWillem de Bruijn 		vh.csum_start = sizeof(struct ethhdr) + sizeof(struct iphdr);
42791a7de85SWillem de Bruijn 		addr.sll_protocol = htons(ETH_P_IP);
42891a7de85SWillem de Bruijn 	}
42991a7de85SWillem de Bruijn 
43091a7de85SWillem de Bruijn 	if (cfg_encap)
43191a7de85SWillem de Bruijn 		vh.csum_start += ENC_HEADER_LEN;
43291a7de85SWillem de Bruijn 
43391a7de85SWillem de Bruijn 	if (cfg_proto == IPPROTO_TCP) {
43491a7de85SWillem de Bruijn 		vh.csum_offset = __builtin_offsetof(struct tcphdr, check);
43591a7de85SWillem de Bruijn 		vh.hdr_len = vh.csum_start + sizeof(struct tcphdr);
43691a7de85SWillem de Bruijn 	} else {
43791a7de85SWillem de Bruijn 		vh.csum_offset = __builtin_offsetof(struct udphdr, check);
43891a7de85SWillem de Bruijn 		vh.hdr_len = vh.csum_start + sizeof(struct udphdr);
43991a7de85SWillem de Bruijn 	}
44091a7de85SWillem de Bruijn 
44191a7de85SWillem de Bruijn 	eth_str_to_addr(cfg_mac_src, eth.h_source);
44291a7de85SWillem de Bruijn 	eth_str_to_addr(cfg_mac_dst, eth.h_dest);
44391a7de85SWillem de Bruijn 	eth.h_proto = addr.sll_protocol;
44491a7de85SWillem de Bruijn 
44591a7de85SWillem de Bruijn 	iov[0].iov_base = &vh;
44691a7de85SWillem de Bruijn 	iov[0].iov_len = sizeof(vh);
44791a7de85SWillem de Bruijn 
44891a7de85SWillem de Bruijn 	iov[1].iov_base = &eth;
44991a7de85SWillem de Bruijn 	iov[1].iov_len = sizeof(eth);
45091a7de85SWillem de Bruijn 
45191a7de85SWillem de Bruijn 	iov[2].iov_base = (void *)buf;
45291a7de85SWillem de Bruijn 	iov[2].iov_len = len;
45391a7de85SWillem de Bruijn 
45491a7de85SWillem de Bruijn 	msg.msg_iov = iov;
4553645c71bSMahmoud Maatuq 	msg.msg_iovlen = ARRAY_SIZE(iov);
45691a7de85SWillem de Bruijn 
45791a7de85SWillem de Bruijn 	msg.msg_name = &addr;
45891a7de85SWillem de Bruijn 	msg.msg_namelen = sizeof(addr);
45991a7de85SWillem de Bruijn 
46091a7de85SWillem de Bruijn 	ret = sendmsg(fd, &msg, 0);
46191a7de85SWillem de Bruijn 	if (ret == -1)
46291a7de85SWillem de Bruijn 		error(1, errno, "sendmsg packet");
46391a7de85SWillem de Bruijn 	if (ret != sizeof(vh) + sizeof(eth) + len)
46491a7de85SWillem de Bruijn 		error(1, errno, "sendmsg packet: %u", ret);
46591a7de85SWillem de Bruijn }
46691a7de85SWillem de Bruijn 
recv_prepare_udp(void)46791a7de85SWillem de Bruijn static int recv_prepare_udp(void)
46891a7de85SWillem de Bruijn {
46991a7de85SWillem de Bruijn 	int fd;
47091a7de85SWillem de Bruijn 
47191a7de85SWillem de Bruijn 	fd = socket(cfg_family, SOCK_DGRAM, 0);
47291a7de85SWillem de Bruijn 	if (fd == -1)
47391a7de85SWillem de Bruijn 		error(1, errno, "socket r");
47491a7de85SWillem de Bruijn 
47591a7de85SWillem de Bruijn 	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
47691a7de85SWillem de Bruijn 		       &cfg_rcvbuf, sizeof(cfg_rcvbuf)))
47791a7de85SWillem de Bruijn 		error(1, errno, "setsockopt SO_RCVBUF r");
47891a7de85SWillem de Bruijn 
47991a7de85SWillem de Bruijn 	if (cfg_family == PF_INET6) {
48091a7de85SWillem de Bruijn 		if (bind(fd, (void *)&cfg_daddr6, sizeof(cfg_daddr6)))
48191a7de85SWillem de Bruijn 			error(1, errno, "bind r");
48291a7de85SWillem de Bruijn 	} else {
48391a7de85SWillem de Bruijn 		if (bind(fd, (void *)&cfg_daddr4, sizeof(cfg_daddr4)))
48491a7de85SWillem de Bruijn 			error(1, errno, "bind r");
48591a7de85SWillem de Bruijn 	}
48691a7de85SWillem de Bruijn 
48791a7de85SWillem de Bruijn 	return fd;
48891a7de85SWillem de Bruijn }
48991a7de85SWillem de Bruijn 
49091a7de85SWillem de Bruijn /* Filter out all traffic that is not cfg_proto with our destination port.
49191a7de85SWillem de Bruijn  *
49291a7de85SWillem de Bruijn  * Otherwise background noise may cause PF_PACKET receive queue overflow,
49391a7de85SWillem de Bruijn  * dropping the expected packets and failing the test.
49491a7de85SWillem de Bruijn  */
__recv_prepare_packet_filter(int fd,int off_nexthdr,int off_dport)49591a7de85SWillem de Bruijn static void __recv_prepare_packet_filter(int fd, int off_nexthdr, int off_dport)
49691a7de85SWillem de Bruijn {
49791a7de85SWillem de Bruijn 	struct sock_filter filter[] = {
49891a7de85SWillem de Bruijn 		BPF_STMT(BPF_LD + BPF_B + BPF_ABS, SKF_AD_OFF + SKF_AD_PKTTYPE),
49991a7de85SWillem de Bruijn 		BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PACKET_HOST, 0, 4),
50091a7de85SWillem de Bruijn 		BPF_STMT(BPF_LD + BPF_B + BPF_ABS, off_nexthdr),
50191a7de85SWillem de Bruijn 		BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_encap ? IPPROTO_UDP : cfg_proto, 0, 2),
50291a7de85SWillem de Bruijn 		BPF_STMT(BPF_LD + BPF_H + BPF_ABS, off_dport),
50391a7de85SWillem de Bruijn 		BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, cfg_port_dst, 1, 0),
50491a7de85SWillem de Bruijn 		BPF_STMT(BPF_RET + BPF_K, 0),
50591a7de85SWillem de Bruijn 		BPF_STMT(BPF_RET + BPF_K, 0xFFFF),
50691a7de85SWillem de Bruijn 	};
50791a7de85SWillem de Bruijn 	struct sock_fprog prog = {};
50891a7de85SWillem de Bruijn 
50991a7de85SWillem de Bruijn 	prog.filter = filter;
5103645c71bSMahmoud Maatuq 	prog.len = ARRAY_SIZE(filter);
51191a7de85SWillem de Bruijn 	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)))
51291a7de85SWillem de Bruijn 		error(1, errno, "setsockopt filter");
51391a7de85SWillem de Bruijn }
51491a7de85SWillem de Bruijn 
recv_prepare_packet_filter(int fd)51591a7de85SWillem de Bruijn static void recv_prepare_packet_filter(int fd)
51691a7de85SWillem de Bruijn {
51791a7de85SWillem de Bruijn 	const int off_dport = offsetof(struct tcphdr, dest); /* same for udp */
51891a7de85SWillem de Bruijn 
51991a7de85SWillem de Bruijn 	if (cfg_family == AF_INET)
52091a7de85SWillem de Bruijn 		__recv_prepare_packet_filter(fd, offsetof(struct iphdr, protocol),
52191a7de85SWillem de Bruijn 					     sizeof(struct iphdr) + off_dport);
52291a7de85SWillem de Bruijn 	else
52391a7de85SWillem de Bruijn 		__recv_prepare_packet_filter(fd, offsetof(struct ipv6hdr, nexthdr),
52491a7de85SWillem de Bruijn 					     sizeof(struct ipv6hdr) + off_dport);
52591a7de85SWillem de Bruijn }
52691a7de85SWillem de Bruijn 
recv_prepare_packet_bind(int fd)52791a7de85SWillem de Bruijn static void recv_prepare_packet_bind(int fd)
52891a7de85SWillem de Bruijn {
52991a7de85SWillem de Bruijn 	struct sockaddr_ll laddr = {0};
53091a7de85SWillem de Bruijn 
53191a7de85SWillem de Bruijn 	laddr.sll_family = AF_PACKET;
53291a7de85SWillem de Bruijn 
53391a7de85SWillem de Bruijn 	if (cfg_family == PF_INET)
53491a7de85SWillem de Bruijn 		laddr.sll_protocol = htons(ETH_P_IP);
53591a7de85SWillem de Bruijn 	else
53691a7de85SWillem de Bruijn 		laddr.sll_protocol = htons(ETH_P_IPV6);
53791a7de85SWillem de Bruijn 
53891a7de85SWillem de Bruijn 	laddr.sll_ifindex = if_nametoindex(cfg_ifname);
53991a7de85SWillem de Bruijn 	if (!laddr.sll_ifindex)
54091a7de85SWillem de Bruijn 		error(1, 0, "if_nametoindex %s", cfg_ifname);
54191a7de85SWillem de Bruijn 
54291a7de85SWillem de Bruijn 	if (bind(fd, (void *)&laddr, sizeof(laddr)))
54391a7de85SWillem de Bruijn 		error(1, errno, "bind pf_packet");
54491a7de85SWillem de Bruijn }
54591a7de85SWillem de Bruijn 
recv_prepare_packet(void)54691a7de85SWillem de Bruijn static int recv_prepare_packet(void)
54791a7de85SWillem de Bruijn {
54891a7de85SWillem de Bruijn 	int fd, one = 1;
54991a7de85SWillem de Bruijn 
55091a7de85SWillem de Bruijn 	fd = socket(PF_PACKET, SOCK_DGRAM, 0);
55191a7de85SWillem de Bruijn 	if (fd == -1)
55291a7de85SWillem de Bruijn 		error(1, errno, "socket p");
55391a7de85SWillem de Bruijn 
55491a7de85SWillem de Bruijn 	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
55591a7de85SWillem de Bruijn 		       &cfg_rcvbuf, sizeof(cfg_rcvbuf)))
55691a7de85SWillem de Bruijn 		error(1, errno, "setsockopt SO_RCVBUF p");
55791a7de85SWillem de Bruijn 
55891a7de85SWillem de Bruijn 	/* enable auxdata to recv checksum status (valid vs unknown) */
55991a7de85SWillem de Bruijn 	if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one)))
56091a7de85SWillem de Bruijn 		error(1, errno, "setsockopt auxdata");
56191a7de85SWillem de Bruijn 
56291a7de85SWillem de Bruijn 	/* install filter to restrict packet flow to match */
56391a7de85SWillem de Bruijn 	recv_prepare_packet_filter(fd);
56491a7de85SWillem de Bruijn 
56591a7de85SWillem de Bruijn 	/* bind to address family to start packet flow */
56691a7de85SWillem de Bruijn 	recv_prepare_packet_bind(fd);
56791a7de85SWillem de Bruijn 
56891a7de85SWillem de Bruijn 	return fd;
56991a7de85SWillem de Bruijn }
57091a7de85SWillem de Bruijn 
recv_udp(int fd)57191a7de85SWillem de Bruijn static int recv_udp(int fd)
57291a7de85SWillem de Bruijn {
57391a7de85SWillem de Bruijn 	static char buf[MAX_PAYLOAD_LEN];
57491a7de85SWillem de Bruijn 	int ret, count = 0;
57591a7de85SWillem de Bruijn 
57691a7de85SWillem de Bruijn 	while (1) {
57791a7de85SWillem de Bruijn 		ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
57891a7de85SWillem de Bruijn 		if (ret == -1 && errno == EAGAIN)
57991a7de85SWillem de Bruijn 			break;
58091a7de85SWillem de Bruijn 		if (ret == -1)
58191a7de85SWillem de Bruijn 			error(1, errno, "recv r");
58291a7de85SWillem de Bruijn 
58391a7de85SWillem de Bruijn 		fprintf(stderr, "rx: udp: len=%u\n", ret);
58491a7de85SWillem de Bruijn 		count++;
58591a7de85SWillem de Bruijn 	}
58691a7de85SWillem de Bruijn 
58791a7de85SWillem de Bruijn 	return count;
58891a7de85SWillem de Bruijn }
58991a7de85SWillem de Bruijn 
recv_verify_csum(void * th,int len,uint16_t sport,uint16_t csum_field)59091a7de85SWillem de Bruijn static int recv_verify_csum(void *th, int len, uint16_t sport, uint16_t csum_field)
59191a7de85SWillem de Bruijn {
59291a7de85SWillem de Bruijn 	uint16_t csum;
59391a7de85SWillem de Bruijn 
59491a7de85SWillem de Bruijn 	csum = checksum(th, cfg_proto, len);
59591a7de85SWillem de Bruijn 
59691a7de85SWillem de Bruijn 	fprintf(stderr, "rx: pkt: sport=%hu len=%u csum=0x%hx verify=0x%hx\n",
59791a7de85SWillem de Bruijn 		sport, len, csum_field, csum);
59891a7de85SWillem de Bruijn 
59991a7de85SWillem de Bruijn 	/* csum must be zero unless cfg_bad_csum indicates bad csum */
60091a7de85SWillem de Bruijn 	if (csum && !cfg_bad_csum) {
60191a7de85SWillem de Bruijn 		fprintf(stderr, "pkt: bad csum\n");
60291a7de85SWillem de Bruijn 		return 1;
60391a7de85SWillem de Bruijn 	} else if (cfg_bad_csum && !csum) {
60491a7de85SWillem de Bruijn 		fprintf(stderr, "pkt: good csum, while bad expected\n");
60591a7de85SWillem de Bruijn 		return 1;
60691a7de85SWillem de Bruijn 	}
60791a7de85SWillem de Bruijn 
60891a7de85SWillem de Bruijn 	if (cfg_zero_sum && csum_field != 0xFFFF) {
60991a7de85SWillem de Bruijn 		fprintf(stderr, "pkt: zero csum: field should be 0xFFFF, is 0x%hx\n", csum_field);
61091a7de85SWillem de Bruijn 		return 1;
61191a7de85SWillem de Bruijn 	}
61291a7de85SWillem de Bruijn 
61391a7de85SWillem de Bruijn 	return 0;
61491a7de85SWillem de Bruijn }
61591a7de85SWillem de Bruijn 
recv_verify_packet_tcp(void * th,int len)61691a7de85SWillem de Bruijn static int recv_verify_packet_tcp(void *th, int len)
61791a7de85SWillem de Bruijn {
61891a7de85SWillem de Bruijn 	struct tcphdr *tcph = th;
61991a7de85SWillem de Bruijn 
62091a7de85SWillem de Bruijn 	if (len < sizeof(*tcph) || tcph->dest != htons(cfg_port_dst))
62191a7de85SWillem de Bruijn 		return -1;
62291a7de85SWillem de Bruijn 
62391a7de85SWillem de Bruijn 	return recv_verify_csum(th, len, ntohs(tcph->source), tcph->check);
62491a7de85SWillem de Bruijn }
62591a7de85SWillem de Bruijn 
recv_verify_packet_udp_encap(void * th,int len)62691a7de85SWillem de Bruijn static int recv_verify_packet_udp_encap(void *th, int len)
62791a7de85SWillem de Bruijn {
62891a7de85SWillem de Bruijn 	struct udp_encap_hdr *eh = th;
62991a7de85SWillem de Bruijn 
63091a7de85SWillem de Bruijn 	if (len < sizeof(*eh) || eh->nexthdr != IPPROTO_TCP)
63191a7de85SWillem de Bruijn 		return -1;
63291a7de85SWillem de Bruijn 
63391a7de85SWillem de Bruijn 	return recv_verify_packet_tcp(eh + 1, len - sizeof(*eh));
63491a7de85SWillem de Bruijn }
63591a7de85SWillem de Bruijn 
recv_verify_packet_udp(void * th,int len)63691a7de85SWillem de Bruijn static int recv_verify_packet_udp(void *th, int len)
63791a7de85SWillem de Bruijn {
63891a7de85SWillem de Bruijn 	struct udphdr *udph = th;
63991a7de85SWillem de Bruijn 
64091a7de85SWillem de Bruijn 	if (len < sizeof(*udph))
64191a7de85SWillem de Bruijn 		return -1;
64291a7de85SWillem de Bruijn 
64391a7de85SWillem de Bruijn 	if (udph->dest != htons(cfg_port_dst))
64491a7de85SWillem de Bruijn 		return -1;
64591a7de85SWillem de Bruijn 
64691a7de85SWillem de Bruijn 	if (udph->source == htons(cfg_port_src_encap))
64791a7de85SWillem de Bruijn 		return recv_verify_packet_udp_encap(udph + 1,
64891a7de85SWillem de Bruijn 						    len - sizeof(*udph));
64991a7de85SWillem de Bruijn 
65091a7de85SWillem de Bruijn 	return recv_verify_csum(th, len, ntohs(udph->source), udph->check);
65191a7de85SWillem de Bruijn }
65291a7de85SWillem de Bruijn 
recv_verify_packet_ipv4(void * nh,int len)65391a7de85SWillem de Bruijn static int recv_verify_packet_ipv4(void *nh, int len)
65491a7de85SWillem de Bruijn {
65591a7de85SWillem de Bruijn 	struct iphdr *iph = nh;
65691a7de85SWillem de Bruijn 	uint16_t proto = cfg_encap ? IPPROTO_UDP : cfg_proto;
657*033a71efSSean Anderson 	uint16_t ip_len;
65891a7de85SWillem de Bruijn 
65991a7de85SWillem de Bruijn 	if (len < sizeof(*iph) || iph->protocol != proto)
66091a7de85SWillem de Bruijn 		return -1;
66191a7de85SWillem de Bruijn 
662*033a71efSSean Anderson 	ip_len = ntohs(iph->tot_len);
663*033a71efSSean Anderson 	if (ip_len > len || ip_len < sizeof(*iph))
664*033a71efSSean Anderson 		return -1;
665*033a71efSSean Anderson 
666*033a71efSSean Anderson 	len = ip_len;
66791a7de85SWillem de Bruijn 	iph_addr_p = &iph->saddr;
66891a7de85SWillem de Bruijn 	if (proto == IPPROTO_TCP)
66991a7de85SWillem de Bruijn 		return recv_verify_packet_tcp(iph + 1, len - sizeof(*iph));
67091a7de85SWillem de Bruijn 	else
67191a7de85SWillem de Bruijn 		return recv_verify_packet_udp(iph + 1, len - sizeof(*iph));
67291a7de85SWillem de Bruijn }
67391a7de85SWillem de Bruijn 
recv_verify_packet_ipv6(void * nh,int len)67491a7de85SWillem de Bruijn static int recv_verify_packet_ipv6(void *nh, int len)
67591a7de85SWillem de Bruijn {
67691a7de85SWillem de Bruijn 	struct ipv6hdr *ip6h = nh;
67791a7de85SWillem de Bruijn 	uint16_t proto = cfg_encap ? IPPROTO_UDP : cfg_proto;
678*033a71efSSean Anderson 	uint16_t ip_len;
67991a7de85SWillem de Bruijn 
68091a7de85SWillem de Bruijn 	if (len < sizeof(*ip6h) || ip6h->nexthdr != proto)
68191a7de85SWillem de Bruijn 		return -1;
68291a7de85SWillem de Bruijn 
683*033a71efSSean Anderson 	ip_len = ntohs(ip6h->payload_len);
684*033a71efSSean Anderson 	if (ip_len > len - sizeof(*ip6h))
685*033a71efSSean Anderson 		return -1;
686*033a71efSSean Anderson 
687*033a71efSSean Anderson 	len = ip_len;
68891a7de85SWillem de Bruijn 	iph_addr_p = &ip6h->saddr;
68991a7de85SWillem de Bruijn 
69091a7de85SWillem de Bruijn 	if (proto == IPPROTO_TCP)
691*033a71efSSean Anderson 		return recv_verify_packet_tcp(ip6h + 1, len);
69291a7de85SWillem de Bruijn 	else
693*033a71efSSean Anderson 		return recv_verify_packet_udp(ip6h + 1, len);
69491a7de85SWillem de Bruijn }
69591a7de85SWillem de Bruijn 
69691a7de85SWillem de Bruijn /* return whether auxdata includes TP_STATUS_CSUM_VALID */
recv_verify_packet_csum(struct msghdr * msg)69791a7de85SWillem de Bruijn static bool recv_verify_packet_csum(struct msghdr *msg)
69891a7de85SWillem de Bruijn {
69991a7de85SWillem de Bruijn 	struct tpacket_auxdata *aux = NULL;
70091a7de85SWillem de Bruijn 	struct cmsghdr *cm;
70191a7de85SWillem de Bruijn 
70291a7de85SWillem de Bruijn 	if (msg->msg_flags & MSG_CTRUNC)
70391a7de85SWillem de Bruijn 		error(1, 0, "cmsg: truncated");
70491a7de85SWillem de Bruijn 
70591a7de85SWillem de Bruijn 	for (cm = CMSG_FIRSTHDR(msg); cm; cm = CMSG_NXTHDR(msg, cm)) {
70691a7de85SWillem de Bruijn 		if (cm->cmsg_level != SOL_PACKET ||
70791a7de85SWillem de Bruijn 		    cm->cmsg_type != PACKET_AUXDATA)
70891a7de85SWillem de Bruijn 			error(1, 0, "cmsg: level=%d type=%d\n",
70991a7de85SWillem de Bruijn 			      cm->cmsg_level, cm->cmsg_type);
71091a7de85SWillem de Bruijn 
71191a7de85SWillem de Bruijn 		if (cm->cmsg_len != CMSG_LEN(sizeof(struct tpacket_auxdata)))
71291a7de85SWillem de Bruijn 			error(1, 0, "cmsg: len=%lu expected=%lu",
71391a7de85SWillem de Bruijn 			      cm->cmsg_len, CMSG_LEN(sizeof(struct tpacket_auxdata)));
71491a7de85SWillem de Bruijn 
71591a7de85SWillem de Bruijn 		aux = (void *)CMSG_DATA(cm);
71691a7de85SWillem de Bruijn 	}
71791a7de85SWillem de Bruijn 
71891a7de85SWillem de Bruijn 	if (!aux)
71991a7de85SWillem de Bruijn 		error(1, 0, "cmsg: no auxdata");
72091a7de85SWillem de Bruijn 
72191a7de85SWillem de Bruijn 	return aux->tp_status & TP_STATUS_CSUM_VALID;
72291a7de85SWillem de Bruijn }
72391a7de85SWillem de Bruijn 
recv_packet(int fd)72491a7de85SWillem de Bruijn static int recv_packet(int fd)
72591a7de85SWillem de Bruijn {
72691a7de85SWillem de Bruijn 	static char _buf[MAX_HEADER_LEN + MAX_PAYLOAD_LEN];
72791a7de85SWillem de Bruijn 	unsigned long total = 0, bad_csums = 0, bad_validations = 0;
72891a7de85SWillem de Bruijn 	char ctrl[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
72991a7de85SWillem de Bruijn 	struct pkt *buf = (void *)_buf;
73091a7de85SWillem de Bruijn 	struct msghdr msg = {0};
73191a7de85SWillem de Bruijn 	struct iovec iov;
73291a7de85SWillem de Bruijn 	int len, ret;
73391a7de85SWillem de Bruijn 
73491a7de85SWillem de Bruijn 	iov.iov_base = _buf;
73591a7de85SWillem de Bruijn 	iov.iov_len = sizeof(_buf);
73691a7de85SWillem de Bruijn 
73791a7de85SWillem de Bruijn 	msg.msg_iov = &iov;
73891a7de85SWillem de Bruijn 	msg.msg_iovlen = 1;
73991a7de85SWillem de Bruijn 
74091a7de85SWillem de Bruijn 	msg.msg_control = ctrl;
74191a7de85SWillem de Bruijn 	msg.msg_controllen = sizeof(ctrl);
74291a7de85SWillem de Bruijn 
74391a7de85SWillem de Bruijn 	while (1) {
74491a7de85SWillem de Bruijn 		msg.msg_flags = 0;
74591a7de85SWillem de Bruijn 
74691a7de85SWillem de Bruijn 		len = recvmsg(fd, &msg, MSG_DONTWAIT);
74791a7de85SWillem de Bruijn 		if (len == -1 && errno == EAGAIN)
74891a7de85SWillem de Bruijn 			break;
74991a7de85SWillem de Bruijn 		if (len == -1)
75091a7de85SWillem de Bruijn 			error(1, errno, "recv p");
75191a7de85SWillem de Bruijn 
75291a7de85SWillem de Bruijn 		if (cfg_family == PF_INET6)
75391a7de85SWillem de Bruijn 			ret = recv_verify_packet_ipv6(buf, len);
75491a7de85SWillem de Bruijn 		else
75591a7de85SWillem de Bruijn 			ret = recv_verify_packet_ipv4(buf, len);
75691a7de85SWillem de Bruijn 
75791a7de85SWillem de Bruijn 		if (ret == -1 /* skip: non-matching */)
75891a7de85SWillem de Bruijn 			continue;
75991a7de85SWillem de Bruijn 
76091a7de85SWillem de Bruijn 		total++;
76191a7de85SWillem de Bruijn 		if (ret == 1)
76291a7de85SWillem de Bruijn 			bad_csums++;
76391a7de85SWillem de Bruijn 
76491a7de85SWillem de Bruijn 		/* Fail if kernel returns valid for known bad csum.
76591a7de85SWillem de Bruijn 		 * Do not fail if kernel does not validate a good csum:
76691a7de85SWillem de Bruijn 		 * Absence of validation does not imply invalid.
76791a7de85SWillem de Bruijn 		 */
76891a7de85SWillem de Bruijn 		if (recv_verify_packet_csum(&msg) && cfg_bad_csum) {
76991a7de85SWillem de Bruijn 			fprintf(stderr, "cmsg: expected bad csum, pf_packet returns valid\n");
77091a7de85SWillem de Bruijn 			bad_validations++;
77191a7de85SWillem de Bruijn 		}
77291a7de85SWillem de Bruijn 	}
77391a7de85SWillem de Bruijn 
77491a7de85SWillem de Bruijn 	if (bad_csums || bad_validations)
77591a7de85SWillem de Bruijn 		error(1, 0, "rx: errors at pf_packet: total=%lu bad_csums=%lu bad_valids=%lu\n",
77691a7de85SWillem de Bruijn 		      total, bad_csums, bad_validations);
77791a7de85SWillem de Bruijn 
77891a7de85SWillem de Bruijn 	return total;
77991a7de85SWillem de Bruijn }
78091a7de85SWillem de Bruijn 
parse_args(int argc,char * const argv[])78191a7de85SWillem de Bruijn static void parse_args(int argc, char *const argv[])
78291a7de85SWillem de Bruijn {
78391a7de85SWillem de Bruijn 	const char *daddr = NULL, *saddr = NULL;
78491a7de85SWillem de Bruijn 	int c;
78591a7de85SWillem de Bruijn 
78691a7de85SWillem de Bruijn 	while ((c = getopt(argc, argv, "46d:D:eEi:l:L:n:r:PRs:S:tTuUzZ")) != -1) {
78791a7de85SWillem de Bruijn 		switch (c) {
78891a7de85SWillem de Bruijn 		case '4':
78991a7de85SWillem de Bruijn 			cfg_family = PF_INET;
79091a7de85SWillem de Bruijn 			break;
79191a7de85SWillem de Bruijn 		case '6':
79291a7de85SWillem de Bruijn 			cfg_family = PF_INET6;
79391a7de85SWillem de Bruijn 			break;
79491a7de85SWillem de Bruijn 		case 'd':
79591a7de85SWillem de Bruijn 			cfg_mac_dst = optarg;
79691a7de85SWillem de Bruijn 			break;
79791a7de85SWillem de Bruijn 		case 'D':
79891a7de85SWillem de Bruijn 			daddr = optarg;
79991a7de85SWillem de Bruijn 			break;
80091a7de85SWillem de Bruijn 		case 'e':
80191a7de85SWillem de Bruijn 			cfg_encap = true;
80291a7de85SWillem de Bruijn 			break;
80391a7de85SWillem de Bruijn 		case 'E':
80491a7de85SWillem de Bruijn 			cfg_bad_csum = true;
80591a7de85SWillem de Bruijn 			break;
80691a7de85SWillem de Bruijn 		case 'i':
80791a7de85SWillem de Bruijn 			cfg_ifname = optarg;
80891a7de85SWillem de Bruijn 			break;
80991a7de85SWillem de Bruijn 		case 'l':
81091a7de85SWillem de Bruijn 			cfg_payload_len = strtol(optarg, NULL, 0);
81191a7de85SWillem de Bruijn 			break;
81291a7de85SWillem de Bruijn 		case 'L':
81391a7de85SWillem de Bruijn 			cfg_timeout_ms = strtol(optarg, NULL, 0) * 1000;
81491a7de85SWillem de Bruijn 			break;
81591a7de85SWillem de Bruijn 		case 'n':
81691a7de85SWillem de Bruijn 			cfg_num_pkt = strtol(optarg, NULL, 0);
81791a7de85SWillem de Bruijn 			break;
81891a7de85SWillem de Bruijn 		case 'r':
81991a7de85SWillem de Bruijn 			cfg_random_seed = strtol(optarg, NULL, 0);
82091a7de85SWillem de Bruijn 			break;
82191a7de85SWillem de Bruijn 		case 'P':
82291a7de85SWillem de Bruijn 			cfg_send_pfpacket = true;
82391a7de85SWillem de Bruijn 			break;
82491a7de85SWillem de Bruijn 		case 'R':
82591a7de85SWillem de Bruijn 			/* only Rx: used with two machine tests */
82691a7de85SWillem de Bruijn 			cfg_do_tx = false;
82791a7de85SWillem de Bruijn 			break;
82891a7de85SWillem de Bruijn 		case 's':
82991a7de85SWillem de Bruijn 			cfg_mac_src = optarg;
83091a7de85SWillem de Bruijn 			break;
83191a7de85SWillem de Bruijn 		case 'S':
83291a7de85SWillem de Bruijn 			saddr = optarg;
83391a7de85SWillem de Bruijn 			break;
83491a7de85SWillem de Bruijn 		case 't':
83591a7de85SWillem de Bruijn 			cfg_proto = IPPROTO_TCP;
83691a7de85SWillem de Bruijn 			break;
83791a7de85SWillem de Bruijn 		case 'T':
83891a7de85SWillem de Bruijn 			/* only Tx: used with two machine tests */
83991a7de85SWillem de Bruijn 			cfg_do_rx = false;
84091a7de85SWillem de Bruijn 			break;
84191a7de85SWillem de Bruijn 		case 'u':
84291a7de85SWillem de Bruijn 			cfg_proto = IPPROTO_UDP;
84391a7de85SWillem de Bruijn 			break;
84491a7de85SWillem de Bruijn 		case 'U':
84591a7de85SWillem de Bruijn 			/* send using real udp socket,
84691a7de85SWillem de Bruijn 			 * to exercise tx checksum offload
84791a7de85SWillem de Bruijn 			 */
84891a7de85SWillem de Bruijn 			cfg_send_udp = true;
84991a7de85SWillem de Bruijn 			break;
85091a7de85SWillem de Bruijn 		case 'z':
85191a7de85SWillem de Bruijn 			cfg_zero_disable = true;
85291a7de85SWillem de Bruijn 			break;
85391a7de85SWillem de Bruijn 		case 'Z':
85491a7de85SWillem de Bruijn 			cfg_zero_sum = true;
85591a7de85SWillem de Bruijn 			break;
85691a7de85SWillem de Bruijn 		default:
85791a7de85SWillem de Bruijn 			error(1, 0, "unknown arg %c", c);
85891a7de85SWillem de Bruijn 		}
85991a7de85SWillem de Bruijn 	}
86091a7de85SWillem de Bruijn 
86191a7de85SWillem de Bruijn 	if (!daddr || !saddr)
86291a7de85SWillem de Bruijn 		error(1, 0, "Must pass -D <daddr> and -S <saddr>");
86391a7de85SWillem de Bruijn 
86491a7de85SWillem de Bruijn 	if (cfg_do_tx && cfg_send_pfpacket && (!cfg_mac_src || !cfg_mac_dst))
86591a7de85SWillem de Bruijn 		error(1, 0, "Transmit with pf_packet requires mac addresses");
86691a7de85SWillem de Bruijn 
86791a7de85SWillem de Bruijn 	if (cfg_payload_len > MAX_PAYLOAD_LEN)
86891a7de85SWillem de Bruijn 		error(1, 0, "Payload length exceeds max");
86991a7de85SWillem de Bruijn 
87091a7de85SWillem de Bruijn 	if (cfg_proto != IPPROTO_UDP && (cfg_zero_sum || cfg_zero_disable))
87191a7de85SWillem de Bruijn 		error(1, 0, "Only UDP supports zero csum");
87291a7de85SWillem de Bruijn 
87391a7de85SWillem de Bruijn 	if (cfg_zero_sum && !cfg_send_udp)
87491a7de85SWillem de Bruijn 		error(1, 0, "Zero checksum conversion requires -U for tx csum offload");
87591a7de85SWillem de Bruijn 	if (cfg_zero_sum && cfg_bad_csum)
87691a7de85SWillem de Bruijn 		error(1, 0, "Cannot combine zero checksum conversion and invalid checksum");
87791a7de85SWillem de Bruijn 	if (cfg_zero_sum && cfg_random_seed)
87891a7de85SWillem de Bruijn 		error(1, 0, "Cannot combine zero checksum conversion with randomization");
87991a7de85SWillem de Bruijn 
88091a7de85SWillem de Bruijn 	if (cfg_family == PF_INET6) {
88191a7de85SWillem de Bruijn 		cfg_saddr6.sin6_port = htons(cfg_port_src);
88291a7de85SWillem de Bruijn 		cfg_daddr6.sin6_port = htons(cfg_port_dst);
88391a7de85SWillem de Bruijn 
88491a7de85SWillem de Bruijn 		if (inet_pton(cfg_family, daddr, &cfg_daddr6.sin6_addr) != 1)
88591a7de85SWillem de Bruijn 			error(1, errno, "Cannot parse ipv6 -D");
88691a7de85SWillem de Bruijn 		if (inet_pton(cfg_family, saddr, &cfg_saddr6.sin6_addr) != 1)
88791a7de85SWillem de Bruijn 			error(1, errno, "Cannot parse ipv6 -S");
88891a7de85SWillem de Bruijn 	} else {
88991a7de85SWillem de Bruijn 		cfg_saddr4.sin_port = htons(cfg_port_src);
89091a7de85SWillem de Bruijn 		cfg_daddr4.sin_port = htons(cfg_port_dst);
89191a7de85SWillem de Bruijn 
89291a7de85SWillem de Bruijn 		if (inet_pton(cfg_family, daddr, &cfg_daddr4.sin_addr) != 1)
89391a7de85SWillem de Bruijn 			error(1, errno, "Cannot parse ipv4 -D");
89491a7de85SWillem de Bruijn 		if (inet_pton(cfg_family, saddr, &cfg_saddr4.sin_addr) != 1)
89591a7de85SWillem de Bruijn 			error(1, errno, "Cannot parse ipv4 -S");
89691a7de85SWillem de Bruijn 	}
89791a7de85SWillem de Bruijn 
89891a7de85SWillem de Bruijn 	if (cfg_do_tx && cfg_random_seed) {
89991a7de85SWillem de Bruijn 		/* special case: time-based seed */
90091a7de85SWillem de Bruijn 		if (cfg_random_seed == 1)
90191a7de85SWillem de Bruijn 			cfg_random_seed = (unsigned int)gettimeofday_ms();
90291a7de85SWillem de Bruijn 		srand(cfg_random_seed);
90391a7de85SWillem de Bruijn 		fprintf(stderr, "randomization seed: %u\n", cfg_random_seed);
90491a7de85SWillem de Bruijn 	}
90591a7de85SWillem de Bruijn }
90691a7de85SWillem de Bruijn 
do_tx(void)90791a7de85SWillem de Bruijn static void do_tx(void)
90891a7de85SWillem de Bruijn {
90991a7de85SWillem de Bruijn 	static char _buf[MAX_HEADER_LEN + MAX_PAYLOAD_LEN];
91091a7de85SWillem de Bruijn 	char *buf;
91191a7de85SWillem de Bruijn 	int fd, len, i;
91291a7de85SWillem de Bruijn 
91391a7de85SWillem de Bruijn 	buf = build_packet(_buf, sizeof(_buf), &len);
91491a7de85SWillem de Bruijn 
91591a7de85SWillem de Bruijn 	if (cfg_send_pfpacket)
91691a7de85SWillem de Bruijn 		fd = open_packet();
91791a7de85SWillem de Bruijn 	else if (cfg_send_udp)
91891a7de85SWillem de Bruijn 		fd = open_inet(SOCK_DGRAM, 0);
91991a7de85SWillem de Bruijn 	else
92091a7de85SWillem de Bruijn 		fd = open_inet(SOCK_RAW, IPPROTO_RAW);
92191a7de85SWillem de Bruijn 
92291a7de85SWillem de Bruijn 	for (i = 0; i < cfg_num_pkt; i++) {
92391a7de85SWillem de Bruijn 		if (cfg_send_pfpacket)
92491a7de85SWillem de Bruijn 			send_packet(fd, buf, len);
92591a7de85SWillem de Bruijn 		else
92691a7de85SWillem de Bruijn 			send_inet(fd, buf, len);
92791a7de85SWillem de Bruijn 
92891a7de85SWillem de Bruijn 		/* randomize each packet individually to increase coverage */
92991a7de85SWillem de Bruijn 		if (cfg_random_seed) {
93091a7de85SWillem de Bruijn 			cfg_payload_len = rand() % MAX_PAYLOAD_LEN;
93191a7de85SWillem de Bruijn 			buf = build_packet(_buf, sizeof(_buf), &len);
93291a7de85SWillem de Bruijn 		}
93391a7de85SWillem de Bruijn 	}
93491a7de85SWillem de Bruijn 
93591a7de85SWillem de Bruijn 	if (close(fd))
93691a7de85SWillem de Bruijn 		error(1, errno, "close tx");
93791a7de85SWillem de Bruijn }
93891a7de85SWillem de Bruijn 
do_rx(int fdp,int fdr)93991a7de85SWillem de Bruijn static void do_rx(int fdp, int fdr)
94091a7de85SWillem de Bruijn {
94191a7de85SWillem de Bruijn 	unsigned long count_udp = 0, count_pkt = 0;
94291a7de85SWillem de Bruijn 	long tleft, tstop;
94391a7de85SWillem de Bruijn 	struct pollfd pfd;
94491a7de85SWillem de Bruijn 
94591a7de85SWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_timeout_ms;
94691a7de85SWillem de Bruijn 	tleft = cfg_timeout_ms;
94791a7de85SWillem de Bruijn 
94891a7de85SWillem de Bruijn 	do {
94991a7de85SWillem de Bruijn 		pfd.events = POLLIN;
95091a7de85SWillem de Bruijn 		pfd.fd = fdp;
95191a7de85SWillem de Bruijn 		if (poll(&pfd, 1, tleft) == -1)
95291a7de85SWillem de Bruijn 			error(1, errno, "poll");
95391a7de85SWillem de Bruijn 
95491a7de85SWillem de Bruijn 		if (pfd.revents & POLLIN)
95591a7de85SWillem de Bruijn 			count_pkt += recv_packet(fdp);
95691a7de85SWillem de Bruijn 
95791a7de85SWillem de Bruijn 		if (cfg_proto == IPPROTO_UDP)
95891a7de85SWillem de Bruijn 			count_udp += recv_udp(fdr);
95991a7de85SWillem de Bruijn 
96091a7de85SWillem de Bruijn 		tleft = tstop - gettimeofday_ms();
96191a7de85SWillem de Bruijn 	} while (tleft > 0);
96291a7de85SWillem de Bruijn 
96391a7de85SWillem de Bruijn 	if (close(fdr))
96491a7de85SWillem de Bruijn 		error(1, errno, "close r");
96591a7de85SWillem de Bruijn 	if (close(fdp))
96691a7de85SWillem de Bruijn 		error(1, errno, "close p");
96791a7de85SWillem de Bruijn 
96891a7de85SWillem de Bruijn 	if (count_pkt < cfg_num_pkt)
96991a7de85SWillem de Bruijn 		error(1, 0, "rx: missing packets at pf_packet: %lu < %u",
97091a7de85SWillem de Bruijn 		      count_pkt, cfg_num_pkt);
97191a7de85SWillem de Bruijn 
97291a7de85SWillem de Bruijn 	if (cfg_proto == IPPROTO_UDP) {
97391a7de85SWillem de Bruijn 		if (cfg_bad_csum && count_udp)
97491a7de85SWillem de Bruijn 			error(1, 0, "rx: unexpected packets at udp");
97591a7de85SWillem de Bruijn 		if (!cfg_bad_csum && !count_udp)
97691a7de85SWillem de Bruijn 			error(1, 0, "rx: missing packets at udp");
97791a7de85SWillem de Bruijn 	}
97891a7de85SWillem de Bruijn }
97991a7de85SWillem de Bruijn 
main(int argc,char * const argv[])98091a7de85SWillem de Bruijn int main(int argc, char *const argv[])
98191a7de85SWillem de Bruijn {
98291a7de85SWillem de Bruijn 	int fdp = -1, fdr = -1;		/* -1 to silence -Wmaybe-uninitialized */
98391a7de85SWillem de Bruijn 
98491a7de85SWillem de Bruijn 	parse_args(argc, argv);
98591a7de85SWillem de Bruijn 
98691a7de85SWillem de Bruijn 	/* open receive sockets before transmitting */
98791a7de85SWillem de Bruijn 	if (cfg_do_rx) {
98891a7de85SWillem de Bruijn 		fdp = recv_prepare_packet();
98991a7de85SWillem de Bruijn 		fdr = recv_prepare_udp();
99091a7de85SWillem de Bruijn 	}
99191a7de85SWillem de Bruijn 
99291a7de85SWillem de Bruijn 	if (cfg_do_tx)
99391a7de85SWillem de Bruijn 		do_tx();
99491a7de85SWillem de Bruijn 
99591a7de85SWillem de Bruijn 	if (cfg_do_rx)
99691a7de85SWillem de Bruijn 		do_rx(fdp, fdr);
99791a7de85SWillem de Bruijn 
99891a7de85SWillem de Bruijn 	fprintf(stderr, "OK\n");
99991a7de85SWillem de Bruijn 	return 0;
100091a7de85SWillem de Bruijn }
1001