/*
 * Copyright (c) 2009
 *	Siemens AG, All rights reserved.
 *	Dmitry Eremin-Solenikov (dbaryshkov@gmail.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may be used to endorse
 * or promote products derived from this software without specific prior
 * written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* \summary: IEEE 802.15.4 printer */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "netdissect-stdinc.h"

#define ND_LONGJMP_FROM_TCHECK
#include "netdissect.h"
#include "addrtoname.h"

#include "extract.h"

#define CHECK_BIT(num,bit) (((num) >> (bit)) & 0x1)

#define BROKEN_6TISCH_PAN_ID_COMPRESSION 0

/* Frame types from Table 7-1 of 802.15.4-2015 */
static const char *ftypes[] = {
	"Beacon",			/* 0 */
	"Data",				/* 1 */
	"ACK",				/* 2 */
	"Command",			/* 3 */
	"Reserved",			/* 4 */
	"Multipurpose",			/* 5 */
	"Fragment",			/* 6 */
	"Extended"			/* 7 */
};

/* Element IDs for Header IEs from Table 7-7 of 802.15.4-2015 */
static const char *h_ie_names[] = {
	"Vendor Specific Header IE",			/* 0x00 */
	"Reserved 0x01",				/* 0x01 */
	"Reserved 0x02",				/* 0x02 */
	"Reserved 0x03",				/* 0x03 */
	"Reserved 0x04",				/* 0x04 */
	"Reserved 0x05",				/* 0x05 */
	"Reserved 0x06",				/* 0x06 */
	"Reserved 0x07",				/* 0x07 */
	"Reserved 0x08",				/* 0x08 */
	"Reserved 0x09",				/* 0x09 */
	"Reserved 0x0a",				/* 0x0a */
	"Reserved 0x0b",				/* 0x0b */
	"Reserved 0x0c",				/* 0x0c */
	"Reserved 0x0d",				/* 0x0d */
	"Reserved 0x0e",				/* 0x0e */
	"Reserved 0x0f",				/* 0x0f */
	"Reserved 0x10",				/* 0x10 */
	"Reserved 0x11",				/* 0x11 */
	"Reserved 0x12",				/* 0x12 */
	"Reserved 0x13",				/* 0x13 */
	"Reserved 0x14",				/* 0x14 */
	"Reserved 0x15",				/* 0x15 */
	"Reserved 0x16",				/* 0x16 */
	"Reserved 0x17",				/* 0x17 */
	"Reserved 0x18",				/* 0x18 */
	"Reserved 0x19",				/* 0x19 */
	"LE CSL IE",					/* 0x1a */
	"LE RIT IE",					/* 0x1b */
	"DSME PAN descriptor IE",			/* 0x1c */
	"Rendezvous Time IE",				/* 0x1d */
	"Time Correction IE",				/* 0x1e */
	"Reserved 0x1f",				/* 0x1f */
	"Reserved 0x20",				/* 0x20 */
	"Extended DSME PAN descriptor IE",		/* 0x21 */
	"Fragment Sequence Context Description IE",	/* 0x22 */
	"Simplified Superframe Specification IE",	/* 0x23 */
	"Simplified GTS Specification IE",		/* 0x24 */
	"LECIM Capabilities IE",			/* 0x25 */
	"TRLE Descriptor IE",				/* 0x26 */
	"RCC Capabilities IE",				/* 0x27 */
	"RCCN Descriptor IE",				/* 0x28 */
	"Global Time IE",				/* 0x29 */
	"Omnibus Header IE",				/* 0x2a */
	"DA IE",					/* 0x2b */
	"Reserved 0x2c",				/* 0x2c */
	"Reserved 0x2d",				/* 0x2d */
	"Reserved 0x2e",				/* 0x2e */
	"Reserved 0x2f",				/* 0x2f */
	"Reserved 0x30",				/* 0x30 */
	"Reserved 0x31",				/* 0x31 */
	"Reserved 0x32",				/* 0x32 */
	"Reserved 0x33",				/* 0x33 */
	"Reserved 0x34",				/* 0x34 */
	"Reserved 0x35",				/* 0x35 */
	"Reserved 0x36",				/* 0x36 */
	"Reserved 0x37",				/* 0x37 */
	"Reserved 0x38",				/* 0x38 */
	"Reserved 0x39",				/* 0x39 */
	"Reserved 0x3a",				/* 0x3a */
	"Reserved 0x3b",				/* 0x3b */
	"Reserved 0x3c",				/* 0x3c */
	"Reserved 0x3d",				/* 0x3d */
	"Reserved 0x3e",				/* 0x3e */
	"Reserved 0x3f",				/* 0x3f */
	"Reserved 0x40",				/* 0x40 */
	"Reserved 0x41",				/* 0x41 */
	"Reserved 0x42",				/* 0x42 */
	"Reserved 0x43",				/* 0x43 */
	"Reserved 0x44",				/* 0x44 */
	"Reserved 0x45",				/* 0x45 */
	"Reserved 0x46",				/* 0x46 */
	"Reserved 0x47",				/* 0x47 */
	"Reserved 0x48",				/* 0x48 */
	"Reserved 0x49",				/* 0x49 */
	"Reserved 0x4a",				/* 0x4a */
	"Reserved 0x4b",				/* 0x4b */
	"Reserved 0x4c",				/* 0x4c */
	"Reserved 0x4d",				/* 0x4d */
	"Reserved 0x4e",				/* 0x4e */
	"Reserved 0x4f",				/* 0x4f */
	"Reserved 0x50",				/* 0x50 */
	"Reserved 0x51",				/* 0x51 */
	"Reserved 0x52",				/* 0x52 */
	"Reserved 0x53",				/* 0x53 */
	"Reserved 0x54",				/* 0x54 */
	"Reserved 0x55",				/* 0x55 */
	"Reserved 0x56",				/* 0x56 */
	"Reserved 0x57",				/* 0x57 */
	"Reserved 0x58",				/* 0x58 */
	"Reserved 0x59",				/* 0x59 */
	"Reserved 0x5a",				/* 0x5a */
	"Reserved 0x5b",				/* 0x5b */
	"Reserved 0x5c",				/* 0x5c */
	"Reserved 0x5d",				/* 0x5d */
	"Reserved 0x5e",				/* 0x5e */
	"Reserved 0x5f",				/* 0x5f */
	"Reserved 0x60",				/* 0x60 */
	"Reserved 0x61",				/* 0x61 */
	"Reserved 0x62",				/* 0x62 */
	"Reserved 0x63",				/* 0x63 */
	"Reserved 0x64",				/* 0x64 */
	"Reserved 0x65",				/* 0x65 */
	"Reserved 0x66",				/* 0x66 */
	"Reserved 0x67",				/* 0x67 */
	"Reserved 0x68",				/* 0x68 */
	"Reserved 0x69",				/* 0x69 */
	"Reserved 0x6a",				/* 0x6a */
	"Reserved 0x6b",				/* 0x6b */
	"Reserved 0x6c",				/* 0x6c */
	"Reserved 0x6d",				/* 0x6d */
	"Reserved 0x6e",				/* 0x6e */
	"Reserved 0x6f",				/* 0x6f */
	"Reserved 0x70",				/* 0x70 */
	"Reserved 0x71",				/* 0x71 */
	"Reserved 0x72",				/* 0x72 */
	"Reserved 0x73",				/* 0x73 */
	"Reserved 0x74",				/* 0x74 */
	"Reserved 0x75",				/* 0x75 */
	"Reserved 0x76",				/* 0x76 */
	"Reserved 0x77",				/* 0x77 */
	"Reserved 0x78",				/* 0x78 */
	"Reserved 0x79",				/* 0x79 */
	"Reserved 0x7a",				/* 0x7a */
	"Reserved 0x7b",				/* 0x7b */
	"Reserved 0x7c",				/* 0x7c */
	"Reserved 0x7d",				/* 0x7d */
	"Header Termination 1 IE",			/* 0x7e */
	"Header Termination 2 IE"			/* 0x7f */
};

/* Payload IE Group IDs from Table 7-15 of 802.15.4-2015 */
static const char *p_ie_names[] = {
	"ESDU IE",			/* 0x00 */
	"MLME IE",			/* 0x01 */
	"Vendor Specific Nested IE",	/* 0x02 */
	"Multiplexed IE (802.15.9)",	/* 0x03 */
	"Omnibus Payload Group IE",	/* 0x04 */
	"IETF IE",			/* 0x05 */
	"Reserved 0x06",		/* 0x06 */
	"Reserved 0x07",		/* 0x07 */
	"Reserved 0x08",		/* 0x08 */
	"Reserved 0x09",		/* 0x09 */
	"Reserved 0x0a",		/* 0x0a */
	"Reserved 0x0b",		/* 0x0b */
	"Reserved 0x0c",		/* 0x0c */
	"Reserved 0x0d",		/* 0x0d */
	"Reserved 0x0e",		/* 0x0e */
	"List termination"		/* 0x0f */
};

