Richard Gobert wrote:
> 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.
> 
> 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 <[email protected]>

Thanks for adding test coverage along with the feature, and as part
of the existing gro test too.

> ---
>  tools/testing/selftests/net/gro.c | 78 ++++++++++++++++++++++++++++---
>  1 file changed, 71 insertions(+), 7 deletions(-)
> 
> diff --git a/tools/testing/selftests/net/gro.c 
> b/tools/testing/selftests/net/gro.c
> index 30024d0ed373..4ee34dca8e5f 100644
> --- a/tools/testing/selftests/net/gro.c
> +++ b/tools/testing/selftests/net/gro.c
> @@ -71,6 +71,10 @@
>  #define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) - sizeof(struct 
> ipv6hdr))
>  #define NUM_LARGE_PKT (MAX_PAYLOAD / MSS)
>  #define MAX_HDR_LEN (ETH_HLEN + sizeof(struct ipv6hdr) + sizeof(struct 
> tcphdr))
> +#define MIN_EXTHDR_SIZE 8   /* minimum size of IPv6 extension header */
> +
> +#define ipv6_optlen(p)  (((p)->hdrlen+1) << 3) /* calculate IPv6 extension 
> header len */
> +
>  
>  static const char *addr6_src = "fdaa::2";
>  static const char *addr6_dst = "fdaa::1";
> @@ -104,7 +108,7 @@ static void setup_sock_filter(int fd)
>       const int dport_off = tcp_offset + offsetof(struct tcphdr, dest);
>       const int ethproto_off = offsetof(struct ethhdr, h_proto);
>       int optlen = 0;
> -     int ipproto_off;
> +     int ipproto_off, opt_ipproto_off;
>       int next_off;
>  
>       if (proto == PF_INET)
> @@ -116,14 +120,27 @@ static void setup_sock_filter(int fd)
>       if (strcmp(testname, "ip") == 0) {
>               if (proto == PF_INET)
>                       optlen = sizeof(struct ip_timestamp);
> -             else
> -                     optlen = sizeof(struct ip6_frag);
> +             else {
> +                     // same size for HBH and Fragment extension header types

no C++ style comments

Also, instead of comments here and at the MIN_EXTHDR_SIZE definition,
perhaps cleaner as self documenting code:

    BUILD_BUG_ON(MIN_EXTHDR_SIZE != sizeof(struct ip6_hbh));
    BUILD_BUG_ON(MIN_EXTHDR_SIZE != sizeof(struct ip6_dest));
    BUILD_BUG_ON(MIN_EXTHDR_SIZE < sizeof(struct ip6_frag));

> +                     optlen = MIN_EXTHDR_SIZE;
> +                     opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr) +
> +                             offsetof(struct ip6_ext, ip6e_nxt);
> +             }
>       }
>  
> +     /*
> +      * this filter validates the following:
> +      *      - packet is IPv4/IPv6 according to the running test.
> +      *      - packet is TCP. Also handles the case of one extension header 
> and then TCP.
> +      *      - checks the packet tcp dport equals to DPORT.
> +      *     (also handles the case of one extension header and then TCP.)
> +      */

nit: for filewide consistency: netdev style:

       /* start comment
        * more comment
        */

>       struct sock_filter filter[] = {
>                       BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, ethproto_off),
> -                     BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 
> ntohs(ethhdr_proto), 0, 7),
> +                     BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 
> ntohs(ethhdr_proto), 0, 9),
>                       BPF_STMT(BPF_LD  + BPF_B   + BPF_ABS, ipproto_off),
> +                     BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 2, 0),
> +                     BPF_STMT(BPF_LD  + BPF_B   + BPF_ABS, opt_ipproto_off),
>                       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_TCP, 0, 5),
>                       BPF_STMT(BPF_LD  + BPF_H   + BPF_ABS, dport_off),
>                       BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DPORT, 2, 0),
> @@ -576,6 +593,39 @@ static void add_ipv4_ts_option(void *buf, void *optpkt)
>       iph->check = checksum_fold(iph, sizeof(struct iphdr) + optlen, 0);
>  }
>  
> +static void add_ipv6_exthdr(void *buf, void *optpkt, __u8 exthdr_type)
> +{
> +     struct ipv6_opt_hdr *hbh_hdr = (struct ipv6_opt_hdr *)(optpkt + 
> tcp_offset);
> +     struct ipv6hdr *iph = (struct ipv6hdr *)(optpkt + ETH_HLEN);
> +     int opt_len;
> +
> +     hbh_hdr->hdrlen = 0;
> +     hbh_hdr->nexthdr = IPPROTO_TCP;
> +     opt_len = ipv6_optlen(hbh_hdr);
> +
> +     memcpy(optpkt, buf, tcp_offset);
> +     memcpy(optpkt + tcp_offset + MIN_EXTHDR_SIZE, buf + tcp_offset,
> +             sizeof(struct tcphdr) + PAYLOAD_LEN);
> +
> +     iph->nexthdr = exthdr_type;
> +     iph->payload_len = htons(ntohs(iph->payload_len) + MIN_EXTHDR_SIZE);
> +}
> +
> +/* Send IPv6 packet with extension header */
> +static void send_ipv6_exthdr(int fd, struct sockaddr_ll *daddr)
> +{
> +     static char buf[MAX_HDR_LEN + PAYLOAD_LEN];
> +     static char exthdr_pck[sizeof(buf) + MIN_EXTHDR_SIZE];
> +
> +     create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
> +     add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_HOPOPTS);
> +     write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + 
> MIN_EXTHDR_SIZE, daddr);
> +
> +     create_packet(buf, PAYLOAD_LEN * 1, 0, PAYLOAD_LEN, 0);
> +     add_ipv6_exthdr(buf, exthdr_pck, IPPROTO_HOPOPTS);
> +     write_packet(fd, exthdr_pck, total_hdr_len + PAYLOAD_LEN + 
> MIN_EXTHDR_SIZE, daddr);
> +}
> +
>  /* IPv4 options shouldn't coalesce */
>  static void send_ip_options(int fd, struct sockaddr_ll *daddr)
>  {
> @@ -697,7 +747,7 @@ static void send_fragment6(int fd, struct sockaddr_ll 
> *daddr)
>               create_packet(buf, PAYLOAD_LEN * i, 0, PAYLOAD_LEN, 0);
>               write_packet(fd, buf, bufpkt_len, daddr);
>       }
> -
> +     sleep(1);

