xref: /openbmc/linux/tools/testing/selftests/net/gro.c (revision 7d1575014a63caeebb13b000ee152ce711580119)
1*7d157501SCoco Li // SPDX-License-Identifier: GPL-2.0
2*7d157501SCoco Li /*
3*7d157501SCoco Li  * This testsuite provides conformance testing for GRO coalescing.
4*7d157501SCoco Li  *
5*7d157501SCoco Li  * Test cases:
6*7d157501SCoco Li  * 1.data
7*7d157501SCoco Li  *  Data packets of the same size and same header setup with correct
8*7d157501SCoco Li  *  sequence numbers coalesce. The one exception being the last data
9*7d157501SCoco Li  *  packet coalesced: it can be smaller than the rest and coalesced
10*7d157501SCoco Li  *  as long as it is in the same flow.
11*7d157501SCoco Li  * 2.ack
12*7d157501SCoco Li  *  Pure ACK does not coalesce.
13*7d157501SCoco Li  * 3.flags
14*7d157501SCoco Li  *  Specific test cases: no packets with PSH, SYN, URG, RST set will
15*7d157501SCoco Li  *  be coalesced.
16*7d157501SCoco Li  * 4.tcp
17*7d157501SCoco Li  *  Packets with incorrect checksum, non-consecutive seqno and
18*7d157501SCoco Li  *  different TCP header options shouldn't coalesce. Nit: given that
19*7d157501SCoco Li  *  some extension headers have paddings, such as timestamp, headers
20*7d157501SCoco Li  *  that are padding differently would not be coalesced.
21*7d157501SCoco Li  * 5.ip:
22*7d157501SCoco Li  *  Packets with different (ECN, TTL, TOS) header, ip options or
23*7d157501SCoco Li  *  ip fragments (ipv6) shouldn't coalesce.
24*7d157501SCoco Li  * 6.large:
25*7d157501SCoco Li  *  Packets larger than GRO_MAX_SIZE packets shouldn't coalesce.
26*7d157501SCoco Li  *
27*7d157501SCoco Li  * MSS is defined as 4096 - header because if it is too small
28*7d157501SCoco Li  * (i.e. 1500 MTU - header), it will result in many packets,
29*7d157501SCoco Li  * increasing the "large" test case's flakiness. This is because
30*7d157501SCoco Li  * due to time sensitivity in the coalescing window, the receiver
31*7d157501SCoco Li  * may not coalesce all of the packets.
32*7d157501SCoco Li  *
33*7d157501SCoco Li  * Note the timing issue applies to all of the test cases, so some
34*7d157501SCoco Li  * flakiness is to be expected.
35*7d157501SCoco Li  *
36*7d157501SCoco Li  */
37*7d157501SCoco Li 
38*7d157501SCoco Li #define _GNU_SOURCE
39*7d157501SCoco Li 
40*7d157501SCoco Li #include <arpa/inet.h>
41*7d157501SCoco Li #include <errno.h>
42*7d157501SCoco Li #include <error.h>
43*7d157501SCoco Li #include <getopt.h>
44*7d157501SCoco Li #include <linux/filter.h>
45*7d157501SCoco Li #include <linux/if_packet.h>
46*7d157501SCoco Li #include <linux/ipv6.h>
47*7d157501SCoco Li #include <net/ethernet.h>
48*7d157501SCoco Li #include <net/if.h>
49*7d157501SCoco Li #include <netinet/in.h>
50*7d157501SCoco Li #include <netinet/ip.h>
51*7d157501SCoco Li #include <netinet/ip6.h>
52*7d157501SCoco Li #include <netinet/tcp.h>
53*7d157501SCoco Li #include <stdbool.h>
54*7d157501SCoco Li #include <stddef.h>
55*7d157501SCoco Li #include <stdio.h>
56*7d157501SCoco Li #include <stdarg.h>
57*7d157501SCoco Li #include <string.h>
58*7d157501SCoco Li #include <unistd.h>
59*7d157501SCoco Li 
60*7d157501SCoco Li #define DPORT 8000
61*7d157501SCoco Li #define SPORT 1500
62*7d157501SCoco Li #define PAYLOAD_LEN 100
63*7d157501SCoco Li #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
64*7d157501SCoco Li #define NUM_PACKETS 4
65*7d157501SCoco Li #define START_SEQ 100
66*7d157501SCoco Li #define START_ACK 100
67*7d157501SCoco Li #define SIP6 "fdaa::2"
68*7d157501SCoco Li #define DIP6 "fdaa::1"
69*7d157501SCoco Li #define SIP4 "192.168.1.200"
70*7d157501SCoco Li #define DIP4 "192.168.1.100"
71*7d157501SCoco Li #define ETH_P_NONE 0
72*7d157501SCoco Li #define TOTAL_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct tcphdr))
73*7d157501SCoco Li #define MSS (4096 - sizeof(struct tcphdr) - sizeof(struct ipv6hdr))
74*7d157501SCoco Li #define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) - sizeof(struct ipv6hdr))
75*7d157501SCoco Li #define NUM_LARGE_PKT (MAX_PAYLOAD / MSS)
76*7d157501SCoco Li #define MAX_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct tcphdr))
77*7d157501SCoco Li 
78*7d157501SCoco Li static int proto = -1;
79*7d157501SCoco Li static uint8_t src_mac[ETH_ALEN], dst_mac[ETH_ALEN];
80*7d157501SCoco Li static char *testname = "data";
81*7d157501SCoco Li static char *ifname = "eth0";
82*7d157501SCoco Li static char *smac = "aa:00:00:00:00:02";
83*7d157501SCoco Li static char *dmac = "aa:00:00:00:00:01";
84*7d157501SCoco Li static bool verbose;
85*7d157501SCoco Li static bool tx_socket = true;
86*7d157501SCoco Li static int tcp_offset = -1;
87*7d157501SCoco Li static int total_hdr_len = -1;
88*7d157501SCoco Li static int ethhdr_proto = -1;
89*7d157501SCoco Li 
90*7d157501SCoco Li static void vlog(const char *fmt, ...)
91*7d157501SCoco Li {
92*7d157501SCoco Li 	va_list args;
93*7d157501SCoco Li 
94*7d157501SCoco Li 	if (verbose) {
95*7d157501SCoco Li 		va_start(args, fmt);
96*7d157501SCoco Li 		vfprintf(stderr, fmt, args);
97*7d157501SCoco Li 		va_end(args);
98*7d157501SCoco Li 	}
99*7d157501SCoco Li }
100*7d157501SCoco Li 
101*7d157501SCoco Li static void setup_sock_filter(int fd)
102*7d157501SCoco Li {
103*7d157501SCoco Li 	const int dport_off = tcp_offset + offsetof(struct tcphdr, dest);
104*7d157501SCoco Li 	const int ethproto_off = offsetof(struct ethhdr, h_proto);
105*7d157501SCoco Li 	int optlen = 0;
106*7d157501SCoco Li 	int ipproto_off;
107*7d157501SCoco Li 	int next_off;
108*7d157501SCoco Li 
109*7d157501SCoco Li 	if (proto == PF_INET)
110*7d157501SCoco Li 		next_off = offsetof(struct iphdr, protocol);
111*7d157501SCoco Li 	else
112*7d157501SCoco Li 		next_off = offsetof(struct ipv6hdr, nexthdr);
113*7d157501SCoco Li 	ipproto_off = ETH_HLEN + next_off;
114*7d157501SCoco Li 
115*7d157501SCoco Li 	if (strcmp(testname, "ip") == 0) {
116*7d157501SCoco Li 		if (proto == PF_INET)
117*7d157501SCoco Li 			optlen = sizeof(struct ip_timestamp);
118*7d157501SCoco Li 		else
119*7d157501SCoco Li 			optlen = sizeof(struct ip6_frag);
120*7d157501SCoco Li 	}
121*7d157501SCoco Li 
122*7d157501SCoco Li 	struct sock_filter filter[] = {
123*7d157501SCoco Li 			BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, ethproto_off),
124*7d157501SCoco Li 			BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ntohs(ethhdr_proto), 0, 7),
125*7d157501SCoco Li 			BPF_STMT(BPF_LD  + BPF_B   + BPF_ABS, ipproto_off),
126*7d157501SCoco Li 			BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 5),
127*7d157501SCoco Li 			BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, dport_off),
128*7d157501SCoco Li 			BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 2, 0),
129*7d157501SCoco Li 			BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, dport_off + optlen),
130*7d157501SCoco Li 			BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 0, 1),
131*7d157501SCoco Li 			BPF_STMT(BPF_RET + BPF_K, 0xFFFFFFFF),
132*7d157501SCoco Li 			BPF_STMT(BPF_RET + BPF_K, 0),
133*7d157501SCoco Li 	};
134*7d157501SCoco Li 
135*7d157501SCoco Li 	struct sock_fprog bpf = {
136*7d157501SCoco Li 		.len = ARRAY_SIZE(filter),
137*7d157501SCoco Li 		.filter = filter,
138*7d157501SCoco Li 	};
139*7d157501SCoco Li 
140*7d157501SCoco Li 	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)) < 0)
141*7d157501SCoco Li 		error(1, errno, "error setting filter");
142*7d157501SCoco Li }
143*7d157501SCoco Li 
144*7d157501SCoco Li static uint32_t checksum_nofold(void *data, size_t len, uint32_t sum)
145*7d157501SCoco Li {
146*7d157501SCoco Li 	uint16_t *words = data;
147*7d157501SCoco Li 	int i;
148*7d157501SCoco Li 
149*7d157501SCoco Li 	for (i = 0; i < len / 2; i++)
150*7d157501SCoco Li 		sum += words[i];
151*7d157501SCoco Li 	if (len & 1)
152*7d157501SCoco Li 		sum += ((char *)data)[len - 1];
153*7d157501SCoco Li 	return sum;
154*7d157501SCoco Li }
155*7d157501SCoco Li 
156*7d157501SCoco Li static uint16_t checksum_fold(void *data, size_t len, uint32_t sum)
157*7d157501SCoco Li {
158*7d157501SCoco Li 	sum = checksum_nofold(data, len, sum);
159*7d157501SCoco Li 	while (sum > 0xFFFF)
160*7d157501SCoco Li 		sum = (sum & 0xFFFF) + (sum >> 16);
161*7d157501SCoco Li 	return ~sum;
162*7d157501SCoco Li }
163*7d157501SCoco Li 
164*7d157501SCoco Li static uint16_t tcp_checksum(void *buf, int payload_len)
165*7d157501SCoco Li {
166*7d157501SCoco Li 	struct pseudo_header6 {
167*7d157501SCoco Li 		struct in6_addr saddr;
168*7d157501SCoco Li 		struct in6_addr daddr;
169*7d157501SCoco Li 		uint16_t protocol;
170*7d157501SCoco Li 		uint16_t payload_len;
171*7d157501SCoco Li 	} ph6;
172*7d157501SCoco Li 	struct pseudo_header4 {
173*7d157501SCoco Li 		struct in_addr saddr;
174*7d157501SCoco Li 		struct in_addr daddr;
175*7d157501SCoco Li 		uint16_t protocol;
176*7d157501SCoco Li 		uint16_t payload_len;
177*7d157501SCoco Li 	} ph4;
178*7d157501SCoco Li 	uint32_t sum = 0;
179*7d157501SCoco Li 
180*7d157501SCoco Li 	if (proto == PF_INET6) {
181*7d157501SCoco Li 		if (inet_pton(AF_INET6, SIP6, &ph6.saddr) != 1)
182*7d157501SCoco Li 			error(1, errno, "inet_pton6 source ip pseudo");
183*7d157501SCoco Li 		if (inet_pton(AF_INET6, DIP6, &ph6.daddr) != 1)
184*7d157501SCoco Li 			error(1, errno, "inet_pton6 dest ip pseudo");
185*7d157501SCoco Li 		ph6.protocol = htons(IPPROTO_TCP);
186*7d157501SCoco Li 		ph6.payload_len = htons(sizeof(struct tcphdr) + payload_len);
187*7d157501SCoco Li 
188*7d157501SCoco Li 		sum = checksum_nofold(&ph6, sizeof(ph6), 0);
189*7d157501SCoco Li 	} else if (proto == PF_INET) {
190*7d157501SCoco Li 		if (inet_pton(AF_INET, SIP4, &ph4.saddr) != 1)
191*7d157501SCoco Li 			error(1, errno, "inet_pton source ip pseudo");
192*7d157501SCoco Li 		if (inet_pton(AF_INET, DIP4, &ph4.daddr) != 1)
193*7d157501SCoco Li 			error(1, errno, "inet_pton dest ip pseudo");
194*7d157501SCoco Li 		ph4.protocol = htons(IPPROTO_TCP);
195*7d157501SCoco Li 		ph4.payload_len = htons(sizeof(struct tcphdr) + payload_len);
196*7d157501SCoco Li 
197*7d157501SCoco Li 		sum = checksum_nofold(&ph4, sizeof(ph4), 0);
198*7d157501SCoco Li 	}
199*7d157501SCoco Li 
200*7d157501SCoco Li 	return checksum_fold(buf, sizeof(struct tcphdr) + payload_len, sum);
201*7d157501SCoco Li }
202*7d157501SCoco Li 
203*7d157501SCoco Li static void read_MAC(uint8_t *mac_addr, char *mac)
204*7d157501SCoco Li {
205*7d157501SCoco Li 	if (sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
206*7d157501SCoco Li 		   &mac_addr[0], &mac_addr[1], &mac_addr[2],
207*7d157501SCoco Li 		   &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6)
208*7d157501SCoco Li 		error(1, 0, "sscanf");
209*7d157501SCoco Li }
210*7d157501SCoco Li 
211*7d157501SCoco Li static void fill_datalinklayer(void *buf)
212*7d157501SCoco Li {
213*7d157501SCoco Li 	struct ethhdr *eth = buf;
214*7d157501SCoco Li 
215*7d157501SCoco Li 	memcpy(eth->h_dest, dst_mac, ETH_ALEN);
216*7d157501SCoco Li 	memcpy(eth->h_source, src_mac, ETH_ALEN);
217*7d157501SCoco Li 	eth->h_proto = ethhdr_proto;
218*7d157501SCoco Li }
219*7d157501SCoco Li 
220*7d157501SCoco Li static void fill_networklayer(void *buf, int payload_len)
221*7d157501SCoco Li {
222*7d157501SCoco Li 	struct ipv6hdr *ip6h = buf;
223*7d157501SCoco Li 	struct iphdr *iph = buf;
224*7d157501SCoco Li 
225*7d157501SCoco Li 	if (proto == PF_INET6) {
226*7d157501SCoco Li 		memset(ip6h, 0, sizeof(*ip6h));
227*7d157501SCoco Li 
228*7d157501SCoco Li 		ip6h->version = 6;
229*7d157501SCoco Li 		ip6h->payload_len = htons(sizeof(struct tcphdr) + payload_len);
230*7d157501SCoco Li 		ip6h->nexthdr = IPPROTO_TCP;
231*7d157501SCoco Li 		ip6h->hop_limit = 8;
232*7d157501SCoco Li 		if (inet_pton(AF_INET6, SIP6, &ip6h->saddr) != 1)
233*7d157501SCoco Li 			error(1, errno, "inet_pton source ip6");
234*7d157501SCoco Li 		if (inet_pton(AF_INET6, DIP6, &ip6h->daddr) != 1)
235*7d157501SCoco Li 			error(1, errno, "inet_pton dest ip6");
236*7d157501SCoco Li 	} else if (proto == PF_INET) {
237*7d157501SCoco Li 		memset(iph, 0, sizeof(*iph));
238*7d157501SCoco Li 
239*7d157501SCoco Li 		iph->version = 4;
240*7d157501SCoco Li 		iph->ihl = 5;
241*7d157501SCoco Li 		iph->ttl = 8;
242*7d157501SCoco Li 		iph->protocol	= IPPROTO_TCP;
243*7d157501SCoco Li 		iph->tot_len = htons(sizeof(struct tcphdr) +
244*7d157501SCoco Li 				payload_len + sizeof(struct iphdr));
245*7d157501SCoco Li 		iph->frag_off = htons(0x4000); /* DF = 1, MF = 0 */
246*7d157501SCoco Li 		if (inet_pton(AF_INET, SIP4, &iph->saddr) != 1)
247*7d157501SCoco Li 			error(1, errno, "inet_pton source ip");
248*7d157501SCoco Li 		if (inet_pton(AF_INET, DIP4, &iph->daddr) != 1)
249*7d157501SCoco Li 			error(1, errno, "inet_pton dest ip");
250*7d157501SCoco Li 		iph->check = checksum_fold(buf, sizeof(struct iphdr), 0);
251*7d157501SCoco Li 	}
252*7d157501SCoco Li }
253*7d157501SCoco Li 
254*7d157501SCoco Li static void fill_transportlayer(void *buf, int seq_offset, int ack_offset,
255*7d157501SCoco Li 				int payload_len, int fin)
256*7d157501SCoco Li {
257*7d157501SCoco Li 	struct tcphdr *tcph = buf;
258*7d157501SCoco Li 
259*7d157501SCoco Li 	memset(tcph, 0, sizeof(*tcph));
260*7d157501SCoco Li 
261*7d157501SCoco Li 	tcph->source = htons(SPORT);
262*7d157501SCoco Li 	tcph->dest = htons(DPORT);
263*7d157501SCoco Li 	tcph->seq = ntohl(START_SEQ + seq_offset);
264*7d157501SCoco Li 	tcph->ack_seq = ntohl(START_ACK + ack_offset);
265*7d157501SCoco Li 	tcph->ack = 1;
266*7d157501SCoco Li 	tcph->fin = fin;
267*7d157501SCoco Li 	tcph->doff = 5;
268*7d157501SCoco Li 	tcph->window = htons(TCP_MAXWIN);
269*7d157501SCoco Li 	tcph->urg_ptr = 0;
270*7d157501SCoco Li 	tcph->check = tcp_checksum(tcph, payload_len);
271*7d157501SCoco Li }
272*7d157501SCoco Li 
273*7d157501SCoco Li static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)
274*7d157501SCoco Li {
275*7d157501SCoco Li 	int ret = -1;
276*7d157501SCoco Li 
277*7d157501SCoco Li 	ret = sendto(fd, buf, len, 0, (struct sockaddr *)daddr, sizeof(*daddr));
278*7d157501SCoco Li 	if (ret == -1)
279*7d157501SCoco Li 		error(1, errno, "sendto failure");
280*7d157501SCoco Li 	if (ret != len)
281*7d157501SCoco Li 		error(1, errno, "sendto wrong length");
282*7d157501SCoco Li }
283*7d157501SCoco Li 
284*7d157501SCoco Li static void create_packet(void *buf, int seq_offset, int ack_offset,
285*7d157501SCoco Li 			  int payload_len, int fin)
286*7d157501SCoco Li {
287*7d157501SCoco Li 	memset(buf, 0, total_hdr_len);
288*7d157501SCoco Li 	memset(buf + total_hdr_len, 'a', payload_len);
289*7d157501SCoco Li 	fill_transportlayer(buf + tcp_offset, seq_offset, ack_offset,
290*7d157501SCoco Li 			    payload_len, fin);
291*7d157501SCoco Li 	fill_networklayer(buf + ETH_HLEN, payload_len);
292*7d157501SCoco Li 	fill_datalinklayer(buf);
293*7d157501SCoco Li }
294*7d157501SCoco Li 
295*7d157501SCoco Li /* send one extra flag, not first and not last pkt */
296*7d157501SCoco Li static void send_flags(int fd, struct sockaddr_ll *daddr, int psh, int syn,
297*7d157501SCoco Li 		       int rst, int urg)
298*7d157501SCoco Li {
299*7d157501SCoco Li 	static char flag_buf[MAX_HDR_LEN + PAYLOAD_LEN];
300*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
301*7d157501SCoco Li 	int payload_len, pkt_size, flag, i;
302*7d157501SCoco Li 	struct tcphdr *tcph;
303*7d157501SCoco Li 
304*7d157501SCoco Li 	payload_len = PAYLOAD_LEN * psh;
305*7d157501SCoco Li 	pkt_size = total_hdr_len + payload_len;
306*7d157501SCoco Li 	flag = NUM_PACKETS / 2;
307*7d157501SCoco Li 
308*7d157501SCoco Li 	create_packet(flag_buf, flag * payload_len, 0, payload_len, 0);
309*7d157501SCoco Li 
310*7d157501SCoco Li 	tcph = (struct tcphdr *)(flag_buf + tcp_offset);
311*7d157501SCoco Li 	tcph->psh = psh;
312*7d157501SCoco Li 	tcph->syn = syn;
313*7d157501SCoco Li 	tcph->rst = rst;
314*7d157501SCoco Li 	tcph->urg = urg;
315*7d157501SCoco Li 	tcph->check = 0;
316*7d157501SCoco Li 	tcph->check = tcp_checksum(tcph, payload_len);
317*7d157501SCoco Li 
318*7d157501SCoco Li 	for (i = 0; i < NUM_PACKETS + 1; i++) {
319*7d157501SCoco Li 		if (i == flag) {
320*7d157501SCoco Li 			write_packet(fd, flag_buf, pkt_size, daddr);
321*7d157501SCoco Li 			continue;
322*7d157501SCoco Li 		}
323*7d157501SCoco Li 		create_packet(buf, i * PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
324*7d157501SCoco Li 		write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);
325*7d157501SCoco Li 	}
326*7d157501SCoco Li }
327*7d157501SCoco Li 
328*7d157501SCoco Li /* Test for data of same length, smaller than previous
329*7d157501SCoco Li  * and of different lengths
330*7d157501SCoco Li  */
331*7d157501SCoco Li static void send_data_pkts(int fd, struct sockaddr_ll *daddr,
332*7d157501SCoco Li 			   int payload_len1, int payload_len2)
333*7d157501SCoco Li {
334*7d157501SCoco Li 	static char buf[ETH_HLEN + IP_MAXPACKET];
335*7d157501SCoco Li 
336*7d157501SCoco Li 	create_packet(buf, 0, 0, payload_len1, 0);
337*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len + payload_len1, daddr);
338*7d157501SCoco Li 	create_packet(buf, payload_len1, 0, payload_len2, 0);
339*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len + payload_len2, daddr);
340*7d157501SCoco Li }
341*7d157501SCoco Li 
342*7d157501SCoco Li /* If incoming segments make tracked segment length exceed
343*7d157501SCoco Li  * legal IP datagram length, do not coalesce
344*7d157501SCoco Li  */
345*7d157501SCoco Li static void send_large(int fd, struct sockaddr_ll *daddr, int remainder)
346*7d157501SCoco Li {
347*7d157501SCoco Li 	static char pkts[NUM_LARGE_PKT][TOTAL_HDR_LEN + MSS];
348*7d157501SCoco Li 	static char last[TOTAL_HDR_LEN + MSS];
349*7d157501SCoco Li 	static char new_seg[TOTAL_HDR_LEN + MSS];
350*7d157501SCoco Li 	int i;
351*7d157501SCoco Li 
352*7d157501SCoco Li 	for (i = 0; i < NUM_LARGE_PKT; i++)
353*7d157501SCoco Li 		create_packet(pkts[i], i * MSS, 0, MSS, 0);
354*7d157501SCoco Li 	create_packet(last, NUM_LARGE_PKT * MSS, 0, remainder, 0);
355*7d157501SCoco Li 	create_packet(new_seg, (NUM_LARGE_PKT + 1) * MSS, 0, remainder, 0);
356*7d157501SCoco Li 
357*7d157501SCoco Li 	for (i = 0; i < NUM_LARGE_PKT; i++)
358*7d157501SCoco Li 		write_packet(fd, pkts[i], total_hdr_len + MSS, daddr);
359*7d157501SCoco Li 	write_packet(fd, last, total_hdr_len + remainder, daddr);
360*7d157501SCoco Li 	write_packet(fd, new_seg, total_hdr_len + remainder, daddr);
361*7d157501SCoco Li }
362*7d157501SCoco Li 
363*7d157501SCoco Li /* Pure acks and dup acks don't coalesce */
364*7d157501SCoco Li static void send_ack(int fd, struct sockaddr_ll *daddr)
365*7d157501SCoco Li {
366*7d157501SCoco Li 	static char buf[MAX_HDR_LEN];
367*7d157501SCoco Li 
368*7d157501SCoco Li 	create_packet(buf, 0, 0, 0, 0);
369*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len, daddr);
370*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len, daddr);
371*7d157501SCoco Li 	create_packet(buf, 0, 1, 0, 0);
372*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len, daddr);
373*7d157501SCoco Li }
374*7d157501SCoco Li 
375*7d157501SCoco Li static void recompute_packet(char *buf, char *no_ext, int extlen)
376*7d157501SCoco Li {
377*7d157501SCoco Li 	struct tcphdr *tcphdr = (struct tcphdr *)(buf + tcp_offset);
378*7d157501SCoco Li 	struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);
379*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);
380*7d157501SCoco Li 
381*7d157501SCoco Li 	memmove(buf, no_ext, total_hdr_len);
382*7d157501SCoco Li 	memmove(buf + total_hdr_len + extlen,
383*7d157501SCoco Li 		no_ext + total_hdr_len, PAYLOAD_LEN);
384*7d157501SCoco Li 
385*7d157501SCoco Li 	tcphdr->doff = tcphdr->doff + (extlen / 4);
386*7d157501SCoco Li 	tcphdr->check = 0;
387*7d157501SCoco Li 	tcphdr->check = tcp_checksum(tcphdr, PAYLOAD_LEN + extlen);
388*7d157501SCoco Li 	if (proto == PF_INET) {
389*7d157501SCoco Li 		iph->tot_len = htons(ntohs(iph->tot_len) + extlen);
390*7d157501SCoco Li 		iph->check = 0;
391*7d157501SCoco Li 		iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);
392*7d157501SCoco Li 	} else {
393*7d157501SCoco Li 		ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);
394*7d157501SCoco Li 	}
395*7d157501SCoco Li }
396*7d157501SCoco Li 
397*7d157501SCoco Li static void tcp_write_options(char *buf, int kind, int ts)
398*7d157501SCoco Li {
399*7d157501SCoco Li 	struct tcp_option_ts {
400*7d157501SCoco Li 		uint8_t kind;
401*7d157501SCoco Li 		uint8_t len;
402*7d157501SCoco Li 		uint32_t tsval;
403*7d157501SCoco Li 		uint32_t tsecr;
404*7d157501SCoco Li 	} *opt_ts = (void *)buf;
405*7d157501SCoco Li 	struct tcp_option_window {
406*7d157501SCoco Li 		uint8_t kind;
407*7d157501SCoco Li 		uint8_t len;
408*7d157501SCoco Li 		uint8_t shift;
409*7d157501SCoco Li 	} *opt_window = (void *)buf;
410*7d157501SCoco Li 
411*7d157501SCoco Li 	switch (kind) {
412*7d157501SCoco Li 	case TCPOPT_NOP:
413*7d157501SCoco Li 		buf[0] = TCPOPT_NOP;
414*7d157501SCoco Li 		break;
415*7d157501SCoco Li 	case TCPOPT_WINDOW:
416*7d157501SCoco Li 		memset(opt_window, 0, sizeof(struct tcp_option_window));
417*7d157501SCoco Li 		opt_window->kind = TCPOPT_WINDOW;
418*7d157501SCoco Li 		opt_window->len = TCPOLEN_WINDOW;
419*7d157501SCoco Li 		opt_window->shift = 0;
420*7d157501SCoco Li 		break;
421*7d157501SCoco Li 	case TCPOPT_TIMESTAMP:
422*7d157501SCoco Li 		memset(opt_ts, 0, sizeof(struct tcp_option_ts));
423*7d157501SCoco Li 		opt_ts->kind = TCPOPT_TIMESTAMP;
424*7d157501SCoco Li 		opt_ts->len = TCPOLEN_TIMESTAMP;
425*7d157501SCoco Li 		opt_ts->tsval = ts;
426*7d157501SCoco Li 		opt_ts->tsecr = 0;
427*7d157501SCoco Li 		break;
428*7d157501SCoco Li 	default:
429*7d157501SCoco Li 		error(1, 0, "unimplemented TCP option");
430*7d157501SCoco Li 		break;
431*7d157501SCoco Li 	}
432*7d157501SCoco Li }
433*7d157501SCoco Li 
434*7d157501SCoco Li /* TCP with options is always a permutation of {TS, NOP, NOP}.
435*7d157501SCoco Li  * Implement different orders to verify coalescing stops.
436*7d157501SCoco Li  */
437*7d157501SCoco Li static void add_standard_tcp_options(char *buf, char *no_ext, int ts, int order)
438*7d157501SCoco Li {
439*7d157501SCoco Li 	switch (order) {
440*7d157501SCoco Li 	case 0:
441*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len, TCPOPT_NOP, 0);
442*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + 1, TCPOPT_NOP, 0);
443*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + 2 /* two NOP opts */,
444*7d157501SCoco Li 				  TCPOPT_TIMESTAMP, ts);
445*7d157501SCoco Li 		break;
446*7d157501SCoco Li 	case 1:
447*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len, TCPOPT_NOP, 0);
448*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + 1,
449*7d157501SCoco Li 				  TCPOPT_TIMESTAMP, ts);
450*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + 1 + TCPOLEN_TIMESTAMP,
451*7d157501SCoco Li 				  TCPOPT_NOP, 0);
452*7d157501SCoco Li 		break;
453*7d157501SCoco Li 	case 2:
454*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len, TCPOPT_TIMESTAMP, ts);
455*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 1,
456*7d157501SCoco Li 				  TCPOPT_NOP, 0);
457*7d157501SCoco Li 		tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 2,
458*7d157501SCoco Li 				  TCPOPT_NOP, 0);
459*7d157501SCoco Li 		break;
460*7d157501SCoco Li 	default:
461*7d157501SCoco Li 		error(1, 0, "unknown order");
462*7d157501SCoco Li 		break;
463*7d157501SCoco Li 	}
464*7d157501SCoco Li 	recompute_packet(buf, no_ext, TCPOLEN_TSTAMP_APPA);
465*7d157501SCoco Li }
466*7d157501SCoco Li 
467*7d157501SCoco Li /* Packets with invalid checksum don't coalesce. */
468*7d157501SCoco Li static void send_changed_checksum(int fd, struct sockaddr_ll *daddr)
469*7d157501SCoco Li {
470*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
471*7d157501SCoco Li 	struct tcphdr *tcph = (struct tcphdr *)(buf + tcp_offset);
472*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
473*7d157501SCoco Li 
474*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
475*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
476*7d157501SCoco Li 
477*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
478*7d157501SCoco Li 	tcph->check = tcph->check - 1;
479*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
480*7d157501SCoco Li }
481*7d157501SCoco Li 
482*7d157501SCoco Li  /* Packets with non-consecutive sequence number don't coalesce.*/
483*7d157501SCoco Li static void send_changed_seq(int fd, struct sockaddr_ll *daddr)
484*7d157501SCoco Li {
485*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
486*7d157501SCoco Li 	struct tcphdr *tcph = (struct tcphdr *)(buf + tcp_offset);
487*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
488*7d157501SCoco Li 
489*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
490*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
491*7d157501SCoco Li 
492*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
493*7d157501SCoco Li 	tcph->seq = ntohl(htonl(tcph->seq) + 1);
494*7d157501SCoco Li 	tcph->check = 0;
495*7d157501SCoco Li 	tcph->check = tcp_checksum(tcph, PAYLOAD_LEN);
496*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
497*7d157501SCoco Li }
498*7d157501SCoco Li 
499*7d157501SCoco Li  /* Packet with different timestamp option or different timestamps
500*7d157501SCoco Li   * don't coalesce.
501*7d157501SCoco Li   */
502*7d157501SCoco Li static void send_changed_ts(int fd, struct sockaddr_ll *daddr)
503*7d157501SCoco Li {
504*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
505*7d157501SCoco Li 	static char extpkt[sizeof(buf) + TCPOLEN_TSTAMP_APPA];
506*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_TSTAMP_APPA;
507*7d157501SCoco Li 
508*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
509*7d157501SCoco Li 	add_standard_tcp_options(extpkt, buf, 0, 0);
510*7d157501SCoco Li 	write_packet(fd, extpkt, pkt_size, daddr);
511*7d157501SCoco Li 
512*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
513*7d157501SCoco Li 	add_standard_tcp_options(extpkt, buf, 0, 0);
514*7d157501SCoco Li 	write_packet(fd, extpkt, pkt_size, daddr);
515*7d157501SCoco Li 
516*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);
517*7d157501SCoco Li 	add_standard_tcp_options(extpkt, buf, 100, 0);
518*7d157501SCoco Li 	write_packet(fd, extpkt, pkt_size, daddr);
519*7d157501SCoco Li 
520*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 3, 0, PAYLOAD_LEN, 0);
521*7d157501SCoco Li 	add_standard_tcp_options(extpkt, buf, 100, 1);
522*7d157501SCoco Li 	write_packet(fd, extpkt, pkt_size, daddr);
523*7d157501SCoco Li 
524*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 4, 0, PAYLOAD_LEN, 0);
525*7d157501SCoco Li 	add_standard_tcp_options(extpkt, buf, 100, 2);
526*7d157501SCoco Li 	write_packet(fd, extpkt, pkt_size, daddr);
527*7d157501SCoco Li }
528*7d157501SCoco Li 
529*7d157501SCoco Li /* Packet with different tcp options don't coalesce. */
530*7d157501SCoco Li static void send_diff_opt(int fd, struct sockaddr_ll *daddr)
531*7d157501SCoco Li {
532*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
533*7d157501SCoco Li 	static char extpkt1[sizeof(buf) + TCPOLEN_TSTAMP_APPA];
534*7d157501SCoco Li 	static char extpkt2[sizeof(buf) + TCPOLEN_MAXSEG];
535*7d157501SCoco Li 	int extpkt1_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_TSTAMP_APPA;
536*7d157501SCoco Li 	int extpkt2_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_MAXSEG;
537*7d157501SCoco Li 
538*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
539*7d157501SCoco Li 	add_standard_tcp_options(extpkt1, buf, 0, 0);
540*7d157501SCoco Li 	write_packet(fd, extpkt1, extpkt1_size, daddr);
541*7d157501SCoco Li 
542*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
543*7d157501SCoco Li 	add_standard_tcp_options(extpkt1, buf, 0, 0);
544*7d157501SCoco Li 	write_packet(fd, extpkt1, extpkt1_size, daddr);
545*7d157501SCoco Li 
546*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);
547*7d157501SCoco Li 	tcp_write_options(extpkt2 + MAX_HDR_LEN, TCPOPT_NOP, 0);
548*7d157501SCoco Li 	tcp_write_options(extpkt2 + MAX_HDR_LEN + 1, TCPOPT_WINDOW, 0);
549*7d157501SCoco Li 	recompute_packet(extpkt2, buf, TCPOLEN_WINDOW + 1);
550*7d157501SCoco Li 	write_packet(fd, extpkt2, extpkt2_size, daddr);
551*7d157501SCoco Li }
552*7d157501SCoco Li 
553*7d157501SCoco Li static void add_ipv4_ts_option(void *buf, void *optpkt)
554*7d157501SCoco Li {
555*7d157501SCoco Li 	struct ip_timestamp *ts = (struct ip_timestamp *)(optpkt + tcp_offset);
556*7d157501SCoco Li 	int optlen = sizeof(struct ip_timestamp);
557*7d157501SCoco Li 	struct iphdr *iph;
558*7d157501SCoco Li 
559*7d157501SCoco Li 	if (optlen % 4)
560*7d157501SCoco Li 		error(1, 0, "ipv4 timestamp length is not a multiple of 4B");
561*7d157501SCoco Li 
562*7d157501SCoco Li 	ts->ipt_code = IPOPT_TS;
563*7d157501SCoco Li 	ts->ipt_len = optlen;
564*7d157501SCoco Li 	ts->ipt_ptr = 5;
565*7d157501SCoco Li 	ts->ipt_flg = IPOPT_TS_TSONLY;
566*7d157501SCoco Li 
567*7d157501SCoco Li 	memcpy(optpkt, buf, tcp_offset);
568*7d157501SCoco Li 	memcpy(optpkt + tcp_offset + optlen, buf + tcp_offset,
569*7d157501SCoco Li 	       sizeof(struct tcphdr) + PAYLOAD_LEN);
570*7d157501SCoco Li 
571*7d157501SCoco Li 	iph = (struct iphdr *)(optpkt + ETH_HLEN);
572*7d157501SCoco Li 	iph->ihl = 5 + (optlen / 4);
573*7d157501SCoco Li 	iph->tot_len = htons(ntohs(iph->tot_len) + optlen);
574*7d157501SCoco Li 	iph->check = 0;
575*7d157501SCoco Li 	iph->check = checksum_fold(iph, sizeof(struct iphdr) + optlen, 0);
576*7d157501SCoco Li }
577*7d157501SCoco Li 
578*7d157501SCoco Li /* IPv4 options shouldn't coalesce */
579*7d157501SCoco Li static void send_ip_options(int fd, struct sockaddr_ll *daddr)
580*7d157501SCoco Li {
581*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
582*7d157501SCoco Li 	static char optpkt[sizeof(buf) + sizeof(struct ip_timestamp)];
583*7d157501SCoco Li 	int optlen = sizeof(struct ip_timestamp);
584*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN + optlen;
585*7d157501SCoco Li 
586*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
587*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);
588*7d157501SCoco Li 
589*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 1, 0, PAYLOAD_LEN, 0);
590*7d157501SCoco Li 	add_ipv4_ts_option(buf, optpkt);
591*7d157501SCoco Li 	write_packet(fd, optpkt, pkt_size, daddr);
592*7d157501SCoco Li 
593*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);
594*7d157501SCoco Li 	write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);
595*7d157501SCoco Li }
596*7d157501SCoco Li 
597*7d157501SCoco Li /*  IPv4 fragments shouldn't coalesce */
598*7d157501SCoco Li static void send_fragment4(int fd, struct sockaddr_ll *daddr)
599*7d157501SCoco Li {
600*7d157501SCoco Li 	static char buf[IP_MAXPACKET];
601*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);
602*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
603*7d157501SCoco Li 
604*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
605*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
606*7d157501SCoco Li 
607*7d157501SCoco Li 	/* Once fragmented, packet would retain the total_len.
608*7d157501SCoco Li 	 * Tcp header is prepared as if rest of data is in follow-up frags,
609*7d157501SCoco Li 	 * but follow up frags aren't actually sent.
610*7d157501SCoco Li 	 */
611*7d157501SCoco Li 	memset(buf + total_hdr_len, 'a', PAYLOAD_LEN * 2);
612*7d157501SCoco Li 	fill_transportlayer(buf + tcp_offset, PAYLOAD_LEN, 0, PAYLOAD_LEN * 2, 0);
613*7d157501SCoco Li 	fill_networklayer(buf + ETH_HLEN, PAYLOAD_LEN);
614*7d157501SCoco Li 	fill_datalinklayer(buf);
615*7d157501SCoco Li 
616*7d157501SCoco Li 	iph->frag_off = htons(0x6000); // DF = 1, MF = 1
617*7d157501SCoco Li 	iph->check = 0;
618*7d157501SCoco Li 	iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);
619*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
620*7d157501SCoco Li }
621*7d157501SCoco Li 
622*7d157501SCoco Li /* IPv4 packets with different ttl don't coalesce.*/
623*7d157501SCoco Li static void send_changed_ttl(int fd, struct sockaddr_ll *daddr)
624*7d157501SCoco Li {
625*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
626*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
627*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);
628*7d157501SCoco Li 
629*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
630*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
631*7d157501SCoco Li 
632*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
633*7d157501SCoco Li 	iph->ttl = 7;
634*7d157501SCoco Li 	iph->check = 0;
635*7d157501SCoco Li 	iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);
636*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
637*7d157501SCoco Li }
638*7d157501SCoco Li 
639*7d157501SCoco Li /* Packets with different tos don't coalesce.*/
640*7d157501SCoco Li static void send_changed_tos(int fd, struct sockaddr_ll *daddr)
641*7d157501SCoco Li {
642*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
643*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
644*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);
645*7d157501SCoco Li 	struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);
646*7d157501SCoco Li 
647*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
648*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
649*7d157501SCoco Li 
650*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
651*7d157501SCoco Li 	if (proto == PF_INET) {
652*7d157501SCoco Li 		iph->tos = 1;
653*7d157501SCoco Li 		iph->check = 0;
654*7d157501SCoco Li 		iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);
655*7d157501SCoco Li 	} else if (proto == PF_INET6) {
656*7d157501SCoco Li 		ip6h->priority = 0xf;
657*7d157501SCoco Li 	}
658*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
659*7d157501SCoco Li }
660*7d157501SCoco Li 
661*7d157501SCoco Li /* Packets with different ECN don't coalesce.*/
662*7d157501SCoco Li static void send_changed_ECN(int fd, struct sockaddr_ll *daddr)
663*7d157501SCoco Li {
664*7d157501SCoco Li 	int pkt_size = total_hdr_len + PAYLOAD_LEN;
665*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
666*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);
667*7d157501SCoco Li 
668*7d157501SCoco Li 	create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
669*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
670*7d157501SCoco Li 
671*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
672*7d157501SCoco Li 	if (proto == PF_INET) {
673*7d157501SCoco Li 		buf[ETH_HLEN + 1] ^= 0x2; // ECN set to 10
674*7d157501SCoco Li 		iph->check = 0;
675*7d157501SCoco Li 		iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);
676*7d157501SCoco Li 	} else {
677*7d157501SCoco Li 		buf[ETH_HLEN + 1] ^= 0x20; // ECN set to 10
678*7d157501SCoco Li 	}
679*7d157501SCoco Li 	write_packet(fd, buf, pkt_size, daddr);
680*7d157501SCoco Li }
681*7d157501SCoco Li 
682*7d157501SCoco Li /* IPv6 fragments and packets with extensions don't coalesce.*/
683*7d157501SCoco Li static void send_fragment6(int fd, struct sockaddr_ll *daddr)
684*7d157501SCoco Li {
685*7d157501SCoco Li 	static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
686*7d157501SCoco Li 	static char extpkt[MAX_HDR_LEN + PAYLOAD_LEN +
687*7d157501SCoco Li 			   sizeof(struct ip6_frag)];
688*7d157501SCoco Li 	struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);
689*7d157501SCoco Li 	struct ip6_frag *frag = (void *)(extpkt + tcp_offset);
690*7d157501SCoco Li 	int extlen = sizeof(struct ip6_frag);
691*7d157501SCoco Li 	int bufpkt_len = total_hdr_len + PAYLOAD_LEN;
692*7d157501SCoco Li 	int extpkt_len = bufpkt_len + extlen;
693*7d157501SCoco Li 	int i;
694*7d157501SCoco Li 
695*7d157501SCoco Li 	for (i = 0; i < 2; i++) {
696*7d157501SCoco Li 		create_packet(buf, PAYLOAD_LEN * i, 0, PAYLOAD_LEN, 0);
697*7d157501SCoco Li 		write_packet(fd, buf, bufpkt_len, daddr);
698*7d157501SCoco Li 	}
699*7d157501SCoco Li 
700*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);
701*7d157501SCoco Li 	memset(extpkt, 0, extpkt_len);
702*7d157501SCoco Li 
703*7d157501SCoco Li 	ip6h->nexthdr = IPPROTO_FRAGMENT;
704*7d157501SCoco Li 	ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);
705*7d157501SCoco Li 	frag->ip6f_nxt = IPPROTO_TCP;
706*7d157501SCoco Li 
707*7d157501SCoco Li 	memcpy(extpkt, buf, tcp_offset);
708*7d157501SCoco Li 	memcpy(extpkt + tcp_offset + extlen, buf + tcp_offset,
709*7d157501SCoco Li 	       sizeof(struct tcphdr) + PAYLOAD_LEN);
710*7d157501SCoco Li 	write_packet(fd, extpkt, extpkt_len, daddr);
711*7d157501SCoco Li 
712*7d157501SCoco Li 	create_packet(buf, PAYLOAD_LEN * 3, 0, PAYLOAD_LEN, 0);
713*7d157501SCoco Li 	write_packet(fd, buf, bufpkt_len, daddr);
714*7d157501SCoco Li }
715*7d157501SCoco Li 
716*7d157501SCoco Li static void bind_packetsocket(int fd)
717*7d157501SCoco Li {
718*7d157501SCoco Li 	struct sockaddr_ll daddr = {};
719*7d157501SCoco Li 
720*7d157501SCoco Li 	daddr.sll_family = AF_PACKET;
721*7d157501SCoco Li 	daddr.sll_protocol = ethhdr_proto;
722*7d157501SCoco Li 	daddr.sll_ifindex = if_nametoindex(ifname);
723*7d157501SCoco Li 	if (daddr.sll_ifindex == 0)
724*7d157501SCoco Li 		error(1, errno, "if_nametoindex");
725*7d157501SCoco Li 
726*7d157501SCoco Li 	if (bind(fd, (void *)&daddr, sizeof(daddr)) < 0)
727*7d157501SCoco Li 		error(1, errno, "could not bind socket");
728*7d157501SCoco Li }
729*7d157501SCoco Li 
730*7d157501SCoco Li static void set_timeout(int fd)
731*7d157501SCoco Li {
732*7d157501SCoco Li 	struct timeval timeout;
733*7d157501SCoco Li 
734*7d157501SCoco Li 	timeout.tv_sec = 120;
735*7d157501SCoco Li 	timeout.tv_usec = 0;
736*7d157501SCoco Li 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
737*7d157501SCoco Li 		       sizeof(timeout)) < 0)
738*7d157501SCoco Li 		error(1, errno, "cannot set timeout, setsockopt failed");
739*7d157501SCoco Li }
740*7d157501SCoco Li 
741*7d157501SCoco Li static void check_recv_pkts(int fd, int *correct_payload,
742*7d157501SCoco Li 			    int correct_num_pkts)
743*7d157501SCoco Li {
744*7d157501SCoco Li 	static char buffer[IP_MAXPACKET + ETH_HLEN + 1];
745*7d157501SCoco Li 	struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN);
746*7d157501SCoco Li 	struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN);
747*7d157501SCoco Li 	struct tcphdr *tcph;
748*7d157501SCoco Li 	bool bad_packet = false;
749*7d157501SCoco Li 	int tcp_ext_len = 0;
750*7d157501SCoco Li 	int ip_ext_len = 0;
751*7d157501SCoco Li 	int pkt_size = -1;
752*7d157501SCoco Li 	int data_len = 0;
753*7d157501SCoco Li 	int num_pkt = 0;
754*7d157501SCoco Li 	int i;
755*7d157501SCoco Li 
756*7d157501SCoco Li 	vlog("Expected {");
757*7d157501SCoco Li 	for (i = 0; i < correct_num_pkts; i++)
758*7d157501SCoco Li 		vlog("%d ", correct_payload[i]);
759*7d157501SCoco Li 	vlog("}, Total %d packets\nReceived {", correct_num_pkts);
760*7d157501SCoco Li 
761*7d157501SCoco Li 	while (1) {
762*7d157501SCoco Li 		pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);
763*7d157501SCoco Li 		if (pkt_size < 0)
764*7d157501SCoco Li 			error(1, errno, "could not receive");
765*7d157501SCoco Li 
766*7d157501SCoco Li 		if (iph->version == 4)
767*7d157501SCoco Li 			ip_ext_len = (iph->ihl - 5) * 4;
768*7d157501SCoco Li 		else if (ip6h->version == 6 && ip6h->nexthdr != IPPROTO_TCP)
769*7d157501SCoco Li 			ip_ext_len = sizeof(struct ip6_frag);
770*7d157501SCoco Li 
771*7d157501SCoco Li 		tcph = (struct tcphdr *)(buffer + tcp_offset + ip_ext_len);
772*7d157501SCoco Li 
773*7d157501SCoco Li 		if (tcph->fin)
774*7d157501SCoco Li 			break;
775*7d157501SCoco Li 
776*7d157501SCoco Li 		tcp_ext_len = (tcph->doff - 5) * 4;
777*7d157501SCoco Li 		data_len = pkt_size - total_hdr_len - tcp_ext_len - ip_ext_len;
778*7d157501SCoco Li 		/* Min ethernet frame payload is 46(ETH_ZLEN - ETH_HLEN) by RFC 802.3.
779*7d157501SCoco Li 		 * Ipv4/tcp packets without at least 6 bytes of data will be padded.
780*7d157501SCoco Li 		 * Packet sockets are protocol agnostic, and will not trim the padding.
781*7d157501SCoco Li 		 */
782*7d157501SCoco Li 		if (pkt_size == ETH_ZLEN && iph->version == 4) {
783*7d157501SCoco Li 			data_len = ntohs(iph->tot_len)
784*7d157501SCoco Li 				- sizeof(struct tcphdr) - sizeof(struct iphdr);
785*7d157501SCoco Li 		}
786*7d157501SCoco Li 		vlog("%d ", data_len);
787*7d157501SCoco Li 		if (data_len != correct_payload[num_pkt]) {
788*7d157501SCoco Li 			vlog("[!=%d]", correct_payload[num_pkt]);
789*7d157501SCoco Li 			bad_packet = true;
790*7d157501SCoco Li 		}
791*7d157501SCoco Li 		num_pkt++;
792*7d157501SCoco Li 	}
793*7d157501SCoco Li 	vlog("}, Total %d packets.\n", num_pkt);
794*7d157501SCoco Li 	if (num_pkt != correct_num_pkts)
795*7d157501SCoco Li 		error(1, 0, "incorrect number of packets");
796*7d157501SCoco Li 	if (bad_packet)
797*7d157501SCoco Li 		error(1, 0, "incorrect packet geometry");
798*7d157501SCoco Li 
799*7d157501SCoco Li 	printf("Test succeeded\n\n");
800*7d157501SCoco Li }
801*7d157501SCoco Li 
802*7d157501SCoco Li static void gro_sender(void)
803*7d157501SCoco Li {
804*7d157501SCoco Li 	static char fin_pkt[MAX_HDR_LEN];
805*7d157501SCoco Li 	struct sockaddr_ll daddr = {};
806*7d157501SCoco Li 	int txfd = -1;
807*7d157501SCoco Li 
808*7d157501SCoco Li 	txfd = socket(PF_PACKET, SOCK_RAW, IPPROTO_RAW);
809*7d157501SCoco Li 	if (txfd < 0)
810*7d157501SCoco Li 		error(1, errno, "socket creation");
811*7d157501SCoco Li 
812*7d157501SCoco Li 	memset(&daddr, 0, sizeof(daddr));
813*7d157501SCoco Li 	daddr.sll_ifindex = if_nametoindex(ifname);
814*7d157501SCoco Li 	if (daddr.sll_ifindex == 0)
815*7d157501SCoco Li 		error(1, errno, "if_nametoindex");
816*7d157501SCoco Li 	daddr.sll_family = AF_PACKET;
817*7d157501SCoco Li 	memcpy(daddr.sll_addr, dst_mac, ETH_ALEN);
818*7d157501SCoco Li 	daddr.sll_halen = ETH_ALEN;
819*7d157501SCoco Li 	create_packet(fin_pkt, PAYLOAD_LEN * 2, 0, 0, 1);
820*7d157501SCoco Li 
821*7d157501SCoco Li 	if (strcmp(testname, "data") == 0) {
822*7d157501SCoco Li 		send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN);
823*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
824*7d157501SCoco Li 
825*7d157501SCoco Li 		send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2);
826*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
827*7d157501SCoco Li 
828*7d157501SCoco Li 		send_data_pkts(txfd, &daddr, PAYLOAD_LEN / 2, PAYLOAD_LEN);
829*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
830*7d157501SCoco Li 	} else if (strcmp(testname, "ack") == 0) {
831*7d157501SCoco Li 		send_ack(txfd, &daddr);
832*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
833*7d157501SCoco Li 	} else if (strcmp(testname, "flags") == 0) {
834*7d157501SCoco Li 		send_flags(txfd, &daddr, 1, 0, 0, 0);
835*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
836*7d157501SCoco Li 
837*7d157501SCoco Li 		send_flags(txfd, &daddr, 0, 1, 0, 0);
838*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
839*7d157501SCoco Li 
840*7d157501SCoco Li 		send_flags(txfd, &daddr, 0, 0, 1, 0);
841*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
842*7d157501SCoco Li 
843*7d157501SCoco Li 		send_flags(txfd, &daddr, 0, 0, 0, 1);
844*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
845*7d157501SCoco Li 	} else if (strcmp(testname, "tcp") == 0) {
846*7d157501SCoco Li 		send_changed_checksum(txfd, &daddr);
847*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
848*7d157501SCoco Li 
849*7d157501SCoco Li 		send_changed_seq(txfd, &daddr);
850*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
851*7d157501SCoco Li 
852*7d157501SCoco Li 		send_changed_ts(txfd, &daddr);
853*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
854*7d157501SCoco Li 
855*7d157501SCoco Li 		send_diff_opt(txfd, &daddr);
856*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
857*7d157501SCoco Li 	} else if (strcmp(testname, "ip") == 0) {
858*7d157501SCoco Li 		send_changed_ECN(txfd, &daddr);
859*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
860*7d157501SCoco Li 
861*7d157501SCoco Li 		send_changed_tos(txfd, &daddr);
862*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
863*7d157501SCoco Li 		if (proto == PF_INET) {
864*7d157501SCoco Li 			/* Modified packets may be received out of order.
865*7d157501SCoco Li 			 * Sleep function added to enforce test boundaries
866*7d157501SCoco Li 			 * so that fin pkts are not received prior to other pkts.
867*7d157501SCoco Li 			 */
868*7d157501SCoco Li 			sleep(1);
869*7d157501SCoco Li 			send_changed_ttl(txfd, &daddr);
870*7d157501SCoco Li 			write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
871*7d157501SCoco Li 
872*7d157501SCoco Li 			sleep(1);
873*7d157501SCoco Li 			send_ip_options(txfd, &daddr);
874*7d157501SCoco Li 			sleep(1);
875*7d157501SCoco Li 			write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
876*7d157501SCoco Li 
877*7d157501SCoco Li 			sleep(1);
878*7d157501SCoco Li 			send_fragment4(txfd, &daddr);
879*7d157501SCoco Li 			sleep(1);
880*7d157501SCoco Li 			write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
881*7d157501SCoco Li 		} else if (proto == PF_INET6) {
882*7d157501SCoco Li 			send_fragment6(txfd, &daddr);
883*7d157501SCoco Li 			write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
884*7d157501SCoco Li 		}
885*7d157501SCoco Li 	} else if (strcmp(testname, "large") == 0) {
886*7d157501SCoco Li 		/* 20 is the difference between min iphdr size
887*7d157501SCoco Li 		 * and min ipv6hdr size. Like MAX_HDR_SIZE,
888*7d157501SCoco Li 		 * MAX_PAYLOAD is defined with the larger header of the two.
889*7d157501SCoco Li 		 */
890*7d157501SCoco Li 		int offset = proto == PF_INET ? 20 : 0;
891*7d157501SCoco Li 		int remainder = (MAX_PAYLOAD + offset) % MSS;
892*7d157501SCoco Li 
893*7d157501SCoco Li 		send_large(txfd, &daddr, remainder);
894*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
895*7d157501SCoco Li 
896*7d157501SCoco Li 		send_large(txfd, &daddr, remainder + 1);
897*7d157501SCoco Li 		write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
898*7d157501SCoco Li 	} else {
899*7d157501SCoco Li 		error(1, 0, "Unknown testcase");
900*7d157501SCoco Li 	}
901*7d157501SCoco Li 
902*7d157501SCoco Li 	if (close(txfd))
903*7d157501SCoco Li 		error(1, errno, "socket close");
904*7d157501SCoco Li }
905*7d157501SCoco Li 
906*7d157501SCoco Li static void gro_receiver(void)
907*7d157501SCoco Li {
908*7d157501SCoco Li 	static int correct_payload[NUM_PACKETS];
909*7d157501SCoco Li 	int rxfd = -1;
910*7d157501SCoco Li 
911*7d157501SCoco Li 	rxfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_NONE));
912*7d157501SCoco Li 	if (rxfd < 0)
913*7d157501SCoco Li 		error(1, 0, "socket creation");
914*7d157501SCoco Li 	setup_sock_filter(rxfd);
915*7d157501SCoco Li 	set_timeout(rxfd);
916*7d157501SCoco Li 	bind_packetsocket(rxfd);
917*7d157501SCoco Li 
918*7d157501SCoco Li 	memset(correct_payload, 0, sizeof(correct_payload));
919*7d157501SCoco Li 
920*7d157501SCoco Li 	if (strcmp(testname, "data") == 0) {
921*7d157501SCoco Li 		printf("pure data packet of same size: ");
922*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 2;
923*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 1);
924*7d157501SCoco Li 
925*7d157501SCoco Li 		printf("large data packets followed by a smaller one: ");
926*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 1.5;
927*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 1);
928*7d157501SCoco Li 
929*7d157501SCoco Li 		printf("small data packets followed by a larger one: ");
930*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN / 2;
931*7d157501SCoco Li 		correct_payload[1] = PAYLOAD_LEN;
932*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
933*7d157501SCoco Li 	} else if (strcmp(testname, "ack") == 0) {
934*7d157501SCoco Li 		printf("duplicate ack and pure ack: ");
935*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 3);
936*7d157501SCoco Li 	} else if (strcmp(testname, "flags") == 0) {
937*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 3;
938*7d157501SCoco Li 		correct_payload[1] = PAYLOAD_LEN * 2;
939*7d157501SCoco Li 
940*7d157501SCoco Li 		printf("psh flag ends coalescing: ");
941*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
942*7d157501SCoco Li 
943*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 2;
944*7d157501SCoco Li 		correct_payload[1] = 0;
945*7d157501SCoco Li 		correct_payload[2] = PAYLOAD_LEN * 2;
946*7d157501SCoco Li 		printf("syn flag ends coalescing: ");
947*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 3);
948*7d157501SCoco Li 
949*7d157501SCoco Li 		printf("rst flag ends coalescing: ");
950*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 3);
951*7d157501SCoco Li 
952*7d157501SCoco Li 		printf("urg flag ends coalescing: ");
953*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 3);
954*7d157501SCoco Li 	} else if (strcmp(testname, "tcp") == 0) {
955*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN;
956*7d157501SCoco Li 		correct_payload[1] = PAYLOAD_LEN;
957*7d157501SCoco Li 		correct_payload[2] = PAYLOAD_LEN;
958*7d157501SCoco Li 		correct_payload[3] = PAYLOAD_LEN;
959*7d157501SCoco Li 
960*7d157501SCoco Li 		printf("changed checksum does not coalesce: ");
961*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
962*7d157501SCoco Li 
963*7d157501SCoco Li 		printf("Wrong Seq number doesn't coalesce: ");
964*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
965*7d157501SCoco Li 
966*7d157501SCoco Li 		printf("Different timestamp doesn't coalesce: ");
967*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 2;
968*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 4);
969*7d157501SCoco Li 
970*7d157501SCoco Li 		printf("Different options doesn't coalesce: ");
971*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN * 2;
972*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
973*7d157501SCoco Li 	} else if (strcmp(testname, "ip") == 0) {
974*7d157501SCoco Li 		correct_payload[0] = PAYLOAD_LEN;
975*7d157501SCoco Li 		correct_payload[1] = PAYLOAD_LEN;
976*7d157501SCoco Li 
977*7d157501SCoco Li 		printf("different ECN doesn't coalesce: ");
978*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
979*7d157501SCoco Li 
980*7d157501SCoco Li 		printf("different tos doesn't coalesce: ");
981*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
982*7d157501SCoco Li 
983*7d157501SCoco Li 		if (proto == PF_INET) {
984*7d157501SCoco Li 			printf("different ttl doesn't coalesce: ");
985*7d157501SCoco Li 			check_recv_pkts(rxfd, correct_payload, 2);
986*7d157501SCoco Li 
987*7d157501SCoco Li 			printf("ip options doesn't coalesce: ");
988*7d157501SCoco Li 			correct_payload[2] = PAYLOAD_LEN;
989*7d157501SCoco Li 			check_recv_pkts(rxfd, correct_payload, 3);
990*7d157501SCoco Li 
991*7d157501SCoco Li 			printf("fragmented ip4 doesn't coalesce: ");
992*7d157501SCoco Li 			check_recv_pkts(rxfd, correct_payload, 2);
993*7d157501SCoco Li 		} else if (proto == PF_INET6) {
994*7d157501SCoco Li 			/* GRO doesn't check for ipv6 hop limit when flushing.
995*7d157501SCoco Li 			 * Hence no corresponding test to the ipv4 case.
996*7d157501SCoco Li 			 */
997*7d157501SCoco Li 			printf("fragmented ip6 doesn't coalesce: ");
998*7d157501SCoco Li 			correct_payload[0] = PAYLOAD_LEN * 2;
999*7d157501SCoco Li 			check_recv_pkts(rxfd, correct_payload, 2);
1000*7d157501SCoco Li 		}
1001*7d157501SCoco Li 	} else if (strcmp(testname, "large") == 0) {
1002*7d157501SCoco Li 		int offset = proto == PF_INET ? 20 : 0;
1003*7d157501SCoco Li 		int remainder = (MAX_PAYLOAD + offset) % MSS;
1004*7d157501SCoco Li 
1005*7d157501SCoco Li 		correct_payload[0] = (MAX_PAYLOAD + offset);
1006*7d157501SCoco Li 		correct_payload[1] = remainder;
1007*7d157501SCoco Li 		printf("Shouldn't coalesce if exceed IP max pkt size: ");
1008*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 2);
1009*7d157501SCoco Li 
1010*7d157501SCoco Li 		/* last segment sent individually, doesn't start new segment */
1011*7d157501SCoco Li 		correct_payload[0] = correct_payload[0] - remainder;
1012*7d157501SCoco Li 		correct_payload[1] = remainder + 1;
1013*7d157501SCoco Li 		correct_payload[2] = remainder + 1;
1014*7d157501SCoco Li 		check_recv_pkts(rxfd, correct_payload, 3);
1015*7d157501SCoco Li 	} else {
1016*7d157501SCoco Li 		error(1, 0, "Test case error, should never trigger");
1017*7d157501SCoco Li 	}
1018*7d157501SCoco Li 
1019*7d157501SCoco Li 	if (close(rxfd))
1020*7d157501SCoco Li 		error(1, 0, "socket close");
1021*7d157501SCoco Li }
1022*7d157501SCoco Li 
1023*7d157501SCoco Li static void parse_args(int argc, char **argv)
1024*7d157501SCoco Li {
1025*7d157501SCoco Li 	static const struct option opts[] = {
1026*7d157501SCoco Li 		{ "dmac", required_argument, NULL, 'D' },
1027*7d157501SCoco Li 		{ "iface", required_argument, NULL, 'i' },
1028*7d157501SCoco Li 		{ "ipv4", no_argument, NULL, '4' },
1029*7d157501SCoco Li 		{ "ipv6", no_argument, NULL, '6' },
1030*7d157501SCoco Li 		{ "rx", no_argument, NULL, 'r' },
1031*7d157501SCoco Li 		{ "smac", required_argument, NULL, 'S' },
1032*7d157501SCoco Li 		{ "test", required_argument, NULL, 't' },
1033*7d157501SCoco Li 		{ "verbose", no_argument, NULL, 'v' },
1034*7d157501SCoco Li 		{ 0, 0, 0, 0 }
1035*7d157501SCoco Li 	};
1036*7d157501SCoco Li 	int c;
1037*7d157501SCoco Li 
1038*7d157501SCoco Li 	while ((c = getopt_long(argc, argv, "46D:i:rS:t:v", opts, NULL)) != -1) {
1039*7d157501SCoco Li 		switch (c) {
1040*7d157501SCoco Li 		case '4':
1041*7d157501SCoco Li 			proto = PF_INET;
1042*7d157501SCoco Li 			ethhdr_proto = htons(ETH_P_IP);
1043*7d157501SCoco Li 			break;
1044*7d157501SCoco Li 		case '6':
1045*7d157501SCoco Li 			proto = PF_INET6;
1046*7d157501SCoco Li 			ethhdr_proto = htons(ETH_P_IPV6);
1047*7d157501SCoco Li 			break;
1048*7d157501SCoco Li 		case 'D':
1049*7d157501SCoco Li 			dmac = optarg;
1050*7d157501SCoco Li 			break;
1051*7d157501SCoco Li 		case 'i':
1052*7d157501SCoco Li 			ifname = optarg;
1053*7d157501SCoco Li 			break;
1054*7d157501SCoco Li 		case 'r':
1055*7d157501SCoco Li 			tx_socket = false;
1056*7d157501SCoco Li 			break;
1057*7d157501SCoco Li 		case 'S':
1058*7d157501SCoco Li 			smac = optarg;
1059*7d157501SCoco Li 			break;
1060*7d157501SCoco Li 		case 't':
1061*7d157501SCoco Li 			testname = optarg;
1062*7d157501SCoco Li 			break;
1063*7d157501SCoco Li 		case 'v':
1064*7d157501SCoco Li 			verbose = true;
1065*7d157501SCoco Li 			break;
1066*7d157501SCoco Li 		default:
1067*7d157501SCoco Li 			error(1, 0, "%s invalid option %c\n", __func__, c);
1068*7d157501SCoco Li 			break;
1069*7d157501SCoco Li 		}
1070*7d157501SCoco Li 	}
1071*7d157501SCoco Li }
1072*7d157501SCoco Li 
1073*7d157501SCoco Li int main(int argc, char **argv)
1074*7d157501SCoco Li {
1075*7d157501SCoco Li 	parse_args(argc, argv);
1076*7d157501SCoco Li 
1077*7d157501SCoco Li 	if (proto == PF_INET) {
1078*7d157501SCoco Li 		tcp_offset = ETH_HLEN + sizeof(struct iphdr);
1079*7d157501SCoco Li 		total_hdr_len = tcp_offset + sizeof(struct tcphdr);
1080*7d157501SCoco Li 	} else if (proto == PF_INET6) {
1081*7d157501SCoco Li 		tcp_offset = ETH_HLEN + sizeof(struct ipv6hdr);
1082*7d157501SCoco Li 		total_hdr_len = MAX_HDR_LEN;
1083*7d157501SCoco Li 	} else {
1084*7d157501SCoco Li 		error(1, 0, "Protocol family is not ipv4 or ipv6");
1085*7d157501SCoco Li 	}
1086*7d157501SCoco Li 
1087*7d157501SCoco Li 	read_MAC(src_mac, smac);
1088*7d157501SCoco Li 	read_MAC(dst_mac, dmac);
1089*7d157501SCoco Li 
1090*7d157501SCoco Li 	if (tx_socket)
1091*7d157501SCoco Li 		gro_sender();
1092*7d157501SCoco Li 	else
1093*7d157501SCoco Li 		gro_receiver();
1094*7d157501SCoco Li 	return 0;
1095*7d157501SCoco Li }
1096