/* Sub-ID for short format from Table 7-16 of 802.15.4-2015 */
static const char *p_mlme_short_names[] = {
	"Reserved for long format 0x0",			/* 0x00 */
	"Reserved for long format 0x1",			/* 0x01 */
	"Reserved for long format 0x2",			/* 0x02 */
	"Reserved for long format 0x3",			/* 0x03 */
	"Reserved for long format 0x4",			/* 0x04 */
	"Reserved for long format 0x5",			/* 0x05 */
	"Reserved for long format 0x6",			/* 0x06 */
	"Reserved for long format 0x7",			/* 0x07 */
	"Reserved for long format 0x8",			/* 0x08 */
	"Reserved for long format 0x9",			/* 0x09 */
	"Reserved for long format 0xa",			/* 0x0a */
	"Reserved for long format 0xb",			/* 0x0b */
	"Reserved for long format 0xc",			/* 0x0c */
	"Reserved for long format 0xd",			/* 0x0d */
	"Reserved for long format 0xe",			/* 0x0e */
	"Reserved for long format 0xf",			/* 0x0f */
	"Reserved 0x10",				/* 0x10 */
	"Reserved 0x11",				/* 0x11 */
	"Reserved 0x12",				/* 0x12 */
	"Reserved 0x13",				/* 0x13 */
	"Reserved 0x14",				/* 0x14 */
	"Reserved 0x15",				/* 0x15 */
	"Reserved 0x16",				/* 0x16 */
	"Reserved 0x17",				/* 0x17 */
	"Reserved 0x18",				/* 0x18 */
	"Reserved 0x19",				/* 0x19 */
	"TSCH Synchronization IE",			/* 0x1a */
	"TSCH Slotframe and Link IE",			/* 0x1b */
	"TSCH Timeslot IE",				/* 0x1c */
	"Hopping timing IE",				/* 0x1d */
	"Enhanced Beacon Filter IE",			/* 0x1e */
	"MAC Metrics IE",				/* 0x1f */
	"All MAC Metrics IE",				/* 0x20 */
	"Coexistence Specification IE",			/* 0x21 */
	"SUN Device Capabilities IE",			/* 0x22 */
	"SUN FSK Generic PHY IE",			/* 0x23 */
	"Mode Switch Parameter IE",			/* 0x24 */
	"PHY Parameter Change IE",			/* 0x25 */
	"O-QPSK PHY Mode IE",				/* 0x26 */
	"PCA Allocation IE",				/* 0x27 */
	"LECIM DSSS Operating Mode IE",			/* 0x28 */
	"LECIM FSK Operating Mode IE",			/* 0x29 */
	"Reserved 0x2a",				/* 0x2a */
	"TVWS PHY Operating Mode Description IE",	/* 0x2b */
	"TVWS Device Capabilities IE",			/* 0x2c */
	"TVWS Device Category IE",			/* 0x2d */
	"TVWS Device Identiication IE",			/* 0x2e */
	"TVWS Device Location IE",			/* 0x2f */
	"TVWS Channel Information Query IE",		/* 0x30 */
	"TVWS Channel Information Source IE",		/* 0x31 */
	"CTM IE",					/* 0x32 */
	"Timestamp IE",					/* 0x33 */
	"Timestamp Difference IE",			/* 0x34 */
	"TMCTP Specification IE",			/* 0x35 */
	"RCC PHY Operating Mode IE",			/* 0x36 */
	"Reserved 0x37",				/* 0x37 */
	"Reserved 0x38",				/* 0x38 */
	"Reserved 0x39",				/* 0x39 */
	"Reserved 0x3a",				/* 0x3a */
	"Reserved 0x3b",				/* 0x3b */
	"Reserved 0x3c",				/* 0x3c */
	"Reserved 0x3d",				/* 0x3d */
	"Reserved 0x3e",				/* 0x3e */
	"Reserved 0x3f",				/* 0x3f */
	"Reserved 0x40",				/* 0x40 */
	"Reserved 0x41",				/* 0x41 */
	"Reserved 0x42",				/* 0x42 */
	"Reserved 0x43",				/* 0x43 */
	"Reserved 0x44",				/* 0x44 */
	"Reserved 0x45",				/* 0x45 */
	"Reserved 0x46",				/* 0x46 */
	"Reserved 0x47",				/* 0x47 */
	"Reserved 0x48",				/* 0x48 */
	"Reserved 0x49",				/* 0x49 */
	"Reserved 0x4a",				/* 0x4a */
	"Reserved 0x4b",				/* 0x4b */
	"Reserved 0x4c",				/* 0x4c */
	"Reserved 0x4d",				/* 0x4d */
	"Reserved 0x4e",				/* 0x4e */
	"Reserved 0x4f",				/* 0x4f */
	"Reserved 0x50",				/* 0x50 */
	"Reserved 0x51",				/* 0x51 */
	"Reserved 0x52",				/* 0x52 */
	"Reserved 0x53",				/* 0x53 */
	"Reserved 0x54",				/* 0x54 */
	"Reserved 0x55",				/* 0x55 */
	"Reserved 0x56",				/* 0x56 */
	"Reserved 0x57",				/* 0x57 */
	"Reserved 0x58",				/* 0x58 */
	"Reserved 0x59",				/* 0x59 */
	"Reserved 0x5a",				/* 0x5a */
	"Reserved 0x5b",				/* 0x5b */
	"Reserved 0x5c",				/* 0x5c */
	"Reserved 0x5d",				/* 0x5d */
	"Reserved 0x5e",				/* 0x5e */
	"Reserved 0x5f",				/* 0x5f */
	"Reserved 0x60",				/* 0x60 */
	"Reserved 0x61",				/* 0x61 */
	"Reserved 0x62",				/* 0x62 */
	"Reserved 0x63",				/* 0x63 */
	"Reserved 0x64",				/* 0x64 */
	"Reserved 0x65",				/* 0x65 */
	"Reserved 0x66",				/* 0x66 */
	"Reserved 0x67",				/* 0x67 */
	"Reserved 0x68",				/* 0x68 */
	"Reserved 0x69",				/* 0x69 */
	"Reserved 0x6a",				/* 0x6a */
	"Reserved 0x6b",				/* 0x6b */
	"Reserved 0x6c",				/* 0x6c */
	"Reserved 0x6d",				/* 0x6d */
	"Reserved 0x6e",				/* 0x6e */
	"Reserved 0x6f",				/* 0x6f */
	"Reserved 0x70",				/* 0x70 */
	"Reserved 0x71",				/* 0x71 */
	"Reserved 0x72",				/* 0x72 */
	"Reserved 0x73",				/* 0x73 */
	"Reserved 0x74",				/* 0x74 */
	"Reserved 0x75",				/* 0x75 */
	"Reserved 0x76",				/* 0x76 */
	"Reserved 0x77",				/* 0x77 */
	"Reserved 0x78",				/* 0x78 */
	"Reserved 0x79",				/* 0x79 */
	"Reserved 0x7a",				/* 0x7a */
	"Reserved 0x7b",				/* 0x7b */
	"Reserved 0x7c",				/* 0x7c */
	"Reserved 0x7d",				/* 0x7d */
	"Reserved 0x7e",				/* 0x7e */
	"Reserved 0x7f"					/* 0x7f */
};

/* Sub-ID for long format from Table 7-17 of 802.15.4-2015 */
static const char *p_mlme_long_names[] = {
	"Reserved 0x00",			/* 0x00 */
	"Reserved 0x01",			/* 0x01 */
	"Reserved 0x02",			/* 0x02 */
	"Reserved 0x03",			/* 0x03 */
	"Reserved 0x04",			/* 0x04 */
	"Reserved 0x05",			/* 0x05 */
	"Reserved 0x06",			/* 0x06 */
	"Reserved 0x07",			/* 0x07 */
	"Vendor Specific MLME Nested IE",	/* 0x08 */
	"Channel Hopping IE",			/* 0x09 */
	"Reserved 0x0a",			/* 0x0a */
	"Reserved 0x0b",			/* 0x0b */
	"Reserved 0x0c",			/* 0x0c */
	"Reserved 0x0d",			/* 0x0d */
	"Reserved 0x0e",			/* 0x0e */
	"Reserved 0x0f"				/* 0x0f */
};

/* MAC commands from Table 7-49 of 802.15.4-2015 */
static const char *mac_c_names[] = {
	"Reserved 0x00",				/* 0x00 */
	"Association Request command",			/* 0x01 */
	"Association Response command",			/* 0x02 */
	"Disassociation Notification command",		/* 0x03 */
	"Data Request command",				/* 0x04 */
	"PAN ID Conflict Notification command",		/* 0x05 */
	"Orphan Notification command",			/* 0x06 */
	"Beacon Request command",			/* 0x07 */
	"Coordinator realignment command",		/* 0x08 */
	"GTS request command",				/* 0x09 */
	"TRLE Management Request command",		/* 0x0a */
	"TRLE Management Response command",		/* 0x0b */
	"Reserved 0x0c",				/* 0x0c */
	"Reserved 0x0d",				/* 0x0d */
	"Reserved 0x0e",				/* 0x0e */
	"Reserved 0x0f",				/* 0x0f */
	"Reserved 0x10",				/* 0x10 */
	"Reserved 0x11",				/* 0x11 */
	"Reserved 0x12",				/* 0x12 */
	"DSME Association Request command",		/* 0x13 */
	"DSME Association Response command",		/* 0x14 */
	"DSME GTS Request command",			/* 0x15 */
	"DSME GTS Response command",			/* 0x16 */
	"DSME GTS Notify command",			/* 0x17 */
	"DSME Information Request command",		/* 0x18 */
	"DSME Information Response command",		/* 0x19 */
	"DSME Beacon Allocation Notification command",	/* 0x1a */
	"DSME Beacon Collision Notification command",	/* 0x1b */
	"DSME Link Report command",			/* 0x1c */
	"Reserved 0x1d",				/* 0x1d */
	"Reserved 0x1e",				/* 0x1e */
	"Reserved 0x1f",				/* 0x1f */
	"RIT Data Request command",			/* 0x20 */
	"DBS Request command",				/* 0x21 */
	"DBS Response command",				/* 0x22 */
	"RIT Data Response command",			/* 0x23 */
	"Vendor Specific command",			/* 0x24 */
	"Reserved 0x25",				/* 0x25 */
	"Reserved 0x26",				/* 0x26 */
	"Reserved 0x27",				/* 0x27 */
	"Reserved 0x28",				/* 0x28 */
	"Reserved 0x29",				/* 0x29 */
	"Reserved 0x2a",				/* 0x2a */
	"Reserved 0x2b",				/* 0x2b */
	"Reserved 0x2c",				/* 0x2c */
	"Reserved 0x2d",				/* 0x2d */
	"Reserved 0x2e",				/* 0x2e */
	"Reserved 0x2f"					/* 0x2f */
};

/*
 * Frame Control subfields.
 */
#define FC_FRAME_TYPE(fc)              ((fc) & 0x7)
#define FC_FRAME_VERSION(fc)           (((fc) >> 12) & 0x3)

#define FC_ADDRESSING_MODE_NONE         0x00
#define FC_ADDRESSING_MODE_RESERVED     0x01
#define FC_ADDRESSING_MODE_SHORT        0x02
#define FC_ADDRESSING_MODE_LONG         0x03

/*
 * IEEE 802.15.4 CRC 16 function. This is using CCITT polynomical of 0x1021,
 * but the initial value is 0, and the bits are reversed for both in and out.
 * See section 7.2.10 of 802.15.4-2015 for more information.
 */
static uint16_t
ieee802_15_4_crc16(netdissect_options *ndo, const u_char *p,
		   u_int data_len)
{
	uint16_t crc;
	u_char x, y;

	crc = 0x0000; /* Note, initial value is 0x0000 not 0xffff. */

	while (data_len != 0){
		y = GET_U_1(p);
		p++;
		/* Reverse bits on input */
		y = (((y & 0xaa) >> 1) | ((y & 0x55) << 1));
		y = (((y & 0xcc) >> 2) | ((y & 0x33) << 2));
		y = (((y & 0xf0) >> 4) | ((y & 0x0f) << 4));
		/* Update CRC */
		x = crc >> 8 ^ y;
		x ^= x >> 4;
		crc = ((uint16_t)(crc << 8)) ^
			((uint16_t)(x << 12)) ^
			((uint16_t)(x << 5)) ^
			((uint16_t)x);
		data_len--;
	}
	/* Reverse bits on output */
	crc = (((crc & 0xaaaa) >> 1) | ((crc & 0x5555) << 1));
	crc = (((crc & 0xcccc) >> 2) | ((crc & 0x3333) << 2));
	crc = (((crc & 0xf0f0) >> 4) | ((crc & 0x0f0f) << 4));
	crc = (((crc & 0xff00) >> 8) | ((crc & 0x00ff) << 8));
	return crc;
}

/*
 * Reverses the bits of the 32-bit word.
 */
static uint32_t
ieee802_15_4_reverse32(uint32_t x)
{
	x = ((x & 0x55555555) <<  1) | ((x >>  1) & 0x55555555);
	x = ((x & 0x33333333) <<  2) | ((x >>  2) & 0x33333333);
	x = ((x & 0x0F0F0F0F) <<  4) | ((x >>  4) & 0x0F0F0F0F);
	x = (x << 24) | ((x & 0xFF00) << 8) |
		((x >> 8) & 0xFF00) | (x >> 24);
	return x;
}

