]> The Tcpdump Group git mirrors - tcpdump/commitdiff
Handle the IPv6 Jumbo Payload option.
authorGuy Harris <[email protected]>
Fri, 3 May 2019 03:01:08 +0000 (20:01 -0700)
committerGuy Harris <[email protected]>
Fri, 3 May 2019 03:01:08 +0000 (20:01 -0700)
If we see one when processing the hop-by-hop extension header, use it to
set the payload length.

In UDP, if we have a zero length field in the UDP header, and the length
of the data handed to us is > 65535, treat that as a Jumbo Payload
packet.

netdissect.c
netdissect.h
print-ip6.c
print-ip6opts.c
print-udp.c

index 15594de8f374435db6d2ecd03dc3044427547a42..4d4a4a59149cf5bed98364045dd5a8cd63929a85 100644 (file)
@@ -193,6 +193,27 @@ nd_push_snapend(netdissect_options *ndo, const u_char *new_snapend)
        return (1);     /* success */
 }
 
+/*
+ * Change an already-pushed snapshot end.  This may increase the
+ * snapshot end, as it may be used, for example, for a Jumbo Payload
+ * option in IPv6.  It must not increase it past the snapshot length
+ * atop which the current one was pushed, however.
+ */
+void
+nd_change_snapend(netdissect_options *ndo, const u_char *new_snapend)
+{
+       struct netdissect_saved_packet_info *ndspi;
+
+       ndspi = ndo->ndo_packet_info_stack;
+       if (ndspi->ndspi_prev != NULL) {
+               if (new_snapend <= ndspi->ndspi_prev->ndspi_snapend)
+                       ndo->ndo_snapend = new_snapend;
+       } else {
+               if (new_snapend < ndo->ndo_snapend)
+                       ndo->ndo_snapend = new_snapend;
+       }
+}
+
 void
 nd_pop_packet_info(netdissect_options *ndo)
 {
index f2839e0947dc8bb3a12eb386b499c0be72a805a1..ca42421bf9909652ce1f7d216d45a7e382f1f3a9 100644 (file)
@@ -262,6 +262,7 @@ struct netdissect_options {
 extern int nd_push_buffer(netdissect_options *, u_char *, const u_char *,
     const u_char *);
 extern int nd_push_snapend(netdissect_options *, const u_char *);
+extern void nd_change_snapend(netdissect_options *, const u_char *);
 extern void nd_pop_packet_info(netdissect_options *);
 extern void nd_pop_all_packet_info(netdissect_options *);
 
@@ -585,7 +586,7 @@ extern void cnfp_print(netdissect_options *, const u_char *);
 extern void dccp_print(netdissect_options *, const u_char *, const u_char *, u_int);
 extern void decnet_print(netdissect_options *, const u_char *, u_int, u_int);
 extern void dhcp6_print(netdissect_options *, const u_char *, u_int);
-extern int dstopt_print(netdissect_options *, const u_char *);
+extern int dstopt_process(netdissect_options *, const u_char *);
 extern void dtp_print(netdissect_options *, const u_char *, u_int);
 extern void dvmrp_print(netdissect_options *, const u_char *, u_int);
 extern void eap_print(netdissect_options *, const u_char *, u_int);
@@ -603,7 +604,7 @@ extern void ftp_print(netdissect_options *, const u_char *, u_int);
 extern void geneve_print(netdissect_options *, const u_char *, u_int);
 extern void geonet_print(netdissect_options *, const u_char *, u_int, const struct lladdr_info *);
 extern void gre_print(netdissect_options *, const u_char *, u_int);
-extern int hbhopt_print(netdissect_options *, const u_char *);
+extern int hbhopt_process(netdissect_options *, const u_char *, int *, uint32_t *);
 extern void hex_and_ascii_print(netdissect_options *, const char *, const u_char *, u_int);
 extern void hex_print(netdissect_options *, const char *ident, const u_char *cp, u_int);
 extern void hex_print_with_offset(netdissect_options *, const char *ident, const u_char *cp, u_int, u_int);
index a9a18faf5acc9d79fae2050d72c4d5f54e5e112f..2cc4e3090e35ece11633b58a99cf6e8b05cee5cc 100644 (file)
@@ -231,11 +231,14 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
        const struct ip6_hdr *ip6;
        int advance;
        u_int len;
+       u_int total_advance;
        const u_char *cp;
-       u_int payload_len;
+       uint32_t payload_len;
        uint8_t nh;
        int fragmented = 0;
        u_int flow;
+       int found_extension_header;
+       int found_jumbo;
 
        ndo->ndo_protocol = "ip6";
        ip6 = (const struct ip6_hdr *)bp;
@@ -255,10 +258,35 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
        }
 
        payload_len = GET_BE_U_2(ip6->ip6_plen);
-       len = payload_len + sizeof(struct ip6_hdr);
-       if (length < len)
-               ND_PRINT("truncated-ip6 - %u bytes missing!",
-                       len - length);
+       /*
+        * RFC 1883 says:
+        *
+        * The Payload Length field in the IPv6 header must be set to zero
+        * in every packet that carries the Jumbo Payload option.  If a
+        * packet is received with a valid Jumbo Payload option present and
+        * a non-zero IPv6 Payload Length field, an ICMP Parameter Problem
+        * message, Code 0, should be sent to the packet's source, pointing
+        * to the Option Type field of the Jumbo Payload option.
+        *
+        * Later versions of the IPv6 spec don't discuss the Jumbo Payload
+        * option.
+        *
+        * If the payload length is 0, we temporarily just set the total
+        * length to the remaining data in the packet (which, for Ethernet,
+        * could include frame padding, but if it's a Jumbo Payload frame,
+        * it shouldn't even be sendable over Ethernet, so we don't worry
+        * about that), so we can process the extension headers in order
+        * to *find* a Jumbo Payload hop-by-hop option and, when we've
+        * processed all the extension headers, check whether we found
+        * a Jumbo Payload option, and fail if we haven't.
+        */
+       if (payload_len != 0) {
+               len = payload_len + sizeof(struct ip6_hdr);
+               if (length < len)
+                       ND_PRINT("truncated-ip6 - %u bytes missing!",
+                               len - length);
+       } else
+               len = length + sizeof(struct ip6_hdr);
 
         nh = GET_U_1(ip6->ip6_nxt);
         if (ndo->ndo_vflag) {
@@ -292,12 +320,16 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
 
        cp = (const u_char *)ip6;
        advance = sizeof(struct ip6_hdr);
+       total_advance = 0;
        /* Process extension headers */
+       found_extension_header = 0;
+       found_jumbo = 0;
        while (cp < ndo->ndo_snapend && advance > 0) {
                if (len < (u_int)advance)
                        goto trunc;
                cp += advance;
                len -= advance;
+               total_advance += advance;
 
                if (cp == (const u_char *)(ip6 + 1) &&
                    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
@@ -309,20 +341,22 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                switch (nh) {
 
                case IPPROTO_HOPOPTS:
-                       advance = hbhopt_print(ndo, cp);
+                       advance = hbhopt_process(ndo, cp, &found_jumbo, &payload_len);
                        if (advance < 0) {
                                nd_pop_packet_info(ndo);
                                return;
                        }
+                       found_extension_header = 1;
                        nh = GET_U_1(cp);
                        break;
 
                case IPPROTO_DSTOPTS:
-                       advance = dstopt_print(ndo, cp);
+                       advance = dstopt_process(ndo, cp);
                        if (advance < 0) {
                                nd_pop_packet_info(ndo);
                                return;
                        }
+                       found_extension_header = 1;
                        nh = GET_U_1(cp);
                        break;
 
@@ -332,6 +366,7 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                                nd_pop_packet_info(ndo);
                                return;
                        }
+                       found_extension_header = 1;
                        nh = GET_U_1(cp);
                        fragmented = 1;
                        break;
@@ -351,6 +386,7 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                                nd_pop_packet_info(ndo);
                                return;
                        }
+                       found_extension_header = 1;
                        nh = GET_U_1(cp);
                        nd_pop_packet_info(ndo);
                        return;
@@ -362,6 +398,7 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                                nd_pop_packet_info(ndo);
                                return;
                        }
+                       found_extension_header = 1;
                        nh = GET_U_1(cp);
                        break;
 
@@ -370,6 +407,62 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                         * Not an extension header; hand off to the
                         * IP protocol demuxer.
                         */
+                       if (found_jumbo) {
+                               /*
+                                * We saw a Jumbo Payload option.
+                                * Set the length to the payload length
+                                * plus the IPv6 header length, and
+                                * change the snapshot length accordingly.
+                                */
+                               len = payload_len + sizeof(struct ip6_hdr);
+                               if (length < len)
+                                       ND_PRINT("truncated-ip6 - %u bytes missing!",
+                                               len - length);
+                               nd_change_snapend(ndo, bp + len);
+
+                               /*
+                                * Now subtract the length of the IPv6
+                                * header plus extension headers to get
+                                * the payload length.
+                                */
+                               len -= total_advance;
+                       } else {
+                               /*
+                                * We didn't see a Jumbo Payload option;
+                                * was the payload length zero?
+                                */
+                               if (payload_len == 0) {
+                                       /*
+                                        * Yes.  If we found an extension
+                                        * header, treat that as a truncated
+                                        * packet header, as there was
+                                        * no payload to contain an
+                                        * extension header.
+                                        */
+                                       if (found_extension_header)
+                                               goto trunc;
+
+                                       /*
+                                        * OK, we didn't see any extnesion
+                                        * header, but that means we have
+                                        * no payload, so set the length
+                                        * to the IPv6 header length,
+                                        * and change the snapshot length
+                                        * accordingly.
+                                        */
+                                       len = sizeof(struct ip6_hdr);
+                                       nd_change_snapend(ndo, bp + len);
+
+                                       /*
+                                        * Now subtract the length of
+                                        * the IPv6 header plus extension
+                                        * headers (there weren't any, so
+                                        * that's just the IPv6 header
+                                        * length) to get the payload length.
+                                        */
+                                       len -= total_advance;
+                               }
+                       }
                        ip_print_demux(ndo, cp, len, 6, fragmented,
                            GET_U_1(ip6->ip6_hlim), nh, bp);
                        nd_pop_packet_info(ndo);
index acc36713a43947c239e80bc7c074e8786a88655c..4039f44e32ca94cdd0bcde3bc81476f973f64b9a 100644 (file)
@@ -87,7 +87,8 @@ trunc:
 }
 
 static int
-ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
+ip6_opt_process(netdissect_options *ndo, const u_char *bp, int len,
+               int *found_jumbo, uint32_t *jumbolen)
 {
     int i;
     int optlen = 0;
@@ -108,14 +109,16 @@ ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
 
        switch (GET_U_1(bp + i)) {
        case IP6OPT_PAD1:
-            ND_PRINT("(pad1)");
+           if (ndo->ndo_vflag)
+                ND_PRINT("(pad1)");
            break;
        case IP6OPT_PADN:
            if (len - i < IP6OPT_MINLEN) {
                ND_PRINT("(padn: trunc)");
                goto trunc;
            }
-            ND_PRINT("(padn)");
+           if (ndo->ndo_vflag)
+                ND_PRINT("(padn)");
            break;
        case IP6OPT_ROUTER_ALERT:
            if (len - i < IP6OPT_RTALERT_LEN) {
@@ -126,7 +129,8 @@ ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
                ND_PRINT("(rtalert: invalid len %u)", GET_U_1(bp + i + 1));
                goto trunc;
            }
-           ND_PRINT("(rtalert: 0x%04x) ", GET_BE_U_2(bp + i + 2));
+           if (ndo->ndo_vflag)
+               ND_PRINT("(rtalert: 0x%04x) ", GET_BE_U_2(bp + i + 2));
            break;
        case IP6OPT_JUMBO:
            if (len - i < IP6OPT_JUMBO_LEN) {
@@ -137,7 +141,10 @@ ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
                ND_PRINT("(jumbo: invalid len %u)", GET_U_1(bp + i + 1));
                goto trunc;
            }
-           ND_PRINT("(jumbo: %u) ", GET_BE_U_4(bp + i + 2));
+           *found_jumbo = 1;
+           *jumbolen = GET_BE_U_4(bp + i + 2);
+           if (ndo->ndo_vflag)
+               ND_PRINT("(jumbo: %u) ", *jumbolen);
            break;
         case IP6OPT_HOME_ADDRESS:
            if (len - i < IP6OPT_HOMEADDR_MINLEN) {
@@ -148,25 +155,29 @@ ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
                ND_PRINT("(homeaddr: invalid len %u)", GET_U_1(bp + i + 1));
                goto trunc;
            }
-           ND_PRINT("(homeaddr: %s", ip6addr_string(ndo, bp + i + 2));
-           if (GET_U_1(bp + i + 1) > IP6OPT_HOMEADDR_MINLEN - 2) {
-               if (ip6_sopt_print(ndo, bp + i + IP6OPT_HOMEADDR_MINLEN,
-                                  (optlen - IP6OPT_HOMEADDR_MINLEN)) == -1)
+           if (ndo->ndo_vflag) {
+               ND_PRINT("(homeaddr: %s", ip6addr_string(ndo, bp + i + 2));
+               if (GET_U_1(bp + i + 1) > IP6OPT_HOMEADDR_MINLEN - 2) {
+                   if (ip6_sopt_print(ndo, bp + i + IP6OPT_HOMEADDR_MINLEN,
+                                      (optlen - IP6OPT_HOMEADDR_MINLEN)) == -1)
                        goto trunc;
+               }
+               ND_PRINT(")");
            }
-            ND_PRINT(")");
            break;
        default:
            if (len - i < IP6OPT_MINLEN) {
                ND_PRINT("(type %u: trunc)", GET_U_1(bp + i));
                goto trunc;
            }
-           ND_PRINT("(opt_type 0x%02x: len=%u)", GET_U_1(bp + i),
-                     GET_U_1(bp + i + 1));
+           if (ndo->ndo_vflag)
+               ND_PRINT("(opt_type 0x%02x: len=%u)", GET_U_1(bp + i),
+                        GET_U_1(bp + i + 1));
            break;
        }
     }
-    ND_PRINT(" ");
+    if (ndo->ndo_vflag)
+        ND_PRINT(" ");
     return 0;
 
 trunc:
@@ -174,7 +185,8 @@ trunc:
 }
 
 int
-hbhopt_print(netdissect_options *ndo, const u_char *bp)
+hbhopt_process(netdissect_options *ndo, const u_char *bp, int *found_jumbo,
+              uint32_t *jumbolen)
 {
     const struct ip6_hbh *dp = (const struct ip6_hbh *)bp;
     u_int hbhlen = 0;
@@ -184,10 +196,9 @@ hbhopt_print(netdissect_options *ndo, const u_char *bp)
     hbhlen = (GET_U_1(dp->ip6h_len) + 1) << 3;
     ND_TCHECK_LEN(dp, hbhlen);
     ND_PRINT("HBH ");
-    if (ndo->ndo_vflag)
-       if (ip6_opt_print(ndo, (const u_char *)dp + sizeof(*dp),
-                         hbhlen - sizeof(*dp)) == -1)
-           goto trunc;
+    if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
+                       hbhlen - sizeof(*dp), found_jumbo, jumbolen) == -1)
+       goto trunc;
     return hbhlen;
 
 trunc:
