On Tue, Nov 25, 2014 at 18:42 +0100, Mike Belopuhov wrote:
> On Mon, Nov 24, 2014 at 19:04 +0100, Mike Belopuhov wrote:
> > Hi,
> > 
> > IP header is not always aligned since bpf copies out the mbuf
> > chain into the contigous buffer provided by the userland.  I've
> > seen this with large packet sizes on VLANs.  ip_print will then
> > copy the packet but the Ethernet header into the internal buffer
> > so that it can cast it to the IP header structure and update
> > global packetp and snapend pointers hence preventing the -Xx
> > dumping code from printing out the Ethernet header itself.
> > 
> > Diff below fixes it.  OK?
> > 
> 
> better diff.  the problem is that dissectors use packetp and
> snapend pointers themselves therefore they should be pointing
> to the newly allocated structure.  we can restore them once
> we're done with the inner content and go back to the caller
> to see if we need to hexdump the contents.
> 
> i'll see if i can cook and test the ipv6 version.
> 
> OK?
> 

now with an ip6 version and i've made sure that this fixes
dumping unaligned ipv6 packets as well.  in the meantime
jsg@ has lured me into looking at the afl crash in the same
code and it looks like the check from ip6_print is useful
here: if we haven't got enough data for a header, don't
bother with anything else and just bail.

ok?

diff --git usr.sbin/tcpdump/print-ip.c usr.sbin/tcpdump/print-ip.c
index 3f4194c..e9d2185 100644
--- usr.sbin/tcpdump/print-ip.c
+++ usr.sbin/tcpdump/print-ip.c
@@ -351,22 +351,27 @@ in_cksum(const u_short *addr, register int len, int csum)
  * print an IP datagram.
  */
 void
 ip_print(register const u_char *bp, register u_int length)
 {
+       static u_char *abuf = NULL;
        register const struct ip *ip;
        register u_int hlen, len, off;
        register const u_char *cp;
+       const u_char *pktp = packetp;
+       const u_char *send = snapend;
 
        ip = (const struct ip *)bp;
+       if ((u_char *)(ip + 1) > snapend) {
+               printf("[|ip]");
+               return;
+       }
+
        /*
         * If the IP header is not aligned, copy into abuf.
-        * This will never happen with BPF.  It does happen with raw packet
-        * dumps from -r.
         */
        if ((intptr_t)ip & (sizeof(long)-1)) {
-               static u_char *abuf = NULL;
                static int didwarn = 0;
                int clen = snapend - bp;
 
                if (clen > snaplen)
                        clen = snaplen;
@@ -387,11 +392,11 @@ ip_print(register const u_char *bp, register u_int length)
        }
 
        TCHECK(*ip);
        if (ip->ip_v != IPVERSION) {
                (void)printf("bad-ip-version %u", ip->ip_v);
-               return;
+               goto out;
        }
 
        len = ntohs(ip->ip_len);
        if (length < len) {
                (void)printf("truncated-ip - %d bytes missing!",
@@ -400,11 +405,11 @@ ip_print(register const u_char *bp, register u_int length)
        }
 
        hlen = ip->ip_hl * 4;
        if (hlen < sizeof(struct ip) || hlen > len) {
                (void)printf("bad-hlen %d", hlen);
-               return;
+               goto out;
        }
 
        len -= hlen;
 
        /*
@@ -465,11 +470,11 @@ ip_print(register const u_char *bp, register u_int length)
                                             ipaddr_string(&ip->ip_src),
                                             ipaddr_string(&ip->ip_dst));
                        ip_print(cp, len);
                        if (! vflag) {
                                printf(" (encap)");
-                               return;
+                               goto out;
                        }
                        break;
 
 #ifdef INET6
 #ifndef IPPROTO_IPV6
@@ -482,11 +487,11 @@ ip_print(register const u_char *bp, register u_int length)
                                             ipaddr_string(&ip->ip_src),
                                             ipaddr_string(&ip->ip_dst));
                        ip6_print(cp, len);
                        if (! vflag) {
                                printf(" (encap)");
-                               return;
+                               goto out;
                        }
                        break;
 #endif /*INET6*/
 
 #ifndef IPPROTO_GRE
@@ -499,11 +504,11 @@ ip_print(register const u_char *bp, register u_int length)
                                             ipaddr_string(&ip->ip_dst));
                        /* do it */
                        gre_print(cp, len);
                        if (! vflag) {
                                printf(" (gre encap)");
-                               return;
+                               goto out;
                        }
                        break;
 
 #ifndef IPPROTO_ESP
 #define IPPROTO_ESP 50
@@ -528,11 +533,11 @@ ip_print(register const u_char *bp, register u_int length)
                                             ipaddr_string(&ip->ip_src),
                                             ipaddr_string(&ip->ip_dst));
                        mobile_print(cp, len);
                        if (! vflag) {
                                printf(" (mobile encap)");
-                               return;
+                               goto out;
                        }
                        break;
 
 #ifndef IPPROTO_ETHERIP
 #define IPPROTO_ETHERIP        97