/*
 * IEEE 802.15.4 CRC 32 function. This is using ANSI X3.66-1979 polynomical of
 * 0x04C11DB7, but the initial value is 0, and the bits are reversed for both
 * in and out. See section 7.2.10 of 802.15.4-2015 for more information.
 */
static uint32_t
ieee802_15_4_crc32(netdissect_options *ndo, const u_char *p,
		   u_int data_len)
{
	uint32_t crc, byte;
	int b;

	crc = 0x00000000; /* Note, initial value is 0x00000000 not 0xffffffff */

	while (data_len != 0){
		byte = GET_U_1(p);
		p++;
		/* Reverse bits on input */
		byte = ieee802_15_4_reverse32(byte);
		/* Update CRC */
		for(b = 0; b <= 7; b++) {
		  if ((int) (crc ^ byte) < 0)
		    crc = (crc << 1) ^ 0x04C11DB7;
		  else
		    crc = crc << 1;
		  byte = byte << 1;
		}
		data_len--;
	}
	/* Reverse bits on output */
	crc = ieee802_15_4_reverse32(crc);
	return crc;
}

/*
 * Find out the address length based on the address type. See table 7-3 of
 * 802.15.4-2015. Returns the address length.
 */
static int
ieee802_15_4_addr_len(uint16_t addr_type)
{
	switch (addr_type) {
	case FC_ADDRESSING_MODE_NONE: /* None. */
		return 0;
		break;
	case FC_ADDRESSING_MODE_RESERVED: /* Reserved, there used to be 8-bit
					   * address type in one amendment, but
					   * that and the feature using it was
					   * removed during 802.15.4-2015
					   * maintenance process. */
		return -1;
		break;
	case FC_ADDRESSING_MODE_SHORT: /* Short. */
		return 2;
		break;
	case FC_ADDRESSING_MODE_LONG: /* Extended. */
		return 8;
		break;
	}
	return 0;
}

/*
 * Print out the ieee 802.15.4 address.
 */
static void
ieee802_15_4_print_addr(netdissect_options *ndo, const u_char *p,
			int dst_addr_len)
{
	switch (dst_addr_len) {
	case 0:
		ND_PRINT("none");
		break;
	case 2:
		ND_PRINT("%04x", GET_LE_U_2(p));
		break;
	case 8:
		ND_PRINT("%s", GET_LE64ADDR_STRING(p));
		break;
	}
}

/*
 * Beacon frame superframe specification structure. Used in the old Beacon
 * frames, and in the DSME PAN Descriptor IE. See section 7.3.1.3 of the
 * 802.15.4-2015.
 */
static void
ieee802_15_4_print_superframe_specification(netdissect_options *ndo,
					    uint16_t ss)
{
	if (ndo->ndo_vflag < 1) {
		return;
	}
	ND_PRINT("\n\tBeacon order = %d, Superframe order = %d, ",
		 (ss & 0xf), ((ss >> 4) & 0xf));
	ND_PRINT("Final CAP Slot = %d",
		 ((ss >> 8) & 0xf));
	if (CHECK_BIT(ss, 12)) { ND_PRINT(", BLE enabled"); }
	if (CHECK_BIT(ss, 14)) { ND_PRINT(", PAN Coordinator"); }
	if (CHECK_BIT(ss, 15)) { ND_PRINT(", Association Permit"); }
}

/*
 * Beacon frame gts info structure. Used in the old Beacon frames, and
 * in the DSME PAN Descriptor IE. See section 7.3.1.4 of 802.15.4-2015.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int
ieee802_15_4_print_gts_info(netdissect_options *ndo,
			    const u_char *p,
			    u_int data_len)
{
	uint8_t gts_spec, gts_cnt;
	u_int len;
	int i;

	gts_spec = GET_U_1(p);
	gts_cnt = gts_spec & 0x7;

	if (gts_cnt == 0) {
		if (ndo->ndo_vflag > 0) {
			ND_PRINT("\n\tGTS Descriptor Count = %d, ", gts_cnt);
		}
		return 1;
	}
	len = 1 + 1 + gts_cnt * 3;

	if (data_len < len) {
		ND_PRINT(" [ERROR: Truncated GTS Info List]");
		return -1;
	}
	if (ndo->ndo_vflag < 2) {
		return len;
	}
	ND_PRINT("GTS Descriptor Count = %d, ", gts_cnt);
	ND_PRINT("GTS Directions Mask = %02x, [ ",
		 GET_U_1(p + 1) & 0x7f);

	for(i = 0; i < gts_cnt; i++) {
		ND_PRINT("[ ");
		ieee802_15_4_print_addr(ndo, p + 2 + i * 3, 2);
		ND_PRINT(", Start slot = %d, Length = %d ] ",
			 GET_U_1(p + 2 + i * 3 + 1) & 0x0f,
			 (GET_U_1(p + 2 + i * 3 + 1) >> 4) & 0x0f);
	}
	ND_PRINT("]");
	return len;
}

/*
 * Beacon frame pending address structure. Used in the old Beacon frames, and
 * in the DSME PAN Descriptor IE. See section 7.3.1.5 of 802.15.4-2015.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int16_t
ieee802_15_4_print_pending_addresses(netdissect_options *ndo,
				     const u_char *p,
				     u_int data_len)
{
	uint8_t pas, s_cnt, e_cnt, len, i;

	pas = GET_U_1(p);
	s_cnt = pas & 0x7;
	e_cnt = (pas >> 4) & 0x7;
	len = 1 + s_cnt * 2 + e_cnt * 8;
	if (ndo->ndo_vflag > 0) {
		ND_PRINT("\n\tPending address list, "
			 "# short addresses = %d, # extended addresses = %d",
			 s_cnt, e_cnt);
	}
	if (data_len < len) {
		ND_PRINT(" [ERROR: Pending address list truncated]");
		return -1;
	}
	if (ndo->ndo_vflag < 2) {
		return len;
	}
	if (s_cnt != 0) {
		ND_PRINT(", Short address list = [ ");
		for(i = 0; i < s_cnt; i++) {
			ieee802_15_4_print_addr(ndo, p + 1 + i * 2, 2);
			ND_PRINT(" ");
		}
		ND_PRINT("]");
	}
	if (e_cnt != 0) {
		ND_PRINT(", Extended address list = [ ");
		for(i = 0; i < e_cnt; i++) {
			ieee802_15_4_print_addr(ndo, p + 1 + s_cnt * 2 +
						i * 8, 8);
			ND_PRINT(" ");
		}
		ND_PRINT("]");
	}
	return len;
}

/*
 * Print header ie content.
 */
static void
ieee802_15_4_print_header_ie(netdissect_options *ndo,
			     const u_char *p,
			     uint16_t ie_len,
			     int element_id)
{
	int i;

	switch (element_id) {
	case 0x00: /* Vendor Specific Header IE */
		if (ie_len < 3) {
			ND_PRINT("[ERROR: Vendor OUI missing]");
		} else {
			ND_PRINT("OUI = 0x%02x%02x%02x, ", GET_U_1(p),
				 GET_U_1(p + 1), GET_U_1(p + 2));
			ND_PRINT("Data = ");
			for(i = 3; i < ie_len; i++) {
				ND_PRINT("%02x ", GET_U_1(p + i));
			}
		}
		break;
	case 0x1a: /* LE CSL IE */
		if (ie_len < 4) {
			ND_PRINT("[ERROR: Truncated CSL IE]");
		} else {
			ND_PRINT("CSL Phase = %d, CSL Period = %d",
				 GET_LE_U_2(p), GET_LE_U_2(p + 2));
			if (ie_len >= 6) {
				ND_PRINT(", Rendezvous time = %d",
					 GET_LE_U_2(p + 4));
			}
			if (ie_len != 4 && ie_len != 6) {
				ND_PRINT(" [ERROR: CSL IE length wrong]");
			}
		}
		break;
	case 0x1b: /* LE RIT IE */
		if (ie_len < 4) {
			ND_PRINT("[ERROR: Truncated RIT IE]");
		} else {
			ND_PRINT("Time to First Listen = %d, # of Repeat Listen = %d, Repeat Listen Interval = %d",
				 GET_U_1(p),
				 GET_U_1(p + 1),
				 GET_LE_U_2(p + 2));
		}
		break;
	case 0x1c: /* DSME PAN Descriptor IE */
		/*FALLTHROUGH*/
	case 0x21: /* Extended DSME PAN descriptor IE */
		if (ie_len < 2) {
			ND_PRINT("[ERROR: Truncated DSME PAN IE]");
		} else {
			uint16_t ss, ptr, ulen;
			int16_t len;
			int hopping_present;

			hopping_present = 0;

			ss = GET_LE_U_2(p);
			ieee802_15_4_print_superframe_specification(ndo, ss);
			if (ie_len < 3) {
				ND_PRINT("[ERROR: Truncated before pending addresses field]");
				break;
			}
			ptr = 2;
			len = ieee802_15_4_print_pending_addresses(ndo,
								   p + ptr,
								   ie_len -
								   ptr);
			if (len < 0) {
				break;
			}
			ptr += len;

			if (element_id == 0x21) {
				/* Extended version. */
				if (ie_len < ptr + 2) {
					ND_PRINT("[ERROR: Truncated before DSME Superframe Specification]");
					break;
				}
				ss = GET_LE_U_2(p + ptr);
				ptr += 2;
				ND_PRINT("Multi-superframe Order = %d", ss & 0xff);
				ND_PRINT(", %s", ((ss & 0x100) ?
						  "Channel hopping mode" :
						  "Channel adaptation mode"));
				if (ss & 0x400) {
					ND_PRINT(", CAP reduction enabled");
				}
				if (ss & 0x800) {
					ND_PRINT(", Deferred beacon enabled");
				}
				if (ss & 0x1000) {
					ND_PRINT(", Hopping Sequence Present");
					hopping_present = 1;
				}
			} else {
				if (ie_len < ptr + 1) {
					ND_PRINT("[ERROR: Truncated before DSME Superframe Specification]");
					break;
				}
				ss = GET_U_1(p + ptr);
				ptr++;
				ND_PRINT("Multi-superframe Order = %d",
					 ss & 0x0f);
				ND_PRINT(", %s", ((ss & 0x10) ?
						  "Channel hopping mode" :
						  "Channel adaptation mode"));
				if (ss & 0x40) {
					ND_PRINT(", CAP reduction enabled");
				}
				if (ss & 0x80) {
					ND_PRINT(", Deferred beacon enabled");
				}
			}
			if (ie_len < ptr + 8) {
				ND_PRINT(" [ERROR: Truncated before Time synchronization specification]");
				break;
			}
			ND_PRINT("Beacon timestamp = %" PRIu64 ", offset = %d",
				 GET_LE_U_6(p + ptr),
				 GET_LE_U_2(p + ptr + 6));
			ptr += 8;
			if (ie_len < ptr + 4) {
				ND_PRINT(" [ERROR: Truncated before Beacon Bitmap]");
				break;
			}

			ulen = GET_LE_U_2(p + ptr + 2);
			ND_PRINT("SD Index = %d, Bitmap len = %d, ",
				 GET_LE_U_2(p + ptr), ulen);
			ptr += 4;
			if (ie_len < ptr + ulen) {
				ND_PRINT(" [ERROR: Truncated in SD bitmap]");
				break;
			}
			ND_PRINT(" SD Bitmap = ");
			for(i = 0; i < ulen; i++) {
				ND_PRINT("%02x ", GET_U_1(p + ptr + i));
			}
			ptr += ulen;

			if (ie_len < ptr + 5) {
				ND_PRINT(" [ERROR: Truncated before Channel hopping specification]");
				break;
			}

			ulen = GET_LE_U_2(p + ptr + 4);
			ND_PRINT("Hopping Seq ID = %d, PAN Coordinator BSN = %d, "
				 "Channel offset = %d, Bitmap length = %d, ",
				 GET_U_1(p + ptr),
				 GET_U_1(p + ptr + 1),
				 GET_LE_U_2(p + ptr + 2),
				 ulen);
			ptr += 5;
			if (ie_len < ptr + ulen) {
				ND_PRINT(" [ERROR: Truncated in Channel offset bitmap]");
				break;
			}
			ND_PRINT(" Channel offset bitmap = ");
			for(i = 0; i < ulen; i++) {
				ND_PRINT("%02x ", GET_U_1(p + ptr + i));
			}
			ptr += ulen;
			if (hopping_present) {
				if (ie_len < ptr + 1) {
					ND_PRINT(" [ERROR: Truncated in Hopping Sequence length]");
					break;
				}
				ulen = GET_U_1(p + ptr);
				ptr++;
				ND_PRINT("Hopping Seq length = %d [ ", ulen);

				/* The specification is not clear how the
				   hopping sequence is encoded, I assume two
				   octet unsigned integers for each channel. */

				if (ie_len < ptr + ulen * 2) {
					ND_PRINT(" [ERROR: Truncated in Channel offset bitmap]");
					break;
				}
				for(i = 0; i < ulen; i++) {
					ND_PRINT("%02x ",
						 GET_LE_U_2(p + ptr + i * 2));
				}
				ND_PRINT("]");
				ptr += ulen * 2;
			}
		}
		break;
	case 0x1d: /* Rendezvous Tome IE */
		if (ie_len != 4) {
			ND_PRINT("[ERROR: Length != 2]");
		} else {
			uint16_t r_time, w_u_interval;
			r_time = GET_LE_U_2(p);
			w_u_interval = GET_LE_U_2(p + 2);

			ND_PRINT("Rendezvous time = %d, Wake-up Interval = %d",
				 r_time, w_u_interval);
		}
		break;
	case 0x1e: /* Time correction IE */
		if (ie_len != 2) {
			ND_PRINT("[ERROR: Length != 2]");
		} else {
			uint16_t val;
			int16_t timecorr;

			val = GET_LE_U_2(p);
			if (val & 0x8000) { ND_PRINT("Negative "); }
			val &= 0xfff;
			val <<= 4;
			timecorr = val;
			timecorr >>= 4;

			ND_PRINT("Ack time correction = %d, ", timecorr);
		}
		break;
	case 0x22: /* Fragment Sequence Content Description IE */
		/* XXX Not implemented */
	case 0x23: /* Simplified Superframe Specification IE */
		/* XXX Not implemented */
	case 0x24: /* Simplified GTS Specification IE */
		/* XXX Not implemented */
	case 0x25: /* LECIM Capabilities IE */
		/* XXX Not implemented */
	case 0x26: /* TRLE Descriptor IE */
		/* XXX Not implemented */
	case 0x27: /* RCC Capabilities IE */
		/* XXX Not implemented */
	case 0x28: /* RCCN Descriptor IE */
		/* XXX Not implemented */
	case 0x29: /* Global Time IE */
		/* XXX Not implemented */
	case 0x2b: /* DA IE */
		/* XXX Not implemented */
	default:
		ND_PRINT("IE Data = ");
		for(i = 0; i < ie_len; i++) {
			ND_PRINT("%02x ", GET_U_1(p + i));
		}
		break;
	}
}

