wireshark/epan/dissectors/packet-ieee802154.c

3138 lines
131 KiB
C
Raw Normal View History

/* packet-ieee802154.c
*
* Auxiliary Security Header support and
* option to force TI CC24xx FCS format
* By Jean-Francois Wauthy <jfw@info.fundp.ac.be>
* Copyright 2009 The University of Namur, Belgium
*
* IEEE 802.15.4 Dissectors for Wireshark
* By Owen Kirby <osk@exegin.com>
* Copyright 2007 Exegin Technologies Limited
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*------------------------------------------------------------
*
* In IEEE 802.15.4 packets, all fields are little endian. And
* Each byte is transmitted least significant bit first (reflected
* bit ordering).
*------------------------------------------------------------
*
* IEEE 802.15.4 Packets have the following format:
* | FCF |Seq No| Addressing | Data | FCS |
* |2 bytes|1 byte|0 to 20 bytes|Length-(Overhead) bytes|2 Bytes|
*------------------------------------------------------------
*
* CRC16 is calculated using the x^16 + x^12 + x^5 + 1 polynomial
* as specified by ITU-T, and is calculated over the IEEE 802.15.4
* packet (excluding the FCS) as transmitted over the air. Note,
* that because the least significan bits are transmitted first, this
* will require reversing the bit-order in each byte. Also, unlike
* most CRC algorithms, IEEE 802.15.4 uses an initial and final value
* of 0x0000, instead of 0xffff (which is used by the CCITT).
*------------------------------------------------------------
*
* This dissector supports both link-layer IEEE 802.15.4 captures
* and IEEE 802.15.4 packets encapsulated within other layers.
* Additionally, support has been provided for various formats
* of the frame check sequence:
* - IEEE 802.15.4 compliant FCS.
* - ChipCon/Texas Instruments CC24xx style FCS.
*------------------------------------------------------------
*/
/* Include files */
#include "config.h"
#include <epan/packet.h>
#include <epan/decode_as.h>
#include <epan/exceptions.h>
#include <epan/crc16-tvb.h>
#include <epan/expert.h>
#include <epan/addr_resolv.h>
#include <epan/address_types.h>
#include <epan/prefs.h>
#include <epan/uat.h>
#include <epan/strutil.h>
#include <epan/to_str.h>
Move show_exception() and show_reported_bounds_error() to epan/show_exception.c, as it's used outside epan/dissectors/packet-frame.c. Update their callers to include <epan/show_exception.h> to get their declaration. Add a CATCH_NONFATAL_ERRORS macro that catches all exceptions that, if there's more stuff in the packet to dissect after the dissector call that threw the exception, doesn't mean you shouldn't go ahead and dissect that stuff. Use it in all those cases, including ones where BoundsError was inappropriately being caught (you want those passed up to the top level, so that the packet is reported as having been cut short in the capture process). Add a CATCH_BOUNDS_ERRORS macro that catches all exceptions that correspond to running past the end of the data for a tvbuff; use it rather than explicitly catching those exceptions individually, and rather than just catching all exceptions (the only place that DissectorError should be caught, for example, is at the top level, so dissector bugs show up in the protocol tree). Don't catch and then immediately rethrow exceptions without doing anything else; just let the exceptions go up to the final catcher. Use show_exception() to report non-fatal errors, rather than doing it yourself. If a dissector is called from Lua, catch all non-fatal errors and use show_exception() to report them rather than catching only ReportedBoundsError and adding a proto_malformed item. Don't catch exceptions when constructing a trailer tvbuff in packet-ieee8023.c - just construct it after the payload has been dissected, and let whatever exceptions that throws be handled at the top level. Avoid some TRY/CATCH/ENDTRY cases by using checks such as tvb_bytes_exist() before even looking in the tvbuff. svn path=/trunk/; revision=47924
2013-02-27 22:43:54 +00:00
#include <epan/show_exception.h>
#include <wsutil/pint.h>
/* Use libgcrypt for cipher libraries. */
#include <wsutil/wsgcrypt.h>
#include "packet-ieee802154.h"
#include "packet-sll.h"
void proto_register_ieee802154(void);
void proto_reg_handoff_ieee802154(void);
/* Dissection Options for dissect_ieee802154_common */
#define DISSECT_IEEE802154_OPTION_CC24xx 0x00000001 /* FCS field contains a TI CC24xx style FCS. */
#define DISSECT_IEEE802154_OPTION_LINUX 0x00000002 /* Addressing fields are padded DLT_IEEE802_15_4_LINUX, not implemented. */
/* ethertype for 802.15.4 tag - encapsulating an Ethernet packet */
static unsigned int ieee802154_ethertype = 0x809A;
/* boolean value set if the FCS field is using the TI CC24xx format */
static gboolean ieee802154_cc24xx = FALSE;
/* boolean value set if the FCS must be ok before payload is dissected */
static gboolean ieee802154_fcs_ok = TRUE;
/* User string with the decryption key. */
static const gchar *ieee802154_key_str = NULL;
static gboolean ieee802154_key_valid;
static guint8 ieee802154_key[IEEE802154_CIPHER_SIZE];
static const char *ieee802154_user = "User";
/*
* Address Hash Tables
*
*/
static ieee802154_map_tab_t ieee802154_map = { NULL, NULL };
/*
* Static Address Mapping UAT
*
*/
/* UAT entry structure. */
typedef struct {
guchar *eui64;
guint eui64_len;
guint addr16;
guint pan;
} static_addr_t;
/* UAT variables */
static uat_t *static_addr_uat = NULL;
static static_addr_t *static_addrs = NULL;
static guint num_static_addrs = 0;
/* Sanity-checks a UAT record. */
static gboolean
addr_uat_update_cb(void *r, char **err)
{
static_addr_t *map = (static_addr_t *)r;
/* Ensure a valid short address */
if (map->addr16 >= IEEE802154_NO_ADDR16) {
*err = g_strdup("Invalid short address");
return FALSE;
}
/* Ensure a valid PAN identifier. */
if (map->pan >= IEEE802154_BCAST_PAN) {
*err = g_strdup("Invalid PAN identifier");
return FALSE;
}
/* Ensure a valid EUI-64 length */
if (map->eui64_len != sizeof(guint64)) {
*err = g_strdup("Invalid EUI-64 length");
return FALSE;
}
return TRUE;
} /* ieee802154_addr_uat_update_cb */
/* Field callbacks. */
UAT_HEX_CB_DEF(addr_uat, addr16, static_addr_t)
UAT_HEX_CB_DEF(addr_uat, pan, static_addr_t)
UAT_BUFFER_CB_DEF(addr_uat, eui64, static_addr_t, eui64, eui64_len)
/*-------------------------------------
* Dissector Function Prototypes
*-------------------------------------
*/
/* Dissection Routines. */
static int dissect_ieee802154_nonask_phy (tvbuff_t *, packet_info *, proto_tree *, void *);
static int dissect_ieee802154 (tvbuff_t *, packet_info *, proto_tree *, void *);
static int dissect_ieee802154_nofcs (tvbuff_t *, packet_info *, proto_tree *, void *);
static int dissect_ieee802154_cc24xx (tvbuff_t *, packet_info *, proto_tree *, void *);
/*static void dissect_ieee802154_linux (tvbuff_t *, packet_info *, proto_tree *); TODO: Implement Me. */
static void dissect_ieee802154_common (tvbuff_t *, packet_info *, proto_tree *, guint);
/* Information Elements */
static void dissect_ieee802154_header_ie (tvbuff_t *, packet_info *, proto_tree *, guint *, ieee802154_packet *);
static void dissect_ieee802154_payload_ie (tvbuff_t *, packet_info *, proto_tree *, guint *);
/* Sub-dissector helpers. */
static void dissect_ieee802154_fcf (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *, guint *);
static void dissect_ieee802154_superframe (tvbuff_t *, packet_info *, proto_tree *, guint *);
static void dissect_ieee802154_gtsinfo (tvbuff_t *, packet_info *, proto_tree *, guint *);
static void dissect_ieee802154_pendaddr (tvbuff_t *, packet_info *, proto_tree *, guint *);
static void dissect_ieee802154_command (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_assoc_req (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_assoc_rsp (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_disassoc (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_realign (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
static void dissect_ieee802154_gtsreq (tvbuff_t *, packet_info *, proto_tree *, ieee802154_packet *);
/* Decryption helpers. */
typedef enum {
DECRYPT_PACKET_SUCCEEDED,
DECRYPT_NOT_ENCRYPTED,
DECRYPT_VERSION_UNSUPPORTED,
DECRYPT_PACKET_TOO_SMALL,
DECRYPT_PACKET_NO_EXT_SRC_ADDR,
DECRYPT_PACKET_NO_KEY,
DECRYPT_PACKET_DECRYPT_FAILED,
DECRYPT_PACKET_MIC_CHECK_FAILED
} ws_decrypt_status;
static tvbuff_t *dissect_ieee802154_decrypt(tvbuff_t *, guint, packet_info *, ieee802154_packet *,
ws_decrypt_status *);
static void ccm_init_block (gchar *, gboolean, gint, guint64, ieee802154_packet *, gint);
static gboolean ccm_ctr_encrypt (const gchar *, const gchar *, gchar *, gchar *, gint);
static gboolean ccm_cbc_mac (const gchar *, const gchar *, const gchar *, gint, const gchar *, gint, gchar *);
/* Initialize Protocol and Registered fields */
static int proto_ieee802154_nonask_phy = -1;
static int hf_ieee802154_nonask_phy_preamble = -1;
static int hf_ieee802154_nonask_phy_sfd = -1;
static int hf_ieee802154_nonask_phy_length = -1;
static int hf_ieee802154_nonask_phr = -1;
static int proto_ieee802154 = -1;
static int hf_ieee802154_frame_length = -1;
static int hf_ieee802154_fcf = -1;
static int hf_ieee802154_frame_type = -1;
static int hf_ieee802154_security = -1;
static int hf_ieee802154_pending = -1;
static int hf_ieee802154_ack_request = -1;
static int hf_ieee802154_pan_id_compression = -1;
static int hf_ieee802154_seqno_suppression = -1;
static int hf_ieee802154_ie_present = -1;
static int hf_ieee802154_src_addr_mode = -1;
static int hf_ieee802154_version = -1;
static int hf_ieee802154_dst_addr_mode = -1;
static int hf_ieee802154_header_ie = -1;
static int hf_ieee802154_header_ie_type = -1;
static int hf_ieee802154_header_ie_id = -1;
static int hf_ieee802154_header_ie_length = -1;
static int hf_ieee802154_header_ie_data = -1;
static int hf_ieee802154_payload_ie = -1;
static int hf_ieee802154_payload_ie_type = -1;
static int hf_ieee802154_payload_ie_id = -1;
static int hf_ieee802154_payload_ie_length = -1;
static int hf_ieee802154_payload_ie_data = -1;
static int hf_ieee802154_seqno = -1;
static int hf_ieee802154_dst_panID = -1;
static int hf_ieee802154_dst16 = -1;
static int hf_ieee802154_dst64 = -1;
static int hf_ieee802154_src_panID = -1;
static int hf_ieee802154_src16 = -1;
static int hf_ieee802154_src64 = -1;
static int hf_ieee802154_src64_origin = -1;
static int hf_ieee802154_fcs = -1;
static int hf_ieee802154_rssi = -1;
static int hf_ieee802154_fcs_ok = -1;
static int hf_ieee802154_correlation = -1;
/* Registered fields for Command Packets */
static int hf_ieee802154_cmd_id = -1;
static int hf_ieee802154_cinfo_alt_coord = -1;
static int hf_ieee802154_cinfo_device_type = -1;
static int hf_ieee802154_cinfo_power_src = -1;
static int hf_ieee802154_cinfo_idle_rx = -1;
static int hf_ieee802154_cinfo_sec_capable = -1;
static int hf_ieee802154_cinfo_alloc_addr = -1;
static int hf_ieee802154_assoc_addr = -1;
static int hf_ieee802154_assoc_status = -1;
static int hf_ieee802154_disassoc_reason = -1;
static int hf_ieee802154_realign_pan = -1;
static int hf_ieee802154_realign_caddr = -1;
static int hf_ieee802154_realign_channel = -1;
static int hf_ieee802154_realign_addr = -1;
static int hf_ieee802154_realign_channel_page = -1;
static int hf_ieee802154_gtsreq_len = -1;
static int hf_ieee802154_gtsreq_dir = -1;
static int hf_ieee802154_gtsreq_type = -1;
/* Registered fields for Beacon Packets */
static int hf_ieee802154_beacon_order = -1;
static int hf_ieee802154_superframe_order = -1;
static int hf_ieee802154_cap = -1;
static int hf_ieee802154_superframe_battery_ext = -1;
static int hf_ieee802154_superframe_coord = -1;
static int hf_ieee802154_assoc_permit = -1;
static int hf_ieee802154_gts_count = -1;
static int hf_ieee802154_gts_permit = -1;
static int hf_ieee802154_gts_direction = -1;
static int hf_ieee802154_gts_address = -1;
static int hf_ieee802154_pending16 = -1;
static int hf_ieee802154_pending64 = -1;
/* Registered fields for Auxiliary Security Header */
static int hf_ieee802154_security_control_field = -1;
static int hf_ieee802154_security_level = -1;
static int hf_ieee802154_key_id_mode = -1;
static int hf_ieee802154_aux_sec_reserved = -1;
static int hf_ieee802154_aux_sec_frame_counter = -1;
static int hf_ieee802154_aux_sec_key_source = -1;
static int hf_ieee802154_aux_sec_key_index = -1;
/* 802.15.4-2003 security */
static int hf_ieee802154_sec_frame_counter = -1;
static int hf_ieee802154_sec_key_sequence_counter = -1;
/* Initialize Subtree Pointers */
static gint ett_ieee802154_nonask_phy = -1;
static gint ett_ieee802154_nonask_phy_phr = -1;
static gint ett_ieee802154 = -1;
static gint ett_ieee802154_fcf = -1;
static gint ett_ieee802154_auxiliary_security = -1;
static gint ett_ieee802154_aux_sec_control = -1;
static gint ett_ieee802154_aux_sec_key_id = -1;
static gint ett_ieee802154_fcs = -1;
static gint ett_ieee802154_cmd = -1;
static gint ett_ieee802154_superframe = -1;
static gint ett_ieee802154_gts = -1;
static gint ett_ieee802154_gts_direction = -1;
static gint ett_ieee802154_gts_descriptors = -1;
static gint ett_ieee802154_pendaddr = -1;
static gint ett_ieee802154_header = -1;
static gint ett_ieee802154_header_ie = -1;
static gint ett_ieee802154_payload = -1;
static gint ett_ieee802154_payload_ie = -1;
static expert_field ei_ieee802154_invalid_addressing = EI_INIT;
static expert_field ei_ieee802154_invalid_panid_compression = EI_INIT;
static expert_field ei_ieee802154_fcs = EI_INIT;
static expert_field ei_ieee802154_decrypt_error = EI_INIT;
static expert_field ei_ieee802154_dst = EI_INIT;
static expert_field ei_ieee802154_src = EI_INIT;
static expert_field ei_ieee802154_frame_ver = EI_INIT;
static expert_field ei_ieee802154_frame_type = EI_INIT;
static expert_field ei_ieee802154_seqno_suppression = EI_INIT;
static int ieee802_15_4_short_address_type = -1;
/*
* Dissector handles
* - beacon dissection is always heuristic.
* - the PANID table is for stateful dissectors only (ie: Decode-As)
* - otherwise, data dissectors fall back to the heuristic dissectors.
*/
static dissector_handle_t data_handle;
static dissector_table_t panid_dissector_table;
static heur_dissector_list_t ieee802154_beacon_subdissector_list;
static heur_dissector_list_t ieee802154_heur_subdissector_list;
/* Versions */
static const value_string ieee802154_frame_versions[] = {
{ IEEE802154_VERSION_2003, "IEEE Std 802.15.4-2003" },
{ IEEE802154_VERSION_2006, "IEEE Std 802.15.4-2006" },
{ IEEE802154_VERSION_2012e, "IEEE Std 802.15.4-2012e" },
{ IEEE802154_VERSION_RESERVED, "Reserved" },
{ 0, NULL }
};
/* Name Strings */
static const value_string ieee802154_frame_types[] = {
{ IEEE802154_FCF_BEACON, "Beacon" },
{ IEEE802154_FCF_DATA, "Data" },
{ IEEE802154_FCF_ACK, "Ack" },
{ IEEE802154_FCF_CMD, "Command" },
{ IEEE802154_FCF_RESERVED, "Reserved" },
{ IEEE802154_FCF_MULTIPURPOSE, "Multipurpose" },
{ IEEE802154_FCF_FRAGMENT, "Fragment or Frak" },
{ IEEE802154_FCF_EXTENDED, "Extended" },
{ 0, NULL }
};
static const value_string ieee802154_addr_modes[] = {
{ IEEE802154_FCF_ADDR_NONE, "None" },
{ IEEE802154_FCF_ADDR_RESERVED, "Reserved" },
{ IEEE802154_FCF_ADDR_SHORT, "Short/16-bit" },
{ IEEE802154_FCF_ADDR_EXT, "Long/64-bit" },
{ 0, NULL }
};
static const value_string ieee802154_cmd_names[] = {
{ IEEE802154_CMD_ASSOC_REQ, "Association Request" },
{ IEEE802154_CMD_ASSOC_RSP, "Association Response" },
{ IEEE802154_CMD_DISASSOC_NOTIFY, "Disassociation Notification" },
{ IEEE802154_CMD_DATA_RQ, "Data Request" },
{ IEEE802154_CMD_PANID_CONFLICT, "PAN ID Conflict" },
{ IEEE802154_CMD_ORPHAN_NOTIFY, "Orphan Notification" },
{ IEEE802154_CMD_BEACON_REQ, "Beacon Request" },
{ IEEE802154_CMD_COORD_REALIGN, "Coordinator Realignment" },
{ IEEE802154_CMD_GTS_REQ, "GTS Request" },
{ IEEE802154_CMD_TRLE_MGMT_REQ, "TRLE Management Request"},
{ IEEE802154_CMD_TRLE_MGMT_RSP, "TRLE Management Response"},
{ IEEE802154_CMD_DSME_ASSOC_REQ, "DSME Association Request"},
{ IEEE802154_CMD_DSME_ASSOC_RSP, "DSME Association Response"},
{ IEEE802154_CMD_DSME_GTS_REQ, "DSME GTS Request"},
{ IEEE802154_CMD_DSME_GTS_RSP, "DSME GTS Response"},
{ IEEE802154_CMD_DSME_GTS_NOTIFY, "DSME GTS Notify"},
{ IEEE802154_CMD_DSME_INFO_REQ, "DSME Information Request"},
{ IEEE802154_CMD_DSME_INFO_RSP, "DSME Information Reponse"},
{ IEEE802154_CMD_DSME_BEACON_ALLOC_NOTIFY, "DSME Beacon Allocation Notification"},
{ IEEE802154_CMD_DSME_BEACON_COLL_NOTIFY, "DSME Beacon Collision Notification"},
{ IEEE802154_CMD_DSME_LINK_REPORT, "DSME Link Report"},
{ IEEE802154_CMD_RIT_DATA_REQ, "RIT Data Request"},
{ IEEE802154_CMD_DBS_REQ, "DBS Request"},
{ IEEE802154_CMD_DBS_RSP, "DBS Response"},
{ 0, NULL }
};
static const value_string ieee802154_sec_level_names[] = {
{ SECURITY_LEVEL_NONE, "No Security" },
{ SECURITY_LEVEL_MIC_32, "32-bit Message Integrity Code" },
{ SECURITY_LEVEL_MIC_64, "64-bit Message Integrity Code" },
{ SECURITY_LEVEL_MIC_128, "128-bit Message Integrity Code" },
{ SECURITY_LEVEL_ENC, "Encryption" },
{ SECURITY_LEVEL_ENC_MIC_32, "Encryption with 32-bit Message Integrity Code" },
{ SECURITY_LEVEL_ENC_MIC_64, "Encryption with 64-bit Message Integrity Code" },
{ SECURITY_LEVEL_ENC_MIC_128, "Encryption with 128-bit Message Integrity Code" },
{ 0, NULL }
};
static const value_string ieee802154_key_id_mode_names[] = {
{ KEY_ID_MODE_IMPLICIT, "Implicit Key" },
{ KEY_ID_MODE_KEY_INDEX, "Indexed Key using the Default Key Source" },
{ KEY_ID_MODE_KEY_EXPLICIT_4, "Explicit Key with 4-octet Key Source" },
{ KEY_ID_MODE_KEY_EXPLICIT_8, "Explicit Key with 8-octet Key Source" },
{ 0, NULL }
};
static const true_false_string ieee802154_gts_direction_tfs = {
"Receive Only",
"Transmit Only"
};
/* The 802.15.4-2003 security suites for the security preferences (only AES-CCM suites are supported). */
/* NOTE: The equivalent 2006 security level identifer enumerations are used to simplify 2003 & 2006 integration! */
static const enum_val_t ieee802154_2003_sec_suite_enums[] = {
{ "AES-CCM-128", "AES-128 Encryption, 128-bit Integrity Protection", SECURITY_LEVEL_ENC_MIC_128 },
{ "AES-CCM-64", "AES-128 Encryption, 64-bit Integrity Protection", SECURITY_LEVEL_ENC_MIC_64 },
{ "AES-CCM-32", "AES-128 Encryption, 32-bit Integrity Protection", SECURITY_LEVEL_ENC_MIC_32 },
{ NULL, NULL, 0 }
};
static const value_string ieee802154_ie_types[] = {
{ 0, "Header" },
{ 1, "Payload" },
{ 0, NULL }
};
static const value_string ieee802154_header_ie_names[] = {
{ IEEE802154_HEADER_IE_EID_TERM1, "Header Termination 1" },
{ IEEE802154_HEADER_IE_EID_TERM2, "Header Termination 2" },
{ 0, NULL }
};
static const value_string ieee802154_payload_ie_names[] = {
{ IEEE802154_PAYLOAD_IE_GID_TERM, "Payload Termination IE" },
{ 0, NULL }
};
/* Preferences for 2003 security */
static gint ieee802154_sec_suite = SECURITY_LEVEL_ENC_MIC_64;
static gboolean ieee802154_extend_auth = TRUE;
/* Macro to check addressing, and throw a warning flag if incorrect. */
#define IEEE802154_CMD_ADDR_CHECK(_pinfo_, _item_, _cmdid_, _x_) \
if (!(_x_)) \
expert_add_info_format(_pinfo_, _item_, &ei_ieee802154_invalid_addressing, \
"Invalid Addressing for %s", \
val_to_str_const(_cmdid_, ieee802154_cmd_names, "Unknown Command"))
/* CRC definitions. IEEE 802.15.4 CRCs vary from CCITT by using an initial value of
* 0x0000, and no XOR out. IEEE802154_CRC_XOR is defined as 0xFFFF in order to un-XOR
* the output from the CCITT CRC routines in Wireshark.
*/
#define IEEE802154_CRC_SEED 0x0000
#define IEEE802154_CRC_XOROUT 0xFFFF
#define ieee802154_crc_tvb(tvb, offset) (crc16_ccitt_tvb_seed(tvb, offset, IEEE802154_CRC_SEED) ^ IEEE802154_CRC_XOROUT)
static int ieee802_15_4_short_address_to_str(const address* addr, gchar *buf, int buf_len)
{
guint16 ieee_802_15_4_short_addr = pletoh16(addr->data);
if (ieee_802_15_4_short_addr == 0xffff)
{
g_strlcpy(buf, "Broadcast", buf_len);
return 10;
}
*buf++ = '0';
*buf++ = 'x';
buf = word_to_hex(buf, ieee_802_15_4_short_addr);
*buf = '\0'; /* NULL terminate */
return 7;
}
static int ieee802_15_4_short_address_str_len(const address* addr _U_)
{
return 11;
}
static int ieee802_15_4_short_address_len(void)
{
return 2;
}
/**
* Dissector helper, parses and displays the frame control field.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields
*@param tree pointer to data tree wireshark uses to display packet.
*@param packet IEEE 802.15.4 packet information.
*@param offset offset into the tvb to find the FCF.
*
*/
static void
dissect_ieee802154_fcf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet, guint *offset)
{
guint16 fcf;
static const int * fields[] = {
&hf_ieee802154_frame_type,
&hf_ieee802154_security,
&hf_ieee802154_pending,
&hf_ieee802154_ack_request,
&hf_ieee802154_pan_id_compression,
&hf_ieee802154_seqno_suppression,
&hf_ieee802154_ie_present,
&hf_ieee802154_dst_addr_mode,
&hf_ieee802154_version,
&hf_ieee802154_src_addr_mode,
NULL
};
/* Get the FCF field. */
fcf = tvb_get_letohs(tvb, *offset);
/* Parse FCF Flags. */
packet->frame_type = (fcf & IEEE802154_FCF_TYPE_MASK);
packet->security_enable = (fcf & IEEE802154_FCF_SEC_EN) >> 3;
packet->frame_pending = (fcf & IEEE802154_FCF_FRAME_PND) >> 4;
packet->ack_request = (fcf & IEEE802154_FCF_ACK_REQ) >> 5;
packet->pan_id_compression = (fcf & IEEE802154_FCF_PAN_ID_COMPRESSION) >> 6;
/* bit 7 reserved */
packet->seqno_suppression = (fcf & IEEE802154_FCF_SEQNO_SUPPRESSION) >> 8;
packet->ie_present = (fcf & IEEE802154_FCF_IE_PRESENT) >> 9;
packet->dst_addr_mode = (fcf & IEEE802154_FCF_DADDR_MASK) >> 10;
packet->version = (fcf & IEEE802154_FCF_VERSION) >> 12;
packet->src_addr_mode = (fcf & IEEE802154_FCF_SADDR_MASK) >> 14;
/* Display the frame type. */
proto_item_append_text(tree, " %s", val_to_str_const(packet->frame_type, ieee802154_frame_types, "Reserved"));
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(packet->frame_type, ieee802154_frame_types, "Reserved"));
proto_tree_add_bitmask(tree, tvb, *offset, hf_ieee802154_fcf,
ett_ieee802154_fcf, fields, ENC_LITTLE_ENDIAN);
*offset += 2;
} /* dissect_ieee802154_fcf */
/*
*Dissector for IEEE 802.15.4 non-ASK PHY packet with an FCS containing a 16-bit CRC value.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields
*@param tree pointer to data tree wireshark uses to display packet.
*/
static int
dissect_ieee802154_nonask_phy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
proto_tree *ieee802154_tree = NULL;
proto_item *proto_root = NULL;
guint offset = 0;
guint8 phr;
tvbuff_t* mac;
/* Create the protocol tree. */
if (tree) {
proto_root = proto_tree_add_protocol_format(tree, proto_ieee802154_nonask_phy, tvb, 0, tvb_captured_length(tvb), "IEEE 802.15.4 non-ASK PHY");
ieee802154_tree = proto_item_add_subtree(proto_root, ett_ieee802154_nonask_phy);
}
/* Add the protocol name. */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEEE 802.15.4 non-ASK PHY");
/* Add the packet length. */
col_add_fstr(pinfo->cinfo, COL_PACKET_LENGTH, "%i", tvb_captured_length(tvb));
phr=tvb_get_guint8(tvb,offset+4+1);
if(tree) {
guint loffset=offset;
static const int * phr_fields[] = {
&hf_ieee802154_nonask_phy_length,
NULL
};
proto_tree_add_item(ieee802154_tree, hf_ieee802154_nonask_phy_preamble, tvb, loffset, 4, ENC_LITTLE_ENDIAN);
loffset+=4;
proto_tree_add_item(ieee802154_tree, hf_ieee802154_nonask_phy_sfd, tvb, loffset, 1, ENC_LITTLE_ENDIAN);
loffset+=1;
proto_tree_add_bitmask(ieee802154_tree, tvb, loffset, hf_ieee802154_nonask_phr, ett_ieee802154_nonask_phy_phr,
phr_fields, ENC_NA);
}
offset+=4+2*1;
mac=tvb_new_subset(tvb,offset,-1, phr & IEEE802154_PHY_LENGTH_MASK);
/* Call the common dissector. */
dissect_ieee802154(mac, pinfo, ieee802154_tree, NULL);
return tvb_captured_length(tvb);
} /* dissect_ieee802154_nonask_phy */
/**
*Dissector for IEEE 802.15.4 packet with an FCS containing a 16-bit CRC value.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields.
*@param tree pointer to data tree wireshark uses to display packet.
*/
static int
dissect_ieee802154(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
/* Call the common dissector. */
dissect_ieee802154_common(tvb, pinfo, tree, (ieee802154_cc24xx ? DISSECT_IEEE802154_OPTION_CC24xx : 0));
return tvb_captured_length(tvb);
} /* dissect_ieee802154 */
/**
* Dissector for IEEE 802.15.4 packet with no FCS present.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields
*@param tree pointer to data tree wireshark uses to display packet.
*@return captured length.
*/
static int
dissect_ieee802154_nofcs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * data _U_)
{
tvbuff_t *new_tvb;
/* If there is no FCS present in the reported packet, then the length of
* the true IEEE 802.15.4 packet is actually 2 bytes longer. Re-create
* the buffer with an extended reported length so that the packet will
* be handled as though the FCS were truncated.
*
* Note, we can't just call tvb_set_reported_length(), because it includes
* checks to ensure that the new reported length is not longer than the old
* reported length (why?), and will throw an exception.
*/
new_tvb = tvb_new_subset(tvb, 0, -1, tvb_reported_length(tvb)+IEEE802154_FCS_LEN);
/* Call the common dissector. */
dissect_ieee802154_common(new_tvb, pinfo, tree, 0);
return tvb_captured_length(tvb);
} /* dissect_ieee802154_nofcs */
/**
*Dissector for IEEE 802.15.4 packet with a ChipCon/Texas
*Instruments compatible FCS. This is typically called by
*layers encapsulating an IEEE 802.15.4 packet.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields
*@param tree pointer to data tree wireshark uses to display packet.
*/
static int
dissect_ieee802154_cc24xx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * data _U_)
{
/* Call the common dissector. */
dissect_ieee802154_common(tvb, pinfo, tree, DISSECT_IEEE802154_OPTION_CC24xx);
return tvb_captured_length(tvb);
} /* dissect_ieee802154_cc24xx */
/**
*IEEE 802.15.4 packet dissection routine for Wireshark.
*
*This function extracts all the information first before displaying.
*If payload exists, that portion will be passed into another dissector
*for further processing.
*
*This is called after the individual dissect_ieee802154* functions
*have been called to determine what sort of FCS is present.
*The dissect_ieee802154* functions will set the parameters
*in the ieee802154_packet structure, and pass it to this one
*through the data parameter.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields
*@param tree pointer to data tree wireshark uses to display packet.
*@param options bitwise or of dissector options (see DISSECT_IEEE802154_OPTION_xxx).
*/
static void
dissect_ieee802154_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint options)
{
tvbuff_t *volatile payload_tvb;
proto_tree *volatile ieee802154_tree = NULL;
proto_item *volatile proto_root = NULL;
proto_item *hidden_item;
proto_item *ti;
guint offset = 0;
volatile gboolean fcs_ok = TRUE;
const char *saved_proto;
ws_decrypt_status status;
gboolean dstPanPresent = FALSE;
gboolean srcPanPresent = FALSE;
ieee802154_packet *packet = wmem_new0(wmem_packet_scope(), ieee802154_packet);
ieee802154_short_addr addr16;
ieee802154_hints_t *ieee_hints;
heur_dtbl_entry_t *hdtbl_entry;
packet->short_table = ieee802154_map.short_table;
/* Allocate frame data with hints for upper layers */
if(!pinfo->fd->flags.visited){
ieee_hints = wmem_new0(wmem_file_scope(), ieee802154_hints_t);
p_add_proto_data(wmem_file_scope(), pinfo, proto_ieee802154, 0, ieee_hints);
} else {
ieee_hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ieee802154, 0);
}
/* Create the protocol tree. */
if (tree) {
proto_root = proto_tree_add_protocol_format(tree, proto_ieee802154, tvb, 0, tvb_captured_length(tvb), "IEEE 802.15.4");
ieee802154_tree = proto_item_add_subtree(proto_root, ett_ieee802154);
}
/* Add the protocol name. */
col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEEE 802.15.4");
/* Add the packet length. */
col_add_fstr(pinfo->cinfo, COL_PACKET_LENGTH, "%i", tvb_captured_length(tvb));
/* Add the packet length to the filter field */
hidden_item = proto_tree_add_uint(ieee802154_tree, hf_ieee802154_frame_length, NULL, 0, 0, tvb_reported_length(tvb));
PROTO_ITEM_SET_HIDDEN(hidden_item);
/* Frame Control Field */
dissect_ieee802154_fcf(tvb, pinfo, ieee802154_tree, packet, &offset);
/* Sequence Number */
if (packet->seqno_suppression) {
if (packet->version != IEEE802154_VERSION_2012e) {
expert_add_info(pinfo, proto_root, &ei_ieee802154_seqno_suppression);
}
} else { /* IEEE 802.15.4 Sequence Number Suppression */
packet->seqno = tvb_get_guint8(tvb, offset);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_seqno, tvb, offset, 1, packet->seqno);
/* For Ack packets display this in the root. */
if (packet->frame_type == IEEE802154_FCF_ACK) {
proto_item_append_text(proto_root, ", Sequence Number: %u", packet->seqno);
}
}
offset += 1;
}
/*
* ADDRESSING FIELDS
*/
/* Clear out the addressing strings. */
set_address(&pinfo->net_dst, AT_NONE, 0, NULL);
copy_address_shallow(&pinfo->dl_dst, &pinfo->net_dst);
copy_address_shallow(&pinfo->dst, &pinfo->net_dst);
set_address(&pinfo->net_src, AT_NONE, 0, NULL);
copy_address_shallow(&pinfo->dl_src, &pinfo->net_src);
copy_address_shallow(&pinfo->src, &pinfo->net_src);
if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_RESERVED) {
/* Invalid Destination Address Mode. Abort Dissection. */
expert_add_info(pinfo, proto_root, &ei_ieee802154_dst);
return;
}
if (packet->src_addr_mode == IEEE802154_FCF_ADDR_RESERVED) {
/* Invalid Source Address Mode. Abort Dissection. */
expert_add_info(pinfo, proto_root, &ei_ieee802154_src);
return;
}
if (packet->version == IEEE802154_VERSION_RESERVED) {
/* Unknown Frame Version. Abort Dissection. */
expert_add_info(pinfo, proto_root, &ei_ieee802154_frame_ver);
return;
}
if ((packet->frame_type == IEEE802154_FCF_BEACON) ||
(packet->frame_type == IEEE802154_FCF_DATA) ||
(packet->frame_type == IEEE802154_FCF_ACK) ||
(packet->frame_type == IEEE802154_FCF_CMD) ) {
/*
* Table 6 in 802.15.4-2015
*/
if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_NONE) { /* dst not present */
if (packet->src_addr_mode == IEEE802154_FCF_ADDR_NONE) {
if ((packet->version == IEEE802154_VERSION_2003) ||
(packet->version == IEEE802154_VERSION_2006)) {
if (packet->frame_type != IEEE802154_FCF_ACK) {
/* In IEEE 802.15.4-2003 and 2006 only Acknowledgment is
* allowed to have neither a source nor destination address */
expert_add_info(pinfo, proto_root, &ei_ieee802154_invalid_addressing);
return;
}
if (packet->pan_id_compression == 1) {
expert_add_info(pinfo, proto_root, &ei_ieee802154_invalid_panid_compression);
return;
}
}
else { /* Frame Version 0b10 */
if (packet->pan_id_compression == 1) {
dstPanPresent = TRUE;
}
/* else neither are present */
}
}
else { /* src present, dst not present */
if ((packet->version == IEEE802154_VERSION_2003) ||
(packet->version == IEEE802154_VERSION_2006)) {
if (packet->pan_id_compression == 1) {
expert_add_info(pinfo, proto_root, &ei_ieee802154_invalid_panid_compression);
return;
}
else {
srcPanPresent = TRUE;
}
}
else { /* Frame Version 0b10 */
if (packet->pan_id_compression == 0) {
srcPanPresent = TRUE;
}
/* else neither present */
}
}
}
/*
* Destination Present
*/
else {
if (packet->src_addr_mode == IEEE802154_FCF_ADDR_NONE) {
if ((packet->version == IEEE802154_VERSION_2003) ||
(packet->version == IEEE802154_VERSION_2006)) {
if (packet->pan_id_compression == 1) {
expert_add_info(pinfo, proto_root, &ei_ieee802154_invalid_panid_compression);
return;
}
else {
dstPanPresent = TRUE;
}
}
else { /* Frame Version 0b10 */
if (packet->pan_id_compression == 0) {
dstPanPresent = TRUE;
}
}
}
else { /* src present, dst present */
if ((packet->version == IEEE802154_VERSION_2003) ||
(packet->version == IEEE802154_VERSION_2006)) {
if (packet->pan_id_compression == 0) {
dstPanPresent = TRUE;
srcPanPresent = TRUE;
}
else {
dstPanPresent = TRUE;
}
}
else { /* Frame Version 0b10 */
if (packet->pan_id_compression == 0) {
dstPanPresent = TRUE;
}
/* else neither PAN Ids */
}
}
}
/*
* Addressing Fields
*/
/* Destination PAN Id */
if (dstPanPresent) {
packet->dst_pan = tvb_get_letohs(tvb, offset);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_dst_panID, tvb, offset, 2, packet->dst_pan);
}
offset += 2;
}
/* Destination Address */
if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
char dst_addr[32];
/* Get the address. */
packet->dst16 = tvb_get_letohs(tvb, offset);
/* Display the destination address. */
if ( packet->dst16 == IEEE802154_BCAST_ADDR ) {
g_snprintf(dst_addr, 32, "Broadcast");
}
else {
g_snprintf(dst_addr, 32, "0x%04x", packet->dst16);
}
/* Provide address hints to higher layers that need it. */
if (ieee_hints) {
ieee_hints->dst16 = packet->dst16;
}
set_address_tvb(&pinfo->dl_dst, ieee802_15_4_short_address_type, 2, tvb, offset);
copy_address_shallow(&pinfo->dst, &pinfo->dl_dst);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_dst16, tvb, offset, 2, packet->dst16);
proto_item_append_text(proto_root, ", Dst: %s", dst_addr);
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", Dst: %s", dst_addr);
offset += 2;
}
else if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) {
guint64 *p_addr = (guint64 *)wmem_new(pinfo->pool, guint64);
/* Get the address */
packet->dst64 = tvb_get_letoh64(tvb, offset);
/* Copy and convert the address to network byte order. */
*p_addr = pntoh64(&(packet->dst64));
/* Display the destination address. */
/* XXX - OUI resolution doesn't happen when displaying resolved
* EUI64 addresses; that should probably be fixed in
* epan/addr_resolv.c.
*/
set_address(&pinfo->dl_dst, AT_EUI64, 8, p_addr);
copy_address_shallow(&pinfo->dst, &pinfo->dl_dst);
if (tree) {
proto_tree_add_item(ieee802154_tree, hf_ieee802154_dst64, tvb, offset, 8, ENC_LITTLE_ENDIAN);
proto_item_append_text(proto_root, ", Dst: %s", eui64_to_display(wmem_packet_scope(), packet->dst64));
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", Dst: %s", eui64_to_display(wmem_packet_scope(), packet->dst64));
offset += 8;
}
/* Source PAN Id */
if (srcPanPresent) {
packet->src_pan = tvb_get_letohs(tvb, offset);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_src_panID, tvb, offset, 2, packet->src_pan);
}
offset += 2;
}
else {
if (dstPanPresent) {
packet->src_pan = packet->dst_pan;
}
else {
packet->src_pan = IEEE802154_BCAST_PAN;
}
}
if (ieee_hints) {
ieee_hints->src_pan = packet->src_pan;
}
/* Source Address */
if (packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
char src_addr[32];
/* Get the address. */
packet->src16 = tvb_get_letohs(tvb, offset);
/* Update the Address fields. */
if (packet->src16==IEEE802154_BCAST_ADDR) {
g_snprintf(src_addr, 32, "Broadcast");
}
else {
g_snprintf(src_addr, 32, "0x%04x", packet->src16);
if (!pinfo->fd->flags.visited) {
/* If we know our extended source address from previous packets,
* provide a pointer to it in a hint for upper layers */
addr16.addr = packet->src16;
addr16.pan = packet->src_pan;
if (ieee_hints) {
ieee_hints->src16 = packet->src16;
ieee_hints->map_rec = (ieee802154_map_rec *)
g_hash_table_lookup(ieee802154_map.short_table, &addr16);
}
}
}
set_address_tvb(&pinfo->dl_src, ieee802_15_4_short_address_type, 2, tvb, offset);
copy_address_shallow(&pinfo->src, &pinfo->dl_src);
/* Add the addressing info to the tree. */
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_src16, tvb, offset, 2, packet->src16);
proto_item_append_text(proto_root, ", Src: %s", src_addr);
if (ieee_hints && ieee_hints->map_rec) {
/* Display inferred source address info */
ti = proto_tree_add_eui64(ieee802154_tree, hf_ieee802154_src64, tvb, offset, 0,
ieee_hints->map_rec->addr64);
PROTO_ITEM_SET_GENERATED(ti);
if ( ieee_hints->map_rec->start_fnum ) {
ti = proto_tree_add_uint(ieee802154_tree, hf_ieee802154_src64_origin, tvb, 0, 0,
ieee_hints->map_rec->start_fnum);
}
else {
ti = proto_tree_add_uint_format_value(ieee802154_tree, hf_ieee802154_src64_origin, tvb, 0, 0,
ieee_hints->map_rec->start_fnum, "Pre-configured");
}
PROTO_ITEM_SET_GENERATED(ti);
}
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: %s", src_addr);
offset += 2;
}
else if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) {
guint64 *p_addr = (guint64 *)wmem_new(pinfo->pool, guint64);
/* Get the address. */
packet->src64 = tvb_get_letoh64(tvb, offset);
/* Copy and convert the address to network byte order. */
*p_addr = pntoh64(&(packet->src64));
/* Display the source address. */
/* XXX - OUI resolution doesn't happen when displaying resolved
* EUI64 addresses; that should probably be fixed in
* epan/addr_resolv.c.
*/
set_address(&pinfo->dl_src, AT_EUI64, 8, p_addr);
copy_address_shallow(&pinfo->src, &pinfo->dl_src);
if (tree) {
proto_tree_add_item(ieee802154_tree, hf_ieee802154_src64, tvb, offset, 8, ENC_LITTLE_ENDIAN);
proto_item_append_text(proto_root, ", Src: %s", eui64_to_display(wmem_packet_scope(), packet->src64));
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: %s", eui64_to_display(wmem_packet_scope(), packet->src64));
offset += 8;
}
}
else {
/* Unsupported Frame Type. Abort Dissection. */
expert_add_info(pinfo, proto_root, &ei_ieee802154_frame_type);
return;
}
/* Check, but don't display the FCS yet, otherwise the payload dissection
* may be out of place in the tree. But we want to know if the FCS is OK in
* case the CRC is bad (don't want to continue dissection to the NWK layer).
*/
if (tvb_bytes_exist(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN, IEEE802154_FCS_LEN)) {
/* The FCS is in the last two bytes of the packet. */
guint16 fcs = tvb_get_letohs(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN);
/* Check if we are expecting a CC2420-style FCS*/
if (options & DISSECT_IEEE802154_OPTION_CC24xx) {
fcs_ok = (fcs & IEEE802154_CC24xx_CRC_OK);
}
else {
guint16 fcs_calc = ieee802154_crc_tvb(tvb, tvb_reported_length(tvb)-IEEE802154_FCS_LEN);
fcs_ok = (fcs == fcs_calc);
}
}
/* Existance of the Auxiliary Security Header is controlled by the Security Enabled Field */
if (packet->security_enable) {
proto_tree *header_tree, *field_tree;
guint8 security_control;
guint aux_length = 5; /* Minimum length of the auxiliary header. */
static const int * security_fields[] = {
&hf_ieee802154_security_level,
&hf_ieee802154_key_id_mode,
&hf_ieee802154_aux_sec_reserved,
NULL
};
/* Parse the security control field. */
security_control = tvb_get_guint8(tvb, offset);
packet->security_level = (ieee802154_security_level)(security_control & IEEE802154_AUX_SEC_LEVEL_MASK);
packet->key_id_mode = (ieee802154_key_id_mode)((security_control & IEEE802154_AUX_KEY_ID_MODE_MASK) >> IEEE802154_AUX_KEY_ID_MODE_SHIFT);
/* Compute the length of the auxiliary header and create a subtree. */
if (packet->key_id_mode != KEY_ID_MODE_IMPLICIT) aux_length++;
if (packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_4) aux_length += 4;
if (packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_8) aux_length += 8;
header_tree = proto_tree_add_subtree(ieee802154_tree, tvb, offset, aux_length,
ett_ieee802154_auxiliary_security, NULL, "Auxiliary Security Header");
/* Security Control Field */
proto_tree_add_bitmask(header_tree, tvb, offset, hf_ieee802154_security_control_field, ett_ieee802154_aux_sec_control, security_fields, ENC_NA);
offset++;
/* Frame Counter Field */
packet->frame_counter = tvb_get_letohl (tvb, offset);
proto_tree_add_uint(header_tree, hf_ieee802154_aux_sec_frame_counter, tvb, offset,4, packet->frame_counter);
offset +=4;
/* Key identifier field(s). */
if (packet->key_id_mode != KEY_ID_MODE_IMPLICIT) {
/* Create a subtree. */
field_tree = proto_tree_add_subtree(header_tree, tvb, offset, 1,
ett_ieee802154_aux_sec_key_id, &ti, "Key Identifier Field"); /* Will fix length later. */
/* Add key source, if it exists. */
if (packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_4) {
packet->key_source.addr32 = tvb_get_ntohl(tvb, offset);
proto_tree_add_uint64(field_tree, hf_ieee802154_aux_sec_key_source, tvb, offset, 4, packet->key_source.addr32);
proto_item_set_len(ti, 1 + 4);
offset += (int)sizeof (guint32);
}
if (packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_8) {
packet->key_source.addr64 = tvb_get_ntoh64(tvb, offset);
proto_tree_add_uint64(field_tree, hf_ieee802154_aux_sec_key_source, tvb, offset, 8, packet->key_source.addr64);
proto_item_set_len(ti, 1 + 8);
offset += 8;
}
/* Add key identifier. */
packet->key_index = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(field_tree, hf_ieee802154_aux_sec_key_index, tvb, offset,1, packet->key_index);
offset++;
}
}
/*
* NONPAYLOAD FIELDS
*
*/
/* All of the beacon fields, except the beacon payload are considered nonpayload. */
if ((packet->version == IEEE802154_VERSION_2003) || (packet->version == IEEE802154_VERSION_2006)) {
if (packet->frame_type == IEEE802154_FCF_BEACON) { /* Regular Beacon. These are not present in Enhanced frame version 2 Beacons */
dissect_ieee802154_superframe(tvb, pinfo, ieee802154_tree, &offset); /* superframe spec */
dissect_ieee802154_gtsinfo(tvb, pinfo, ieee802154_tree, &offset); /* GTS information fields */
dissect_ieee802154_pendaddr(tvb, pinfo, ieee802154_tree, &offset); /* Pending address list */
}
if (packet->frame_type == IEEE802154_FCF_CMD) {
/**
* In IEEE802.15.4-2003 and 2006 the command identifier is considered to be part of the header
* and is thus not encrypted. For IEEE802.15.4-2012e and later the command id is considered to be
* part of the payload, is encrypted, and follows the payload IEs. Thus we only parse the command id
* here for 2006 and earlier frames. */
packet->command_id = tvb_get_guint8(tvb, offset);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_cmd_id, tvb, offset, 1, packet->command_id);
}
offset++;
/* Display the command identifier in the info column. */
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
}
}
else {
if (packet->ie_present) {
dissect_ieee802154_header_ie(tvb, pinfo, ieee802154_tree, &offset, packet);
}
}
/* IEEE 802.15.4-2003 may have security information pre-pended to payload */
if (packet->security_enable && (packet->version == IEEE802154_VERSION_2003)) {
/* Store security suite preference in the 2006 security level identifier to simplify 2003 integration! */
packet->security_level = (ieee802154_security_level)ieee802154_sec_suite;
/* Frame Counter and Key Sequence Counter prepended to the payload of an encrypted frame */
if (IEEE802154_IS_ENCRYPTED(packet->security_level)) {
packet->frame_counter = tvb_get_letohl (tvb, offset);
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_sec_frame_counter, tvb, offset, (int)sizeof(guint32), packet->frame_counter);
offset += (int)sizeof(guint32);
packet->key_sequence_counter = tvb_get_guint8 (tvb, offset);
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_sec_key_sequence_counter, tvb, offset, (int)sizeof(guint8), packet->key_sequence_counter);
offset += (int)sizeof(guint8);
}
}
/* Encrypted Payload. */
if (packet->security_enable) {
payload_tvb = dissect_ieee802154_decrypt(tvb, offset, pinfo, packet, &status);
/* Get the unencrypted data if decryption failed. */
if (!payload_tvb) {
/* Deal with possible truncation and the FCS field at the end. */
gint reported_len = tvb_reported_length(tvb)-offset-IEEE802154_FCS_LEN;
gint captured_len = tvb_captured_length(tvb)-offset;
if (reported_len < captured_len) captured_len = reported_len;
payload_tvb = tvb_new_subset(tvb, offset, captured_len, reported_len);
}
/* Display the reason for failure, and abort if the error was fatal. */
switch (status) {
case DECRYPT_PACKET_SUCCEEDED:
case DECRYPT_NOT_ENCRYPTED:
/* No problem. */
break;
case DECRYPT_VERSION_UNSUPPORTED:
/* We don't support decryption with that version of the protocol */
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "We don't support decryption with protocol version %u", packet->version);
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
case DECRYPT_PACKET_TOO_SMALL:
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "Packet was too small to include the CRC and MIC");
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
case DECRYPT_PACKET_NO_EXT_SRC_ADDR:
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "No extended source address - can't decrypt");
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
case DECRYPT_PACKET_NO_KEY:
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "No encryption key set - can't decrypt");
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
case DECRYPT_PACKET_DECRYPT_FAILED:
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "Decrypt failed");
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
case DECRYPT_PACKET_MIC_CHECK_FAILED:
expert_add_info_format(pinfo, proto_root, &ei_ieee802154_decrypt_error, "MIC check failed");
/*
* Abort only if the payload was encrypted, in which case we
* probably didn't decrypt the packet right (eg: wrong key).
*/
if (IEEE802154_IS_ENCRYPTED(packet->security_level)) {
call_dissector(data_handle, payload_tvb, pinfo, tree);
goto dissect_ieee802154_fcs;
}
break;
}
}
/* Plaintext Payload. */
else {
/* Deal with possible truncation and the FCS field at the end. */
gint reported_len = tvb_reported_length(tvb)-offset-IEEE802154_FCS_LEN;
gint captured_len = tvb_captured_length(tvb)-offset;
if (reported_len < captured_len) captured_len = reported_len;
payload_tvb = tvb_new_subset(tvb, offset, captured_len, reported_len);
}
/* presense of Payload IEs is defined by the termination of the Header IEs */
if (packet->payload_ie_present) {
dissect_ieee802154_payload_ie(tvb, pinfo, ieee802154_tree, &offset);
}
if ((packet->version == IEEE802154_VERSION_2012e) && (packet->frame_type == IEEE802154_FCF_CMD)) {
/* In 802.15.4e and later the Command Id follows the Payload IEs. */
packet->command_id = tvb_get_guint8(tvb, offset);
if (tree) {
proto_tree_add_uint(ieee802154_tree, hf_ieee802154_cmd_id, tvb, offset, 1, packet->command_id);
}
offset++;
/* Display the command identifier in the info column. */
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
}
if (tvb_captured_length_remaining(tvb, offset) > IEEE802154_FCS_LEN) {
/*
* Wrap the sub-dissection in a try/catch block in case the payload is
* broken. First we store the current protocol so we can fix it if an
* exception is thrown by the subdissectors.
*/
saved_proto = pinfo->current_proto;
/* Try to dissect the payload. */
TRY {
switch (packet->frame_type) {
case IEEE802154_FCF_BEACON:
if (!dissector_try_heuristic(ieee802154_beacon_subdissector_list, payload_tvb, pinfo, tree, &hdtbl_entry, packet)) {
/* Could not subdissect, call the data dissector instead. */
call_dissector(data_handle, payload_tvb, pinfo, tree);
}
break;
case IEEE802154_FCF_CMD:
dissect_ieee802154_command(payload_tvb, pinfo, ieee802154_tree, packet);
break;
case IEEE802154_FCF_DATA:
/* Sanity-check. */
if ((!fcs_ok && ieee802154_fcs_ok) || !tvb_reported_length(payload_tvb)) {
call_dissector(data_handle, payload_tvb, pinfo, tree);
break;
}
/* Try the PANID dissector table for stateful dissection. */
if (dissector_try_uint_new(panid_dissector_table, packet->src_pan, payload_tvb, pinfo, tree, TRUE, packet)) {
break;
}
/* Try again with the destination PANID (if different) */
if (((packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) ||
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT)) &&
(packet->dst_pan != packet->src_pan) &&
dissector_try_uint_new(panid_dissector_table, packet->src_pan, payload_tvb, pinfo, tree, TRUE, packet)) {
break;
}
/* Try heuristic dissection. */
if (dissector_try_heuristic(ieee802154_heur_subdissector_list, payload_tvb, pinfo, tree, &hdtbl_entry, packet)) break;
/* Fall-through to dump undissectable payloads. */
default:
/* Could not subdissect, call the data dissector instead. */
call_dissector(data_handle, payload_tvb, pinfo, tree);
} /* switch */
}
CATCH_ALL {
/*
* Someone encountered an error while dissecting the payload. But
* we haven't yet finished processing all of our layer. Catch and
* display the exception, then fall-through to finish displaying
* the FCS (which we display last so the frame is ordered correctly
* in the tree).
*/
show_exception(payload_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
pinfo->current_proto = saved_proto;
}
ENDTRY;
}
/*
* Frame Check Sequence (FCS)
*
*/
dissect_ieee802154_fcs:
/* The FCS should be the last bytes of the reported packet. */
offset = tvb_reported_length(tvb)-IEEE802154_FCS_LEN;
/* Dissect the FCS only if it exists (captures which don't or can't get the
* FCS will simply truncate the packet to omit it, but should still set the
* reported length to cover the original packet length), so if the snapshot
* is too short for an FCS don't make a fuss.
*/
if (tvb_bytes_exist(tvb, offset, IEEE802154_FCS_LEN) && (tree)) {
proto_tree *field_tree;
guint16 fcs = tvb_get_letohs(tvb, offset);
/* Display the FCS depending on expected FCS format */
if ((options & DISSECT_IEEE802154_OPTION_CC24xx)) {
/* Create a subtree for the FCS. */
field_tree = proto_tree_add_subtree_format(ieee802154_tree, tvb, offset, 2, ett_ieee802154_fcs, NULL,
"Frame Check Sequence (TI CC24xx format): FCS %s", (fcs_ok) ? "OK" : "Bad");
/* Display FCS contents. */
ti = proto_tree_add_int(field_tree, hf_ieee802154_rssi, tvb, offset++, 1, (gint8) (fcs & IEEE802154_CC24xx_RSSI));
proto_item_append_text(ti, " dB"); /* Displaying Units */
proto_tree_add_boolean(field_tree, hf_ieee802154_fcs_ok, tvb, offset, 1, (gboolean) (fcs & IEEE802154_CC24xx_CRC_OK));
proto_tree_add_uint(field_tree, hf_ieee802154_correlation, tvb, offset, 1, (guint8) ((fcs & IEEE802154_CC24xx_CORRELATION) >> 8));
}
else {
ti = proto_tree_add_uint(ieee802154_tree, hf_ieee802154_fcs, tvb, offset, 2, fcs);
if (fcs_ok) {
proto_item_append_text(ti, " (Correct)");
}
else {
proto_item_append_text(ti, " (Incorrect, expected FCS=0x%04x", ieee802154_crc_tvb(tvb, offset));
}
/* To Help with filtering, add the fcs_ok field to the tree. */
ti = proto_tree_add_boolean(ieee802154_tree, hf_ieee802154_fcs_ok, tvb, offset, 2, fcs_ok);
PROTO_ITEM_SET_HIDDEN(ti);
}
}
else if (tree) {
/* Even if the FCS isn't present, add the fcs_ok field to the tree to
* help with filter. Be sure not to make it visible though.
*/
ti = proto_tree_add_boolean_format_value(ieee802154_tree, hf_ieee802154_fcs_ok, tvb, offset, 2, fcs_ok, "Unknown");
PROTO_ITEM_SET_HIDDEN(ti);
}
/* If the CRC is invalid, make a note of it in the info column. */
if (!fcs_ok) {
col_append_str(pinfo->cinfo, COL_INFO, ", Bad FCS");
if (tree) proto_item_append_text(proto_root, ", Bad FCS");
/* Flag packet as having a bad crc. */
expert_add_info(pinfo, proto_root, &ei_ieee802154_fcs);
}
} /* dissect_ieee802154_common */
/**
*Subdissector command for the Superframe specification sub-field within the beacon frame.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to command subtree.
*@param offset offset into the tvbuff to begin dissection.
*/
static void
dissect_ieee802154_superframe(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint *offset)
{
static const int * superframe[] = {
&hf_ieee802154_beacon_order,
&hf_ieee802154_superframe_order,
&hf_ieee802154_cap,
&hf_ieee802154_superframe_battery_ext,
&hf_ieee802154_superframe_coord,
&hf_ieee802154_assoc_permit,
NULL
};
proto_tree_add_bitmask_text(tree, tvb, *offset, 2, "Superframe Specification", NULL , ett_ieee802154_superframe, superframe, ENC_LITTLE_ENDIAN, BMT_NO_INT|BMT_NO_TFS);
(*offset) += 2;
} /* dissect_ieee802154_superframe */
/**
*Subdissector command for the GTS information fields within the beacon frame.
*
*@param tvb - pointer to buffer containing raw packet.
*@param pinfo - pointer to packet information fields (unused).
*@param tree - pointer to command subtree.
*@param offset - offset into the tvbuff to begin dissection.
*/
static void
dissect_ieee802154_gtsinfo(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint *offset)
{
proto_tree *field_tree = NULL;
proto_tree *subtree = NULL;
proto_item *ti;
guint8 gts_spec;
guint8 gts_count;
/* Get and display the GTS specification field */
gts_spec = tvb_get_guint8(tvb, *offset);
gts_count = gts_spec & IEEE802154_GTS_COUNT_MASK;
if (tree) {
/* Add Subtree for GTS information. */
if (gts_count) {
field_tree = proto_tree_add_subtree(tree, tvb, *offset, 2 + (gts_count * 3), ett_ieee802154_gts, NULL, "GTS");
}
else {
field_tree = proto_tree_add_subtree(tree, tvb, *offset, 1, ett_ieee802154_gts, NULL, "GTS");
}
proto_tree_add_uint(field_tree, hf_ieee802154_gts_count, tvb, *offset, 1, gts_count);
proto_tree_add_boolean(field_tree, hf_ieee802154_gts_permit, tvb, *offset, 1, gts_spec & IEEE802154_GTS_PERMIT_MASK);
}
(*offset) += 1;
/* If the GTS descriptor count is nonzero, then the GTS directions mask and descriptor list are present. */
if (gts_count) {
guint8 gts_directions = tvb_get_guint8(tvb, *offset);
guint gts_rx = 0;
int i;
/* Display the directions mask. */
if (tree) {
proto_tree *dir_tree;
/* Create a subtree. */
dir_tree = proto_tree_add_subtree(field_tree, tvb, *offset, 1, ett_ieee802154_gts_direction, &ti, "GTS Directions");
/* Add the directions to the subtree. */
for (i=0; i<gts_count; i++) {
gboolean dir = gts_directions & IEEE802154_GTS_DIRECTION_SLOT(i);
proto_tree_add_boolean_format(dir_tree, hf_ieee802154_gts_direction, tvb, *offset, 1, dir, "GTS Slot %i: %s", i+1, dir?"Receive Only":"Transmit Only");
if (dir) gts_rx++;
} /* for */
proto_item_append_text(ti, ": %i Receive & %i Transmit", gts_rx, gts_count - gts_rx);
}
(*offset) += 1;
/* Create a subtree for the GTS descriptors. */
subtree = proto_tree_add_subtree(field_tree, tvb, *offset, gts_count * 3, ett_ieee802154_gts_descriptors, NULL, "GTS Descriptors");
/* Get and display the GTS descriptors. */
for (i=0; i<gts_count; i++) {
guint16 gts_addr = tvb_get_letohs(tvb, (*offset));
guint8 gts_slot = tvb_get_guint8(tvb, (*offset)+2);
guint8 gts_length = (gts_slot & IEEE802154_GTS_LENGTH_MASK) >> IEEE802154_GTS_LENGTH_SHIFT;
gts_slot = (gts_slot & IEEE802154_GTS_SLOT_MASK);
if (tree) {
/* Add address, slot, and time length fields. */
ti = proto_tree_add_uint(subtree, hf_ieee802154_gts_address, tvb, (*offset), 3, gts_addr);
proto_item_append_text(ti, ", Slot: %i", gts_slot);
proto_item_append_text(ti, ", Length: %i", gts_length);
}
(*offset) += 3;
} /* for */
}
} /* dissect_ieee802154_gtsinfo */
/**
*Subdissector command for the pending address list fields within the beacon frame.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to command subtree.
*@offset offset into the tvbuff to begin dissection.
*/
static void
dissect_ieee802154_pendaddr(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint *offset)
{
proto_tree *subtree;
guint8 pend_spec;
guint8 pend_num16;
guint8 pend_num64;
int i;
/* Get the Pending Addresses specification fields */
pend_spec = tvb_get_guint8(tvb, *offset);
pend_num16 = pend_spec & IEEE802154_PENDADDR_SHORT_MASK;
pend_num64 = (pend_spec & IEEE802154_PENDADDR_LONG_MASK) >> IEEE802154_PENDADDR_LONG_SHIFT;
/* Add Subtree for the addresses */
subtree = proto_tree_add_subtree_format(tree, tvb, *offset, 1 + 2*pend_num16 + 8*pend_num64,
ett_ieee802154_pendaddr, NULL, "Pending Addresses: %i Short and %i Long", pend_num16, pend_num64);
(*offset) += 1;
for (i=0; i<pend_num16; i++) {
guint16 addr = tvb_get_letohs(tvb, *offset);
proto_tree_add_uint(subtree, hf_ieee802154_pending16, tvb, *offset, 2, addr);
(*offset) += 2;
} /* for */
for (i=0; i<pend_num64; i++) {
proto_tree_add_item(subtree, hf_ieee802154_pending64, tvb, *offset, 8, ENC_LITTLE_ENDIAN);
(*offset) += 8;
} /* for */
} /* dissect_ieee802154_pendaddr */
/**
*Subdissector for Header IEs (Information Elements)
*Since the header is never encrypted and the payload may be encrypted,
*we dissect header and payload IEs separately.
*The termination of the Header IE tells us whether there are any
*payload IEs to follow, so it is always set by the termination.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to command subtree.
*@param offset offset into the tvbuff to begin dissection.
*@param packet IEEE 802.15.4 packet information.
*/
static void
dissect_ieee802154_header_ie(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint *offset, ieee802154_packet *packet)
{
proto_tree *subtree;
guint16 header_ie;
guint16 id;
guint16 length;
GByteArray *gba = g_byte_array_new();
static const int * fields[] = {
&hf_ieee802154_header_ie_type,
&hf_ieee802154_header_ie_id,
&hf_ieee802154_header_ie_length,
NULL
};
do {
header_ie = tvb_get_letohs(tvb, *offset);
id = (header_ie & IEEE802154_HEADER_IE_ID_MASK) >> 7;
length = header_ie & IEEE802154_HEADER_IE_LENGTH_MASK;
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, *offset, 1, ett_ieee802154_header, NULL, "Header IE");
proto_item_append_text(subtree, ", Element ID: %s, Length: %d", val_to_str_const(id, ieee802154_header_ie_names, "Unknown IE"), length);
proto_tree_add_bitmask(subtree, tvb, *offset, hf_ieee802154_header_ie,
ett_ieee802154_header_ie, fields, ENC_LITTLE_ENDIAN);
*offset += 2;
/* until the Header IEs are finalized, just use the data dissector */
if (length > 0) {
proto_tree_add_bytes_item(subtree, hf_ieee802154_header_ie_data, tvb, *offset, length, ENC_NA, gba, NULL, NULL);
*offset += length;
}
} while ((tvb_reported_length(tvb) > 1) &&
(id != IEEE802154_HEADER_IE_EID_TERM1) &&
(id != IEEE802154_HEADER_IE_EID_TERM2));
/* Presense of Payload IEs is determined by how the Header IEs are terminated */
if ((tvb_reported_length(tvb) > 1) && (id == IEEE802154_HEADER_IE_EID_TERM1)) {
packet->payload_ie_present = TRUE;
}
else {
packet->payload_ie_present = FALSE;
}
} /* dissect_ieee802154_header_ie */
/**
*Subdissector command for Header IEs (Information Elements)
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to command subtree.
*@param offset offset into the tvbuff to begin dissection.
*/
static void
dissect_ieee802154_payload_ie(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint *offset)
{
proto_tree *subtree;
guint16 payload_ie;
guint16 id;
guint16 length;
GByteArray *gba = g_byte_array_new();
static const int * fields[] = {
&hf_ieee802154_payload_ie_type,
&hf_ieee802154_payload_ie_id,
&hf_ieee802154_payload_ie_length,
NULL
};
do {
payload_ie = tvb_get_letohs(tvb, *offset);
id = (payload_ie & IEEE802154_PAYLOAD_IE_ID_MASK) >> 11;
length = payload_ie & IEEE802154_PAYLOAD_IE_LENGTH_MASK;
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, *offset, 1, ett_ieee802154_payload, NULL, "Payload IE");
proto_item_append_text(subtree, ", Group ID: %s, Length: %d", val_to_str_const(id, ieee802154_payload_ie_names, "Unknown IE"), length);
proto_tree_add_bitmask(subtree, tvb, *offset, hf_ieee802154_payload_ie,
ett_ieee802154_payload_ie, fields, ENC_LITTLE_ENDIAN);
*offset += 2;
/* until the Header IEs are finalized, just use the data dissector */
if (length > 0) {
proto_tree_add_bytes_item(subtree, hf_ieee802154_payload_ie_data, tvb, *offset, length, ENC_NA, gba, NULL, NULL);
*offset += length;
}
} while ((tvb_reported_length(tvb) > 1) && (id != IEEE802154_PAYLOAD_IE_GID_TERM));
}
static const true_false_string tfs_cinfo_device_type = { "FFD", "RFD" };
static const true_false_string tfs_cinfo_power_src = { "AC/Mains Power", "Battery" };
/**
*Command subdissector routine for the Association request command.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields.
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information.
*/
static void
dissect_ieee802154_assoc_req(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
proto_tree *subtree;
static const int * capability[] = {
&hf_ieee802154_cinfo_alt_coord,
&hf_ieee802154_cinfo_device_type,
&hf_ieee802154_cinfo_power_src,
&hf_ieee802154_cinfo_idle_rx,
&hf_ieee802154_cinfo_sec_capable,
&hf_ieee802154_cinfo_alloc_addr,
NULL
};
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, 0, 1, ett_ieee802154_cmd, NULL,
val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
/* Get and display capability info. */
proto_tree_add_bitmask_list(subtree, tvb, 0, 1, capability, ENC_NA);
/* Call the data dissector for any leftover bytes. */
if (tvb_reported_length(tvb) > 1) {
call_dissector(data_handle, tvb_new_subset_remaining(tvb, 1), pinfo, tree);
}
} /* dissect_ieee802154_assoc_req */
/**
*Command subdissector routine for the Association response command.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields.
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information.
*/
static void
dissect_ieee802154_assoc_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
proto_tree *subtree;
proto_item *ti;
guint16 short_addr;
guint8 status;
guint offset = 0;
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, offset, 3, ett_ieee802154_cmd, NULL,
val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
/* Get and display the short address. */
short_addr = tvb_get_letohs(tvb, offset);
proto_tree_add_uint(subtree, hf_ieee802154_assoc_addr, tvb, offset, 2, short_addr);
offset += 2;
/* Get and display the status. */
status = tvb_get_guint8(tvb, offset);
if (tree) {
ti = proto_tree_add_uint(subtree, hf_ieee802154_assoc_status, tvb, offset, 1, status);
if (status == IEEE802154_CMD_ASRSP_AS_SUCCESS) proto_item_append_text(ti, " (Association Successful)");
else if (status == IEEE802154_CMD_ASRSP_PAN_FULL) proto_item_append_text(ti, " (PAN Full)");
else if (status == IEEE802154_CMD_ASRSP_PAN_DENIED) proto_item_append_text(ti, " (Association Denied)");
else proto_item_append_text(ti, " (Reserved)");
}
offset += 1;
/* Update the info column. */
if (status == IEEE802154_CMD_ASRSP_AS_SUCCESS) {
/* Association was successful. */
if (packet->src_addr_mode != IEEE802154_FCF_ADDR_SHORT) {
col_append_fstr(pinfo->cinfo, COL_INFO, ", PAN: 0x%04x", packet->dst_pan);
}
if (short_addr != IEEE802154_NO_ADDR16) {
col_append_fstr(pinfo->cinfo, COL_INFO, " Addr: 0x%04x", short_addr);
}
}
else {
/* Association was unsuccessful. */
col_append_str(pinfo->cinfo, COL_INFO, ", Unsuccessful");
}
/* Update the address table. */
if ((status == IEEE802154_CMD_ASRSP_AS_SUCCESS) && (short_addr != IEEE802154_NO_ADDR16)) {
ieee802154_addr_update(&ieee802154_map, short_addr, packet->dst_pan, packet->dst64,
pinfo->current_proto, pinfo->fd->num);
}
/* Call the data dissector for any leftover bytes. */
if (tvb_captured_length(tvb) > offset) {
call_dissector(data_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
}
} /* dissect_ieee802154_assoc_rsp */
/**
* Command subdissector routine for the Disassociate command.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields.
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information.
*/
static void
dissect_ieee802154_disassoc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
proto_tree *subtree;
proto_item *ti;
guint8 reason;
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, 0, 1, ett_ieee802154_cmd, NULL,
val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
/* Get and display the disassociation reason. */
reason = tvb_get_guint8(tvb, 0);
if (tree) {
ti = proto_tree_add_uint(subtree, hf_ieee802154_disassoc_reason, tvb, 0, 1, reason);
switch(reason) {
case 0x01:
proto_item_append_text(ti, " (Coordinator requests device to leave)");
break;
case 0x02:
proto_item_append_text(ti, " (Device wishes to leave)");
break;
default:
proto_item_append_text(ti, " (Reserved)");
break;
} /* switch */
}
if (!pinfo->fd->flags.visited) {
/* Update the address tables */
if ( packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT ) {
ieee802154_long_addr_invalidate(packet->dst64, pinfo->fd->num);
} else if ( packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT ) {
ieee802154_short_addr_invalidate(packet->dst16, packet->dst_pan, pinfo->fd->num);
}
}
/* Call the data dissector for any leftover bytes. */
if (tvb_captured_length(tvb) > 1) {
call_dissector(data_handle, tvb_new_subset_remaining(tvb, 1), pinfo, tree);
}
} /* dissect_ieee802154_disassoc */
/**
* Command subdissector routine for the Coordinator Realignment command.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields.
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information.
*/
static void
dissect_ieee802154_realign(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
proto_tree *subtree;
proto_item *subitem;
guint16 pan_id;
guint16 coord_addr;
guint8 channel;
guint16 short_addr;
guint offset = 0;
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_ieee802154_cmd, &subitem,
val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
/* Get and display the command PAN ID. */
pan_id = tvb_get_letohs(tvb, offset);
proto_tree_add_uint(subtree, hf_ieee802154_realign_pan, tvb, offset, 2, pan_id);
col_append_fstr(pinfo->cinfo, COL_INFO, ", PAN: 0x%04x", pan_id);
offset += 2;
/* Get and display the coordinator address. */
coord_addr = tvb_get_letohs(tvb, offset);
proto_tree_add_uint(subtree, hf_ieee802154_realign_caddr, tvb, offset, 2, coord_addr);
col_append_fstr(pinfo->cinfo, COL_INFO, ", Coordinator: 0x%04x", coord_addr);
offset += 2;
/* Get and display the channel. */
channel = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(subtree, hf_ieee802154_realign_channel, tvb, offset, 1, channel);
col_append_fstr(pinfo->cinfo, COL_INFO, ", Channel: %u", channel);
offset += 1;
/* Get and display the short address. */
short_addr = tvb_get_letohs(tvb, offset);
if (tree) proto_tree_add_uint(subtree, hf_ieee802154_realign_addr, tvb, offset, 2, short_addr);
if ((packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT)
&& (short_addr != IEEE802154_NO_ADDR16)) {
col_append_fstr(pinfo->cinfo, COL_INFO, ", Addr: 0x%04x", short_addr);
}
offset += 2;
/* Update the address table. */
if ((short_addr != IEEE802154_NO_ADDR16) && (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT)) {
ieee802154_addr_update(&ieee802154_map, short_addr, packet->dst_pan, packet->dst64,
pinfo->current_proto, pinfo->fd->num);
}
/* Get and display the channel page, if it exists. Added in IEEE802.15.4-2006 */
if (tvb_bytes_exist(tvb, offset, 1)) {
guint8 channel_page = tvb_get_guint8(tvb, offset);
if (tree) proto_tree_add_uint(subtree, hf_ieee802154_realign_channel_page, tvb, offset, 1, channel_page);
offset += 1;
}
/* Fix the length of the command subtree. */
if (tree) {
proto_item_set_len(subitem, offset);
}
/* Call the data dissector for any leftover bytes. */
if (tvb_captured_length(tvb) > offset) {
call_dissector(data_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
}
} /* dissect_ieee802154_realign */
static const true_false_string tfs_gtsreq_dir = { "Receive", "Transmit" };
static const true_false_string tfs_gtsreq_type= { "Allocate GTS", "Deallocate GTS" };
/**
*Command subdissector routine for the GTS request command.
*
*Assumes that COL_INFO will be set to the command name,
*command name will already be appended to the command subtree
*and protocol root. In addition, assumes that the command ID
*has already been parsed.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information (unused).
*/
static void
dissect_ieee802154_gtsreq(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
proto_tree *subtree;
static const int * characteristics[] = {
&hf_ieee802154_gtsreq_len,
&hf_ieee802154_gtsreq_dir,
&hf_ieee802154_gtsreq_type,
NULL
};
/* Create a subtree for this command frame. */
subtree = proto_tree_add_subtree(tree, tvb, 0, 1, ett_ieee802154_cmd, NULL,
val_to_str_const(packet->command_id, ieee802154_cmd_names, "Unknown Command"));
proto_tree_add_bitmask_list(subtree, tvb, 0, 1, characteristics, ENC_NA);
/* Call the data dissector for any leftover bytes. */
if (tvb_reported_length(tvb) > 1) {
call_dissector(data_handle, tvb_new_subset_remaining(tvb, 1), pinfo, tree);
}
} /* dissect_ieee802154_gtsreq */
/**
* Subdissector routine all commands.
*
*@param tvb pointer to buffer containing raw packet.
*@param pinfo pointer to packet information fields (unused).
*@param tree pointer to protocol tree.
*@param packet IEEE 802.15.4 packet information (unused).
*/
static void
dissect_ieee802154_command(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, ieee802154_packet *packet)
{
switch (packet->command_id) {
case IEEE802154_CMD_ASSOC_REQ:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE));
dissect_ieee802154_assoc_req(tvb, pinfo, tree, packet);
break;
case IEEE802154_CMD_ASSOC_RSP:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));
dissect_ieee802154_assoc_rsp(tvb, pinfo, tree, packet);
break;
case IEEE802154_CMD_DISASSOC_NOTIFY:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));
dissect_ieee802154_disassoc(tvb, pinfo, tree, packet);
return;
case IEEE802154_CMD_DATA_RQ:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id, packet->src_addr_mode != IEEE802154_FCF_ADDR_NONE);
/* No payload expected. */
break;
case IEEE802154_CMD_PANID_CONFLICT:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT));
/* No payload expected. */
break;
case IEEE802154_CMD_ORPHAN_NOTIFY:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) &&
(packet->dst16 == IEEE802154_BCAST_ADDR) &&
(packet->src_pan == IEEE802154_BCAST_PAN) &&
(packet->dst_pan == IEEE802154_BCAST_PAN));
/* No payload expected. */
break;
case IEEE802154_CMD_BEACON_REQ:
if ((packet->version == IEEE802154_VERSION_2003) || (packet->version == IEEE802154_VERSION_2006)) {
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) &&
(packet->src_addr_mode == IEEE802154_FCF_ADDR_NONE) &&
(packet->dst16 == IEEE802154_BCAST_ADDR) &&
(packet->dst_pan == IEEE802154_BCAST_PAN));
}
/* No payload expected. */
break;
case IEEE802154_CMD_COORD_REALIGN:
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) &&
(packet->dst_pan == IEEE802154_BCAST_PAN) &&
(packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE));
if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_SHORT) {
/* If directed to a 16-bit address, check that it is being broadcast. */
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id, packet->dst16 == IEEE802154_BCAST_ADDR);
}
dissect_ieee802154_realign(tvb, pinfo, tree, packet);
return;
case IEEE802154_CMD_GTS_REQ:
/* Check that the addressing is correct for this command type. */
IEEE802154_CMD_ADDR_CHECK(pinfo, tree, packet->command_id,
(packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) &&
(packet->dst_addr_mode == IEEE802154_FCF_ADDR_NONE) &&
(packet->src16 != IEEE802154_BCAST_ADDR) &&
(packet->src16 != IEEE802154_NO_ADDR16));
dissect_ieee802154_gtsreq(tvb, pinfo, tree, packet);
return;
case IEEE802154_CMD_TRLE_MGMT_REQ:
case IEEE802154_CMD_TRLE_MGMT_RSP:
case IEEE802154_CMD_DSME_ASSOC_REQ:
case IEEE802154_CMD_DSME_ASSOC_RSP:
case IEEE802154_CMD_DSME_GTS_REQ:
case IEEE802154_CMD_DSME_GTS_RSP:
case IEEE802154_CMD_DSME_GTS_NOTIFY:
case IEEE802154_CMD_DSME_INFO_REQ:
case IEEE802154_CMD_DSME_INFO_RSP:
case IEEE802154_CMD_DSME_BEACON_ALLOC_NOTIFY:
case IEEE802154_CMD_DSME_BEACON_COLL_NOTIFY:
case IEEE802154_CMD_DSME_LINK_REPORT:
case IEEE802154_CMD_RIT_DATA_REQ:
case IEEE802154_CMD_DBS_REQ:
case IEEE802154_CMD_DBS_RSP:
/* TODO add support for these commands, for now
* if anything remains other than the FCS, dump it */
if (tvb_captured_length_remaining(tvb, 0) > 2) {
call_dissector(data_handle, tvb, pinfo, tree);
}
return;
} /* switch */
} /* dissect_ieee802154_command */
/**
*IEEE 802.15.4 decryption algorithm. Tries to find the
*appropriate key from the information in the IEEE 802.15.4
*packet structure and dissector config.
*
*This function implements the security proceedures for the
*2006 version of the spec only. IEEE 802.15.4-2003 is
*unsupported.
*@param tvb IEEE 802.15.4 packet.
*@param pinfo Packet info structure.
*@param offset Offset where the ciphertext 'c' starts.
*@param packet IEEE 802.15.4 packet information.
*@param status status of decryption returned through here on failure.
*@return decrypted payload.
*/
static tvbuff_t *
dissect_ieee802154_decrypt(tvbuff_t *tvb, guint offset, packet_info *pinfo, ieee802154_packet *packet, ws_decrypt_status *status)
{
tvbuff_t *ptext_tvb;
gboolean have_mic = FALSE;
guint64 srcAddr;
unsigned char key[16];
unsigned char tmp[16];
unsigned char rx_mic[16];
guint M;
gint captured_len;
gint reported_len;
ieee802154_hints_t *ieee_hints;
/*
* Check the version; we only support IEEE 802.15.4-2003 and IEEE 802.15.4-2006.
* We must do this first, as, if this isn't IEEE 802.15.4-2003 or IEEE 802.15.4-2006,
* we don't have the Auxiliary Security Header, and haven't
* filled in the information for it, and none of the stuff
* we do afterwards, which uses that information, is doable.
*/
if ((packet->version != IEEE802154_VERSION_2006) && (packet->version != IEEE802154_VERSION_2003)) {
*status = DECRYPT_VERSION_UNSUPPORTED;
return NULL;
}
ieee_hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ieee802154, 0);
/* Get the captured and on-the-wire length of the payload. */
M = IEEE802154_MIC_LENGTH(packet->security_level);
reported_len = tvb_reported_length_remaining(tvb, offset) - IEEE802154_FCS_LEN - M;
if (reported_len < 0) {
*status = DECRYPT_PACKET_TOO_SMALL;
return NULL;
}
/* Check of the payload is truncated. */
if (tvb_bytes_exist(tvb, offset, reported_len)) {
captured_len = reported_len;
}
else {
captured_len = tvb_captured_length_remaining(tvb, offset);
}
/* Check if the MIC is present in the captured data. */
have_mic = tvb_bytes_exist(tvb, offset + reported_len, M);
if (have_mic) {
tvb_memcpy(tvb, rx_mic, offset + reported_len, M);
}
/*
* Key Lookup - Need to find the appropriate key.
*
*/
/*
* Oh God! The specification is so bad. This is the worst
* case of design-by-committee I've ever seen in my life.
* The IEEE has created an unintelligible mess in order
* to decipher which key is used for which message.
*
* Let's hope it's simpler to implement for dissecting only.
*
* Also need to find the extended address of the sender.
*/
if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) {
/* The source EUI-64 is included in the headers. */
srcAddr = packet->src64;
}
else if (ieee_hints && ieee_hints->map_rec && ieee_hints->map_rec->addr64) {
/* Use the hint */
srcAddr = ieee_hints->map_rec->addr64;
}
else {
/* Lookup failed. */
*status = DECRYPT_PACKET_NO_EXT_SRC_ADDR;
return NULL;
}
/* Lookup the key. */
/*
* TODO: What this dissector really needs is a UAT to store multiple keys
* and a variety of key configuration data. However, a single shared key
* should be sufficient to get packet encryption off to a start.
*/
if (!ieee802154_key_valid) {
*status = DECRYPT_PACKET_NO_KEY;
return NULL;
}
memcpy(key, ieee802154_key, IEEE802154_CIPHER_SIZE);
/*
* CCM* - CTR mode payload encryption
*
*/
/* Create the CCM* initial block for decryption (Adata=0, M=0, counter=0). */
ccm_init_block(tmp, FALSE, 0, srcAddr, packet, 0);
/* Decrypt the ciphertext, and place the plaintext in a new tvb. */
if (IEEE802154_IS_ENCRYPTED(packet->security_level) && captured_len) {
guint8 *text;
/*
* Make a copy of the ciphertext in heap memory.
*
* We will decrypt the message in-place and then use the buffer as the
* real data for the new tvb.
*/
text = (guint8 *)tvb_memdup(NULL, tvb, offset, captured_len);
/* Perform CTR-mode transformation. */
if (!ccm_ctr_encrypt(key, tmp, rx_mic, text, captured_len)) {
g_free(text);
*status = DECRYPT_PACKET_DECRYPT_FAILED;
return NULL;
}
/* Create a tvbuff for the plaintext. */
ptext_tvb = tvb_new_child_real_data(tvb, text, captured_len, reported_len);
tvb_set_free_cb(ptext_tvb, g_free);
add_new_data_source(pinfo, ptext_tvb, "Decrypted IEEE 802.15.4 payload");
*status = DECRYPT_PACKET_SUCCEEDED;
}
/* There is no ciphertext. Wrap the plaintext in a new tvb. */
else {
/* Decrypt the MIC (if present). */
if ((have_mic) && (!ccm_ctr_encrypt(key, tmp, rx_mic, NULL, 0))) {
*status = DECRYPT_PACKET_DECRYPT_FAILED;
return NULL;
}
/* Create a tvbuff for the plaintext. This might result in a zero-length tvbuff. */
ptext_tvb = tvb_new_subset(tvb, offset, captured_len, reported_len);
*status = DECRYPT_PACKET_SUCCEEDED;
}
/*
* CCM* - CBC-mode message authentication
*
*/
/* We can only verify the message if the MIC wasn't truncated. */
if (have_mic) {
unsigned char dec_mic[16];
guint l_m = captured_len;
guint l_a = offset;
/* Adjust the lengths of the plaintext and additional data if unencrypted. */
if (!IEEE802154_IS_ENCRYPTED(packet->security_level)) {
l_a += l_m;
l_m = 0;
}
else if ((packet->version == IEEE802154_VERSION_2003) && !ieee802154_extend_auth)
l_a -= 5; /* Exclude Frame Counter (4 bytes) and Key Sequence Counter (1 byte) from authentication data */
/* Create the CCM* initial block for authentication (Adata!=0, M!=0, counter=l(m)). */
ccm_init_block(tmp, TRUE, M, srcAddr, packet, l_m);
/* Compute CBC-MAC authentication tag. */
/*
* And yes, despite the warning in tvbuff.h, I think tvb_get_ptr is the
* right function here since either A) the payload wasn't encrypted, in
* which case l_m is zero, or B) the payload was encrypted, and the tvb
* already points to contiguous memory, since we just allocated it in
* decryption phase.
*/
if (!ccm_cbc_mac(key, tmp, (const gchar *)tvb_memdup(wmem_packet_scope(), tvb, 0, l_a), l_a, tvb_get_ptr(ptext_tvb, 0, l_m), l_m, dec_mic)) {
*status = DECRYPT_PACKET_MIC_CHECK_FAILED;
}
/* Compare the received MIC with the one we generated. */
else if (memcmp(rx_mic, dec_mic, M) != 0) {
*status = DECRYPT_PACKET_MIC_CHECK_FAILED;
}
}
/* Done! */
return ptext_tvb;
} /* dissect_ieee802154_decrypt */
/**
*Creates the CCM* initial block value for IEEE 802.15.4.
*
*@param block Output pointer for the initial block.
*@param adata TRUE if additional auth data is present
*@param M CCM* parameter M.
*@param addr Source extended address.
*@param packet IEEE 802.15.4 packet information.
*@param ctr_val Value in the last L bytes of the block.
*/
static void
ccm_init_block(gchar *block, gboolean adata, gint M, guint64 addr, ieee802154_packet *packet, gint ctr_val)
{
gint i = 0;
/* Flags: Reserved(0) || Adata || (M-2)/2 || (L-1) */
block[i] = (0x2 - 1); /* (L-1) */
if (M > 0) block[i] |= (((M-2)/2) << 3); /* (M-2)/2 */
if (adata) block[i] |= (1 << 6); /* Adata */
i++;
/* 2003 CCM Nonce: Source Address || Frame Counter || Key Sequence Counter */
/* 2006 CCM* Nonce: Source Address || Frame Counter || Security Level */
block[i++] = (guint8)((addr >> 56) & 0xff);
block[i++] = (guint8)((addr >> 48) & 0xff);
block[i++] = (guint8)((addr >> 40) & 0xff);
block[i++] = (guint8)((addr >> 32) & 0xff);
block[i++] = (guint8)((addr >> 24) & 0xff);
block[i++] = (guint8)((addr >> 16) & 0xff);
block[i++] = (guint8)((addr >> 8) & 0xff);
block[i++] = (guint8)((addr >> 0) & 0xff);
block[i++] = (guint8)((packet->frame_counter >> 24) & 0xff);
block[i++] = (guint8)((packet->frame_counter >> 16) & 0xff);
block[i++] = (guint8)((packet->frame_counter >> 8) & 0xff);
block[i++] = (guint8)((packet->frame_counter >> 0) & 0xff);
if (packet->version == IEEE802154_VERSION_2003)
block[i++] = packet->key_sequence_counter;
else
block[i++] = packet->security_level;
/* Plaintext length. */
block[i++] = (guint8)((ctr_val >> 8) & 0xff);
block[i] = (guint8)((ctr_val >> 0) & 0xff);
} /* ccm_init_block */
/*
* Perform an in-place CTR-mode encryption/decryption.
*
*@param key Encryption Key.
*@param iv Counter initial value.
*@param mic MIC to encrypt/decrypt.
*@param data Buffer to encrypt/decrypt.
*@param length Length of the buffer.
*@return TRUE on SUCCESS, FALSE on error.
*/
#ifdef HAVE_LIBGCRYPT
static gboolean
ccm_ctr_encrypt(const gchar *key, const gchar *iv, gchar *mic, gchar *data, gint length)
{
gcry_cipher_hd_t cipher_hd;
/* Open the cipher. */
if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0)) {
return FALSE;
}
/* Set the key and initial value. */
if (gcry_cipher_setkey(cipher_hd, key, 16)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
if (gcry_cipher_setctr(cipher_hd, iv, 16)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Decrypt the MIC. */
if (gcry_cipher_encrypt(cipher_hd, mic, 16, NULL, 0)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Decrypt the payload. */
if (gcry_cipher_encrypt(cipher_hd, data, length, NULL, 0)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Done with the cipher. */
gcry_cipher_close(cipher_hd);
return TRUE;
} /* ccm_ctr_encrypt */
#else
static gboolean
ccm_ctr_encrypt(const gchar *key _U_, const gchar *iv _U_, gchar *mic _U_, gchar *data _U_, gint length _U_)
{
return FALSE;
}
#endif /* HAVE_LIBGCRYPT */
/**
*Generate a CBC-MAC of the decrypted payload and additional authentication headers.
*@param key Encryption Key.
*@param iv Counter initial value.
*@param a Additional auth headers.
*@param a_len Length of the additional headers.
*@param m Plaintext message.
*@param m_len Length of plaintext message.
*@param mic Output for CBC-MAC.
*@return TRUE on SUCCESS, FALSE on error.
*/
#ifdef HAVE_LIBGCRYPT
static gboolean
ccm_cbc_mac(const gchar *key, const gchar *iv, const gchar *a, gint a_len, const gchar *m, gint m_len, gchar *mic)
{
gcry_cipher_hd_t cipher_hd;
guint i = 0;
unsigned char block[16];
/* Open the cipher. */
if (gcry_cipher_open(&cipher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_MAC)) return FALSE;
/* Set the key. */
if (gcry_cipher_setkey(cipher_hd, key, 16)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Process the initial value. */
if (gcry_cipher_encrypt(cipher_hd, mic, 16, iv, 16)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Encode L(a) */
i = 0;
/* XXX: GINT_MAX is not defined so #if ... will always be false */
#if (GINT_MAX >= (1LL << 32))
if (a_len >= (1LL << 32)) {
block[i++] = 0xff;
block[i++] = 0xff;
block[i++] = (a_len >> 56) & 0xff;
block[i++] = (a_len >> 48) & 0xff;
block[i++] = (a_len >> 40) & 0xff;
block[i++] = (a_len >> 32) & 0xff;
block[i++] = (a_len >> 24) & 0xff;
block[i++] = (a_len >> 16) & 0xff;
block[i++] = (a_len >> 8) & 0xff;
block[i++] = (a_len >> 0) & 0xff;
}
else
#endif
if (a_len >= ((1 << 16) - (1 << 8))) {
block[i++] = 0xff;
block[i++] = 0xfe;
block[i++] = (a_len >> 24) & 0xff;
block[i++] = (a_len >> 16) & 0xff;
block[i++] = (a_len >> 8) & 0xff;
block[i++] = (a_len >> 0) & 0xff;
}
else {
block[i++] = (a_len >> 8) & 0xff;
block[i++] = (a_len >> 0) & 0xff;
}
/* Append a to get the first block of input (pad if we encounter the end of a). */
while ((i < sizeof(block)) && (a_len-- > 0)) block[i++] = *a++;
while (i < sizeof(block)) block[i++] = 0;
/* Process the first block of AuthData. */
if (gcry_cipher_encrypt(cipher_hd, mic, 16, block, 16)) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
/* Transform and process the remainder of a. */
while (a_len > 0) {
/* Copy and pad. */
if ((guint)a_len >= sizeof(block)) memcpy(block, a, sizeof(block));
else {memcpy(block, a, a_len); memset(block+a_len, 0, sizeof(block)-a_len);}
/* Adjust pointers. */
a += sizeof(block);
a_len -= (int)sizeof(block);
/* Execute the CBC-MAC algorithm. */
if (gcry_cipher_encrypt(cipher_hd, mic, 16, block, sizeof(block))) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
} /* while */
/* Process the message, m. */
while (m_len > 0) {
/* Copy and pad. */
if ((guint)m_len >= sizeof(block)) memcpy(block, m, sizeof(block));
else {memcpy(block, m, m_len); memset(block+m_len, 0, sizeof(block)-m_len);}
/* Adjust pointers. */
m += sizeof(block);
m_len -= (int)sizeof(block);
/* Execute the CBC-MAC algorithm. */
if (gcry_cipher_encrypt(cipher_hd, mic, 16, block, sizeof(block))) {
gcry_cipher_close(cipher_hd);
return FALSE;
}
}
/* Done with the cipher. */
gcry_cipher_close(cipher_hd);
return TRUE;
} /* ccm_cbc_mac */
#else
static gboolean
ccm_cbc_mac(const gchar *key _U_, const gchar *iv _U_,
const gchar *a _U_, gint a_len _U_, const gchar *m _U_, gint m_len _U_, gchar *mic _U_)
{
return FALSE;
}
#endif /* HAVE_LIBGCRYPT */
/* Key hash function. */
guint ieee802154_short_addr_hash(gconstpointer key)
{
return (((const ieee802154_short_addr *)key)->addr) | (((const ieee802154_short_addr *)key)->pan << 16);
}
/* Key equal function. */
gboolean ieee802154_short_addr_equal(gconstpointer a, gconstpointer b)
{
return (((const ieee802154_short_addr *)a)->pan == ((const ieee802154_short_addr *)b)->pan) &&
(((const ieee802154_short_addr *)a)->addr == ((const ieee802154_short_addr *)b)->addr);
}
/* Key hash function. */
guint ieee802154_long_addr_hash(gconstpointer key)
{
return (guint)(((const ieee802154_long_addr *)key)->addr) & 0xFFFFFFFF;
}
/* Key equal function. */
gboolean ieee802154_long_addr_equal(gconstpointer a, gconstpointer b)
{
return (((const ieee802154_long_addr *)a)->addr == ((const ieee802154_long_addr *)b)->addr);
}
/**
*Creates a record that maps the given short address and pan to a long (extended) address.
*@param short_addr 16-bit short address
*@param pan 16-bit PAN id
*@param long_addr 64-bit long (extended) address
*@param proto pointer to name of current protocol
*@param fnum Frame number this mapping became valid
*@return TRUE Record was updated, FALSE Couldn't find it
*/
ieee802154_map_rec *ieee802154_addr_update(ieee802154_map_tab_t *au_ieee802154_map,
guint16 short_addr, guint16 pan, guint64 long_addr, const char *proto, guint fnum)
{
ieee802154_short_addr addr16;
ieee802154_map_rec *p_map_rec;
gpointer old_key;
/* Look up short address hash */
addr16.pan = pan;
addr16.addr = short_addr;
p_map_rec = (ieee802154_map_rec *)g_hash_table_lookup(au_ieee802154_map->short_table, &addr16);
/* Update mapping record */
if (p_map_rec) {
/* record already exists */
if ( p_map_rec->addr64 == long_addr ) {
/* no change */
return p_map_rec;
}
else {
/* mark current mapping record invalid */
p_map_rec->end_fnum = fnum;
}
}
/* create a new mapping record */
p_map_rec = wmem_new(wmem_file_scope(), ieee802154_map_rec);
p_map_rec->proto = proto;
p_map_rec->start_fnum = fnum;
p_map_rec->end_fnum = 0;
p_map_rec->addr64 = long_addr;
/* link new mapping record to addr hash tables */
if ( g_hash_table_lookup_extended(au_ieee802154_map->short_table, &addr16, &old_key, NULL) ) {
/* update short addr hash table, reusing pointer to old key */
g_hash_table_insert(au_ieee802154_map->short_table, old_key, p_map_rec);
} else {
/* create new hash entry */
g_hash_table_insert(au_ieee802154_map->short_table, wmem_memdup(wmem_file_scope(), &addr16, sizeof(addr16)), p_map_rec);
}
if ( g_hash_table_lookup_extended(au_ieee802154_map->long_table, &long_addr, &old_key, NULL) ) {
/* update long addr hash table, reusing pointer to old key */
g_hash_table_insert(au_ieee802154_map->long_table, old_key, p_map_rec);
} else {
/* create new hash entry */
g_hash_table_insert(au_ieee802154_map->long_table, wmem_memdup(wmem_file_scope(), &long_addr, sizeof(long_addr)), p_map_rec);
}
return p_map_rec;
} /* ieee802154_addr_update */
/**
*Marks a mapping record associated with device with short_addr
*as invalid at a certain frame number, typically when a
*disassociation occurs.
*
*@param short_addr 16-bit short address
*@param pan 16-bit PAN id
*@param fnum Frame number when mapping became invalid
*@return TRUE Record was updated, FALSE Couldn't find it
*/
gboolean ieee802154_short_addr_invalidate(guint16 short_addr, guint16 pan, guint fnum)
{
ieee802154_short_addr addr16;
ieee802154_map_rec *map_rec;
addr16.pan = pan;
addr16.addr = short_addr;
map_rec = (ieee802154_map_rec *)g_hash_table_lookup(ieee802154_map.short_table, &addr16);
if ( map_rec ) {
/* indicates this mapping is invalid at frame fnum */
map_rec->end_fnum = fnum;
return TRUE;
}
return FALSE;
} /* ieee802154_short_addr_invalidate */
/**
* Mark a mapping record associated with device with long_addr
* as invalid at a certain frame number, typically when a
* disassociation occurs.
*
*@param long_addr 16-bit short address
*@param fnum Frame number when mapping became invalid
*@return TRUE If record was updated, FALSE otherwise
*/
gboolean ieee802154_long_addr_invalidate(guint64 long_addr, guint fnum)
{
ieee802154_map_rec *map_rec;
map_rec = (ieee802154_map_rec *)g_hash_table_lookup(ieee802154_map.long_table, &long_addr);
if ( map_rec ) {
/* indicates this mapping is invalid at frame fnum */
map_rec->end_fnum = fnum;
return TRUE;
}
return FALSE;
} /* ieee802154_long_addr_invalidate */
/**
* Init routine for the IEEE 802.15.4 dissector. Creates hash
* tables for mapping between 16-bit to 64-bit addresses and
* populates them with static address pairs from a UAT
* preference table.
*/
static void
proto_init_ieee802154(void)
{
guint i;
ieee802154_map.short_table = g_hash_table_new(ieee802154_short_addr_hash, ieee802154_short_addr_equal);
ieee802154_map.long_table = g_hash_table_new(ieee802154_long_addr_hash, ieee802154_long_addr_equal);
/* Reload the hash table from the static address UAT. */
for (i=0; (i<num_static_addrs) && (static_addrs); i++) {
ieee802154_addr_update(&ieee802154_map,(guint16)static_addrs[i].addr16, (guint16)static_addrs[i].pan,
pntoh64(static_addrs[i].eui64), ieee802154_user, IEEE802154_USER_MAPPING);
} /* for */
} /* proto_init_ieee802154 */
/**
* Cleanup for the IEEE 802.15.4 dissector.
*/
static void
proto_cleanup_ieee802154(void)
{
g_hash_table_destroy(ieee802154_map.short_table);
g_hash_table_destroy(ieee802154_map.long_table);
}
/* Returns the prompt string for the Decode-As dialog. */
static void ieee802154_da_prompt(packet_info *pinfo _U_, gchar* result)
{
ieee802154_hints_t *hints;
hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo,
proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN), 0);
if (hints)
g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "IEEE 802.15.4 PAN 0x%04x as", hints->src_pan);
else
g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "IEEE 802.15.4 PAN Unknown");
} /* iee802154_da_prompt */
/* Returns the value to index the panid decode table with (source PAN)*/
static gpointer ieee802154_da_value(packet_info *pinfo _U_)
{
ieee802154_hints_t *hints;
hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo,
proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN), 0);
if (hints)
return GUINT_TO_POINTER((guint)(hints->src_pan));
else
return NULL;
} /* iee802154_da_value */
/**
* IEEE 802.15.4 protocol registration routine.
*/
void proto_register_ieee802154(void)
{
/* Protocol fields */
static hf_register_info hf_phy[] = {
/* PHY level */
{ &hf_ieee802154_nonask_phy_preamble,
{ "Preamble", "wpan-nonask-phy.preamble", FT_UINT32, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_nonask_phy_sfd,
{ "Start of Frame Delimiter", "wpan-nonask-phy.sfd", FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_nonask_phy_length,
{ "Frame Length", "wpan-nonask-phy.frame_length", FT_UINT8, BASE_HEX, NULL,
IEEE802154_PHY_LENGTH_MASK, NULL, HFILL }},
{ &hf_ieee802154_nonask_phr,
{ "PHR", "wpan-nonask-phy.phr", FT_UINT8, BASE_HEX, NULL,
0x0, NULL, HFILL }},
};
static hf_register_info hf[] = {
{ &hf_ieee802154_frame_length,
{ "Frame Length", "wpan.frame_length", FT_UINT8, BASE_DEC, NULL, 0x0,
"Frame Length as reported from lower layer", HFILL }},
{ &hf_ieee802154_fcf,
{ "Frame Control Field", "wpan.fcf", FT_UINT16, BASE_HEX, NULL,
0x0, NULL, HFILL }},
{ &hf_ieee802154_frame_type,
{ "Frame Type", "wpan.frame_type", FT_UINT16, BASE_HEX, VALS(ieee802154_frame_types),
IEEE802154_FCF_TYPE_MASK, NULL, HFILL }},
{ &hf_ieee802154_security,
{ "Security Enabled", "wpan.security", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_SEC_EN,
"Whether security operations are performed at the MAC layer or not.", HFILL }},
{ &hf_ieee802154_pending,
{ "Frame Pending", "wpan.pending", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_FRAME_PND,
"Indication of additional packets waiting to be transferred from the source device.", HFILL }},
{ &hf_ieee802154_ack_request,
{ "Acknowledge Request", "wpan.ack_request", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_ACK_REQ,
"Whether the sender of this packet requests acknowledgment or not.", HFILL }},
{ &hf_ieee802154_pan_id_compression,
{ "PAN ID Compression", "wpan.pan_id_compression", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_PAN_ID_COMPRESSION,
"Whether this packet contains the PAN ID or not.", HFILL }},
{ &hf_ieee802154_seqno_suppression,
{ "Sequence Number Suppression", "wpan.seqno_suppression", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_SEQNO_SUPPRESSION,
"Whether this packet contains the Sequence Number or not.", HFILL }},
{ &hf_ieee802154_ie_present,
{ "Information Elements Present", "wpan.ie_present", FT_BOOLEAN, 16, NULL, IEEE802154_FCF_IE_PRESENT,
"Whether this packet contains the Information Elements or not.", HFILL }},
{ &hf_ieee802154_seqno,
{ "Sequence Number", "wpan.seq_no", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_dst_addr_mode,
{ "Destination Addressing Mode", "wpan.dst_addr_mode", FT_UINT16, BASE_HEX, VALS(ieee802154_addr_modes),
IEEE802154_FCF_DADDR_MASK, NULL, HFILL }},
{ &hf_ieee802154_src_addr_mode,
{ "Source Addressing Mode", "wpan.src_addr_mode", FT_UINT16, BASE_HEX, VALS(ieee802154_addr_modes),
IEEE802154_FCF_SADDR_MASK, NULL, HFILL }},
{ &hf_ieee802154_version,
{ "Frame Version", "wpan.version", FT_UINT16, BASE_DEC, VALS(ieee802154_frame_versions),
IEEE802154_FCF_VERSION, NULL, HFILL }},
{ &hf_ieee802154_dst_panID,
{ "Destination PAN", "wpan.dst_pan", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_dst16,
{ "Destination", "wpan.dst16", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_dst64,
{ "Destination", "wpan.dst64", FT_EUI64, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_src_panID,
{ "Source PAN", "wpan.src_pan", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_src16,
{ "Source", "wpan.src16", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_src64,
{ "Extended Source", "wpan.src64", FT_EUI64, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_src64_origin,
{ "Origin", "wpan.src64.origin", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_fcs,
{ "FCS", "wpan.fcs", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_rssi,
{ "RSSI", "wpan.rssi", FT_INT8, BASE_DEC, NULL, 0x0,
"Received Signal Strength", HFILL }},
{ &hf_ieee802154_fcs_ok,
{ "FCS Valid", "wpan.fcs_ok", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_correlation,
{ "LQI Correlation Value", "wpan.correlation", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
/* Header IE */
{ &hf_ieee802154_header_ie,
{ "Header IE", "wpan.header_ie", FT_UINT16, BASE_HEX, NULL,
0x0, NULL, HFILL }},
{ &hf_ieee802154_header_ie_type,
{ "Type", "wpan.header_ie.type", FT_UINT16, BASE_DEC, VALS(ieee802154_ie_types),
IEEE802154_HEADER_IE_TYPE_MASK, NULL, HFILL }},
{ &hf_ieee802154_header_ie_id,
{ "Id", "wpan.header_ie.id", FT_UINT16, BASE_HEX, VALS(ieee802154_header_ie_names),
IEEE802154_HEADER_IE_ID_MASK, NULL, HFILL }},
{ &hf_ieee802154_header_ie_length,
{ "Length", "wpan.header_ie.length", FT_UINT16, BASE_DEC, NULL,
IEEE802154_HEADER_IE_LENGTH_MASK, NULL, HFILL }},
{ &hf_ieee802154_header_ie_data,
{ "Data", "wpan.header_ie.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Payload IEs */
{ &hf_ieee802154_payload_ie,
{ "Payload IE", "wpan.payload_ie", FT_UINT16, BASE_HEX, NULL,
0x0, NULL, HFILL }},
{ &hf_ieee802154_payload_ie_type,
{ "Type", "wpan.payload_ie.type", FT_UINT16, BASE_DEC, VALS(ieee802154_ie_types),
IEEE802154_PAYLOAD_IE_TYPE_MASK, NULL, HFILL }},
{ &hf_ieee802154_payload_ie_id,
{ "Id", "wpan.payload_ie.id", FT_UINT16, BASE_HEX, VALS(ieee802154_payload_ie_names),
IEEE802154_PAYLOAD_IE_ID_MASK, NULL, HFILL }},
{ &hf_ieee802154_payload_ie_length,
{ "Length", "wpan.payload_ie.length", FT_UINT16, BASE_DEC, NULL,
IEEE802154_PAYLOAD_IE_LENGTH_MASK, NULL, HFILL }},
{ &hf_ieee802154_payload_ie_data,
{ "Data", "wpan.payload_ie.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/*
* Command Frame Specific Fields
*/
{ &hf_ieee802154_cmd_id,
{ "Command Identifier", "wpan.cmd", FT_UINT8, BASE_HEX, VALS(ieee802154_cmd_names), 0x0,
NULL, HFILL }},
/* Capability Information Fields */
{ &hf_ieee802154_cinfo_alt_coord,
{ "Alternate PAN Coordinator", "wpan.cinfo.alt_coord", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_ALT_PAN_COORD,
"Whether this device can act as a PAN coordinator or not.", HFILL }},
{ &hf_ieee802154_cinfo_device_type,
{ "Device Type", "wpan.cinfo.device_type", FT_BOOLEAN, 8, TFS(&tfs_cinfo_device_type), IEEE802154_CMD_CINFO_DEVICE_TYPE,
"Whether this device is RFD (reduced-function device) or FFD (full-function device).", HFILL }},
{ &hf_ieee802154_cinfo_power_src,
{ "Power Source", "wpan.cinfo.power_src", FT_BOOLEAN, 8, TFS(&tfs_cinfo_power_src), IEEE802154_CMD_CINFO_POWER_SRC,
"Whether this device is operating on AC/mains or battery power.", HFILL }},
{ &hf_ieee802154_cinfo_idle_rx,
{ "Receive On When Idle", "wpan.cinfo.idle_rx", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_IDLE_RX,
"Whether this device can receive packets while idle or not.", HFILL }},
{ &hf_ieee802154_cinfo_sec_capable,
{ "Security Capability", "wpan.cinfo.sec_capable", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_SEC_CAPABLE,
"Whether this device is capable of receiving encrypted packets.", HFILL }},
{ &hf_ieee802154_cinfo_alloc_addr,
{ "Allocate Address", "wpan.cinfo.alloc_addr", FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_ALLOC_ADDR,
"Whether this device wishes to use a 16-bit short address instead of its IEEE 802.15.4 64-bit long address.", HFILL }},
/* Association response fields */
{ &hf_ieee802154_assoc_addr,
{ "Short Address", "wpan.asoc.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
"The short address that the device should assume. An address of 0xfffe indicates that the device should use its IEEE 64-bit long address.", HFILL }},
{ &hf_ieee802154_assoc_status,
{ "Association Status", "wpan.assoc.status", FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_disassoc_reason,
{ "Disassociation Reason", "wpan.disassoc.reason", FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
/* Coordinator Realignment fields */
{ &hf_ieee802154_realign_pan,
{ "PAN ID", "wpan.realign.pan", FT_UINT16, BASE_HEX, NULL, 0x0,
"The PAN identifier the coordinator wishes to use for future communication.", HFILL }},
{ &hf_ieee802154_realign_caddr,
{ "Coordinator Short Address", "wpan.realign.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
"The 16-bit address the coordinator wishes to use for future communication.", HFILL }},
{ &hf_ieee802154_realign_channel,
{ "Logical Channel", "wpan.realign.channel", FT_UINT8, BASE_DEC, NULL, 0x0,
"The logical channel the coordinator wishes to use for future communication.", HFILL }},
{ &hf_ieee802154_realign_addr,
{ "Short Address", "wpan.realign.addr", FT_UINT16, BASE_HEX, NULL, 0x0,
"A short-address that the orphaned device shall assume if applicable.", HFILL }},
{ &hf_ieee802154_realign_channel_page,
{ "Channel Page", "wpan.realign.channel_page", FT_UINT8, BASE_DEC, NULL, 0x0,
"The logical channel page the coordinator wishes to use for future communication.", HFILL }},
{ &hf_ieee802154_gtsreq_len,
{ "GTS Length", "wpan.gtsreq.length", FT_UINT8, BASE_DEC, NULL, IEEE802154_CMD_GTS_REQ_LEN,
"Number of superframe slots the device is requesting.", HFILL }},
{ &hf_ieee802154_gtsreq_dir,
{ "GTS Direction", "wpan.gtsreq.direction", FT_BOOLEAN, 8, TFS(&tfs_gtsreq_dir), IEEE802154_CMD_GTS_REQ_DIR,
"The direction of traffic in the guaranteed timeslot.", HFILL }},
{ &hf_ieee802154_gtsreq_type,
{ "Characteristic Type", "wpan.gtsreq.type", FT_BOOLEAN, 8, TFS(&tfs_gtsreq_type), IEEE802154_CMD_GTS_REQ_TYPE,
"Whether this request is to allocate or deallocate a timeslot.", HFILL }},
/*
* Beacon Frame Specific Fields
*/
{ &hf_ieee802154_beacon_order,
{ "Beacon Interval", "wpan.beacon_order", FT_UINT16, BASE_DEC, NULL, IEEE802154_BEACON_ORDER_MASK,
"Specifies the transmission interval of the beacons.", HFILL }},
{ &hf_ieee802154_superframe_order,
{ "Superframe Interval", "wpan.superframe_order", FT_UINT16, BASE_DEC, NULL,
IEEE802154_SUPERFRAME_ORDER_MASK,
"Specifies the length of time the coordinator will interact with the PAN.", HFILL }},
{ &hf_ieee802154_cap,
{ "Final CAP Slot", "wpan.cap", FT_UINT16, BASE_DEC, NULL, IEEE802154_SUPERFRAME_CAP_MASK,
"Specifies the final superframe slot used by the CAP.", HFILL }},
{ &hf_ieee802154_superframe_battery_ext,
{ "Battery Extension", "wpan.battery_ext", FT_BOOLEAN, 16, NULL, IEEE802154_BATT_EXTENSION_MASK,
"Whether transmissions may not extend past the length of the beacon frame.", HFILL }},
{ &hf_ieee802154_superframe_coord,
{ "PAN Coordinator", "wpan.bcn_coord", FT_BOOLEAN, 16, NULL, IEEE802154_SUPERFRAME_COORD_MASK,
"Whether this beacon frame is being transmitted by the PAN coordinator or not.", HFILL }},
{ &hf_ieee802154_assoc_permit,
{ "Association Permit", "wpan.assoc_permit", FT_BOOLEAN, 16, NULL, IEEE802154_ASSOC_PERMIT_MASK,
"Whether this PAN is accepting association requests or not.", HFILL }},
{ &hf_ieee802154_gts_count,
{ "GTS Descriptor Count", "wpan.gts.count", FT_UINT8, BASE_DEC, NULL, 0x0,
"The number of GTS descriptors present in this beacon frame.", HFILL }},
{ &hf_ieee802154_gts_permit,
{ "GTS Permit", "wpan.gts.permit", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Whether the PAN coordinator is accepting GTS requests or not.", HFILL }},
{ &hf_ieee802154_gts_direction,
{ "Direction", "wpan.gts.direction", FT_BOOLEAN, BASE_NONE, TFS(&ieee802154_gts_direction_tfs), 0x0,
"A flag defining the direction of the GTS Slot.", HFILL }},
{ &hf_ieee802154_gts_address,
{ "Address", "wpan.gts.address", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_ieee802154_pending16,
{ "Address", "wpan.pending16", FT_UINT16, BASE_HEX, NULL, 0x0,
"Device with pending data to receive.", HFILL }},
{ &hf_ieee802154_pending64,
{ "Address", "wpan.pending64", FT_EUI64, BASE_NONE, NULL, 0x0,
"Device with pending data to receive.", HFILL }},
/*
* Auxiliary Security Header Fields
*/
{ &hf_ieee802154_security_level,
{ "Security Level", "wpan.aux_sec.sec_level", FT_UINT8, BASE_HEX, VALS(ieee802154_sec_level_names),
IEEE802154_AUX_SEC_LEVEL_MASK, "The Security Level of the frame", HFILL }},
{ &hf_ieee802154_security_control_field,
{ "Security Control Field", "wpan.aux_sec.security_control_field", FT_UINT8, BASE_HEX, NULL,
0x0, NULL, HFILL }},
{ &hf_ieee802154_key_id_mode,
{ "Key Identifier Mode", "wpan.aux_sec.key_id_mode", FT_UINT8, BASE_HEX, VALS(ieee802154_key_id_mode_names),
IEEE802154_AUX_KEY_ID_MODE_MASK,
"The scheme to use by the recipient to lookup the key in its key table", HFILL }},
{ &hf_ieee802154_aux_sec_reserved,
{ "Reserved", "wpan.aux_sec.reserved", FT_UINT8, BASE_HEX, NULL, IEEE802154_AUX_KEY_RESERVED_MASK,
NULL, HFILL }},
{ &hf_ieee802154_aux_sec_frame_counter,
{ "Frame Counter", "wpan.aux_sec.frame_counter", FT_UINT32, BASE_DEC, NULL, 0x0,
"Frame counter of the originator of the protected frame", HFILL }},
{ &hf_ieee802154_aux_sec_key_source,
{ "Key Source", "wpan.aux_sec.key_source", FT_UINT64, BASE_HEX, NULL, 0x0,
"Key Source for processing of the protected frame", HFILL }},
{ &hf_ieee802154_aux_sec_key_index,
{ "Key Index", "wpan.aux_sec.key_index", FT_UINT8, BASE_HEX, NULL, 0x0,
"Key Index for processing of the protected frame", HFILL }},
/* IEEE 802.15.4-2003 Security Header Fields */
{ &hf_ieee802154_sec_frame_counter,
{ "Frame Counter", "wpan.sec_frame_counter", FT_UINT32, BASE_HEX, NULL, 0x0,
"Frame counter of the originator of the protected frame (802.15.4-2003)", HFILL }},
{ &hf_ieee802154_sec_key_sequence_counter,
{ "Key Sequence Counter", "wpan.sec_key_sequence_counter", FT_UINT8, BASE_HEX, NULL, 0x0,
"Key Sequence counter of the originator of the protected frame (802.15.4-2003)", HFILL }}
};
/* Subtrees */
static gint *ett[] = {
&ett_ieee802154_nonask_phy,
&ett_ieee802154_nonask_phy_phr,
&ett_ieee802154,
&ett_ieee802154_fcf,
&ett_ieee802154_auxiliary_security,
&ett_ieee802154_aux_sec_control,
&ett_ieee802154_aux_sec_key_id,
&ett_ieee802154_fcs,
&ett_ieee802154_cmd,
&ett_ieee802154_superframe,
&ett_ieee802154_gts,
&ett_ieee802154_gts_direction,
&ett_ieee802154_gts_descriptors,
&ett_ieee802154_pendaddr,
&ett_ieee802154_header,
&ett_ieee802154_header_ie,
&ett_ieee802154_payload,
&ett_ieee802154_payload_ie,
};
static ei_register_info ei[] = {
{ &ei_ieee802154_invalid_addressing, { "wpan.invalid_addressing", PI_MALFORMED, PI_WARN,
"Invalid Addressing", EXPFILL }},
{ &ei_ieee802154_invalid_panid_compression, { "wpan.invalid_panid_compression", PI_MALFORMED, PI_ERROR,
"Invalid Setting for PAN Id Compression", EXPFILL }},
{ &ei_ieee802154_dst, { "wpan.dst_invalid", PI_MALFORMED, PI_ERROR,
"Invalid Destination Address Mode", EXPFILL }},
{ &ei_ieee802154_src, { "wpan.src_invalid", PI_MALFORMED, PI_ERROR,
"Invalid Source Address Mode", EXPFILL }},
{ &ei_ieee802154_frame_ver, { "wpan.frame_version_unknown", PI_MALFORMED, PI_ERROR,
"Frame Version Unknown Cannot Dissect", EXPFILL }},
{ &ei_ieee802154_frame_type, { "wpan.frame_type_unknown", PI_MALFORMED, PI_ERROR,
"Frame Type Unknown Cannot Dissect", EXPFILL }},
{ &ei_ieee802154_decrypt_error, { "wpan.decrypt_error", PI_UNDECODED, PI_WARN,
"Decryption error", EXPFILL }},
{ &ei_ieee802154_fcs, { "wpan.fcs.bad", PI_CHECKSUM, PI_WARN,
"Bad FCS", EXPFILL }},
{ &ei_ieee802154_seqno_suppression, { "wpan.seqno_supression_invalid", PI_MALFORMED, PI_WARN,
"Sequence Number Suppression invalid for 802.15.4-2003 and 2006", EXPFILL }},
};
/* Preferences. */
module_t *ieee802154_module;
expert_module_t* expert_ieee802154;
static uat_field_t addr_uat_flds[] = {
UAT_FLD_HEX(addr_uat,addr16,"Short Address",
"16-bit short address in hexadecimal."),
UAT_FLD_HEX(addr_uat,pan,"PAN Identifier",
"16-bit PAN identifier in hexadecimal."),
UAT_FLD_BUFFER(addr_uat,eui64,"EUI-64",
"64-bit extended unique identifier."),
UAT_END_FIELDS
};
static build_valid_func ieee802154_da_build_value[1] = {ieee802154_da_value};
static decode_as_value_t ieee802154_da_values = {ieee802154_da_prompt, 1, ieee802154_da_build_value};
static decode_as_t ieee802154_da = {
IEEE802154_PROTOABBREV_WPAN, "PAN", IEEE802154_PROTOABBREV_WPAN_PANID,
1, 0, &ieee802154_da_values, NULL, NULL,
decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL
};
/* Register the init routine. */
register_init_routine(proto_init_ieee802154);
register_cleanup_routine(proto_cleanup_ieee802154);
/* Register Protocol name and description. */
proto_ieee802154 = proto_register_protocol("IEEE 802.15.4 Low-Rate Wireless PAN", "IEEE 802.15.4",
IEEE802154_PROTOABBREV_WPAN);
proto_ieee802154_nonask_phy = proto_register_protocol("IEEE 802.15.4 Low-Rate Wireless PAN non-ASK PHY",
"IEEE 802.15.4 non-ASK PHY", "wpan-nonask-phy");
/* Register header fields and subtrees. */
proto_register_field_array(proto_ieee802154, hf, array_length(hf));
proto_register_field_array(proto_ieee802154, hf_phy, array_length(hf_phy));
proto_register_subtree_array(ett, array_length(ett));
expert_ieee802154 = expert_register_protocol(proto_ieee802154);
expert_register_field_array(expert_ieee802154, ei, array_length(ei));
ieee802_15_4_short_address_type = address_type_dissector_register("AT_IEEE_802_15_4_SHORT", "IEEE 802.15.4 16-bit short address",
ieee802_15_4_short_address_to_str, ieee802_15_4_short_address_str_len, NULL, ieee802_15_4_short_address_len, NULL, NULL);
/* add a user preference to set the 802.15.4 ethertype */
ieee802154_module = prefs_register_protocol(proto_ieee802154,
proto_reg_handoff_ieee802154);
prefs_register_uint_preference(ieee802154_module, "802154_ethertype",
"802.15.4 Ethertype (in hex)",
"(Hexadecimal) Ethertype used to indicate IEEE 802.15.4 frame.",
16, &ieee802154_ethertype);
prefs_register_bool_preference(ieee802154_module, "802154_cc24xx",
"TI CC24xx FCS format",
"Set if the FCS field is in TI CC24xx format.",
&ieee802154_cc24xx);
prefs_register_bool_preference(ieee802154_module, "802154_fcs_ok",
"Dissect only good FCS",
"Dissect payload only if FCS is valid.",
&ieee802154_fcs_ok);
/* Create a UAT for static address mappings. */
static_addr_uat = uat_new("Static Addresses",
sizeof(static_addr_t), /* record size */
"802154_addresses", /* filename */
TRUE, /* from_profile */
&static_addrs, /* data_ptr */
&num_static_addrs, /* numitems_ptr */
UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */
NULL, /* help */
NULL, /* copy callback */
addr_uat_update_cb, /* update callback */
NULL, /* free callback */
NULL, /* post update callback */
addr_uat_flds); /* UAT field definitions */
prefs_register_uat_preference(ieee802154_module, "static_addr",
"Static Addresses",
"A table of static address mappings between 16-bit short addressing and EUI-64 addresses",
static_addr_uat);
/* Register preferences for a decryption key */
/* TODO: Implement a UAT for multiple keys, and with more advanced key management. */
prefs_register_string_preference(ieee802154_module, "802154_key", "Decryption key",
"128-bit decryption key in hexadecimal format", (const char **)&ieee802154_key_str);
prefs_register_enum_preference(ieee802154_module, "802154_sec_suite",
"Security Suite (802.15.4-2003)",
"Specifies the security suite to use for 802.15.4-2003 secured frames"
" (only supported suites are listed). Option ignored for 802.15.4-2006"
" and unsecured frames.",
&ieee802154_sec_suite, ieee802154_2003_sec_suite_enums, FALSE);
prefs_register_bool_preference(ieee802154_module, "802154_extend_auth",
"Extend authentication data (802.15.4-2003)",
"Set if the manufacturer extends the authentication data with the"
" security header. Option ignored for 802.15.4-2006 and unsecured frames.",
&ieee802154_extend_auth);
/* Register the subdissector list */
panid_dissector_table = register_dissector_table(IEEE802154_PROTOABBREV_WPAN_PANID, "IEEE 802.15.4 PANID", FT_UINT16, BASE_HEX, DISSECTOR_TABLE_NOT_ALLOW_DUPLICATE);
ieee802154_heur_subdissector_list = register_heur_dissector_list(IEEE802154_PROTOABBREV_WPAN);
ieee802154_beacon_subdissector_list = register_heur_dissector_list(IEEE802154_PROTOABBREV_WPAN_BEACON);
/* Register dissectors with Wireshark. */
register_dissector(IEEE802154_PROTOABBREV_WPAN, dissect_ieee802154, proto_ieee802154);
register_dissector("wpan_nofcs", dissect_ieee802154_nofcs, proto_ieee802154);
register_dissector("wpan_cc24xx", dissect_ieee802154_cc24xx, proto_ieee802154);
register_dissector("wpan-nonask-phy", dissect_ieee802154_nonask_phy, proto_ieee802154_nonask_phy);
/* Register a Decode-As handler. */
register_decode_as(&ieee802154_da);
} /* proto_register_ieee802154 */
/**
* Registers the IEEE 802.15.4 dissector with Wireshark.
* Will be called every time 'apply' is pressed in the preferences menu.
* as well as during Wireshark initialization
*/
void proto_reg_handoff_ieee802154(void)
{
static gboolean prefs_initialized = FALSE;
static dissector_handle_t ieee802154_handle;
static dissector_handle_t ieee802154_nonask_phy_handle;
static dissector_handle_t ieee802154_nofcs_handle;
static unsigned int old_ieee802154_ethertype;
GByteArray *bytes;
gboolean res;
if (!prefs_initialized){
/* Get the dissector handles. */
ieee802154_handle = find_dissector(IEEE802154_PROTOABBREV_WPAN);
ieee802154_nonask_phy_handle = find_dissector("wpan-nonask-phy");
ieee802154_nofcs_handle = find_dissector("wpan_nofcs");
data_handle = find_dissector("data");
dissector_add_uint("wtap_encap", WTAP_ENCAP_IEEE802_15_4, ieee802154_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_IEEE802_15_4_NONASK_PHY, ieee802154_nonask_phy_handle);
dissector_add_uint("wtap_encap", WTAP_ENCAP_IEEE802_15_4_NOFCS, ieee802154_nofcs_handle);
dissector_add_uint("sll.ltype", LINUX_SLL_P_IEEE802154, ieee802154_handle);
prefs_initialized = TRUE;
} else {
dissector_delete_uint("ethertype", old_ieee802154_ethertype, ieee802154_handle);
}
old_ieee802154_ethertype = ieee802154_ethertype;
/* Get the IEEE 802.15.4 decryption key. */
bytes = g_byte_array_new();
res = hex_str_to_bytes(ieee802154_key_str, bytes, FALSE);
ieee802154_key_valid = (res && bytes->len >= IEEE802154_CIPHER_SIZE);
if (ieee802154_key_valid) {
memcpy(ieee802154_key, bytes->data, IEEE802154_CIPHER_SIZE);
}
g_byte_array_free(bytes, TRUE);
/* Register dissector handles. */
dissector_add_uint("ethertype", ieee802154_ethertype, ieee802154_handle);
} /* proto_reg_handoff_ieee802154 */