@@ -653,10 +658,13 @@ ip_print(register const u_char *bp, register u_int length)
                        (void)printf("%soptlen=%d", sep, hlen);
                        ip_optprint((u_char *)(ip + 1), hlen);
                }
                printf(")");
        }
+out:
+       packetp = pktp;
+       snapend = send;
        return;
 
 trunc:
        printf("[|ip]");
 }
diff --git usr.sbin/tcpdump/print-ip6.c usr.sbin/tcpdump/print-ip6.c
index 648de8c..03c9c84 100644
--- usr.sbin/tcpdump/print-ip6.c
+++ usr.sbin/tcpdump/print-ip6.c
@@ -50,26 +50,30 @@
  * print an IP6 datagram.
  */
 void
 ip6_print(register const u_char *bp, register u_int length)
 {
+       static u_char *abuf = NULL;
        register const struct ip6_hdr *ip6;
        register int hlen;
        register int len;
        register const u_char *cp;
+       const u_char *pktp = packetp;
+       const u_char *send = snapend;
        int nh;
        u_int flow;
-       
+
        ip6 = (const struct ip6_hdr *)bp;
+       if ((u_char *)(ip6 + 1) > snapend) {
+               printf("[|ip6]");
+               return;
+       }
 
        /*
         * The IP header is not word aligned, so copy into abuf.
-        * This will never happen with BPF.  It does happen with
-        * raw packet dumps from -r.
         */
        if ((intptr_t)ip6 & (sizeof(long)-1)) {
-               static u_char *abuf = NULL;
                static int didwarn = 0;
                int clen = snapend - bp;
                if (clen > snaplen)
                        clen = snaplen;
 
@@ -87,21 +91,17 @@ ip6_print(register const u_char *bp, register u_int length)
                        warning("compensating for unaligned libpcap packets");
                        ++didwarn;
                }
        }
 
-       if ((u_char *)(ip6 + 1) > snapend) {
-               printf("[|ip6]");
-               return;
-       }
        if (length < sizeof (struct ip6_hdr)) {
                (void)printf("truncated-ip6 %d", length);
-               return;
+               goto out;
        }
        if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
                (void)printf("bad-ip6-version %u", ip6->ip6_vfc >> 4);
-               return;
+               goto out;
        }
        hlen = sizeof(struct ip6_hdr);
 
        len = ntohs(ip6->ip6_plen);
        if (length < len + hlen)
@@ -204,11 +204,11 @@ ip6_print(register const u_char *bp, register u_int 
length)
                if (hlen == 0)
                        break;
        }
 
  end:
-       
+
        flow = ntohl(ip6->ip6_flow);
 #if 0
        /* rfc1883 */
        if (flow & 0x0f000000)
                (void)printf(" [pri 0x%x]", (flow & 0x0f000000) >> 24);
@@ -230,8 +230,12 @@ ip6_print(register const u_char *bp, register u_int length)
                (void)printf("len %d", len);
                if (ip6->ip6_hlim > 1)
                        (void)printf(", hlim %d", (int)ip6->ip6_hlim);
                printf(")");
        }
+
+ out:
+       packetp = pktp;
+       snapend = send;
 }
 
 #endif /* INET6 */

Reply via email to