/*
 * Parse and print Header IE list. See 7.4.2 of 802.15.4-2015 for
 * more information.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int
ieee802_15_4_print_header_ie_list(netdissect_options *ndo,
				  const u_char *p,
				  u_int caplen,
				  int *payload_ie_present)
{
	int len, ie, element_id, i;
	uint16_t ie_len;

	*payload_ie_present = 0;
	len = 0;
	do {
		if (caplen < 2) {
			ND_PRINT("[ERROR: Truncated header IE]");
			return -1;
		}
		/* Extract IE Header */
		ie = GET_LE_U_2(p);
		if (CHECK_BIT(ie, 15)) {
			ND_PRINT("[ERROR: Header IE with type 1] ");
		}
		/* Get length and Element ID */
		ie_len = ie & 0x7f;
		element_id = (ie >> 7) & 0xff;
		if (element_id > 127) {
			ND_PRINT("Reserved Element ID %02x, length = %d ",
				 element_id, ie_len);
		} else {
			if (ie_len == 0) {
				ND_PRINT("\n\t%s [", h_ie_names[element_id]);
			} else {
				ND_PRINT("\n\t%s [ length = %d, ",
					 h_ie_names[element_id], ie_len);
			}
		}
		if (caplen < 2U + ie_len) {
			ND_PRINT("[ERROR: Truncated IE data]");
			return -1;
		}
		/* Skip header */
		p += 2;

		/* Parse and print content. */
		if (ndo->ndo_vflag > 3 && ie_len != 0) {
			ieee802_15_4_print_header_ie(ndo, p,
						     ie_len, element_id);
		} else {
			if (ie_len != 0) {
				ND_PRINT("IE Data = ");
				for(i = 0; i < ie_len; i++) {
					ND_PRINT("%02x ", GET_U_1(p + i));
				}
			}
		}
		ND_PRINT("] ");
		len += 2 + ie_len;
		p += ie_len;
		caplen -= 2 + ie_len;
		if (element_id == 0x7e) {
			*payload_ie_present = 1;
			break;
		}
		if (element_id == 0x7f) {
			break;
		}
	} while (caplen != 0);
	return len;
}

/*
 * Print MLME ie content.
 */
static void
ieee802_15_4_print_mlme_ie(netdissect_options *ndo,
			   const u_char *p,
			   uint16_t sub_ie_len,
			   int sub_id)
{
	int i, j;
	uint16_t len;

	/* Note, as there is no overlap with the long and short
	   MLME sub IDs, we can just use one switch here. */
	switch (sub_id) {
	case 0x08: /* Vendor Specific Nested IE */
		if (sub_ie_len < 3) {
			ND_PRINT("[ERROR: Vendor OUI missing]");
		} else {
			ND_PRINT("OUI = 0x%02x%02x%02x, ",
				 GET_U_1(p),
				 GET_U_1(p + 1),
				 GET_U_1(p + 2));
			ND_PRINT("Data = ");
			for(i = 3; i < sub_ie_len; i++) {
				ND_PRINT("%02x ", GET_U_1(p + i));
			}
		}
		break;
	case 0x09: /* Channel Hopping IE */
		if (sub_ie_len < 1) {
			ND_PRINT("[ERROR: Hopping sequence ID missing]");
		} else if (sub_ie_len == 1) {
			ND_PRINT("Hopping Sequence ID = %d", GET_U_1(p));
			p++;
			sub_ie_len--;
		} else {
			uint16_t channel_page, number_of_channels;

			ND_PRINT("Hopping Sequence ID = %d", GET_U_1(p));
			p++;
			sub_ie_len--;
			if (sub_ie_len < 7) {
				ND_PRINT("[ERROR: IE truncated]");
				break;
			}
			channel_page = GET_U_1(p);
			number_of_channels = GET_LE_U_2(p + 1);
			ND_PRINT("Channel Page = %d, Number of Channels = %d, ",
				 channel_page, number_of_channels);
			ND_PRINT("Phy Configuration = 0x%08x, ",
				 GET_LE_U_4(p + 3));
			p += 7;
			sub_ie_len -= 7;
			if (channel_page == 9 || channel_page == 10) {
				len = (number_of_channels + 7) / 8;
				if (sub_ie_len < len) {
					ND_PRINT("[ERROR: IE truncated]");
					break;
				}
				ND_PRINT("Extended bitmap = 0x");
				for(i = 0; i < len; i++) {
					ND_PRINT("%02x", GET_U_1(p + i));
				}
				ND_PRINT(", ");
				p += len;
				sub_ie_len -= len;
			}
			if (sub_ie_len < 2) {
				ND_PRINT("[ERROR: IE truncated]");
				break;
			}
			len = GET_LE_U_2(p);
			p += 2;
			sub_ie_len -= 2;
			ND_PRINT("Hopping Seq length = %d [ ", len);

			if (sub_ie_len < len * 2) {
				ND_PRINT(" [ERROR: IE truncated]");
				break;
			}
			for(i = 0; i < len; i++) {
				ND_PRINT("%02x ", GET_LE_U_2(p + i * 2));
			}
			ND_PRINT("]");
			p += len * 2;
			sub_ie_len -= len * 2;
			if (sub_ie_len < 2) {
				ND_PRINT("[ERROR: IE truncated]");
				break;
			}
			ND_PRINT("Current hop = %d", GET_LE_U_2(p));
		}

		break;
	case 0x1a: /* TSCH Synchronization IE. */
		if (sub_ie_len < 6) {
			ND_PRINT("[ERROR: Length != 6]");
		}
		ND_PRINT("ASN = %010" PRIx64 ", Join Metric = %d ",
			 GET_LE_U_5(p), GET_U_1(p + 5));
		break;
	case 0x1b: /* TSCH Slotframe and Link IE. */
		{
			int sf_num, off, links, opts;

			if (sub_ie_len < 1) {
				ND_PRINT("[ERROR: Truncated IE]");
				break;
			}
			sf_num = GET_U_1(p);
			ND_PRINT("Slotframes = %d ", sf_num);
			off = 1;
			for(i = 0; i < sf_num; i++) {
				if (sub_ie_len < off + 4) {
					ND_PRINT("[ERROR: Truncated IE before slotframes]");
					break;
				}
				links = GET_U_1(p + off + 3);
				ND_PRINT("\n\t\t\t[ Handle %d, size = %d, links = %d ",
					 GET_U_1(p + off),
					 GET_LE_U_2(p + off + 1),
					 links);
				off += 4;
				for(j = 0; j < links; j++) {
					if (sub_ie_len < off + 5) {
						ND_PRINT("[ERROR: Truncated IE links]");
						break;
					}
					opts = GET_U_1(p + off + 4);
					ND_PRINT("\n\t\t\t\t[ Timeslot =  %d, Offset = %d, Options = ",
						 GET_LE_U_2(p + off),
						 GET_LE_U_2(p + off + 2));
					if (opts & 0x1) { ND_PRINT("TX "); }
					if (opts & 0x2) { ND_PRINT("RX "); }
					if (opts & 0x4) { ND_PRINT("Shared "); }
					if (opts & 0x8) {
						ND_PRINT("Timekeeping ");
					}
					if (opts & 0x10) {
						ND_PRINT("Priority ");
					}
					off += 5;
					ND_PRINT("] ");
				}
				ND_PRINT("] ");
			}
		}
		break;
	case 0x1c: /* TSCH Timeslot IE. */
		if (sub_ie_len == 1) {
			ND_PRINT("Time slot ID = %d ", GET_U_1(p));
		} else if (sub_ie_len == 25) {
			ND_PRINT("Time slot ID = %d, CCA Offset = %d, CCA = %d, TX Offset = %d, RX Offset = %d, RX Ack Delay = %d, TX Ack Delay = %d, RX Wait = %d, Ack Wait = %d, RX TX = %d, Max Ack = %d, Max TX = %d, Time slot Length = %d ",
				 GET_U_1(p),
				 GET_LE_U_2(p + 1),
				 GET_LE_U_2(p + 3),
				 GET_LE_U_2(p + 5),
				 GET_LE_U_2(p + 7),
				 GET_LE_U_2(p + 9),
				 GET_LE_U_2(p + 11),
				 GET_LE_U_2(p + 13),
				 GET_LE_U_2(p + 15),
				 GET_LE_U_2(p + 17),
				 GET_LE_U_2(p + 19),
				 GET_LE_U_2(p + 21),
				 GET_LE_U_2(p + 23));
		} else if (sub_ie_len == 27) {
			ND_PRINT("Time slot ID = %d, CCA Offset = %d, CCA = %d, TX Offset = %d, RX Offset = %d, RX Ack Delay = %d, TX Ack Delay = %d, RX Wait = %d, Ack Wait = %d, RX TX = %d, Max Ack = %d, Max TX = %d, Time slot Length = %d ",
				 GET_U_1(p),
				 GET_LE_U_2(p + 1),
				 GET_LE_U_2(p + 3),
				 GET_LE_U_2(p + 5),
				 GET_LE_U_2(p + 7),
				 GET_LE_U_2(p + 9),
				 GET_LE_U_2(p + 11),
				 GET_LE_U_2(p + 13),
				 GET_LE_U_2(p + 15),
				 GET_LE_U_2(p + 17),
				 GET_LE_U_2(p + 19),
				 GET_LE_U_3(p + 21),
				 GET_LE_U_3(p + 24));
		} else {
			ND_PRINT("[ERROR: Length not 1, 25, or 27]");
			ND_PRINT("\n\t\t\tIE Data = ");
			for(i = 0; i < sub_ie_len; i++) {
				ND_PRINT("%02x ", GET_U_1(p + i));
			}
		}
		break;
	case 0x1d: /* Hopping timing IE */
		/* XXX Not implemented */
	case 0x1e: /* Enhanced Beacon Filter IE */
		/* XXX Not implemented */
	case 0x1f: /* MAC Metrics IE */
		/* XXX Not implemented */
	case 0x20: /* All MAC Metrics IE */
		/* XXX Not implemented */
	case 0x21: /* Coexistence Specification IE */
		/* XXX Not implemented */
	case 0x22: /* SUN Device Capabilities IE */
		/* XXX Not implemented */
	case 0x23: /* SUN FSK Generic PHY IE */
		/* XXX Not implemented */
	case 0x24: /* Mode Switch Parameter IE */
		/* XXX Not implemented */
	case 0x25: /* PHY Parameter Change IE */
		/* XXX Not implemented */
	case 0x26: /* O-QPSK PHY Mode IE */
		/* XXX Not implemented */
	case 0x27: /* PCA Allocation IE */
		/* XXX Not implemented */
	case 0x28: /* LECIM DSSS Operating Mode IE */
		/* XXX Not implemented */
	case 0x29: /* LECIM FSK Operating Mode IE */
		/* XXX Not implemented */
	case 0x2b: /* TVWS PHY Operating Mode Description IE */
		/* XXX Not implemented */
	case 0x2c: /* TVWS Device Capabilities IE */
		/* XXX Not implemented */
	case 0x2d: /* TVWS Device Category IE */
		/* XXX Not implemented */
	case 0x2e: /* TVWS Device Identification IE */
		/* XXX Not implemented */
	case 0x2f: /* TVWS Device Location IE */
		/* XXX Not implemented */
	case 0x30: /* TVWS Channel Information Query IE */
		/* XXX Not implemented */
	case 0x31: /* TVWS Channel Information Source IE */
		/* XXX Not implemented */
	case 0x32: /* CTM IE */
		/* XXX Not implemented */
	case 0x33: /* Timestamp IE */
		/* XXX Not implemented */
	case 0x34: /* Timestamp Difference IE */
		/* XXX Not implemented */
	case 0x35: /* TMCTP Specification IE */
		/* XXX Not implemented */
	case 0x36: /* TCC PHY Operating Mode IE */
		/* XXX Not implemented */
	default:
		ND_PRINT("IE Data = ");
		for(i = 0; i < sub_ie_len; i++) {
			ND_PRINT("%02x ", GET_U_1(p + i));
		}
		break;
	}
}