@@ -196,10 +207,12 @@ trunc:
 }
 
 int
-dstopt_print(netdissect_options *ndo, const u_char *bp)
+dstopt_process(netdissect_options *ndo, const u_char *bp)
 {
     const struct ip6_dest *dp = (const struct ip6_dest *)bp;
     u_int dstoptlen = 0;
+    int found_jumbo;
+    uint32_t jumbolen;
 
     ndo->ndo_protocol = "dstopt";
     ND_TCHECK_1(dp->ip6d_len);
@@ -207,8 +220,13 @@ dstopt_print(netdissect_options *ndo, const u_char *bp)
     ND_TCHECK_LEN(dp, dstoptlen);
     ND_PRINT("DSTOPT ");
     if (ndo->ndo_vflag) {
-       if (ip6_opt_print(ndo, (const u_char *)dp + sizeof(*dp),
-                         dstoptlen - sizeof(*dp)) == -1)
+       /*
+        * The Jumbo Payload option is a hop-by-hop option; we print,
+        * but don't honor, Jumbo Payload destination options.
+        */
+       if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
+                           dstoptlen - sizeof(*dp), &found_jumbo,
+                           &jumbolen) == -1)
            goto trunc;
     }
 
index bf5c7ba52ff4c11794261bf358f97e4c1e7a1a74..77bf330e90d947ad24da7ba8ea6bbc1aebb102e5 100644 (file)
@@ -389,7 +389,8 @@ udp_print(netdissect_options *ndo, const u_char *bp, u_int length,
        const struct ip *ip;
        const u_char *cp;
        const u_char *ep = ndo->ndo_snapend;
-       uint16_t sport, dport, ulen;
+       uint16_t sport, dport;
+       u_int ulen;
        const struct ip6_hdr *ip6;
 
        ndo->ndo_protocol = "udp";
@@ -417,6 +418,13 @@ udp_print(netdissect_options *ndo, const u_char *bp, u_int length,
                goto trunc;
        }
        ulen = GET_BE_U_2(up->uh_ulen);
+       /*
+        * IPv6 Jumbo Datagrams; see RFC 2675.
+        * If the length is zero, and the length provided to us is
+        * > 65535, use the provided length as the length.
+        */
+       if (ulen == 0 && length > 65535)
+               ulen = length;
        if (ulen < sizeof(struct udphdr)) {
                udpipaddr_print(ndo, ip, sport, dport);
                ND_PRINT("truncated-udplength %u", ulen);