| /* |
| * Copyright (c) 2009 |
| * Siemens AG, All rights reserved. |
| * Dmitry Eremin-Solenikov ([email protected]) |
| * |
| * 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; |
| } |