Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/net: fix GRO coalesce test and add ext header coalesce tests

Currently there is no test which checks that IPv6 extension header packets
successfully coalesce. This commit adds a test, which verifies two IPv6
packets with HBH extension headers do coalesce, and another test which
checks that packets with different extension header data do not coalesce
in GRO.

I changed the receive socket filter to accept a packet with one extension
header. This change exposed a bug in the fragment test -- the old BPF did
not accept the fragment packet. I updated correct_num_packets in the
fragment test accordingly.

Signed-off-by: Richard Gobert <richardbgobert@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://lore.kernel.org/r/69282fed-2415-47e8-b3d3-34939ec3eb56@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Richard Gobert and committed by
Jakub Kicinski
4e321d59 dff0b016

+87 -6
+87 -6
tools/testing/selftests/net/gro.c
··· 71 71 #define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) - sizeof(struct ipv6hdr)) 72 72 #define NUM_LARGE_PKT (MAX_PAYLOAD / MSS) 73 73 #define MAX_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct tcphdr)) 74 + #define MIN_EXTHDR_SIZE 8 75 + #define EXT_PAYLOAD_1 "\x00\x00\x00\x00\x00\x00" 76 + #define EXT_PAYLOAD_2 "\x11\x11\x11\x11\x11\x11" 77 + 78 + #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */ 79 + #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 74 80 75 81 static const char *addr6_src = "fdaa::2"; 76 82 static const char *addr6_dst = "fdaa::1"; ··· 110 104 const int dport_off = tcp_offset + offsetof(struct tcphdr, dest); 111 105 const int ethproto_off = offsetof(struct ethhdr, h_proto); 112 106 int optlen = 0; 113 - int ipproto_off; 107 + int ipproto_off, opt_ipproto_off; 114 108 int next_off; 115 109 116 110 if (proto == PF_INET) ··· 122 116 if (strcmp(testname, "ip") == 0) { 123 117 if (proto == PF_INET) 124 118 optlen = sizeof(struct ip_timestamp); 125 - else 126 - optlen = sizeof(struct ip6_frag); 119 + else { 120 + BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE); 121 + BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE); 122 + BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE); 123 + 124 + /* same size for HBH and Fragment extension header types */ 125 + optlen = MIN_EXTHDR_SIZE; 126 + opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr) 127 + + offsetof(struct ip6_ext, ip6e_nxt); 128 + } 127 129 } 128 130 131 + /* this filter validates the following: 132 + * - packet is IPv4/IPv6 according to the running test. 133 + * - packet is TCP. Also handles the case of one extension header and then TCP. 134 + * - checks the packet tcp dport equals to DPORT. Also handles the case of one 135 + * extension header and then TCP. 136 + */ 129 137 struct sock_filter filter[] = { 130 138 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, ethproto_off), 131 - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ntohs(ethhdr_proto), 0, 7), 139 + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ntohs(ethhdr_proto), 0, 9), 132 140 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ipproto_off), 141 + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 2, 0), 142 + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, opt_ipproto_off), 133 143 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 5), 134 144 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, dport_off), 135 145 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 2, 0), ··· 598 576 iph->check = checksum_fold(iph, sizeof(struct iphdr) + optlen, 0); 599 577 } 600 578 579 + static void add_ipv6_exthdr(void *buf, void *optpkt, __u8 exthdr_type, char *ext_payload) 580 + { 581 + struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr *)(optpkt + tcp_offset); 582 + struct ipv6hdr *iph = (struct ipv6hdr *)(optpkt + ETH_HLEN); 583 + char *exthdr_payload_start = (char *)(exthdr + 1); 584 + 585 + exthdr->hdrlen = 0; 586 + exthdr->nexthdr = IPPROTO_TCP; 587 + 588 + memcpy(exthdr_payload_start, ext_payload, MIN_EXTHDR_SIZE - sizeof(*exthdr)); 589 + 590 + memcpy(optpkt, buf, tcp_offset); 591 + memcpy(optpkt + tcp_offset + MIN_EXTHDR_SIZE, buf + tcp_offset, 592 + sizeof(struct tcphdr) + PAYLOAD_LEN); 593 + 594 + iph->nexthdr = exthdr_type; 595 + iph->payload_len = htons(ntohs(iph->payload_len) + MIN_EXTHDR_SIZE); 596 + } 597 + 598 + static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr, char *ext_data1, char *ext_data2) 599 + { 600 + static char buf[MAX_HDR_LEN + PAYLOAD_LEN]; 601 + static char exthdr_pck[sizeof(buf) + MIN_EXTHDR_SIZE]; 602 + 603 + create_packet(buf, 0, 0, PAYLOAD_LEN, 0); 604 + add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_HOPOPTS, ext_data1); 605 + write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + MIN_EXTHDR_SIZE, daddr); 606 + 607 + create_packet(buf, PAYLOAD_LEN * 1, 0, PAYLOAD_LEN, 0); 608 + add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_HOPOPTS, ext_data2); 609 + write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + MIN_EXTHDR_SIZE, daddr); 610 + } 611 + 601 612 /* IPv4 options shouldn't coalesce */ 602 613 static void send_ip_options(int fd, struct sockaddr_ll *daddr) 603 614 { ··· 752 697 create_packet(buf, PAYLOAD_LEN * i, 0, PAYLOAD_LEN, 0); 753 698 write_packet(fd, buf, bufpkt_len, daddr); 754 699 } 755 - 700 + sleep(1); 756 701 create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0); 757 702 memset(extpkt, 0, extpkt_len); 758 703 ··· 815 760 vlog("}, Total %d packets\nReceived {", correct_num_pkts); 816 761 817 762 while (1) { 763 + ip_ext_len = 0; 818 764 pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0); 819 765 if (pkt_size < 0) 820 766 error(1, errno, "could not receive"); ··· 823 767 if (iph->version == 4) 824 768 ip_ext_len = (iph->ihl - 5) * 4; 825 769 else if (ip6h->version == 6 && ip6h->nexthdr != IPPROTO_TCP) 826 - ip_ext_len = sizeof(struct ip6_frag); 770 + ip_ext_len = MIN_EXTHDR_SIZE; 827 771 828 772 tcph = (struct tcphdr *)(buffer + tcp_offset + ip_ext_len); 829 773 ··· 936 880 sleep(1); 937 881 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 938 882 } else if (proto == PF_INET6) { 883 + sleep(1); 939 884 send_fragment6(txfd, &daddr); 885 + sleep(1); 886 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 887 + 888 + sleep(1); 889 + /* send IPv6 packets with ext header with same payload */ 890 + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1); 891 + sleep(1); 892 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 893 + 894 + sleep(1); 895 + /* send IPv6 packets with ext header with different payload */ 896 + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2); 897 + sleep(1); 940 898 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 941 899 } 942 900 } else if (strcmp(testname, "large") == 0) { ··· 1067 997 */ 1068 998 printf("fragmented ip6 doesn't coalesce: "); 1069 999 correct_payload[0] = PAYLOAD_LEN * 2; 1000 + correct_payload[1] = PAYLOAD_LEN; 1001 + correct_payload[2] = PAYLOAD_LEN; 1002 + check_recv_pkts(rxfd, correct_payload, 3); 1003 + 1004 + printf("ipv6 with ext header does coalesce: "); 1005 + correct_payload[0] = PAYLOAD_LEN * 2; 1006 + check_recv_pkts(rxfd, correct_payload, 1); 1007 + 1008 + printf("ipv6 with ext header with different payloads doesn't coalesce: "); 1009 + correct_payload[0] = PAYLOAD_LEN; 1010 + correct_payload[1] = PAYLOAD_LEN; 1070 1011 check_recv_pkts(rxfd, correct_payload, 2); 1071 1012 } 1072 1013 } else if (strcmp(testname, "large") == 0) {