Path: blob/master/tools/testing/selftests/drivers/net/gro.c
122953 views
// SPDX-License-Identifier: GPL-2.01/*2* This testsuite provides conformance testing for GRO coalescing.3*4* Test cases:5*6* data_*:7* 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* - data_same: same size packets coalesce12* - data_lrg_sml: large then small coalesces13* - data_sml_lrg: small then large doesn't coalesce14*15* ack:16* Pure ACK does not coalesce.17*18* flags_*:19* No packets with PSH, SYN, URG, RST, CWR set will be coalesced.20* - flags_psh, flags_syn, flags_rst, flags_urg, flags_cwr21*22* tcp_*:23* Packets with incorrect checksum, non-consecutive seqno and24* different TCP header options shouldn't coalesce. Nit: given that25* some extension headers have paddings, such as timestamp, headers26* that are padded differently would not be coalesced.27* - tcp_csum: incorrect checksum28* - tcp_seq: non-consecutive sequence numbers29* - tcp_ts: different timestamps30* - tcp_opt: different TCP options31*32* ip_*:33* Packets with different (ECN, TTL, TOS) header, IP options or34* IP fragments shouldn't coalesce.35* - ip_ecn, ip_tos: shared between IPv4/IPv636* - ip_ttl, ip_opt, ip_frag4: IPv4 only37* - ip_id_df*: IPv4 IP ID field coalescing tests38* - ip_frag6, ip_v6ext_*: IPv6 only39*40* large_*:41* Packets larger than GRO_MAX_SIZE packets shouldn't coalesce.42* - large_max: exceeding max size43* - large_rem: remainder handling44*45* MSS is defined as 4096 - header because if it is too small46* (i.e. 1500 MTU - header), it will result in many packets,47* increasing the "large" test case's flakiness. This is because48* due to time sensitivity in the coalescing window, the receiver49* may not coalesce all of the packets.50*51* Note the timing issue applies to all of the test cases, so some52* flakiness is to be expected.53*54*/5556#define _GNU_SOURCE5758#include <arpa/inet.h>59#include <errno.h>60#include <error.h>61#include <getopt.h>62#include <linux/filter.h>63#include <linux/if_packet.h>64#include <linux/ipv6.h>65#include <net/ethernet.h>66#include <net/if.h>67#include <netinet/in.h>68#include <netinet/ip.h>69#include <netinet/ip6.h>70#include <netinet/tcp.h>71#include <stdbool.h>72#include <stddef.h>73#include <stdio.h>74#include <stdarg.h>75#include <string.h>76#include <unistd.h>7778#include "kselftest.h"79#include "../../net/lib/ksft.h"8081#define DPORT 800082#define SPORT 150083#define PAYLOAD_LEN 10084#define NUM_PACKETS 485#define START_SEQ 10086#define START_ACK 10087#define ETH_P_NONE 088#define TOTAL_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct tcphdr))89#define MSS (4096 - sizeof(struct tcphdr) - sizeof(struct ipv6hdr))90#define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) - sizeof(struct ipv6hdr))91#define NUM_LARGE_PKT (MAX_PAYLOAD / MSS)92#define MAX_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct tcphdr))93#define MIN_EXTHDR_SIZE 894#define EXT_PAYLOAD_1 "\x00\x00\x00\x00\x00\x00"95#define EXT_PAYLOAD_2 "\x11\x11\x11\x11\x11\x11"9697#define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */98#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))99100enum flush_id_case {101FLUSH_ID_DF1_INC,102FLUSH_ID_DF1_FIXED,103FLUSH_ID_DF0_INC,104FLUSH_ID_DF0_FIXED,105FLUSH_ID_DF1_INC_FIXED,106FLUSH_ID_DF1_FIXED_INC,107};108109static const char *addr6_src = "fdaa::2";110static const char *addr6_dst = "fdaa::1";111static const char *addr4_src = "192.168.1.200";112static const char *addr4_dst = "192.168.1.100";113static int proto = -1;114static uint8_t src_mac[ETH_ALEN], dst_mac[ETH_ALEN];115static char *testname = "data";116static char *ifname = "eth0";117static char *smac = "aa:00:00:00:00:02";118static char *dmac = "aa:00:00:00:00:01";119static bool verbose;120static bool tx_socket = true;121static int tcp_offset = -1;122static int total_hdr_len = -1;123static int ethhdr_proto = -1;124static bool ipip;125126static void vlog(const char *fmt, ...)127{128va_list args;129130if (verbose) {131va_start(args, fmt);132vfprintf(stderr, fmt, args);133va_end(args);134}135}136137static void setup_sock_filter(int fd)138{139const int dport_off = tcp_offset + offsetof(struct tcphdr, dest);140const int ethproto_off = offsetof(struct ethhdr, h_proto);141int optlen = 0;142int ipproto_off, opt_ipproto_off;143int next_off;144145if (ipip)146next_off = sizeof(struct iphdr) + offsetof(struct iphdr, protocol);147else if (proto == PF_INET)148next_off = offsetof(struct iphdr, protocol);149else150next_off = offsetof(struct ipv6hdr, nexthdr);151ipproto_off = ETH_HLEN + next_off;152153/* Overridden later if exthdrs are used: */154opt_ipproto_off = ipproto_off;155156if (strcmp(testname, "ip_opt") == 0) {157optlen = sizeof(struct ip_timestamp);158} else if (strcmp(testname, "ip_frag6") == 0 ||159strcmp(testname, "ip_v6ext_same") == 0 ||160strcmp(testname, "ip_v6ext_diff") == 0) {161BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE);162BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE);163BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE);164165/* same size for HBH and Fragment extension header types */166optlen = MIN_EXTHDR_SIZE;167opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr)168+ offsetof(struct ip6_ext, ip6e_nxt);169}170171/* this filter validates the following:172* - packet is IPv4/IPv6 according to the running test.173* - packet is TCP. Also handles the case of one extension header and then TCP.174* - checks the packet tcp dport equals to DPORT. Also handles the case of one175* extension header and then TCP.176*/177struct sock_filter filter[] = {178BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ethproto_off),179BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ntohs(ethhdr_proto), 0, 9),180BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ipproto_off),181BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 2, 0),182BPF_STMT(BPF_LD + BPF_B + BPF_ABS, opt_ipproto_off),183BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 5),184BPF_STMT(BPF_LD + BPF_H + BPF_ABS, dport_off),185BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 2, 0),186BPF_STMT(BPF_LD + BPF_H + BPF_ABS, dport_off + optlen),187BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 0, 1),188BPF_STMT(BPF_RET + BPF_K, 0xFFFFFFFF),189BPF_STMT(BPF_RET + BPF_K, 0),190};191192struct sock_fprog bpf = {193.len = ARRAY_SIZE(filter),194.filter = filter,195};196197if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)) < 0)198error(1, errno, "error setting filter");199}200201static uint32_t checksum_nofold(void *data, size_t len, uint32_t sum)202{203uint16_t *words = data;204int i;205206for (i = 0; i < len / 2; i++)207sum += words[i];208if (len & 1)209sum += ((char *)data)[len - 1];210return sum;211}212213static uint16_t checksum_fold(void *data, size_t len, uint32_t sum)214{215sum = checksum_nofold(data, len, sum);216while (sum > 0xFFFF)217sum = (sum & 0xFFFF) + (sum >> 16);218return ~sum;219}220221static uint16_t tcp_checksum(void *buf, int payload_len)222{223struct pseudo_header6 {224struct in6_addr saddr;225struct in6_addr daddr;226uint16_t protocol;227uint16_t payload_len;228} ph6;229struct pseudo_header4 {230struct in_addr saddr;231struct in_addr daddr;232uint16_t protocol;233uint16_t payload_len;234} ph4;235uint32_t sum = 0;236237if (proto == PF_INET6) {238if (inet_pton(AF_INET6, addr6_src, &ph6.saddr) != 1)239error(1, errno, "inet_pton6 source ip pseudo");240if (inet_pton(AF_INET6, addr6_dst, &ph6.daddr) != 1)241error(1, errno, "inet_pton6 dest ip pseudo");242ph6.protocol = htons(IPPROTO_TCP);243ph6.payload_len = htons(sizeof(struct tcphdr) + payload_len);244245sum = checksum_nofold(&ph6, sizeof(ph6), 0);246} else if (proto == PF_INET) {247if (inet_pton(AF_INET, addr4_src, &ph4.saddr) != 1)248error(1, errno, "inet_pton source ip pseudo");249if (inet_pton(AF_INET, addr4_dst, &ph4.daddr) != 1)250error(1, errno, "inet_pton dest ip pseudo");251ph4.protocol = htons(IPPROTO_TCP);252ph4.payload_len = htons(sizeof(struct tcphdr) + payload_len);253254sum = checksum_nofold(&ph4, sizeof(ph4), 0);255}256257return checksum_fold(buf, sizeof(struct tcphdr) + payload_len, sum);258}259260static void read_MAC(uint8_t *mac_addr, char *mac)261{262if (sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",263&mac_addr[0], &mac_addr[1], &mac_addr[2],264&mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6)265error(1, 0, "sscanf");266}267268static void fill_datalinklayer(void *buf)269{270struct ethhdr *eth = buf;271272memcpy(eth->h_dest, dst_mac, ETH_ALEN);273memcpy(eth->h_source, src_mac, ETH_ALEN);274eth->h_proto = ethhdr_proto;275}276277static void fill_networklayer(void *buf, int payload_len, int protocol)278{279struct ipv6hdr *ip6h = buf;280struct iphdr *iph = buf;281282if (proto == PF_INET6) {283memset(ip6h, 0, sizeof(*ip6h));284285ip6h->version = 6;286ip6h->payload_len = htons(sizeof(struct tcphdr) + payload_len);287ip6h->nexthdr = protocol;288ip6h->hop_limit = 8;289if (inet_pton(AF_INET6, addr6_src, &ip6h->saddr) != 1)290error(1, errno, "inet_pton source ip6");291if (inet_pton(AF_INET6, addr6_dst, &ip6h->daddr) != 1)292error(1, errno, "inet_pton dest ip6");293} else if (proto == PF_INET) {294memset(iph, 0, sizeof(*iph));295296iph->version = 4;297iph->ihl = 5;298iph->ttl = 8;299iph->protocol = protocol;300iph->tot_len = htons(sizeof(struct tcphdr) +301payload_len + sizeof(struct iphdr));302iph->frag_off = htons(0x4000); /* DF = 1, MF = 0 */303if (inet_pton(AF_INET, addr4_src, &iph->saddr) != 1)304error(1, errno, "inet_pton source ip");305if (inet_pton(AF_INET, addr4_dst, &iph->daddr) != 1)306error(1, errno, "inet_pton dest ip");307iph->check = checksum_fold(buf, sizeof(struct iphdr), 0);308}309}310311static void fill_transportlayer(void *buf, int seq_offset, int ack_offset,312int payload_len, int fin)313{314struct tcphdr *tcph = buf;315316memset(tcph, 0, sizeof(*tcph));317318tcph->source = htons(SPORT);319tcph->dest = htons(DPORT);320tcph->seq = ntohl(START_SEQ + seq_offset);321tcph->ack_seq = ntohl(START_ACK + ack_offset);322tcph->ack = 1;323tcph->fin = fin;324tcph->doff = 5;325tcph->window = htons(TCP_MAXWIN);326tcph->urg_ptr = 0;327tcph->check = tcp_checksum(tcph, payload_len);328}329330static void write_packet(int fd, char *buf, int len, struct sockaddr_ll *daddr)331{332int ret = -1;333334ret = sendto(fd, buf, len, 0, (struct sockaddr *)daddr, sizeof(*daddr));335if (ret == -1)336error(1, errno, "sendto failure");337if (ret != len)338error(1, errno, "sendto wrong length");339}340341static void create_packet(void *buf, int seq_offset, int ack_offset,342int payload_len, int fin)343{344memset(buf, 0, total_hdr_len);345memset(buf + total_hdr_len, 'a', payload_len);346347fill_transportlayer(buf + tcp_offset, seq_offset, ack_offset,348payload_len, fin);349350if (ipip) {351fill_networklayer(buf + ETH_HLEN, payload_len + sizeof(struct iphdr),352IPPROTO_IPIP);353fill_networklayer(buf + ETH_HLEN + sizeof(struct iphdr),354payload_len, IPPROTO_TCP);355} else {356fill_networklayer(buf + ETH_HLEN, payload_len, IPPROTO_TCP);357}358359fill_datalinklayer(buf);360}361362#ifndef TH_CWR363#define TH_CWR 0x80364#endif365static void set_flags(struct tcphdr *tcph, int payload_len, int psh, int syn,366int rst, int urg, int cwr)367{368tcph->psh = psh;369tcph->syn = syn;370tcph->rst = rst;371tcph->urg = urg;372if (cwr)373tcph->th_flags |= TH_CWR;374else375tcph->th_flags &= ~TH_CWR;376tcph->check = 0;377tcph->check = tcp_checksum(tcph, payload_len);378}379380/* send extra flags of the (NUM_PACKETS / 2) and (NUM_PACKETS / 2 - 1)381* pkts, not first and not last pkt382*/383static void send_flags(int fd, struct sockaddr_ll *daddr, int psh, int syn,384int rst, int urg, int cwr)385{386static char flag_buf[2][MAX_HDR_LEN + PAYLOAD_LEN];387static char buf[MAX_HDR_LEN + PAYLOAD_LEN];388int payload_len, pkt_size, i;389struct tcphdr *tcph;390int flag[2];391392payload_len = PAYLOAD_LEN * (psh || cwr);393pkt_size = total_hdr_len + payload_len;394flag[0] = NUM_PACKETS / 2;395flag[1] = NUM_PACKETS / 2 - 1;396397/* Create and configure packets with flags398*/399for (i = 0; i < 2; i++) {400if (flag[i] > 0) {401create_packet(flag_buf[i], flag[i] * payload_len, 0,402payload_len, 0);403tcph = (struct tcphdr *)(flag_buf[i] + tcp_offset);404set_flags(tcph, payload_len, psh, syn, rst, urg, cwr);405}406}407408for (i = 0; i < NUM_PACKETS + 1; i++) {409if (i == flag[0]) {410write_packet(fd, flag_buf[0], pkt_size, daddr);411continue;412} else if (i == flag[1] && cwr) {413write_packet(fd, flag_buf[1], pkt_size, daddr);414continue;415}416create_packet(buf, i * PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);417write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);418}419}420421/* Test for data of same length, smaller than previous422* and of different lengths423*/424static void send_data_pkts(int fd, struct sockaddr_ll *daddr,425int payload_len1, int payload_len2)426{427static char buf[ETH_HLEN + IP_MAXPACKET];428429create_packet(buf, 0, 0, payload_len1, 0);430write_packet(fd, buf, total_hdr_len + payload_len1, daddr);431create_packet(buf, payload_len1, 0, payload_len2, 0);432write_packet(fd, buf, total_hdr_len + payload_len2, daddr);433}434435/* If incoming segments make tracked segment length exceed436* legal IP datagram length, do not coalesce437*/438static void send_large(int fd, struct sockaddr_ll *daddr, int remainder)439{440static char pkts[NUM_LARGE_PKT][TOTAL_HDR_LEN + MSS];441static char last[TOTAL_HDR_LEN + MSS];442static char new_seg[TOTAL_HDR_LEN + MSS];443int i;444445for (i = 0; i < NUM_LARGE_PKT; i++)446create_packet(pkts[i], i * MSS, 0, MSS, 0);447create_packet(last, NUM_LARGE_PKT * MSS, 0, remainder, 0);448create_packet(new_seg, (NUM_LARGE_PKT + 1) * MSS, 0, remainder, 0);449450for (i = 0; i < NUM_LARGE_PKT; i++)451write_packet(fd, pkts[i], total_hdr_len + MSS, daddr);452write_packet(fd, last, total_hdr_len + remainder, daddr);453write_packet(fd, new_seg, total_hdr_len + remainder, daddr);454}455456/* Pure acks and dup acks don't coalesce */457static void send_ack(int fd, struct sockaddr_ll *daddr)458{459static char buf[MAX_HDR_LEN];460461create_packet(buf, 0, 0, 0, 0);462write_packet(fd, buf, total_hdr_len, daddr);463write_packet(fd, buf, total_hdr_len, daddr);464create_packet(buf, 0, 1, 0, 0);465write_packet(fd, buf, total_hdr_len, daddr);466}467468static void recompute_packet(char *buf, char *no_ext, int extlen)469{470struct tcphdr *tcphdr = (struct tcphdr *)(buf + tcp_offset);471struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);472struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);473474memmove(buf, no_ext, total_hdr_len);475memmove(buf + total_hdr_len + extlen,476no_ext + total_hdr_len, PAYLOAD_LEN);477478tcphdr->doff = tcphdr->doff + (extlen / 4);479tcphdr->check = 0;480tcphdr->check = tcp_checksum(tcphdr, PAYLOAD_LEN + extlen);481if (proto == PF_INET) {482iph->tot_len = htons(ntohs(iph->tot_len) + extlen);483iph->check = 0;484iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);485486if (ipip) {487iph += 1;488iph->tot_len = htons(ntohs(iph->tot_len) + extlen);489iph->check = 0;490iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);491}492} else {493ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);494}495}496497static void tcp_write_options(char *buf, int kind, int ts)498{499struct tcp_option_ts {500uint8_t kind;501uint8_t len;502uint32_t tsval;503uint32_t tsecr;504} *opt_ts = (void *)buf;505struct tcp_option_window {506uint8_t kind;507uint8_t len;508uint8_t shift;509} *opt_window = (void *)buf;510511switch (kind) {512case TCPOPT_NOP:513buf[0] = TCPOPT_NOP;514break;515case TCPOPT_WINDOW:516memset(opt_window, 0, sizeof(struct tcp_option_window));517opt_window->kind = TCPOPT_WINDOW;518opt_window->len = TCPOLEN_WINDOW;519opt_window->shift = 0;520break;521case TCPOPT_TIMESTAMP:522memset(opt_ts, 0, sizeof(struct tcp_option_ts));523opt_ts->kind = TCPOPT_TIMESTAMP;524opt_ts->len = TCPOLEN_TIMESTAMP;525opt_ts->tsval = ts;526opt_ts->tsecr = 0;527break;528default:529error(1, 0, "unimplemented TCP option");530break;531}532}533534/* TCP with options is always a permutation of {TS, NOP, NOP}.535* Implement different orders to verify coalescing stops.536*/537static void add_standard_tcp_options(char *buf, char *no_ext, int ts, int order)538{539switch (order) {540case 0:541tcp_write_options(buf + total_hdr_len, TCPOPT_NOP, 0);542tcp_write_options(buf + total_hdr_len + 1, TCPOPT_NOP, 0);543tcp_write_options(buf + total_hdr_len + 2 /* two NOP opts */,544TCPOPT_TIMESTAMP, ts);545break;546case 1:547tcp_write_options(buf + total_hdr_len, TCPOPT_NOP, 0);548tcp_write_options(buf + total_hdr_len + 1,549TCPOPT_TIMESTAMP, ts);550tcp_write_options(buf + total_hdr_len + 1 + TCPOLEN_TIMESTAMP,551TCPOPT_NOP, 0);552break;553case 2:554tcp_write_options(buf + total_hdr_len, TCPOPT_TIMESTAMP, ts);555tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 1,556TCPOPT_NOP, 0);557tcp_write_options(buf + total_hdr_len + TCPOLEN_TIMESTAMP + 2,558TCPOPT_NOP, 0);559break;560default:561error(1, 0, "unknown order");562break;563}564recompute_packet(buf, no_ext, TCPOLEN_TSTAMP_APPA);565}566567/* Packets with invalid checksum don't coalesce. */568static void send_changed_checksum(int fd, struct sockaddr_ll *daddr)569{570static char buf[MAX_HDR_LEN + PAYLOAD_LEN];571struct tcphdr *tcph = (struct tcphdr *)(buf + tcp_offset);572int pkt_size = total_hdr_len + PAYLOAD_LEN;573574create_packet(buf, 0, 0, PAYLOAD_LEN, 0);575write_packet(fd, buf, pkt_size, daddr);576577create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);578tcph->check = tcph->check - 1;579write_packet(fd, buf, pkt_size, daddr);580}581582/* Packets with non-consecutive sequence number don't coalesce.*/583static void send_changed_seq(int fd, struct sockaddr_ll *daddr)584{585static char buf[MAX_HDR_LEN + PAYLOAD_LEN];586struct tcphdr *tcph = (struct tcphdr *)(buf + tcp_offset);587int pkt_size = total_hdr_len + PAYLOAD_LEN;588589create_packet(buf, 0, 0, PAYLOAD_LEN, 0);590write_packet(fd, buf, pkt_size, daddr);591592create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);593tcph->seq = ntohl(htonl(tcph->seq) + 1);594tcph->check = 0;595tcph->check = tcp_checksum(tcph, PAYLOAD_LEN);596write_packet(fd, buf, pkt_size, daddr);597}598599/* Packet with different timestamp option or different timestamps600* don't coalesce.601*/602static void send_changed_ts(int fd, struct sockaddr_ll *daddr)603{604static char buf[MAX_HDR_LEN + PAYLOAD_LEN];605static char extpkt[sizeof(buf) + TCPOLEN_TSTAMP_APPA];606int pkt_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_TSTAMP_APPA;607608create_packet(buf, 0, 0, PAYLOAD_LEN, 0);609add_standard_tcp_options(extpkt, buf, 0, 0);610write_packet(fd, extpkt, pkt_size, daddr);611612create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);613add_standard_tcp_options(extpkt, buf, 0, 0);614write_packet(fd, extpkt, pkt_size, daddr);615616create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);617add_standard_tcp_options(extpkt, buf, 100, 0);618write_packet(fd, extpkt, pkt_size, daddr);619620create_packet(buf, PAYLOAD_LEN * 3, 0, PAYLOAD_LEN, 0);621add_standard_tcp_options(extpkt, buf, 100, 1);622write_packet(fd, extpkt, pkt_size, daddr);623624create_packet(buf, PAYLOAD_LEN * 4, 0, PAYLOAD_LEN, 0);625add_standard_tcp_options(extpkt, buf, 100, 2);626write_packet(fd, extpkt, pkt_size, daddr);627}628629/* Packet with different tcp options don't coalesce. */630static void send_diff_opt(int fd, struct sockaddr_ll *daddr)631{632static char buf[MAX_HDR_LEN + PAYLOAD_LEN];633static char extpkt1[sizeof(buf) + TCPOLEN_TSTAMP_APPA];634static char extpkt2[sizeof(buf) + TCPOLEN_MAXSEG];635int extpkt1_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_TSTAMP_APPA;636int extpkt2_size = total_hdr_len + PAYLOAD_LEN + TCPOLEN_MAXSEG;637638create_packet(buf, 0, 0, PAYLOAD_LEN, 0);639add_standard_tcp_options(extpkt1, buf, 0, 0);640write_packet(fd, extpkt1, extpkt1_size, daddr);641642create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);643add_standard_tcp_options(extpkt1, buf, 0, 0);644write_packet(fd, extpkt1, extpkt1_size, daddr);645646create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);647tcp_write_options(extpkt2 + MAX_HDR_LEN, TCPOPT_NOP, 0);648tcp_write_options(extpkt2 + MAX_HDR_LEN + 1, TCPOPT_WINDOW, 0);649recompute_packet(extpkt2, buf, TCPOLEN_WINDOW + 1);650write_packet(fd, extpkt2, extpkt2_size, daddr);651}652653static void add_ipv4_ts_option(void *buf, void *optpkt)654{655struct ip_timestamp *ts = (struct ip_timestamp *)(optpkt + tcp_offset);656int optlen = sizeof(struct ip_timestamp);657struct iphdr *iph;658659if (optlen % 4)660error(1, 0, "ipv4 timestamp length is not a multiple of 4B");661662ts->ipt_code = IPOPT_TS;663ts->ipt_len = optlen;664ts->ipt_ptr = 5;665ts->ipt_flg = IPOPT_TS_TSONLY;666667memcpy(optpkt, buf, tcp_offset);668memcpy(optpkt + tcp_offset + optlen, buf + tcp_offset,669sizeof(struct tcphdr) + PAYLOAD_LEN);670671iph = (struct iphdr *)(optpkt + ETH_HLEN);672iph->ihl = 5 + (optlen / 4);673iph->tot_len = htons(ntohs(iph->tot_len) + optlen);674iph->check = 0;675iph->check = checksum_fold(iph, sizeof(struct iphdr) + optlen, 0);676}677678static void add_ipv6_exthdr(void *buf, void *optpkt, __u8 exthdr_type, char *ext_payload)679{680struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr *)(optpkt + tcp_offset);681struct ipv6hdr *iph = (struct ipv6hdr *)(optpkt + ETH_HLEN);682char *exthdr_payload_start = (char *)(exthdr + 1);683684exthdr->hdrlen = 0;685exthdr->nexthdr = IPPROTO_TCP;686687memcpy(exthdr_payload_start, ext_payload, MIN_EXTHDR_SIZE - sizeof(*exthdr));688689memcpy(optpkt, buf, tcp_offset);690memcpy(optpkt + tcp_offset + MIN_EXTHDR_SIZE, buf + tcp_offset,691sizeof(struct tcphdr) + PAYLOAD_LEN);692693iph->nexthdr = exthdr_type;694iph->payload_len = htons(ntohs(iph->payload_len) + MIN_EXTHDR_SIZE);695}696697static void fix_ip4_checksum(struct iphdr *iph)698{699iph->check = 0;700iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);701}702703static void send_flush_id_case(int fd, struct sockaddr_ll *daddr,704enum flush_id_case tcase)705{706static char buf1[MAX_HDR_LEN + PAYLOAD_LEN];707static char buf2[MAX_HDR_LEN + PAYLOAD_LEN];708static char buf3[MAX_HDR_LEN + PAYLOAD_LEN];709bool send_three = false;710struct iphdr *iph1;711struct iphdr *iph2;712struct iphdr *iph3;713714iph1 = (struct iphdr *)(buf1 + ETH_HLEN);715iph2 = (struct iphdr *)(buf2 + ETH_HLEN);716iph3 = (struct iphdr *)(buf3 + ETH_HLEN);717718create_packet(buf1, 0, 0, PAYLOAD_LEN, 0);719create_packet(buf2, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);720create_packet(buf3, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);721722switch (tcase) {723case FLUSH_ID_DF1_INC: /* DF=1, Incrementing - should coalesce */724iph1->frag_off |= htons(IP_DF);725iph1->id = htons(8);726727iph2->frag_off |= htons(IP_DF);728iph2->id = htons(9);729break;730731case FLUSH_ID_DF1_FIXED: /* DF=1, Fixed - should coalesce */732iph1->frag_off |= htons(IP_DF);733iph1->id = htons(8);734735iph2->frag_off |= htons(IP_DF);736iph2->id = htons(8);737break;738739case FLUSH_ID_DF0_INC: /* DF=0, Incrementing - should coalesce */740iph1->frag_off &= ~htons(IP_DF);741iph1->id = htons(8);742743iph2->frag_off &= ~htons(IP_DF);744iph2->id = htons(9);745break;746747case FLUSH_ID_DF0_FIXED: /* DF=0, Fixed - should coalesce */748iph1->frag_off &= ~htons(IP_DF);749iph1->id = htons(8);750751iph2->frag_off &= ~htons(IP_DF);752iph2->id = htons(8);753break;754755case FLUSH_ID_DF1_INC_FIXED: /* DF=1, two packets incrementing, and756* one fixed - should coalesce only the757* first two packets758*/759iph1->frag_off |= htons(IP_DF);760iph1->id = htons(8);761762iph2->frag_off |= htons(IP_DF);763iph2->id = htons(9);764765iph3->frag_off |= htons(IP_DF);766iph3->id = htons(9);767send_three = true;768break;769770case FLUSH_ID_DF1_FIXED_INC: /* DF=1, two packets fixed, and one771* incrementing - should coalesce only772* the first two packets773*/774iph1->frag_off |= htons(IP_DF);775iph1->id = htons(8);776777iph2->frag_off |= htons(IP_DF);778iph2->id = htons(8);779780iph3->frag_off |= htons(IP_DF);781iph3->id = htons(9);782send_three = true;783break;784}785786fix_ip4_checksum(iph1);787fix_ip4_checksum(iph2);788write_packet(fd, buf1, total_hdr_len + PAYLOAD_LEN, daddr);789write_packet(fd, buf2, total_hdr_len + PAYLOAD_LEN, daddr);790791if (send_three) {792fix_ip4_checksum(iph3);793write_packet(fd, buf3, total_hdr_len + PAYLOAD_LEN, daddr);794}795}796797static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr, char *ext_data1, char *ext_data2)798{799static char buf[MAX_HDR_LEN + PAYLOAD_LEN];800static char exthdr_pck[sizeof(buf) + MIN_EXTHDR_SIZE];801802create_packet(buf, 0, 0, PAYLOAD_LEN, 0);803add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_DSTOPTS, ext_data1);804write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + MIN_EXTHDR_SIZE, daddr);805806create_packet(buf, PAYLOAD_LEN * 1, 0, PAYLOAD_LEN, 0);807add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_DSTOPTS, ext_data2);808write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + MIN_EXTHDR_SIZE, daddr);809}810811/* IPv4 options shouldn't coalesce */812static void send_ip_options(int fd, struct sockaddr_ll *daddr)813{814static char buf[MAX_HDR_LEN + PAYLOAD_LEN];815static char optpkt[sizeof(buf) + sizeof(struct ip_timestamp)];816int optlen = sizeof(struct ip_timestamp);817int pkt_size = total_hdr_len + PAYLOAD_LEN + optlen;818819create_packet(buf, 0, 0, PAYLOAD_LEN, 0);820write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);821822create_packet(buf, PAYLOAD_LEN * 1, 0, PAYLOAD_LEN, 0);823add_ipv4_ts_option(buf, optpkt);824write_packet(fd, optpkt, pkt_size, daddr);825826create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);827write_packet(fd, buf, total_hdr_len + PAYLOAD_LEN, daddr);828}829830/* IPv4 fragments shouldn't coalesce */831static void send_fragment4(int fd, struct sockaddr_ll *daddr)832{833static char buf[IP_MAXPACKET];834struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);835int pkt_size = total_hdr_len + PAYLOAD_LEN;836837create_packet(buf, 0, 0, PAYLOAD_LEN, 0);838write_packet(fd, buf, pkt_size, daddr);839840/* Once fragmented, packet would retain the total_len.841* Tcp header is prepared as if rest of data is in follow-up frags,842* but follow up frags aren't actually sent.843*/844memset(buf + total_hdr_len, 'a', PAYLOAD_LEN * 2);845fill_transportlayer(buf + tcp_offset, PAYLOAD_LEN, 0, PAYLOAD_LEN * 2, 0);846fill_networklayer(buf + ETH_HLEN, PAYLOAD_LEN, IPPROTO_TCP);847fill_datalinklayer(buf);848849iph->frag_off = htons(0x6000); // DF = 1, MF = 1850iph->check = 0;851iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);852write_packet(fd, buf, pkt_size, daddr);853}854855/* IPv4 packets with different ttl don't coalesce.*/856static void send_changed_ttl(int fd, struct sockaddr_ll *daddr)857{858int pkt_size = total_hdr_len + PAYLOAD_LEN;859static char buf[MAX_HDR_LEN + PAYLOAD_LEN];860struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);861862create_packet(buf, 0, 0, PAYLOAD_LEN, 0);863write_packet(fd, buf, pkt_size, daddr);864865create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);866iph->ttl = 7;867iph->check = 0;868iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);869write_packet(fd, buf, pkt_size, daddr);870}871872/* Packets with different tos don't coalesce.*/873static void send_changed_tos(int fd, struct sockaddr_ll *daddr)874{875int pkt_size = total_hdr_len + PAYLOAD_LEN;876static char buf[MAX_HDR_LEN + PAYLOAD_LEN];877struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);878struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);879880create_packet(buf, 0, 0, PAYLOAD_LEN, 0);881write_packet(fd, buf, pkt_size, daddr);882883create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);884if (proto == PF_INET) {885iph->tos = 1;886iph->check = 0;887iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);888} else if (proto == PF_INET6) {889ip6h->priority = 0xf;890}891write_packet(fd, buf, pkt_size, daddr);892}893894/* Packets with different ECN don't coalesce.*/895static void send_changed_ECN(int fd, struct sockaddr_ll *daddr)896{897int pkt_size = total_hdr_len + PAYLOAD_LEN;898static char buf[MAX_HDR_LEN + PAYLOAD_LEN];899struct iphdr *iph = (struct iphdr *)(buf + ETH_HLEN);900901create_packet(buf, 0, 0, PAYLOAD_LEN, 0);902write_packet(fd, buf, pkt_size, daddr);903904create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);905if (proto == PF_INET) {906buf[ETH_HLEN + 1] ^= 0x2; // ECN set to 10907iph->check = 0;908iph->check = checksum_fold(iph, sizeof(struct iphdr), 0);909} else {910buf[ETH_HLEN + 1] ^= 0x20; // ECN set to 10911}912write_packet(fd, buf, pkt_size, daddr);913}914915/* IPv6 fragments and packets with extensions don't coalesce.*/916static void send_fragment6(int fd, struct sockaddr_ll *daddr)917{918static char buf[MAX_HDR_LEN + PAYLOAD_LEN];919static char extpkt[MAX_HDR_LEN + PAYLOAD_LEN +920sizeof(struct ip6_frag)];921struct ipv6hdr *ip6h = (struct ipv6hdr *)(buf + ETH_HLEN);922struct ip6_frag *frag = (void *)(extpkt + tcp_offset);923int extlen = sizeof(struct ip6_frag);924int bufpkt_len = total_hdr_len + PAYLOAD_LEN;925int extpkt_len = bufpkt_len + extlen;926int i;927928for (i = 0; i < 2; i++) {929create_packet(buf, PAYLOAD_LEN * i, 0, PAYLOAD_LEN, 0);930write_packet(fd, buf, bufpkt_len, daddr);931}932sleep(1);933create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);934memset(extpkt, 0, extpkt_len);935936ip6h->nexthdr = IPPROTO_FRAGMENT;937ip6h->payload_len = htons(ntohs(ip6h->payload_len) + extlen);938frag->ip6f_nxt = IPPROTO_TCP;939940memcpy(extpkt, buf, tcp_offset);941memcpy(extpkt + tcp_offset + extlen, buf + tcp_offset,942sizeof(struct tcphdr) + PAYLOAD_LEN);943write_packet(fd, extpkt, extpkt_len, daddr);944945create_packet(buf, PAYLOAD_LEN * 3, 0, PAYLOAD_LEN, 0);946write_packet(fd, buf, bufpkt_len, daddr);947}948949static void bind_packetsocket(int fd)950{951struct sockaddr_ll daddr = {};952953daddr.sll_family = AF_PACKET;954daddr.sll_protocol = ethhdr_proto;955daddr.sll_ifindex = if_nametoindex(ifname);956if (daddr.sll_ifindex == 0)957error(1, errno, "if_nametoindex");958959if (bind(fd, (void *)&daddr, sizeof(daddr)) < 0)960error(1, errno, "could not bind socket");961}962963static void set_timeout(int fd)964{965struct timeval timeout;966967timeout.tv_sec = 3;968timeout.tv_usec = 0;969if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,970sizeof(timeout)) < 0)971error(1, errno, "cannot set timeout, setsockopt failed");972}973974static void set_rcvbuf(int fd)975{976int bufsize = 1 * 1024 * 1024; /* 1 MB */977978if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)))979error(1, errno, "cannot set rcvbuf size, setsockopt failed");980}981982static void recv_error(int fd, int rcv_errno)983{984struct tpacket_stats stats;985socklen_t len;986987len = sizeof(stats);988if (getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len))989error(1, errno, "can't get stats");990991fprintf(stderr, "Socket stats: packets=%u, drops=%u\n",992stats.tp_packets, stats.tp_drops);993error(1, rcv_errno, "could not receive");994}995996static void check_recv_pkts(int fd, int *correct_payload,997int correct_num_pkts)998{999static char buffer[IP_MAXPACKET + ETH_HLEN + 1];1000struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN);1001struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN);1002struct tcphdr *tcph;1003bool bad_packet = false;1004int tcp_ext_len = 0;1005int ip_ext_len = 0;1006int pkt_size = -1;1007int data_len = 0;1008int num_pkt = 0;1009int i;10101011vlog("Expected {");1012for (i = 0; i < correct_num_pkts; i++)1013vlog("%d ", correct_payload[i]);1014vlog("}, Total %d packets\nReceived {", correct_num_pkts);10151016while (1) {1017ip_ext_len = 0;1018pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);1019if (pkt_size < 0)1020recv_error(fd, errno);10211022if (iph->version == 4)1023ip_ext_len = (iph->ihl - 5) * 4;1024else if (ip6h->version == 6 && ip6h->nexthdr != IPPROTO_TCP)1025ip_ext_len = MIN_EXTHDR_SIZE;10261027tcph = (struct tcphdr *)(buffer + tcp_offset + ip_ext_len);10281029if (tcph->fin)1030break;10311032tcp_ext_len = (tcph->doff - 5) * 4;1033data_len = pkt_size - total_hdr_len - tcp_ext_len - ip_ext_len;1034/* Min ethernet frame payload is 46(ETH_ZLEN - ETH_HLEN) by RFC 802.3.1035* Ipv4/tcp packets without at least 6 bytes of data will be padded.1036* Packet sockets are protocol agnostic, and will not trim the padding.1037*/1038if (pkt_size == ETH_ZLEN && iph->version == 4) {1039data_len = ntohs(iph->tot_len)1040- sizeof(struct tcphdr) - sizeof(struct iphdr);1041}1042vlog("%d ", data_len);1043if (data_len != correct_payload[num_pkt]) {1044vlog("[!=%d]", correct_payload[num_pkt]);1045bad_packet = true;1046}1047num_pkt++;1048}1049vlog("}, Total %d packets.\n", num_pkt);1050if (num_pkt != correct_num_pkts)1051error(1, 0, "incorrect number of packets");1052if (bad_packet)1053error(1, 0, "incorrect packet geometry");10541055printf("Test succeeded\n\n");1056}10571058static void gro_sender(void)1059{1060const int fin_delay_us = 100 * 1000;1061static char fin_pkt[MAX_HDR_LEN];1062struct sockaddr_ll daddr = {};1063int txfd = -1;10641065txfd = socket(PF_PACKET, SOCK_RAW, IPPROTO_RAW);1066if (txfd < 0)1067error(1, errno, "socket creation");10681069memset(&daddr, 0, sizeof(daddr));1070daddr.sll_ifindex = if_nametoindex(ifname);1071if (daddr.sll_ifindex == 0)1072error(1, errno, "if_nametoindex");1073daddr.sll_family = AF_PACKET;1074memcpy(daddr.sll_addr, dst_mac, ETH_ALEN);1075daddr.sll_halen = ETH_ALEN;1076create_packet(fin_pkt, PAYLOAD_LEN * 2, 0, 0, 1);10771078/* data sub-tests */1079if (strcmp(testname, "data_same") == 0) {1080send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN);1081write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1082} else if (strcmp(testname, "data_lrg_sml") == 0) {1083send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2);1084write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1085} else if (strcmp(testname, "data_sml_lrg") == 0) {1086send_data_pkts(txfd, &daddr, PAYLOAD_LEN / 2, PAYLOAD_LEN);1087write_packet(txfd, fin_pkt, total_hdr_len, &daddr);10881089/* ack test */1090} else if (strcmp(testname, "ack") == 0) {1091send_ack(txfd, &daddr);1092write_packet(txfd, fin_pkt, total_hdr_len, &daddr);10931094/* flags sub-tests */1095} else if (strcmp(testname, "flags_psh") == 0) {1096send_flags(txfd, &daddr, 1, 0, 0, 0, 0);1097write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1098} else if (strcmp(testname, "flags_syn") == 0) {1099send_flags(txfd, &daddr, 0, 1, 0, 0, 0);1100write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1101} else if (strcmp(testname, "flags_rst") == 0) {1102send_flags(txfd, &daddr, 0, 0, 1, 0, 0);1103write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1104} else if (strcmp(testname, "flags_urg") == 0) {1105send_flags(txfd, &daddr, 0, 0, 0, 1, 0);1106write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1107} else if (strcmp(testname, "flags_cwr") == 0) {1108send_flags(txfd, &daddr, 0, 0, 0, 0, 1);1109write_packet(txfd, fin_pkt, total_hdr_len, &daddr);11101111/* tcp sub-tests */1112} else if (strcmp(testname, "tcp_csum") == 0) {1113send_changed_checksum(txfd, &daddr);1114usleep(fin_delay_us);1115write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1116} else if (strcmp(testname, "tcp_seq") == 0) {1117send_changed_seq(txfd, &daddr);1118usleep(fin_delay_us);1119write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1120} else if (strcmp(testname, "tcp_ts") == 0) {1121send_changed_ts(txfd, &daddr);1122usleep(fin_delay_us);1123write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1124} else if (strcmp(testname, "tcp_opt") == 0) {1125send_diff_opt(txfd, &daddr);1126usleep(fin_delay_us);1127write_packet(txfd, fin_pkt, total_hdr_len, &daddr);11281129/* ip sub-tests - shared between IPv4 and IPv6 */1130} else if (strcmp(testname, "ip_ecn") == 0) {1131send_changed_ECN(txfd, &daddr);1132write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1133} else if (strcmp(testname, "ip_tos") == 0) {1134send_changed_tos(txfd, &daddr);1135write_packet(txfd, fin_pkt, total_hdr_len, &daddr);11361137/* ip sub-tests - IPv4 only */1138} else if (strcmp(testname, "ip_ttl") == 0) {1139send_changed_ttl(txfd, &daddr);1140write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1141} else if (strcmp(testname, "ip_opt") == 0) {1142send_ip_options(txfd, &daddr);1143usleep(fin_delay_us);1144write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1145} else if (strcmp(testname, "ip_frag4") == 0) {1146send_fragment4(txfd, &daddr);1147usleep(fin_delay_us);1148write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1149} else if (strcmp(testname, "ip_id_df1_inc") == 0) {1150send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_INC);1151usleep(fin_delay_us);1152write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1153} else if (strcmp(testname, "ip_id_df1_fixed") == 0) {1154send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_FIXED);1155usleep(fin_delay_us);1156write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1157} else if (strcmp(testname, "ip_id_df0_inc") == 0) {1158send_flush_id_case(txfd, &daddr, FLUSH_ID_DF0_INC);1159usleep(fin_delay_us);1160write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1161} else if (strcmp(testname, "ip_id_df0_fixed") == 0) {1162send_flush_id_case(txfd, &daddr, FLUSH_ID_DF0_FIXED);1163usleep(fin_delay_us);1164write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1165} else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {1166send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_INC_FIXED);1167usleep(fin_delay_us);1168write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1169} else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {1170send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_FIXED_INC);1171usleep(fin_delay_us);1172write_packet(txfd, fin_pkt, total_hdr_len, &daddr);11731174/* ip sub-tests - IPv6 only */1175} else if (strcmp(testname, "ip_frag6") == 0) {1176send_fragment6(txfd, &daddr);1177usleep(fin_delay_us);1178write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1179} else if (strcmp(testname, "ip_v6ext_same") == 0) {1180send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1);1181usleep(fin_delay_us);1182write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1183} else if (strcmp(testname, "ip_v6ext_diff") == 0) {1184send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2);1185usleep(fin_delay_us);1186write_packet(txfd, fin_pkt, total_hdr_len, &daddr);11871188/* large sub-tests */1189} else if (strcmp(testname, "large_max") == 0) {1190int offset = (proto == PF_INET && !ipip) ? 20 : 0;1191int remainder = (MAX_PAYLOAD + offset) % MSS;11921193send_large(txfd, &daddr, remainder);1194write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1195} else if (strcmp(testname, "large_rem") == 0) {1196int offset = (proto == PF_INET && !ipip) ? 20 : 0;1197int remainder = (MAX_PAYLOAD + offset) % MSS;11981199send_large(txfd, &daddr, remainder + 1);1200write_packet(txfd, fin_pkt, total_hdr_len, &daddr);1201} else {1202error(1, 0, "Unknown testcase: %s", testname);1203}12041205if (close(txfd))1206error(1, errno, "socket close");1207}12081209static void gro_receiver(void)1210{1211static int correct_payload[NUM_PACKETS];1212int rxfd = -1;12131214rxfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_NONE));1215if (rxfd < 0)1216error(1, 0, "socket creation");1217setup_sock_filter(rxfd);1218set_timeout(rxfd);1219set_rcvbuf(rxfd);1220bind_packetsocket(rxfd);12211222ksft_ready();12231224memset(correct_payload, 0, sizeof(correct_payload));12251226/* data sub-tests */1227if (strcmp(testname, "data_same") == 0) {1228printf("pure data packet of same size: ");1229correct_payload[0] = PAYLOAD_LEN * 2;1230check_recv_pkts(rxfd, correct_payload, 1);1231} else if (strcmp(testname, "data_lrg_sml") == 0) {1232printf("large data packets followed by a smaller one: ");1233correct_payload[0] = PAYLOAD_LEN * 1.5;1234check_recv_pkts(rxfd, correct_payload, 1);1235} else if (strcmp(testname, "data_sml_lrg") == 0) {1236printf("small data packets followed by a larger one: ");1237correct_payload[0] = PAYLOAD_LEN / 2;1238correct_payload[1] = PAYLOAD_LEN;1239check_recv_pkts(rxfd, correct_payload, 2);12401241/* ack test */1242} else if (strcmp(testname, "ack") == 0) {1243printf("duplicate ack and pure ack: ");1244check_recv_pkts(rxfd, correct_payload, 3);12451246/* flags sub-tests */1247} else if (strcmp(testname, "flags_psh") == 0) {1248correct_payload[0] = PAYLOAD_LEN * 3;1249correct_payload[1] = PAYLOAD_LEN * 2;1250printf("psh flag ends coalescing: ");1251check_recv_pkts(rxfd, correct_payload, 2);1252} else if (strcmp(testname, "flags_syn") == 0) {1253correct_payload[0] = PAYLOAD_LEN * 2;1254correct_payload[1] = 0;1255correct_payload[2] = PAYLOAD_LEN * 2;1256printf("syn flag ends coalescing: ");1257check_recv_pkts(rxfd, correct_payload, 3);1258} else if (strcmp(testname, "flags_rst") == 0) {1259correct_payload[0] = PAYLOAD_LEN * 2;1260correct_payload[1] = 0;1261correct_payload[2] = PAYLOAD_LEN * 2;1262printf("rst flag ends coalescing: ");1263check_recv_pkts(rxfd, correct_payload, 3);1264} else if (strcmp(testname, "flags_urg") == 0) {1265correct_payload[0] = PAYLOAD_LEN * 2;1266correct_payload[1] = 0;1267correct_payload[2] = PAYLOAD_LEN * 2;1268printf("urg flag ends coalescing: ");1269check_recv_pkts(rxfd, correct_payload, 3);1270} else if (strcmp(testname, "flags_cwr") == 0) {1271correct_payload[0] = PAYLOAD_LEN;1272correct_payload[1] = PAYLOAD_LEN * 2;1273correct_payload[2] = PAYLOAD_LEN * 2;1274printf("cwr flag ends coalescing: ");1275check_recv_pkts(rxfd, correct_payload, 3);12761277/* tcp sub-tests */1278} else if (strcmp(testname, "tcp_csum") == 0) {1279correct_payload[0] = PAYLOAD_LEN;1280correct_payload[1] = PAYLOAD_LEN;1281printf("changed checksum does not coalesce: ");1282check_recv_pkts(rxfd, correct_payload, 2);1283} else if (strcmp(testname, "tcp_seq") == 0) {1284correct_payload[0] = PAYLOAD_LEN;1285correct_payload[1] = PAYLOAD_LEN;1286printf("Wrong Seq number doesn't coalesce: ");1287check_recv_pkts(rxfd, correct_payload, 2);1288} else if (strcmp(testname, "tcp_ts") == 0) {1289correct_payload[0] = PAYLOAD_LEN * 2;1290correct_payload[1] = PAYLOAD_LEN;1291correct_payload[2] = PAYLOAD_LEN;1292correct_payload[3] = PAYLOAD_LEN;1293printf("Different timestamp doesn't coalesce: ");1294check_recv_pkts(rxfd, correct_payload, 4);1295} else if (strcmp(testname, "tcp_opt") == 0) {1296correct_payload[0] = PAYLOAD_LEN * 2;1297correct_payload[1] = PAYLOAD_LEN;1298printf("Different options doesn't coalesce: ");1299check_recv_pkts(rxfd, correct_payload, 2);13001301/* ip sub-tests - shared between IPv4 and IPv6 */1302} else if (strcmp(testname, "ip_ecn") == 0) {1303correct_payload[0] = PAYLOAD_LEN;1304correct_payload[1] = PAYLOAD_LEN;1305printf("different ECN doesn't coalesce: ");1306check_recv_pkts(rxfd, correct_payload, 2);1307} else if (strcmp(testname, "ip_tos") == 0) {1308correct_payload[0] = PAYLOAD_LEN;1309correct_payload[1] = PAYLOAD_LEN;1310printf("different tos doesn't coalesce: ");1311check_recv_pkts(rxfd, correct_payload, 2);13121313/* ip sub-tests - IPv4 only */1314} else if (strcmp(testname, "ip_ttl") == 0) {1315correct_payload[0] = PAYLOAD_LEN;1316correct_payload[1] = PAYLOAD_LEN;1317printf("different ttl doesn't coalesce: ");1318check_recv_pkts(rxfd, correct_payload, 2);1319} else if (strcmp(testname, "ip_opt") == 0) {1320correct_payload[0] = PAYLOAD_LEN;1321correct_payload[1] = PAYLOAD_LEN;1322correct_payload[2] = PAYLOAD_LEN;1323printf("ip options doesn't coalesce: ");1324check_recv_pkts(rxfd, correct_payload, 3);1325} else if (strcmp(testname, "ip_frag4") == 0) {1326correct_payload[0] = PAYLOAD_LEN;1327correct_payload[1] = PAYLOAD_LEN;1328printf("fragmented ip4 doesn't coalesce: ");1329check_recv_pkts(rxfd, correct_payload, 2);1330} else if (strcmp(testname, "ip_id_df1_inc") == 0) {1331printf("DF=1, Incrementing - should coalesce: ");1332correct_payload[0] = PAYLOAD_LEN * 2;1333check_recv_pkts(rxfd, correct_payload, 1);1334} else if (strcmp(testname, "ip_id_df1_fixed") == 0) {1335printf("DF=1, Fixed - should coalesce: ");1336correct_payload[0] = PAYLOAD_LEN * 2;1337check_recv_pkts(rxfd, correct_payload, 1);1338} else if (strcmp(testname, "ip_id_df0_inc") == 0) {1339printf("DF=0, Incrementing - should coalesce: ");1340correct_payload[0] = PAYLOAD_LEN * 2;1341check_recv_pkts(rxfd, correct_payload, 1);1342} else if (strcmp(testname, "ip_id_df0_fixed") == 0) {1343printf("DF=0, Fixed - should coalesce: ");1344correct_payload[0] = PAYLOAD_LEN * 2;1345check_recv_pkts(rxfd, correct_payload, 1);1346} else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) {1347printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: ");1348correct_payload[0] = PAYLOAD_LEN * 2;1349correct_payload[1] = PAYLOAD_LEN;1350check_recv_pkts(rxfd, correct_payload, 2);1351} else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) {1352printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: ");1353correct_payload[0] = PAYLOAD_LEN * 2;1354correct_payload[1] = PAYLOAD_LEN;1355check_recv_pkts(rxfd, correct_payload, 2);13561357/* ip sub-tests - IPv6 only */1358} else if (strcmp(testname, "ip_frag6") == 0) {1359/* GRO doesn't check for ipv6 hop limit when flushing.1360* Hence no corresponding test to the ipv4 case.1361*/1362printf("fragmented ip6 doesn't coalesce: ");1363correct_payload[0] = PAYLOAD_LEN * 2;1364correct_payload[1] = PAYLOAD_LEN;1365correct_payload[2] = PAYLOAD_LEN;1366check_recv_pkts(rxfd, correct_payload, 3);1367} else if (strcmp(testname, "ip_v6ext_same") == 0) {1368printf("ipv6 with ext header does coalesce: ");1369correct_payload[0] = PAYLOAD_LEN * 2;1370check_recv_pkts(rxfd, correct_payload, 1);1371} else if (strcmp(testname, "ip_v6ext_diff") == 0) {1372printf("ipv6 with ext header with different payloads doesn't coalesce: ");1373correct_payload[0] = PAYLOAD_LEN;1374correct_payload[1] = PAYLOAD_LEN;1375check_recv_pkts(rxfd, correct_payload, 2);13761377/* large sub-tests */1378} else if (strcmp(testname, "large_max") == 0) {1379int offset = (proto == PF_INET && !ipip) ? 20 : 0;1380int remainder = (MAX_PAYLOAD + offset) % MSS;13811382correct_payload[0] = (MAX_PAYLOAD + offset);1383correct_payload[1] = remainder;1384printf("Shouldn't coalesce if exceed IP max pkt size: ");1385check_recv_pkts(rxfd, correct_payload, 2);1386} else if (strcmp(testname, "large_rem") == 0) {1387int offset = (proto == PF_INET && !ipip) ? 20 : 0;1388int remainder = (MAX_PAYLOAD + offset) % MSS;13891390/* last segment sent individually, doesn't start new segment */1391correct_payload[0] = (MAX_PAYLOAD + offset) - remainder;1392correct_payload[1] = remainder + 1;1393correct_payload[2] = remainder + 1;1394printf("last segment sent individually: ");1395check_recv_pkts(rxfd, correct_payload, 3);1396} else {1397error(1, 0, "Test case error: unknown testname %s", testname);1398}13991400if (close(rxfd))1401error(1, 0, "socket close");1402}14031404static void parse_args(int argc, char **argv)1405{1406static const struct option opts[] = {1407{ "daddr", required_argument, NULL, 'd' },1408{ "dmac", required_argument, NULL, 'D' },1409{ "iface", required_argument, NULL, 'i' },1410{ "ipv4", no_argument, NULL, '4' },1411{ "ipv6", no_argument, NULL, '6' },1412{ "ipip", no_argument, NULL, 'e' },1413{ "rx", no_argument, NULL, 'r' },1414{ "saddr", required_argument, NULL, 's' },1415{ "smac", required_argument, NULL, 'S' },1416{ "test", required_argument, NULL, 't' },1417{ "verbose", no_argument, NULL, 'v' },1418{ 0, 0, 0, 0 }1419};1420int c;14211422while ((c = getopt_long(argc, argv, "46d:D:ei:rs:S:t:v", opts, NULL)) != -1) {1423switch (c) {1424case '4':1425proto = PF_INET;1426ethhdr_proto = htons(ETH_P_IP);1427break;1428case '6':1429proto = PF_INET6;1430ethhdr_proto = htons(ETH_P_IPV6);1431break;1432case 'e':1433ipip = true;1434proto = PF_INET;1435ethhdr_proto = htons(ETH_P_IP);1436break;1437case 'd':1438addr4_dst = addr6_dst = optarg;1439break;1440case 'D':1441dmac = optarg;1442break;1443case 'i':1444ifname = optarg;1445break;1446case 'r':1447tx_socket = false;1448break;1449case 's':1450addr4_src = addr6_src = optarg;1451break;1452case 'S':1453smac = optarg;1454break;1455case 't':1456testname = optarg;1457break;1458case 'v':1459verbose = true;1460break;1461default:1462error(1, 0, "%s invalid option %c\n", __func__, c);1463break;1464}1465}1466}14671468int main(int argc, char **argv)1469{1470parse_args(argc, argv);14711472if (ipip) {1473tcp_offset = ETH_HLEN + sizeof(struct iphdr) * 2;1474total_hdr_len = tcp_offset + sizeof(struct tcphdr);1475} else if (proto == PF_INET) {1476tcp_offset = ETH_HLEN + sizeof(struct iphdr);1477total_hdr_len = tcp_offset + sizeof(struct tcphdr);1478} else if (proto == PF_INET6) {1479tcp_offset = ETH_HLEN + sizeof(struct ipv6hdr);1480total_hdr_len = MAX_HDR_LEN;1481} else {1482error(1, 0, "Protocol family is not ipv4 or ipv6");1483}14841485read_MAC(src_mac, smac);1486read_MAC(dst_mac, dmac);14871488if (tx_socket) {1489gro_sender();1490} else {1491/* Only the receiver exit status determines test success. */1492gro_receiver();1493fprintf(stderr, "Gro::%s test passed.\n", testname);1494}14951496return 0;1497}149814991500