Module Name: src Committed By: msaitoh Date: Tue Jul 17 05:52:07 UTC 2018
Modified Files: src/share/man/man4: ddb.4 src/sys/kern: files.kern uipc_mbuf.c src/sys/sys: mbuf.h socket.h Added Files: src/sys/kern: uipc_mbufdebug.c Log Message: Add /d(dump) and /v(verbose) modifiers to DDB's "show mbuf" command. Mainly written by Hiroki SUENAGA. Currently, /v supports Ethernet, PPP, PPPoE, ARP, IPv4, ICMP, IPv6, ICMPv6, TCP and UDP. To generate a diff of this commit: cvs rdiff -u -r1.178 -r1.179 src/share/man/man4/ddb.4 cvs rdiff -u -r1.18 -r1.19 src/sys/kern/files.kern cvs rdiff -u -r1.215 -r1.216 src/sys/kern/uipc_mbuf.c cvs rdiff -u -r0 -r1.1 src/sys/kern/uipc_mbufdebug.c cvs rdiff -u -r1.207 -r1.208 src/sys/sys/mbuf.h cvs rdiff -u -r1.124 -r1.125 src/sys/sys/socket.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/ddb.4 diff -u src/share/man/man4/ddb.4:1.178 src/share/man/man4/ddb.4:1.179 --- src/share/man/man4/ddb.4:1.178 Mon Mar 19 08:41:21 2018 +++ src/share/man/man4/ddb.4 Tue Jul 17 05:52:07 2018 @@ -1,4 +1,4 @@ -.\" $NetBSD: ddb.4,v 1.178 2018/03/19 08:41:21 ozaki-r Exp $ +.\" $NetBSD: ddb.4,v 1.179 2018/07/17 05:52:07 msaitoh Exp $ .\" .\" Copyright (c) 1997 - 2009 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -56,7 +56,7 @@ .\" any improvements or extensions that they make and grant Carnegie Mellon .\" the rights to redistribute these changes. .\" -.Dd March 19, 2018 +.Dd July 17, 2018 .Dt DDB 4 .Os .Sh NAME @@ -673,12 +673,20 @@ Print the mount structure at If .Cm /f is specified, the complete vnode list is printed. -.It Ic show mbuf Ns Oo Cm /c Oc Ar address +.It Ic show mbuf Ns Oo Cm /cdv Oc Ar address Print the mbuf structure at .Ar address . -If -.Cm /c -is specified, the mbufs in the chain are followed. +Valid modifiers: +.Bl -tag -width 4n -compact +.It Cm /c +The mbufs in the chain are followed. +.It Cm /d +The data is dumped. +.It Cm /v +Decode the mbuf chain as a packet. +It currently supports Ethernet, PPP, PPPoE, ARP, IPv4, ICMP, IPv6, ICMP6, TCP +and UDP. +.El .It Ic show ncache Ar address Dump the namecache list associated with vnode at .Ar address . Index: src/sys/kern/files.kern diff -u src/sys/kern/files.kern:1.18 src/sys/kern/files.kern:1.19 --- src/sys/kern/files.kern:1.18 Thu Jul 12 10:46:48 2018 +++ src/sys/kern/files.kern Tue Jul 17 05:52:07 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.kern,v 1.18 2018/07/12 10:46:48 maxv Exp $ +# $NetBSD: files.kern,v 1.19 2018/07/17 05:52:07 msaitoh Exp $ # # kernel sources @@ -175,6 +175,7 @@ file kern/uipc_accf.c kern file kern/uipc_domain.c kern file kern/uipc_mbuf.c kern file kern/uipc_mbuf2.c kern +file kern/uipc_mbufdebug.c kern file net/link_proto.c kern # XXX file kern/uipc_proto.c kern file kern/uipc_sem.c kern Index: src/sys/kern/uipc_mbuf.c diff -u src/sys/kern/uipc_mbuf.c:1.215 src/sys/kern/uipc_mbuf.c:1.216 --- src/sys/kern/uipc_mbuf.c:1.215 Mon May 7 09:57:37 2018 +++ src/sys/kern/uipc_mbuf.c Tue Jul 17 05:52:07 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: uipc_mbuf.c,v 1.215 2018/05/07 09:57:37 maxv Exp $ */ +/* $NetBSD: uipc_mbuf.c,v 1.216 2018/07/17 05:52:07 msaitoh Exp $ */ /* * Copyright (c) 1999, 2001 The NetBSD Foundation, Inc. @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uipc_mbuf.c,v 1.215 2018/05/07 09:57:37 maxv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uipc_mbuf.c,v 1.216 2018/07/17 05:52:07 msaitoh Exp $"); #ifdef _KERNEL_OPT #include "opt_mbuftrace.h" @@ -1646,7 +1646,11 @@ void m_print(const struct mbuf *m, const char *modif, void (*pr)(const char *, ...)) { char ch; + const struct mbuf *m0 = NULL; bool opt_c = false; + bool opt_d = false; + bool opt_v = false; + int no = 0; char buf[512]; while ((ch = *(modif++)) != '\0') { @@ -1654,14 +1658,35 @@ m_print(const struct mbuf *m, const char case 'c': opt_c = true; break; + case 'd': + opt_d = true; + break; + case 'v': + opt_v = true; + m0 = m; + break; } } nextchain: - (*pr)("MBUF %p\n", m); + (*pr)("MBUF(%d) %p\n", no, m); snprintb(buf, sizeof(buf), M_FLAGS_BITS, (u_int)m->m_flags); (*pr)(" data=%p, len=%d, type=%d, flags=%s\n", m->m_data, m->m_len, m->m_type, buf); + if (opt_d) { + int i; + unsigned char *p = m->m_data; + + (*pr)(" data:"); + + for (i = 0; i < m->m_len; i++) { + if (i % 16 == 0) + (*pr)("\n"); + (*pr)(" %02x", p[i]); + } + + (*pr)("\n"); + } (*pr)(" owner=%p, next=%p, nextpkt=%p\n", m->m_owner, m->m_next, m->m_nextpkt); (*pr)(" leadingspace=%u, trailingspace=%u, readonly=%u\n", @@ -1697,9 +1722,14 @@ nextchain: if (opt_c) { m = m->m_next; if (m != NULL) { + no++; goto nextchain; } } + + if (opt_v && m0) { + m_examine(m0, AF_ETHER, modif, pr); + } } #endif /* defined(DDB) */ Index: src/sys/sys/mbuf.h diff -u src/sys/sys/mbuf.h:1.207 src/sys/sys/mbuf.h:1.208 --- src/sys/sys/mbuf.h:1.207 Fri Jun 1 08:56:00 2018 +++ src/sys/sys/mbuf.h Tue Jul 17 05:52:07 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: mbuf.h,v 1.207 2018/06/01 08:56:00 maxv Exp $ */ +/* $NetBSD: mbuf.h,v 1.208 2018/07/17 05:52:07 msaitoh Exp $ */ /* * Copyright (c) 1996, 1997, 1999, 2001, 2007 The NetBSD Foundation, Inc. @@ -900,6 +900,10 @@ m_copy_rcvif(struct mbuf *m, const struc void m_print(const struct mbuf *, const char *, void (*)(const char *, ...) __printflike(1, 2)); +/* from uipc_mbufdebug.c */ +void m_examine(const struct mbuf *, int, const char *, void (*)(const char *, ...) + __printflike(1, 2)); + /* * Get rcvif of a mbuf. * Index: src/sys/sys/socket.h diff -u src/sys/sys/socket.h:1.124 src/sys/sys/socket.h:1.125 --- src/sys/sys/socket.h:1.124 Thu Apr 19 21:19:07 2018 +++ src/sys/sys/socket.h Tue Jul 17 05:52:07 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: socket.h,v 1.124 2018/04/19 21:19:07 christos Exp $ */ +/* $NetBSD: socket.h,v 1.125 2018/07/17 05:52:07 msaitoh Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -222,7 +222,8 @@ struct accept_filter_arg { #define AF_MPLS 33 /* MultiProtocol Label Switching */ #define AF_ROUTE 34 /* Internal Routing Protocol */ #define AF_CAN 35 -#define AF_MAX 36 +#define AF_ETHER 36 +#define AF_MAX 37 /* * Structure used by kernel to store most @@ -332,6 +333,7 @@ struct sockaddr_storage { #define PF_MPLS AF_MPLS #define PF_ROUTE AF_ROUTE #define PF_CAN AF_CAN +#define PF_ETHER AF_ETHER #define PF_MAX AF_MAX Added files: Index: src/sys/kern/uipc_mbufdebug.c diff -u /dev/null src/sys/kern/uipc_mbufdebug.c:1.1 --- /dev/null Tue Jul 17 05:52:07 2018 +++ src/sys/kern/uipc_mbufdebug.c Tue Jul 17 05:52:07 2018 @@ -0,0 +1,959 @@ +/* $NetBSD: uipc_mbufdebug.c,v 1.1 2018/07/17 05:52:07 msaitoh Exp $ */ + +/* + * Copyright (C) 2017 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$SEIL: uipc_mbufseil.c$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> + +#include <net/if.h> +#include <net/if_ether.h> +#include <net/ppp_defs.h> +#include <net/if_arp.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/if_inarp.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> + +#define EXAMINE_HEX_LIMIT 128 +#define EXAMINE_HEX_COL 4 + +/* mbuf operations without change of mbuf chain */ +static int m_peek_data(const struct mbuf *, int, int, void *); +static unsigned int m_peek_len(const struct mbuf *, const char *); + +/* utility */ +static char *str_ethaddr(const uint8_t *); +static char *str_ipaddr(const struct in_addr *); +static char *str_ip6addr(const struct in6_addr *); +static const char *str_ipproto(const uint8_t); + +/* parsers for m_examine() */ +static void m_examine_ether(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_pppoe(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_ppp(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_arp(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_ip(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_icmp(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_ip6(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_icmp6(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_tcp(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_udp(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); +static void m_examine_hex(const struct mbuf *, int, const char *, + void (*)(const char *, ...)); + +/* header structure for some protocol */ +struct pppoehdr { + uint8_t vertype; + uint8_t code; + uint16_t session; + uint16_t plen; +} __attribute__((__packed__)); + +struct pppoetag { + uint16_t tag; + uint16_t len; +} __attribute__((__packed__)); + +#define PPPOE_TAG_EOL 0x0000 +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +struct ppp_header { + uint8_t address; + uint8_t control; + uint16_t protocol; +} __attribute__((__packed__)); + +#define CISCO_MULTICAST 0x8f /* Cisco multicast address */ +#define CISCO_UNICAST 0x0f /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ + +#ifndef NELEMS +#define NELEMS(elem) ((sizeof(elem))/(sizeof((elem)[0]))) +#endif + +static int +m_peek_data(const struct mbuf *m, int off, int len, void *vp) +{ + unsigned int count; + char *cp = vp; + + if (off < 0 || len < 0) + return -1; + + while (off > 0) { + if (m == 0) + return -1; + if (off < m->m_len) + break; + off -= m->m_len; + m = m->m_next; + } + while (len > 0) { + if (m == 0) + return -1; + count = min(m->m_len - off, len); + memcpy(cp, mtod(m, char *) + off, count); + len -= count; + cp += count; + off = 0; + m = m->m_next; + } + + return 0; +} + +static unsigned int +m_peek_len(const struct mbuf *m, const char *modif) +{ + const struct mbuf *m0; + unsigned int pktlen; + boolean_t opt_c = FALSE; + unsigned char ch; + + while ( modif && (ch = *(modif++)) != '\0') { + switch (ch) { + case 'c': + opt_c = TRUE; + break; + } + } + + if (opt_c == TRUE) { + return m->m_len; + } + + if ((m->m_flags & M_PKTHDR) != 0) + return m->m_pkthdr.len; + + pktlen = 0; + for (m0 = m; m0 != NULL; m0 = m0->m_next) + pktlen += m0->m_len; + + return pktlen; +} + +static char * +str_ethaddr(const uint8_t *ap) +{ + static char buf[3 * ETHER_ADDR_LEN]; + + return ether_snprintf(buf, sizeof(buf), ap); +} + +static char * +str_ipaddr(const struct in_addr *ap) +{ + static char buf[INET_ADDRSTRLEN]; + + return IN_PRINT(buf, ap); +} + +static char * +str_ip6addr(const struct in6_addr *ap) +{ + static char buf[INET6_ADDRSTRLEN]; + + return IN6_PRINT(buf, ap); +} + +static const char * +str_ipproto(const uint8_t proto) +{ + switch (proto) { + case IPPROTO_HOPOPTS: + return ("IPv6 Hop-by-Hop"); + break; + case IPPROTO_TCP: + return("TCP"); + break; + case IPPROTO_UDP: + return("UDP"); + break; + case IPPROTO_ICMP: + return("ICMP"); + break; + case IPPROTO_IGMP: + return("IGMP"); + break; + case IPPROTO_ESP: + return("ESP"); + break; + case IPPROTO_AH: + return("AH"); + break; + case IPPROTO_IPV6_ICMP: + return("ICMP6"); + default: + return("unknown"); + break; + } + + /* not reached */ + return NULL; +} + +static void +m_examine_ether(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + struct ether_header eh; + unsigned int pktlen; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(eh)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(eh), (void *)(&eh)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(eh); + + (*pr)("ETHER: DST = %s\n", str_ethaddr(eh.ether_dhost)); + (*pr)("ETHER: SRC = %s\n", str_ethaddr(eh.ether_shost)); + + (*pr)("ETHER: TYPE = 0x%04x(", ntohs(eh.ether_type)); + switch (ntohs(eh.ether_type)) { + case ETHERTYPE_PPPOE: + (*pr)("PPPoE)\n"); + return m_examine_pppoe(m, off, modif, pr); + break; + case ETHERTYPE_ARP: + (*pr)("ARP)\n"); + return m_examine_arp(m, off, modif, pr); + break; + case ETHERTYPE_IP: + (*pr)("IPv4)\n"); + return m_examine_ip(m, off, modif, pr); + break; + case ETHERTYPE_IPV6: + (*pr)("IPv6)\n"); + return m_examine_ip6(m, off, modif, pr); + break; + default: + (*pr)("unknown)\n"); + break; + } + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_pppoe(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + struct pppoehdr ph; + struct pppoetag pt; + unsigned int pktlen; + uint8_t vt; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(ph)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(ph), (void *)(&ph)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(ph); + + while (off + sizeof(pt) > pktlen) { + if (m_peek_data(m, off, sizeof(pt), (void *)(&pt)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(pt); + + if (ntohs(pt.tag) == PPPOE_TAG_EOL) + break; + off += ntohs(pt.len); + } + + vt = ph.vertype; + + (*pr)("PPPoE: Version = %u\n", ((vt >> 4) & 0xff)); + (*pr)("PPPoE: Type = %u\n", (vt & 0xff)); + (*pr)("PPPoE: Code = %u(", ph.code); + switch (ph.code) { + case 0: + (*pr)("DATA"); + break; + case PPPOE_CODE_PADI: + (*pr)("PADI"); + break; + case PPPOE_CODE_PADO: + (*pr)("PADO"); + break; + case PPPOE_CODE_PADS: + (*pr)("PADS"); + break; + case PPPOE_CODE_PADT: + (*pr)("PADT"); + break; + default: + (*pr)("unknown"); + break; + } + (*pr)(")\n"); + + (*pr)("PPPoE: Session = 0x%04x\n", ntohs(ph.session)); + (*pr)("PPPoE: Payload Length = %u\n", ntohs(ph.plen)); + + switch (ph.code) { + case PPPOE_CODE_PADI: + case PPPOE_CODE_PADO: + case PPPOE_CODE_PADS: + case PPPOE_CODE_PADT: + (*pr)("No parser for PPPoE control frame.\n"); + return m_examine_hex(m, off, modif, pr); + break; + } + + if (ph.code != 0) { + (*pr)("Unknown PPPoE code.\n"); + return m_examine_hex(m, off, modif, pr); + } + + return m_examine_ppp(m, off, modif, pr); +} + +static void +m_examine_ppp(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + struct ppp_header h; + unsigned int pktlen; + uint16_t protocol; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(h)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(h), (void *)(&h)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(h); + + protocol = ntohs(h.protocol); + + (*pr)("SPPP: Address = %d(", h.address); + switch (h.address) { + case PPP_ALLSTATIONS: + (*pr)("ALLSTATIONS)\n"); + (*pr)("SPPP: Protocol = %d(", protocol); + switch (protocol) { + case PPP_LCP: + (*pr)("LCP)\n"); + break; + case PPP_PAP: + (*pr)("PAP)\n"); + break; + case PPP_CHAP: + (*pr)("CHAP)\n"); + break; + case PPP_IPCP: + (*pr)("IPCP)\n"); + break; + case PPP_IPV6CP: + (*pr)("IPV6CP)\n"); + break; + case PPP_IP: + (*pr)("IP)\n"); + return m_examine_ip(m, off, modif, pr); + break; + case PPP_IPV6: + (*pr)("IPv6)\n"); + return m_examine_ip6(m, off, modif, pr); + break; + default: + (*pr)("unknown)\n"); + break; + } + break; + case CISCO_MULTICAST: + case CISCO_UNICAST: + if (h.address == CISCO_MULTICAST) { + (*pr)("MULTICAST)\n"); + } + else { + (*pr)("UNICAST)\n"); + } + (*pr)("SPPP: Protocol = %d(", protocol); + switch (protocol) { + case CISCO_KEEPALIVE: + (*pr)("Keepalive)\n"); + break; + case ETHERTYPE_IP: + (*pr)("IP)\n"); + return m_examine_ip(m, off, modif, pr); + break; + case ETHERTYPE_IPV6: + (*pr)("IPv6)\n"); + return m_examine_ip6(m, off, modif, pr); + break; + default: + (*pr)("unknown)\n"); + break; + } + break; + default: + (*pr)("unknown)\n", h.address); + break; + } + + (*pr)("No parser for address %d, protocol %d\n", h.address, protocol); + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_arp(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct arphdr ar; + uint16_t hrd, op; + struct in_addr isaddr, itaddr; + uint8_t esaddr[ETHER_ADDR_LEN], etaddr[ETHER_ADDR_LEN]; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(ar)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(ar), (void *)(&ar)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(ar); + + hrd = ntohs(ar.ar_hrd); + (*pr)("ARP: AddressType = %u(", hrd); + switch (hrd) { + case ARPHRD_ETHER: + (*pr)("ETHER)\n"); + break; + case ARPHRD_IEEE802: + (*pr)("IEEE802)\n"); + break; + default: + (*pr)("unknown)\n"); + return m_examine_hex(m, off, modif, pr); + break; + } + (*pr)("ARP: Protocol Address Format = %u\n", ntohs(ar.ar_pro)); + (*pr)("ARP: Protocol Address Length = %u\n", ar.ar_pln); + (*pr)("ARP: H/W Address Length = %u\n", ar.ar_hln); + op = ntohs(ar.ar_op); + (*pr)("ARP: Operation = %u(", op); + switch (op) { + case ARPOP_REQUEST: + (*pr)("REQUEST)\n"); + break; + case ARPOP_REPLY: + (*pr)("REPLY)\n"); + break; + case ARPOP_REVREQUEST: + (*pr)("REVREQUEST)\n"); + break; + case ARPOP_REVREPLY: + (*pr)("REVREPLY)\n"); + break; + case ARPOP_INVREQUEST: + (*pr)("INVREQUEST)\n"); + break; + case ARPOP_INVREPLY: + (*pr)("INVREPLY)\n"); + break; + } + + if (ar.ar_hln == 0 || ar.ar_pln == 0 || + ar.ar_hln != sizeof(esaddr) || ar.ar_pln != sizeof(isaddr)) { + (*pr)("Cannot parse.\n"); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(esaddr), (void *)(esaddr)) < 0) { + (*pr)("Cannot read payload\n"); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(esaddr); + (*pr)("ARP: Ether Src = %s\n", str_ethaddr(esaddr)); + + if (m_peek_data(m, off, sizeof(isaddr), (void *)(&isaddr)) < 0) { + (*pr)("Cannot read payload\n"); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(isaddr); + (*pr)("ARP: IP Src = %s\n", str_ipaddr(&isaddr)); + + if (m_peek_data(m, off, sizeof(etaddr), (void *)(etaddr)) < 0) { + (*pr)("Cannot read payload\n"); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(etaddr); + (*pr)("ARP: Ether Tgt = %s\n", str_ethaddr(etaddr)); + + if (m_peek_data(m, off, sizeof(itaddr), (void *)(&itaddr)) < 0) { + (*pr)("Cannot read payload\n"); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(itaddr); + (*pr)("ARP: IP Tgt = %s\n", str_ipaddr(&itaddr)); + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_ip(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct ip ip; + uint16_t offset; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(ip)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(ip), (void *)(&ip)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(ip); + + (*pr)("IP: Version = %u\n", ip.ip_v); + (*pr)("IP: Header Length = %u\n", (ip.ip_hl << 2)); + (*pr)("IP: ToS = 0x%02x\n", ip.ip_tos); + (*pr)("IP: Packet Length = %u\n", ntohs(ip.ip_len)); + (*pr)("IP: ID = %u\n", ntohs(ip.ip_id)); + offset = ntohs(ip.ip_off); + (*pr)("IP: Offset = %u\n", (offset & IP_OFFMASK)); + if (offset & IP_RF) { + (*pr)("IP: Flag 0x%04x (reserved)\n", IP_RF); + } + if (offset & IP_EF) { + (*pr)("IP: Flag 0x%04x (evil flag)\n", IP_EF); + } + if (offset & IP_DF) { + (*pr)("IP: Flag 0x%04x (don't fragment)\n", IP_DF); + } + if (offset & IP_MF) { + (*pr)("IP: Flag 0x%04x (more fragment)\n", IP_MF); + } + (*pr)("IP: TTL = %u\n", ip.ip_ttl); + (*pr)("IP: protocol = %u(%s)\n", ip.ip_p, str_ipproto(ip.ip_p)); + (*pr)("IP: Src = %s\n", str_ipaddr(&ip.ip_src)); + (*pr)("IP: Dst = %s\n", str_ipaddr(&ip.ip_dst)); + + + switch (ip.ip_p) { + case IPPROTO_ICMP: + return m_examine_icmp(m, off, modif, pr); + break; + case IPPROTO_TCP: + return m_examine_tcp(m, off, modif, pr); + break; + case IPPROTO_UDP: + return m_examine_udp(m, off, modif, pr); + break; + default: + break; + } + + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_icmp(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct icmp icmphdr; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(icmphdr)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(icmphdr), (void *)(&icmphdr)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(icmphdr); + + (*pr)("ICMP: Type = %u(", icmphdr.icmp_type); + switch (icmphdr.icmp_type) { + case ICMP_ECHOREPLY: + (*pr)("Echo Reply)\n"); + break; + case ICMP_UNREACH: + (*pr)("Destination Unreachable)\n"); + break; + case ICMP_SOURCEQUENCH: + (*pr)("Source Quench)\n"); + break; + case ICMP_REDIRECT: + (*pr)("Redirect)\n"); + break; + case ICMP_TIMXCEED: + (*pr)("Time Exceeded)\n"); + break; + default: + (*pr)("unknown)\n"); + break; + } + (*pr)("ICMP: Code = %d\n", icmphdr.icmp_code); + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_ip6(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct ip6_hdr ip6; + struct ip6_hbh hbh; + int hbhlen; + uint32_t flow; + uint8_t vfc; + uint8_t nxt; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(ip6)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(ip6), (void *)(&ip6)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(ip6); + + vfc = ip6.ip6_vfc; + (*pr)("IPv6: Version = %u\n", (vfc & IPV6_VERSION_MASK) >> 4); + flow = ntohl(ip6.ip6_flow); + (*pr)("IPv6: Flow INFO = 0x%07x\n", flow & IPV6_FLOWINFO_MASK); + (*pr)("IPv6: Payload Length = %u\n", ip6.ip6_plen); + nxt = ip6.ip6_nxt; + (*pr)("IPv6: Next Header = %u(%s)\n", nxt, str_ipproto(nxt)); + (*pr)("IPv6: Hop Limit = %u\n", ip6.ip6_hlim); + (*pr)("IPv6: Src = %s\n", str_ip6addr(&ip6.ip6_src)); + (*pr)("IPv6: Dst = %s\n", str_ip6addr(&ip6.ip6_dst)); + + /* Strip Hop-by-Hop options */ + if (nxt == IPPROTO_HOPOPTS) { + if (m_peek_data(m, off, sizeof(hbh), (void *)(&hbh)) < 0) { + (*pr)("Cannot read option\n"); + return m_examine_hex(m, off, modif, pr); + } + hbhlen = (hbh.ip6h_len + 1) << 3; + nxt = hbh.ip6h_nxt; + off += hbhlen; + + (*pr)("IPv6: Stripped Hop-by-Hop\n"); + (*pr)("IPv6: Next Header = %u(%s)\n", nxt, str_ipproto(nxt)); + } + + switch (nxt) { + case IPPROTO_IPV6_ICMP: + return m_examine_icmp6(m, off, modif, pr); + break; + case IPPROTO_TCP: + return m_examine_tcp(m, off, modif, pr); + break; + case IPPROTO_UDP: + return m_examine_udp(m, off, modif, pr); + break; + default: + break; + } + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_icmp6(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct icmp6_hdr icmp6; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(icmp6)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(icmp6), (void *)(&icmp6)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(icmp6); + + (*pr)("ICMP6: Type = %u(", icmp6.icmp6_type); + switch (icmp6.icmp6_type) { + case ICMP6_DST_UNREACH: + (*pr)("Destination Unreachable)\n"); + break; + case ICMP6_PACKET_TOO_BIG: + (*pr)("Packet Too Big)\n"); + break; + case ICMP6_TIME_EXCEEDED: + (*pr)("Time Exceeded)\n"); + break; + case ICMP6_PARAM_PROB: + (*pr)("Parameter Problem)\n"); + break; + case ICMP6_ECHO_REQUEST: + (*pr)("Echo Request)\n"); + break; + case ICMP6_ECHO_REPLY: + (*pr)("Echo Reply)\n"); + break; + + case MLD_LISTENER_QUERY: + (*pr)("MLD Listener Query)\n"); + break; + case MLD_LISTENER_REPORT: + (*pr)("MLD Listener Report)\n"); + break; + case MLD_LISTENER_DONE: + (*pr)("MLD Listener Done)\n"); + break; + + case ND_ROUTER_SOLICIT: + (*pr)("Router Solicitation)\n"); + break; + case ND_ROUTER_ADVERT: + (*pr)("Router Advertizement)\n"); + break; + case ND_NEIGHBOR_SOLICIT: + (*pr)("Neighbor Solicitation)\n"); + break; + case ND_NEIGHBOR_ADVERT: + (*pr)("Neighbor Advertizement)\n"); + break; + case ND_REDIRECT: + (*pr)("Redirect)\n"); + break; + + default: + (*pr)("unknown)\n"); + break; + } + (*pr)("ICMP6: Code = %u\n", icmp6.icmp6_code); + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_tcp(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct tcphdr tcp; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(tcp)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(tcp), (void *)(&tcp)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(tcp); + + (*pr)("TCP: Src = %u\n", ntohs(tcp.th_sport)); + (*pr)("TCP: Dst = %u\n", ntohs(tcp.th_dport)); + (*pr)("TCP: Seq. = %u\n", ntohl(tcp.th_seq)); + (*pr)("TCP: Ack. = %u\n", ntohl(tcp.th_ack)); + (*pr)("TCP: Header Length = %u\n", ntohl(tcp.th_off) << 2); + if (tcp.th_flags) { + (*pr)("TCP: Flags 0x%02x : ", tcp.th_flags); + if (tcp.th_flags & TH_FIN) + (*pr)("FIN "); + if (tcp.th_flags & TH_SYN) + (*pr)("SYN "); + if (tcp.th_flags & TH_RST) + (*pr)("RST "); + if (tcp.th_flags & TH_PUSH) + (*pr)("PUSH "); + if (tcp.th_flags & TH_URG) + (*pr)("URG "); + if (tcp.th_flags & TH_ECE) + (*pr)("ECE "); + if (tcp.th_flags & TH_CWR) + (*pr)("CWR "); + (*pr)("\n"); + } + (*pr)("TCP: Windows Size = %u\n", ntohs(tcp.th_win)); + (*pr)("TCP: Urgent Pointer = %u\n", ntohs(tcp.th_urp)); + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_udp(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + struct udphdr udp; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen < sizeof(udp)) { + (*pr)("%s: too short mbuf chain\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + + if (m_peek_data(m, off, sizeof(udp), (void *)(&udp)) < 0) { + (*pr)("%s: cannot read header\n", __func__); + return m_examine_hex(m, off, modif, pr); + } + off += sizeof(udp); + + (*pr)("UDP: Src = %u\n", ntohs(udp.uh_sport)); + (*pr)("UDP: Dst = %u\n", ntohs(udp.uh_dport)); + (*pr)("UDP: Length = %u\n", ntohs(udp.uh_ulen)); + + return m_examine_hex(m, off, modif, pr); +} + +static void +m_examine_hex(const struct mbuf *m, int off, const char *modif, + void (*pr)(const char *, ...)) +{ + unsigned int pktlen; + int newline = 0; + uint8_t v; + + pktlen = m_peek_len(m, modif) - off; + if (pktlen > EXAMINE_HEX_LIMIT) + pktlen = EXAMINE_HEX_LIMIT; + + if (pktlen == 0) + return; + + (*pr)("offset %04d: ", off); + while (pktlen > 0) { + if (m_peek_data(m, off, sizeof(v), (void *)(&v)) < 0) + break; + pktlen --; + off++; + newline++; + + (*pr)("%02x", v); + if (pktlen == 0) + break; + + if ((newline % EXAMINE_HEX_COL) == 0) { + (*pr)("\n"); + (*pr)("offset %04d: ", off); + } + else { + (*pr)(" "); + } + } + (*pr)("\n"); +} + +void +m_examine(const struct mbuf *m, int af, const char *modif, + void (*pr)(const char *, ...)) +{ + if (m == NULL) + return; + + if (pr == NULL) + return; + + switch (af) { + case AF_UNSPEC: + return m_examine_hex(m, 0, modif, pr); + break; + case AF_ETHER: + return m_examine_ether(m, 0, modif, pr); + break; + case AF_ARP: + return m_examine_arp(m, 0, modif, pr); + break; + case AF_INET: + return m_examine_ip(m, 0, modif, pr); + break; + case AF_INET6: + return m_examine_ip6(m, 0, modif, pr); + break; + default: + (*pr)("No parser for AF %d\n", af); + return m_examine_hex(m, 0, modif, pr); + break; + } + + /* not reached */ + return; +}