]> The Tcpdump Group git mirrors - tcpdump/commitdiff
Don't overwrite the destination IPv6 address for routing headers.
authorGuy Harris <[email protected]>
Sat, 13 Feb 2016 04:26:39 +0000 (20:26 -0800)
committerGuy Harris <[email protected]>
Sat, 13 Feb 2016 04:26:39 +0000 (20:26 -0800)
If we have a routing header, instead of overwriting the packet's IPv6
destination address in the packet with the final destination, so that
the next protocol's checksum routine can use it, we do as we do for
IPv4, and, in the "next protocol checksum" routine, scan the headers
looking for a routing header and, if we find one, copy the final
destination from it.

While we're at it, clean up a few things.

ip6.h
netdissect.h
print-dccp.c
print-icmp6.c
print-ip6.c
print-pim.c
print-rt6.c
print-tcp.c
print-udp.c

diff --git a/ip6.h b/ip6.h
index 1ad914985b62dd11f83692483f50c79aa5c4450e..2ea1d0abe8ecda6e576aa9683edb64bb922c52a5 100644 (file)
--- a/ip6.h
+++ b/ip6.h
@@ -172,7 +172,11 @@ struct ip6_rthdr {
        /* followed by routing type specific data */
 } UNALIGNED;
 
+#define IPV6_RTHDR_TYPE_0 0
+#define IPV6_RTHDR_TYPE_2 2
+
 /* Type 0 Routing header */
+/* Also used for Type 2 */
 struct ip6_rthdr0 {
        uint8_t  ip6r0_nxt;             /* next header */
        uint8_t  ip6r0_len;             /* length in units of 8 octets */
@@ -195,7 +199,4 @@ struct ip6_frag {
 #define IP6F_RESERVED_MASK     0x0006  /* reserved bits in ip6f_offlg */
 #define IP6F_MORE_FRAG         0x0001  /* more-fragments flag */
 
-/* in print-ip6.c */
-extern int nextproto6_cksum(const struct ip6_hdr *, const uint8_t *, u_int, u_int, u_int);
-
 #endif /* not _NETINET_IP6_H_ */
index dc3a473d6926a22a547c6cbcdfb35de9a200676b..8a6b01880152af0dd04451f517e63e33a32f543a 100644 (file)
@@ -75,6 +75,7 @@ typedef signed char nd_int8_t;
 #include <pcap.h>
 
 #include "ip.h" /* struct ip for nextproto4_cksum() */
+#include "ip6.h" /* struct ip6 for nextproto6_cksum() */
 
 extern int32_t thiszone;       /* seconds offset from gmt to local time */
 /* invalid string to print '(invalid)' for malformed or corrupted packets */
@@ -603,6 +604,9 @@ extern uint16_t in_cksum_shouldbe(uint16_t, uint16_t);
 
 extern int nextproto4_cksum(netdissect_options *, const struct ip *, const uint8_t *, u_int, u_int, u_int);
 
+/* in print-ip6.c */
+extern int nextproto6_cksum(netdissect_options *, const struct ip6_hdr *, const uint8_t *, u_int, u_int, u_int);
+
 /* Utilities */
 extern int mask2plen(uint32_t);
 extern int mask62plen(const u_char *);
index f552f0535034372f900154529f12affc0c477402..0e30e99c505505566e76b0faf00f7d82a00b6f15 100644 (file)
@@ -202,9 +202,10 @@ static int dccp_cksum(netdissect_options *ndo, const struct ip *ip,
                                dccp_csum_coverage(dh, len), IPPROTO_DCCP);
 }
 
-static int dccp6_cksum(const struct ip6_hdr *ip6, const struct dccp_hdr *dh, u_int len)
+static int dccp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+       const struct dccp_hdr *dh, u_int len)
 {
-       return nextproto6_cksum(ip6, (const uint8_t *)(const void *)dh, len,
+       return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)dh, len,
                                dccp_csum_coverage(dh, len), IPPROTO_DCCP);
 }
 
@@ -342,7 +343,7 @@ void dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2,
                if (IP_V(ip) == 4)
                        sum = dccp_cksum(ndo, ip, dh, len);
                else if (IP_V(ip) == 6)
-                       sum = dccp6_cksum(ip6, dh, len);
+                       sum = dccp6_cksum(ndo, ip6, dh, len);
                if (sum != 0)
                        ND_PRINT((ndo, "(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum)));
                else
index 56f80bfbb287f0a4ce7f1da9220f15b5a1e594df..ae53187452f6d0263ede548747f1082fcff58648 100644 (file)
@@ -620,10 +620,10 @@ print_lladdr(netdissect_options *ndo, const uint8_t *p, size_t l)
        }
 }
 