Leftover debug, or necessary fix to existing test?
>       create_packet(buf, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0);
>       memset(extpkt, 0, extpkt_len);
>  
> @@ -760,6 +810,7 @@ static void check_recv_pkts(int fd, int *correct_payload,
>       vlog("}, Total %d packets\nReceived {", correct_num_pkts);
>  
>       while (1) {
> +             ip_ext_len = 0;
>               pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);
>               if (pkt_size < 0)
>                       error(1, errno, "could not receive");
> @@ -767,7 +818,7 @@ static void check_recv_pkts(int fd, int *correct_payload,
>               if (iph->version == 4)
>                       ip_ext_len = (iph->ihl - 5) * 4;
>               else if (ip6h->version == 6 && ip6h->nexthdr != IPPROTO_TCP)
> -                     ip_ext_len = sizeof(struct ip6_frag);
> +                     ip_ext_len = MIN_EXTHDR_SIZE;

struct ip6_frag is > MIN_EXTHDR_SIZE. Need both cases?

        else if (ip6h->version == 6 && ip6h->nexthdr == NEXTHDR_FRAGMENT
                ip_ext_len = sizeof(struct ip6_frag);
        else if (ip6h->version == 6 && ip6h->nexthdr != IPPROTO_TCP)
                ip_ext_len = MIN_EXTHDR_SIZE;

>  
>               tcph = (struct tcphdr *)(buffer + tcp_offset + ip_ext_len);
>  
> @@ -880,7 +931,14 @@ static void gro_sender(void)
>                       sleep(1);
>                       write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>               } else if (proto == PF_INET6) {
> +                     sleep(1);
>                       send_fragment6(txfd, &daddr);
> +                     sleep(1);
> +                     write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
> +
> +                     sleep(1);
> +                     send_ipv6_exthdr(txfd, &daddr);
> +                     sleep(1);

For the casual reader: these sleeps are not leftover debug statements
unfortunately. The same exists in the PF_INET branch:

        /* Modified packets may be received out of order.
         * Sleep function added to enforce test boundaries
         * so that fin pkts are not received prior to other pkts.
         */

>                       write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
>               }
>       } else if (strcmp(testname, "large") == 0) {
> @@ -997,7 +1055,13 @@ static void gro_receiver(void)
>                        */
>                       printf("fragmented ip6 doesn't coalesce: ");
>                       correct_payload[0] = PAYLOAD_LEN * 2;
> -                     check_recv_pkts(rxfd, correct_payload, 2);
> +                     correct_payload[1] = PAYLOAD_LEN;
> +                     correct_payload[2] = PAYLOAD_LEN;
> +                     check_recv_pkts(rxfd, correct_payload, 3);
> +
> +                     correct_payload[0] = PAYLOAD_LEN * 2;
> +                     printf("ipv6 with extension header DOES coalesce: ");
> +                     check_recv_pkts(rxfd, correct_payload, 1);

It might be worth adding a test with two different extension headers.
To verify that these should not coalesce.

Either different ipprotos, like IPPROTO_DSTOPTS vs IPPROTO_HOPOPTS.
Perhaps more intesting are two of the same ipproto but with different
content.

>               }
>       } else if (strcmp(testname, "large") == 0) {
>               int offset = proto == PF_INET ? 20 : 0;
> -- 
> 2.36.1
> 



Reply via email to