diff options
author | Robert Cragie <Robert.Cragie@arm.com> | 2017-03-17 16:59:23 +0000 |
---|---|---|
committer | Michael Mann <mmann78@netscape.net> | 2017-04-10 20:34:34 +0000 |
commit | 14ba81598da88390d26d3082da735743d6d41074 (patch) | |
tree | 2c483024b966cb78be95f9aac5371b98cafcd0ce | |
parent | 043df01c5645378bd61318c20cfdbf86c8f73288 (diff) |
MLE and Thread: Additional dissectors
Additional dissectors for Mesh Link Establishment (MLE) and Thread CoAP
TMF messages. MLE is also used in ZigBee IP.
Change-Id: I5b9c224d7df48855b79ccac67dca7661a51d0a9b
Bug: 13495
Reviewed-on: https://code.wireshark.org/review/20594
Reviewed-by: Stig Bjørlykke <stig@bjorlykke.org>
Petri-Dish: Stig Bjørlykke <stig@bjorlykke.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
-rw-r--r-- | docbook/release-notes.asciidoc | 3 | ||||
-rw-r--r-- | epan/dissectors/CMakeLists.txt | 3 | ||||
-rw-r--r-- | epan/dissectors/Makefile.am | 4 | ||||
-rw-r--r-- | epan/dissectors/packet-ieee802154.c | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-mle.c | 2065 | ||||
-rw-r--r-- | epan/dissectors/packet-mle.h | 32 | ||||
-rw-r--r-- | epan/dissectors/packet-openthread.c | 113 | ||||
-rw-r--r-- | epan/dissectors/packet-thread.c | 3470 | ||||
-rwxr-xr-x | tools/checkfiltername.pl | 1 |
9 files changed, 5693 insertions, 0 deletions
diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 472a414cf1..0ecb0f59de 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -69,12 +69,15 @@ iPerf2 ISO 15765 Local Service Discovery (LSD) M2 Application Protocol +Mesh Link Establishment (MLE) Nordic BLE Sniffer NVMe Fabrics RDMA NVMe +OpenThread simulator RFTap Protocol SCTE-35 Digital Program Insertion Messages Snort Post-dissector +Thread CoAP Unified Diagnostic Services (UDS) vSocket Windows Cluster Management API (clusapi) diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index 969c48a14e..24167e7314 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -892,6 +892,7 @@ set(DISSECTOR_SRC packet-mip.c packet-mip6.c packet-mka.c + packet-mle.c packet-mmse.c packet-mndp.c packet-mojito.c @@ -1013,6 +1014,7 @@ set(DISSECTOR_SRC packet-openflow_v4.c packet-openflow_v5.c packet-opensafety.c + packet-openthread.c packet-openvpn.c packet-openwire.c packet-opsi.c @@ -1262,6 +1264,7 @@ set(DISSECTOR_SRC packet-text-media.c packet-tfp.c packet-tftp.c + packet-thread.c packet-thrift.c packet-time.c packet-tipc.c diff --git a/epan/dissectors/Makefile.am b/epan/dissectors/Makefile.am index d6782ce15d..503b061e04 100644 --- a/epan/dissectors/Makefile.am +++ b/epan/dissectors/Makefile.am @@ -913,6 +913,7 @@ DISSECTOR_SRC = \ packet-mip.c \ packet-mip6.c \ packet-mka.c \ + packet-mle.c \ packet-mmse.c \ packet-mndp.c \ packet-mojito.c \ @@ -1035,6 +1036,7 @@ DISSECTOR_SRC = \ packet-openflow_v4.c \ packet-openflow_v5.c \ packet-opensafety.c \ + packet-openthread.c \ packet-openvpn.c \ packet-openwire.c \ packet-opsi.c \ @@ -1284,6 +1286,7 @@ DISSECTOR_SRC = \ packet-text-media.c \ packet-tfp.c \ packet-tftp.c \ + packet-thread.c \ packet-thrift.c \ packet-time.c \ packet-tipc.c \ @@ -1629,6 +1632,7 @@ DISSECTOR_INCLUDES = \ packet-mbim.h \ packet-mbtcp.h \ packet-mgcp.h \ + packet-mle.h \ packet-mms.h \ packet-mount.h \ packet-mp4ves.h \ diff --git a/epan/dissectors/packet-ieee802154.c b/epan/dissectors/packet-ieee802154.c index ee7d326dcb..b2b298949e 100644 --- a/epan/dissectors/packet-ieee802154.c +++ b/epan/dissectors/packet-ieee802154.c @@ -240,6 +240,8 @@ static void* ieee802154_key_copy_cb(void* n, const void* o, size_t siz _U_) { const ieee802154_key_t* old_record = (const ieee802154_key_t*)o; new_record->pref_key = g_strdup(old_record->pref_key); + new_record->key_index = old_record->key_index; + new_record->hash_type = old_record->hash_type; return new_record; } diff --git a/epan/dissectors/packet-mle.c b/epan/dissectors/packet-mle.c new file mode 100644 index 0000000000..9840a534e9 --- /dev/null +++ b/epan/dissectors/packet-mle.c @@ -0,0 +1,2065 @@ +/* packet-mle.c + * Routines for MLE packet dissection + * + * Colin O'Flynn <coflynn@newae.com> + * + * The entire security section of this is lifted from the IEEE 802.15.4 + * dissectory, as this is done the same way. Should eventually make the + * two use some common functions or something. But that section is: + * By Owen Kirby <osk@exegin.com> + * Copyright 2007 Exegin Technologies Limited + * + * Thread parts added by Robert Cragie <robert.cragie@arm.com> + * + * 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. + */ + +#include "config.h" + +#include <glib.h> +#include <math.h> +#include <epan/packet.h> +#include <epan/conversation.h> +#include <epan/proto_data.h> +#include <epan/wmem/wmem.h> +#include <epan/expert.h> +#include <epan/prefs.h> +#include <epan/strutil.h> +#include <epan/to_str.h> +#include "packet-ieee802154.h" +#include "packet-mle.h" + +#define MLE_32768_TO_NSEC_FACTOR ((double)30517.578125) + +/* Forward declarations */ +void proto_register_mle(void); +void proto_reg_handoff_mle(void); + +static int proto_mle = -1; +static int proto_ieee802154 = -1; /* cache 802.15.4 protocol ID */ + +/* Registered fields for Auxiliary Security Header */ +static int hf_mle_security_suite = -1; +static int hf_mle_mic = -1; + +static int hf_mle_command = -1; +static int hf_mle_tlv = -1; +static int hf_mle_tlv_type = -1; +static int hf_mle_tlv_length = -1; +static int hf_mle_tlv_source_addr = -1; +static int hf_mle_tlv_mode_device_type = -1; +static int hf_mle_tlv_mode_idle_rx = -1; +static int hf_mle_tlv_mode_sec_data_req = -1; +static int hf_mle_tlv_mode_nwk_data = -1; +static int hf_mle_tlv_timeout = -1; +static int hf_mle_tlv_challenge = -1; +static int hf_mle_tlv_response = -1; +static int hf_mle_tlv_ll_frm_cntr = -1; +static int hf_mle_tlv_lqi_c = -1; +static int hf_mle_tlv_lqi_size = -1; +static int hf_mle_tlv_neighbor = -1; +static int hf_mle_tlv_neighbor_flagI = -1; +static int hf_mle_tlv_neighbor_flagO = -1; +static int hf_mle_tlv_neighbor_flagP = -1; +static int hf_mle_tlv_neighbor_idr = -1; +static int hf_mle_tlv_neighbor_addr = -1; +static int hf_mle_tlv_network_param_id = -1; +static int hf_mle_tlv_network_delay = -1; +static int hf_mle_tlv_network_channel = -1; +static int hf_mle_tlv_network_pan_id = -1; +static int hf_mle_tlv_network_pmt_join = -1; +static int hf_mle_tlv_network_bcn_payload = -1; +static int hf_mle_tlv_network_unknown = -1; +static int hf_mle_tlv_mle_frm_cntr = -1; +static int hf_mle_tlv_unknown = -1; +static int hf_mle_tlv_route64_id_seq = -1; +static int hf_mle_tlv_route64_id_mask = -1; +static int hf_mle_tlv_route64_entry = -1; +static int hf_mle_tlv_route64_nbr_out = -1; +static int hf_mle_tlv_route64_nbr_in = -1; +static int hf_mle_tlv_route64_cost = -1; +#if 0 +static int hf_mle_tlv_route64_unknown = -1; +#endif +static int hf_mle_tlv_addr16 = -1; +static int hf_mle_tlv_leader_data_partition_id = -1; +static int hf_mle_tlv_leader_data_weighting = -1; +static int hf_mle_tlv_leader_data_version = -1; +static int hf_mle_tlv_leader_data_stable_version = -1; +static int hf_mle_tlv_leader_data_router_id = -1; +#if 0 +static int hf_mle_tlv_network_data = -1; +#endif +static int hf_mle_tlv_scan_mask_r = -1; +static int hf_mle_tlv_scan_mask_e = -1; +static int hf_mle_tlv_conn_max_child_cnt = -1; +static int hf_mle_tlv_conn_child_cnt = -1; +static int hf_mle_tlv_conn_flags = -1; +static int hf_mle_tlv_conn_flags_pp = -1; +static int hf_mle_tlv_conn_lq3 = -1; +static int hf_mle_tlv_conn_lq2 = -1; +static int hf_mle_tlv_conn_lq1 = -1; +static int hf_mle_tlv_conn_leader_cost = -1; +static int hf_mle_tlv_conn_id_seq = -1; +static int hf_mle_tlv_conn_active_rtrs = -1; +static int hf_mle_tlv_conn_sed_buf_size = -1; +static int hf_mle_tlv_conn_sed_dgram_cnt = -1; +static int hf_mle_tlv_link_margin = -1; +static int hf_mle_tlv_status = -1; +static int hf_mle_tlv_version = -1; +static int hf_mle_tlv_addr_reg_entry = -1; +static int hf_mle_tlv_addr_reg_iid_type = -1; +static int hf_mle_tlv_addr_reg_cid = -1; +static int hf_mle_tlv_addr_reg_iid = -1; +static int hf_mle_tlv_addr_reg_ipv6 = -1; +#if 0 +static int hf_mle_tlv_hold_time = -1; +#endif +static int hf_mle_tlv_channel_page = -1; /* v1.1-draft-2 */ +static int hf_mle_tlv_channel = -1; /* v1.1-draft-2 */ +static int hf_mle_tlv_pan_id = -1; /* v1.1-draft-2 */ +static int hf_mle_tlv_active_tstamp = -1; /* SPEC-472 */ +static int hf_mle_tlv_pending_tstamp = -1; /* SPEC-472 */ +#if 0 +static int hf_mle_tlv_active_op_dataset = -1; /* SPEC-472 */ +static int hf_mle_tlv_pending_op_dataset = -1; /* SPEC-472 */ +#endif + +static gint ett_mle = -1; +static gint ett_mle_tlv = -1; +static gint ett_mle_neighbor = -1; +static gint ett_mle_router = -1; +static gint ett_mle_addr_reg = -1; +static gint ett_mle_conn_flg = -1; +static gint ett_mle_thread_nwd = -1; +static gint ett_mle_auxiliary_security = -1; +static gint ett_mle_aux_sec_control = -1; +static gint ett_mle_aux_sec_key_id = -1; + +static expert_field ei_mle_cbc_mac_failed = EI_INIT; +static expert_field ei_mle_packet_too_small = EI_INIT; +static expert_field ei_mle_no_key = EI_INIT; +static expert_field ei_mle_decrypt_failed = EI_INIT; +static expert_field ei_mle_mic_check_failed = EI_INIT; +static expert_field ei_mle_tlv_length_failed = EI_INIT; +static expert_field ei_mle_len_size_mismatch = EI_INIT; + +static dissector_handle_t mle_handle; +static dissector_handle_t thread_nwd_handle; +static dissector_handle_t thread_mc_handle; + +#define UDP_PORT_MLE_RANGE "19788" /* IANA registered */ + +/* boolean value set if the MIC must be ok before payload is dissected */ +static gboolean mle_mic_ok = FALSE; + +static wmem_tree_t* mle_key_hash_handlers; + +static const value_string mle_sec_suite_names[] = { + { 0, "802.15.4 Security" }, + { 255, "No Security" }, + { 0, NULL } +}; + +static const value_string mle_status_tlv_enums[] = { + { 1, "Error" }, + { 2, "Duplicate Address Detected" }, + { 0, NULL } +}; + +static const value_string mle_conn_tlv_flags_pp_enums[] = { + { 1, "High" }, + { 0, "Medium" }, + { -1, "Low" }, + { 0, NULL } +}; + +#define MLE_CMD_REQUEST 0 +#define MLE_CMD_ACCEPT 1 +#define MLE_CMD_ACCEPTREQ 2 +#define MLE_CMD_REJECT 3 +#define MLE_CMD_ADVERTISE 4 +#define MLE_CMD_UPDATE 5 +#define MLE_CMD_UPDATE_REQUEST 6 +#define MLE_CMD_DATA_REQUEST 7 +#define MLE_CMD_DATA_RESPONSE 8 +#define MLE_CMD_PARENT_REQUEST 9 +#define MLE_CMD_PARENT_RESPONSE 10 +#define MLE_CMD_CHILD_ID_REQUEST 11 +#define MLE_CMD_CHILD_ID_RESPONSE 12 +#define MLE_CMD_CHILD_UPDATE_REQUEST 13 +#define MLE_CMD_CHILD_UPDATE_RESPONSE 14 +#define MLE_CMD_ANNOUNCE 15 +#define MLE_CMD_DISCOVERY_REQUEST 16 +#define MLE_CMD_DISCOVERY_RESPONSE 17 + +static const value_string mle_command_vals[] = { + { MLE_CMD_REQUEST, "Link Request" }, + { MLE_CMD_ACCEPT, "Link Accept" }, + { MLE_CMD_ACCEPTREQ, "Link Accept and Request" }, + { MLE_CMD_REJECT, "Link Reject" }, + { MLE_CMD_ADVERTISE, "Advertisement" }, + { MLE_CMD_UPDATE, "Update" }, + { MLE_CMD_UPDATE_REQUEST, "Update Request" }, + { MLE_CMD_DATA_REQUEST, "Data Request" }, + { MLE_CMD_DATA_RESPONSE, "Data Response" }, + { MLE_CMD_PARENT_REQUEST, "Parent Request" }, + { MLE_CMD_PARENT_RESPONSE, "Parent Response" }, + { MLE_CMD_CHILD_ID_REQUEST, "Child ID Request" }, + { MLE_CMD_CHILD_ID_RESPONSE, "Child ID Response" }, + { MLE_CMD_CHILD_UPDATE_REQUEST, "Child Update Request" }, + { MLE_CMD_CHILD_UPDATE_RESPONSE, "Child Update Response" }, + { MLE_CMD_ANNOUNCE, "Announce" }, + { MLE_CMD_DISCOVERY_REQUEST, "Discovery Request" }, + { MLE_CMD_DISCOVERY_RESPONSE, "Discovery Response" }, + { 0, NULL} +}; + +#define MLE_TLV_SOURCE_ADDRESS 0 +#define MLE_TLV_MODE 1 /* Modified in Ch04_Mesh Link Establishment */ +#define MLE_TLV_TIMEOUT 2 +#define MLE_TLV_CHALLENGE 3 +#define MLE_TLV_RESPONSE 4 +#define MLE_TLV_LINK_LAYER_FRAME_COUNTER 5 +#define MLE_TLV_LINK_QUALITY 6 +#define MLE_TLV_NETWORK_PARAMETER 7 +#define MLE_TLV_MLE_FRAME_COUNTER 8 +#define MLE_TLV_ROUTE64 9 /* Defined in Ch05_Network Layer v1.1-rc1 */ +#define MLE_TLV_ADDRESS16 10 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_LEADER_DATA 11 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_NETWORK_DATA 12 /* Defined in Ch05_Network Layer v1.1-rc1 */ +#define MLE_TLV_TLV_REQUEST 13 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_SCAN_MASK 14 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_CONNECTIVITY 15 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_LINK_MARGIN 16 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_STATUS 17 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_VERSION 18 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_ADDRESS_REGISTRATION 19 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_CHANNEL 20 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_PAN_ID 21 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_ACTIVE_TSTAMP 22 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_PENDING_TSTAMP 23 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_ACTIVE_OP_DATASET 24 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_PENDING_OP_DATASET 25 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ +#define MLE_TLV_THREAD_DISCOVERY 26 /* Defined in Ch04_Mesh Link Establishment v1.1-rc1 */ + +static const value_string mle_tlv_vals[] = { + { MLE_TLV_SOURCE_ADDRESS, "Source Address" }, + { MLE_TLV_MODE, "Mode" }, + { MLE_TLV_TIMEOUT, "Timeout" }, + { MLE_TLV_CHALLENGE, "Challenge" }, + { MLE_TLV_RESPONSE, "Response" }, + { MLE_TLV_LINK_LAYER_FRAME_COUNTER, "Link Layer Frame Counter"}, + { MLE_TLV_LINK_QUALITY, "Link Quality"}, + { MLE_TLV_NETWORK_PARAMETER, "Network Parameter"}, + { MLE_TLV_MLE_FRAME_COUNTER, "MLE Frame Counter"}, + { MLE_TLV_ROUTE64, "Route64"}, + { MLE_TLV_ADDRESS16, "Address16"}, + { MLE_TLV_LEADER_DATA, "Leader Data"}, + { MLE_TLV_NETWORK_DATA, "Network Data"}, + { MLE_TLV_TLV_REQUEST, "TLV Request"}, + { MLE_TLV_SCAN_MASK, "Scan Mask"}, + { MLE_TLV_CONNECTIVITY, "Connectivity"}, + { MLE_TLV_LINK_MARGIN, "Link Margin"}, + { MLE_TLV_STATUS, "Status"}, + { MLE_TLV_VERSION, "Version"}, + { MLE_TLV_ADDRESS_REGISTRATION, "Address Registration"}, + { MLE_TLV_CHANNEL, "Channel"}, + { MLE_TLV_PAN_ID, "PAN ID"}, + { MLE_TLV_ACTIVE_TSTAMP, "Active Timestamp"}, + { MLE_TLV_PENDING_TSTAMP, "Pending Timestamp"}, + { MLE_TLV_ACTIVE_OP_DATASET, "Active Operational Dataset"}, + { MLE_TLV_PENDING_OP_DATASET, "Pending Operational Dataset"}, + { MLE_TLV_THREAD_DISCOVERY, "Thread Discovery"}, + { 0, NULL} +}; + +#define LQI_FLAGS_C 0x80 +#define LQI_FLAGS_SIZE 0x0F + +#define NEIGHBOR_FLAG_I 0x80 +#define NEIGHBOR_FLAG_O 0x40 +#define NEIGHBOR_FLAG_P 0x20 + +#define NETWORK_PARAM_ID_CHANNEL 0 +#define NETWORK_PARAM_ID_PAN_ID 1 +#define NETWORK_PARAM_ID_PERMIT_JOIN 2 +#define NETWORK_PARAM_ID_BCN_PAYLOAD 3 + +static const value_string mle_tlv_nwk_param_vals[] = { + { NETWORK_PARAM_ID_CHANNEL, "Channel" }, + { NETWORK_PARAM_ID_PAN_ID, "PAN ID" }, + { NETWORK_PARAM_ID_PERMIT_JOIN, "Permit Join" }, + { NETWORK_PARAM_ID_BCN_PAYLOAD, "Beacon Payload" }, + { 0, NULL} +}; + +static const true_false_string mle_tlv_mode_device_type = { + "FFD", + "RFD" +}; +static const true_false_string mle_tlv_mode_nwk_data = { + "Full", + "Stable" +}; +static const true_false_string mle_tlv_addr_reg_iid_type = { + "Compressed", + "Full" +}; + +#define ROUTE_TBL_OUT_MASK 0xC0 +#define ROUTE_TBL_IN_MASK 0x30 +#define ROUTE_TBL_COST_MASK 0x0F + +#define SCAN_MASK_R_MASK 0x80 +#define SCAN_MASK_D_MASK 0x40 + +#define CONN_MASK_FLAGS_PP_MASK 0xC0 + +#define ADDR_REG_MASK_IID_TYPE_MASK 0x80 +#define ADDR_REG_MASK_CID_MASK 0x0F + +#define MLE_CMD_CINFO_SEC_DATA_REQ 0x04 +#define MLE_CMD_CINFO_NWK_DATA 0x01 + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_mle_decrypt + * DESCRIPTION + * MLE dissector. + * PARAMETERS + * tvbuff_t *tvb - IEEE 802.15.4 packet. + * packet_info * pinfo - Packet info structure. + * guint offset - Offset where the ciphertext 'c' starts. + * ieee802154_packet *packet - IEEE 802.15.4 packet information. + * ws_decrypt_status *status - status of decryption returned through here on failure. + * RETURNS + * tvbuff_t * - Decrypted payload. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_mle_decrypt(tvbuff_t * tvb, + guint offset, + packet_info * pinfo, + ieee802154_packet * packet, + ieee802154_payload_info_t* payload_info) +{ + tvbuff_t * ptext_tvb; + gboolean have_mic = FALSE; + guint64 srcAddr; + unsigned char tmp[16]; + guint M; + gint captured_len; + gint reported_len; + + *payload_info->rx_mic_length = 0; + memset(payload_info->rx_mic, 0, 16); + + /* Get the captured and on-the-wire length of the payload. */ + if (packet->security_level > 0) { + M = IEEE802154_MIC_LENGTH(packet->security_level); + } + else { + M = 0; + } + + reported_len = tvb_reported_length_remaining(tvb, offset) - M; + if (reported_len < 0) { + *payload_info->status = DECRYPT_PACKET_TOO_SMALL; + return NULL; + } + /* Check if 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); + } + + if (packet->security_level > 0) { + /* 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, payload_info->rx_mic, offset + reported_len, M); + } + } + + /*===================================================== + * Key Lookup - Need to find the appropriate key. + *===================================================== + */ + if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) { + /* The source EUI-64 is included in the headers. */ + srcAddr = packet->src64; /* GUINT64_SWAP_LE_BE(packet->src64); */ + } + else { + /* Lookup failed. */ + *payload_info->status = DECRYPT_PACKET_NO_EXT_SRC_ADDR; + return NULL; + } + + /*===================================================== + * 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->frame_counter, packet->security_level, 0); + + /* Decrypt the ciphertext, and place the plaintext in a new tvb. */ + if (IEEE802154_IS_ENCRYPTED(packet->security_level) && captured_len) { + gchar *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 = (gchar *)tvb_memdup(pinfo->pool, tvb, offset, captured_len); + + /* Perform CTR-mode transformation. Try both the likely key and the alternate key */ + if (!ccm_ctr_encrypt(payload_info->key, tmp, payload_info->rx_mic, text, captured_len)) { + *payload_info->status = DECRYPT_PACKET_DECRYPT_FAILED; + return NULL; + } + + /* Create a tvbuff for the plaintext. */ + ptext_tvb = tvb_new_real_data((const guint8 *)text, captured_len, reported_len); + tvb_set_child_real_data_tvbuff(tvb, ptext_tvb); + add_new_data_source(pinfo, ptext_tvb, "Decrypted MLE payload"); + *payload_info->status = DECRYPT_PACKET_SUCCEEDED; + } + /* There is no ciphertext. Wrap the plaintext in a new tvb. */ + else { + /* Decrypt the MIC (if present). */ + if (have_mic) { + if (!ccm_ctr_encrypt(payload_info->key, tmp, payload_info->rx_mic, NULL, 0)) { + *payload_info->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_length_caplen(tvb, offset, captured_len, reported_len); + *payload_info->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; + guint8 d_a[256]; + + DISSECTOR_ASSERT(pinfo->src.len == 16); + DISSECTOR_ASSERT(pinfo->dst.len == 16); + memcpy(d_a, (guint8 *)pinfo->src.data, pinfo->src.len); + memcpy(d_a+16, (guint8 *)pinfo->dst.data, pinfo->dst.len); + + tvb_memcpy(tvb, d_a+32, payload_info->aux_offset, payload_info->aux_length); + l_a = 32 + payload_info->aux_length; + + /* Adjust the lengths of the plantext and additional data if unencrypted. */ + if (!IEEE802154_IS_ENCRYPTED(packet->security_level)) { + l_a += l_m; + l_m = 0; + } + + /* Create the CCM* initial block for authentication (Adata!=0, M!=0, counter=l(m)). */ + ccm_init_block(tmp, TRUE, M, srcAddr, packet->frame_counter, packet->security_level, 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(payload_info->key, tmp, d_a, l_a, tvb_get_ptr(ptext_tvb, 0, l_m), l_m, dec_mic)) { + *payload_info->status = DECRYPT_PACKET_MIC_CHECK_FAILED; + } + /* Compare the received MIC with the one we generated. */ + else if (memcmp(payload_info->rx_mic, dec_mic, M) != 0) { + *payload_info->status = DECRYPT_PACKET_MIC_CHECK_FAILED; + } + } + + *payload_info->rx_mic_length = M; + + /* Done! */ + return ptext_tvb; +} /* dissect_mle_decrypt */ + +void register_mle_key_hash_handler(guint hash_identifier, ieee802154_set_mac_key_func key_func) +{ + /* Ensure no duplication */ + DISSECTOR_ASSERT(wmem_tree_lookup32(mle_key_hash_handlers, hash_identifier) == NULL); + + wmem_tree_insert32(mle_key_hash_handlers, hash_identifier, (void*)key_func); +} + +/* Set MLE key function. */ +static gboolean ieee802154_set_mle_key(ieee802154_packet *packet, unsigned char *key, unsigned char *alt_key, ieee802154_key_t* uat_key) +{ + mle_set_mle_key_func func = (mle_set_mle_key_func)wmem_tree_lookup32(mle_key_hash_handlers, uat_key->hash_type); + + if (func != NULL) + return func(packet, key, alt_key, uat_key); + + /* Right now, KEY_HASH_NONE and KEY_HASH_ZIP are not registered because they + work with this "default" behavior */ + if (packet->key_index == uat_key->key_index) + { + memcpy(key, uat_key->key, IEEE802154_CIPHER_SIZE); + return TRUE; + } + + return FALSE; +} + +static int +dissect_mle(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + tvbuff_t *volatile payload_tvb = NULL; + proto_tree *volatile mle_tree = NULL; + proto_item *volatile proto_root = NULL; + + guint offset = 0; + guint aux_header_offset = 0; + ws_decrypt_status status; + + proto_item *ti; + proto_item *mic_item = NULL; + proto_tree *header_tree = NULL; + guint8 security_suite; + guint aux_length = 0; + ieee802154_packet *packet = (ieee802154_packet *)wmem_alloc(wmem_packet_scope(), sizeof(ieee802154_packet)); + ieee802154_packet *original_packet; + ieee802154_payload_info_t payload_info; + ieee802154_hints_t *ieee_hints; + gboolean mic_ok=TRUE; + + unsigned char rx_mic[16]; + unsigned int rx_mic_len = 0; + + guint8 cmd; + guint8 tlv_type, tlv_len; + proto_tree *tlv_tree; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "MLE"); + col_clear(pinfo->cinfo, COL_INFO); + + ieee_hints = (ieee802154_hints_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ieee802154, 0); + original_packet = (ieee802154_packet *)ieee_hints->packet; + + /* Copy IEEE 802.15.4 Source Address */ + packet->src_addr_mode = original_packet->src_addr_mode; + if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) { + packet->src64 = original_packet->src64; + } else { + packet->src16 = original_packet->src16; + } + + /* Copy IEEE 802.15.4 Source PAN ID */ + packet->src_pan = original_packet->src_pan; + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_mle, tvb, 0, tvb_reported_length(tvb), ENC_NA); + mle_tree = proto_item_add_subtree(proto_root, ett_mle); + + /* Parse the security suite field. */ + /* Security Suite Field */ + security_suite = tvb_get_guint8(tvb, offset); + proto_tree_add_item(mle_tree, hf_mle_security_suite, tvb, offset, 1, security_suite); + offset++; + + aux_header_offset = offset; + + /* Security material present if security suite = 0 */ + if (security_suite == 0) { + dissect_ieee802154_aux_sec_header_and_key(tvb, pinfo, mle_tree, packet, &offset); + aux_length = offset-aux_header_offset; + } else { + packet->security_level = SECURITY_LEVEL_NONE; + } + + payload_info.key_number = 0; + + /* Add additional fields for security level > SECURITY_LEVEL_NONE */ + if (packet->security_level > SECURITY_LEVEL_NONE) { + + /* Pass to decryption process */ + payload_info.rx_mic = rx_mic; + payload_info.rx_mic_length = &rx_mic_len; + payload_info.aux_offset = aux_header_offset; + payload_info.aux_length = aux_length; + payload_info.status = &status; + payload_info.key = NULL; /* payload function will fill that in */ + + payload_tvb = dissect_ieee802154_payload(tvb, offset, pinfo, header_tree, packet, &payload_info, + ieee802154_set_mle_key, dissect_mle_decrypt); + if (status == DECRYPT_PACKET_MIC_CHECK_FAILED) + expert_add_info(pinfo, proto_root, &ei_mle_cbc_mac_failed); + + /* MIC */ + mic_item = NULL; + if (rx_mic_len) { + mic_item = proto_tree_add_bytes(header_tree, hf_mle_mic, tvb, 0, rx_mic_len, rx_mic); + PROTO_ITEM_SET_GENERATED(mic_item); + } + } else { + status = DECRYPT_NOT_ENCRYPTED; + } + + /* 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_remaining(tvb, offset); + gint captured_len = tvb_captured_length_remaining(tvb, offset); + if (reported_len < captured_len) captured_len = reported_len; + payload_tvb = tvb_new_subset_length_caplen(tvb, offset, captured_len, reported_len); + } + + /* Display the reason for failure, and abort if the error was fatal. */ + switch (status) { + case DECRYPT_PACKET_SUCCEEDED: + /* No problem. */ + proto_item_append_text(mic_item, " [correct (key no. %d)]", payload_info.key_number); + break; + + case DECRYPT_PACKET_TOO_SMALL: + expert_add_info(pinfo, proto_root, &ei_mle_packet_too_small); + call_data_dissector(payload_tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case DECRYPT_PACKET_NO_KEY: + expert_add_info(pinfo, proto_root, &ei_mle_no_key); + call_data_dissector(payload_tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case DECRYPT_PACKET_DECRYPT_FAILED: + expert_add_info(pinfo, proto_root, &ei_mle_decrypt_failed); + call_data_dissector(payload_tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case DECRYPT_PACKET_MIC_CHECK_FAILED: + expert_add_info(pinfo, proto_root, &ei_mle_mic_check_failed); + proto_item_append_text(mic_item, " [incorrect]"); + /* + * 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)) { + mic_ok = FALSE; + } + break; + case DECRYPT_NOT_ENCRYPTED: + default: + break; + } + /* This can cause a lot of problems so remove it by default */ + if (!mic_ok && mle_mic_ok) { + call_data_dissector(payload_tvb, pinfo, tree); + col_add_fstr(pinfo->cinfo, COL_INFO, "MIC Failed"); + return tvb_captured_length(tvb); + } + + /***** NEW CODE HERE ****/ + /* If we're good, carry on and display the MLE payload */ + offset = 0; + + /* MLE Command */ + proto_tree_add_item(mle_tree, hf_mle_command, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + + cmd = tvb_get_guint8(payload_tvb, offset); + col_add_str(pinfo->cinfo, COL_INFO, val_to_str(cmd, mle_command_vals, "Unknown (%x)")); + + offset++; + + /* MLE TLVs */ + while (tvb_offset_exists(payload_tvb, offset)) { + + /* Get the length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_len = tvb_get_guint8(payload_tvb, offset+1); + + ti = proto_tree_add_item(mle_tree, hf_mle_tlv, payload_tvb, offset, tlv_len+2, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_mle_tlv); + + /* Type */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_type, payload_tvb, offset, 1, ENC_NA); + tlv_type = tvb_get_guint8(payload_tvb, offset); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s", val_to_str(tlv_type, mle_tlv_vals, "Unknown (%d)")); + + /* Length */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_length, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + switch(tlv_type){ + case MLE_TLV_SOURCE_ADDRESS: + { + gboolean haveShortTLV = FALSE; + guint16 shortAddr = 0; + + if (!((tlv_len == 2) || (tlv_len == 8))) { + /* TLV Length must be 2 or 8 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + if (tlv_len == 2) { + haveShortTLV = TRUE; + shortAddr = tvb_get_ntohs(payload_tvb, offset); + } + + proto_tree_add_item(tlv_tree, hf_mle_tlv_source_addr, payload_tvb, offset, tlv_len, ENC_NA); + proto_item_append_text(ti, " = "); + while (tlv_len) { + guint8 addr; + addr = tvb_get_guint8(payload_tvb, offset); + proto_item_append_text(ti, "%02x", addr); + if (--tlv_len) { + proto_item_append_text(ti, ":"); + } + offset++; + } + if ((original_packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) && haveShortTLV) { + /* Source TLV: use this to update src/long mapping */ + ieee802154_addr_update(&ieee802154_map, shortAddr, original_packet->src_pan, original_packet->src64, pinfo->current_proto, pinfo->fd->num); + } + } + proto_item_append_text(ti, ")"); + } + break; + + case MLE_TLV_MODE: + if (tlv_len == 1) { + guint8 capability; + + capability = tvb_get_guint8(payload_tvb, offset); + proto_item_append_text(ti, " = %02x)", capability); + /* Get and display capability info. (blatantly plagiarised from packet-ieee802154.c */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_mode_nwk_data, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_mle_tlv_mode_device_type, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_mle_tlv_mode_sec_data_req, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_mle_tlv_mode_idle_rx, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + } + else { + /* TLV Length must be 1 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + break; + + case MLE_TLV_TIMEOUT: + if (tlv_len != 4) { + /* TLV Length must be 4 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint32 to_data = 0; + proto_tree_add_item_ret_uint(tlv_tree, hf_mle_tlv_timeout, payload_tvb, offset, 4, ENC_BIG_ENDIAN, &to_data); + proto_item_append_text(ti, " = %d", (guint16)to_data); + } + proto_item_append_text(ti, ")"); + offset += tlv_len; + break; + + case MLE_TLV_CHALLENGE: + proto_tree_add_item(tlv_tree, hf_mle_tlv_challenge, payload_tvb, offset, tlv_len, ENC_NA); + proto_item_append_text(ti, " = %s)", tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, tlv_len)); + offset += tlv_len; + break; + + case MLE_TLV_RESPONSE: + proto_tree_add_item(tlv_tree, hf_mle_tlv_response, payload_tvb, offset, tlv_len, ENC_NA); + proto_item_append_text(ti, " = %s)", tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, tlv_len)); + offset += tlv_len; + break; + + case MLE_TLV_LINK_LAYER_FRAME_COUNTER: + case MLE_TLV_MLE_FRAME_COUNTER: + if (tlv_len != 4) { + /* TLV Length must be 4 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint32 cntr; + + if (tlv_type == MLE_TLV_LINK_LAYER_FRAME_COUNTER) { + proto_tree_add_item_ret_uint(tlv_tree, hf_mle_tlv_ll_frm_cntr, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN, &cntr); + } else { + proto_tree_add_item_ret_uint(tlv_tree, hf_mle_tlv_mle_frm_cntr, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN, &cntr); + } + proto_item_append_text(ti, " = %u", cntr); + } + proto_item_append_text(ti, ")"); + offset += tlv_len; + break; + + case MLE_TLV_LINK_QUALITY: + { + guint numNeighbors; + guint8 size = tvb_get_guint8(payload_tvb, offset) & LQI_FLAGS_SIZE; + proto_tree *neig_tree; + proto_tree_add_item(tlv_tree, hf_mle_tlv_lqi_c, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_mle_tlv_lqi_size, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + if ((tlv_len - 1) % (size + 3)) { + expert_add_info(pinfo, proto_root, &ei_mle_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + numNeighbors = 0; + } else { + numNeighbors = (tlv_len - 1) / (size + 3); + } + + if (numNeighbors == 0) { + proto_item_append_text(ti, ")"); + } else if (numNeighbors == 1) { + proto_item_append_text(ti, ": 1 Neighbor)"); + } else { + proto_item_append_text(ti, ": %d Neighbors)", numNeighbors); + } + + /* Add subtrees */ + + //Size is off by 1 + size++; + + while (numNeighbors) { + ti = proto_tree_add_item(tlv_tree, hf_mle_tlv_neighbor, payload_tvb, offset, size+2, ENC_NA); + neig_tree = proto_item_add_subtree(ti, ett_mle_neighbor); + + proto_tree_add_item(neig_tree, hf_mle_tlv_neighbor_flagI, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(neig_tree, hf_mle_tlv_neighbor_flagO, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(neig_tree, hf_mle_tlv_neighbor_flagP, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + proto_tree_add_item(neig_tree, hf_mle_tlv_neighbor_idr, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + proto_tree_add_item(neig_tree, hf_mle_tlv_neighbor_addr, payload_tvb, offset,size, ENC_NA); + offset += size; + + numNeighbors--; + } + } + break; + + case MLE_TLV_NETWORK_PARAMETER: + { + guint8 param_id = tvb_get_guint8(payload_tvb, offset); + + proto_item_append_text(ti, " = %s)", val_to_str(param_id, mle_tlv_nwk_param_vals, "Unknown (%d)")); + + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_param_id, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_delay, payload_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + + switch (param_id) { + case NETWORK_PARAM_ID_CHANNEL: + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_channel, payload_tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case NETWORK_PARAM_ID_PAN_ID: + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_pan_id, payload_tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + break; + case NETWORK_PARAM_ID_PERMIT_JOIN: + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_pmt_join, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + break; + case NETWORK_PARAM_ID_BCN_PAYLOAD: + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_bcn_payload, payload_tvb, offset, tlv_len-5, ENC_NA); + offset += tlv_len-5; + break; + default: + proto_tree_add_item(tlv_tree, hf_mle_tlv_network_unknown, payload_tvb, offset, tlv_len-5, ENC_NA); + offset += tlv_len-5; + break; + } + } + break; + + case MLE_TLV_ROUTE64: + { + proto_tree *rtr_tree; + guint i, j; + guint8 count; + guint64 id_mask, test_mask; + + proto_item_append_text(ti, ")"); + proto_tree_add_item(tlv_tree, hf_mle_tlv_route64_id_seq, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Count number of table entries */ + count = 0; + for (i = 0; i < 8; i++) { /* TODO magic - number of routers/8 */ + guint8 id_mask_octet = tvb_get_guint8(payload_tvb, offset + i); + for (j = 0; j < 8; j++) { + if (id_mask_octet & (1 << j)) { + count++; + } + } + } + + /* + * | | | | | | | | | | |1|1|1|1|1|1|...|6| + * |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|...|3| + * --------------------------------------- + * |1|0|1|1|1|0|0|0|1|1|0|0|0|1|0|1|... + * + * is sent as 0xb8, 0xc5 + * and represents table entry for routers 0, 2, 3, 4, 8, 9, 13, 15... + */ + /* Get the ID mask as a 64-bit number (BE) */ + id_mask = tvb_get_ntoh64(payload_tvb, offset); + + /* Just show the string of octets - best representation for a bit mask */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_route64_id_mask, payload_tvb, offset, 8, ENC_NA); + offset += 8; + + if (count != (tlv_len - 9)) + { + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += (tlv_len - 9); + } else { + /* Add subtrees */ + for (i = 0; i < count; i++) { + /* Find first bit set */ + for (j = 0, test_mask = (G_GUINT64_CONSTANT(1) << 63); test_mask != 1; test_mask >>= 1, j++) { + if (test_mask & id_mask) { + id_mask &= ~test_mask; + break; + } + } + ti = proto_tree_add_item(tlv_tree, hf_mle_tlv_route64_entry, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_item_append_text(ti, " (%d)", j); + rtr_tree = proto_item_add_subtree(ti, ett_mle_router); + + proto_tree_add_item(rtr_tree, hf_mle_tlv_route64_nbr_out, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(rtr_tree, hf_mle_tlv_route64_nbr_in, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(rtr_tree, hf_mle_tlv_route64_cost, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + } + break; + + case MLE_TLV_ADDRESS16: + if (tlv_len != 2) { + /* TLV Length must be 2 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint16 addr16 = tvb_get_ntohs(payload_tvb, offset); + proto_item_append_text(ti, " = "); + { + guint8 a16_len = 2; /* Fix it at 2 */ + guint stroffset = offset; + + while (a16_len) { + guint8 a16_data; + a16_data = tvb_get_guint8(payload_tvb, stroffset); + proto_item_append_text(ti, "%02x", a16_data); + if (--a16_len) { + proto_item_append_text(ti, ":"); + } + stroffset++; + } + } + proto_tree_add_item(tlv_tree, hf_mle_tlv_addr16, payload_tvb, offset, 2, ENC_NA); + if (original_packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) { + /* Allocated Address16 TLV: use this to update dst/long mapping */ + ieee802154_addr_update(&ieee802154_map, addr16, original_packet->dst_pan, original_packet->dst64, pinfo->current_proto, pinfo->fd->num); + } + } + proto_item_append_text(ti, ")"); + offset += tlv_len; + break; + + case MLE_TLV_LEADER_DATA: + proto_item_append_text(ti, ")"); + if (tlv_len != 8) { + /* TLV Length must be 8 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + proto_tree_add_item(tlv_tree, hf_mle_tlv_leader_data_partition_id, payload_tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(tlv_tree, hf_mle_tlv_leader_data_weighting, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_leader_data_version, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_leader_data_stable_version, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_leader_data_router_id, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + break; + + case MLE_TLV_NETWORK_DATA: + { + tvbuff_t *sub_tvb; + proto_item_append_text(ti, ")"); + if (tlv_len > 0) { + sub_tvb = tvb_new_subset_length(payload_tvb, offset, tlv_len); + call_dissector(thread_nwd_handle, sub_tvb, pinfo, tlv_tree); + } + offset += tlv_len; + } + break; + + case MLE_TLV_ACTIVE_OP_DATASET: + case MLE_TLV_PENDING_OP_DATASET: + case MLE_TLV_THREAD_DISCOVERY: + { + tvbuff_t *sub_tvb; + proto_item_append_text(ti, ")"); + if (tlv_len > 0) { + sub_tvb = tvb_new_subset_length(payload_tvb, offset, tlv_len); + call_dissector(thread_mc_handle, sub_tvb, pinfo, tlv_tree); + } + offset += tlv_len; + } + break; + + case MLE_TLV_TLV_REQUEST: + proto_item_append_text(ti, ")"); + while (tlv_len) { + proto_tree_add_item(tlv_tree, hf_mle_tlv_type, payload_tvb, offset, 1, ENC_NA); + offset++; + tlv_len--; + } + break; + + case MLE_TLV_SCAN_MASK: + if (tlv_len != 1) { + /* TLV Length must be 1 */ + proto_item_append_text(ti, ")"); + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint8 mask; + + mask = tvb_get_guint8(payload_tvb, offset); + proto_item_append_text(ti, " = %02x)", mask); + proto_tree_add_item(tlv_tree, hf_mle_tlv_scan_mask_r, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_mle_tlv_scan_mask_e, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + } + offset += tlv_len; + break; + + case MLE_TLV_CONNECTIVITY: + if (tlv_len == 7) { + /* 1.0 style - keep in for now */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_max_child_cnt, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_child_cnt, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq3, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq2, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq1, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_leader_cost, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_id_seq, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } else if (tlv_len == 10) { + /* 1.1 style */ + proto_tree *fl_tree; + + ti = proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_flags, payload_tvb, offset, 1, ENC_NA); + fl_tree = proto_item_add_subtree(ti, ett_mle_conn_flg); + proto_tree_add_item(fl_tree, hf_mle_tlv_conn_flags_pp, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq3, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq2, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_lq1, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_leader_cost, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_id_seq, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_active_rtrs, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_sed_buf_size, payload_tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(tlv_tree, hf_mle_tlv_conn_sed_dgram_cnt, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } else { + /* TLV Length must be 7 (old style) or 10 */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + proto_item_append_text(ti, ")"); + break; + + case MLE_TLV_LINK_MARGIN: + if (tlv_len != 1) { + /* TLV Length must be 1 */ + proto_item_append_text(ti, ")"); + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint8 link_margin; + + link_margin = tvb_get_guint8(payload_tvb, offset); + proto_item_append_text(ti, " = %udB)", link_margin); + proto_tree_add_item(tlv_tree, hf_mle_tlv_link_margin, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + break; + + case MLE_TLV_STATUS: + if (tlv_len != 1) { + /* TLV Length must be 1 */ + proto_item_append_text(ti, ")"); + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint8 stat; + + stat = tvb_get_guint8(payload_tvb, offset); + proto_item_append_text(ti, " = %d)", stat); + proto_tree_add_item(tlv_tree, hf_mle_tlv_status, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + break; + + case MLE_TLV_VERSION: + if (tlv_len != 2) { + /* TLV Length must be 2 */ + proto_item_append_text(ti, ")"); + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + guint16 version; + + version = tvb_get_ntohs(payload_tvb, offset); + proto_item_append_text(ti, " = %d)", version); + proto_tree_add_item(tlv_tree, hf_mle_tlv_version, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + break; + + case MLE_TLV_ADDRESS_REGISTRATION: + { + guint8 iid_type, i; + guint8 entries = 0; + guint8 check_len = tlv_len; + guint8 check_offset = offset; + + /* Check consistency of entries */ + while (check_len > 0) { + guint8 ar_len; + + iid_type = tvb_get_guint8(payload_tvb, check_offset); + if (iid_type & ADDR_REG_MASK_IID_TYPE_MASK) { + ar_len = 9; + } else { + ar_len = 17; + } + check_offset += ar_len; + check_len -= ar_len; + entries++; + } + + proto_item_append_text(ti, ")"); + if (check_len != 0) { + /* Not an integer number of entries */ + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + for (i = 0; i < entries; i++) { + proto_tree *ar_tree; + + ti = proto_tree_add_item(tlv_tree, hf_mle_tlv_addr_reg_entry, payload_tvb, offset, 1, ENC_NA); + ar_tree = proto_item_add_subtree(ti, ett_mle_addr_reg); + iid_type = tvb_get_guint8(payload_tvb, offset); + if (iid_type & ADDR_REG_MASK_IID_TYPE_MASK) { + proto_tree_add_item(ar_tree, hf_mle_tlv_addr_reg_iid_type, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(ar_tree, hf_mle_tlv_addr_reg_cid, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(ar_tree, hf_mle_tlv_addr_reg_iid, payload_tvb, offset, 8, ENC_NA); + offset += 8; + } else { + proto_tree_add_item(ar_tree, hf_mle_tlv_addr_reg_iid_type, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(ar_tree, hf_mle_tlv_addr_reg_ipv6, payload_tvb, offset, 16, ENC_NA); + offset += 16; + } + } + } + } + break; + + case MLE_TLV_CHANNEL: + { + proto_item_append_text(ti, ")"); + + /* Check length is consistent */ + if (tlv_len != 3) { + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + /* Channel page */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_channel_page, payload_tvb, offset, 1, ENC_BIG_ENDIAN); + /* Channel */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_channel, payload_tvb, offset+1, 2, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case MLE_TLV_PAN_ID: + { + proto_item_append_text(ti, ")"); + + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + /* PAN ID */ + proto_tree_add_item(tlv_tree, hf_mle_tlv_pan_id, payload_tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case MLE_TLV_ACTIVE_TSTAMP: + case MLE_TLV_PENDING_TSTAMP: + { + nstime_t timestamp; + + proto_item_append_text(ti, ")"); + + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_mle_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + } else { + /* Fill in the nstime_t structure */ + timestamp.secs = (time_t)tvb_get_ntoh48(payload_tvb, offset); + timestamp.nsecs = (int)lround((double)(tvb_get_ntohs(payload_tvb, offset + 6) >> 1) * MLE_32768_TO_NSEC_FACTOR); + if (tlv_type == MLE_TLV_ACTIVE_TSTAMP) { + proto_tree_add_time(tlv_tree, hf_mle_tlv_active_tstamp, payload_tvb, offset, 8, ×tamp); + } else { + proto_tree_add_time(tlv_tree, hf_mle_tlv_pending_tstamp, payload_tvb, offset, 8, ×tamp); + } + } + offset += tlv_len; + } + break; + + default: + proto_item_append_text(ti, ")"); + proto_tree_add_item(tlv_tree, hf_mle_tlv_unknown, payload_tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + } + + return tvb_captured_length(tvb); +} + +void +proto_register_mle(void) +{ + static hf_register_info hf[] = { + + /* Auxiliary Security Header Fields */ + /*----------------------------------*/ + { &hf_mle_security_suite, + { "Security Suite", + "wpan.aux_sec.sec_suite", + FT_UINT8, BASE_HEX, VALS(mle_sec_suite_names), 0x0, + "The Security Suite of the frame", + HFILL + } + }, + + { &hf_mle_mic, + { "Decrypted MIC", + "mle.mic", + FT_BYTES, BASE_NONE, NULL, 0x0, + "The decrypted MIC", + HFILL + } + }, + + /*MLE Command*/ + { &hf_mle_command, + { "Command", + "mle.cmd", + FT_UINT8, BASE_DEC, VALS(mle_command_vals), 0x0, + "MLE command type", + HFILL + } + }, + + /* Generic TLV */ + { &hf_mle_tlv, + { "TLV", + "mle.tlv", + FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", + HFILL + } + }, + + { &hf_mle_tlv_type, + { "Type", + "mle.tlv.type", + FT_UINT8, BASE_DEC, VALS(mle_tlv_vals), 0x0, + "Type of value", + HFILL + } + }, + + { &hf_mle_tlv_length, + { "Length", + "mle.tlv.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of value", + HFILL + } + }, + + /* Type-Specific TLV Fields */ + { &hf_mle_tlv_source_addr, + { "Address", + "mle.tlv.source_addr", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Source address", + HFILL + } + }, + + /* Capability Information Fields */ + { &hf_mle_tlv_mode_nwk_data, + { "Network Data", + "mle.tlv.mode.nwk_data", + FT_BOOLEAN, 8, TFS(&mle_tlv_mode_nwk_data), MLE_CMD_CINFO_NWK_DATA, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_mode_device_type, + { "Device Type", + "mle.tlv.mode.device_type", + FT_BOOLEAN, 8, TFS(&mle_tlv_mode_device_type), IEEE802154_CMD_CINFO_DEVICE_TYPE, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_mode_sec_data_req, + { "Secure Data Requests", + "mle.tlv.mode.sec_data_req", + FT_BOOLEAN, 8, NULL, MLE_CMD_CINFO_SEC_DATA_REQ, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_mode_idle_rx, + { "Receive On When Idle", + "mle.tlv.mode.idle_rx", + FT_BOOLEAN, 8, NULL, IEEE802154_CMD_CINFO_IDLE_RX, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_timeout, + { "Timeout", + "mle.tlv.timeout", + FT_UINT32, BASE_DEC, NULL, 0x0, + "Expected interval between transmissions in seconds", + HFILL + } + }, + + { &hf_mle_tlv_challenge, + { "Challenge", + "mle.tlv.challenge", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Challenge to be echoed back", + HFILL + } + }, + + { &hf_mle_tlv_response, + { "Response", + "mle.tlv.response", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Response to a challenge", + HFILL + } + }, + + { &hf_mle_tlv_ll_frm_cntr, + { "Link Layer Frame Counter", + "mle.tlv.ll_frm_cntr", + FT_UINT32, BASE_DEC, NULL, 0x0, + "The Link layer frame counter", + HFILL + } + }, + + { &hf_mle_tlv_mle_frm_cntr, + { "MLE Frame Counter", + "mle.tlv.mle_frm_cntr", + FT_UINT32, BASE_DEC, NULL, 0x0, + "The MLE frame counter", + HFILL + } + }, + + { &hf_mle_tlv_unknown, + { "Unknown", + "mle.tlv.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", + HFILL + } + }, + + { &hf_mle_tlv_lqi_c, + { "Complete Flag", + "mle.tlv.lqi.complete", + FT_BOOLEAN, 8, NULL, LQI_FLAGS_C, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_lqi_size, + { "Address Size", + "mle.tlv.lqi.size", + FT_UINT8, BASE_DEC, NULL, LQI_FLAGS_SIZE, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_neighbor, + { "Neighbor Record", + "mle.tlv.neighbor", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_neighbor_flagI, + { "Incoming", + "mle.tlv.neighbor.flagI", + FT_BOOLEAN, 8, NULL, NEIGHBOR_FLAG_I, + "Set if the sender has configured its link with this neighbor and will accept incoming messages from them.", + HFILL + } + }, + + { &hf_mle_tlv_neighbor_flagO, + { "Outgoing", + "mle.tlv.neighbor.flagO", + FT_BOOLEAN, 8, NULL, NEIGHBOR_FLAG_O, + "Set if the sender believes that the neighbor has configured its link with the sender and will accept incoming messages from the sender.", + HFILL + } + }, + + { &hf_mle_tlv_neighbor_flagP, + { "Priority", + "mle.tlv.neighbor.flagP", + FT_BOOLEAN, 8, NULL, NEIGHBOR_FLAG_P, + "Set if the sender expects to use this link for sending messages to this neighbor.", + HFILL + } + }, + + { &hf_mle_tlv_neighbor_idr, + { "Inverse Delivery Ratio", + "mle.tlv.neighbor.idr", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_neighbor_addr, + { "Address", + "mle.tlv.neighbor.addr", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_param_id, + { "Parameter ID", + "mle.tlv.network.param_id", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_delay, + { "Delay", + "mle.tlv.network.delay", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_channel, + { "Channel", + "mle.tlv.network.channel", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_pan_id, + { "PAN ID", + "mle.tlv.network.pan_id", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_pmt_join, + { "Permit Join", + "mle.tlv.network.pmt_join", + FT_BOOLEAN, 8, NULL, 0x1, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_network_bcn_payload, + { "Beacon Payload", + "mle.tlv.network.bcn_payload", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_id_seq, + { "ID Sequence", + "mle.tlv.route64.id_seq", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_id_mask, + { "Assigned Router ID Mask", + "mle.tlv.route64.id_mask", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_entry, + { "Routing Table Entry", + "mle.tlv.route64", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_nbr_out, + { "Neighbor Out Link Quality", + "mle.tlv.route64.nbr_out", + FT_UINT8, BASE_DEC, NULL, ROUTE_TBL_OUT_MASK, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_nbr_in, + { "Neighbor In Link Quality", + "mle.tlv.route64.nbr_in", + FT_UINT8, BASE_DEC, NULL, ROUTE_TBL_IN_MASK, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_route64_cost, + { "Router Cost", + "mle.tlv.route64.cost", + FT_UINT8, BASE_DEC, NULL, ROUTE_TBL_COST_MASK, + NULL, + HFILL + } + }, +#if 0 + { &hf_mle_tlv_route64_unknown, + { "(unknown)", + "mle.tlv.route64.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, +#endif + { &hf_mle_tlv_addr16, + { "Address16", + "mle.tlv.addr16", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_leader_data_partition_id, + { "Partition ID", + "mle.tlv.leader_data.partition_id", + FT_UINT32, BASE_HEX, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_leader_data_weighting, + { "Weighting", + "mle.tlv.leader_data.weighting", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_leader_data_version, + { "Data Version", + "mle.tlv.leader_data.data_version", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_leader_data_stable_version, + { "Stable Data Version", + "mle.tlv.leader_data.stable_data_version", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_leader_data_router_id, + { "Leader Router ID", + "mle.tlv.leader_data.router_id", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, +#if 0 + { &hf_mle_tlv_network_data, + { "Network Data", + "mle.tlv.network_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Network data (opaque data)", + HFILL + } + }, +#endif + { &hf_mle_tlv_scan_mask_r, + { "Router", + "mle.tlv.scan_mask.r", + FT_BOOLEAN, 8, NULL, SCAN_MASK_R_MASK, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_scan_mask_e, + { "End Device", + "mle.tlv.scan_mask.e", + FT_BOOLEAN, 8, NULL, SCAN_MASK_D_MASK, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_max_child_cnt, + { "Max Child Count", + "mle.tlv.conn.max_child_cnt", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_child_cnt, + { "Child Count", + "mle.tlv.conn.child_cnt", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_flags, + { "Flags", + "mle.tlv.conn.flags", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_flags_pp, + { "Parent Priority", + "mle.tlv.conn.flags.pp", + FT_INT8, BASE_DEC, VALS(mle_conn_tlv_flags_pp_enums), CONN_MASK_FLAGS_PP_MASK, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_lq3, + { "Link Quality 3", + "mle.tlv.conn.lq3", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_lq2, + { "Link Quality 2", + "mle.tlv.conn.lq2", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_lq1, + { "Link Quality 1", + "mle.tlv.conn.lq1", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_leader_cost, + { "Leader Cost", + "mle.tlv.conn.leader_cost", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_id_seq, + { "ID Sequence", + "mle.tlv.conn.id_seq", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_active_rtrs, + { "Active Routers", + "mle.tlv.conn.active_rtrs", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_sed_buf_size, + { "SED Buffer Size", + "mle.tlv.conn.sed_buf_size", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_conn_sed_dgram_cnt, + { "SED Datagram Count", + "mle.tlv.conn.sed_dgram_cnt", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_link_margin, + { "Link Margin", + "mle.tlv.link_margin", + FT_UINT8, BASE_DEC, NULL, 0, + "Link margin in dB", + HFILL + } + }, + + { &hf_mle_tlv_status, + { "Status", + "mle.tlv.status", + FT_UINT8, BASE_DEC, VALS(mle_status_tlv_enums), 0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_version, + { "Version", + "mle.tlv.version", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_addr_reg_entry, + { "Address Registration Entry", + "mle.tlv.addr_reg", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_addr_reg_iid_type, + { "IID type", + "mle.tlv.addr_reg_iid_type", + FT_BOOLEAN, 8, TFS(&mle_tlv_addr_reg_iid_type), ADDR_REG_MASK_IID_TYPE_MASK, + "Context ID", + HFILL + } + }, + + { &hf_mle_tlv_addr_reg_cid, + { "Context ID", + "mle.tlv.addr_reg_cid", + FT_UINT8, BASE_DEC, NULL, ADDR_REG_MASK_CID_MASK, + "6LoWPAN Context ID", + HFILL + } + }, + + { &hf_mle_tlv_addr_reg_iid, + { "IID", + "mle.tlv.addr_reg_iid", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Interface identifier", + HFILL + } + }, + + { &hf_mle_tlv_addr_reg_ipv6, + { "IPv6 Address", + "mle.tlv.addr_reg_ipv6", + FT_IPv6, BASE_NONE, NULL, 0x0, + "IID", + HFILL + } + }, +#if 0 + { &hf_mle_tlv_hold_time, + { "Hold Time", + "mle.tlv.hold_time", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, + HFILL + } + }, +#endif + { &hf_mle_tlv_network_unknown, + { "(unknown)", + "mle.tlv.network.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_channel_page, + { "Channel Page", + "mle.tlv.channel_page", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_channel, + { "Channel", + "mle.tlv.channel", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_pan_id, + { "PAN ID", + "mle.tlv.pan_id", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_active_tstamp, + { "Active Timestamp", + "mle.tlv.active_tstamp", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, + NULL, + HFILL + } + }, + + { &hf_mle_tlv_pending_tstamp, + { "Pending Timestamp", + "mle.tlv.pending_tstamp", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, + NULL, + HFILL + } + }, +#if 0 + { &hf_mle_tlv_active_op_dataset, + { "Active Operational Dataset", + "mle.tlv.active_op_dataset", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Thread Active Operational Dataset", + HFILL + } + }, + { &hf_mle_tlv_pending_op_dataset, + { "Pending Operational Dataset", + "mle.tlv.active_op_dataset", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Thread Pending Operational Dataset", + HFILL + } + }, +#endif + }; + + static gint *ett[] = { + &ett_mle, + &ett_mle_auxiliary_security, + &ett_mle_aux_sec_control, + &ett_mle_aux_sec_key_id, + &ett_mle_tlv, + &ett_mle_neighbor, + &ett_mle_router, + &ett_mle_addr_reg, + &ett_mle_conn_flg, + &ett_mle_thread_nwd +}; + + static ei_register_info ei[] = { + { &ei_mle_cbc_mac_failed, { "mle.cbc_mac_failed", PI_UNDECODED, PI_WARN, "Call to ccm_cbc_mac() failed", EXPFILL }}, + { &ei_mle_packet_too_small, { "mle.packet_too_small", PI_UNDECODED, PI_WARN, "Packet was too small to include the CRC and MIC", EXPFILL }}, + { &ei_mle_no_key, { "mle.no_key", PI_UNDECODED, PI_WARN, "No encryption key set - can't decrypt", EXPFILL }}, + { &ei_mle_decrypt_failed, { "mle.decrypt_failed", PI_UNDECODED, PI_WARN, "Decrypt failed", EXPFILL }}, + { &ei_mle_mic_check_failed, { "mle.mic_check_failed", PI_UNDECODED, PI_WARN, "MIC check failed", EXPFILL }}, + { &ei_mle_tlv_length_failed, { "mle.tlv_length_failed", PI_UNDECODED, PI_WARN, "TLV Length inconsistent", EXPFILL }}, + { &ei_mle_len_size_mismatch, { "mle.len_size_mismatch", PI_UNDECODED, PI_WARN, "TLV Length & Size field disagree", EXPFILL }}, + }; + + module_t *mle_module; + expert_module_t* expert_mle; + + proto_mle = proto_register_protocol("Mesh Link Establishment", "MLE", "mle"); + proto_register_field_array(proto_mle, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_mle = expert_register_protocol(proto_mle); + expert_register_field_array(expert_mle, ei, array_length(ei)); + + mle_handle = register_dissector("mle", dissect_mle, proto_mle); + + mle_module = prefs_register_protocol(proto_mle, NULL); + + prefs_register_bool_preference(mle_module, "meshlink_mic_ok", + "Dissect only good MIC", + "Dissect payload only if MIC is valid.", + &mle_mic_ok); + + /* setup registration for other dissectors to provide mle key hash algorithms */ + mle_key_hash_handlers = wmem_tree_new(wmem_epan_scope()); +} + +void +proto_reg_handoff_mle(void) +{ + thread_nwd_handle = find_dissector_add_dependency("thread_nwd", proto_mle); + thread_mc_handle = find_dissector_add_dependency("thread_meshcop", proto_mle); + + //heur_dissector_add("stun", dissect_embeddedmle_heur, proto_mle); + + dissector_add_uint_range_with_preference("udp.port", UDP_PORT_MLE_RANGE, mle_handle); + + proto_ieee802154 = proto_get_id_by_filter_name(IEEE802154_PROTOABBREV_WPAN); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab + * :indentSize=2:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-mle.h b/epan/dissectors/packet-mle.h new file mode 100644 index 0000000000..0029484166 --- /dev/null +++ b/epan/dissectors/packet-mle.h @@ -0,0 +1,32 @@ +/* packet-mle.h + * Routines for MLE packet dissection + * + * 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. + */ + +#ifndef __PACKET_MLE_H__ +#define __PACKET_MLE_H__ + +#include "packet-ieee802154.h" + +typedef gboolean (*mle_set_mle_key_func) (ieee802154_packet * packet, unsigned char* key, unsigned char* alt_key, ieee802154_key_t* uat_key); +extern void register_mle_key_hash_handler(guint hash_identifier, mle_set_mle_key_func key_func); + +#endif diff --git a/epan/dissectors/packet-openthread.c b/epan/dissectors/packet-openthread.c new file mode 100644 index 0000000000..26088f0085 --- /dev/null +++ b/epan/dissectors/packet-openthread.c @@ -0,0 +1,113 @@ +/* packet-openthread.c + * Simple dissector for OpenThread loopback interface + * + * Robert Cragie <robert.cragie@arm.com> + * + * 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. + */ + +#include "config.h" + +#include <epan/packet.h> + +/* Forward declarations */ +void proto_register_openthread(void); +void proto_reg_handoff_openthread(void); + +static int proto_openthread = -1; + +static dissector_handle_t openthread_handle; +static dissector_handle_t wpan_handle; + +static int hf_openthread_channel = -1; +/* static int hf_openthread_psdu = -1; */ + +static gint ett_openthread = -1; + +static int +dissect_openthread(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *proto_root; + proto_tree *openthread_tree; + tvbuff_t *sub_tvb; + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_openthread, tvb, 0, -1, ENC_NA); + openthread_tree = proto_item_add_subtree(proto_root, ett_openthread); + + proto_tree_add_item(openthread_tree, hf_openthread_channel, tvb, 0, 1, ENC_NA); + sub_tvb = tvb_new_subset_length(tvb, 1, tvb_reported_length_remaining(tvb, 3)); /* Note - truncate the last two "phoney" CRC bytes */ + call_dissector(wpan_handle, sub_tvb, pinfo, tree); + + return tvb_captured_length(tvb); +} + +void +proto_register_openthread(void) +{ + static hf_register_info hf[] = { + + /* Generic TLV */ + { &hf_openthread_channel, + { "Channel", + "openthread.channel", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, +#if 0 + { &hf_openthread_psdu, + { "PSDU", + "openthread.psdu", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + } +#endif + }; + + static gint *ett[] = { + &ett_openthread + }; + + proto_openthread = proto_register_protocol("OpenThread", "OpenThread", "openthread"); + proto_register_field_array(proto_openthread, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + openthread_handle = register_dissector("openthread", dissect_openthread, proto_openthread); +} + +void +proto_reg_handoff_openthread(void) +{ + wpan_handle = find_dissector_add_dependency("wpan_nofcs", proto_openthread); + dissector_add_for_decode_as("udp.port", openthread_handle); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-thread.c b/epan/dissectors/packet-thread.c new file mode 100644 index 0000000000..86e7845674 --- /dev/null +++ b/epan/dissectors/packet-thread.c @@ -0,0 +1,3470 @@ +/* packet-thread.c + * Routines for Thread CoAP and beacon packet dissection + * + * Robert Cragie <robert.cragie@arm.com> + * + * 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. + */ + +#include "config.h" +#include <glib.h> +#include <stdlib.h> +#include <math.h> +#include <epan/packet.h> +#include <epan/conversation.h> +#include <epan/proto_data.h> +#include <epan/wmem/wmem.h> +#include <epan/expert.h> +#include <epan/range.h> +#include <epan/oui.h> +#include <epan/prefs.h> +#include <epan/strutil.h> +#include <epan/to_str.h> +#include "packet-coap.h" +#include "packet-ieee802154.h" +#include "packet-mle.h" + +/* Use libgcrypt for cipher libraries. */ +#include <wsutil/wsgcrypt.h> + +/* Forward declarations */ +void proto_register_thread_coap(void); +void proto_reg_handoff_thread_coap(void); + +void proto_register_thread_address(void); +void proto_reg_handoff_thread_address(void); + +void proto_register_thread_dg(void); +void proto_reg_handoff_thread_dg(void); + +void proto_register_thread_mc(void); +void proto_reg_handoff_thread_mc(void); + +void proto_register_thread_nwd(void); + +void proto_register_thread_bcn(void); +void proto_reg_handoff_thread_bcn(void); + +void proto_register_thread(void); +void proto_reg_handoff_thread(void); + +static int proto_thread_address = -1; +static int proto_thread_dg = -1; +static int proto_thread_mc = -1; +static int proto_thread_nwd = -1; +static int proto_thread_coap = -1; +static int proto_thread_bcn = -1; +static int proto_thread = -1; +static int proto_coap = -1; + +/* Header fields */ + +/* Thread address */ + +static int hf_thread_address_tlv = -1; +static int hf_thread_address_tlv_type = -1; +static int hf_thread_address_tlv_length = -1; +static int hf_thread_address_tlv_unknown = -1; +/* static int hf_thread_address_tlv_sub_tlvs = -1; */ + +/* Target EID TLV fields */ +static int hf_thread_address_tlv_target_eid = -1; + +/* Ext. MAC address TLV fields */ +static int hf_thread_address_tlv_ext_mac_addr = -1; + +/* RLOC16 TLV fields */ +static int hf_thread_address_tlv_rloc16 = -1; + +/* Mesh Local IID TLV fields */ +static int hf_thread_address_tlv_ml_eid = -1; + +/* Status TLV fields */ +static int hf_thread_address_tlv_status = -1; + +/* Attached time TLV fields */ +/* static int hf_thread_address_tlv_attached_time = -1; */ + +/* Last transaction time TLV fields */ +static int hf_thread_address_tlv_last_transaction_time = -1; + +/* Router Mask TLV fields */ +static int hf_thread_address_tlv_router_mask_id_seq = -1; +static int hf_thread_address_tlv_router_mask_assigned = -1; + +/* ND option fields */ +static int hf_thread_address_tlv_nd_option = -1; + +/* ND data fields */ +static int hf_thread_address_tlv_nd_data = -1; + +/* Thread diagnostics */ + +static int hf_thread_dg_tlv = -1; +static int hf_thread_dg_tlv_type = -1; +static int hf_thread_dg_tlv_length8 = -1; +static int hf_thread_dg_tlv_length16 = -1; +static int hf_thread_dg_tlv_general = -1; +static int hf_thread_dg_tlv_unknown = -1; + +#if 0 +/**** TBC: will be added later. For now, just use general string ****/ +static int hf_thread_dg_tlv_source_addr = -1; +static int hf_thread_dg_tlv_mode_device_type = -1; +static int hf_thread_dg_tlv_mode_idle_rx = -1; +static int hf_thread_dg_tlv_mode_sec_data_req = -1; +static int hf_thread_dg_tlv_mode_nwk_data = -1; +static int hf_thread_dg_tlv_timeout = -1; +static int hf_thread_dg_tlv_lqi_c = -1; +static int hf_thread_dg_tlv_lqi_size = -1; +static int hf_thread_dg_tlv_neighbor = -1; +static int hf_thread_dg_tlv_neighbor_flagI = -1; +static int hf_thread_dg_tlv_neighbor_flagO = -1; +static int hf_thread_dg_tlv_neighbor_flagP = -1; +static int hf_thread_dg_tlv_neighbor_idr = -1; +static int hf_thread_dg_tlv_neighbor_addr = -1; +static int hf_thread_dg_tlv_network_param_id = -1; +static int hf_thread_dg_tlv_network_delay = -1; +static int hf_thread_dg_tlv_network_channel = -1; +static int hf_thread_dg_tlv_network_pan_id = -1; +static int hf_thread_dg_tlv_network_pmt_join = -1; +static int hf_thread_dg_tlv_network_bcn_payload = -1; +static int hf_thread_dg_tlv_network_unknown = -1; +static int hf_thread_dg_tlv_mle_frm_cntr = -1; +static int hf_thread_dg_tlv_route_tbl_id_seq = -1; +static int hf_thread_dg_tlv_route_tbl_id_mask = -1; +static int hf_thread_dg_tlv_route_tbl_entry = -1; +static int hf_thread_dg_tlv_route_tbl_nbr_out = -1; +static int hf_thread_dg_tlv_route_tbl_nbr_in = -1; +static int hf_thread_dg_tlv_route_tbl_cost = -1; +static int hf_thread_dg_tlv_route_tbl_unknown = -1; +static int hf_thread_dg_tlv_addr_16 = -1; +static int hf_thread_dg_tlv_leader_data_partition_id = -1; +static int hf_thread_dg_tlv_leader_data_weighting = -1; +static int hf_thread_dg_tlv_leader_data_version = -1; +static int hf_thread_dg_tlv_leader_data_stable_version = -1; +static int hf_thread_dg_tlv_leader_data_router_id = -1; +static int hf_thread_dg_tlv_network_data = -1; +static int hf_thread_dg_tlv_scan_mask_r = -1; +static int hf_thread_dg_tlv_scan_mask_e = -1; +static int hf_thread_dg_tlv_conn_max_child_cnt = -1; +static int hf_thread_dg_tlv_conn_child_cnt = -1; +static int hf_thread_dg_tlv_conn_lq3 = -1; +static int hf_thread_dg_tlv_conn_lq2 = -1; +static int hf_thread_dg_tlv_conn_lq1 = -1; +static int hf_thread_dg_tlv_conn_leader_cost = -1; +static int hf_thread_dg_tlv_conn_id_seq = -1; +static int hf_thread_dg_tlv_link_margin = -1; +static int hf_thread_dg_tlv_status = -1; +static int hf_thread_dg_tlv_version = -1; +static int hf_thread_dg_tlv_addr_reg_entry = -1; +static int hf_thread_dg_tlv_addr_reg_iid_type = -1; +static int hf_thread_dg_tlv_addr_reg_cid = -1; +static int hf_thread_dg_tlv_addr_reg_iid = -1; +static int hf_thread_dg_tlv_addr_reg_ipv6 = -1; +static int hf_thread_dg_tlv_hold_time = -1; +#endif + +/* Thread MeshCoP */ + +static int hf_thread_mc_tlv = -1; +static int hf_thread_mc_tlv_type = -1; +static int hf_thread_mc_tlv_length8 = -1; +static int hf_thread_mc_tlv_length16 = -1; +static int hf_thread_mc_tlv_unknown = -1; +/* static int hf_thread_mc_tlv_sub_tlvs = -1; */ + +/* Channel TLV fields */ +static int hf_thread_mc_tlv_channel_page = -1; +static int hf_thread_mc_tlv_channel = -1; + +/* PAN ID TLV fields */ +static int hf_thread_mc_tlv_pan_id = -1; + +/* Extended PAN ID TLV fields */ +static int hf_thread_mc_tlv_xpan_id = -1; + +/* Network Name TLV fields */ +static int hf_thread_mc_tlv_net_name = -1; + +/* PSKc TLV fields */ +static int hf_thread_mc_tlv_pskc = -1; + +/* Master Key TLV fields */ +static int hf_thread_mc_tlv_master_key = -1; + +/* Network Key Sequence TLV fields */ +static int hf_thread_mc_tlv_net_key_seq_ctr = -1; + +/* Mesh Local ULA TLV fields */ +static int hf_thread_mc_tlv_ml_prefix = -1; + +/* Steering Data TLV fields */ +static int hf_thread_mc_tlv_steering_data = -1; + +/* Border Agent Locator TLV fields */ +static int hf_thread_mc_tlv_ba_locator = -1; + +/* Commissioner ID TLV fields */ +static int hf_thread_mc_tlv_commissioner_id = -1; + +/* Commissioner ID TLV fields */ +static int hf_thread_mc_tlv_commissioner_sess_id = -1; + +/* Security Policy TLV fields */ +static int hf_thread_mc_tlv_sec_policy_rot = -1; +static int hf_thread_mc_tlv_sec_policy_o = -1; +static int hf_thread_mc_tlv_sec_policy_n = -1; +static int hf_thread_mc_tlv_sec_policy_r = -1; +static int hf_thread_mc_tlv_sec_policy_c = -1; +static int hf_thread_mc_tlv_sec_policy_b = -1; + +/* State TLV fields */ +static int hf_thread_mc_tlv_state = -1; + +/* Timestamp TLV fields */ +static int hf_thread_mc_tlv_active_tstamp = -1; +static int hf_thread_mc_tlv_pending_tstamp = -1; + +/* Delay Timer TLV fields */ +static int hf_thread_mc_tlv_delay_timer = -1; + +/* UDP Encapsulation TLV fields */ +static int hf_thread_mc_tlv_udp_encap_src_port = -1; +static int hf_thread_mc_tlv_udp_encap_dst_port = -1; + +/* IPv6 Address fields */ +static int hf_thread_mc_tlv_ipv6_addr = -1; + +/* UDP Port TLV fields */ +static int hf_thread_mc_tlv_udp_port = -1; + +/* IID TLV fields */ +static int hf_thread_mc_tlv_iid = -1; + +/* Joiner Router locator TLV fields */ +static int hf_thread_mc_tlv_jr_locator = -1; + +/* KEK TLV fields */ +static int hf_thread_mc_tlv_kek = -1; + +/* Provisioning URL TLV fields */ +static int hf_thread_mc_tlv_provisioning_url = -1; + +/* Vendor TLV fields */ +static int hf_thread_mc_tlv_vendor_name = -1; +static int hf_thread_mc_tlv_vendor_model = -1; +static int hf_thread_mc_tlv_vendor_sw_ver = -1; +static int hf_thread_mc_tlv_vendor_data = -1; +static int hf_thread_mc_tlv_vendor_stack_ver_oui = -1; +static int hf_thread_mc_tlv_vendor_stack_ver_build = -1; +static int hf_thread_mc_tlv_vendor_stack_ver_rev = -1; +static int hf_thread_mc_tlv_vendor_stack_ver_min = -1; +static int hf_thread_mc_tlv_vendor_stack_ver_maj = -1; + +/* Channel Mask TLV fields */ +static int hf_thread_mc_tlv_chan_mask = -1; +static int hf_thread_mc_tlv_chan_mask_page = -1; +static int hf_thread_mc_tlv_chan_mask_len = -1; +static int hf_thread_mc_tlv_chan_mask_mask = -1; + +/* Count TLV fields */ +static int hf_thread_mc_tlv_count = -1; + +/* Period TLV fields */ +static int hf_thread_mc_tlv_period = -1; + +/* Period TLV fields */ +static int hf_thread_mc_tlv_scan_duration = -1; + +/* Energy List TLV fields */ +static int hf_thread_mc_tlv_energy_list = -1; +static int hf_thread_mc_tlv_el_count = -1; + +/* Discovery Request TLV fields */ +static int hf_thread_mc_tlv_discovery_req_ver = -1; +static int hf_thread_mc_tlv_discovery_req_j = -1; + +/* Discovery Response TLV fields */ +static int hf_thread_mc_tlv_discovery_rsp_ver = -1; +static int hf_thread_mc_tlv_discovery_rsp_n = -1; + +/* Thread Network Data */ + +static int hf_thread_nwd_tlv = -1; +static int hf_thread_nwd_tlv_type = -1; +static int hf_thread_nwd_tlv_stable = -1; +static int hf_thread_nwd_tlv_length = -1; +static int hf_thread_nwd_tlv_unknown = -1; +static int hf_thread_nwd_tlv_sub_tlvs = -1; + +/* Has Route TLV fields */ +static int hf_thread_nwd_tlv_has_route = -1; +static int hf_thread_nwd_tlv_has_route_br_16 = -1; +static int hf_thread_nwd_tlv_has_route_pref = -1; + +/* Prefix TLV fields */ +static int hf_thread_nwd_tlv_prefix = -1; +static int hf_thread_nwd_tlv_prefix_domain_id = -1; +static int hf_thread_nwd_tlv_prefix_length = -1; + +/* Border Router TLV fields */ +static int hf_thread_nwd_tlv_border_router = -1; +static int hf_thread_nwd_tlv_border_router_16 = -1; +static int hf_thread_nwd_tlv_border_router_pref = -1; +static int hf_thread_nwd_tlv_border_router_p = -1; +static int hf_thread_nwd_tlv_border_router_s = -1; +static int hf_thread_nwd_tlv_border_router_d = -1; +static int hf_thread_nwd_tlv_border_router_c = -1; +static int hf_thread_nwd_tlv_border_router_r = -1; +static int hf_thread_nwd_tlv_border_router_o = -1; +static int hf_thread_nwd_tlv_border_router_n = -1; + +/* 6LoWPAN ID TLV fields */ +static int hf_thread_nwd_tlv_6lowpan_id_6co_context_length = -1; +static int hf_thread_nwd_tlv_6lowpan_id_6co_flag = -1; +static int hf_thread_nwd_tlv_6lowpan_id_6co_flag_c = -1; +static int hf_thread_nwd_tlv_6lowpan_id_6co_flag_cid = -1; +static int hf_thread_nwd_tlv_6lowpan_id_6co_flag_reserved = -1; + +/* Commissioning Data fields */ +/* static int hf_thread_nwd_tlv_comm_data = -1; */ + +/* Service fields */ +static int hf_thread_nwd_tlv_service_t = -1; +static int hf_thread_nwd_tlv_service_s_id = -1; +static int hf_thread_nwd_tlv_service_s_ent_num = -1; +static int hf_thread_nwd_tlv_service_s_data_len = -1; +static int hf_thread_nwd_tlv_service_s_data = -1; + +/* Server fields */ +static int hf_thread_nwd_tlv_server_16 = -1; +static int hf_thread_nwd_tlv_server_data = -1; + +/* Thread Beacon */ + +static int hf_thread_bcn_protocol = -1; +static int hf_thread_bcn_joining = -1; +static int hf_thread_bcn_native = -1; +static int hf_thread_bcn_version = -1; +static int hf_thread_bcn_network_id = -1; +static int hf_thread_bcn_epid = -1; +static int hf_thread_bcn_tlv = -1; +static int hf_thread_bcn_tlv_type = -1; +static int hf_thread_bcn_tlv_length = -1; +static int hf_thread_bcn_tlv_steering_data = -1; +static int hf_thread_bcn_tlv_unknown = -1; + +/* Tree types */ + +static gint ett_thread_address = -1; +static gint ett_thread_address_tlv = -1; +static gint ett_thread_dg = -1; +static gint ett_thread_dg_tlv = -1; +static gint ett_thread_mc = -1; +static gint ett_thread_mc_tlv = -1; +static gint ett_thread_mc_chan_mask = -1; +static gint ett_thread_mc_el_count = -1; +static gint ett_thread_nwd = -1; +static gint ett_thread_nwd_tlv = -1; +static gint ett_thread_nwd_has_route = -1; +static gint ett_thread_nwd_6co_flag = -1; +static gint ett_thread_nwd_border_router = -1; +static gint ett_thread_nwd_prefix_sub_tlvs = -1; +static gint ett_thread_bcn = -1; +static gint ett_thread_bcn_tlv = -1; + +/* Expert info. */ + +/* static expert_field ei_thread_address_tlv_length_failed = EI_INIT; */ +static expert_field ei_thread_address_len_size_mismatch = EI_INIT; +/* static expert_field ei_thread_dg_tlv_length_failed = EI_INIT; */ +/* static expert_field ei_thread_dg_len_size_mismatch = EI_INIT; */ +static expert_field ei_thread_mc_tlv_length_failed = EI_INIT; +static expert_field ei_thread_mc_len_size_mismatch = EI_INIT; +static expert_field ei_thread_mc_len_too_long = EI_INIT; +/* static expert_field ei_thread_nwd_tlv_length_failed = EI_INIT; */ +static expert_field ei_thread_nwd_len_size_mismatch = EI_INIT; + +static dissector_table_t thread_coap_namespace; + +/* Dissector handles */ +static dissector_handle_t thread_address_nwd_handle; +static dissector_handle_t thread_dg_handle; +static dissector_handle_t thread_mc_handle; +static dissector_handle_t thread_dtls_handle; +static dissector_handle_t thread_udp_handle; +static dissector_handle_t thread_coap_handle; +static dissector_handle_t thread_address_handle; + +#define THREAD_TLV_LENGTH_ESC 0xFF + +#define THREAD_MC_32768_TO_NSEC_FACTOR ((double)30517.578125) +#define THREAD_MC_TSTAMP_MASK_U_MASK 0x80 +#define THREAD_MC_SEC_POLICY_MASK_O_MASK 0x80 +#define THREAD_MC_SEC_POLICY_MASK_N_MASK 0x40 +#define THREAD_MC_SEC_POLICY_MASK_R_MASK 0x20 +#define THREAD_MC_SEC_POLICY_MASK_C_MASK 0x10 +#define THREAD_MC_SEC_POLICY_MASK_B_MASK 0x08 +#define THREAD_MC_STACK_VER_REV_MASK 0x0F +#define THREAD_MC_STACK_VER_MIN_MASK 0xF0 +#define THREAD_MC_STACK_VER_MAJ_MASK 0x0F +#define THREAD_MC_DISCOVERY_REQ_MASK_VER_MASK 0xF0 +#define THREAD_MC_DISCOVERY_REQ_MASK_J_MASK 0x08 +#define THREAD_MC_DISCOVERY_RSP_MASK_VER_MASK 0xF0 +#define THREAD_MC_DISCOVERY_RSP_MASK_N_MASK 0x08 +#define THREAD_MC_INVALID_CHAN_COUNT 0xFFFF + +#define THREAD_NWD_TLV_HAS_ROUTE_PREF 0xC0 +#define THREAD_NWD_TLV_HAS_ROUTE_SIZE 3 + +#define THREAD_NWD_TLV_BORDER_ROUTER_PREF 0xC0 +#define THREAD_NWD_TLV_BORDER_ROUTER_P 0x20 +#define THREAD_NWD_TLV_BORDER_ROUTER_S 0x10 +#define THREAD_NWD_TLV_BORDER_ROUTER_D 0x08 +#define THREAD_NWD_TLV_BORDER_ROUTER_C 0x04 +#define THREAD_NWD_TLV_BORDER_ROUTER_R 0x02 +#define THREAD_NWD_TLV_BORDER_ROUTER_O 0x01 +#define THREAD_NWD_TLV_BORDER_ROUTER_N 0x80 + +#define THREAD_BCN_PROTOCOL_ID 0x03 +#define THREAD_BCN_JOINING 0x01 +#define THREAD_BCN_NATIVE 0x08 +#define THREAD_BCN_PROTOCOL_VERSION 0xf0 +#define THREAD_BCN_TLV_STEERING_DATA_S 0x80 +#define THREAD_BCN_TLV_STEERING_DATA 8 + +#define ND_OPT_6CO_FLAG_C 0x10 +#define ND_OPT_6CO_FLAG_CID 0x0F +#define ND_OPT_6CO_FLAG_RESERVED 0xE0 + +#define THREAD_NWD_TLV_SERVICE_T 0x80 +#define THREAD_NWD_TLV_SERVICE_S_ID 0x0F + +typedef enum { + TLV_LEN_LEN8 = 1, + TLV_LEN_LEN16 = 3 +} tlv_len_len_e; + +typedef struct { + guint16 src_port; + guint16 dst_port; + guint16 length; + guint16 checksum; +} udp_hdr_t; + +/* TLV values */ + +#define THREAD_ADDRESS_TLV_TARGET_EID 0 +#define THREAD_ADDRESS_TLV_EXT_MAC_ADDR 1 +#define THREAD_ADDRESS_TLV_RLOC16 2 +#define THREAD_ADDRESS_TLV_ML_EID 3 +#define THREAD_ADDRESS_TLV_STATUS 4 +/* Gap */ +#define THREAD_ADDRESS_TLV_LAST_TRANSACTION_TIME 6 +#define THREAD_ADDRESS_TLV_ROUTER_MASK 7 +#define THREAD_ADDRESS_TLV_ND_OPTION 8 +#define THREAD_ADDRESS_TLV_ND_DATA 9 +#define THREAD_ADDRESS_TLV_THREAD_NETWORK_DATA 10 + +static const value_string thread_address_tlv_vals[] = { +{ THREAD_ADDRESS_TLV_TARGET_EID, "Target EID" }, +{ THREAD_ADDRESS_TLV_EXT_MAC_ADDR, "Extended MAC Address" }, +{ THREAD_ADDRESS_TLV_RLOC16, "RLOC16" }, +{ THREAD_ADDRESS_TLV_ML_EID, "ML-EID" }, +{ THREAD_ADDRESS_TLV_STATUS, "Status" }, +/* Gap */ +{ THREAD_ADDRESS_TLV_LAST_TRANSACTION_TIME, "Last Transaction Time" }, +{ THREAD_ADDRESS_TLV_ND_OPTION, "ND Option" }, +{ THREAD_ADDRESS_TLV_ND_DATA, "ND Data" }, +{ THREAD_ADDRESS_TLV_THREAD_NETWORK_DATA, "Thread Network Data" }, +{ 0, NULL } +}; + +static const value_string thread_address_tlv_status_vals[] = { +{ 0, "Success" }, +{ 1, "No Address Available" }, +{ 0, NULL } +}; + +/* Network Layer (Address) mirrors */ +#define THREAD_DG_TLV_EXT_MAC_ADDR 0 /* As THREAD_ADDRESS_TLV_EXT_MAC_ADDR */ +/* MLE mirrors */ +#define THREAD_DG_TLV_ADDRESS16 1 /* As MLE_TLV_ADDRESS16 */ +#define THREAD_DG_TLV_MODE 2 /* As MLE_TLV_MODE */ +#define THREAD_DG_TLV_TIMEOUT 3 /* As MLE_TLV_TIMEOUT */ +#define THREAD_DG_TLV_CONNECTIVITY 4 /* As MLE_TLV_CONNECTIVITY */ +#define THREAD_DG_TLV_ROUTE64 5 /* As MLE_TLV_ROUTE64 */ +#define THREAD_DG_TLV_LEADER_DATA 6 /* As MLE_TLV_LEADER_DATA */ +#define THREAD_DG_TLV_NETWORK_DATA 7 /* As MLE_TLV_NETWORK_DATA */ +/* Statistics */ +#define THREAD_DG_TLV_IPV6_ADDR_LIST 8 +#define THREAD_DG_TLV_MAC_COUNTERS 9 +/* Others */ +#define THREAD_DG_TLV_BATTERY_LEVEL 14 +#define THREAD_DG_TLV_VOLTAGE 15 +#define THREAD_DG_TLV_CHILD_TABLE 16 +#define THREAD_DG_TLV_CHANNEL_PAGES 17 +#define THREAD_DG_TLV_TYPE_LIST 18 +#define THREAD_DG_TLV_UNKNOWN 255 + +static const value_string thread_dg_tlv_vals[] = { +/* Network Layer (Address) mirrors */ +{ THREAD_DG_TLV_EXT_MAC_ADDR, "Extended MAC Address" }, +/* MLE mirrors */ +{ THREAD_DG_TLV_ADDRESS16, "Address16" }, +{ THREAD_DG_TLV_MODE, "Mode" }, +{ THREAD_DG_TLV_TIMEOUT, "Timeout" }, +{ THREAD_DG_TLV_CONNECTIVITY, "Connectivity" }, +{ THREAD_DG_TLV_ROUTE64, "Route64" }, +{ THREAD_DG_TLV_LEADER_DATA, "Leader Data" }, +{ THREAD_DG_TLV_NETWORK_DATA, "Network Data" }, +/* Statistics */ +{ THREAD_DG_TLV_IPV6_ADDR_LIST, "IPv6 Address List" }, +{ THREAD_DG_TLV_MAC_COUNTERS, "MAC Counters" }, +/* Others */ +{ THREAD_DG_TLV_BATTERY_LEVEL, "Battery level (%)" }, +{ THREAD_DG_TLV_VOLTAGE, "Voltage (mV)" }, +{ THREAD_DG_TLV_CHILD_TABLE, "Child Table" }, +{ THREAD_DG_TLV_CHANNEL_PAGES, "Channel Pages" }, +{ THREAD_DG_TLV_TYPE_LIST, "Type List" }, +{ THREAD_DG_TLV_UNKNOWN, "Unknown" }, +{ 0, NULL } +}; + +#define THREAD_MC_TLV_CHANNEL 0 /* Modified for new features */ +#define THREAD_MC_TLV_PANID 1 +#define THREAD_MC_TLV_XPANID 2 +#define THREAD_MC_TLV_NETWORK_NAME 3 +#define THREAD_MC_TLV_PSKC 4 +#define THREAD_MC_TLV_NETWORK_MASTER_KEY 5 +#define THREAD_MC_TLV_NETWORK_KEY_SEQ_CTR 6 +#define THREAD_MC_TLV_NETWORK_ML_PREFIX 7 +#define THREAD_MC_TLV_STEERING_DATA 8 +#define THREAD_MC_TLV_BORDER_AGENT_LOCATOR 9 +#define THREAD_MC_TLV_COMMISSIONER_ID 10 +#define THREAD_MC_TLV_COMMISSIONER_SESSION_ID 11 +#define THREAD_MC_TLV_SECURITY_POLICY 12 +#define THREAD_MC_TLV_GET 13 +#define THREAD_MC_TLV_ACTIVE_TSTAMP 14 /* Was "Commissioning Dataset Timestamp TLV" */ +#define THREAD_MC_TLV_COMMISSIONER_UDP_PORT 15 +#define THREAD_MC_TLV_STATE 16 +#define THREAD_MC_TLV_JOINER_DTLS_ENCAP 17 +#define THREAD_MC_TLV_JOINER_UDP_PORT 18 +#define THREAD_MC_TLV_JOINER_IID 19 +#define THREAD_MC_TLV_JOINER_ROUTER_LOCATOR 20 +#define THREAD_MC_TLV_JOINER_KEK 21 +/* Gap */ +#define THREAD_MC_TLV_PROVISIONING_URL 32 +#define THREAD_MC_TLV_VENDOR_NAME 33 +#define THREAD_MC_TLV_VENDOR_MODEL 34 +#define THREAD_MC_TLV_VENDOR_SW_VERSION 35 +#define THREAD_MC_TLV_VENDOR_DATA 36 +#define THREAD_MC_TLV_VENDOR_STACK_VERSION 37 +/* Gap */ +#define THREAD_MC_TLV_UDP_ENCAPSULATION 48 +#define THREAD_MC_TLV_IPV6_ADDRESS 49 +/* Gap */ +/* New features */ +#define THREAD_MC_TLV_PENDING_TSTAMP 51 +#define THREAD_MC_TLV_DELAY_TIMER 52 +#define THREAD_MC_TLV_CHANNEL_MASK 53 +#define THREAD_MC_TLV_COUNT 54 +#define THREAD_MC_TLV_PERIOD 55 +#define THREAD_MC_TLV_SCAN_DURATION 56 +#define THREAD_MC_TLV_ENERGY_LIST 57 +/* Gap */ +/* New discovery mechanism */ +#define THREAD_MC_TLV_DISCOVERY_REQUEST 128 +#define THREAD_MC_TLV_DISCOVERY_RESPONSE 129 + +static const value_string thread_mc_tlv_vals[] = { +{ THREAD_MC_TLV_CHANNEL, "Channel" }, +{ THREAD_MC_TLV_PANID, "PAN ID" }, +{ THREAD_MC_TLV_XPANID, "Extended PAN ID" }, +{ THREAD_MC_TLV_NETWORK_NAME, "Network Name" }, +{ THREAD_MC_TLV_PSKC, "PSKc" }, +{ THREAD_MC_TLV_NETWORK_MASTER_KEY, "Network Master Key" }, +{ THREAD_MC_TLV_NETWORK_KEY_SEQ_CTR, "Network Key Sequence Counter" }, +{ THREAD_MC_TLV_NETWORK_ML_PREFIX, "Mesh Local ULA Prefix" }, +{ THREAD_MC_TLV_STEERING_DATA, "Steering Data" }, +{ THREAD_MC_TLV_BORDER_AGENT_LOCATOR, "Border Agent Locator" }, +{ THREAD_MC_TLV_COMMISSIONER_ID, "Commissioner ID" }, +{ THREAD_MC_TLV_COMMISSIONER_SESSION_ID, "Commissioner Session ID" }, +{ THREAD_MC_TLV_SECURITY_POLICY, "Security Policy" }, +{ THREAD_MC_TLV_GET, "Get" }, +{ THREAD_MC_TLV_ACTIVE_TSTAMP, "Active Timestamp" }, +{ THREAD_MC_TLV_COMMISSIONER_UDP_PORT, "Commissioner UDP Port" }, +{ THREAD_MC_TLV_STATE, "State" }, +{ THREAD_MC_TLV_JOINER_DTLS_ENCAP, "Joiner DTLS Encapsulation" }, +{ THREAD_MC_TLV_JOINER_UDP_PORT, "Joiner UDP Port" }, +{ THREAD_MC_TLV_JOINER_IID, "Joiner IID" }, +{ THREAD_MC_TLV_JOINER_ROUTER_LOCATOR, "Joiner Router Locator" }, +{ THREAD_MC_TLV_JOINER_KEK, "Joiner KEK" }, +{ THREAD_MC_TLV_PROVISIONING_URL, "Provisioning URL" }, +{ THREAD_MC_TLV_VENDOR_NAME, "Vendor Name" }, +{ THREAD_MC_TLV_VENDOR_MODEL, "Vendor Model" }, +{ THREAD_MC_TLV_VENDOR_SW_VERSION, "Vendor Software Version" }, +{ THREAD_MC_TLV_VENDOR_DATA, "Vendor Data" }, +{ THREAD_MC_TLV_VENDOR_STACK_VERSION, "Vendor Stack Version" }, +{ THREAD_MC_TLV_UDP_ENCAPSULATION, "UDP Encapsulation" }, +{ THREAD_MC_TLV_IPV6_ADDRESS, "IPv6 Address" }, +/* New features */ +{ THREAD_MC_TLV_PENDING_TSTAMP, "Pending Timestamp" }, +{ THREAD_MC_TLV_DELAY_TIMER, "Delay Timer" }, +{ THREAD_MC_TLV_CHANNEL_MASK, "Channel Mask" }, +{ THREAD_MC_TLV_COUNT, "Count" }, +{ THREAD_MC_TLV_PERIOD, "Period" }, +{ THREAD_MC_TLV_SCAN_DURATION, "Scan Duration" }, +{ THREAD_MC_TLV_ENERGY_LIST, "Energy List" }, +/* New discovery mechanism */ +{ THREAD_MC_TLV_DISCOVERY_REQUEST, "Discovery Request" }, +{ THREAD_MC_TLV_DISCOVERY_RESPONSE, "Discovery Response" }, +{ 0, NULL} +}; + +static const value_string thread_mc_state_vals[] = { +{ -1, "Reject" }, +{ 0, "Pending" }, +{ 1, "Accept" }, +{ 0, NULL} +}; + +static const true_false_string thread_mc_tlv_join_intent = { + "Intending", + "Not Intending" +}; + +#define THREAD_NWD_TLV_HAS_ROUTE 0 +#define THREAD_NWD_TLV_PREFIX 1 +#define THREAD_NWD_TLV_BORDER_ROUTER 2 +#define THREAD_NWD_TLV_6LOWPAN_ID 3 +#define THREAD_NWD_TLV_COMMISSIONING_DATA 4 +#define THREAD_NWD_TLV_SERVICE 5 +#define THREAD_NWD_TLV_SERVER 6 + +static const value_string thread_nwd_tlv_vals[] = { +{ THREAD_NWD_TLV_HAS_ROUTE, "Has Route" }, +{ THREAD_NWD_TLV_PREFIX, "Prefix" }, +{ THREAD_NWD_TLV_BORDER_ROUTER, "Border Router" }, +{ THREAD_NWD_TLV_6LOWPAN_ID, "6LoWPAN ID" }, +{ THREAD_NWD_TLV_COMMISSIONING_DATA, "Commissioning Data" }, +{ THREAD_NWD_TLV_SERVICE, "Service" }, +{ THREAD_NWD_TLV_SERVER, "Server" }, +{ 0, NULL} +}; + +#define THREAD_NWD_TLV_TYPE_M 0xFE +#define THREAD_NWD_TLV_STABLE_M 0x01 + +static const true_false_string tfs_thread_nwd_tlv_border_router_p = { + "Autoconfigured preferred", + "Autoconfigured deprecated" +}; + +static const true_false_string tfs_thread_nwd_tlv_border_router_c = { + "Additional config. data", + "No additional config. data" +}; + +static const true_false_string tfs_thread_nwd_tlv_border_router_o = { + "On mesh", + "Not on mesh" +}; + +/* Thread Beacon TLV Values. */ +static const value_string thread_bcn_tlv_vals[] = { + { THREAD_BCN_TLV_STEERING_DATA, "Steering Data" }, + { 0, NULL } +}; + +/* Preferences */ +static gboolean thread_coap_decode = FALSE; +static gboolean thread_use_pan_id_in_key = FALSE; +static const gchar *thread_seq_ctr_str = NULL; +static gboolean thread_auto_acq_seq_ctr = TRUE; + + +static gboolean thread_seq_ctr_acqd = FALSE; +static guint8 thread_seq_ctr_bytes[4]; +static const guint8 thread_well_known_key[IEEE802154_CIPHER_SIZE] = +{ 0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f, 0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66 }; + +static GByteArray *set_thread_seq_ctr_from_key_index(guint8 key_index) +{ + GByteArray *seq_ctr_bytes = NULL; + + seq_ctr_bytes = g_byte_array_new(); + if (thread_seq_ctr_acqd) { + seq_ctr_bytes = g_byte_array_set_size(seq_ctr_bytes, 4); + memcpy(seq_ctr_bytes->data, thread_seq_ctr_bytes, 4); + } else { + hex_str_to_bytes(thread_seq_ctr_str, seq_ctr_bytes, FALSE); + if (seq_ctr_bytes->len != 4) { + /* Not read correctly - assume value is 0 */ + seq_ctr_bytes = g_byte_array_set_size(seq_ctr_bytes, 4); + memset(seq_ctr_bytes->data, 0, 4); + } + } + /* Replace lower part with counter based on packet key index */ + seq_ctr_bytes->data[3] = (seq_ctr_bytes->data[3] & 0x80) + ((key_index - 1) & 0x7F); + + return seq_ctr_bytes; +} + +static void create_thread_temp_keys(GByteArray *seq_ctr_bytes, guint16 src_pan, ieee802154_key_t* key, unsigned char *mac_key, unsigned char *mle_key) +{ + GByteArray *bytes; + char buffer[10]; + gboolean res; + gboolean key_valid; + gboolean verbatim_key = TRUE; + + /* Get the IEEE 802.15.4 decryption key. */ + bytes = g_byte_array_new(); + res = hex_str_to_bytes(key->pref_key, bytes, FALSE); + key_valid = (res && bytes->len >= IEEE802154_CIPHER_SIZE); + if (key_valid) { + if (thread_use_pan_id_in_key) { + /* Substitute the bottom two keys bytes with PAN ID */ + bytes->data[0] = (guint8)(src_pan & 0xFF); + bytes->data[1] = (guint8)(src_pan >> 8); + } + if (key->hash_type != KEY_HASH_NONE) { + char digest[32]; + + if (key->hash_type == KEY_HASH_THREAD) { + memcpy(buffer, seq_ctr_bytes->data, 4); + memcpy(&buffer[4], "Thread", 6); /* len("Thread") */ + + if (!ws_hmac_buffer(GCRY_MD_SHA256, digest, buffer, 10, bytes->data, IEEE802154_CIPHER_SIZE)) { + /* Copy upper hashed bytes to the MAC key */ + if (mac_key) { + memcpy(mac_key, &digest[IEEE802154_CIPHER_SIZE], IEEE802154_CIPHER_SIZE); + } + /* Copy lower hashed bytes to the MLE key */ + if (mle_key) { + memcpy(mle_key, digest, IEEE802154_CIPHER_SIZE); + } + verbatim_key = FALSE; + } + } + } + if (verbatim_key) { + /* Just copy the keys verbatim */ + if (mac_key) { + memcpy(mac_key, bytes->data, IEEE802154_CIPHER_SIZE); + } + if (mle_key) { + memcpy(mle_key, bytes->data, IEEE802154_CIPHER_SIZE); + } + } + } + g_byte_array_free(bytes, TRUE); +} + +/* Set MAC key for Thread hash */ +static gboolean set_thread_mac_key(ieee802154_packet * packet, unsigned char* key, unsigned char* alt_key, ieee802154_key_t* uat_key) +{ + GByteArray *seq_ctr_bytes = NULL; + + if (packet->key_id_mode == KEY_ID_MODE_KEY_INDEX) { + seq_ctr_bytes = set_thread_seq_ctr_from_key_index(packet->key_index); + } else if ((packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_4) && + (packet->key_index == IEEE802154_THR_WELL_KNOWN_KEY_INDEX) && + (packet->key_source.addr32 == IEEE802154_THR_WELL_KNOWN_KEY_SRC)) + { + /* This is the well-known Thread key. No need for an alternative key */ + memcpy(key, thread_well_known_key, IEEE802154_CIPHER_SIZE); + return TRUE; + } + if (seq_ctr_bytes != NULL) { + create_thread_temp_keys(seq_ctr_bytes, packet->src_pan, uat_key, key, NULL); + /* Create an alternate key based on the wraparound case */ + seq_ctr_bytes->data[3] ^= 0x80; + create_thread_temp_keys(seq_ctr_bytes, packet->src_pan, uat_key, alt_key, NULL); + g_byte_array_free(seq_ctr_bytes, TRUE); + return TRUE; + } + + return FALSE; +} + +/* Set MLE key for Thread hash */ +static gboolean set_thread_mle_key(ieee802154_packet * packet, unsigned char* key, unsigned char* alt_key, ieee802154_key_t* uat_key) +{ + GByteArray *seq_ctr_bytes = NULL; + if (packet->key_id_mode == KEY_ID_MODE_KEY_INDEX) { + seq_ctr_bytes = set_thread_seq_ctr_from_key_index(packet->key_index); + } + else if (packet->key_id_mode == KEY_ID_MODE_KEY_EXPLICIT_4) { + /* Reconstruct the key source from the key source in the packet */ + seq_ctr_bytes = g_byte_array_new(); + seq_ctr_bytes = g_byte_array_set_size(seq_ctr_bytes, 4); + seq_ctr_bytes->data[0] = (packet->key_source.addr32 >> 24) & 0xFF; + seq_ctr_bytes->data[1] = (packet->key_source.addr32 >> 16) & 0xFF; + seq_ctr_bytes->data[2] = (packet->key_source.addr32 >> 8) & 0xFF; + seq_ctr_bytes->data[3] = packet->key_source.addr32 & 0xFF; + /* Acquire the sequence counter if configured in preferences */ + if (thread_auto_acq_seq_ctr && !thread_seq_ctr_acqd) { + memcpy(thread_seq_ctr_bytes, seq_ctr_bytes->data, 4); + thread_seq_ctr_acqd = TRUE; + } + } + if (seq_ctr_bytes != NULL) { + create_thread_temp_keys(seq_ctr_bytes, packet->src_pan, uat_key, NULL, key); + /* Create an alternate key based on the wraparound case */ + seq_ctr_bytes->data[3] ^= 0x80; + create_thread_temp_keys(seq_ctr_bytes, packet->src_pan, uat_key, NULL, alt_key); + g_byte_array_free(seq_ctr_bytes, TRUE); + return TRUE; + } + + return FALSE; +} + +static guint +count_bits_in_byte(guint8 byte) +{ + static const guint8 lut[16] = {0, /* 0b0000 */ + 1, /* 0b0001 */ + 1, /* 0b0010 */ + 2, /* 0b0011 */ + 1, /* 0b0100 */ + 2, /* 0b0101 */ + 2, /* 0b0110 */ + 3, /* 0b0111 */ + 1, /* 0b1000 */ + 2, /* 0b1001 */ + 2, /* 0b1010 */ + 3, /* 0b1011 */ + 2, /* 0b1100 */ + 3, /* 0b1101 */ + 3, /* 0b1110 */ + 4 /* 0b1111 */}; + return lut[byte >> 4] + lut[byte & 0xf]; +} + +static guint +get_chancount(tvbuff_t *tvb) +{ + guint offset; + guint8 tlv_type; + guint16 tlv_len; + tlv_len_len_e tlv_len_len; + guint chancount = THREAD_MC_INVALID_CHAN_COUNT; + + offset = 0; + + /* Thread Network Data TLVs */ + while (tvb_offset_exists(tvb, offset)) { + + /* Get the type and length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_type = tvb_get_guint8(tvb, offset); + tlv_len = (guint16)tvb_get_guint8(tvb, offset + 1); + + /* TODO: need to make sure this applies to all MeshCoP TLVs */ + if (THREAD_TLV_LENGTH_ESC == tlv_len) { + /* 16-bit length field */ + tlv_len = tvb_get_ntohs(tvb, offset + 2); + tlv_len_len = TLV_LEN_LEN16; + } else { + tlv_len_len = TLV_LEN_LEN8; + } + + /* Skip over Type and Length */ + offset += 1 + tlv_len_len; + + switch(tlv_type) { + + case THREAD_MC_TLV_CHANNEL_MASK: + { + int i, j; + guint8 entries = 0; + guint16 check_len = tlv_len; + guint8 check_offset = offset + 1; /* Channel page first */ + guint8 masklen; + + /* Check consistency of entries */ + while (check_len > 0) { + + masklen = tvb_get_guint8(tvb, check_offset); + if (masklen == 0) { + break; /* Get out or we might spin forever */ + } + masklen += 2; /* Add in page and length */ + check_offset += masklen; + check_len -= masklen; + entries++; + } + + if (check_len != 0) { + /* Not an integer number of entries */ + /* offset += tlv_len; */ + return chancount; + } else { + chancount = 0; + for (i = 0; i < entries; i++) { + /* Skip over channel page */ + offset++; + masklen = tvb_get_guint8(tvb, offset); + offset++; + /* Count the number of channels in the channel mask */ + for (j = 0; j < masklen; j++) { + chancount += count_bits_in_byte(tvb_get_guint8(tvb, offset)); + offset++; + } + } + } + } + break; + + default: + /* Skip over any other TLVs */ + offset += tlv_len; + } + } + return chancount; +} + +static int +dissect_thread_address(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *proto_root; + proto_tree *thread_address_tree; + proto_tree *tlv_tree; + tvbuff_t *sub_tvb; + guint offset = 0; + proto_item *ti; + guint8 tlv_type, tlv_len; + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_thread_address, tvb, 0, tvb_reported_length(tvb), ENC_NA); + thread_address_tree = proto_item_add_subtree(proto_root, ett_thread_address); + + /* Thread Network Data TLVs */ + while (tvb_offset_exists(tvb, offset)) { + + /* Get the length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_len = tvb_get_guint8(tvb, offset + 1); + + ti = proto_tree_add_item(thread_address_tree, hf_thread_address_tlv, tvb, offset, tlv_len+2, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_thread_address_tlv); + + /* Type */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + tlv_type = tvb_get_guint8(tvb, offset); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s)", val_to_str(tlv_type, thread_address_tlv_vals, "Unknown (%d)")); + + /* Length */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + switch(tlv_type) { + case THREAD_ADDRESS_TLV_TARGET_EID: + { + /* Check length is consistent */ + if (tlv_len != 16) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Target EID */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_target_eid, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_EXT_MAC_ADDR: + { + /* Check length is consistent */ + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Extended MAC address */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_ext_mac_addr, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_RLOC16: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Mesh Locator */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_rloc16, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_ML_EID: + { + /* Check length is consistent */ + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* ML IID */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_ml_eid, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_STATUS: + { + /* Check length is consistent */ + if (tlv_len != 1) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Status */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_status, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_LAST_TRANSACTION_TIME: + { + /* Check length is consistent */ + if (tlv_len != 4) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Last transaction time */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_last_transaction_time, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_ADDRESS_TLV_ROUTER_MASK: + { + /* Check length is consistent */ + if (tlv_len != 9) { + expert_add_info(pinfo, proto_root, &ei_thread_address_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + /* Router Mask */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_router_mask_id_seq, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* + * | | | | | | | | | | |1|1|1|1|1|1|...|6| + * |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|...|3| + * --------------------------------------- + * |1|0|1|1|1|0|0|0|1|1|0|0|0|1|0|1|... + * + * is sent as 0xb8, 0xc5 + * and represents table entry for routers 0, 2, 3, 4, 8, 9, 13, 15... + */ + + /* Just show the string of octets - best representation for a bit mask */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_router_mask_assigned, tvb, offset, 8, ENC_NA); + offset += 8; + } + } + break; + + case THREAD_ADDRESS_TLV_ND_OPTION: + /* Just show the data */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_nd_option, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + break; + + case THREAD_ADDRESS_TLV_ND_DATA: + /* Just show the data. Note there is no icmpv6 options dissector so would have to copy it */ + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_nd_data, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + break; + + case THREAD_ADDRESS_TLV_THREAD_NETWORK_DATA: + if (tlv_len > 0) { + sub_tvb = tvb_new_subset_length(tvb, offset, tlv_len); + call_dissector(thread_address_nwd_handle, sub_tvb, pinfo, tlv_tree); + } + offset += tlv_len; + break; + + default: + proto_tree_add_item(tlv_tree, hf_thread_address_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + } + return tvb_captured_length(tvb); +} + +static int +dissect_thread_dg(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_) +{ + proto_item *proto_root; + proto_tree *thread_dg_tree; + proto_tree *tlv_tree; + guint offset = 0; + proto_item *ti; + guint8 tlv_type; + guint16 tlv_len; + tlv_len_len_e tlv_len_len; + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_thread_dg, tvb, 0, tvb_reported_length(tvb), ENC_NA); + thread_dg_tree = proto_item_add_subtree(proto_root, ett_thread_dg); + + /* Thread Network Data TLVs */ + while (tvb_offset_exists(tvb, offset)) { + + /* Get the type and length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_type = tvb_get_guint8(tvb, offset); + tlv_len = (guint16)tvb_get_guint8(tvb, offset + 1); + + /* TODO: need to make sure this applies to all Diagnostic TLVs */ + if (THREAD_TLV_LENGTH_ESC == tlv_len) { + /* 16-bit length field */ + tlv_len = tvb_get_ntohs(tvb, offset + 2); + tlv_len_len = TLV_LEN_LEN16; + } else { + tlv_len_len = TLV_LEN_LEN8; + } + + /* Create the tree */ + ti = proto_tree_add_item(thread_dg_tree, hf_thread_dg_tlv, tvb, offset, 1 + tlv_len_len + tlv_len, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_thread_dg_tlv); + + /* Type */ + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s)", val_to_str(tlv_type, thread_dg_tlv_vals, "Unknown (%d)")); + + /* Length */ + switch (tlv_len_len) { + case TLV_LEN_LEN8: + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_length8, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case TLV_LEN_LEN16: + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_length16, tvb, offset + 1, 2, ENC_BIG_ENDIAN); + break; + default: + break; + } + offset += tlv_len_len; + + switch(tlv_type) { + case THREAD_DG_TLV_TYPE_LIST: + { + int i; + + for (i = 0; i < tlv_len; i++) { + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + break; + + case THREAD_DG_TLV_EXT_MAC_ADDR: + case THREAD_DG_TLV_ADDRESS16: + case THREAD_DG_TLV_MODE: + case THREAD_DG_TLV_TIMEOUT: + case THREAD_DG_TLV_CONNECTIVITY: + case THREAD_DG_TLV_ROUTE64: + case THREAD_DG_TLV_LEADER_DATA: + case THREAD_DG_TLV_NETWORK_DATA: + case THREAD_DG_TLV_IPV6_ADDR_LIST: + /* Counters */ + case THREAD_DG_TLV_MAC_COUNTERS: + case THREAD_DG_TLV_BATTERY_LEVEL: + case THREAD_DG_TLV_VOLTAGE: + case THREAD_DG_TLV_CHILD_TABLE: + case THREAD_DG_TLV_CHANNEL_PAGES: + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_general, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + break; + + default: + proto_tree_add_item(tlv_tree, hf_thread_dg_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + } + return tvb_captured_length(tvb); +} + +static int +dissect_thread_mc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *proto_root; + proto_tree *thread_mc_tree; + proto_tree *tlv_tree; + guint offset = 0; + proto_item *ti; + proto_item *pi; + guint8 tlv_type; + guint16 tlv_len; + tlv_len_len_e tlv_len_len; + guint chancount; + + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_thread_mc, tvb, 0, tvb_reported_length(tvb), ENC_NA); + thread_mc_tree = proto_item_add_subtree(proto_root, ett_thread_mc); + + /* Get channel count a priori so we can process energy list better */ + chancount = get_chancount(tvb); + + /* Thread Network Data TLVs */ + while (tvb_offset_exists(tvb, offset)) { + + /* Get the type and length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_type = tvb_get_guint8(tvb, offset); + tlv_len = (guint16)tvb_get_guint8(tvb, offset + 1); + + /* TODO: need to make sure this applies to all MeshCoP TLVs */ + if (THREAD_TLV_LENGTH_ESC == tlv_len) { + /* 16-bit length field */ + tlv_len = tvb_get_ntohs(tvb, offset + 2); + tlv_len_len = TLV_LEN_LEN16; + } else { + tlv_len_len = TLV_LEN_LEN8; + } + + /* Create the tree */ + ti = proto_tree_add_item(thread_mc_tree, hf_thread_mc_tlv, tvb, offset, 1 + tlv_len_len + tlv_len, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_thread_mc_tlv); + + /* Type */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s)", val_to_str(tlv_type, thread_mc_tlv_vals, "Unknown (%d)")); + + /* Length */ + switch (tlv_len_len) { + case TLV_LEN_LEN8: + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_length8, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + case TLV_LEN_LEN16: + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_length16, tvb, offset + 1, 2, ENC_BIG_ENDIAN); + break; + default: + break; + } + offset += tlv_len_len; + + switch(tlv_type) { + case THREAD_MC_TLV_CHANNEL: + { + /* Check length is consistent */ + if (tlv_len != 3) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Channel page */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_channel_page, tvb, offset, 1, ENC_BIG_ENDIAN); + /* Channel */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_channel, tvb, offset+1, 2, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_PANID: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* PAN ID */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_pan_id, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_XPANID: + { + /* Check length is consistent */ + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* PAN ID */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_xpan_id, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_NETWORK_NAME: + { + /* Check length is consistent */ + if (tlv_len > 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_net_name, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_PSKC: + { + /* Check length is consistent */ + if (tlv_len != 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_pskc, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_NETWORK_MASTER_KEY: + { + /* Check length is consistent */ + if (tlv_len != 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_master_key, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_NETWORK_KEY_SEQ_CTR: + { + /* Check length is consistent */ + if (tlv_len != 4) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_net_key_seq_ctr, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_NETWORK_ML_PREFIX: + { + /* Check length is consistent */ + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + struct e_in6_addr prefix; + + memset(&prefix, 0, sizeof(prefix)); + tvb_memcpy(tvb, (guint8 *)&prefix.bytes, offset, tlv_len); + pi = proto_tree_add_ipv6(tlv_tree, hf_thread_mc_tlv_ml_prefix, tvb, offset, tlv_len, &prefix); + proto_item_append_text(pi, "/%d", tlv_len * 8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_STEERING_DATA: + { + /* Check length is consistent */ + if (tlv_len > 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Display it simply */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_steering_data, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_BORDER_AGENT_LOCATOR: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_ba_locator, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_COMMISSIONER_ID: + { + /* Check length is consistent */ + if (tlv_len > 64) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_commissioner_id, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_COMMISSIONER_SESSION_ID: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_commissioner_sess_id, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_SECURITY_POLICY: + { + /* Check length is consistent */ + if (tlv_len != 3) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_rot, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_o, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_n, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_r, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_c, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_sec_policy_b, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + break; + + case THREAD_MC_TLV_GET: + { + int i; + + for (i = 0; i < tlv_len; i++) { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + break; + + case THREAD_MC_TLV_ACTIVE_TSTAMP: + case THREAD_MC_TLV_PENDING_TSTAMP: + { + nstime_t timestamp; + + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Fill in the nstime_t structure */ + timestamp.secs = (time_t)tvb_get_ntoh48(tvb, offset); + timestamp.nsecs = (int)lround((double)(tvb_get_ntohs(tvb, offset + 6) >> 1) * THREAD_MC_32768_TO_NSEC_FACTOR); + if (tlv_type == THREAD_MC_TLV_ACTIVE_TSTAMP) { + proto_tree_add_time(tlv_tree, hf_thread_mc_tlv_active_tstamp, tvb, offset, 8, ×tamp); + } else { + proto_tree_add_time(tlv_tree, hf_thread_mc_tlv_pending_tstamp, tvb, offset, 8, ×tamp); + } + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_STATE: + { + /* Check length is consistent */ + if (tlv_len != 1) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_state, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_JOINER_DTLS_ENCAP: + { + tvbuff_t *sub_tvb; + + if (tlv_len > 0) { + sub_tvb = tvb_new_subset_length(tvb, offset, tlv_len); + call_dissector(thread_dtls_handle, sub_tvb, pinfo, tree); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_COMMISSIONER_UDP_PORT: + case THREAD_MC_TLV_JOINER_UDP_PORT: + { + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* UDP Port */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_udp_port, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_JOINER_IID: + { + if (tlv_len != 8) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* IID */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_iid, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_JOINER_ROUTER_LOCATOR: + { + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_jr_locator, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_JOINER_KEK: + { + if (tlv_len != 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_kek, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_PROVISIONING_URL: + { + /* Check length is consistent */ + if (tlv_len > 64) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_provisioning_url, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_VENDOR_NAME: + { + /* Check length is consistent */ + if (tlv_len > 32) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_name, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_VENDOR_MODEL: + { + /* Check length is consistent: TODO not specified in spec. */ + if (tlv_len > 32) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_model, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_VENDOR_SW_VERSION: + { + /* Check length is consistent */ + if (tlv_len > 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_sw_ver, tvb, offset, tlv_len, ENC_NA|ENC_UTF_8); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_VENDOR_DATA: + { + /* Check length is consistent */ + if (tlv_len > 64) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_too_long); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + /* Display it simply */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_data, tvb, offset, tlv_len, ENC_ASCII|ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_VENDOR_STACK_VERSION: + { + /* Check length is consistent */ + if (tlv_len != 6) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + guint8 build_u8; + guint16 build; + + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_stack_ver_oui, tvb, offset, 3, ENC_BIG_ENDIAN); + offset += 3; + build_u8 = tvb_get_guint8(tvb, offset); + offset++; + build = (guint16)build_u8 << 4; + build_u8 = tvb_get_guint8(tvb, offset); + build |= (guint16)build_u8 >> 4; + pi = proto_tree_add_uint(tlv_tree, hf_thread_mc_tlv_vendor_stack_ver_build, tvb, 0, 0, build); + PROTO_ITEM_SET_GENERATED(pi); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_stack_ver_rev, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_stack_ver_min, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_vendor_stack_ver_maj, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + break; + + case THREAD_MC_TLV_UDP_ENCAPSULATION: + { + tvbuff_t *sub_tvb; + guint16 src_port; + guint16 dst_port; + udp_hdr_t *udp_hdr; + guint8 *buffer; + + src_port = tvb_get_ntohs(tvb, offset); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_udp_encap_src_port, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + dst_port = tvb_get_ntohs(tvb, offset); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_udp_encap_dst_port, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + if (tlv_len >= 4) + { + /* Allocate a buffer for the fake UDP datagram and create the fake header. */ + buffer = (guint8 *)wmem_alloc(pinfo->pool, sizeof(udp_hdr_t) + (tlv_len - 4)); + + /* Create pseudo UDP header */ + udp_hdr = (udp_hdr_t *)buffer; + udp_hdr->src_port = g_htons(src_port); + udp_hdr->dst_port = g_htons(dst_port); + udp_hdr->length = g_htons(tlv_len + 4); /* Includes UDP header length */ + udp_hdr->checksum = 0; + /* Copy UDP payload in */ + tvb_memcpy(tvb, udp_hdr + 1, offset, tlv_len - 4); + /* Create child tvb */ + sub_tvb = tvb_new_child_real_data(tvb, buffer, tlv_len + 4, tvb_reported_length(tvb) + 4); + call_dissector(thread_udp_handle, sub_tvb, pinfo, tree); + } + offset += (tlv_len-4); + } + break; + + case THREAD_MC_TLV_IPV6_ADDRESS: + { + /* Check length is consistent */ + if (tlv_len != 16) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_ipv6_addr, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + /* case THREAD_MC_TLV_PENDING_TSTAMP: Handled in THREAD_MC_TLV_ACTIVE_TSTAMP case */ + + case THREAD_MC_TLV_DELAY_TIMER: + { + if (tlv_len != 4) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_delay_timer, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_CHANNEL_MASK: + { + proto_tree *cm_tree; + int i; + guint8 entries = 0; + guint16 check_len = tlv_len; + guint8 check_offset = offset + 1; /* Channel page first */ + guint8 masklen; + + /* Check consistency of entries */ + while (check_len > 0) { + + masklen = tvb_get_guint8(tvb, check_offset); + if (masklen == 0) { + break; /* Get out or we might spin forever */ + } + masklen += 2; /* Add in page and length */ + check_offset += masklen; + check_len -= masklen; + entries++; + } + + if (check_len != 0) { + /* Not an integer number of entries */ + expert_add_info(pinfo, proto_root, &ei_thread_mc_tlv_length_failed); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + for (i = 0; i < entries; i++) { + pi = proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_chan_mask, tvb, offset, 1, ENC_NA); + cm_tree = proto_item_add_subtree(pi, ett_thread_mc_chan_mask); + proto_tree_add_item(cm_tree, hf_thread_mc_tlv_chan_mask_page, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + masklen = tvb_get_guint8(tvb, offset); + proto_tree_add_item(cm_tree, hf_thread_mc_tlv_chan_mask_len, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(cm_tree, hf_thread_mc_tlv_chan_mask_mask, tvb, offset, masklen, ENC_NA); + offset += masklen; + } + } + } + break; + + case THREAD_MC_TLV_COUNT: + { + if (tlv_len != 1) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_count, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_PERIOD: + { + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_period, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_SCAN_DURATION: + { + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_scan_duration, tvb, offset, tlv_len, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_ENERGY_LIST: + { + proto_tree *it_tree; + int i; + + if ((chancount != THREAD_MC_INVALID_CHAN_COUNT) && ((tlv_len % chancount) == 0)) { + /* Go through the number of el_counts of scan */ + for (i = 0; i < (int)(tlv_len / (guint16)chancount); i++) { + pi = proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_el_count, tvb, offset, 1, ENC_NA); + proto_item_append_text(pi, " %d", i + 1); + it_tree = proto_item_add_subtree(pi, ett_thread_mc_el_count); + proto_tree_add_item(it_tree, hf_thread_mc_tlv_energy_list, tvb, offset, chancount, ENC_NA); + offset += chancount; + } + } else { + /* This might not work but try and display as string */ + /* Something wrong with channel count so just show it as a simple string */ + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_energy_list, tvb, offset, tlv_len, ENC_NA); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_DISCOVERY_REQUEST: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_discovery_req_ver, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_discovery_req_j, tvb, offset, 1, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + case THREAD_MC_TLV_DISCOVERY_RESPONSE: + { + /* Check length is consistent */ + if (tlv_len != 2) { + expert_add_info(pinfo, proto_root, &ei_thread_mc_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + } else { + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_discovery_rsp_ver, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_discovery_rsp_n, tvb, offset, 1, ENC_BIG_ENDIAN); + } + offset += tlv_len; + } + break; + + default: + proto_tree_add_item(tlv_tree, hf_thread_mc_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + } + return tvb_captured_length(tvb); +} + +static int +dissect_thread_nwd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *proto_root; + proto_tree *thread_nwd_tree; + proto_tree *tlv_tree; + tvbuff_t *sub_tvb; + guint offset = 0, tlv_offset; + proto_item *ti; + guint8 tlv_type, tlv_len; + + /* Create the protocol tree. */ + proto_root = proto_tree_add_item(tree, proto_thread_nwd, tvb, 0, tvb_reported_length(tvb), ENC_NA); + thread_nwd_tree = proto_item_add_subtree(proto_root, ett_thread_nwd); + + /* Thread Network Data TLVs */ + while (tvb_offset_exists(tvb, offset)) { + + /* Get the length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_len = tvb_get_guint8(tvb, offset + 1); + + ti = proto_tree_add_item(thread_nwd_tree, hf_thread_nwd_tlv, tvb, offset, tlv_len+2, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_thread_nwd_tlv); + + /* Type */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + tlv_type = tvb_get_guint8(tvb, offset) >> 1; + + /* Stable */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_stable, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s)", val_to_str(tlv_type, thread_nwd_tlv_vals, "Unknown (%d)")); + + /* Length */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + switch(tlv_type) { + case THREAD_NWD_TLV_HAS_ROUTE: + { + /* Has Route TLV can be top level TLV or sub-TLV */ + + /* Check length is consistent */ + if ((tlv_len % THREAD_NWD_TLV_HAS_ROUTE_SIZE) != 0) + { + expert_add_info(pinfo, proto_root, &ei_thread_nwd_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + proto_tree *has_route_tree; + guint i; + guint count = tlv_len / THREAD_NWD_TLV_HAS_ROUTE_SIZE; + + /* Add subtrees */ + for (i = 0; i < count; i++) { + ti = proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_has_route, tvb, offset, 1, ENC_NA); + has_route_tree = proto_item_add_subtree(ti, ett_thread_nwd_has_route); + proto_tree_add_item(has_route_tree, hf_thread_nwd_tlv_has_route_br_16, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(has_route_tree, hf_thread_nwd_tlv_has_route_pref, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + } + } + break; + + case THREAD_NWD_TLV_PREFIX: + { + guint8 prefix_len; + guint8 prefix_byte_len; + struct e_in6_addr prefix; + address prefix_addr; + + /* Domain ID */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_prefix_domain_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + tlv_offset = 1; + + /* Prefix Length */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_prefix_length, tvb, offset, 1, ENC_BIG_ENDIAN); + prefix_len = tvb_get_guint8(tvb, offset); + prefix_byte_len = (prefix_len + 7) / 8; + offset++; + tlv_offset++; + + /* Prefix */ + memset(&prefix.bytes, 0, sizeof(prefix)); + if (prefix_byte_len <= sizeof(prefix)) + tvb_memcpy(tvb, (guint8 *)&prefix.bytes, offset, prefix_byte_len); + proto_tree_add_ipv6(tlv_tree, hf_thread_nwd_tlv_prefix, tvb, offset, prefix_byte_len, &prefix); + set_address(&prefix_addr, AT_IPv6, 16, prefix.bytes); + proto_item_append_text(ti, " = %s/%d", address_to_str(wmem_packet_scope(), &prefix_addr), prefix_len); + offset += prefix_byte_len; + tlv_offset += prefix_byte_len; + + if (tlv_offset < tlv_len) { + proto_tree *sub_tlv_tree; + guint remainder = tlv_len - tlv_offset; + + ti = proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_sub_tlvs, tvb, offset, 1, ENC_NA); + sub_tlv_tree = proto_item_add_subtree(ti, ett_thread_nwd_prefix_sub_tlvs); + /* Call this dissector for sub-TLVs */ + sub_tvb = tvb_new_subset_length(tvb, offset, remainder); /* remove prefix length (1) and prefix (prefix_byte_len) */ + dissect_thread_nwd(sub_tvb, pinfo, sub_tlv_tree, data); + offset += remainder; + } + } + break; + + case THREAD_NWD_TLV_BORDER_ROUTER: + { + /* Border Router TLV can only be sub-TLV */ + + /* Check length is consistent */ + if ((tlv_len % 4) != 0) + { + expert_add_info(pinfo, proto_root, &ei_thread_nwd_len_size_mismatch); + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } else { + proto_tree *border_router_tree; + guint i; + guint count = tlv_len / 4; + + /* Add subtrees */ + for (i = 0; i < count; i++) { + ti = proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_border_router, tvb, offset, 1, ENC_NA); + border_router_tree = proto_item_add_subtree(ti, ett_thread_nwd_border_router); + + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_16, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_pref, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_p, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_s, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_d, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_c, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_r, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_o, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + proto_tree_add_item(border_router_tree, hf_thread_nwd_tlv_border_router_n, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + } + } + break; + + case THREAD_NWD_TLV_6LOWPAN_ID: + { + static const int * nwd_6lowpan_flags[] = { + &hf_thread_nwd_tlv_6lowpan_id_6co_flag_reserved, + &hf_thread_nwd_tlv_6lowpan_id_6co_flag_c, + &hf_thread_nwd_tlv_6lowpan_id_6co_flag_cid, + NULL + }; + + /* 6lowpan-ND */ + proto_tree_add_bitmask(tlv_tree, tvb, offset, hf_thread_nwd_tlv_6lowpan_id_6co_flag, ett_thread_nwd_6co_flag, nwd_6lowpan_flags, ENC_BIG_ENDIAN); + offset++; + + /* Context Length */ + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_6lowpan_id_6co_context_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + } + break; + + case THREAD_NWD_TLV_COMMISSIONING_DATA: + { + if (tlv_len > 0) { + sub_tvb = tvb_new_subset_length(tvb, offset, tlv_len); + call_dissector(thread_mc_handle, sub_tvb, pinfo, tlv_tree); + } + offset += tlv_len; + } + break; + + case THREAD_NWD_TLV_SERVICE: + { + guint8 flags; + guint8 s_data_len; + + /* Flags and S_id */ + flags = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_service_t, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_service_s_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + tlv_offset = 1; + + /* Enterprise number */ + if ((flags & THREAD_NWD_TLV_SERVICE_T) == 0) { + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_service_s_ent_num, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + tlv_offset += 4; + } + + /* S_data */ + s_data_len = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_service_s_data_len, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + tlv_offset++; + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_service_s_data, tvb, offset, s_data_len, ENC_NA); + offset += s_data_len; + tlv_offset += s_data_len; + + /* Server sub-TLVs */ + if (tlv_offset < tlv_len) { + proto_tree *sub_tlv_tree; + guint remainder = tlv_len - tlv_offset; + + ti = proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_sub_tlvs, tvb, offset, 1, ENC_NA); + sub_tlv_tree = proto_item_add_subtree(ti, ett_thread_nwd_prefix_sub_tlvs); + /* Call this dissector for sub-TLVs. Should only be server TLVs */ + sub_tvb = tvb_new_subset_length(tvb, offset, remainder); /* remove prefix length (1) and prefix (prefix_byte_len) */ + dissect_thread_nwd(sub_tvb, pinfo, sub_tlv_tree, data); + offset += remainder; + } + } + break; + + case THREAD_NWD_TLV_SERVER: + { + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_server_16, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_server_data, tvb, offset, tlv_len - 2, ENC_NA); + offset += tlv_len - 2; + } + break; + + default: + proto_tree_add_item(tlv_tree, hf_thread_nwd_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + offset += tlv_len; + } + } + return tvb_captured_length(tvb); +} + +static int +dissect_thread_coap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + coap_info *coinfo; + gchar *uri; + gchar **tokens; + + /* Obtain the CoAP info */ + coinfo = (coap_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_coap, 0); + uri = (gchar *)wmem_strbuf_get_str(coinfo->uri_str_strbuf); + + tokens = g_strsplit(uri, "/", 3); + if ((tokens[0] != NULL) && (tokens[1] != NULL)) { + /* No need to create a subset as we are dissecting the tvb as it is */ + dissector_try_string(thread_coap_namespace, tokens[1], tvb, pinfo, tree, NULL); + } + g_strfreev(tokens); + + return tvb_captured_length(tvb); +} + +static int dissect_thread_bcn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + ieee802154_packet *packet = (ieee802154_packet *)data; + + proto_item *ti, *beacon_root; + proto_tree *beacon_tree; + guint offset = 0; + const guint8 *ssid; + guint8 tlv_type, tlv_len; + proto_tree *tlv_tree = NULL; + + /* Reject the packet if data is NULL */ + if (!packet) return 0; + + /* Add ourself to the protocol column. */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Thread"); + /* Create the tree for this beacon. */ + beacon_root = proto_tree_add_item(tree, proto_thread_bcn, tvb, 0, -1, ENC_NA); + beacon_tree = proto_item_add_subtree(beacon_root, ett_thread_bcn); + + /* Update the info column. */ + col_clear(pinfo->cinfo, COL_INFO); + col_append_fstr(pinfo->cinfo, COL_INFO, "Beacon, Src: 0x%04x", packet->src16); + + /* Get and display the protocol id, must be 0x03 on all Thread beacons. */ + proto_tree_add_item(beacon_tree, hf_thread_bcn_protocol, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Get and display the beacon flags */ + proto_tree_add_item(beacon_tree, hf_thread_bcn_joining, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(beacon_tree, hf_thread_bcn_native, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(beacon_tree, hf_thread_bcn_version, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Get and display the network ID. */ + proto_tree_add_item_ret_string(beacon_tree, hf_thread_bcn_network_id, tvb, offset, 16, ENC_ASCII|ENC_NA, wmem_packet_scope(), &ssid); + col_append_fstr(pinfo->cinfo, COL_INFO, ", Network ID: %s", ssid); + offset += 16; + + /* See if we're at the end */ + if (offset >= tvb_captured_length(tvb)) { + return tvb_captured_length(tvb); + } + + /* XPANID */ + proto_tree_add_item(beacon_tree, hf_thread_bcn_epid, tvb, offset, 8, ENC_BIG_ENDIAN); + offset += 8; + + /* See if we're at the end */ + if (offset >= tvb_captured_length(tvb)) { + return tvb_captured_length(tvb); + } + + /* Steering data TLV present */ + + /* Get the length ahead of time to pass to next function so we can highlight + proper amount of bytes */ + tlv_len = tvb_get_guint8(tvb, offset+1); + + /* Type */ + ti = proto_tree_add_item(beacon_tree, hf_thread_bcn_tlv, tvb, offset, tlv_len+2, ENC_NA); + tlv_tree = proto_item_add_subtree(ti, ett_thread_bcn_tlv); + proto_tree_add_item(tlv_tree, hf_thread_bcn_tlv_type, tvb, offset, 1, ENC_BIG_ENDIAN); + + tlv_type = tvb_get_guint8(tvb, offset); + offset++; + + /* Add value name to value root label */ + proto_item_append_text(ti, " (%s)", val_to_str(tlv_type, thread_bcn_tlv_vals, "Unknown (%d)")); + + /* Length */ + proto_tree_add_item(tlv_tree, hf_thread_bcn_tlv_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset++; + + if (tlv_len) { /* Belt 'n' braces check */ + switch (tlv_type) { + case THREAD_BCN_TLV_STEERING_DATA: + proto_tree_add_item(tlv_tree, hf_thread_bcn_tlv_steering_data, tvb, offset, tlv_len, ENC_NA); + /* offset += tlv_len; */ + break; + default: + proto_tree_add_item(tlv_tree, hf_thread_bcn_tlv_unknown, tvb, offset, tlv_len, ENC_NA); + /* offset += tlv_len; */ + break; + } + } + return tvb_captured_length(tvb); +} + +static gboolean +dissect_thread_bcn_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + ieee802154_packet *packet = (ieee802154_packet *)data; + + /* Thread beacon frames can be 16 or 64-bit source */ + if (!packet) return FALSE; + if (!((packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) || + (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT))) return FALSE; + + if (tvb_captured_length(tvb) > 0) { + /* Thread beacons begin with a protocol identifier. */ + if (tvb_get_guint8(tvb, 0) != THREAD_BCN_PROTOCOL_ID) return FALSE; + dissect_thread_bcn(tvb, pinfo, tree, packet); + return TRUE; + } + return FALSE; +} + +void +proto_register_thread_address(void) +{ + static hf_register_info hf[] = { + + /* Generic TLV */ + { &hf_thread_address_tlv, + { "TLV", + "thread_address.tlv", + FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", + HFILL } + }, + + { &hf_thread_address_tlv_type, + { "Type", + "thread_address.tlv.type", + FT_UINT8, BASE_DEC, VALS(thread_address_tlv_vals), 0x0, + "Type of value", + HFILL } + }, + + { &hf_thread_address_tlv_length, + { "Length", + "thread_address.tlv.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of value", + HFILL } + }, + + { &hf_thread_address_tlv_unknown, + { "Unknown", + "thread_address.tlv.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", + HFILL } + }, +#if 0 + { &hf_thread_address_tlv_sub_tlvs, + { "Sub-TLV(s)", + "thread_address.tlv.sub_tlvs", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, +#endif + /* Type-Specific TLV Fields */ + { &hf_thread_address_tlv_target_eid, + { "Target EID", + "thread_address.tlv.target_eid", + FT_IPv6, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_ext_mac_addr, + { "Extended MAC Address", + "thread_address.tlv.ext_mac_addr", + FT_EUI64, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_rloc16, + { "RLOC16", + "thread_address.tlv.rloc16", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_ml_eid, + { "ML-EID", + "thread_address.tlv.ml_eid", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_status, + { "Status", + "thread_address.tlv.status", + FT_UINT8, BASE_DEC, VALS(thread_address_tlv_status_vals), 0x0, + NULL, + HFILL } + }, +#if 0 + { &hf_thread_address_tlv_attached_time, + { "Attached Time", + "thread_address.tlv.attached_time", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, +#endif + { &hf_thread_address_tlv_last_transaction_time, + { "Last Transaction Time", + "thread_address.tlv.last_transaction_time", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_router_mask_id_seq, + { "ID Sequence", + "thread_address.tlv.router_mask_id_seq", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_router_mask_assigned, + { "Assigned Router ID Mask", + "thread_address.tlv.router_mask_assigned", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_nd_option, + { "ND Option", + "thread_address.tlv.nd_option", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_address_tlv_nd_data, + { "ND Data", + "thread_address.tlv.nd_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + } + }; + + static gint *ett[] = { + &ett_thread_address, + &ett_thread_address_tlv, + }; + + static ei_register_info ei[] = { +#if 0 + { &ei_thread_address_tlv_length_failed, { "thread_address.tlv_length_failed", PI_UNDECODED, PI_WARN, "TLV Length inconsistent", EXPFILL }}, +#endif + { &ei_thread_address_len_size_mismatch, { "thread_address.len_size_mismatch", PI_UNDECODED, PI_WARN, "TLV Length & Size field disagree", EXPFILL }}, + }; + + expert_module_t* expert_thread_address; + + proto_thread_address = proto_register_protocol("Thread Address", "Thread Address", "thread_address"); + proto_register_field_array(proto_thread_address, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_thread_address = expert_register_protocol(proto_thread_address); + expert_register_field_array(expert_thread_address, ei, array_length(ei)); + + thread_address_handle = register_dissector("thread_address", dissect_thread_address, proto_thread_address); +} + +void +proto_register_thread_dg(void) +{ + static hf_register_info hf[] = { + + /* Generic TLV */ + { &hf_thread_dg_tlv, + { "TLV", + "thread_diagnostic.tlv", + FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", + HFILL } + }, + + { &hf_thread_dg_tlv_type, + { "Type", + "thread_diagnostic.tlv.type", + FT_UINT8, BASE_DEC, VALS(thread_dg_tlv_vals), 0x0, + "Type of value", + HFILL } + }, + + { &hf_thread_dg_tlv_length8, + { "Length", + "thread_diagnostic.tlv.len8", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of value (8-bit)", + HFILL } + }, + + { &hf_thread_dg_tlv_length16, + { "Length", + "thread_diagnostic.tlv.len16", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of value (16-bit)", + HFILL } + }, + + { &hf_thread_dg_tlv_general, + { "General", + "thread_diagnostic.tlv.general", + FT_BYTES, BASE_NONE, NULL, 0x0, + "General TLV, raw value", + HFILL } + }, + + { &hf_thread_dg_tlv_unknown, + { "Unknown", + "thread_diagnostic.tlv.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", + HFILL } + } + }; + + static gint *ett[] = { + &ett_thread_dg, + &ett_thread_dg_tlv, + }; + +#if 0 + static ei_register_info ei[] = { + { &ei_thread_dg_tlv_length_failed, { "thread_diagnostic.tlv_length_failed", PI_UNDECODED, PI_WARN, "TLV Length inconsistent", EXPFILL }}, + { &ei_thread_dg_len_size_mismatch, { "thread_diagnostic.len_size_mismatch", PI_UNDECODED, PI_WARN, "TLV Length & Size field disagree", EXPFILL }}, + }; + + expert_module_t* expert_thread_dg; +#endif + + proto_thread_dg = proto_register_protocol("Thread Diagnostics", "Thread Diagnostics", "thread_diagnostic"); + proto_register_field_array(proto_thread_dg, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +#if 0 + expert_thread_dg = expert_register_protocol(proto_thread_dg); + expert_register_field_array(expert_thread_dg, ei, array_length(ei)); +#endif + + thread_dg_handle = register_dissector("thread_diagnostic", dissect_thread_dg, proto_thread_dg); +} + +void +proto_register_thread_mc(void) +{ + static hf_register_info hf[] = { + + /* Generic TLV */ + { &hf_thread_mc_tlv, + { "TLV", + "thread_meshcop.tlv", + FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", + HFILL } + }, + + { &hf_thread_mc_tlv_type, + { "Type", + "thread_meshcop.tlv.type", + FT_UINT8, BASE_DEC, VALS(thread_mc_tlv_vals), 0x0, + "Type of value", + HFILL } + }, + + { &hf_thread_mc_tlv_length8, + { "Length", + "thread_meshcop.tlv.len8", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of value (8-bit)", + HFILL } + }, + + { &hf_thread_mc_tlv_length16, + { "Length", + "thread_meshcop.tlv.len16", + FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of value (16-bit)", + HFILL } + }, + + { &hf_thread_mc_tlv_unknown, + { "Unknown", + "thread_meshcop.tlv.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", + HFILL } + }, +#if 0 + { &hf_thread_mc_tlv_sub_tlvs, + { "Sub-TLV(s)", + "thread_meshcop.tlv.sub_tlvs", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, +#endif + /* Type-Specific TLV Fields */ + { &hf_thread_mc_tlv_channel_page, + { "Channel Page", + "thread_meshcop.tlv.channel_page", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_channel, + { "Channel", + "thread_meshcop.tlv.channel", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_pan_id, + { "PAN ID", + "thread_meshcop.tlv.pan_id", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_xpan_id, + { "Extended PAN ID", + "thread_meshcop.tlv.xpan_id", + FT_UINT64, BASE_HEX, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_net_name, + { "Network Name", + "thread_meshcop.tlv.net_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_pskc, + { "PSKc", + "thread_meshcop.tlv.pskc", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_master_key, + { "Master Key", + "thread_meshcop.tlv.master_key", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_net_key_seq_ctr, + { "Network Key Sequence Counter", + "thread_meshcop.tlv.net_key_seq_ctr", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_ml_prefix, + { "Mesh Local Prefix", + "thread_meshcop.tlv.ml_prefix", + FT_IPv6, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_steering_data, + { "Steering Data", + "thread_meshcop.tlv.steering_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_ba_locator, + { "Border Agent Locator", + "thread_meshcop.tlv.ba_locator", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_commissioner_id, + { "Commissioner ID", + "thread_meshcop.tlv.commissioner_id", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_commissioner_sess_id, + { "Commissioner Session ID", + "thread_meshcop.tlv.commissioner_sess_id", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_rot, + { "Rotation Time", + "thread_meshcop.tlv.sec_policy_rot", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_o, + { "Out-of-band Commissioning", + "thread_meshcop.tlv.sec_policy_o", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_MC_SEC_POLICY_MASK_O_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_n, + { "Native Commissioning", + "thread_meshcop.tlv.sec_policy_n", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_MC_SEC_POLICY_MASK_N_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_r, + { "Thread 1.x Routers", + "thread_meshcop.tlv.sec_policy_r", + FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), THREAD_MC_SEC_POLICY_MASK_R_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_c, + { "PSKc-based Commissioning", + "thread_meshcop.tlv.sec_policy_c", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_MC_SEC_POLICY_MASK_C_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_sec_policy_b, + { "Thread 1.x Beacons", + "thread_meshcop.tlv.sec_policy_b", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_MC_SEC_POLICY_MASK_B_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_state, + { "State", + "thread_meshcop.tlv.state", + FT_INT8, BASE_DEC, VALS(thread_mc_state_vals), 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_active_tstamp, + { "Active Timestamp", + "thread_meshcop.tlv.active_tstamp", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_pending_tstamp, + { "Pending Timestamp", + "thread_meshcop.tlv.pending_tstamp", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_udp_port, + { "UDP Port", + "thread_meshcop.tlv.udp_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_iid, + { "Interface Identifier", + "thread_meshcop.tlv.iid", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_jr_locator, + { "Joiner Router Locator", + "thread_meshcop.tlv.jr_locator", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_kek, + { "Key Encryption Key (KEK)", + "thread_meshcop.tlv.kek", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_provisioning_url, + { "Provisioning URL", + "thread_meshcop.tlv.provisioning_url", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_name, + { "Vendor Name", + "thread_meshcop.tlv.vendor_name", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_model, + { "Vendor Model", + "thread_meshcop.tlv.vendor_model", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_sw_ver, + { "Vendor Software Version", + "thread_meshcop.tlv.vendor_sw_ver", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_data, + { "Vendor Data", + "thread_meshcop.tlv.vendor_model", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_stack_ver_oui, + { "OUI", + "thread_meshcop.tlv.vendor_stack_ver_oui", + FT_UINT24, BASE_HEX, VALS(oui_vals), 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_stack_ver_build, + { "Build", + "thread_meshcop.tlv.vendor_stack_ver_build", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_stack_ver_rev, + { "Revision", + "thread_meshcop.tlv.vendor_stack_ver_rev", + FT_UINT8, BASE_DEC, NULL, THREAD_MC_STACK_VER_REV_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_stack_ver_min, + { "Minor", + "thread_meshcop.tlv.vendor_stack_ver_min", + FT_UINT8, BASE_DEC, NULL, THREAD_MC_STACK_VER_MIN_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_vendor_stack_ver_maj, + { "Major", + "thread_meshcop.tlv.vendor_stack_ver_maj", + FT_UINT8, BASE_DEC, NULL, THREAD_MC_STACK_VER_MAJ_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_udp_encap_src_port, + { "Source UDP Port", + "thread_meshcop.tlv.udp_encap_src_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_udp_encap_dst_port, + { "Destination UDP Port", + "thread_meshcop.tlv.udp_encap_dst_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_ipv6_addr, + { "IPv6 Address", + "thread_meshcop.tlv.ipv6_addr", + FT_IPv6, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_delay_timer, + { "Delay Timer", + "thread_meshcop.tlv.delay_timer", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_chan_mask, + { "Channel Mask", + "thread_meshcop.tlv.chan_mask", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_chan_mask_page, + { "Channel Page", + "thread_meshcop.tlv.chan_mask_page", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_chan_mask_len, + { "Mask Length", + "thread_meshcop.tlv.chan_mask_len", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_chan_mask_mask, + { "Mask", + "thread_meshcop.tlv.chan_mask_mask", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_el_count, + { "Count", + "thread_meshcop.tlv.el_count", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_count, + { "Count", + "thread_meshcop.tlv.count", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_period, + { "Period", + "thread_meshcop.tlv.period", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_scan_duration, + { "Scan Duration", + "thread_meshcop.tlv.scan_duration", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_energy_list, + { "Energy List", + "thread_meshcop.tlv.energy_list", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_discovery_req_ver, + { "Version", + "thread_meshcop.tlv.discovery_req_ver", + FT_UINT8, BASE_DEC, NULL, THREAD_MC_DISCOVERY_REQ_MASK_VER_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_discovery_req_j, + { "Joiner Flag", + "thread_meshcop.tlv.discovery_req_j", + FT_BOOLEAN, 8, TFS(&thread_mc_tlv_join_intent), THREAD_MC_DISCOVERY_REQ_MASK_J_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_discovery_rsp_ver, + { "Version", + "thread_meshcop.tlv.discovery_rsp_ver", + FT_UINT8, BASE_DEC, NULL, THREAD_MC_DISCOVERY_RSP_MASK_VER_MASK, + NULL, + HFILL } + }, + + { &hf_thread_mc_tlv_discovery_rsp_n, + { "Native Commissioning", + "thread_meshcop.tlv.discovery_rsp_n", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_MC_DISCOVERY_RSP_MASK_N_MASK, + NULL, + HFILL } + } + }; + + static gint *ett[] = { + &ett_thread_mc, + &ett_thread_mc_tlv, + &ett_thread_mc_chan_mask, + &ett_thread_mc_el_count + }; + + static ei_register_info ei[] = { + { &ei_thread_mc_tlv_length_failed, { "thread_meshcop.tlv_length_failed", PI_UNDECODED, PI_WARN, "TLV Length inconsistent", EXPFILL }}, + { &ei_thread_mc_len_size_mismatch, { "thread_meshcop.len_size_mismatch", PI_UNDECODED, PI_WARN, "TLV Length & Size field disagree", EXPFILL }}, + { &ei_thread_mc_len_too_long, { "thread_meshcop.len_too_long", PI_UNDECODED, PI_WARN, "TLV Length too long", EXPFILL }} + }; + + expert_module_t* expert_thread_mc; + + proto_thread_mc = proto_register_protocol("Thread MeshCoP", "Thread MeshCoP", "thread_meshcop"); + proto_register_field_array(proto_thread_mc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_thread_mc = expert_register_protocol(proto_thread_mc); + expert_register_field_array(expert_thread_mc, ei, array_length(ei)); + + thread_mc_handle = register_dissector("thread_meshcop", dissect_thread_mc, proto_thread_mc); +} + +void +proto_register_thread_nwd(void) +{ + static hf_register_info hf[] = { + + /* Generic TLV */ + { &hf_thread_nwd_tlv, + { "TLV", + "thread_nwd.tlv", + FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", + HFILL } + }, + + { &hf_thread_nwd_tlv_type, + { "Type", + "thread_nwd.tlv.type", + FT_UINT8, BASE_DEC, VALS(thread_nwd_tlv_vals), THREAD_NWD_TLV_TYPE_M, + "Type of value", + HFILL } + }, + + { &hf_thread_nwd_tlv_stable, + { "Stable", + "thread_nwd.tlv.stable", + FT_BOOLEAN, 8, NULL, THREAD_NWD_TLV_STABLE_M, + "Stability or transience of network data", + HFILL } + }, + + { &hf_thread_nwd_tlv_length, + { "Length", + "thread_nwd.tlv.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of value", + HFILL } + }, + + { &hf_thread_nwd_tlv_unknown, + { "Unknown", + "thread_nwd.tlv.unknown", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", + HFILL } + }, + + { &hf_thread_nwd_tlv_sub_tlvs, + { "Sub-TLV(s)", + "thread_nwd.tlv.sub_tlvs", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + /* Type-Specific TLV Fields */ + { &hf_thread_nwd_tlv_has_route, + { "Has Route", + "thread_nwd.tlv.has_route", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_has_route_br_16, + { "Border Router 16", + "thread_nwd.tlv.has_route.br_16", + FT_UINT16, BASE_HEX, NULL, 0x0, + "Has Route Border Router 16-bit address", + HFILL } + }, + + { &hf_thread_nwd_tlv_has_route_pref, + { "Preference", + "thread_nwd.tlv.has_route.pref", + FT_UINT8, BASE_DEC, NULL, THREAD_NWD_TLV_HAS_ROUTE_PREF, + "Has Route preference", + HFILL } + }, + + { &hf_thread_nwd_tlv_prefix_domain_id, + { "Domain ID", + "thread_nwd.tlv.prefix.domain_id", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Prefix Domain ID", + HFILL } + }, + + { &hf_thread_nwd_tlv_prefix_length, + { "Prefix Length", + "thread_nwd.tlv.prefix.length", + FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of Prefix", + HFILL } + }, + + { &hf_thread_nwd_tlv_prefix, + { "Prefix", + "thread_nwd.tlv.prefix", + FT_IPv6, BASE_NONE, NULL, 0x0, + "IPv6 prefix", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router, + { "Border Router", + "thread_nwd.tlv.border_router", + FT_NONE, BASE_NONE, NULL, 0x0, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_16, + { "Border Router 16", + "thread_nwd.tlv.border_router.16", + FT_UINT16, BASE_HEX, NULL, 0x0, + "Border Router 16-bit address", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_pref, + { "Preference", + "thread_nwd.tlv.border_router.pref", + FT_UINT8, BASE_DEC, NULL, THREAD_NWD_TLV_BORDER_ROUTER_PREF, + "Value of P_preference", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_p, + { "P Flag", + "thread_nwd.tlv.border_router.flag.p", + FT_BOOLEAN, 8, TFS(&tfs_thread_nwd_tlv_border_router_p), THREAD_NWD_TLV_BORDER_ROUTER_P, + "Value of P_preferred", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_s, + { "SLAAC", + "thread_nwd.tlv.border_router.flag.s", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_NWD_TLV_BORDER_ROUTER_S, + "Value of P_slaac", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_d, + { "DHCPv6", + "thread_nwd.tlv.border_router.flag.d", + FT_BOOLEAN, 8, TFS(&tfs_allowed_not_allowed), THREAD_NWD_TLV_BORDER_ROUTER_D, + "Value of P_dhcp", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_c, + { "C Flag", + "thread_nwd.tlv.border_router.flag.c", + FT_BOOLEAN, 8, TFS(&tfs_thread_nwd_tlv_border_router_c), THREAD_NWD_TLV_BORDER_ROUTER_C, + "Value of P_configure", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_r, + { "Default route", + "thread_nwd.tlv.border_router.flag.r", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), THREAD_NWD_TLV_BORDER_ROUTER_R, + "Value of P_default", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_o, + { "O Flag", + "thread_nwd.tlv.border_router.flag.o", + FT_BOOLEAN, 8, TFS(&tfs_thread_nwd_tlv_border_router_o), THREAD_NWD_TLV_BORDER_ROUTER_O, + "Value of P_on_mesh", + HFILL } + }, + + { &hf_thread_nwd_tlv_border_router_n, + { "DNS", + "thread_nwd.tlv.border_router.flag.n", + FT_BOOLEAN, 8, TFS(&tfs_available_not_available), THREAD_NWD_TLV_BORDER_ROUTER_N, + "Value of P_nd_dns", + HFILL } + }, + + { &hf_thread_nwd_tlv_6lowpan_id_6co_flag, + { "Flag", + "thread_nwd.tlv.6co.flag", + FT_UINT8, BASE_HEX, NULL, 0x00, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_6lowpan_id_6co_flag_c, + { "Compression Flag", + "thread_nwd.tlv.6co.flag.c", + FT_BOOLEAN, 8, TFS(&tfs_set_notset), ND_OPT_6CO_FLAG_C, + "This flag indicates if the context is valid for use in compression", + HFILL } + }, + + { &hf_thread_nwd_tlv_6lowpan_id_6co_flag_cid, + { "CID", + "thread_nwd.tlv.6co.flag.cid", + FT_UINT8, BASE_DEC, NULL, ND_OPT_6CO_FLAG_CID, + "Context Identifier for this prefix information", + HFILL } + }, + + { &hf_thread_nwd_tlv_6lowpan_id_6co_flag_reserved, + { "Reserved", + "thread_nwd.tlv.6co.flag.reserved", + FT_UINT8, BASE_DEC, NULL, ND_OPT_6CO_FLAG_RESERVED, + "Must be zero", + HFILL } + }, + + { &hf_thread_nwd_tlv_6lowpan_id_6co_context_length, + { "Context Length", + "thread_nwd.tlv.6co.context_length", + FT_UINT8, BASE_DEC, NULL, 0x00, + "The number of leading bits in the Context Prefix field that are valid", + HFILL } + }, +#if 0 + { &hf_thread_nwd_tlv_comm_data, + { "Commissioning Data", + "thread_nwd.tlv.comm_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Contains Thread Commissioning data", + HFILL } + }, +#endif + { &hf_thread_nwd_tlv_service_t, + { "T flag", + "thread_nwd.tlv.service.t", + FT_UINT8, BASE_HEX, NULL, THREAD_NWD_TLV_SERVICE_T, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_service_s_id, + { "Service Type ID", + "thread_nwd.tlv.service.s_id", + FT_UINT8, BASE_HEX, NULL, THREAD_NWD_TLV_SERVICE_S_ID, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_service_s_ent_num, + { "Enterprise Number", + "thread_nwd.tlv.service.s_ent_num", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_service_s_data_len, + { "Service Data Length", + "thread_nwd.tlv.service.s_data_len", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, + HFILL } + }, + + { &hf_thread_nwd_tlv_service_s_data, + { "Service Data", + "thread_nwd.tlv.service.s_data", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Service data in raw bytes", + HFILL } + }, + + { &hf_thread_nwd_tlv_server_16, + { "Server 16", + "thread_nwd.tlv.server.16", + FT_UINT16, BASE_HEX, NULL, 0x0, + "Server 16-bit address", + HFILL } + }, + + { &hf_thread_nwd_tlv_server_data, + { "Server Data", + "thread_nwd.tlv.server.data", + FT_BYTES, BASE_NONE, NULL, 0x0, + "Server data in raw bytes", + HFILL } + } + }; + + static gint *ett[] = { + &ett_thread_nwd, + &ett_thread_nwd_tlv, + &ett_thread_nwd_has_route, + &ett_thread_nwd_6co_flag, + &ett_thread_nwd_border_router, + &ett_thread_nwd_prefix_sub_tlvs + }; + + static ei_register_info ei[] = { +#if 0 + { &ei_thread_nwd_tlv_length_failed, { "thread_nwd.tlv_length_failed", PI_UNDECODED, PI_WARN, "TLV Length inconsistent", EXPFILL }}, +#endif + { &ei_thread_nwd_len_size_mismatch, { "thread_nwd.len_size_mismatch", PI_UNDECODED, PI_WARN, "TLV Length & Size field disagree", EXPFILL }}, + }; + + expert_module_t* expert_thread_nwd; + + proto_thread_nwd = proto_register_protocol("Thread Network Data", "Thread NWD", "thread_nwd"); + proto_register_field_array(proto_thread_nwd, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_thread_nwd = expert_register_protocol(proto_thread_nwd); + expert_register_field_array(expert_thread_nwd, ei, array_length(ei)); + + thread_address_nwd_handle = register_dissector("thread_nwd", dissect_thread_nwd, proto_thread_nwd); +} + +void proto_register_thread_bcn(void) +{ + static hf_register_info hf[] = { + + { &hf_thread_bcn_protocol, + { "Protocol ID", "thread_bcn.protocol", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_thread_bcn_joining, + { "Joining", "thread_bcn.joining", FT_BOOLEAN, 8, NULL, THREAD_BCN_JOINING, + NULL, HFILL }}, + + { &hf_thread_bcn_native, + { "Native", "thread_bcn.native", FT_BOOLEAN, 8, NULL, THREAD_BCN_NATIVE, + NULL, HFILL }}, + + { &hf_thread_bcn_version, + { "Version", "thread_bcn.version", FT_UINT8, BASE_DEC, NULL, THREAD_BCN_PROTOCOL_VERSION, + NULL, HFILL }}, + + { &hf_thread_bcn_network_id, + { "Network Name", "thread_bcn.network_name", FT_STRING, BASE_NONE, NULL, 0x0, + "A string that uniquely identifies this network.", HFILL }}, + + { &hf_thread_bcn_epid, + { "Extended PAN ID", "thread_bcn.epid", FT_EUI64, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_thread_bcn_tlv, + { "TLV", "thread_bcn.tlv", FT_NONE, BASE_NONE, NULL, 0x0, + "Type-Length-Value", HFILL }}, + + { &hf_thread_bcn_tlv_type, + { "Type", "thread_bcn.tlv.type", FT_UINT8, BASE_DEC, VALS(thread_bcn_tlv_vals), 0x0, + "Type of Value", HFILL }}, + + { &hf_thread_bcn_tlv_length, + { "Length", "thread_bcn.tlv.len", FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of Value", HFILL }}, + + { &hf_thread_bcn_tlv_steering_data, + { "Steering Data", "thread_bcn.tlv.steering_data", FT_BYTES, BASE_NONE, NULL, 0x0, + "Steering data for joining devices", HFILL }}, + + { &hf_thread_bcn_tlv_unknown, + { "Unknown", "thread_bcn.tlv.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, + "Unknown TLV, raw value", HFILL }} + }; + + /* NWK Layer subtrees */ + static gint *ett[] = { + &ett_thread_bcn, + &ett_thread_bcn_tlv + }; + + /* Register the protocol with Wireshark. */ + proto_thread_bcn = proto_register_protocol("Thread Beacon", "Thread Beacon", "thread_bcn"); + proto_register_field_array(proto_thread_bcn, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + /* Register the dissectors with Wireshark. */ + register_dissector("thread_bcn", dissect_thread_bcn, proto_thread_bcn); +} + +static void +proto_init_thread(void) +{ + /* Reset the sequence counter variables */ + thread_seq_ctr_acqd = FALSE; + memset(thread_seq_ctr_bytes, 0, 4); +} + +void +proto_register_thread(void) +{ + module_t *thread_module; + + proto_thread = proto_register_protocol("Thread", "Thread", "thread"); + + thread_module = prefs_register_protocol(proto_thread, proto_reg_handoff_thread); + prefs_register_bool_preference(thread_module, "thr_coap_decode", + "Decode CoAP for Thread", + "Try to decode CoAP for Thread", + &thread_coap_decode); + + prefs_register_string_preference(thread_module, "thr_seq_ctr", + "Thread sequence counter", + "32-bit sequence counter for hash", + (const char **)&thread_seq_ctr_str); + + prefs_register_bool_preference(thread_module, "thr_use_pan_id_in_key", + "Use PAN ID as first two octets of master key", + "Set if the PAN ID should be used as the first two octets of the master key (PAN ID LSB), (PAN ID MSB), Key[2]...", + &thread_use_pan_id_in_key); + + prefs_register_bool_preference(thread_module, "thr_auto_acq_thr_seq_ctr", + "Automatically acquire Thread sequence counter", + "Set if the Thread sequence counter should be automatically acquired from Key ID mode 2 MLE messages.", + &thread_auto_acq_seq_ctr); + + register_init_routine(proto_init_thread); +} + +void +proto_register_thread_coap(void) +{ + proto_thread_coap = proto_register_protocol("Thread CoAP", "Thread CoAP", "thread_coap"); + thread_coap_handle = register_dissector("thread_coap", dissect_thread_coap, proto_thread_coap); + + thread_coap_namespace = register_dissector_table("thread.coap_namespace", "Thread CoAP namespace", proto_thread_coap, FT_STRING, BASE_NONE); +} + +void +proto_reg_handoff_thread_mc(void) +{ + thread_dtls_handle = find_dissector_add_dependency("dtls", proto_thread_mc); + thread_udp_handle = find_dissector_add_dependency("udp", proto_thread_mc); + + dissector_add_string("thread.coap_namespace", "c", thread_mc_handle); +} + +void +proto_reg_handoff_thread_address(void) +{ + dissector_add_string("thread.coap_namespace", "a", thread_address_handle); + dissector_add_string("thread.coap_namespace", "n", thread_address_handle); +} + +void +proto_reg_handoff_thread_dg(void) +{ + dissector_add_string("thread.coap_namespace", "d", thread_dg_handle); +} + +void proto_reg_handoff_thread_bcn(void) +{ + /* Register our dissector with IEEE 802.15.4 */ + heur_dissector_add(IEEE802154_PROTOABBREV_WPAN_BEACON, dissect_thread_bcn_heur, "Thread Beacon", "thread_wlan_beacon", proto_thread_bcn, HEURISTIC_ENABLE); + + register_mle_key_hash_handler(KEY_HASH_THREAD, set_thread_mle_key); + register_ieee802154_mac_key_hash_handler(KEY_HASH_THREAD, set_thread_mac_key); +} + +void +proto_reg_handoff_thread(void) +{ + /* Thread Content-Format is opaque byte string, i.e. application/octet-stream */ + if (thread_coap_decode) { + dissector_add_string("media_type", "application/octet-stream", thread_coap_handle); + } else { + dissector_delete_string("media_type", "application/octet-stream", thread_coap_handle); + } + + proto_coap = proto_get_id_by_filter_name("coap"); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/tools/checkfiltername.pl b/tools/checkfiltername.pl index a4e79fd27e..f17346831a 100755 --- a/tools/checkfiltername.pl +++ b/tools/checkfiltername.pl @@ -420,6 +420,7 @@ sub is_from_other_protocol_whitelist { if (($proto_filename eq "packet-k12.c") && (index($_[0], "aal2") >= 0)) {return 1;} if (($proto_filename eq "packet-k12.c") && (index($_[0], "atm") >= 0)) {return 1;} if (($proto_filename eq "packet-m3ua.c") && (index($_[0], "mtp3") >= 0)) {return 1;} + if (($proto_filename eq "packet-mle.c") && (index($_[0], "wpan") >= 0)) {return 1;} if (($proto_filename eq "packet-mpeg-dsmcc.c") && (index($_[0], "mpeg_sect") >= 0)) {return 1;} if (($proto_filename eq "packet-mpeg-dsmcc.c") && (index($_[0], "etv.dsmcc") >= 0)) {return 1;} if (($proto_filename eq "packet-mpeg1.c") && (index($_[0], "rtp.payload_mpeg_") >= 0)) {return 1;} |