-static int icmp6_cksum(const struct ip6_hdr *ip6, const struct icmp6_hdr *icp,
-       u_int len)
+static int icmp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+       const struct icmp6_hdr *icp, u_int len)
 {
-       return nextproto6_cksum(ip6, (const uint8_t *)(const void *)icp, len, len,
+       return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)icp, len, len,
                                IPPROTO_ICMPV6);
 }
 
@@ -908,7 +908,7 @@ icmp6_print(netdissect_options *ndo,
 
                if (ND_TTEST2(bp[0], length)) {
                        udp_sum = EXTRACT_16BITS(&dp->icmp6_cksum);
-                       sum = icmp6_cksum(ip, dp, length);
+                       sum = icmp6_cksum(ndo, ip, dp, length);
                        if (sum != 0)
                                ND_PRINT((ndo,"[bad icmp6 cksum 0x%04x -> 0x%04x!] ",
                                                 udp_sum,
index 45508ced6cbf11a0ea5eb537f540488c0a3d1d97..3968f333dde2fffa35ef44faff0ddcfd11a08d9b 100644 (file)
 #include "ip6.h"
 #include "ipproto.h"
 
+/*
+ * If routing headers are presend and valid, set dst to the final destination.
+ * Otherwise, set it to the IPv6 destination.
+ *
+ * This is used for UDP and TCP pseudo-header in the checksum
+ * calculation.
+ */
+static void
+ip6_finddst(netdissect_options *ndo, struct in6_addr *dst,
+            const struct ip6_hdr *ip6)
+{
+       const u_char *cp;
+       int advance;
+       u_int nh;
+       const struct in6_addr *dst_addr;
+       const struct ip6_rthdr *dp;
+       const struct ip6_rthdr0 *dp0;
+       const struct in6_addr *addr;
+       int i, len;
+
+       cp = (const u_char *)ip6;
+       advance = sizeof(struct ip6_hdr);
+       nh = ip6->ip6_nxt;
+       dst_addr = &ip6->ip6_dst;
+
+       while (cp < ndo->ndo_snapend) {
+               cp += advance;
+
+               switch (nh) {
+
+               case IPPROTO_HOPOPTS:
+               case IPPROTO_DSTOPTS:
+               case IPPROTO_MOBILITY_OLD:
+               case IPPROTO_MOBILITY:
+                       /*
+                        * These have a header length byte, following
+                        * the next header byte, giving the length of
+                        * the header, in units of 8 octets, excluding
+                        * the first 8 octets.
+                        */
+                       ND_TCHECK2(*cp, 2);
+                       advance = (int)((*(cp + 1) + 1) << 3);
+                       nh = *cp;
+                       break;
+
+               case IPPROTO_FRAGMENT:
+                       /*
+                        * The byte following the next header byte is
+                        * marked as reserved, and the header is always
+                        * the same size.
+                        */
+                       ND_TCHECK2(*cp, 1);
+                       advance = sizeof(struct ip6_frag);
+                       nh = *cp;
+                       break;
+
+               case IPPROTO_ROUTING:
+                       /*
+                        * OK, we found it.
+                        */
+                       dp = (const struct ip6_rthdr *)cp;
+                       ND_TCHECK(*dp);
+                       len = dp->ip6r_len;
+                       switch (dp->ip6r_type) {
+
+                       case IPV6_RTHDR_TYPE_0:
+                       case IPV6_RTHDR_TYPE_2:         /* Mobile IPv6 ID-20 */
+                               dp0 = (const struct ip6_rthdr0 *)dp;
+                               if (len % 2 == 1)
+                                       goto trunc;
+                               len >>= 1;
+                               addr = &dp0->ip6r0_addr[0];
+                               for (i = 0; i < len; i++) {
+                                       if ((const u_char *)(addr + 1) > ndo->ndo_snapend)
+                                               goto trunc;
+
+                                       dst_addr = addr;
+                                       addr++;
+                               }
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       /*
+                        * Only one routing header to a customer.
+                        */
+                       goto done;
+
+               case IPPROTO_AH:
+               case IPPROTO_ESP:
+               case IPPROTO_IPCOMP:
+               default:
+                       /*
+                        * AH and ESP are, in the RFCs that describe them,
+                        * described as being "viewed as an end-to-end
+                        * payload" "in the IPv6 context, so that they
+                        * "should appear after hop-by-hop, routing, and
+                        * fragmentation extension headers".  We assume
+                        * that's the case, and stop as soon as we see
+                        * one.  (We can't handle an ESP header in
+                        * the general case anyway, as its length depends
+                        * on the encryption algorithm.)
+                        *
+                        * IPComp is also "viewed as an end-to-end
+                        * payload" "in the IPv6 context".
+                        *
+                        * All other protocols are assumed to be the final
+                        * protocol.
+                        */
+                       goto done;
+               }
+       }
+
+done:
+trunc:
+       UNALIGNED_MEMCPY(dst, dst_addr, sizeof(struct in6_addr));
+}
+
 /*
  * Compute a V6-style checksum by building a pseudoheader.
  */
 int
-nextproto6_cksum(const struct ip6_hdr *ip6, const uint8_t *data,
+nextproto6_cksum(netdissect_options *ndo,
+                 const struct ip6_hdr *ip6, const uint8_t *data,
                 u_int len, u_int covlen, u_int next_proto)
 {
         struct {
@@ -53,7 +174,26 @@ nextproto6_cksum(const struct ip6_hdr *ip6, const uint8_t *data,
         /* pseudo-header */
         memset(&ph, 0, sizeof(ph));
         UNALIGNED_MEMCPY(&ph.ph_src, &ip6->ip6_src, sizeof (struct in6_addr));
-        UNALIGNED_MEMCPY(&ph.ph_dst, &ip6->ip6_dst, sizeof (struct in6_addr));
+        switch (ip6->ip6_nxt) {
+
+        case IPPROTO_HOPOPTS:
+        case IPPROTO_DSTOPTS:
+        case IPPROTO_MOBILITY_OLD:
+        case IPPROTO_MOBILITY:
+        case IPPROTO_FRAGMENT:
+        case IPPROTO_ROUTING:
+                /*
+                 * The next header is either a routing header or a header
+                 * after which there might be a routing header, so scan
+                 * for a routing header.
+                 */
+                ip6_finddst(ndo, &ph.ph_dst, ip6);
+                break;
+
+        default:
+                UNALIGNED_MEMCPY(&ph.ph_dst, &ip6->ip6_dst, sizeof (struct in6_addr));
+                break;
+        }
         ph.ph_len = htonl(len);
         ph.ph_nxt = next_proto;
 
@@ -168,9 +308,7 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
                case IPPROTO_MOBILITY_OLD:
                case IPPROTO_MOBILITY:
                        /*
-                        * XXX - we don't use "advance"; the current
-                        * "Mobility Support in IPv6" draft
-                        * (draft-ietf-mobileip-ipv6-24) says that
+                        * XXX - we don't use "advance"; RFC 3775 says that
                         * the next header field in a mobility header
                         * should be IPPROTO_NONE, but speaks of
                         * the possiblity of a future extension in
index ce4b2afd5228df258266550cf881b6baf8782712..c93f165617fd697ce21280df1210b61694d8be26 100644 (file)
@@ -620,7 +620,7 @@ enum checksum_status {
 };
 
 static enum checksum_status
-pimv2_check_checksum(const u_char *bp, const u_char *bp2, u_int len)
+pimv2_check_checksum(netdissect_options *ndo, const u_char *bp, const u_char *bp2, u_int len)
 {
        const struct ip *ip;
        u_int cksum;
@@ -637,7 +637,7 @@ pimv2_check_checksum(const u_char *bp, const u_char *bp2, u_int len)
                const struct ip6_hdr *ip6;
 
                ip6 = (const struct ip6_hdr *)bp2;
-               cksum = nextproto6_cksum(ip6, bp, len, len, IPPROTO_PIM);
+               cksum = nextproto6_cksum(ndo, ip6, bp, len, len, IPPROTO_PIM);
                return (cksum ? INCORRECT : CORRECT);
        } else {
                return (UNVERIFIED);
@@ -672,7 +672,7 @@ pimv2_print(netdissect_options *ndo,
                         * The checksum only covers the packet header,
                         * not the encapsulated packet.
                         */
-                       cksum_status = pimv2_check_checksum(bp, bp2, 8);
+                       cksum_status = pimv2_check_checksum(ndo, bp, bp2, 8);
                        if (cksum_status == INCORRECT) {
                                /*
                                 * To quote RFC 4601, "For interoperability
@@ -680,13 +680,13 @@ pimv2_print(netdissect_options *ndo,
                                 * calculated over the entire PIM Register
                                 * message should also be accepted."
                                 */
-                               cksum_status = pimv2_check_checksum(bp, bp2, len);
+                               cksum_status = pimv2_check_checksum(ndo, bp, bp2, len);
                        }
                } else {
                        /*
                         * The checksum covers the entire packet.
                         */
-                       cksum_status = pimv2_check_checksum(bp, bp2, len);
+                       cksum_status = pimv2_check_checksum(ndo, bp, bp2, len);
                }
                switch (cksum_status) {
 
index 231f5ff4ce42150041aa5f4cc10225d38d20600f..0f2379a86984fcb9e25ef122e21dde063b850bf5 100644 (file)
@@ -56,12 +56,6 @@ rt6_print(netdissect_options *ndo, register const u_char *bp, const u_char *bp2
        ND_PRINT((ndo, ", segleft=%d", dp->ip6r_segleft));
 
        switch (dp->ip6r_type) {
-#ifndef IPV6_RTHDR_TYPE_0
-#define IPV6_RTHDR_TYPE_0 0
-#endif
-#ifndef IPV6_RTHDR_TYPE_2
-#define IPV6_RTHDR_TYPE_2 2
-#endif
        case IPV6_RTHDR_TYPE_0:
        case IPV6_RTHDR_TYPE_2:                 /* Mobile IPv6 ID-20 */
                dp0 = (const struct ip6_rthdr0 *)dp;
@@ -84,14 +78,6 @@ rt6_print(netdissect_options *ndo, register const u_char *bp, const u_char *bp2
                        last_addr = addr;
                        addr++;
                }
-               /*
-                * the destination address used in the pseudo-header is that of the final
-                * destination : the last address of the routing header
-                */
-               if (last_addr != NULL) {
-                       const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp2;
-                       UNALIGNED_MEMCPY(&ip6->ip6_dst, last_addr, sizeof (struct in6_addr));
-               }
                /*(*/
                ND_PRINT((ndo, ") "));
                return((dp0->ip6r0_len + 1) << 3);
index 1473cb385a6cb69546cad9c035e5d0eea480e623..c731d949fed9a662aa93fc56032a5c7b9c261d61 100644 (file)
@@ -143,6 +143,16 @@ tcp_cksum(netdissect_options *ndo,
                                IPPROTO_TCP);
 }
 
+static int
+tcp6_cksum(netdissect_options *ndo,
+           register const struct ip6_hdr *ip6,
+           register const struct tcphdr *tp,
+           register u_int len)
+{
+       return nextproto6_cksum(ndo, ip6, (const uint8_t *)tp, len, len,
+                               IPPROTO_TCP);
+}
+
 void
 tcp_print(netdissect_options *ndo,
           register const u_char *bp, register u_int length,
@@ -370,8 +380,7 @@ tcp_print(netdissect_options *ndo,
                         }
                 } else if (IP_V(ip) == 6 && ip6->ip6_plen) {
                         if (ND_TTEST2(tp->th_sport, length)) {
-                                sum = nextproto6_cksum(ip6, (const uint8_t *)tp,
-                                                       length, length, IPPROTO_TCP);
+                                sum = tcp6_cksum(ndo, ip6, tp, length);
                                 tcp_sum = EXTRACT_16BITS(&tp->th_sum);
 
                                 ND_PRINT((ndo, ", cksum 0x%04x", tcp_sum));
index 6bf51c63a7c12bb565ae6363b103a843bcf4c08f..2fc603ae6e690c83e4f23d478dbb40ac52e22862 100644 (file)
@@ -275,11 +275,11 @@ static int udp_cksum(netdissect_options *ndo, register const struct ip *ip,
                                IPPROTO_UDP);
 }
 
-static int udp6_cksum(const struct ip6_hdr *ip6, const struct udphdr *up,
-       u_int len)
+static int udp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+                     const struct udphdr *up, u_int len)
 {
-       return nextproto6_cksum(ip6, (const uint8_t *)(const void *)up, len, len,
-           IPPROTO_UDP);
+       return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)up, len, len,
+                               IPPROTO_UDP);
 }
 
 static void
@@ -523,7 +523,7 @@ udp_print(netdissect_options *ndo, register const u_char *bp, u_int length,
                else if (IP_V(ip) == 6 && ip6->ip6_plen) {
                        /* for IPv6, UDP checksum is mandatory */
                        if (ND_TTEST2(cp[0], length)) {
-                               sum = udp6_cksum(ip6, up, length + sizeof(struct udphdr));
+                               sum = udp6_cksum(ndo, ip6, up, length + sizeof(struct udphdr));
                                udp_sum = EXTRACT_16BITS(&up->uh_sum);
 
                                if (sum != 0) {