summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docbook/release-notes.asciidoc3
-rw-r--r--epan/dissectors/CMakeLists.txt3
-rw-r--r--epan/dissectors/Makefile.am4
-rw-r--r--epan/dissectors/packet-ieee802154.c2
-rw-r--r--epan/dissectors/packet-mle.c2065
-rw-r--r--epan/dissectors/packet-mle.h32
-rw-r--r--epan/dissectors/packet-openthread.c113
-rw-r--r--epan/dissectors/packet-thread.c3470
-rwxr-xr-xtools/checkfiltername.pl1
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, &timestamp);
+ } else {
+ proto_tree_add_time(tlv_tree, hf_mle_tlv_pending_tstamp, payload_tvb, offset, 8, &timestamp);
+ }
+ }
+ 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, &timestamp);
+ } else {
+ proto_tree_add_time(tlv_tree, hf_thread_mc_tlv_pending_tstamp, tvb, offset, 8, &timestamp);
+ }
+ }
+ 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;}