+ 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;