/*
 * MLME IE list parsing and printing. See 7.4.3.2 of 802.15.4-2015
 * for more information.
 */
static void
ieee802_15_4_print_mlme_ie_list(netdissect_options *ndo,
				const u_char *p,
				uint16_t ie_len)
{
	int ie, sub_id, i, type;
	uint16_t sub_ie_len;

	do {
		if (ie_len < 2) {
			ND_PRINT("[ERROR: Truncated MLME IE]");
			return;
		}
		/* Extract IE header */
		ie = GET_LE_U_2(p);
		type = CHECK_BIT(ie, 15);
		if (type) {
			/* Long type */
			sub_ie_len = ie & 0x3ff;
			sub_id = (ie >> 11) & 0x0f;
		} else {
			sub_ie_len = ie & 0xff;
			sub_id = (ie >> 8) & 0x7f;
		}

		/* Skip the IE header */
		p += 2;

		if (type == 0) {
			ND_PRINT("\n\t\t%s [ length = %d, ",
				 p_mlme_short_names[sub_id], sub_ie_len);
		} else {
			ND_PRINT("\n\t\t%s [ length = %d, ",
				 p_mlme_long_names[sub_id], sub_ie_len);
		}

		if (ie_len < 2 + sub_ie_len) {
			ND_PRINT("[ERROR: Truncated IE data]");
			return;
		}
		if (sub_ie_len != 0) {
			if (ndo->ndo_vflag > 3) {
				ieee802_15_4_print_mlme_ie(ndo, p, sub_ie_len, sub_id);
			} else if (ndo->ndo_vflag > 2) {
				ND_PRINT("IE Data = ");
				for(i = 0; i < sub_ie_len; i++) {
					ND_PRINT("%02x ", GET_U_1(p + i));
				}
			}
		}
		ND_PRINT("] ");
		p += sub_ie_len;
		ie_len -= 2 + sub_ie_len;
	} while (ie_len > 0);
}

/*
 * Multiplexd IE (802.15.9) parsing and printing.
 *
 * Returns number of bytes consumed from packet or -1 in case of error.
 */
static void
ieee802_15_4_print_mpx_ie(netdissect_options *ndo,
			  const u_char *p,
			  uint16_t ie_len)
{
	int transfer_type, tid;
	int fragment_number, data_start;
	int i;

	data_start = 0;
	if (ie_len < 1) {
		ND_PRINT("[ERROR: Transaction control byte missing]");
		return;
	}

	transfer_type = GET_U_1(p) & 0x7;
	tid = GET_U_1(p) >> 3;
	switch (transfer_type) {
	case 0x00: /* Full upper layer frame. */
	case 0x01: /* Full upper layer frame with small Multiplex ID. */
		ND_PRINT("Type = Full upper layer fragment%s, ",
			 (transfer_type == 0x01 ?
			  " with small Multiplex ID" : ""));
		if (transfer_type == 0x00) {
			if (ie_len < 3) {
				ND_PRINT("[ERROR: Multiplex ID missing]");
				return;
			}
			data_start = 3;
			ND_PRINT("tid = 0x%02x, Multiplex ID = 0x%04x, ",
				 tid, GET_LE_U_2(p + 1));
		} else {
			data_start = 1;
			ND_PRINT("Multiplex ID = 0x%04x, ", tid);
		}
		break;
	case 0x02: /* First, or middle, Fragments */
	case 0x04: /* Last fragment */
		if (ie_len < 2) {
			ND_PRINT("[ERROR: fragment number missing]");
			return;
		}

		fragment_number = GET_U_1(p + 1);
		ND_PRINT("Type = %s, tid = 0x%02x, fragment = 0x%02x, ",
			 (transfer_type == 0x02 ?
			  (fragment_number == 0 ?
			   "First fragment" : "Middle fragment") :
			  "Last fragment"), tid,
			 fragment_number);
		data_start = 2;
		if (fragment_number == 0) {
			int total_size, multiplex_id;

			if (ie_len < 6) {
				ND_PRINT("[ERROR: Total upper layer size or multiplex ID missing]");
				return;
			}
			total_size = GET_LE_U_2(p + 2);
			multiplex_id = GET_LE_U_2(p + 4);
			ND_PRINT("Total upper layer size = 0x%04x, Multiplex ID = 0x%04x, ",
				 total_size, multiplex_id);
			data_start = 6;
		}
		break;
	case 0x06: /* Abort code */
		if (ie_len == 1) {
			ND_PRINT("Type = Abort, tid = 0x%02x, no max size given",
				 tid);
		} else if (ie_len == 3) {
			ND_PRINT("Type = Abort, tid = 0x%02x, max size = 0x%04x",
				 tid, GET_LE_U_2(p + 1));
		} else {
			ND_PRINT("Type = Abort, tid = 0x%02x, invalid length = %d (not 1 or 3)",
				 tid, ie_len);
			ND_PRINT("Abort data = ");
			for(i = 1; i < ie_len; i++) {
				ND_PRINT("%02x ", GET_U_1(p + i));
			}
		}
		return;
		/* NOTREACHED */
		break;
	case 0x03: /* Reserved */
	case 0x05: /* Reserved */
	case 0x07: /* Reserved */
		ND_PRINT("Type = %d (Reserved), tid = 0x%02x, ",
			 transfer_type, tid);
		data_start = 1;
		break;
	}

	ND_PRINT("Upper layer data = ");
	for(i = data_start; i < ie_len; i++) {
		ND_PRINT("%02x ", GET_U_1(p + i));
	}
}

