コード例 #1
0
ファイル: print-rrcp.c プロジェクト: 0-kaladin/ad-away
/*
 * Print RRCP requests
 */
void
rrcp_print(netdissect_options *ndo,
	  register const u_char *cp,
	  u_int length _U_)
{
	const u_char *rrcp;
	uint8_t rrcp_proto;
	uint8_t rrcp_opcode;
	register const struct ether_header *ep;
	char proto_str[16];
	char opcode_str[32];

	ep = (const struct ether_header *)cp;
	rrcp = cp + ETHER_HDRLEN;

	ND_TCHECK(*(rrcp + RRCP_PROTO_OFFSET));
	rrcp_proto = *(rrcp + RRCP_PROTO_OFFSET);
	ND_TCHECK(*(rrcp + RRCP_OPCODE_ISREPLY_OFFSET));
	rrcp_opcode = (*(rrcp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_OPCODE_MASK;
        ND_PRINT((ndo, "%s > %s, %s %s",
		etheraddr_string(ndo, ESRC(ep)),
		etheraddr_string(ndo, EDST(ep)),
		tok2strbuf(proto_values,"RRCP-0x%02x",rrcp_proto,proto_str,sizeof(proto_str)),
		((*(rrcp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY) ? "reply" : "query"));
	if (rrcp_proto==1){
    	    ND_PRINT((ndo, ": %s",
		     tok2strbuf(opcode_values,"unknown opcode (0x%02x)",rrcp_opcode,opcode_str,sizeof(opcode_str))));
	}
	if (rrcp_opcode==1 || rrcp_opcode==2){
	    ND_TCHECK2(*(rrcp + RRCP_REG_ADDR_OFFSET), 6);
    	    ND_PRINT((ndo, " addr=0x%04x, data=0x%08x",
                     EXTRACT_LE_16BITS(rrcp + RRCP_REG_ADDR_OFFSET),
                     EXTRACT_LE_32BITS(rrcp + RRCP_REG_DATA_OFFSET)));
	}
	if (rrcp_proto==1){
	    ND_TCHECK2(*(rrcp + RRCP_AUTHKEY_OFFSET), 2);
    	    ND_PRINT((ndo, ", auth=0x%04x",
		  EXTRACT_16BITS(rrcp + RRCP_AUTHKEY_OFFSET)));
	}
	if (rrcp_proto==1 && rrcp_opcode==0 &&
	     ((*(rrcp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY)){
	    ND_TCHECK2(*(rrcp + RRCP_VENDOR_ID_OFFSET), 4);
	    ND_PRINT((ndo, " downlink_port=%d, uplink_port=%d, uplink_mac=%s, vendor_id=%08x ,chip_id=%04x ",
		     *(rrcp + RRCP_DOWNLINK_PORT_OFFSET),
		     *(rrcp + RRCP_UPLINK_PORT_OFFSET),
		     etheraddr_string(ndo, rrcp + RRCP_UPLINK_MAC_OFFSET),
		     EXTRACT_32BITS(rrcp + RRCP_VENDOR_ID_OFFSET),
		     EXTRACT_16BITS(rrcp + RRCP_CHIP_ID_OFFSET)));
	}else if (rrcp_opcode==1 || rrcp_opcode==2 || rrcp_proto==2){
	    ND_TCHECK2(*(rrcp + RRCP_COOKIE2_OFFSET), 4);
	    ND_PRINT((ndo, ", cookie=0x%08x%08x ",
		    EXTRACT_32BITS(rrcp + RRCP_COOKIE2_OFFSET),
		    EXTRACT_32BITS(rrcp + RRCP_COOKIE1_OFFSET)));
	}
	return;

trunc:
	ND_PRINT((ndo, "[|rrcp]"));
}
コード例 #2
0
ファイル: print-rrcp.c プロジェクト: jaredmcneill/netbsd-src
/*
 * Print RRCP requests
 */
void
rrcp_print(netdissect_options *ndo,
	  register const u_char *cp,
	  u_int length _U_,
	  const struct lladdr_info *src,
	  const struct lladdr_info *dst)
{
	uint8_t rrcp_proto;
	uint8_t rrcp_opcode;

	ND_TCHECK(*(cp + RRCP_PROTO_OFFSET));
	rrcp_proto = *(cp + RRCP_PROTO_OFFSET);
	ND_TCHECK(*(cp + RRCP_OPCODE_ISREPLY_OFFSET));
	rrcp_opcode = (*(cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_OPCODE_MASK;
	if (src != NULL && dst != NULL) {
		ND_PRINT((ndo, "%s > %s, ",
			(src->addr_string)(ndo, src->addr),
			(dst->addr_string)(ndo, dst->addr)));
	}
	ND_PRINT((ndo, "%s %s",
		tok2str(proto_values,"RRCP-0x%02x",rrcp_proto),
		((*(cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY) ? "reply" : "query"));
	if (rrcp_proto==1){
    	    ND_PRINT((ndo, ": %s",
		     tok2str(opcode_values,"unknown opcode (0x%02x)",rrcp_opcode)));
	}
	if (rrcp_opcode==1 || rrcp_opcode==2){
	    ND_TCHECK2(*(cp + RRCP_REG_ADDR_OFFSET), 6);
    	    ND_PRINT((ndo, " addr=0x%04x, data=0x%08x",
		     EXTRACT_LE_16BITS(cp + RRCP_REG_ADDR_OFFSET),
		     EXTRACT_LE_32BITS(cp + RRCP_REG_DATA_OFFSET)));
	}
	if (rrcp_proto==1){
	    ND_TCHECK2(*(cp + RRCP_AUTHKEY_OFFSET), 2);
    	    ND_PRINT((ndo, ", auth=0x%04x",
		  EXTRACT_16BITS(cp + RRCP_AUTHKEY_OFFSET)));
	}
	if (rrcp_proto==1 && rrcp_opcode==0 &&
	     ((*(cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY)){
	    ND_TCHECK2(*(cp + RRCP_VENDOR_ID_OFFSET), 4);
	    ND_PRINT((ndo, " downlink_port=%d, uplink_port=%d, uplink_mac=%s, vendor_id=%08x ,chip_id=%04x ",
		     *(cp + RRCP_DOWNLINK_PORT_OFFSET),
		     *(cp + RRCP_UPLINK_PORT_OFFSET),
		     etheraddr_string(ndo, cp + RRCP_UPLINK_MAC_OFFSET),
		     EXTRACT_32BITS(cp + RRCP_VENDOR_ID_OFFSET),
		     EXTRACT_16BITS(cp + RRCP_CHIP_ID_OFFSET)));
	}else if (rrcp_opcode==1 || rrcp_opcode==2 || rrcp_proto==2){
	    ND_TCHECK2(*(cp + RRCP_COOKIE2_OFFSET), 4);
	    ND_PRINT((ndo, ", cookie=0x%08x%08x ",
		    EXTRACT_32BITS(cp + RRCP_COOKIE2_OFFSET),
		    EXTRACT_32BITS(cp + RRCP_COOKIE1_OFFSET)));
	}
	return;

trunc:
	ND_PRINT((ndo, "[|rrcp]"));
}
コード例 #3
0
/*
 * Print the 802.11 MAC header if eflag is set, and set "*srcp" and "*dstp"
 * to point to the source and destination MAC addresses in any case if
 * "srcp" and "dstp" aren't null.
 */
static void
ieee_802_11_hdr_print(u_int16_t fc, const u_char *p, u_int hdrlen,
    u_int meshdrlen, const u_int8_t **srcp, const u_int8_t **dstp)
{
	if (vflag) {
		if (FC_MORE_DATA(fc))
			printf("More Data ");
		if (FC_MORE_FLAG(fc))
			printf("More Fragments ");
		if (FC_POWER_MGMT(fc))
			printf("Pwr Mgmt ");
		if (FC_RETRY(fc))
			printf("Retry ");
		if (FC_ORDER(fc))
			printf("Strictly Ordered ");
		if (FC_WEP(fc))
			printf("WEP Encrypted ");
		if (FC_TYPE(fc) != T_CTRL || FC_SUBTYPE(fc) != CTRL_PS_POLL)
			printf("%dus ",
			    EXTRACT_LE_16BITS(
			        &((const struct mgmt_header_t *)p)->duration));
	}
	if (meshdrlen != 0) {
		const struct meshcntl_t *mc =
		    (const struct meshcntl_t *)&p[hdrlen - meshdrlen];
		int ae = mc->flags & 3;

		printf("MeshData (AE %d TTL %u seq %u", ae, mc->ttl,
		    EXTRACT_LE_32BITS(mc->seq));
		if (ae > 0)
			printf(" A4:%s", etheraddr_string(mc->addr4));
		if (ae > 1)
			printf(" A5:%s", etheraddr_string(mc->addr5));
		if (ae > 2)
			printf(" A6:%s", etheraddr_string(mc->addr6));
		printf(") ");
	}

	switch (FC_TYPE(fc)) {
	case T_MGMT:
		mgmt_header_print(p, srcp, dstp);
		break;
	case T_CTRL:
		ctrl_header_print(fc, p, srcp, dstp);
		break;
	case T_DATA:
		data_header_print(fc, p, srcp, dstp);
		break;
	default:
		printf("(header) unknown IEEE802.11 frame type (%d)",
		    FC_TYPE(fc));
		*srcp = NULL;
		*dstp = NULL;
		break;
	}
}
コード例 #4
0
ファイル: print-ppi.c プロジェクト: GerardGarcia/tcpdump
static u_int
ppi_print(netdissect_options *ndo,
               const struct pcap_pkthdr *h, const u_char *p)
{
	if_printer printer;
	const ppi_header_t *hdr;
	u_int caplen = h->caplen;
	u_int length = h->len;
	uint16_t len;
	uint32_t dlt;
	uint32_t hdrlen;
	struct pcap_pkthdr nhdr;

	if (caplen < sizeof(ppi_header_t)) {
		ND_PRINT((ndo, "[|ppi]"));
		return (caplen);
	}

	hdr = (const ppi_header_t *)p;
	len = EXTRACT_LE_16BITS(&hdr->ppi_len);
	if (caplen < len) {
		/*
		 * If we don't have the entire PPI header, don't
		 * bother.
		 */
		ND_PRINT((ndo, "[|ppi]"));
		return (caplen);
	}
	if (len < sizeof(ppi_header_t)) {
		ND_PRINT((ndo, "[|ppi]"));
		return (len);
	}
	dlt = EXTRACT_LE_32BITS(&hdr->ppi_dlt);

	if (ndo->ndo_eflag)
		ppi_header_print(ndo, p, length);

	length -= len;
	caplen -= len;
	p += len;

	if ((printer = lookup_printer(dlt)) != NULL) {
		nhdr = *h;
		nhdr.caplen = caplen;
		nhdr.len = length;
		hdrlen = printer(ndo, &nhdr, p);
	} else {
		if (!ndo->ndo_eflag)
			ppi_header_print(ndo, (const u_char *)hdr, length + len);

		if (!ndo->ndo_suppress_default_print)
			ND_DEFAULTPRINT(p, caplen);
		hdrlen = 0;
	}
	return (len + hdrlen);
}
コード例 #5
0
ファイル: print-pktap.c プロジェクト: raniyuva/tcpdump
static inline void
pktap_header_print(struct netdissect_options *ndo, const u_char *bp, u_int length)
{
	const pktap_header_t *hdr;
	uint32_t dlt, hdrlen;

	hdr = (const pktap_header_t *)bp;

	dlt = EXTRACT_LE_32BITS(&hdr->pkt_dlt);
	hdrlen = EXTRACT_LE_32BITS(&hdr->pkt_len);
	if (!ndo->ndo_qflag) {
		ND_PRINT((ndo,", DLT %s (%d) len %d",
			  pcap_datalink_val_to_name(dlt), dlt, hdrlen));
        } else {
		ND_PRINT((ndo,", %s", pcap_datalink_val_to_name(dlt)));
        }

	ND_PRINT((ndo, ", length %u: ", length));
}
コード例 #6
0
ファイル: cpack.c プロジェクト: 2014-class/freerouter
/* Unpack a 32-bit unsigned integer. */
int
cpack_uint32(struct cpack_state *cs, u_int32_t *u)
{
	u_int8_t *next;

	if ((next = cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
		return -1;

	*u = EXTRACT_LE_32BITS(next);

	/* Move pointer past the u_int32_t. */
	cs->c_next = next + sizeof(*u);
	return 0;
}
コード例 #7
0
static int
wep_print(const u_char *p)
{
	u_int32_t iv;

	if (!TTEST2(*p, IEEE802_11_IV_LEN + IEEE802_11_KID_LEN))
		return 0;
	iv = EXTRACT_LE_32BITS(p);

	printf("Data IV:%3x Pad %x KeyID %x", IV_IV(iv), IV_PAD(iv),
	    IV_KEYID(iv));

	return 1;
}
コード例 #8
0
ファイル: print-msnlb.c プロジェクト: Alkzndr/freebsd
void
msnlb_print(netdissect_options *ndo, const u_char *bp, u_int length)
{
	const struct msnlb_heartbeat_pkt *hb;

	hb = (struct msnlb_heartbeat_pkt *)bp;
	ND_TCHECK(*hb);

	ND_PRINT((ndo, "MS NLB heartbeat, host priority: %u,",
		EXTRACT_LE_32BITS(&(hb->host_prio))));
	ND_PRINT((ndo, " cluster IP: %s,", ipaddr_string(&(hb->virtual_ip))));
	ND_PRINT((ndo, " host IP: %s", ipaddr_string(&(hb->host_ip))));
	return;
trunc:
	ND_PRINT((ndo, "[|MS NLB]"));
}
コード例 #9
0
void
msnlb_print(netdissect_options *ndo, packetbody_t bp, u_int length _U_)
{
	__capability const struct msnlb_heartbeat_pkt *hb;

	hb = (__capability struct msnlb_heartbeat_pkt *)bp;
	ND_PACKET_HAS_ONE_OR_TRUNC(hb);

	ND_PRINT((ndo, "MS NLB heartbeat, host priority: %u,",
		EXTRACT_LE_32BITS(&(hb->host_prio))));
	ND_PRINT((ndo, " cluster IP: %s,", ipaddr_string(&(hb->virtual_ip))));
	ND_PRINT((ndo, " host IP: %s", ipaddr_string(&(hb->host_ip))));
	return;
trunc:
	ND_PRINT((ndo, "[|MS NLB]"));
}
コード例 #10
0
int
juniper_read_tlv_value(packetbody_t p, u_int tlv_type, u_int tlv_len) {

   int tlv_value;

   /* TLVs < 128 are little endian encoded */
   if (tlv_type < 128) {
       switch (tlv_len) {
       case 1:
           tlv_value = *p;
           break;
       case 2:
           tlv_value = EXTRACT_LE_16BITS(p);
           break;
       case 3:
           tlv_value = EXTRACT_LE_24BITS(p);
           break;
       case 4:
           tlv_value = EXTRACT_LE_32BITS(p);
           break;
       default:
           tlv_value = -1;
           break;
       }
   } else {
       /* TLVs >= 128 are big endian encoded */
       switch (tlv_len) {
       case 1:
           tlv_value = *p;
           break;
       case 2:
           tlv_value = EXTRACT_16BITS(p);
           break;
       case 3:
           tlv_value = EXTRACT_24BITS(p);
           break;
       case 4:
           tlv_value = EXTRACT_32BITS(p);
           break;
       default:
           tlv_value = -1;
           break;
       }
   }
   return tlv_value;
}
コード例 #11
0
ファイル: print-ppi.c プロジェクト: EliseuTorres/tcpdump
static inline void
ppi_header_print(netdissect_options *ndo, const u_char *bp, u_int length)
{
	const ppi_header_t *hdr;
	uint16_t len;
	uint32_t dlt;

	hdr = (const ppi_header_t *)bp;

	len = EXTRACT_LE_16BITS(&hdr->ppi_len);
	dlt = EXTRACT_LE_32BITS(&hdr->ppi_dlt);

	if (!ndo->ndo_qflag) {
		ND_PRINT((ndo, "V.%d DLT %s (%d) len %d", hdr->ppi_ver,
			  pcap_datalink_val_to_name(dlt), dlt,
                          len));
        } else {
		ND_PRINT((ndo, "%s", pcap_datalink_val_to_name(dlt)));
        }

	ND_PRINT((ndo, ", length %u: ", length));
}
コード例 #12
0
ファイル: pcap.c プロジェクト: jolin90/smartconfig
static u_int ieee802_11_radio_print(struct smartconfig *sc, const u_char * p, u_int length, u_int caplen)
{
#define BIT(n)  (1U << n)
#define IS_EXTENDED(__p)    \
	(EXTRACT_LE_32BITS(__p) & BIT(IEEE80211_RADIOTAP_EXT)) != 0

	struct cpack_state cpacker;
	const struct ieee80211_radiotap_header *hdr;
	uint32_t presentflags;
	const uint32_t *presentp;
	u_int len;
	uint16_t channel = 0;
	uint8_t flags;

	if (caplen < sizeof(*hdr)) {
		return caplen;
	}

	hdr = (const struct ieee80211_radiotap_header *)p;
	len = EXTRACT_LE_16BITS(&hdr->it_len);

	if (caplen < len) {
		return caplen;
	}

	cpack_init(&cpacker, (const uint8_t *)hdr, len);	/* align against header start */
	cpack_advance(&cpacker, sizeof(*hdr));	/* includes the 1st bitmap */

	flags = 0;

	presentp = &hdr->it_present;
	presentflags = EXTRACT_LE_32BITS(presentp);
	print_in_radiotap_namespace(sc, &cpacker, &flags, presentflags, 0, &channel);

	return len + ieee802_11_print(sc, p + len, length - len, caplen - len, channel);
}
コード例 #13
0
ファイル: print-pktap.c プロジェクト: raniyuva/tcpdump
/*
 * This is the top level routine of the printer.  'p' points
 * to the ether header of the packet, 'h->ts' is the timestamp,
 * 'h->len' is the length of the packet off the wire, and 'h->caplen'
 * is the number of bytes actually captured.
 */
u_int
pktap_if_print(struct netdissect_options *ndo,
               const struct pcap_pkthdr *h, const u_char *p)
{
	uint32_t dlt, hdrlen, rectype;
	u_int caplen = h->caplen;
	u_int length = h->len;
	if_ndo_printer ndo_printer;
        if_printer printer;
	pktap_header_t *hdr;

	if (caplen < sizeof(pktap_header_t) || length < sizeof(pktap_header_t)) {
		ND_PRINT((ndo, "[|pktap]"));
		return (0);
	}
	hdr = (pktap_header_t *)p;
	dlt = EXTRACT_LE_32BITS(&hdr->pkt_dlt);
	hdrlen = EXTRACT_LE_32BITS(&hdr->pkt_len);
	if (hdrlen < sizeof(pktap_header_t)) {
		/*
		 * Claimed header length < structure length.
		 * XXX - does this just mean some fields aren't
		 * being supplied, or is it truly an error (i.e.,
		 * is the length supplied so that the header can
		 * be expanded in the future)?
		 */
		ND_PRINT((ndo, "[|pktap]"));
		return (0);
	}
	if (caplen < hdrlen || length < hdrlen) {
		ND_PRINT((ndo, "[|pktap]"));
		return (hdrlen);
	}

	if (ndo->ndo_eflag)
		pktap_header_print(ndo, p, length);

	length -= hdrlen;
	caplen -= hdrlen;
	p += hdrlen;

	rectype = EXTRACT_LE_32BITS(&hdr->pkt_rectype);
	switch (rectype) {

	case PKT_REC_NONE:
		ND_PRINT((ndo, "no data"));
		break;

	case PKT_REC_PACKET:
		if ((printer = lookup_printer(dlt)) != NULL) {
			printer(h, p);
		} else if ((ndo_printer = lookup_ndo_printer(dlt)) != NULL) {
			ndo_printer(ndo, h, p);
		} else {
			if (!ndo->ndo_eflag)
				pktap_header_print(ndo, (u_char *)hdr,
						length + hdrlen);

			if (!ndo->ndo_suppress_default_print)
				ND_DEFAULTPRINT(p, caplen);
		}
		break;
	}

	return (hdrlen);
}
コード例 #14
0
static u_int
ieee802_11_radio_print(const u_char *p, u_int length, u_int caplen)
{
#define	BITNO_32(x) (((x) >> 16) ? 16 + BITNO_16((x) >> 16) : BITNO_16((x)))
#define	BITNO_16(x) (((x) >> 8) ? 8 + BITNO_8((x) >> 8) : BITNO_8((x)))
#define	BITNO_8(x) (((x) >> 4) ? 4 + BITNO_4((x) >> 4) : BITNO_4((x)))
#define	BITNO_4(x) (((x) >> 2) ? 2 + BITNO_2((x) >> 2) : BITNO_2((x)))
#define	BITNO_2(x) (((x) & 2) ? 1 : 0)
#define	BIT(n)	(1 << n)
#define	IS_EXTENDED(__p)	\
	    (EXTRACT_LE_32BITS(__p) & BIT(IEEE80211_RADIOTAP_EXT)) != 0

	struct cpack_state cpacker;
	struct ieee80211_radiotap_header *hdr;
	u_int32_t present, next_present;
	u_int32_t *presentp, *last_presentp;
	enum ieee80211_radiotap_type bit;
	int bit0;
	const u_char *iter;
	u_int len;
	int pad;

	if (caplen < sizeof(*hdr)) {
		printf("[|802.11]");
		return caplen;
	}

	hdr = (struct ieee80211_radiotap_header *)p;

	len = EXTRACT_LE_16BITS(&hdr->it_len);

	if (caplen < len) {
		printf("[|802.11]");
		return caplen;
	}
	for (last_presentp = &hdr->it_present;
	     IS_EXTENDED(last_presentp) &&
	     (u_char*)(last_presentp + 1) <= p + len;
	     last_presentp++);

	/* are there more bitmap extensions than bytes in header? */
	if (IS_EXTENDED(last_presentp)) {
		printf("[|802.11]");
		return caplen;
	}

	iter = (u_char*)(last_presentp + 1);

	if (cpack_init(&cpacker, (u_int8_t*)iter, len - (iter - p)) != 0) {
		/* XXX */
		printf("[|802.11]");
		return caplen;
	}

	/* Assume no Atheros padding between 802.11 header and body */
	pad = 0;
	for (bit0 = 0, presentp = &hdr->it_present; presentp <= last_presentp;
	     presentp++, bit0 += 32) {
		for (present = EXTRACT_LE_32BITS(presentp); present;
		     present = next_present) {
			/* clear the least significant bit that is set */
			next_present = present & (present - 1);

			/* extract the least significant bit that is set */
			bit = (enum ieee80211_radiotap_type)
			    (bit0 + BITNO_32(present ^ next_present));

			if (print_radiotap_field(&cpacker, bit, &pad) != 0)
				goto out;
		}
	}
out:
	return len + ieee802_11_print(p + len, length - len, caplen - len, pad);
#undef BITNO_32
#undef BITNO_16
#undef BITNO_8
#undef BITNO_4
#undef BITNO_2
#undef BIT
}
コード例 #15
0
ファイル: pcap-print.c プロジェクト: ianrose14/libpktparse
/*
 * Called by pcap_loop; prints a text description of the packet.
 */
static void handle_packet(u_char *user, const struct pcap_pkthdr *h,
    const u_char *sp)
{
    /*
     * note; there is lots of double writing in this function (first stuff is
     * written to tmpbuf, then its copied from there to buf) but the code is
     * "safer" this way (no need to keep track of buffer offsets, etc.) and so
     * far performance isn't an issue.
     */
    char buf[1024] = "", tmpbuf[1024] = "";
    int buflen = sizeof(buf);
    int tmpbuflen = sizeof(tmpbuf);

    ++pkt_count;

    if (use_numbering)
        snprintf(buf, buflen, "%u: ", pkt_count);

    switch (tsfmt) {
    case TS_NONE:
        /* do nothing */
        break;

    case TS_ABS:
        snprintf(tmpbuf, tmpbuflen, "[%u.%06ld] ", h->ts.tv_sec, h->ts.tv_usec);
        break;

    case TS_REL: {
        static struct timeval last_ts = {0, 0};
        assert(h->ts.tv_sec >= last_ts.tv_sec);

        if (last_ts.tv_sec == 0) {
            snprintf(tmpbuf, tmpbuflen, "[%u.%06ld] ", 0, 0l);
        } else if (h->ts.tv_usec >= last_ts.tv_usec) {
            snprintf(tmpbuf, tmpbuflen, "[%u.%06ld] ",
                h->ts.tv_sec - last_ts.tv_sec, h->ts.tv_usec - last_ts.tv_usec);
        } else {  /* h->ts.tv_usec < last_ts.tv_usec */
            assert(h->ts.tv_sec > last_ts.tv_sec);
            snprintf(tmpbuf, tmpbuflen, "[%u.%06ld] ",
                h->ts.tv_sec - last_ts.tv_sec - 1,
                1000000l + h->ts.tv_usec - last_ts.tv_usec);
        }
        last_ts = h->ts;
    }
    case TS_CTIME: {
        char datebuf[32];
        time_t t = h->ts.tv_sec;
        ctime_r(&t, datebuf + 1);
        /* move the year to the front, overwriting the day-of-week */
        memcpy(datebuf, datebuf + 21, 4);
        datebuf[4] = ' ';
        datebuf[20] = '\0';  /* chop the trailing year and newline */
        snprintf(tmpbuf, tmpbuflen, "[%s.%06ld] ", datebuf, h->ts.tv_usec);
        break;
    }
    default:
        assert(0);  /* bad value for tsfmt */
    }

    if (strlcat(buf, tmpbuf, buflen) >= buflen)
        return;

    int start_buflen = strlen(buf);

    int flags = 0;
    if ((enabled_layers[1] + enabled_layers[2] + enabled_layers[3]) == 0) {
        /* if only layer 0 (MAC) is enabled, then ignore bad LLC values */
        flags |= PKTPARSE_IGNORE_BADLLC;
    }

    struct packet pkt;
    if (pktparse_parse(h, sp, dlt, &pkt, flags) == -1) {
        if (quiet) return;
        snprintf(tmpbuf, tmpbuflen, "bad packet: %s", pkt.errmsg);
        strlcat(buf, tmpbuf, buflen);
        goto finish;
    }

    u_char print_sep = 0;

    /* datalink layer */
    if (enabled_layers[0] && ((pkt.ether_hdr != NULL) || (pkt.wifi_hdr != NULL))) {
        int flags = (enabled_layers[0] > 1) ? PKTPARSE_PRINT_VERBOSE : 0;

        if (pkt.ether_hdr) {
            pktparse_print_ethernet(tmpbuf, tmpbuflen, pkt.ether_hdr, flags);
        } else if (pkt.wifi_hdr) {
            pktparse_print_wifi(tmpbuf, tmpbuflen, pkt.wifi_hdr, pkt.mgmt_body,
                flags);
        }

        if (strlcat(buf, tmpbuf, buflen) >= buflen)
            goto finish;

        /* if enabled, also include the FCS field at the end */
        if (print_fcs) {
            /* if packet was truncated at all, then we lost the FCS at the end */
            if ((pkt.caplen == pkt.wirelen) && (pkt.caplen >= 4)) {
                uint32_t index = pkt.caplen - 4;
                uint32_t fcs = EXTRACT_LE_32BITS(pkt.raw_packet + index);
                snprintf(tmpbuf, tmpbuflen, "FCS %08x ", fcs);
                
                if (strlcat(buf, tmpbuf, buflen) >= buflen)
                    goto finish;
            }
        }

        print_sep = 1;
    }

    /* if both network and transport layers are enabled, print them together */
    if (enabled_layers[1] && enabled_layers[2]) {
        int flags = 0;
        if ((enabled_layers[1] > 1) || (enabled_layers[2] > 1))
            flags |= PKTPARSE_PRINT_VERBOSE;

        tmpbuf[0] = '\0';
        switch (pkt.ethertype) {
        case -1:
            /* no ethertype parsed from packet */
            break;
        case ETHERTYPE_IP:
            if (pkt.ip_hdr) {
                if (pkt.tcp_hdr)
                    pktparse_print_tcpip(tmpbuf, tmpbuflen, pkt.ip_hdr, pkt.tcp_hdr, pkt.trans_len, flags);
                else if (pkt.udp_hdr)
                    pktparse_print_udpip(tmpbuf, tmpbuflen, pkt.ip_hdr, pkt.udp_hdr, pkt.trans_len, flags);
                else
                    pktparse_print_ip_proto(tmpbuf, tmpbuflen, pkt.ip_hdr, pkt.ipproto, pkt.trans_len, flags);
            }
            else
                snprintf(tmpbuf, tmpbuflen, "IP ");
            break;
        case ETHERTYPE_IPV6:
            if (pkt.ip6_hdr) {
                if (pkt.tcp_hdr)
                    pktparse_print_tcpip6(tmpbuf, tmpbuflen, pkt.ip6_hdr, pkt.tcp_hdr, pkt.trans_len, flags);
                else if (pkt.udp_hdr)
                    pktparse_print_udpip6(tmpbuf, tmpbuflen, pkt.ip6_hdr, pkt.udp_hdr, pkt.trans_len, flags);
                else
                    pktparse_print_ip6_proto(tmpbuf, tmpbuflen, pkt.ip6_hdr, pkt.ipproto, pkt.trans_len, flags);
            }
            else
                snprintf(tmpbuf, tmpbuflen, "IPv6 ");
            break;
        case ETHERTYPE_ARP:
            snprintf(tmpbuf, tmpbuflen, "ARP ");
            break;
        default:
            snprintf(tmpbuf, tmpbuflen, "ethertype 0x%04X ", pkt.ethertype);
            break;
        }

        if (strlen(tmpbuf) > 0) {
            if (print_sep) {
                if (strlcat(buf, "~ ", buflen) >= buflen)
                    goto finish;
                print_sep = 0;
            }
            if (strlcat(buf, tmpbuf, buflen) >= buflen)
                goto finish;
        }
    }
    else {
        /* if we can't merge network and transport layers, print whichever is enabled */
        tmpbuf[0] = '\0';

        if (enabled_layers[1]) {
            int flags = (enabled_layers[1] > 1) ? PKTPARSE_PRINT_VERBOSE : 0;

            switch (pkt.ethertype) {
            case -1:
                /* no ethertype parsed from packet */
                break;
            case ETHERTYPE_IP:
                if (pkt.ip_hdr)
                    pktparse_print_ip(tmpbuf, tmpbuflen, pkt.ip_hdr, pkt.trans_len, flags);
                break;
            case ETHERTYPE_IPV6:
                if (pkt.ip6_hdr)
                    pktparse_print_ip6(tmpbuf, tmpbuflen, pkt.ip6_hdr, pkt.trans_len, flags);
                break;
            case ETHERTYPE_ARP:
                snprintf(tmpbuf, tmpbuflen, "ARP ");
                break;
            default:
                snprintf(tmpbuf, tmpbuflen, "ethertype 0x%04X ", pkt.ethertype);
                break;
            }
        }
        else if (enabled_layers[2]) {
            int flags = (enabled_layers[2] > 1) ? PKTPARSE_PRINT_VERBOSE : 0;

            switch (pkt.ipproto) {
            case -1:
                /* no ip-proto parsed from packet */
                break;
            case IPPROTO_IP:
                snprintf(tmpbuf, tmpbuflen, "IP (encapsulation) ");
                break;
            case IPPROTO_ICMP:
                snprintf(tmpbuf, tmpbuflen, "ICMP ");
                break;
            case IPPROTO_IGMP:
                snprintf(tmpbuf, tmpbuflen, "IGMP ");
                break;
            case IPPROTO_TCP:
                if (pkt.tcp_hdr)
                    pktparse_print_tcp(tmpbuf, tmpbuflen, pkt.tcp_hdr, pkt.trans_len, flags);
                else
                    snprintf(tmpbuf, tmpbuflen, "TCP ");
                break;
            case IPPROTO_UDP:
                if (pkt.udp_hdr)
                    pktparse_print_udp(tmpbuf, tmpbuflen, pkt.udp_hdr, pkt.trans_len, flags);
                else
                    snprintf(tmpbuf, tmpbuflen, "UDP ");
                break;
            default:
                snprintf(tmpbuf, tmpbuflen, "ipproto 0x%02X  ", pkt.ipproto);
                break;
            }
        }

        if (strlen(tmpbuf) > 0) {
            if (print_sep) {
                if (strlcat(buf, "~ ", buflen) >= buflen)
                    goto finish;
                print_sep = 0;
            }
            if (strlcat(buf, tmpbuf, buflen) >= buflen)
                goto finish;
        }
    }
    
    /* application layer */
    if (enabled_layers[3]) {
        /* only HTTP request parsing is implemented so far */
        struct http_request *req = NULL;

        if (pkt.tcp_hdr != NULL) {
            req = pktparse_parse_http_request((char*)pkt.unparsed,
                pkt.unparsed_len);
        }

        if (req != NULL) {
            if (req->resource[0] == '/')
                snprintf(tmpbuf, tmpbuflen, "%s %s%s ", req->method, req->host,
                    req->resource);
            else
                snprintf(tmpbuf, tmpbuflen, "%s %s/%s ", req->method, req->host,
                    req->resource);
            if (strlcat(buf, tmpbuf, buflen) >= buflen)
                goto finish;

            /* if verbose printing is enabled, include the User-Agent string */
            if (enabled_layers[3] > 1) {
                snprintf(tmpbuf, tmpbuflen, "User-Agent: %s", req->user_agent);
                if (strlcat(buf, tmpbuf, buflen) >= buflen)
                    goto finish;
            }

            free(req);
        }
    }

 finish:
    if (strlen(buf) > start_buflen)
        printf("%s\n", buf);
}