1329 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This testsuite provides conformance testing for GRO coalescing.4 *5 * Test cases:6 * 1.data7 * Data packets of the same size and same header setup with correct8 * sequence numbers coalesce. The one exception being the last data9 * packet coalesced: it can be smaller than the rest and coalesced10 * as long as it is in the same flow.11 * 2.ack12 * Pure ACK does not coalesce.13 * 3.flags14 * Specific test cases: no packets with PSH, SYN, URG, RST set will15 * be coalesced.16 * 4.tcp17 * Packets with incorrect checksum, non-consecutive seqno and18 * different TCP header options shouldn't coalesce. Nit: given that19 * some extension headers have paddings, such as timestamp, headers20 * that are padding differently would not be coalesced.21 * 5.ip:22 * Packets with different (ECN, TTL, TOS) header, ip options or23 * 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 small28 * (i.e. 1500 MTU - header), it will result in many packets,29 * increasing the "large" test case's flakiness. This is because30 * due to time sensitivity in the coalescing window, the receiver31 * may not coalesce all of the packets.32 *33 * Note the timing issue applies to all of the test cases, so some34 * flakiness is to be expected.35 *36 */37 38#define _GNU_SOURCE39 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 800063#define SPORT 150064#define PAYLOAD_LEN 10065#define NUM_PACKETS 466#define START_SEQ 10067#define START_ACK 10068#define ETH_P_NONE 069#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 875#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;96static const int num_flush_id_cases = 6;97 98static void vlog(const char *fmt, ...)99{100 va_list args;101 102 if (verbose) {103 va_start(args, fmt);104 vfprintf(stderr, fmt, args);105 va_end(args);106 }107}108 109static void setup_sock_filter(int fd)110{111 const int dport_off = tcp_offset + offsetof(struct tcphdr, dest);112 const int ethproto_off = offsetof(struct ethhdr, h_proto);113 int optlen = 0;114 int ipproto_off, opt_ipproto_off;115 int next_off;116 117 if (proto == PF_INET)118 next_off = offsetof(struct iphdr, protocol);119 else120 next_off = offsetof(struct ipv6hdr, nexthdr);121 ipproto_off = ETH_HLEN + next_off;122 123 /* Overridden later if exthdrs are used: */124 opt_ipproto_off = ipproto_off;125 126 if (strcmp(testname, "ip") == 0) {127 if (proto == PF_INET)128 optlen = sizeof(struct ip_timestamp);129 else {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);133 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);138 }139 }140 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 one145 * extension header and then TCP.146 */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),160 };161 162 struct sock_fprog bpf = {163 .len = ARRAY_SIZE(filter),164 .filter = filter,165 };166 167 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)) < 0)168 error(1, errno, "error setting filter");169}170 171static uint32_t checksum_nofold(void *data, size_t len, uint32_t sum)172{173 uint16_t *words = data;174 int i;175 176 for (i = 0; i < len / 2; i++)177 sum += words[i];178 if (len & 1)179 sum += ((char *)data)[len - 1];180 return sum;181}182 183static uint16_t checksum_fold(void *data, size_t len, uint32_t sum)184{185 sum = checksum_nofold(data, len, sum);186 while (sum > 0xFFFF)187 sum = (sum & 0xFFFF) + (sum >> 16);188 return ~sum;189}190 191static uint16_t tcp_checksum(void *buf, int payload_len)192{193 struct pseudo_header6 {194 struct in6_addr saddr;195 struct in6_addr daddr;196 uint16_t protocol;197 uint16_t payload_len;198 } ph6;199 struct pseudo_header4 {200 struct in_addr saddr;201 struct in_addr daddr;202 uint16_t protocol;203 uint16_t payload_len;204 } ph4;205 uint32_t sum = 0;206 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);214 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);223 224 sum = checksum_nofold(&ph4, sizeof(ph4), 0);225 }226 227 return checksum_fold(buf, sizeof(struct tcphdr) + payload_len, sum);228}229 230static void read_MAC(uint8_t *mac_addr, char *mac)231{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");236}237 238static void fill_datalinklayer(void *buf)239{240 struct ethhdr *eth = buf;241 242 memcpy(eth->h_dest, dst_mac, ETH_ALEN);243 memcpy(eth->h_source, src_mac, ETH_ALEN);244 eth->h_proto = ethhdr_proto;245}246 247static void fill_networklayer(void *buf, int payload_len)248{249 struct ipv6hdr *ip6h = buf;250 struct iphdr *iph = buf;251 252 if (proto == PF_INET6) {253 memset(ip6h, 0, sizeof(*ip6h));254 255 ip6h->version = 6;256 ip6h->payload_len = htons(sizeof(struct tcphdr) + payload_len);257 ip6h->nexthdr = IPPROTO_TCP;258 ip6h->hop_limit = 8;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));265 266 iph->version = 4;267 iph->ihl = 5;268 iph->ttl = 8;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);278 }279}280 281static void fill_transportlayer(void *buf, int seq_offset, int ack_offset,282 int payload_len, int fin)283{284 struct tcphdr *tcph = buf;285 286 memset(tcph, 0, sizeof(*tcph));287 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);292 tcph->ack = 1;293 tcph->fin = fin;294 tcph->doff = 5;295 tcph->window = htons(TCP_MAXWIN);296 tcph->urg_ptr = 0;297 tcph->check = tcp_checksum(tcph, payload_len);298}299 300static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)301{302 int ret = -1;303 304 ret = sendto(fd, buf, len, 0, (struct sockaddr *)daddr, sizeof(*daddr));305 if (ret == -1)306 error(1, errno, "sendto failure");307 if (ret != len)308 error(1, errno, "sendto wrong length");309}310 311static void create_packet(void *buf, int seq_offset, int ack_offset,312 int payload_len, int fin)313{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,317 payload_len, fin);318 fill_networklayer(buf + ETH_HLEN, payload_len);319 fill_datalinklayer(buf);320}321 322/* send one extra flag, not first and not last pkt */323static void send_flags(int fd, struct sockaddr_ll *daddr, int psh, int syn,324 int rst, int urg)325{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;329 struct tcphdr *tcph;330 331 payload_len = PAYLOAD_LEN * psh;332 pkt_size = total_hdr_len + payload_len;333 flag = NUM_PACKETS / 2;334 335 create_packet(flag_buf, flag * payload_len, 0, payload_len, 0);336 337 tcph = (struct tcphdr *)(flag_buf + tcp_offset);338 tcph->psh = psh;339 tcph->syn = syn;340 tcph->rst = rst;341 tcph->urg = urg;342 tcph->check = 0;343 tcph->check = tcp_checksum(tcph, payload_len);344 345 for (i = 0; i < NUM_PACKETS + 1; i++) {346 if (i == flag) {347 write_packet(fd, flag_buf, pkt_size, daddr);348 continue;349 }350 create_packet(buf, i * PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);351 write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);352 }353}354 355/* Test for data of same length, smaller than previous356 * and of different lengths357 */358static void send_data_pkts(int fd, struct sockaddr_ll *daddr,359 int payload_len1, int payload_len2)360{361 static char buf[ETH_HLEN + IP_MAXPACKET];362 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);367}368 369/* If incoming segments make tracked segment length exceed370 * legal IP datagram length, do not coalesce371 */372static void send_large(int fd, struct sockaddr_ll *daddr, int remainder)373{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];377 int i;378 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);383 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);388}389 390/* Pure acks and dup acks don't coalesce */391static void send_ack(int fd, struct sockaddr_ll *daddr)392{393 static char buf[MAX_HDR_LEN];394 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);400}401 402static void recompute_packet(char *buf, char *no_ext, int extlen)403{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);407 408 memmove(buf, no_ext, total_hdr_len);409 memmove(buf + total_hdr_len + extlen,410 no_ext + total_hdr_len, PAYLOAD_LEN);411 412 tcphdr->doff = tcphdr->doff + (extlen / 4);413 tcphdr->check = 0;414 tcphdr->check = tcp_checksum(tcphdr, PAYLOAD_LEN + extlen);415 if (proto == PF_INET) {416 iph->tot_len = htons(ntohs(iph->tot_len) + extlen);417 iph->check = 0;418 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);419 } else {420 ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);421 }422}423 424static void tcp_write_options(char *buf, int kind, int ts)425{426 struct tcp_option_ts {427 uint8_t kind;428 uint8_t len;429 uint32_t tsval;430 uint32_t tsecr;431 } *opt_ts = (void *)buf;432 struct tcp_option_window {433 uint8_t kind;434 uint8_t len;435 uint8_t shift;436 } *opt_window = (void *)buf;437 438 switch (kind) {439 case TCPOPT_NOP:440 buf[0] = TCPOPT_NOP;441 break;442 case TCPOPT_WINDOW: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;447 break;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;452 opt_ts->tsval = ts;453 opt_ts->tsecr = 0;454 break;455 default:456 error(1, 0, "unimplemented TCP option");457 break;458 }459}460 461/* TCP with options is always a permutation of {TS, NOP, NOP}.462 * Implement different orders to verify coalescing stops.463 */464static void add_standard_tcp_options(char *buf, char *no_ext, int ts, int order)465{466 switch (order) {467 case 0: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);472 break;473 case 1: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,478 TCPOPT_NOP, 0);479 break;480 case 2:481 tcp_write_options(buf + total_hdr_len, TCPOPT_TIMESTAMP, ts);482 tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 1,483 TCPOPT_NOP, 0);484 tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 2,485 TCPOPT_NOP, 0);486 break;487 default:488 error(1, 0, "unknown order");489 break;490 }491 recompute_packet(buf, no_ext, TCPOLEN_TSTAMP_APPA);492}493 494/* Packets with invalid checksum don't coalesce. */495static void send_changed_checksum(int fd, struct sockaddr_ll *daddr)496{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;500 501 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);502 write_packet(fd, buf, pkt_size, daddr);503 504 create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);505 tcph->check = tcph->check - 1;506 write_packet(fd, buf, pkt_size, daddr);507}508 509 /* Packets with non-consecutive sequence number don't coalesce.*/510static void send_changed_seq(int fd, struct sockaddr_ll *daddr)511{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;515 516 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);517 write_packet(fd, buf, pkt_size, daddr);518 519 create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);520 tcph->seq = ntohl(htonl(tcph->seq) + 1);521 tcph->check = 0;522 tcph->check = tcp_checksum(tcph, PAYLOAD_LEN);523 write_packet(fd, buf, pkt_size, daddr);524}525 526 /* Packet with different timestamp option or different timestamps527 * don't coalesce.528 */529static void send_changed_ts(int fd, struct sockaddr_ll *daddr)530{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;534 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);538 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);542 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);546 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);550 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);554}555 556/* Packet with different tcp options don't coalesce. */557static void send_diff_opt(int fd, struct sockaddr_ll *daddr)558{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;564 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);568 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);572 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);578}579 580static void add_ipv4_ts_option(void *buf, void *optpkt)581{582 struct ip_timestamp *ts = (struct ip_timestamp *)(optpkt + tcp_offset);583 int optlen = sizeof(struct ip_timestamp);584 struct iphdr *iph;585 586 if (optlen % 4)587 error(1, 0, "ipv4 timestamp length is not a multiple of 4B");588 589 ts->ipt_code = IPOPT_TS;590 ts->ipt_len = optlen;591 ts->ipt_ptr = 5;592 ts->ipt_flg = IPOPT_TS_TSONLY;593 594 memcpy(optpkt, buf, tcp_offset);595 memcpy(optpkt + tcp_offset + optlen, buf + tcp_offset,596 sizeof(struct tcphdr) + PAYLOAD_LEN);597 598 iph = (struct iphdr *)(optpkt + ETH_HLEN);599 iph->ihl = 5 + (optlen / 4);600 iph->tot_len = htons(ntohs(iph->tot_len) + optlen);601 iph->check = 0;602 iph->check = checksum_fold(iph, sizeof(struct iphdr) + optlen, 0);603}604 605static void add_ipv6_exthdr(void *buf, void *optpkt, __u8 exthdr_type, char *ext_payload)606{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);610 611 exthdr->hdrlen = 0;612 exthdr->nexthdr = IPPROTO_TCP;613 614 memcpy(exthdr_payload_start, ext_payload, MIN_EXTHDR_SIZE - sizeof(*exthdr));615 616 memcpy(optpkt, buf, tcp_offset);617 memcpy(optpkt + tcp_offset + MIN_EXTHDR_SIZE, buf + tcp_offset,618 sizeof(struct tcphdr) + PAYLOAD_LEN);619 620 iph->nexthdr = exthdr_type;621 iph->payload_len = htons(ntohs(iph->payload_len) + MIN_EXTHDR_SIZE);622}623 624static void fix_ip4_checksum(struct iphdr *iph)625{626 iph->check = 0;627 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);628}629 630static void send_flush_id_case(int fd, struct sockaddr_ll *daddr, int tcase)631{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;636 struct iphdr *iph1;637 struct iphdr *iph2;638 struct iphdr *iph3;639 640 iph1 = (struct iphdr *)(buf1 + ETH_HLEN);641 iph2 = (struct iphdr *)(buf2 + ETH_HLEN);642 iph3 = (struct iphdr *)(buf3 + ETH_HLEN);643 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);647 648 switch (tcase) {649 case 0: /* DF=1, Incrementing - should coalesce */650 iph1->frag_off |= htons(IP_DF);651 iph1->id = htons(8);652 653 iph2->frag_off |= htons(IP_DF);654 iph2->id = htons(9);655 break;656 657 case 1: /* DF=1, Fixed - should coalesce */658 iph1->frag_off |= htons(IP_DF);659 iph1->id = htons(8);660 661 iph2->frag_off |= htons(IP_DF);662 iph2->id = htons(8);663 break;664 665 case 2: /* DF=0, Incrementing - should coalesce */666 iph1->frag_off &= ~htons(IP_DF);667 iph1->id = htons(8);668 669 iph2->frag_off &= ~htons(IP_DF);670 iph2->id = htons(9);671 break;672 673 case 3: /* DF=0, Fixed - should not coalesce */674 iph1->frag_off &= ~htons(IP_DF);675 iph1->id = htons(8);676 677 iph2->frag_off &= ~htons(IP_DF);678 iph2->id = htons(8);679 break;680 681 case 4: /* DF=1, two packets incrementing, and one fixed - should682 * coalesce only the first two packets683 */684 iph1->frag_off |= htons(IP_DF);685 iph1->id = htons(8);686 687 iph2->frag_off |= htons(IP_DF);688 iph2->id = htons(9);689 690 iph3->frag_off |= htons(IP_DF);691 iph3->id = htons(9);692 send_three = true;693 break;694 695 case 5: /* DF=1, two packets fixed, and one incrementing - should696 * coalesce only the first two packets697 */698 iph1->frag_off |= htons(IP_DF);699 iph1->id = htons(8);700 701 iph2->frag_off |= htons(IP_DF);702 iph2->id = htons(8);703 704 iph3->frag_off |= htons(IP_DF);705 iph3->id = htons(9);706 send_three = true;707 break;708 }709 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);714 715 if (send_three) {716 fix_ip4_checksum(iph3);717 write_packet(fd, buf3, total_hdr_len + PAYLOAD_LEN, daddr);718 }719}720 721static void test_flush_id(int fd, struct sockaddr_ll *daddr, char *fin_pkt)722{723 for (int i = 0; i < num_flush_id_cases; i++) {724 sleep(1);725 send_flush_id_case(fd, daddr, i);726 sleep(1);727 write_packet(fd, fin_pkt, total_hdr_len, daddr);728 }729}730 731static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr, char *ext_data1, char *ext_data2)732{733 static char buf[MAX_HDR_LEN + PAYLOAD_LEN];734 static char exthdr_pck[sizeof(buf) + MIN_EXTHDR_SIZE];735 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);739 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);743}744 745/* IPv4 options shouldn't coalesce */746static void send_ip_options(int fd, struct sockaddr_ll *daddr)747{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;752 753 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);754 write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);755 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);759 760 create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);761 write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);762}763 764/* IPv4 fragments shouldn't coalesce */765static void send_fragment4(int fd, struct sockaddr_ll *daddr)766{767 static char buf[IP_MAXPACKET];768 struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);769 int pkt_size = total_hdr_len + PAYLOAD_LEN;770 771 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);772 write_packet(fd, buf, pkt_size, daddr);773 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.777 */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);782 783 iph->frag_off = htons(0x6000); // DF = 1, MF = 1784 iph->check = 0;785 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);786 write_packet(fd, buf, pkt_size, daddr);787}788 789/* IPv4 packets with different ttl don't coalesce.*/790static void send_changed_ttl(int fd, struct sockaddr_ll *daddr)791{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);795 796 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);797 write_packet(fd, buf, pkt_size, daddr);798 799 create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);800 iph->ttl = 7;801 iph->check = 0;802 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);803 write_packet(fd, buf, pkt_size, daddr);804}805 806/* Packets with different tos don't coalesce.*/807static void send_changed_tos(int fd, struct sockaddr_ll *daddr)808{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);813 814 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);815 write_packet(fd, buf, pkt_size, daddr);816 817 create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);818 if (proto == PF_INET) {819 iph->tos = 1;820 iph->check = 0;821 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);822 } else if (proto == PF_INET6) {823 ip6h->priority = 0xf;824 }825 write_packet(fd, buf, pkt_size, daddr);826}827 828/* Packets with different ECN don't coalesce.*/829static void send_changed_ECN(int fd, struct sockaddr_ll *daddr)830{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);834 835 create_packet(buf, 0, 0, PAYLOAD_LEN, 0);836 write_packet(fd, buf, pkt_size, daddr);837 838 create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);839 if (proto == PF_INET) {840 buf[ETH_HLEN + 1] ^= 0x2; // ECN set to 10841 iph->check = 0;842 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);843 } else {844 buf[ETH_HLEN + 1] ^= 0x20; // ECN set to 10845 }846 write_packet(fd, buf, pkt_size, daddr);847}848 849/* IPv6 fragments and packets with extensions don't coalesce.*/850static void send_fragment6(int fd, struct sockaddr_ll *daddr)851{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;860 int i;861 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);865 }866 sleep(1);867 create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);868 memset(extpkt, 0, extpkt_len);869 870 ip6h->nexthdr = IPPROTO_FRAGMENT;871 ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);872 frag->ip6f_nxt = IPPROTO_TCP;873 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);878 879 create_packet(buf, PAYLOAD_LEN * 3, 0, PAYLOAD_LEN, 0);880 write_packet(fd, buf, bufpkt_len, daddr);881}882 883static void bind_packetsocket(int fd)884{885 struct sockaddr_ll daddr = {};886 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");892 893 if (bind(fd, (void *)&daddr, sizeof(daddr)) < 0)894 error(1, errno, "could not bind socket");895}896 897static void set_timeout(int fd)898{899 struct timeval timeout;900 901 timeout.tv_sec = 3;902 timeout.tv_usec = 0;903 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,904 sizeof(timeout)) < 0)905 error(1, errno, "cannot set timeout, setsockopt failed");906}907 908static void check_recv_pkts(int fd, int *correct_payload,909 int correct_num_pkts)910{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);914 struct tcphdr *tcph;915 bool bad_packet = false;916 int tcp_ext_len = 0;917 int ip_ext_len = 0;918 int pkt_size = -1;919 int data_len = 0;920 int num_pkt = 0;921 int i;922 923 vlog("Expected {");924 for (i = 0; i < correct_num_pkts; i++)925 vlog("%d ", correct_payload[i]);926 vlog("}, Total %d packets\nReceived {", correct_num_pkts);927 928 while (1) {929 ip_ext_len = 0;930 pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);931 if (pkt_size < 0)932 error(1, errno, "could not receive");933 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;938 939 tcph = (struct tcphdr *)(buffer + tcp_offset + ip_ext_len);940 941 if (tcph->fin)942 break;943 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.949 */950 if (pkt_size == ETH_ZLEN && iph->version == 4) {951 data_len = ntohs(iph->tot_len)952 - sizeof(struct tcphdr) - sizeof(struct iphdr);953 }954 vlog("%d ", data_len);955 if (data_len != correct_payload[num_pkt]) {956 vlog("[!=%d]", correct_payload[num_pkt]);957 bad_packet = true;958 }959 num_pkt++;960 }961 vlog("}, Total %d packets.\n", num_pkt);962 if (num_pkt != correct_num_pkts)963 error(1, 0, "incorrect number of packets");964 if (bad_packet)965 error(1, 0, "incorrect packet geometry");966 967 printf("Test succeeded\n\n");968}969 970static void gro_sender(void)971{972 static char fin_pkt[MAX_HDR_LEN];973 struct sockaddr_ll daddr = {};974 int txfd = -1;975 976 txfd = socket(PF_PACKET, SOCK_RAW, IPPROTO_RAW);977 if (txfd < 0)978 error(1, errno, "socket creation");979 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);988 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);992 993 send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2);994 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);995 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);1004 1005 send_flags(txfd, &daddr, 0, 1, 0, 0);1006 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1007 1008 send_flags(txfd, &daddr, 0, 0, 1, 0);1009 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1010 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);1016 1017 send_changed_seq(txfd, &daddr);1018 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1019 1020 send_changed_ts(txfd, &daddr);1021 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1022 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);1028 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 boundaries1034 * so that fin pkts are not received prior to other pkts.1035 */1036 sleep(1);1037 send_changed_ttl(txfd, &daddr);1038 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1039 1040 sleep(1);1041 send_ip_options(txfd, &daddr);1042 sleep(1);1043 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1044 1045 sleep(1);1046 send_fragment4(txfd, &daddr);1047 sleep(1);1048 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1049 1050 test_flush_id(txfd, &daddr, fin_pkt);1051 } else if (proto == PF_INET6) {1052 sleep(1);1053 send_fragment6(txfd, &daddr);1054 sleep(1);1055 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1056 1057 sleep(1);1058 /* send IPv6 packets with ext header with same payload */1059 send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);1060 sleep(1);1061 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1062 1063 sleep(1);1064 /* send IPv6 packets with ext header with different payload */1065 send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);1066 sleep(1);1067 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1068 }1069 } else if (strcmp(testname, "large") == 0) {1070 /* 20 is the difference between min iphdr size1071 * and min ipv6hdr size. Like MAX_HDR_SIZE,1072 * MAX_PAYLOAD is defined with the larger header of the two.1073 */1074 int offset = proto == PF_INET ? 20 : 0;1075 int remainder = (MAX_PAYLOAD + offset) % MSS;1076 1077 send_large(txfd, &daddr, remainder);1078 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1079 1080 send_large(txfd, &daddr, remainder + 1);1081 write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1082 } else {1083 error(1, 0, "Unknown testcase");1084 }1085 1086 if (close(txfd))1087 error(1, errno, "socket close");1088}1089 1090static void gro_receiver(void)1091{1092 static int correct_payload[NUM_PACKETS];1093 int rxfd = -1;1094 1095 rxfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_NONE));1096 if (rxfd < 0)1097 error(1, 0, "socket creation");1098 setup_sock_filter(rxfd);1099 set_timeout(rxfd);1100 bind_packetsocket(rxfd);1101 1102 memset(correct_payload, 0, sizeof(correct_payload));1103 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);1108 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);1112 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;1123 1124 printf("psh flag ends coalescing: ");1125 check_recv_pkts(rxfd, correct_payload, 2);1126 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);1132 1133 printf("rst flag ends coalescing: ");1134 check_recv_pkts(rxfd, correct_payload, 3);1135 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;1143 1144 printf("changed checksum does not coalesce: ");1145 check_recv_pkts(rxfd, correct_payload, 2);1146 1147 printf("Wrong Seq number doesn't coalesce: ");1148 check_recv_pkts(rxfd, correct_payload, 2);1149 1150 printf("Different timestamp doesn't coalesce: ");1151 correct_payload[0] = PAYLOAD_LEN * 2;1152 check_recv_pkts(rxfd, correct_payload, 4);1153 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;1160 1161 printf("different ECN doesn't coalesce: ");1162 check_recv_pkts(rxfd, correct_payload, 2);1163 1164 printf("different tos doesn't coalesce: ");1165 check_recv_pkts(rxfd, correct_payload, 2);1166 1167 if (proto == PF_INET) {1168 printf("different ttl doesn't coalesce: ");1169 check_recv_pkts(rxfd, correct_payload, 2);1170 1171 printf("ip options doesn't coalesce: ");1172 correct_payload[2] = PAYLOAD_LEN;1173 check_recv_pkts(rxfd, correct_payload, 3);1174 1175 printf("fragmented ip4 doesn't coalesce: ");1176 check_recv_pkts(rxfd, correct_payload, 2);1177 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);1182 1183 printf("DF=1, Fixed - should coalesce: ");1184 correct_payload[0] = PAYLOAD_LEN * 2;1185 check_recv_pkts(rxfd, correct_payload, 1);1186 1187 printf("DF=0, Incrementing - should coalesce: ");1188 correct_payload[0] = PAYLOAD_LEN * 2;1189 check_recv_pkts(rxfd, correct_payload, 1);1190 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);1195 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);1200 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.1208 */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);1214 1215 printf("ipv6 with ext header does coalesce: ");1216 correct_payload[0] = PAYLOAD_LEN * 2;1217 check_recv_pkts(rxfd, correct_payload, 1);1218 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);1223 }1224 } else if (strcmp(testname, "large") == 0) {1225 int offset = proto == PF_INET ? 20 : 0;1226 int remainder = (MAX_PAYLOAD + offset) % MSS;1227 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);1232 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);1238 } else {1239 error(1, 0, "Test case error, should never trigger");1240 }1241 1242 if (close(rxfd))1243 error(1, 0, "socket close");1244}1245 1246static void parse_args(int argc, char **argv)1247{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' },1259 { 0, 0, 0, 0 }1260 };1261 int c;1262 1263 while ((c = getopt_long(argc, argv, "46d:D:i:rs:S:t:v", opts, NULL)) != -1) {1264 switch (c) {1265 case '4':1266 proto = PF_INET;1267 ethhdr_proto = htons(ETH_P_IP);1268 break;1269 case '6':1270 proto = PF_INET6;1271 ethhdr_proto = htons(ETH_P_IPV6);1272 break;1273 case 'd':1274 addr4_dst = addr6_dst = optarg;1275 break;1276 case 'D':1277 dmac = optarg;1278 break;1279 case 'i':1280 ifname = optarg;1281 break;1282 case 'r':1283 tx_socket = false;1284 break;1285 case 's':1286 addr4_src = addr6_src = optarg;1287 break;1288 case 'S':1289 smac = optarg;1290 break;1291 case 't':1292 testname = optarg;1293 break;1294 case 'v':1295 verbose = true;1296 break;1297 default:1298 error(1, 0, "%s invalid option %c\n", __func__, c);1299 break;1300 }1301 }1302}1303 1304int main(int argc, char **argv)1305{1306 parse_args(argc, argv);1307 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;1314 } else {1315 error(1, 0, "Protocol family is not ipv4 or ipv6");1316 }1317 1318 read_MAC(src_mac, smac);1319 read_MAC(dst_mac, dmac);1320 1321 if (tx_socket)1322 gro_sender();1323 else1324 gro_receiver();1325 1326 fprintf(stderr, "Gro::%s test passed.\n", testname);1327 return 0;1328}1329