/*
 * Payload IE list parsing and printing. See 7.4.3 of 802.15.4-2015
 * for more information.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int
ieee802_15_4_print_payload_ie_list(netdissect_options *ndo,
				   const u_char *p,
				   u_int caplen)
{
	int len, ie, group_id, i;
	uint16_t ie_len;

	len = 0;
	do {
		if (caplen < 2) {
			ND_PRINT("[ERROR: Truncated header IE]");
			return -1;
		}
		/* Extract IE header */
		ie = GET_LE_U_2(p);
		if ((CHECK_BIT(ie, 15)) == 0) {
			ND_PRINT("[ERROR: Payload IE with type 0] ");
		}
		ie_len = ie & 0x3ff;
		group_id = (ie >> 11) & 0x0f;

		/* Skip the IE header */
		p += 2;
		if (ie_len == 0) {
			ND_PRINT("\n\t%s [", p_ie_names[group_id]);
		} else {
			ND_PRINT("\n\t%s [ length = %d, ",
				 p_ie_names[group_id], ie_len);
		}
		if (caplen < 2U + ie_len) {
			ND_PRINT("[ERROR: Truncated IE data]");
			return -1;
		}
		if (ndo->ndo_vflag > 3 && ie_len != 0) {
			switch (group_id) {
			case 0x1: /* MLME IE */
				ieee802_15_4_print_mlme_ie_list(ndo, p, ie_len);
				break;
			case 0x2: /* Vendor Specific Nested IE */
				if (ie_len < 3) {
					ND_PRINT("[ERROR: Vendor OUI missing]");
				} else {
					ND_PRINT("OUI = 0x%02x%02x%02x, ",
						 GET_U_1(p),
						 GET_U_1(p + 1),
						 GET_U_1(p + 2));
					ND_PRINT("Data = ");
					for(i = 3; i < ie_len; i++) {
						ND_PRINT("%02x ",
							 GET_U_1(p + i));
					}
				}
				break;
			case 0x3: /* Multiplexed IE (802.15.9) */
				ieee802_15_4_print_mpx_ie(ndo, p, ie_len);
				break;
			case 0x5: /* IETF IE */
				if (ie_len < 1) {
					ND_PRINT("[ERROR: Subtype ID missing]");
				} else {
					ND_PRINT("Subtype ID = 0x%02x, Subtype content = ",
						 GET_U_1(p));
					for(i = 1; i < ie_len; i++) {
						ND_PRINT("%02x ",
							 GET_U_1(p + i));
					}
				}
				break;
			default:
				ND_PRINT("IE Data = ");
				for(i = 0; i < ie_len; i++) {
					ND_PRINT("%02x ", GET_U_1(p + i));
				}
				break;
			}
		} else {
			if (ie_len != 0) {
				ND_PRINT("IE Data = ");
				for(i = 0; i < ie_len; i++) {
					ND_PRINT("%02x ", GET_U_1(p + i));
				}
			}
		}
		ND_PRINT("]\n\t");
		len += 2 + ie_len;
		p += ie_len;
		caplen -= 2 + ie_len;
		if (group_id == 0xf) {
			break;
		}
	} while (caplen > 0);
	return len;
}

/*
 * Parse and print auxiliary security header.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int
ieee802_15_4_print_aux_sec_header(netdissect_options *ndo,
				  const u_char *p,
				  u_int caplen,
				  int *security_level)
{
	int sc, key_id_mode, len;

	if (caplen < 1) {
		ND_PRINT("[ERROR: Truncated before Aux Security Header]");
		return -1;
	}
	sc = GET_U_1(p);
	len = 1;
	*security_level = sc & 0x7;
	key_id_mode = (sc >> 3) & 0x3;

	caplen -= 1;
	p += 1;

	if (ndo->ndo_vflag > 0) {
		ND_PRINT("\n\tSecurity Level %d, Key Id Mode %d, ",
			 *security_level, key_id_mode);
	}
	if ((CHECK_BIT(sc, 5)) == 0) {
		if (caplen < 4) {
			ND_PRINT("[ERROR: Truncated before Frame Counter]");
			return -1;
		}
		if (ndo->ndo_vflag > 1) {
			ND_PRINT("Frame Counter 0x%08x ",
				 GET_LE_U_4(p));
		}
		p += 4;
		caplen -= 4;
		len += 4;
	}
	switch (key_id_mode) {
	case 0x00: /* Implicit. */
		if (ndo->ndo_vflag > 1) {
			ND_PRINT("Implicit");
		}
		return len;
		break;
	case 0x01: /* Key Index, nothing to print here. */
		break;
	case 0x02: /* PAN and Short address Key Source, and Key Index. */
		if (caplen < 4) {
			ND_PRINT("[ERROR: Truncated before Key Source]");
			return -1;
		}
		if (ndo->ndo_vflag > 1) {
			ND_PRINT("KeySource 0x%04x:%0x4x, ",
				 GET_LE_U_2(p), GET_LE_U_2(p + 2));
		}
		p += 4;
		caplen -= 4;
		len += 4;
		break;
	case 0x03: /* Extended address and Key Index. */
		if (caplen < 8) {
			ND_PRINT("[ERROR: Truncated before Key Source]");
			return -1;
		}
		if (ndo->ndo_vflag > 1) {
			ND_PRINT("KeySource %s, ", GET_LE64ADDR_STRING(p));
		}
		p += 4;
		caplen -= 4;
		len += 4;
		break;
	}
	if (caplen < 1) {
		ND_PRINT("[ERROR: Truncated before Key Index]");
		return -1;
	}
	if (ndo->ndo_vflag > 1) {
		ND_PRINT("KeyIndex 0x%02x, ", GET_U_1(p));
	}
	caplen -= 1;
	p += 1;
	len += 1;
	return len;
}

/*
 * Print command data.
 *
 * Returns number of byts consumed from the packet or -1 in case of error.
 */
static int
ieee802_15_4_print_command_data(netdissect_options *ndo,
				uint8_t command_id,
				const u_char *p,
				u_int caplen)
{
	u_int i;

	switch (command_id) {
	case 0x01: /* Association Request */
		if (caplen != 1) {
			ND_PRINT("Invalid Association request command length");
			return -1;
		} else {
			uint8_t cap_info;
			cap_info = GET_U_1(p);
			ND_PRINT("%s%s%s%s%s%s",
				 ((cap_info & 0x02) ?
				  "FFD, " : "RFD, "),
				 ((cap_info & 0x04) ?
				  "AC powered, " : ""),
				 ((cap_info & 0x08) ?
				  "Receiver on when idle, " : ""),
				 ((cap_info & 0x10) ?
				  "Fast association, " : ""),
				 ((cap_info & 0x40) ?
				  "Security supported, " : ""),
				 ((cap_info & 0x80) ?
				  "Allocate address, " : ""));
			return caplen;
		}
		break;
	case 0x02: /* Association Response */
		if (caplen != 3) {
			ND_PRINT("Invalid Association response command length");
			return -1;
		} else {
			ND_PRINT("Short address = ");
			ieee802_15_4_print_addr(ndo, p, 2);
			switch (GET_U_1(p + 2)) {
			case 0x00:
				ND_PRINT(", Association successful");
				break;
			case 0x01:
				ND_PRINT(", PAN at capacity");
				break;
			case 0x02:
				ND_PRINT(", PAN access denied");
				break;
			case 0x03:
				ND_PRINT(", Hooping sequence offset duplication");
				break;
			case 0x80:
				ND_PRINT(", Fast association successful");
				break;
			default:
				ND_PRINT(", Status = 0x%02x",
					 GET_U_1(p + 2));
				break;
			}
			return caplen;
		}
		break;
	case 0x03: /* Diassociation Notification command */
		if (caplen != 1) {
			ND_PRINT("Invalid Disassociation Notification command length");
			return -1;
		} else {
			switch (GET_U_1(p)) {
			case 0x00:
				ND_PRINT("Reserved");
				break;
			case 0x01:
				ND_PRINT("Reason = The coordinator wishes the device to leave PAN");
				break;
			case 0x02:
				ND_PRINT("Reason = The device wishes to leave the PAN");
				break;
			default:
				ND_PRINT("Reason = 0x%02x", GET_U_1(p + 2));
				break;
			}
			return caplen;
		}

		/* Following ones do not have any data. */
	case 0x04: /* Data Request command */
	case 0x05: /* PAN ID Conflict Notification command */
	case 0x06: /* Orphan Notification command */
	case 0x07: /* Beacon Request command */
		/* Should not have any data. */
		return 0;
	case 0x08: /* Coordinator Realignment command */
		if (caplen < 7 || caplen > 8) {
			ND_PRINT("Invalid Coordinator Realignment command length");
			return -1;
		} else {
			uint16_t channel, page;

			ND_PRINT("Pan ID = 0x%04x, Coordinator short address = ",
				 GET_LE_U_2(p));
			ieee802_15_4_print_addr(ndo, p + 2, 2);
			channel = GET_U_1(p + 4);

			if (caplen == 8) {
				page = GET_U_1(p + 7);
			} else {
				page = 0x80;
			}
			if (CHECK_BIT(page, 7)) {
				/* No page present, instead we have msb of
				   channel in the page. */
				channel |= (page & 0x7f) << 8;
				ND_PRINT(", Channel Number = %d", channel);
			} else {
				ND_PRINT(", Channel Number = %d, page = %d",
					 channel, page);
			}
			ND_PRINT(", Short address = ");
			ieee802_15_4_print_addr(ndo, p + 5, 2);
			return caplen;
		}
		break;
	case 0x09: /* GTS Request command */
		if (caplen != 1) {
			ND_PRINT("Invalid GTS Request command length");
			return -1;
		} else {
			uint8_t gts;

			gts = GET_U_1(p);
			ND_PRINT("GTS Length = %d, %s, %s",
				 gts & 0xf,
				 (CHECK_BIT(gts, 4) ?
				  "Receive-only GTS" : "Transmit-only GTS"),
				 (CHECK_BIT(gts, 5) ?
				  "GTS allocation" : "GTS deallocations"));
			return caplen;
		}
		break;
	case 0x13: /* DSME Association Request command */
		/* XXX Not implemented */
	case 0x14: /* DSME Association Response command */
		/* XXX Not implemented */
	case 0x15: /* DSME GTS Request command */
		/* XXX Not implemented */
	case 0x16: /* DSME GTS Response command */
		/* XXX Not implemented */
	case 0x17: /* DSME GTS Notify command */
		/* XXX Not implemented */
	case 0x18: /* DSME Information Request command */
		/* XXX Not implemented */
	case 0x19: /* DSME Information Response command */
		/* XXX Not implemented */
	case 0x1a: /* DSME Beacon Allocation Notification command */
		/* XXX Not implemented */
	case 0x1b: /* DSME Beacon Collision Notification command */
		/* XXX Not implemented */
	case 0x1c: /* DSME Link Report command */
		/* XXX Not implemented */
	case 0x20: /* RIT Data Request command */
		/* XXX Not implemented */
	case 0x21: /* DBS Request command */
		/* XXX Not implemented */
	case 0x22: /* DBS Response command */
		/* XXX Not implemented */
	case 0x23: /* RIT Data Response command */
		/* XXX Not implemented */
	case 0x24: /* Vendor Specific command */
		/* XXX Not implemented */
	case 0x0a: /* TRLE Management Request command */
		/* XXX Not implemented */
	case 0x0b: /* TRLE Management Response command */
		/* XXX Not implemented */
	default:
		ND_PRINT("Command Data = ");
		for(i = 0; i < caplen; i++) {
			ND_PRINT("%02x ", GET_U_1(p + i));
		}
		break;
	}
	return 0;
}

/*
 * Parse and print frames following standard format.
 *
 * Returns FALSE in case of error.
 */
