1 // SPDX-License-Identifier: GPL-2.0
3 * This testsuite provides conformance testing for GRO coalescing.
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.
12 * Pure ACK does not coalesce.
14 * Specific test cases: no packets with PSH, SYN, URG, RST set will
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.
22 * Packets with different (ECN, TTL, TOS) header, ip options or
23 * ip fragments (ipv6) shouldn't coalesce.
25 * Packets larger than GRO_MAX_SIZE packets shouldn't coalesce.
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.
33 * Note the timing issue applies to all of the test cases, so some
34 * flakiness is to be expected.
40 #include <arpa/inet.h>
44 #include <linux/filter.h>
45 #include <linux/if_packet.h>
46 #include <linux/ipv6.h>
47 #include <net/ethernet.h>
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <netinet/ip6.h>
52 #include <netinet/tcp.h>
60 #include "../kselftest.h"
64 #define PAYLOAD_LEN 100
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"
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)]))
81 static const char *addr6_src
= "fdaa::2";
82 static const char *addr6_dst
= "fdaa::1";
83 static const char *addr4_src
= "192.168.1.200";
84 static const char *addr4_dst
= "192.168.1.100";
85 static int proto
= -1;
86 static uint8_t src_mac
[ETH_ALEN
], dst_mac
[ETH_ALEN
];
87 static char *testname
= "data";
88 static char *ifname
= "eth0";
89 static char *smac
= "aa:00:00:00:00:02";
90 static char *dmac
= "aa:00:00:00:00:01";
92 static bool tx_socket
= true;
93 static int tcp_offset
= -1;
94 static int total_hdr_len
= -1;
95 static int ethhdr_proto
= -1;
96 static const int num_flush_id_cases
= 6;
98 static void vlog(const char *fmt
, ...)
104 vfprintf(stderr
, fmt
, args
);
109 static void setup_sock_filter(int fd
)
111 const int dport_off
= tcp_offset
+ offsetof(struct tcphdr
, dest
);
112 const int ethproto_off
= offsetof(struct ethhdr
, h_proto
);
114 int ipproto_off
, opt_ipproto_off
;
117 if (proto
== PF_INET
)
118 next_off
= offsetof(struct iphdr
, protocol
);
120 next_off
= offsetof(struct ipv6hdr
, nexthdr
);
121 ipproto_off
= ETH_HLEN
+ next_off
;
123 /* Overridden later if exthdrs are used: */
124 opt_ipproto_off
= ipproto_off
;
126 if (strcmp(testname
, "ip") == 0) {
127 if (proto
== PF_INET
)
128 optlen
= sizeof(struct ip_timestamp
);
130 BUILD_BUG_ON(sizeof(struct ip6_hbh
) > MIN_EXTHDR_SIZE
);
131 BUILD_BUG_ON(sizeof(struct ip6_dest
) > MIN_EXTHDR_SIZE
);
132 BUILD_BUG_ON(sizeof(struct ip6_frag
) > MIN_EXTHDR_SIZE
);
134 /* same size for HBH and Fragment extension header types */
135 optlen
= MIN_EXTHDR_SIZE
;
136 opt_ipproto_off
= ETH_HLEN
+ sizeof(struct ipv6hdr
)
137 + offsetof(struct ip6_ext
, ip6e_nxt
);
141 /* this filter validates the following:
142 * - packet is IPv4/IPv6 according to the running test.
143 * - packet is TCP. Also handles the case of one extension header and then TCP.
144 * - checks the packet tcp dport equals to DPORT. Also handles the case of one
145 * extension header and then TCP.
147 struct sock_filter filter
[] = {
148 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, ethproto_off
),
149 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ntohs(ethhdr_proto
), 0, 9),
150 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, ipproto_off
),
151 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, IPPROTO_TCP
, 2, 0),
152 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, opt_ipproto_off
),
153 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, IPPROTO_TCP
, 0, 5),
154 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, dport_off
),
155 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, DPORT
, 2, 0),
156 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, dport_off
+ optlen
),
157 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, DPORT
, 0, 1),
158 BPF_STMT(BPF_RET
+ BPF_K
, 0xFFFFFFFF),
159 BPF_STMT(BPF_RET
+ BPF_K
, 0),
162 struct sock_fprog bpf
= {
163 .len
= ARRAY_SIZE(filter
),
167 if (setsockopt(fd
, SOL_SOCKET
, SO_ATTACH_FILTER
, &bpf
, sizeof(bpf
)) < 0)
168 error(1, errno
, "error setting filter");
171 static uint32_t checksum_nofold(void *data
, size_t len
, uint32_t sum
)
173 uint16_t *words
= data
;
176 for (i
= 0; i
< len
/ 2; i
++)
179 sum
+= ((char *)data
)[len
- 1];
183 static uint16_t checksum_fold(void *data
, size_t len
, uint32_t sum
)
185 sum
= checksum_nofold(data
, len
, sum
);
187 sum
= (sum
& 0xFFFF) + (sum
>> 16);
191 static uint16_t tcp_checksum(void *buf
, int payload_len
)
193 struct pseudo_header6
{
194 struct in6_addr saddr
;
195 struct in6_addr daddr
;
197 uint16_t payload_len
;
199 struct pseudo_header4
{
200 struct in_addr saddr
;
201 struct in_addr daddr
;
203 uint16_t payload_len
;
207 if (proto
== PF_INET6
) {
208 if (inet_pton(AF_INET6
, addr6_src
, &ph6
.saddr
) != 1)
209 error(1, errno
, "inet_pton6 source ip pseudo");
210 if (inet_pton(AF_INET6
, addr6_dst
, &ph6
.daddr
) != 1)
211 error(1, errno
, "inet_pton6 dest ip pseudo");
212 ph6
.protocol
= htons(IPPROTO_TCP
);
213 ph6
.payload_len
= htons(sizeof(struct tcphdr
) + payload_len
);
215 sum
= checksum_nofold(&ph6
, sizeof(ph6
), 0);
216 } else if (proto
== PF_INET
) {
217 if (inet_pton(AF_INET
, addr4_src
, &ph4
.saddr
) != 1)
218 error(1, errno
, "inet_pton source ip pseudo");
219 if (inet_pton(AF_INET
, addr4_dst
, &ph4
.daddr
) != 1)
220 error(1, errno
, "inet_pton dest ip pseudo");
221 ph4
.protocol
= htons(IPPROTO_TCP
);
222 ph4
.payload_len
= htons(sizeof(struct tcphdr
) + payload_len
);
224 sum
= checksum_nofold(&ph4
, sizeof(ph4
), 0);
227 return checksum_fold(buf
, sizeof(struct tcphdr
) + payload_len
, sum
);
230 static void read_MAC(uint8_t *mac_addr
, char *mac
)
232 if (sscanf(mac
, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
233 &mac_addr
[0], &mac_addr
[1], &mac_addr
[2],
234 &mac_addr
[3], &mac_addr
[4], &mac_addr
[5]) != 6)
235 error(1, 0, "sscanf");
238 static void fill_datalinklayer(void *buf
)
240 struct ethhdr
*eth
= buf
;
242 memcpy(eth
->h_dest
, dst_mac
, ETH_ALEN
);
243 memcpy(eth
->h_source
, src_mac
, ETH_ALEN
);
244 eth
->h_proto
= ethhdr_proto
;
247 static void fill_networklayer(void *buf
, int payload_len
)
249 struct ipv6hdr
*ip6h
= buf
;
250 struct iphdr
*iph
= buf
;
252 if (proto
== PF_INET6
) {
253 memset(ip6h
, 0, sizeof(*ip6h
));
256 ip6h
->payload_len
= htons(sizeof(struct tcphdr
) + payload_len
);
257 ip6h
->nexthdr
= IPPROTO_TCP
;
259 if (inet_pton(AF_INET6
, addr6_src
, &ip6h
->saddr
) != 1)
260 error(1, errno
, "inet_pton source ip6");
261 if (inet_pton(AF_INET6
, addr6_dst
, &ip6h
->daddr
) != 1)
262 error(1, errno
, "inet_pton dest ip6");
263 } else if (proto
== PF_INET
) {
264 memset(iph
, 0, sizeof(*iph
));
269 iph
->protocol
= IPPROTO_TCP
;
270 iph
->tot_len
= htons(sizeof(struct tcphdr
) +
271 payload_len
+ sizeof(struct iphdr
));
272 iph
->frag_off
= htons(0x4000); /* DF = 1, MF = 0 */
273 if (inet_pton(AF_INET
, addr4_src
, &iph
->saddr
) != 1)
274 error(1, errno
, "inet_pton source ip");
275 if (inet_pton(AF_INET
, addr4_dst
, &iph
->daddr
) != 1)
276 error(1, errno
, "inet_pton dest ip");
277 iph
->check
= checksum_fold(buf
, sizeof(struct iphdr
), 0);
281 static void fill_transportlayer(void *buf
, int seq_offset
, int ack_offset
,
282 int payload_len
, int fin
)
284 struct tcphdr
*tcph
= buf
;
286 memset(tcph
, 0, sizeof(*tcph
));
288 tcph
->source
= htons(SPORT
);
289 tcph
->dest
= htons(DPORT
);
290 tcph
->seq
= ntohl(START_SEQ
+ seq_offset
);
291 tcph
->ack_seq
= ntohl(START_ACK
+ ack_offset
);
295 tcph
->window
= htons(TCP_MAXWIN
);
297 tcph
->check
= tcp_checksum(tcph
, payload_len
);
300 static void write_packet(int fd
, char *buf
, int len
, struct sockaddr_ll
*daddr
)
304 ret
= sendto(fd
, buf
, len
, 0, (struct sockaddr
*)daddr
, sizeof(*daddr
));
306 error(1, errno
, "sendto failure");
308 error(1, errno
, "sendto wrong length");
311 static void create_packet(void *buf
, int seq_offset
, int ack_offset
,
312 int payload_len
, int fin
)
314 memset(buf
, 0, total_hdr_len
);
315 memset(buf
+ total_hdr_len
, 'a', payload_len
);
316 fill_transportlayer(buf
+ tcp_offset
, seq_offset
, ack_offset
,
318 fill_networklayer(buf
+ ETH_HLEN
, payload_len
);
319 fill_datalinklayer(buf
);
322 /* send one extra flag, not first and not last pkt */
323 static void send_flags(int fd
, struct sockaddr_ll
*daddr
, int psh
, int syn
,
326 static char flag_buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
327 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
328 int payload_len
, pkt_size
, flag
, i
;
331 payload_len
= PAYLOAD_LEN
* psh
;
332 pkt_size
= total_hdr_len
+ payload_len
;
333 flag
= NUM_PACKETS
/ 2;
335 create_packet(flag_buf
, flag
* payload_len
, 0, payload_len
, 0);
337 tcph
= (struct tcphdr
*)(flag_buf
+ tcp_offset
);
343 tcph
->check
= tcp_checksum(tcph
, payload_len
);
345 for (i
= 0; i
< NUM_PACKETS
+ 1; i
++) {
347 write_packet(fd
, flag_buf
, pkt_size
, daddr
);
350 create_packet(buf
, i
* PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
351 write_packet(fd
, buf
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
355 /* Test for data of same length, smaller than previous
356 * and of different lengths
358 static void send_data_pkts(int fd
, struct sockaddr_ll
*daddr
,
359 int payload_len1
, int payload_len2
)
361 static char buf
[ETH_HLEN
+ IP_MAXPACKET
];
363 create_packet(buf
, 0, 0, payload_len1
, 0);
364 write_packet(fd
, buf
, total_hdr_len
+ payload_len1
, daddr
);
365 create_packet(buf
, payload_len1
, 0, payload_len2
, 0);
366 write_packet(fd
, buf
, total_hdr_len
+ payload_len2
, daddr
);
369 /* If incoming segments make tracked segment length exceed
370 * legal IP datagram length, do not coalesce
372 static void send_large(int fd
, struct sockaddr_ll
*daddr
, int remainder
)
374 static char pkts
[NUM_LARGE_PKT
][TOTAL_HDR_LEN
+ MSS
];
375 static char last
[TOTAL_HDR_LEN
+ MSS
];
376 static char new_seg
[TOTAL_HDR_LEN
+ MSS
];
379 for (i
= 0; i
< NUM_LARGE_PKT
; i
++)
380 create_packet(pkts
[i
], i
* MSS
, 0, MSS
, 0);
381 create_packet(last
, NUM_LARGE_PKT
* MSS
, 0, remainder
, 0);
382 create_packet(new_seg
, (NUM_LARGE_PKT
+ 1) * MSS
, 0, remainder
, 0);
384 for (i
= 0; i
< NUM_LARGE_PKT
; i
++)
385 write_packet(fd
, pkts
[i
], total_hdr_len
+ MSS
, daddr
);
386 write_packet(fd
, last
, total_hdr_len
+ remainder
, daddr
);
387 write_packet(fd
, new_seg
, total_hdr_len
+ remainder
, daddr
);
390 /* Pure acks and dup acks don't coalesce */
391 static void send_ack(int fd
, struct sockaddr_ll
*daddr
)
393 static char buf
[MAX_HDR_LEN
];
395 create_packet(buf
, 0, 0, 0, 0);
396 write_packet(fd
, buf
, total_hdr_len
, daddr
);
397 write_packet(fd
, buf
, total_hdr_len
, daddr
);
398 create_packet(buf
, 0, 1, 0, 0);
399 write_packet(fd
, buf
, total_hdr_len
, daddr
);
402 static void recompute_packet(char *buf
, char *no_ext
, int extlen
)
404 struct tcphdr
*tcphdr
= (struct tcphdr
*)(buf
+ tcp_offset
);
405 struct ipv6hdr
*ip6h
= (struct ipv6hdr
*)(buf
+ ETH_HLEN
);
406 struct iphdr
*iph
= (struct iphdr
*)(buf
+ ETH_HLEN
);
408 memmove(buf
, no_ext
, total_hdr_len
);
409 memmove(buf
+ total_hdr_len
+ extlen
,
410 no_ext
+ total_hdr_len
, PAYLOAD_LEN
);
412 tcphdr
->doff
= tcphdr
->doff
+ (extlen
/ 4);
414 tcphdr
->check
= tcp_checksum(tcphdr
, PAYLOAD_LEN
+ extlen
);
415 if (proto
== PF_INET
) {
416 iph
->tot_len
= htons(ntohs(iph
->tot_len
) + extlen
);
418 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
420 ip6h
->payload_len
= htons(ntohs(ip6h
->payload_len
) + extlen
);
424 static void tcp_write_options(char *buf
, int kind
, int ts
)
426 struct tcp_option_ts
{
431 } *opt_ts
= (void *)buf
;
432 struct tcp_option_window
{
436 } *opt_window
= (void *)buf
;
443 memset(opt_window
, 0, sizeof(struct tcp_option_window
));
444 opt_window
->kind
= TCPOPT_WINDOW
;
445 opt_window
->len
= TCPOLEN_WINDOW
;
446 opt_window
->shift
= 0;
448 case TCPOPT_TIMESTAMP
:
449 memset(opt_ts
, 0, sizeof(struct tcp_option_ts
));
450 opt_ts
->kind
= TCPOPT_TIMESTAMP
;
451 opt_ts
->len
= TCPOLEN_TIMESTAMP
;
456 error(1, 0, "unimplemented TCP option");
461 /* TCP with options is always a permutation of {TS, NOP, NOP}.
462 * Implement different orders to verify coalescing stops.
464 static void add_standard_tcp_options(char *buf
, char *no_ext
, int ts
, int order
)
468 tcp_write_options(buf
+ total_hdr_len
, TCPOPT_NOP
, 0);
469 tcp_write_options(buf
+ total_hdr_len
+ 1, TCPOPT_NOP
, 0);
470 tcp_write_options(buf
+ total_hdr_len
+ 2 /* two NOP opts */,
471 TCPOPT_TIMESTAMP
, ts
);
474 tcp_write_options(buf
+ total_hdr_len
, TCPOPT_NOP
, 0);
475 tcp_write_options(buf
+ total_hdr_len
+ 1,
476 TCPOPT_TIMESTAMP
, ts
);
477 tcp_write_options(buf
+ total_hdr_len
+ 1 + TCPOLEN_TIMESTAMP
,
481 tcp_write_options(buf
+ total_hdr_len
, TCPOPT_TIMESTAMP
, ts
);
482 tcp_write_options(buf
+ total_hdr_len
+ TCPOLEN_TIMESTAMP
+ 1,
484 tcp_write_options(buf
+ total_hdr_len
+ TCPOLEN_TIMESTAMP
+ 2,
488 error(1, 0, "unknown order");
491 recompute_packet(buf
, no_ext
, TCPOLEN_TSTAMP_APPA
);
494 /* Packets with invalid checksum don't coalesce. */
495 static void send_changed_checksum(int fd
, struct sockaddr_ll
*daddr
)
497 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
498 struct tcphdr
*tcph
= (struct tcphdr
*)(buf
+ tcp_offset
);
499 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
501 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
502 write_packet(fd
, buf
, pkt_size
, daddr
);
504 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
505 tcph
->check
= tcph
->check
- 1;
506 write_packet(fd
, buf
, pkt_size
, daddr
);
509 /* Packets with non-consecutive sequence number don't coalesce.*/
510 static void send_changed_seq(int fd
, struct sockaddr_ll
*daddr
)
512 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
513 struct tcphdr
*tcph
= (struct tcphdr
*)(buf
+ tcp_offset
);
514 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
516 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
517 write_packet(fd
, buf
, pkt_size
, daddr
);
519 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
520 tcph
->seq
= ntohl(htonl(tcph
->seq
) + 1);
522 tcph
->check
= tcp_checksum(tcph
, PAYLOAD_LEN
);
523 write_packet(fd
, buf
, pkt_size
, daddr
);
526 /* Packet with different timestamp option or different timestamps
529 static void send_changed_ts(int fd
, struct sockaddr_ll
*daddr
)
531 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
532 static char extpkt
[sizeof(buf
) + TCPOLEN_TSTAMP_APPA
];
533 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
+ TCPOLEN_TSTAMP_APPA
;
535 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
536 add_standard_tcp_options(extpkt
, buf
, 0, 0);
537 write_packet(fd
, extpkt
, pkt_size
, daddr
);
539 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
540 add_standard_tcp_options(extpkt
, buf
, 0, 0);
541 write_packet(fd
, extpkt
, pkt_size
, daddr
);
543 create_packet(buf
, PAYLOAD_LEN
* 2, 0, PAYLOAD_LEN
, 0);
544 add_standard_tcp_options(extpkt
, buf
, 100, 0);
545 write_packet(fd
, extpkt
, pkt_size
, daddr
);
547 create_packet(buf
, PAYLOAD_LEN
* 3, 0, PAYLOAD_LEN
, 0);
548 add_standard_tcp_options(extpkt
, buf
, 100, 1);
549 write_packet(fd
, extpkt
, pkt_size
, daddr
);
551 create_packet(buf
, PAYLOAD_LEN
* 4, 0, PAYLOAD_LEN
, 0);
552 add_standard_tcp_options(extpkt
, buf
, 100, 2);
553 write_packet(fd
, extpkt
, pkt_size
, daddr
);
556 /* Packet with different tcp options don't coalesce. */
557 static void send_diff_opt(int fd
, struct sockaddr_ll
*daddr
)
559 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
560 static char extpkt1
[sizeof(buf
) + TCPOLEN_TSTAMP_APPA
];
561 static char extpkt2
[sizeof(buf
) + TCPOLEN_MAXSEG
];
562 int extpkt1_size
= total_hdr_len
+ PAYLOAD_LEN
+ TCPOLEN_TSTAMP_APPA
;
563 int extpkt2_size
= total_hdr_len
+ PAYLOAD_LEN
+ TCPOLEN_MAXSEG
;
565 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
566 add_standard_tcp_options(extpkt1
, buf
, 0, 0);
567 write_packet(fd
, extpkt1
, extpkt1_size
, daddr
);
569 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
570 add_standard_tcp_options(extpkt1
, buf
, 0, 0);
571 write_packet(fd
, extpkt1
, extpkt1_size
, daddr
);
573 create_packet(buf
, PAYLOAD_LEN
* 2, 0, PAYLOAD_LEN
, 0);
574 tcp_write_options(extpkt2
+ MAX_HDR_LEN
, TCPOPT_NOP
, 0);
575 tcp_write_options(extpkt2
+ MAX_HDR_LEN
+ 1, TCPOPT_WINDOW
, 0);
576 recompute_packet(extpkt2
, buf
, TCPOLEN_WINDOW
+ 1);
577 write_packet(fd
, extpkt2
, extpkt2_size
, daddr
);
580 static void add_ipv4_ts_option(void *buf
, void *optpkt
)
582 struct ip_timestamp
*ts
= (struct ip_timestamp
*)(optpkt
+ tcp_offset
);
583 int optlen
= sizeof(struct ip_timestamp
);
587 error(1, 0, "ipv4 timestamp length is not a multiple of 4B");
589 ts
->ipt_code
= IPOPT_TS
;
590 ts
->ipt_len
= optlen
;
592 ts
->ipt_flg
= IPOPT_TS_TSONLY
;
594 memcpy(optpkt
, buf
, tcp_offset
);
595 memcpy(optpkt
+ tcp_offset
+ optlen
, buf
+ tcp_offset
,
596 sizeof(struct tcphdr
) + PAYLOAD_LEN
);
598 iph
= (struct iphdr
*)(optpkt
+ ETH_HLEN
);
599 iph
->ihl
= 5 + (optlen
/ 4);
600 iph
->tot_len
= htons(ntohs(iph
->tot_len
) + optlen
);
602 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
) + optlen
, 0);
605 static void add_ipv6_exthdr(void *buf
, void *optpkt
, __u8 exthdr_type
, char *ext_payload
)
607 struct ipv6_opt_hdr
*exthdr
= (struct ipv6_opt_hdr
*)(optpkt
+ tcp_offset
);
608 struct ipv6hdr
*iph
= (struct ipv6hdr
*)(optpkt
+ ETH_HLEN
);
609 char *exthdr_payload_start
= (char *)(exthdr
+ 1);
612 exthdr
->nexthdr
= IPPROTO_TCP
;
614 memcpy(exthdr_payload_start
, ext_payload
, MIN_EXTHDR_SIZE
- sizeof(*exthdr
));
616 memcpy(optpkt
, buf
, tcp_offset
);
617 memcpy(optpkt
+ tcp_offset
+ MIN_EXTHDR_SIZE
, buf
+ tcp_offset
,
618 sizeof(struct tcphdr
) + PAYLOAD_LEN
);
620 iph
->nexthdr
= exthdr_type
;
621 iph
->payload_len
= htons(ntohs(iph
->payload_len
) + MIN_EXTHDR_SIZE
);
624 static void fix_ip4_checksum(struct iphdr
*iph
)
627 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
630 static void send_flush_id_case(int fd
, struct sockaddr_ll
*daddr
, int tcase
)
632 static char buf1
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
633 static char buf2
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
634 static char buf3
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
635 bool send_three
= false;
640 iph1
= (struct iphdr
*)(buf1
+ ETH_HLEN
);
641 iph2
= (struct iphdr
*)(buf2
+ ETH_HLEN
);
642 iph3
= (struct iphdr
*)(buf3
+ ETH_HLEN
);
644 create_packet(buf1
, 0, 0, PAYLOAD_LEN
, 0);
645 create_packet(buf2
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
646 create_packet(buf3
, PAYLOAD_LEN
* 2, 0, PAYLOAD_LEN
, 0);
649 case 0: /* DF=1, Incrementing - should coalesce */
650 iph1
->frag_off
|= htons(IP_DF
);
653 iph2
->frag_off
|= htons(IP_DF
);
657 case 1: /* DF=1, Fixed - should coalesce */
658 iph1
->frag_off
|= htons(IP_DF
);
661 iph2
->frag_off
|= htons(IP_DF
);
665 case 2: /* DF=0, Incrementing - should coalesce */
666 iph1
->frag_off
&= ~htons(IP_DF
);
669 iph2
->frag_off
&= ~htons(IP_DF
);
673 case 3: /* DF=0, Fixed - should not coalesce */
674 iph1
->frag_off
&= ~htons(IP_DF
);
677 iph2
->frag_off
&= ~htons(IP_DF
);
681 case 4: /* DF=1, two packets incrementing, and one fixed - should
682 * coalesce only the first two packets
684 iph1
->frag_off
|= htons(IP_DF
);
687 iph2
->frag_off
|= htons(IP_DF
);
690 iph3
->frag_off
|= htons(IP_DF
);
695 case 5: /* DF=1, two packets fixed, and one incrementing - should
696 * coalesce only the first two packets
698 iph1
->frag_off
|= htons(IP_DF
);
701 iph2
->frag_off
|= htons(IP_DF
);
704 iph3
->frag_off
|= htons(IP_DF
);
710 fix_ip4_checksum(iph1
);
711 fix_ip4_checksum(iph2
);
712 write_packet(fd
, buf1
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
713 write_packet(fd
, buf2
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
716 fix_ip4_checksum(iph3
);
717 write_packet(fd
, buf3
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
721 static void test_flush_id(int fd
, struct sockaddr_ll
*daddr
, char *fin_pkt
)
723 for (int i
= 0; i
< num_flush_id_cases
; i
++) {
725 send_flush_id_case(fd
, daddr
, i
);
727 write_packet(fd
, fin_pkt
, total_hdr_len
, daddr
);
731 static void send_ipv6_exthdr(int fd
, struct sockaddr_ll
*daddr
, char *ext_data1
, char *ext_data2
)
733 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
734 static char exthdr_pck
[sizeof(buf
) + MIN_EXTHDR_SIZE
];
736 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
737 add_ipv6_exthdr(buf
, exthdr_pck
, IPPROTO_HOPOPTS
, ext_data1
);
738 write_packet(fd
, exthdr_pck
, total_hdr_len
+ PAYLOAD_LEN
+ MIN_EXTHDR_SIZE
, daddr
);
740 create_packet(buf
, PAYLOAD_LEN
* 1, 0, PAYLOAD_LEN
, 0);
741 add_ipv6_exthdr(buf
, exthdr_pck
, IPPROTO_HOPOPTS
, ext_data2
);
742 write_packet(fd
, exthdr_pck
, total_hdr_len
+ PAYLOAD_LEN
+ MIN_EXTHDR_SIZE
, daddr
);
745 /* IPv4 options shouldn't coalesce */
746 static void send_ip_options(int fd
, struct sockaddr_ll
*daddr
)
748 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
749 static char optpkt
[sizeof(buf
) + sizeof(struct ip_timestamp
)];
750 int optlen
= sizeof(struct ip_timestamp
);
751 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
+ optlen
;
753 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
754 write_packet(fd
, buf
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
756 create_packet(buf
, PAYLOAD_LEN
* 1, 0, PAYLOAD_LEN
, 0);
757 add_ipv4_ts_option(buf
, optpkt
);
758 write_packet(fd
, optpkt
, pkt_size
, daddr
);
760 create_packet(buf
, PAYLOAD_LEN
* 2, 0, PAYLOAD_LEN
, 0);
761 write_packet(fd
, buf
, total_hdr_len
+ PAYLOAD_LEN
, daddr
);
764 /* IPv4 fragments shouldn't coalesce */
765 static void send_fragment4(int fd
, struct sockaddr_ll
*daddr
)
767 static char buf
[IP_MAXPACKET
];
768 struct iphdr
*iph
= (struct iphdr
*)(buf
+ ETH_HLEN
);
769 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
771 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
772 write_packet(fd
, buf
, pkt_size
, daddr
);
774 /* Once fragmented, packet would retain the total_len.
775 * Tcp header is prepared as if rest of data is in follow-up frags,
776 * but follow up frags aren't actually sent.
778 memset(buf
+ total_hdr_len
, 'a', PAYLOAD_LEN
* 2);
779 fill_transportlayer(buf
+ tcp_offset
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
* 2, 0);
780 fill_networklayer(buf
+ ETH_HLEN
, PAYLOAD_LEN
);
781 fill_datalinklayer(buf
);
783 iph
->frag_off
= htons(0x6000); // DF = 1, MF = 1
785 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
786 write_packet(fd
, buf
, pkt_size
, daddr
);
789 /* IPv4 packets with different ttl don't coalesce.*/
790 static void send_changed_ttl(int fd
, struct sockaddr_ll
*daddr
)
792 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
793 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
794 struct iphdr
*iph
= (struct iphdr
*)(buf
+ ETH_HLEN
);
796 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
797 write_packet(fd
, buf
, pkt_size
, daddr
);
799 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
802 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
803 write_packet(fd
, buf
, pkt_size
, daddr
);
806 /* Packets with different tos don't coalesce.*/
807 static void send_changed_tos(int fd
, struct sockaddr_ll
*daddr
)
809 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
810 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
811 struct iphdr
*iph
= (struct iphdr
*)(buf
+ ETH_HLEN
);
812 struct ipv6hdr
*ip6h
= (struct ipv6hdr
*)(buf
+ ETH_HLEN
);
814 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
815 write_packet(fd
, buf
, pkt_size
, daddr
);
817 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
818 if (proto
== PF_INET
) {
821 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
822 } else if (proto
== PF_INET6
) {
823 ip6h
->priority
= 0xf;
825 write_packet(fd
, buf
, pkt_size
, daddr
);
828 /* Packets with different ECN don't coalesce.*/
829 static void send_changed_ECN(int fd
, struct sockaddr_ll
*daddr
)
831 int pkt_size
= total_hdr_len
+ PAYLOAD_LEN
;
832 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
833 struct iphdr
*iph
= (struct iphdr
*)(buf
+ ETH_HLEN
);
835 create_packet(buf
, 0, 0, PAYLOAD_LEN
, 0);
836 write_packet(fd
, buf
, pkt_size
, daddr
);
838 create_packet(buf
, PAYLOAD_LEN
, 0, PAYLOAD_LEN
, 0);
839 if (proto
== PF_INET
) {
840 buf
[ETH_HLEN
+ 1] ^= 0x2; // ECN set to 10
842 iph
->check
= checksum_fold(iph
, sizeof(struct iphdr
), 0);
844 buf
[ETH_HLEN
+ 1] ^= 0x20; // ECN set to 10
846 write_packet(fd
, buf
, pkt_size
, daddr
);
849 /* IPv6 fragments and packets with extensions don't coalesce.*/
850 static void send_fragment6(int fd
, struct sockaddr_ll
*daddr
)
852 static char buf
[MAX_HDR_LEN
+ PAYLOAD_LEN
];
853 static char extpkt
[MAX_HDR_LEN
+ PAYLOAD_LEN
+
854 sizeof(struct ip6_frag
)];
855 struct ipv6hdr
*ip6h
= (struct ipv6hdr
*)(buf
+ ETH_HLEN
);
856 struct ip6_frag
*frag
= (void *)(extpkt
+ tcp_offset
);
857 int extlen
= sizeof(struct ip6_frag
);
858 int bufpkt_len
= total_hdr_len
+ PAYLOAD_LEN
;
859 int extpkt_len
= bufpkt_len
+ extlen
;
862 for (i
= 0; i
< 2; i
++) {
863 create_packet(buf
, PAYLOAD_LEN
* i
, 0, PAYLOAD_LEN
, 0);
864 write_packet(fd
, buf
, bufpkt_len
, daddr
);
867 create_packet(buf
, PAYLOAD_LEN
* 2, 0, PAYLOAD_LEN
, 0);
868 memset(extpkt
, 0, extpkt_len
);
870 ip6h
->nexthdr
= IPPROTO_FRAGMENT
;
871 ip6h
->payload_len
= htons(ntohs(ip6h
->payload_len
) + extlen
);
872 frag
->ip6f_nxt
= IPPROTO_TCP
;
874 memcpy(extpkt
, buf
, tcp_offset
);
875 memcpy(extpkt
+ tcp_offset
+ extlen
, buf
+ tcp_offset
,
876 sizeof(struct tcphdr
) + PAYLOAD_LEN
);
877 write_packet(fd
, extpkt
, extpkt_len
, daddr
);
879 create_packet(buf
, PAYLOAD_LEN
* 3, 0, PAYLOAD_LEN
, 0);
880 write_packet(fd
, buf
, bufpkt_len
, daddr
);
883 static void bind_packetsocket(int fd
)
885 struct sockaddr_ll daddr
= {};
887 daddr
.sll_family
= AF_PACKET
;
888 daddr
.sll_protocol
= ethhdr_proto
;
889 daddr
.sll_ifindex
= if_nametoindex(ifname
);
890 if (daddr
.sll_ifindex
== 0)
891 error(1, errno
, "if_nametoindex");
893 if (bind(fd
, (void *)&daddr
, sizeof(daddr
)) < 0)
894 error(1, errno
, "could not bind socket");
897 static void set_timeout(int fd
)
899 struct timeval timeout
;
903 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&timeout
,
904 sizeof(timeout
)) < 0)
905 error(1, errno
, "cannot set timeout, setsockopt failed");
908 static void check_recv_pkts(int fd
, int *correct_payload
,
909 int correct_num_pkts
)
911 static char buffer
[IP_MAXPACKET
+ ETH_HLEN
+ 1];
912 struct iphdr
*iph
= (struct iphdr
*)(buffer
+ ETH_HLEN
);
913 struct ipv6hdr
*ip6h
= (struct ipv6hdr
*)(buffer
+ ETH_HLEN
);
915 bool bad_packet
= false;
924 for (i
= 0; i
< correct_num_pkts
; i
++)
925 vlog("%d ", correct_payload
[i
]);
926 vlog("}, Total %d packets\nReceived {", correct_num_pkts
);
930 pkt_size
= recv(fd
, buffer
, IP_MAXPACKET
+ ETH_HLEN
+ 1, 0);
932 error(1, errno
, "could not receive");
934 if (iph
->version
== 4)
935 ip_ext_len
= (iph
->ihl
- 5) * 4;
936 else if (ip6h
->version
== 6 && ip6h
->nexthdr
!= IPPROTO_TCP
)
937 ip_ext_len
= MIN_EXTHDR_SIZE
;
939 tcph
= (struct tcphdr
*)(buffer
+ tcp_offset
+ ip_ext_len
);
944 tcp_ext_len
= (tcph
->doff
- 5) * 4;
945 data_len
= pkt_size
- total_hdr_len
- tcp_ext_len
- ip_ext_len
;
946 /* Min ethernet frame payload is 46(ETH_ZLEN - ETH_HLEN) by RFC 802.3.
947 * Ipv4/tcp packets without at least 6 bytes of data will be padded.
948 * Packet sockets are protocol agnostic, and will not trim the padding.
950 if (pkt_size
== ETH_ZLEN
&& iph
->version
== 4) {
951 data_len
= ntohs(iph
->tot_len
)
952 - sizeof(struct tcphdr
) - sizeof(struct iphdr
);
954 vlog("%d ", data_len
);
955 if (data_len
!= correct_payload
[num_pkt
]) {
956 vlog("[!=%d]", correct_payload
[num_pkt
]);
961 vlog("}, Total %d packets.\n", num_pkt
);
962 if (num_pkt
!= correct_num_pkts
)
963 error(1, 0, "incorrect number of packets");
965 error(1, 0, "incorrect packet geometry");
967 printf("Test succeeded\n\n");
970 static void gro_sender(void)
972 static char fin_pkt
[MAX_HDR_LEN
];
973 struct sockaddr_ll daddr
= {};
976 txfd
= socket(PF_PACKET
, SOCK_RAW
, IPPROTO_RAW
);
978 error(1, errno
, "socket creation");
980 memset(&daddr
, 0, sizeof(daddr
));
981 daddr
.sll_ifindex
= if_nametoindex(ifname
);
982 if (daddr
.sll_ifindex
== 0)
983 error(1, errno
, "if_nametoindex");
984 daddr
.sll_family
= AF_PACKET
;
985 memcpy(daddr
.sll_addr
, dst_mac
, ETH_ALEN
);
986 daddr
.sll_halen
= ETH_ALEN
;
987 create_packet(fin_pkt
, PAYLOAD_LEN
* 2, 0, 0, 1);
989 if (strcmp(testname
, "data") == 0) {
990 send_data_pkts(txfd
, &daddr
, PAYLOAD_LEN
, PAYLOAD_LEN
);
991 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
993 send_data_pkts(txfd
, &daddr
, PAYLOAD_LEN
, PAYLOAD_LEN
/ 2);
994 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
996 send_data_pkts(txfd
, &daddr
, PAYLOAD_LEN
/ 2, PAYLOAD_LEN
);
997 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
998 } else if (strcmp(testname
, "ack") == 0) {
999 send_ack(txfd
, &daddr
);
1000 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1001 } else if (strcmp(testname
, "flags") == 0) {
1002 send_flags(txfd
, &daddr
, 1, 0, 0, 0);
1003 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1005 send_flags(txfd
, &daddr
, 0, 1, 0, 0);
1006 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1008 send_flags(txfd
, &daddr
, 0, 0, 1, 0);
1009 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1011 send_flags(txfd
, &daddr
, 0, 0, 0, 1);
1012 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1013 } else if (strcmp(testname
, "tcp") == 0) {
1014 send_changed_checksum(txfd
, &daddr
);
1015 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1017 send_changed_seq(txfd
, &daddr
);
1018 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1020 send_changed_ts(txfd
, &daddr
);
1021 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1023 send_diff_opt(txfd
, &daddr
);
1024 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1025 } else if (strcmp(testname
, "ip") == 0) {
1026 send_changed_ECN(txfd
, &daddr
);
1027 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1029 send_changed_tos(txfd
, &daddr
);
1030 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1031 if (proto
== PF_INET
) {
1032 /* Modified packets may be received out of order.
1033 * Sleep function added to enforce test boundaries
1034 * so that fin pkts are not received prior to other pkts.
1037 send_changed_ttl(txfd
, &daddr
);
1038 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1041 send_ip_options(txfd
, &daddr
);
1043 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1046 send_fragment4(txfd
, &daddr
);
1048 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1050 test_flush_id(txfd
, &daddr
, fin_pkt
);
1051 } else if (proto
== PF_INET6
) {
1053 send_fragment6(txfd
, &daddr
);
1055 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1058 /* send IPv6 packets with ext header with same payload */
1059 send_ipv6_exthdr(txfd
, &daddr
, EXT_PAYLOAD_1
, EXT_PAYLOAD_1
);
1061 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1064 /* send IPv6 packets with ext header with different payload */
1065 send_ipv6_exthdr(txfd
, &daddr
, EXT_PAYLOAD_1
, EXT_PAYLOAD_2
);
1067 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1069 } else if (strcmp(testname
, "large") == 0) {
1070 /* 20 is the difference between min iphdr size
1071 * and min ipv6hdr size. Like MAX_HDR_SIZE,
1072 * MAX_PAYLOAD is defined with the larger header of the two.
1074 int offset
= proto
== PF_INET
? 20 : 0;
1075 int remainder
= (MAX_PAYLOAD
+ offset
) % MSS
;
1077 send_large(txfd
, &daddr
, remainder
);
1078 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1080 send_large(txfd
, &daddr
, remainder
+ 1);
1081 write_packet(txfd
, fin_pkt
, total_hdr_len
, &daddr
);
1083 error(1, 0, "Unknown testcase");
1087 error(1, errno
, "socket close");
1090 static void gro_receiver(void)
1092 static int correct_payload
[NUM_PACKETS
];
1095 rxfd
= socket(PF_PACKET
, SOCK_RAW
, htons(ETH_P_NONE
));
1097 error(1, 0, "socket creation");
1098 setup_sock_filter(rxfd
);
1100 bind_packetsocket(rxfd
);
1102 memset(correct_payload
, 0, sizeof(correct_payload
));
1104 if (strcmp(testname
, "data") == 0) {
1105 printf("pure data packet of same size: ");
1106 correct_payload
[0] = PAYLOAD_LEN
* 2;
1107 check_recv_pkts(rxfd
, correct_payload
, 1);
1109 printf("large data packets followed by a smaller one: ");
1110 correct_payload
[0] = PAYLOAD_LEN
* 1.5;
1111 check_recv_pkts(rxfd
, correct_payload
, 1);
1113 printf("small data packets followed by a larger one: ");
1114 correct_payload
[0] = PAYLOAD_LEN
/ 2;
1115 correct_payload
[1] = PAYLOAD_LEN
;
1116 check_recv_pkts(rxfd
, correct_payload
, 2);
1117 } else if (strcmp(testname
, "ack") == 0) {
1118 printf("duplicate ack and pure ack: ");
1119 check_recv_pkts(rxfd
, correct_payload
, 3);
1120 } else if (strcmp(testname
, "flags") == 0) {
1121 correct_payload
[0] = PAYLOAD_LEN
* 3;
1122 correct_payload
[1] = PAYLOAD_LEN
* 2;
1124 printf("psh flag ends coalescing: ");
1125 check_recv_pkts(rxfd
, correct_payload
, 2);
1127 correct_payload
[0] = PAYLOAD_LEN
* 2;
1128 correct_payload
[1] = 0;
1129 correct_payload
[2] = PAYLOAD_LEN
* 2;
1130 printf("syn flag ends coalescing: ");
1131 check_recv_pkts(rxfd
, correct_payload
, 3);
1133 printf("rst flag ends coalescing: ");
1134 check_recv_pkts(rxfd
, correct_payload
, 3);
1136 printf("urg flag ends coalescing: ");
1137 check_recv_pkts(rxfd
, correct_payload
, 3);
1138 } else if (strcmp(testname
, "tcp") == 0) {
1139 correct_payload
[0] = PAYLOAD_LEN
;
1140 correct_payload
[1] = PAYLOAD_LEN
;
1141 correct_payload
[2] = PAYLOAD_LEN
;
1142 correct_payload
[3] = PAYLOAD_LEN
;
1144 printf("changed checksum does not coalesce: ");
1145 check_recv_pkts(rxfd
, correct_payload
, 2);
1147 printf("Wrong Seq number doesn't coalesce: ");
1148 check_recv_pkts(rxfd
, correct_payload
, 2);
1150 printf("Different timestamp doesn't coalesce: ");
1151 correct_payload
[0] = PAYLOAD_LEN
* 2;
1152 check_recv_pkts(rxfd
, correct_payload
, 4);
1154 printf("Different options doesn't coalesce: ");
1155 correct_payload
[0] = PAYLOAD_LEN
* 2;
1156 check_recv_pkts(rxfd
, correct_payload
, 2);
1157 } else if (strcmp(testname
, "ip") == 0) {
1158 correct_payload
[0] = PAYLOAD_LEN
;
1159 correct_payload
[1] = PAYLOAD_LEN
;
1161 printf("different ECN doesn't coalesce: ");
1162 check_recv_pkts(rxfd
, correct_payload
, 2);
1164 printf("different tos doesn't coalesce: ");
1165 check_recv_pkts(rxfd
, correct_payload
, 2);
1167 if (proto
== PF_INET
) {
1168 printf("different ttl doesn't coalesce: ");
1169 check_recv_pkts(rxfd
, correct_payload
, 2);
1171 printf("ip options doesn't coalesce: ");
1172 correct_payload
[2] = PAYLOAD_LEN
;
1173 check_recv_pkts(rxfd
, correct_payload
, 3);
1175 printf("fragmented ip4 doesn't coalesce: ");
1176 check_recv_pkts(rxfd
, correct_payload
, 2);
1178 /* is_atomic checks */
1179 printf("DF=1, Incrementing - should coalesce: ");
1180 correct_payload
[0] = PAYLOAD_LEN
* 2;
1181 check_recv_pkts(rxfd
, correct_payload
, 1);
1183 printf("DF=1, Fixed - should coalesce: ");
1184 correct_payload
[0] = PAYLOAD_LEN
* 2;
1185 check_recv_pkts(rxfd
, correct_payload
, 1);
1187 printf("DF=0, Incrementing - should coalesce: ");
1188 correct_payload
[0] = PAYLOAD_LEN
* 2;
1189 check_recv_pkts(rxfd
, correct_payload
, 1);
1191 printf("DF=0, Fixed - should not coalesce: ");
1192 correct_payload
[0] = PAYLOAD_LEN
;
1193 correct_payload
[1] = PAYLOAD_LEN
;
1194 check_recv_pkts(rxfd
, correct_payload
, 2);
1196 printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");
1197 correct_payload
[0] = PAYLOAD_LEN
* 2;
1198 correct_payload
[1] = PAYLOAD_LEN
;
1199 check_recv_pkts(rxfd
, correct_payload
, 2);
1201 printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");
1202 correct_payload
[0] = PAYLOAD_LEN
* 2;
1203 correct_payload
[1] = PAYLOAD_LEN
;
1204 check_recv_pkts(rxfd
, correct_payload
, 2);
1205 } else if (proto
== PF_INET6
) {
1206 /* GRO doesn't check for ipv6 hop limit when flushing.
1207 * Hence no corresponding test to the ipv4 case.
1209 printf("fragmented ip6 doesn't coalesce: ");
1210 correct_payload
[0] = PAYLOAD_LEN
* 2;
1211 correct_payload
[1] = PAYLOAD_LEN
;
1212 correct_payload
[2] = PAYLOAD_LEN
;
1213 check_recv_pkts(rxfd
, correct_payload
, 3);
1215 printf("ipv6 with ext header does coalesce: ");
1216 correct_payload
[0] = PAYLOAD_LEN
* 2;
1217 check_recv_pkts(rxfd
, correct_payload
, 1);
1219 printf("ipv6 with ext header with different payloads doesn't coalesce: ");
1220 correct_payload
[0] = PAYLOAD_LEN
;
1221 correct_payload
[1] = PAYLOAD_LEN
;
1222 check_recv_pkts(rxfd
, correct_payload
, 2);
1224 } else if (strcmp(testname
, "large") == 0) {
1225 int offset
= proto
== PF_INET
? 20 : 0;
1226 int remainder
= (MAX_PAYLOAD
+ offset
) % MSS
;
1228 correct_payload
[0] = (MAX_PAYLOAD
+ offset
);
1229 correct_payload
[1] = remainder
;
1230 printf("Shouldn't coalesce if exceed IP max pkt size: ");
1231 check_recv_pkts(rxfd
, correct_payload
, 2);
1233 /* last segment sent individually, doesn't start new segment */
1234 correct_payload
[0] = correct_payload
[0] - remainder
;
1235 correct_payload
[1] = remainder
+ 1;
1236 correct_payload
[2] = remainder
+ 1;
1237 check_recv_pkts(rxfd
, correct_payload
, 3);
1239 error(1, 0, "Test case error, should never trigger");
1243 error(1, 0, "socket close");
1246 static void parse_args(int argc
, char **argv
)
1248 static const struct option opts
[] = {
1249 { "daddr", required_argument
, NULL
, 'd' },
1250 { "dmac", required_argument
, NULL
, 'D' },
1251 { "iface", required_argument
, NULL
, 'i' },
1252 { "ipv4", no_argument
, NULL
, '4' },
1253 { "ipv6", no_argument
, NULL
, '6' },
1254 { "rx", no_argument
, NULL
, 'r' },
1255 { "saddr", required_argument
, NULL
, 's' },
1256 { "smac", required_argument
, NULL
, 'S' },
1257 { "test", required_argument
, NULL
, 't' },
1258 { "verbose", no_argument
, NULL
, 'v' },
1263 while ((c
= getopt_long(argc
, argv
, "46d:D:i:rs:S:t:v", opts
, NULL
)) != -1) {
1267 ethhdr_proto
= htons(ETH_P_IP
);
1271 ethhdr_proto
= htons(ETH_P_IPV6
);
1274 addr4_dst
= addr6_dst
= optarg
;
1286 addr4_src
= addr6_src
= optarg
;
1298 error(1, 0, "%s invalid option %c\n", __func__
, c
);
1304 int main(int argc
, char **argv
)
1306 parse_args(argc
, argv
);
1308 if (proto
== PF_INET
) {
1309 tcp_offset
= ETH_HLEN
+ sizeof(struct iphdr
);
1310 total_hdr_len
= tcp_offset
+ sizeof(struct tcphdr
);
1311 } else if (proto
== PF_INET6
) {
1312 tcp_offset
= ETH_HLEN
+ sizeof(struct ipv6hdr
);
1313 total_hdr_len
= MAX_HDR_LEN
;
1315 error(1, 0, "Protocol family is not ipv4 or ipv6");
1318 read_MAC(src_mac
, smac
);
1319 read_MAC(dst_mac
, dmac
);
1326 fprintf(stderr
, "Gro::%s test passed.\n", testname
);