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

source code of linux/tools/testing/selftests/net/gro.c