static u_int
ieee802_15_4_std_frames(netdissect_options *ndo,
			const u_char *p, u_int caplen,
			uint16_t fc)
{
	int len, frame_version, pan_id_comp;
	int frame_type;
	int src_pan, dst_pan, src_addr_len, dst_addr_len;
	int security_level;
	u_int miclen = 0;
	int payload_ie_present;
	uint8_t seq;
	uint32_t fcs, crc_check;
	const u_char *mic_start = NULL;

	payload_ie_present = 0;

	crc_check = 0;
	/* Assume 2 octet FCS, the FCS length depends on the PHY, and we do not
	   know about that. */
	if (caplen < 4) {
		/* Cannot have FCS, assume no FCS. */
		fcs = 0;
	} else {
		/* Test for 4 octet FCS. */
		fcs = GET_LE_U_4(p + caplen - 4);
		crc_check = ieee802_15_4_crc32(ndo, p, caplen - 4);
		if (crc_check == fcs) {
			/* Remove FCS */
			caplen -= 4;
		} else {
			/* Test for 2 octet FCS. */
			fcs = GET_LE_U_2(p + caplen - 2);
			crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
			if (crc_check == fcs) {
				/* Remove FCS */
				caplen -= 2;
			} else {
				/* Wrong FCS, FCS might not be included in the
				   captured frame, do not remove it. */
			}
		}
	}

	/* Frame version. */
	frame_version = FC_FRAME_VERSION(fc);
	frame_type = FC_FRAME_TYPE(fc);
	ND_PRINT("v%d ", frame_version);

	if (ndo->ndo_vflag > 2) {
		if (CHECK_BIT(fc, 3)) { ND_PRINT("Security Enabled, "); }
		if (CHECK_BIT(fc, 4)) { ND_PRINT("Frame Pending, "); }
		if (CHECK_BIT(fc, 5)) { ND_PRINT("AR, "); }
		if (CHECK_BIT(fc, 6)) { ND_PRINT("PAN ID Compression, "); }
		if (CHECK_BIT(fc, 8)) { ND_PRINT("Sequence Number Suppression, "); }
		if (CHECK_BIT(fc, 9)) { ND_PRINT("IE present, "); }
	}

	/* Check for the sequence number suppression. */
	if (CHECK_BIT(fc, 8)) {
		/* Sequence number is suppressed. */
		if (frame_version < 2) {
			/* Sequence number can only be suppressed for frame
			   version 2 or higher, this is invalid frame. */
			ND_PRINT("[ERROR: Sequence number suppressed on frames where version < 2]");
		}
		if (ndo->ndo_vflag)
			ND_PRINT("seq suppressed ");
		if (caplen < 2) {
			nd_print_trunc(ndo);
			return 0;
		}
		p += 2;
		caplen -= 2;
	} else {
		seq = GET_U_1(p + 2);
		if (ndo->ndo_vflag)
			ND_PRINT("seq %02x ", seq);
		if (caplen < 3) {
			nd_print_trunc(ndo);
			return 0;
		}
		p += 3;
		caplen -= 3;
	}

	/* See which parts of addresses we have. */
	dst_addr_len = ieee802_15_4_addr_len((fc >> 10) & 0x3);
	src_addr_len = ieee802_15_4_addr_len((fc >> 14) & 0x3);
	if (src_addr_len < 0) {
		ND_PRINT("[ERROR: Invalid src address mode]");
		return 0;
	}
	if (dst_addr_len < 0) {
		ND_PRINT("[ERROR: Invalid dst address mode]");
		return 0;
	}
	src_pan = 0;
	dst_pan = 0;
	pan_id_comp = CHECK_BIT(fc, 6);

	/* The PAN ID Compression rules are complicated. */

	/* First check old versions, where the rules are simple. */
	if (frame_version < 2) {
		if (pan_id_comp) {
			src_pan = 0;
			dst_pan = 1;
			if (dst_addr_len <= 0 || src_addr_len <= 0) {
				/* Invalid frame, PAN ID Compression must be 0
				   if only one address in the frame. */
				ND_PRINT("[ERROR: PAN ID Compression != 0, and only one address with frame version < 2]");
			}
		} else {
			src_pan = 1;
			dst_pan = 1;
		}
		if (dst_addr_len <= 0) {
			dst_pan = 0;
		}
		if (src_addr_len <= 0) {
			src_pan = 0;
		}
	} else {
		/* Frame version 2 rules are more complicated, and they depend
		   on the address modes of the frame, generic rules are same,
		   but then there are some special cases. */
		if (pan_id_comp) {
			src_pan = 0;
			dst_pan = 1;
		} else {
			src_pan = 1;
			dst_pan = 1;
		}
		if (dst_addr_len <= 0) {
			dst_pan = 0;
		}
		if (src_addr_len <= 0) {
			src_pan = 0;
		}
		if (pan_id_comp) {
			if (src_addr_len == 0 &&
			    dst_addr_len == 0) {
				/* Both addresses are missing, but PAN ID
				   compression set, special case we have
				   destination PAN but no addresses. */
				dst_pan = 1;
			} else if ((src_addr_len == 0 &&
				    dst_addr_len > 0) ||
				   (src_addr_len > 0 &&
				    dst_addr_len == 0)) {
				/* Only one address present, and PAN ID
				   compression is set, we do not have PAN id at
				   all. */
				dst_pan = 0;
				src_pan = 0;
			} else if (src_addr_len == 8 &&
				   dst_addr_len == 8) {
				/* Both addresses are Extended, and PAN ID
				   compression set, we do not have PAN ID at
				   all. */
				dst_pan = 0;
				src_pan = 0;
			}
		} else {
			/* Special cases where PAN ID Compression is not set. */
			if (src_addr_len == 8 &&
			    dst_addr_len == 8) {
				/* Both addresses are Extended, and PAN ID
				   compression not set, we do have only one PAN
				   ID (destination). */
				dst_pan = 1;
				src_pan = 0;
			}
#ifdef BROKEN_6TISCH_PAN_ID_COMPRESSION
			if (src_addr_len == 8 &&
			    dst_addr_len == 2) {
				/* Special case for the broken 6tisch
				   implementations. */
				src_pan = 0;
			}
#endif /* BROKEN_6TISCH_PAN_ID_COMPRESSION */
		}
	}

	/* Print dst PAN and address. */
	if (dst_pan) {
		if (caplen < 2) {
			ND_PRINT("[ERROR: Truncated before dst_pan]");
			return 0;
		}
		ND_PRINT("%04x:", GET_LE_U_2(p));
		p += 2;
		caplen -= 2;
	} else {
		ND_PRINT("-:");
	}
	if (caplen < (u_int) dst_addr_len) {
		ND_PRINT("[ERROR: Truncated before dst_addr]");
		return 0;
	}
	ieee802_15_4_print_addr(ndo, p, dst_addr_len);
	p += dst_addr_len;
	caplen -= dst_addr_len;

	ND_PRINT(" < ");

	/* Print src PAN and address. */
	if (src_pan) {
		if (caplen < 2) {
			ND_PRINT("[ERROR: Truncated before dst_pan]");
			return 0;
		}
		ND_PRINT("%04x:", GET_LE_U_2(p));
		p += 2;
		caplen -= 2;
	} else {
		ND_PRINT("-:");
	}
	if (caplen < (u_int) src_addr_len) {
		ND_PRINT("[ERROR: Truncated before dst_addr]");
		return 0;
	}
	ieee802_15_4_print_addr(ndo, p, src_addr_len);
	ND_PRINT(" ");
	p += src_addr_len;
	caplen -= src_addr_len;
	if (CHECK_BIT(fc, 3)) {
		/*
		 * XXX - if frame_version is 0, this is the 2003
		 * spec, and you don't have the auxiliary security
		 * header, you have a frame counter and key index
		 * for the AES-CTR and AES-CCM security suites but
		 * not for the AES-CBC-MAC security suite.
		 */
		len = ieee802_15_4_print_aux_sec_header(ndo, p, caplen,
							&security_level);
		if (len < 0) {
			return 0;
		}
		ND_TCHECK_LEN(p, len);
		p += len;
		caplen -= len;
	} else {
		security_level = 0;
	}

	switch (security_level) {
	case 0: /*FALLTHOUGH */
	case 4:
		miclen = 0;
		break;
	case 1: /*FALLTHOUGH */
	case 5:
		miclen = 4;
		break;
	case 2: /*FALLTHOUGH */
	case 6:
		miclen = 8;
		break;
	case 3: /*FALLTHOUGH */
	case 7:
		miclen = 16;
		break;
	}

	/* Remove MIC */
	if (miclen != 0) {
		if (caplen < miclen) {
			ND_PRINT("[ERROR: Truncated before MIC]");
			return 0;
		}
		caplen -= miclen;
		mic_start = p + caplen;
	}

	/* Parse Information elements if present */
	if (CHECK_BIT(fc, 9)) {
		/* Yes we have those. */
		len = ieee802_15_4_print_header_ie_list(ndo, p, caplen,
							&payload_ie_present);
		if (len < 0) {
			return 0;
		}
		p += len;
		caplen -= len;
	}

	if (payload_ie_present) {
		if (security_level >= 4) {
			ND_PRINT("Payload IEs present, but encrypted, cannot print ");
		} else {
			len = ieee802_15_4_print_payload_ie_list(ndo, p, caplen);
			if (len < 0) {
				return 0;
			}
			p += len;
			caplen -= len;
		}
	}

	/* Print MIC */
	if (ndo->ndo_vflag > 2 && miclen != 0) {
		ND_PRINT("\n\tMIC ");

		for (u_int micoffset = 0; micoffset < miclen; micoffset++) {
			ND_PRINT("%02x", GET_U_1(mic_start + micoffset));
		}
		ND_PRINT(" ");
	}

	/* Print FCS */
	if (ndo->ndo_vflag > 2) {
		if (crc_check == fcs) {
			ND_PRINT("FCS %x ", fcs);
		} else {
			ND_PRINT("wrong FCS %x vs %x (assume no FCS stored) ",
				 fcs, crc_check);
		}
	}

	/* Payload print */
	switch (frame_type) {
	case 0x00: /* Beacon */
		if (frame_version < 2) {
			if (caplen < 2) {
				ND_PRINT("[ERROR: Truncated before beacon information]");
				break;
			} else {
				uint16_t ss;

				ss = GET_LE_U_2(p);
				ieee802_15_4_print_superframe_specification(ndo, ss);
				p += 2;
				caplen -= 2;

				/* GTS */
				if (caplen < 1) {
					ND_PRINT("[ERROR: Truncated before GTS info]");
					break;
				}

				len = ieee802_15_4_print_gts_info(ndo, p, caplen);
				if (len < 0) {
					break;
				}

				p += len;
				caplen -= len;

				/* Pending Addresses */
				if (caplen < 1) {
					ND_PRINT("[ERROR: Truncated before pending addresses]");
					break;
				}
				len = ieee802_15_4_print_pending_addresses(ndo, p, caplen);
				if (len < 0) {
					break;
				}
				ND_TCHECK_LEN(p, len);
				p += len;
				caplen -= len;
			}
		}
		if (!ndo->ndo_suppress_default_print)
			ND_DEFAULTPRINT(p, caplen);

		break;
	case 0x01: /* Data */
	case 0x02: /* Acknowledgement */
		if (!ndo->ndo_suppress_default_print)
			ND_DEFAULTPRINT(p, caplen);
		break;
	case 0x03: /* MAC Command */
		if (caplen < 1) {
			ND_PRINT("[ERROR: Truncated before Command ID]");
		} else {
			uint8_t command_id;

			command_id = GET_U_1(p);
			if (command_id >= 0x30) {
				ND_PRINT("Command ID = Reserved 0x%02x ",
					 command_id);
			} else {
				ND_PRINT("Command ID = %s ",
					 mac_c_names[command_id]);
			}
			p++;
			caplen--;
			if (caplen != 0) {
				len = ieee802_15_4_print_command_data(ndo, command_id, p, caplen);
				if (len >= 0) {
					p += len;
					caplen -= len;
				}
			}
		}
		if (!ndo->ndo_suppress_default_print)
			ND_DEFAULTPRINT(p, caplen);
		break;
	}
	return 1;
}

