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 */