/*
 * Print and parse Multipurpose frames.
 *
 * Returns FALSE in case of error.
 */
static u_int
ieee802_15_4_mp_frame(netdissect_options *ndo,
		      const u_char *p, u_int caplen,
		      uint16_t fc)
{
	int len, frame_version, pan_id_present;
	int src_addr_len, dst_addr_len;
	int security_level;
	u_int miclen = 0;
	int ie_present, payload_ie_present, security_enabled;
	uint8_t seq;
	uint32_t fcs, crc_check;
	const u_char *mic_start = NULL;

	pan_id_present = 0;
	ie_present = 0;
	payload_ie_present = 0;
	security_enabled = 0;
	crc_check = 0;

	/* Assume 2 octet FCS, the FCS length depends on the PHY, and we do not
	   know about that. */
	if (caplen < 3) {
		/* Cannot have FCS, assume no FCS. */
		fcs = 0;
	} else {
		if (caplen > 4) {
			/* Test for 4 octet FCS. */
			fcs = GET_LE_U_4(p + caplen - 4);
			crc_check = ieee802_15_4_crc32(ndo, p, caplen - 4);
			if (crc_check == fcs) {
				/* Remove FCS */
				caplen -= 4;
			} else {
				fcs = GET_LE_U_2(p + caplen - 2);
				crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
				if (crc_check == fcs) {
					/* Remove FCS */
					caplen -= 2;
				}
			}
		} else {
			fcs = GET_LE_U_2(p + caplen - 2);
			crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
			if (crc_check == fcs) {
				/* Remove FCS */
				caplen -= 2;
			}
		}
	}

	if (CHECK_BIT(fc, 3)) {
		/* Long Frame Control */

		/* Frame version. */
		frame_version = FC_FRAME_VERSION(fc);
		ND_PRINT("v%d ", frame_version);

		pan_id_present = CHECK_BIT(fc, 8);
		ie_present = CHECK_BIT(fc, 15);
		security_enabled = CHECK_BIT(fc, 9);

		if (ndo->ndo_vflag > 2) {
			if (security_enabled) { ND_PRINT("Security Enabled, "); }
			if (CHECK_BIT(fc, 11)) { ND_PRINT("Frame Pending, "); }
			if (CHECK_BIT(fc, 14)) { ND_PRINT("AR, "); }
			if (pan_id_present) { ND_PRINT("PAN ID Present, "); }
			if (CHECK_BIT(fc, 10)) {
				ND_PRINT("Sequence Number Suppression, ");
			}
			if (ie_present) { ND_PRINT("IE present, "); }
		}

		/* Check for the sequence number suppression. */
		if (CHECK_BIT(fc, 10)) {
			/* Sequence number is suppressed, but long version. */
			if (caplen < 2) {
				nd_print_trunc(ndo);
				return 0;
			}
			p += 2;
			caplen -= 2;
		} else {
			seq = GET_U_1(p + 2);
			if (ndo->ndo_vflag)
				ND_PRINT("seq %02x ", seq);
			if (caplen < 3) {
				nd_print_trunc(ndo);
				return 0;
			}
			p += 3;
			caplen -= 3;
		}
	} else {
		/* Short format of header, but with seq no */
		seq = GET_U_1(p + 1);
		p += 2;
		caplen -= 2;
		if (ndo->ndo_vflag)
			ND_PRINT("seq %02x ", seq);
	}

	/* See which parts of addresses we have. */
	dst_addr_len = ieee802_15_4_addr_len((fc >> 4) & 0x3);
	src_addr_len = ieee802_15_4_addr_len((fc >> 6) & 0x3);
	if (src_addr_len < 0) {
		ND_PRINT("[ERROR: Invalid src address mode]");
		return 0;
	}
	if (dst_addr_len < 0) {
		ND_PRINT("[ERROR: Invalid dst address mode]");
		return 0;
	}

	/* Print dst PAN and address. */
	if (pan_id_present) {
		if (caplen < 2) {
			ND_PRINT("[ERROR: Truncated before dst_pan]");
			return 0;
		}
		ND_PRINT("%04x:", GET_LE_U_2(p));
		p += 2;
		caplen -= 2;
	} else {
		ND_PRINT("-:");
	}
	if (caplen < (u_int) dst_addr_len) {
		ND_PRINT("[ERROR: Truncated before dst_addr]");
		return 0;
	}
	ieee802_15_4_print_addr(ndo, p, dst_addr_len);
	p += dst_addr_len;
	caplen -= dst_addr_len;

	ND_PRINT(" < ");

	/* Print src PAN and address. */
	ND_PRINT(" -:");
	if (caplen < (u_int) src_addr_len) {
		ND_PRINT("[ERROR: Truncated before dst_addr]");
		return 0;
	}
	ieee802_15_4_print_addr(ndo, p, src_addr_len);
	ND_PRINT(" ");
	p += src_addr_len;
	caplen -= src_addr_len;

	if (security_enabled) {
		len = ieee802_15_4_print_aux_sec_header(ndo, p, caplen,
							&security_level);
		if (len < 0) {
			return 0;
		}
		ND_TCHECK_LEN(p, len);
		p += len;
		caplen -= len;
	} else {
		security_level = 0;
	}

	switch (security_level) {
	case 0: /*FALLTHOUGH */
	case 4:
		miclen = 0;
		break;
	case 1: /*FALLTHOUGH */
	case 5:
		miclen = 4;
		break;
	case 2: /*FALLTHOUGH */
	case 6:
		miclen = 8;
		break;
	case 3: /*FALLTHOUGH */
	case 7:
		miclen = 16;
		break;
	}

	/* Remove MIC */
	if (miclen != 0) {
		if (caplen < miclen) {
			ND_PRINT("[ERROR: Truncated before MIC]");
			return 0;
		}
		caplen -= miclen;
		mic_start = p + caplen;
	}

	/* Parse Information elements if present */
	if (ie_present) {
		/* Yes we have those. */
		len = ieee802_15_4_print_header_ie_list(ndo, p, caplen,
							&payload_ie_present);
		if (len < 0) {
			return 0;
		}
		p += len;
		caplen -= len;
	}

	if (payload_ie_present) {
		if (security_level >= 4) {
			ND_PRINT("Payload IEs present, but encrypted, cannot print ");
		} else {
			len = ieee802_15_4_print_payload_ie_list(ndo, p,
								 caplen);
			if (len < 0) {
				return 0;
			}
			p += len;
			caplen -= len;
		}
	}

	/* Print MIC */
	if (ndo->ndo_vflag > 2 && miclen != 0) {
		ND_PRINT("\n\tMIC ");

		for (u_int micoffset = 0; micoffset < miclen; micoffset++) {
			ND_PRINT("%02x", GET_U_1(mic_start + micoffset));
		}
		ND_PRINT(" ");
	}


	/* Print FCS */
	if (ndo->ndo_vflag > 2) {
		if (crc_check == fcs) {
			ND_PRINT("FCS %x ", fcs);
		} else {
			ND_PRINT("wrong FCS %x vs %x (assume no FCS stored) ",
				 fcs, crc_check);
		}
	}

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

	return 1;
}

/*
 * Print frag frame.
 *
 * Returns FALSE in case of error.
 */
static u_int
ieee802_15_4_frag_frame(netdissect_options *ndo _U_,
			const u_char *p _U_,
			u_int caplen _U_,
			uint16_t fc _U_)
{
	/* Not implement yet, might be bit hard to implement, as the
	 * information to set up the fragment is coming in the previous frame
	 * in the Fragment Sequence Context Description IE, thus we need to
	 * store information from there, so we can use it here. */
	return 0;
}

/*
 * Internal call to dissector taking packet + len instead of pcap_pkthdr.
 *
 * Returns FALSE in case of error.
 */
u_int
ieee802_15_4_print(netdissect_options *ndo,
		   const u_char *p, u_int caplen)
{
	int frame_type;
	uint16_t fc;

	ndo->ndo_protocol = "802.15.4";

	if (caplen < 2) {
		nd_print_trunc(ndo);
		return caplen;
	}

	fc = GET_LE_U_2(p);

	/* First we need to check the frame type to know how to parse the rest
	   of the FC. Frame type is the first 3 bit of the frame control field.
	*/

	frame_type = FC_FRAME_TYPE(fc);
	ND_PRINT("IEEE 802.15.4 %s packet ", ftypes[frame_type]);

	switch (frame_type) {
	case 0x00: /* Beacon */
	case 0x01: /* Data */
	case 0x02: /* Acknowledgement */
	case 0x03: /* MAC Command */
		return ieee802_15_4_std_frames(ndo, p, caplen, fc);
		break;
	case 0x04: /* Reserved */
		return 0;
		break;
	case 0x05: /* Multipurpose */
		return ieee802_15_4_mp_frame(ndo, p, caplen, fc);
		break;
	case 0x06: /* Fragment or Frak */
		return ieee802_15_4_frag_frame(ndo, p, caplen, fc);
		break;
	case 0x07: /* Extended */
		return 0;
		break;
	}
	return 0;
}

/*
 * Main function to print packets.
 */

void
ieee802_15_4_if_print(netdissect_options *ndo,
                      const struct pcap_pkthdr *h, const u_char *p)
{
	u_int caplen = h->caplen;
	ndo->ndo_protocol = "802.15.4";
	ndo->ndo_ll_hdr_len += ieee802_15_4_print(ndo, p, caplen);
}

/* For DLT_IEEE802_15_4_TAP */
/* https://github.com/jkcko/ieee802.15.4-tap */
void
ieee802_15_4_tap_if_print(netdissect_options *ndo,
                          const struct pcap_pkthdr *h, const u_char *p)
{
	uint8_t version;
	uint16_t length;

	ndo->ndo_protocol = "802.15.4_tap";
	if (h->caplen < 4) {
		nd_print_trunc(ndo);
		ndo->ndo_ll_hdr_len += h->caplen;
		return;
	}

	version = GET_U_1(p);
	length = GET_LE_U_2(p + 2);
	if (version != 0 || length < 4) {
		nd_print_invalid(ndo);
		return;
	}

	if (h->caplen < length) {
		nd_print_trunc(ndo);
		ndo->ndo_ll_hdr_len += h->caplen;
		return;
	}

	ndo->ndo_ll_hdr_len += ieee802_15_4_print(ndo, p+length, h->caplen-length) + length;
}
