wireshark/epan/dissectors/packet-lwm.c
John Thacker 203cd2cba9 prefs: More cleanup, auto prefs
Remove callback function from pref registrations for dissectors that
don't need a callback. In other dissectors, move registration that
only needs to be done once inside the check for initialization,
avoiding some console messages when preferences are changed
("Duplicate dissectors (anonymous) and (anonymous) for protocol...")
and the like.

Add a couple auto preferences for dissectors missed in previous waves.

Ping #14319
2022-08-09 06:21:05 +00:00

952 lines
36 KiB
C

/* packet-lwm.c
* Dissector routines for the ATMEL Lightweight Mesh 1.1.1
* Copyright 2013 Martin Leixner <info@sewio.net>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*------------------------------------------------------------
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/expert.h>
#include <wsutil/filesystem.h>
#include "packet-ieee802154.h"
#include <epan/prefs.h>
#include <epan/strutil.h>
#include <wsutil/wsgcrypt.h>
/*LwMesh lengths*/
#define LWM_HEADER_BASE_LEN 7
#define LWM_MIC_LEN 4
#define LWM_MULTI_HEADER_LEN 2
/* Bit-masks for the FCF */
#define LWM_FCF_ACK_REQUEST 0x01
#define LWM_FCF_SEC_EN 0x02
#define LWM_FCF_LINK_LOCAL 0x04
#define LWM_FCF_MULTICAST 0x08
#define LWM_FCF_RESERVED 0xF0
#define LWM_MULTI_NON_MEM_RAD_MASK 0x000F
#define LWM_MULTI_NON_MEM_RAD_OFFSET 0
#define LWM_MULTI_MAX_NON_MEM_RAD_MASK 0x00F0
#define LWM_MULTI_MAX_NON_MEM_RAD_OFFSET 4
#define LWM_MULTI_MEM_RAD_MASK 0x0F00
#define LWM_MULTI_MEM_RAD_OFFSET 8
#define LWM_MULTI_MAX_MEM_RAD_MASK 0xF000
#define LWM_MULTI_MAX_MEM_RAD_OFFSET 12
/*Endpoints*/
#define LWM_SRC_ENDP_MASK 0xF0
#define LWM_SRC_ENDP_OFFSET 4
#define LWM_DST_ENDP_MASK 0x0F
#define LWM_DST_ENDP_OFFSET 0
/*Defined addresses*/
#define LWM_BCAST_ADDR 0xFFFF
/*Command IDs*/
#define LWM_CMD_ACK 0x00
#define LWM_CMD_ROUTE_ERR 0x01
#define LWM_CMD_ROUTE_REQ 0x02
#define LWM_CMD_ROUTE_REPLY 0x03
/*Lengths of command frames*/
#define LWM_CMD_FRAME_ACK_LEN 3
#define LWM_CMD_FRAME_ROUTE_ERR_LEN 6
#define LWM_CMD_FRAME_ROUTE_REQ_LEN 7
#define LWM_CMD_FRAME_ROUTE_REPLY_LEN 8
/*Values for multicast field*/
#define LWM_CMD_MULTI_ADDR_FALSE 0
#define LWM_CMD_MULTI_ADDR_TRUE 1
/*Defined strings*/
#define LWM_CMD_LINKQ_STRING "(Sent by Originate node)"
#define LWM_CMD_UNKNOWN_VAL_STRING "Unknown command (0x%02x)"
#define LWM_MULTI_UNICAST_STRING "(Unicast)"
#define LWM_MULTI_GROUP_STRING "(Group ID)"
/* Function declarations */
void proto_register_lwm(void);
void proto_reg_handoff_lwm(void);
/* User string with the decryption key. */
static const gchar *lwmes_key_str = NULL;
static gboolean lwmes_key_valid;
static guint8 lwmes_key[16];
/* Dissection Routines. */
static int dissect_lwm (tvbuff_t *, packet_info *, proto_tree *, void *data);
static int dissect_lwm_cmd_frame_ack (tvbuff_t *, packet_info *, proto_tree *);
static int dissect_lwm_cmd_frame_route_err (tvbuff_t *, packet_info *, proto_tree *);
static int dissect_lwm_cmd_frame_route_req (tvbuff_t *, packet_info *, proto_tree *);
static int dissect_lwm_cmd_frame_route_reply (tvbuff_t *, packet_info *, proto_tree *);
/* Initialize protocol and registered fields. */
static int proto_lwm = -1;
static int hf_lwm_fcf = -1;
static int hf_lwm_fcf_ack_req = -1;
static int hf_lwm_fcf_security = -1;
static int hf_lwm_fcf_linklocal = -1;
static int hf_lwm_fcf_multicast = -1;
static int hf_lwm_fcf_reserved = -1;
static int hf_lwm_seq = -1;
static int hf_lwm_src_addr = -1;
static int hf_lwm_dst_addr = -1;
static int hf_lwm_src_endp = -1;
static int hf_lwm_dst_endp = -1;
static int hf_lwm_multi_nmrad = -1;
static int hf_lwm_multi_mnmrad = -1;
static int hf_lwm_multi_mrad = -1;
static int hf_lwm_multi_mmrad = -1;
static int hf_lwm_mic = -1;
static int hf_lwm_cmd = -1;
static int hf_lwm_cmd_seq = -1;
static int hf_lwm_cmd_cm = -1;
static int hf_lwm_cmd_route_src = -1;
static int hf_lwm_cmd_route_dst = -1;
static int hf_lwm_cmd_route_multi = -1;
static int hf_lwm_cmd_linkquality = -1;
static int hf_lwm_cmd_forwlinkquality = -1;
static int hf_lwm_cmd_revlinkquality = -1;
/* Initialize protocol subtrees. */
static gint ett_lwm = -1;
static gint ett_lwm_fcf = -1;
static gint ett_lwm_cmd_tree = -1;
static gint ett_lwm_multi_tree = -1;
static expert_field ei_lwm_mal_error = EI_INIT;
static expert_field ei_lwm_n_src_broad = EI_INIT;
static expert_field ei_lwm_mismatch_endp = EI_INIT;
static expert_field ei_lwm_empty_payload = EI_INIT;
static expert_field ei_lwm_no_decryption_key = EI_INIT;
static expert_field ei_lwm_decryption_failed = EI_INIT;
static dissector_handle_t lwm_handle;
static const value_string lwm_cmd_names[] = {
{ LWM_CMD_ACK, "LwMesh ACK" },
{ LWM_CMD_ROUTE_ERR, "Route Error" },
{ LWM_CMD_ROUTE_REQ, "Route Request" },
{ LWM_CMD_ROUTE_REPLY, "Route Reply" },
{ 0, NULL }
};
static const value_string lwm_cmd_multi_names[] = {
{ LWM_CMD_MULTI_ADDR_FALSE, "FALSE" },
{ LWM_CMD_MULTI_ADDR_TRUE, "TRUE" },
{ 0, NULL }
};
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm_heur
* DESCRIPTION
* Heuristic interpreter for the Lightweight Mesh.
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_into *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree Wireshark uses to display packet.
* RETURNS
* Boolean value, whether it handles the packet or not.
*---------------------------------------------------------------
*/
static gboolean
dissect_lwm_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
guint8 endpt, srcep, dstep;
/* 1) first byte must have bits 0000xxxx */
if(tvb_get_guint8(tvb, 0) & LWM_FCF_RESERVED)
return (FALSE);
/* The header should be at least long enough for the base header. */
if (tvb_reported_length(tvb) < LWM_HEADER_BASE_LEN)
return (FALSE);
/* The endpoints should either both be zero, or both non-zero. */
endpt = tvb_get_guint8(tvb, 6);
srcep = (endpt & LWM_SRC_ENDP_MASK) >> LWM_SRC_ENDP_OFFSET;
dstep = (endpt & LWM_DST_ENDP_MASK) >> LWM_DST_ENDP_OFFSET;
if ((srcep == 0) && (dstep != 0))
return (FALSE);
if ((srcep != 0) && (dstep == 0))
return (FALSE);
dissect_lwm(tvb, pinfo, tree, data);
return (TRUE);
} /* dissect_lwm_heur */
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm
* DESCRIPTION
* Lightweight Mesh packet dissection routine for Wireshark.
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_info *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree Wireshark uses to display packet.
* RETURNS
* int - length of data processed, or 0 if not LWM.
*---------------------------------------------------------------
*/
static int dissect_lwm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint lwm_header_len;
guint8 lwm_fcf;
gboolean lwm_fcf_security;
gboolean lwm_fcf_multicast;
guint8 lwm_seq;
guint16 lwm_src_addr;
guint16 lwm_dst_addr;
guint8 lwm_endp_field;
guint8 lwm_src_endp;
guint8 lwm_dst_endp;
proto_tree *lwm_tree = NULL;
proto_item *ti_proto = NULL;
proto_item *ti;
tvbuff_t *new_tvb;
/*---------------------------------------------------------*/
/*Enter name of protocol to info field*/
col_set_str(pinfo->cinfo, COL_PROTOCOL, "LwMesh");
col_clear(pinfo->cinfo, COL_INFO);
/*Set base length of LWM header*/
lwm_header_len = LWM_HEADER_BASE_LEN;
/*--------------------------------------------------*/
/* */
/* Create LwMesh dissector tree */
/* */
/*--------------------------------------------------*/
if(tree){
/*Create subtree for the LwMesh*/
ti_proto = proto_tree_add_protocol_format(tree, proto_lwm, tvb, 0, -1, "Lightweight Mesh");
lwm_tree = proto_item_add_subtree(ti_proto, ett_lwm);
}
col_add_fstr(pinfo->cinfo, COL_INFO, "Lightweight Mesh");
/*--------------------------------------------------*/
/* */
/* Display LwMesh dissector tree */
/* */
/*--------------------------------------------------*/
/*Frame control fields*/
lwm_fcf = tvb_get_guint8(tvb, 0);
lwm_fcf_security = (lwm_fcf & LWM_FCF_SEC_EN);
lwm_fcf_multicast = (lwm_fcf & LWM_FCF_MULTICAST);
if(tree){
proto_tree *field_tree;
ti = proto_tree_add_uint(lwm_tree, hf_lwm_fcf, tvb, 0, 1, lwm_fcf);
field_tree = proto_item_add_subtree(ti, ett_lwm_fcf);
proto_tree_add_item(field_tree, hf_lwm_fcf_ack_req, tvb, 0, 1, ENC_NA);
proto_tree_add_item(field_tree, hf_lwm_fcf_security, tvb, 0, 1, ENC_NA);
proto_tree_add_item(field_tree, hf_lwm_fcf_linklocal, tvb, 0, 1, ENC_NA);
proto_tree_add_item(field_tree, hf_lwm_fcf_multicast, tvb, 0, 1, ENC_NA);
proto_tree_add_item(field_tree, hf_lwm_fcf_reserved, tvb, 0, 1, ENC_NA);
}
/*Sequence number*/
lwm_seq = tvb_get_guint8(tvb, 1);
proto_item_append_text(ti_proto, ", Sequence Number: %i", lwm_seq);
proto_tree_add_uint(lwm_tree, hf_lwm_seq, tvb, 1, 1, lwm_seq);
/*Network addresses*/
/*Parse Source address*/
lwm_src_addr = tvb_get_letohs(tvb, 2);
ti = proto_tree_add_uint(lwm_tree, hf_lwm_src_addr, tvb, 2, 2, lwm_src_addr);
if(lwm_src_addr < 0x8000){
proto_item_append_text(ti, " (Routing node)");
}else{
proto_item_append_text(ti, " (Non-routing node)");
}
/*Check value of source address*/
if(lwm_src_addr == LWM_BCAST_ADDR){
expert_add_info(pinfo, lwm_tree, &ei_lwm_n_src_broad);
}
/*Parse Destination address*/
lwm_dst_addr = tvb_get_letohs(tvb, 4);
if(lwm_dst_addr == LWM_BCAST_ADDR){
proto_tree_add_uint_format_value(lwm_tree, hf_lwm_dst_addr, tvb, 4, 2, lwm_dst_addr,
"Broadcast (0x%04x)", lwm_dst_addr);
}else{
ti = proto_tree_add_uint(lwm_tree, hf_lwm_dst_addr, tvb, 4, 2, lwm_dst_addr);
if(lwm_fcf_multicast){
proto_item_append_text(ti, " %s", LWM_MULTI_GROUP_STRING);
}else{
proto_item_append_text(ti, " %s", LWM_MULTI_UNICAST_STRING);
if(lwm_dst_addr < 0x8000){
proto_item_append_text(ti, " (Routing node)");
}else{
proto_item_append_text(ti, " (Non-routing node)");
}
}
}
/*Enter description to info field*/
col_append_fstr(pinfo->cinfo, COL_INFO, ", Nwk_Dst: 0x%04x, Nwk_Src: 0x%04x", lwm_dst_addr, lwm_src_addr);
/*Endpoints*/
lwm_endp_field = tvb_get_guint8(tvb, 6);
lwm_src_endp = (lwm_endp_field & LWM_SRC_ENDP_MASK) >> LWM_SRC_ENDP_OFFSET;
lwm_dst_endp = (lwm_endp_field & LWM_DST_ENDP_MASK) >> LWM_DST_ENDP_OFFSET;
ti = proto_tree_add_uint(lwm_tree, hf_lwm_src_endp, tvb, 6, 1, lwm_src_endp);
if(lwm_src_endp == 0){
proto_item_append_text(ti, " (Stack command endpoint)");
}
ti = proto_tree_add_uint(lwm_tree, hf_lwm_dst_endp, tvb, 6, 1, lwm_dst_endp);
if(lwm_dst_endp == 0){
proto_item_append_text(ti, " (Stack command endpoint)");
}
if( (lwm_src_endp == 0) && (lwm_dst_endp == 0)){
/*stack command endpoints*/
}
else if( (lwm_src_endp == 0) || (lwm_dst_endp == 0)){
/*If only one endpoint is 0, alert about that*/
col_append_str(pinfo->cinfo, COL_INFO, "[Stack command Endpoints mismatch]");
expert_add_info(pinfo, lwm_tree, &ei_lwm_mismatch_endp);
}
/*Multicast header*/
if( (lwm_fcf_multicast) ){
lwm_header_len += LWM_MULTI_HEADER_LEN;
if(tree){
proto_tree *multi_tree;
guint16 lwm_multi_header;
lwm_multi_header = tvb_get_letohs(tvb, 7);
multi_tree = proto_tree_add_subtree(lwm_tree, tvb, 7, 2, ett_lwm_multi_tree, NULL, "Multicast Header");
proto_tree_add_uint(multi_tree, hf_lwm_multi_nmrad, tvb, 7, 2,
(lwm_multi_header & LWM_MULTI_NON_MEM_RAD_MASK) >> LWM_MULTI_NON_MEM_RAD_OFFSET);
proto_tree_add_uint(multi_tree, hf_lwm_multi_mnmrad, tvb, 7, 2,
(lwm_multi_header & LWM_MULTI_MAX_NON_MEM_RAD_MASK) >> LWM_MULTI_MAX_NON_MEM_RAD_OFFSET);
proto_tree_add_uint(multi_tree, hf_lwm_multi_mrad, tvb, 7, 2,
(lwm_multi_header & LWM_MULTI_MEM_RAD_MASK) >> LWM_MULTI_MEM_RAD_OFFSET);
proto_tree_add_uint(multi_tree, hf_lwm_multi_mmrad, tvb, 7, 2,
(lwm_multi_header & LWM_MULTI_MAX_MEM_RAD_MASK) >> LWM_MULTI_MAX_MEM_RAD_OFFSET);
}
}
/*------------------------------*/
/* */
/* Dissect payload */
/* */
/*------------------------------*/
/*Note: exception will already have occurred if "short header"*/
if (tvb_reported_length(tvb) <= lwm_header_len) {
/*Empty payload*/
expert_add_info(pinfo, lwm_tree, &ei_lwm_empty_payload);
col_append_str(pinfo->cinfo, COL_INFO, "[Empty LwMesh Payload]");
return tvb_captured_length(tvb);
}
new_tvb = tvb_new_subset_remaining(tvb, lwm_header_len);
/*Encrypted data*/
if(lwm_fcf_security){
guint rlen;
gint start;
guint32 lwm_mic;
/*MIC field*/
rlen = tvb_reported_length(new_tvb);
start = (rlen >= LWM_MIC_LEN) ? (rlen-LWM_MIC_LEN) : 0;
/*An exception will occur if there are not enough bytes for the MIC */
proto_tree_add_item_ret_uint(lwm_tree, hf_lwm_mic, new_tvb, start, LWM_MIC_LEN, ENC_LITTLE_ENDIAN, &lwm_mic);
if(lwmes_key_valid)
{
ieee802154_packet *ieee_packet = NULL;
gint payload_length = 0;
gint length = 0;
gint payload_offset = 0;
guint8 block;
tvbuff_t *decrypted_tvb;
gcry_cipher_hd_t cypher_hd;
guint8* vector = NULL;
guint8* text =NULL;
guint8* text_dec =NULL;
guint8 i;
guint32 vmic;
guint32 nwkSecurityVector[4];
int gcrypt_err;
ieee_packet = (ieee802154_packet *)data;
memset(&nwkSecurityVector, 0, sizeof(nwkSecurityVector));
nwkSecurityVector[0] = lwm_seq;
nwkSecurityVector[1] = ((guint32)lwm_dst_addr<< 16) | lwm_dst_endp;
nwkSecurityVector[2]= ((guint32) lwm_src_addr<< 16) | lwm_src_endp;
nwkSecurityVector[3] = ((guint32)ieee_packet->dst_pan << 16) | (guint8)lwm_fcf;
payload_length=tvb_reported_length(new_tvb) - LWM_MIC_LEN;
/* ECB - Nwk security vector*/
text = (guint8 *)tvb_memdup(pinfo->pool, new_tvb, 0, payload_length);
payload_offset=0;
gcrypt_err = gcry_cipher_open(&cypher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
/*Decrypt the actual data */
while(payload_length>0)
{
if(gcrypt_err == 0) {
gcrypt_err = gcry_cipher_setkey(cypher_hd,(guint8 *)lwmes_key, 16);
}
if(gcrypt_err == 0) {
gcrypt_err = gcry_cipher_encrypt(cypher_hd,(guint8 *)nwkSecurityVector,16,(guint8 *)nwkSecurityVector,16);
}
if(gcrypt_err)
{
col_add_fstr(pinfo->cinfo, COL_INFO,
"Encrypted data (%i byte(s)) DECRYPT FAILED",
tvb_reported_length(new_tvb) - LWM_MIC_LEN);
expert_add_info(pinfo, lwm_tree, &ei_lwm_decryption_failed);
tvb_set_reported_length(new_tvb, tvb_reported_length(new_tvb) - LWM_MIC_LEN);
call_data_dissector(new_tvb, pinfo, lwm_tree);
}
text_dec = &text[payload_offset];
vector = (guint8 *)nwkSecurityVector;
block = (payload_length < 16) ? payload_length : 16;
for (i = 0; i < block; i++)
{
text_dec[i] ^= vector[i];
vector[i] ^= text_dec[i];
}
payload_offset += block;
payload_length -= block;
gcry_cipher_reset(cypher_hd);
}
gcry_cipher_close(cypher_hd);
vmic = nwkSecurityVector[0] ^ nwkSecurityVector[1] ^ nwkSecurityVector[2] ^ nwkSecurityVector[3];
length = tvb_reported_length(new_tvb) - LWM_MIC_LEN;
if(vmic == lwm_mic)
{
decrypted_tvb = tvb_new_real_data(text,length, length);
call_data_dissector(decrypted_tvb, pinfo, lwm_tree);
/* XXX - needed?
add_new_data_source(pinfo, decrypted_tvb, "Decrypted LWmesh Payload"); */
col_append_fstr(pinfo->cinfo, COL_INFO, ", MIC SUCCESS");
}
else
{
col_add_fstr(pinfo->cinfo, COL_INFO,
"Encrypted data (%i byte(s)) MIC FAILURE",
tvb_reported_length(new_tvb) - LWM_MIC_LEN);
tvb_set_reported_length(new_tvb, tvb_reported_length(new_tvb) - LWM_MIC_LEN);
call_data_dissector(new_tvb, pinfo, lwm_tree);
}
}
else
{
col_add_fstr(pinfo->cinfo, COL_INFO,
"Encrypted data (%i byte(s)) NO DECRYPT KEY",
tvb_reported_length(new_tvb) - LWM_MIC_LEN);
expert_add_info(pinfo, lwm_tree, &ei_lwm_no_decryption_key);
tvb_set_reported_length(new_tvb, tvb_reported_length(new_tvb) - LWM_MIC_LEN);
call_data_dissector(new_tvb, pinfo, lwm_tree);
}
}
/*stack command endpoint 0 and not secured*/
else if( (lwm_src_endp == 0) && (lwm_dst_endp == 0) ){
proto_tree *lwm_cmd_tree;
guint8 lwm_cmd;
guint len;
/*----------------------------------------------------------------------*/
/* */
/* Call command dissector (depends on value of first byte of payload) */
/* */
/*----------------------------------------------------------------------*/
lwm_cmd = tvb_get_guint8(new_tvb, 0);
col_clear(pinfo->cinfo, COL_INFO); /*XXX: why ?*/
col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
val_to_str(lwm_cmd, lwm_cmd_names, LWM_CMD_UNKNOWN_VAL_STRING));
lwm_cmd_tree = proto_tree_add_subtree(lwm_tree, new_tvb, 0, -1, ett_lwm_cmd_tree, &ti,
val_to_str(lwm_cmd, lwm_cmd_names, LWM_CMD_UNKNOWN_VAL_STRING));
proto_tree_add_uint(lwm_cmd_tree, hf_lwm_cmd, new_tvb, 0, 1, lwm_cmd);
switch (lwm_cmd) {
case LWM_CMD_ACK:
len = dissect_lwm_cmd_frame_ack(new_tvb, pinfo, lwm_cmd_tree);
break;
case LWM_CMD_ROUTE_ERR:
len = dissect_lwm_cmd_frame_route_err(new_tvb, pinfo, lwm_cmd_tree);
break;
case LWM_CMD_ROUTE_REQ:
len = dissect_lwm_cmd_frame_route_req(new_tvb, pinfo, lwm_cmd_tree);
break;
case LWM_CMD_ROUTE_REPLY:
len = dissect_lwm_cmd_frame_route_reply(new_tvb, pinfo, lwm_cmd_tree);
break;
default:
/*Unknown command*/
expert_add_info_format(pinfo, lwm_cmd_tree, &ei_lwm_mal_error, "Unknown command");
call_data_dissector(new_tvb, pinfo, lwm_cmd_tree);
return tvb_captured_length(tvb);
}
proto_item_set_len(ti, len);
/*Here only if additional data after valid 'cmd' data*/
/*Note: exception will have already occurred if tvb was missing required bytes for 'cmd'*/
/* Report error if additional undissected data*/
if (len < tvb_reported_length(new_tvb)) {
/*unknown additional data*/
expert_add_info_format(pinfo, lwm_cmd_tree, &ei_lwm_mal_error,
"Size is %i byte(s), instead of %i bytes", tvb_reported_length(new_tvb), len);
new_tvb = tvb_new_subset_remaining(new_tvb, len);
call_data_dissector(new_tvb, pinfo, lwm_tree);
}
}
else{
/*unknown data*/
call_data_dissector(new_tvb, pinfo, lwm_tree);
}
return tvb_captured_length(tvb);
} /* dissect_lwm */
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm_cmd_frame_ack
* DESCRIPTION
* LwMesh command frame - Ack.
*
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_info *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree wireshark uses to display packet.
* RETURNS
* int length - amount of data processed
*---------------------------------------------------------------
*/
static int dissect_lwm_cmd_frame_ack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *lwm_cmd_tree)
{
guint8 lwm_seq;
/*Get fields*/
lwm_seq = tvb_get_guint8(tvb, 1);
col_append_fstr(pinfo->cinfo, COL_INFO, ", Sequence number: %d", lwm_seq);
if(lwm_cmd_tree){
proto_item_append_text(proto_tree_get_parent(lwm_cmd_tree), ", Sequence number: %d", lwm_seq);
proto_tree_add_uint(lwm_cmd_tree, hf_lwm_cmd_seq, tvb, 1, 1, lwm_seq);
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_cm, tvb, 2, 1, ENC_NA);
}
return LWM_CMD_FRAME_ACK_LEN;
} /* dissect_lwm_cmd_frame_ack*/
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm_cmd_frame_route_err
* DESCRIPTION
* LwMesh command frame - Route error.
*
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_info *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree wireshark uses to display packet.
* RETURNS
* int length - amount of data processed
*---------------------------------------------------------------
*/
static int dissect_lwm_cmd_frame_route_err(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *lwm_cmd_tree)
{
if(lwm_cmd_tree){
proto_item *ti;
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_src, tvb, 1, 2, ENC_LITTLE_ENDIAN);
ti = proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_dst, tvb, 3, 2, ENC_LITTLE_ENDIAN);
if(tvb_get_guint8(tvb, 5) == LWM_CMD_MULTI_ADDR_TRUE){
proto_item_append_text(ti, " %s", LWM_MULTI_GROUP_STRING);
}else{
proto_item_append_text(ti, " %s", LWM_MULTI_UNICAST_STRING);
}
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_multi, tvb, 5, 1, ENC_NA);
}
return LWM_CMD_FRAME_ROUTE_ERR_LEN;
} /* dissect_lwm_cmd_frame_route_err*/
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm_cmd_frame_route_req
* DESCRIPTION
* LwMesh command frame - Route Request.
*
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_info *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree wireshark uses to display packet.
* RETURNS
* int length - amount of data processed
*---------------------------------------------------------------
*/
static int dissect_lwm_cmd_frame_route_req(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *lwm_cmd_tree)
{
if(lwm_cmd_tree){
proto_item *ti;
guint8 lwm_linkqual;
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_src, tvb, 1, 2, ENC_LITTLE_ENDIAN);
ti = proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_dst, tvb, 3, 2, ENC_LITTLE_ENDIAN);
if(tvb_get_guint8(tvb, 5) == LWM_CMD_MULTI_ADDR_TRUE){
proto_item_append_text(ti, " %s", LWM_MULTI_GROUP_STRING);
}else{
proto_item_append_text(ti, " %s", LWM_MULTI_UNICAST_STRING);
}
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_multi, tvb, 5, 1, ENC_NA);
lwm_linkqual = tvb_get_guint8(tvb, 6);
ti = proto_tree_add_uint(lwm_cmd_tree, hf_lwm_cmd_linkquality, tvb, 6, 1, lwm_linkqual);
if(lwm_linkqual == 255){
proto_item_append_text(ti, " %s", LWM_CMD_LINKQ_STRING);
}
}
return LWM_CMD_FRAME_ROUTE_REQ_LEN;
} /* dissect_lwm_cmd_frame_route_req*/
/*FUNCTION:------------------------------------------------------
* NAME
* dissect_lwm_cmd_frame_route_reply
* DESCRIPTION
* LwMesh command frame - Route Reply.
*
* PARAMETERS
* tvbuff_t *tvb - pointer to buffer containing raw packet.
* packet_info *pinfo - pointer to packet information fields
* proto_tree *tree - pointer to data tree wireshark uses to display packet.
* RETURNS
* int length - amount of data processed
*---------------------------------------------------------------
*/
static int dissect_lwm_cmd_frame_route_reply(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *lwm_cmd_tree)
{
if(lwm_cmd_tree){
proto_item *ti;
guint8 lwm_revlinkqual;
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_src, tvb, 1, 2, ENC_LITTLE_ENDIAN);
ti = proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_dst, tvb, 3, 2, ENC_LITTLE_ENDIAN);
if(tvb_get_guint8(tvb, 5) == LWM_CMD_MULTI_ADDR_TRUE){
proto_item_append_text(ti, " %s", LWM_MULTI_GROUP_STRING);
}else{
proto_item_append_text(ti, " %s", LWM_MULTI_UNICAST_STRING);
}
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_route_multi, tvb, 5, 1, ENC_NA);
proto_tree_add_item(lwm_cmd_tree, hf_lwm_cmd_forwlinkquality, tvb, 6, 1, ENC_NA);
lwm_revlinkqual = tvb_get_guint8(tvb, 7);
ti = proto_tree_add_uint(lwm_cmd_tree, hf_lwm_cmd_revlinkquality, tvb, 7, 1, lwm_revlinkqual);
if(lwm_revlinkqual == 255){
proto_item_append_text(ti, " %s", LWM_CMD_LINKQ_STRING);
}
}
return LWM_CMD_FRAME_ROUTE_REPLY_LEN;
} /* dissect_lwm_cmd_frame_route_reply*/
/*FUNCTION:------------------------------------------------------
* NAME
* proto_register_lwm
* DESCRIPTION
* IEEE 802.15.4 protocol registration routine.
* PARAMETERS
* none
* RETURNS
* void
*---------------------------------------------------------------
*/
void proto_register_lwm(void)
{
static hf_register_info hf[] = {
/*Frame control field*/
{ &hf_lwm_fcf,
{ "Frame control field", "lwm.fcf", FT_UINT8, BASE_HEX, NULL, 0x0,
"Control information for the frame.", HFILL }},
{ &hf_lwm_fcf_ack_req,
{ "Acknowledgment Request", "lwm.ack_req", FT_BOOLEAN, 8, NULL, LWM_FCF_ACK_REQUEST,
"Specifies whether an acknowledgment is required from the destination node.", HFILL }},
{ &hf_lwm_fcf_security,
{ "Security Enabled", "lwm.security", FT_BOOLEAN, 8, NULL, LWM_FCF_SEC_EN,
"Specifies whether the frame payload is encrypted.", HFILL }},
{ &hf_lwm_fcf_linklocal,
{ "Link Local", "lwm.linklocal", FT_BOOLEAN, 8, NULL, LWM_FCF_LINK_LOCAL,
"It may be set to one to prevent neighboring nodes from rebroadcasting a frame.", HFILL }},
{ &hf_lwm_fcf_multicast,
{ "Multicast", "lwm.multicast", FT_BOOLEAN, 8, NULL, LWM_FCF_MULTICAST,
"If the Multicast subfield is set to one, Multicast Header should be present and the Destination Address is a group address.", HFILL }},
{ &hf_lwm_fcf_reserved,
{ "Reserved bits", "lwm.fcf.reserved", FT_UINT8, BASE_HEX, NULL, LWM_FCF_RESERVED,
"The 4 bits are reserved.", HFILL }},
/*Other fields*/
{ &hf_lwm_seq,
{ "Sequence Number", "lwm.seq", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies the sequence identifier for the frame.", HFILL }},
{ &hf_lwm_src_addr,
{ "Network Source Address", "lwm.src_addr", FT_UINT16, BASE_HEX, NULL, 0x0,
"Specifies the network address of the node originating the frame.", HFILL }},
{ &hf_lwm_dst_addr,
{ "Network Destination Address", "lwm.dst_addr", FT_UINT16, BASE_HEX, NULL, 0x0,
"Specifies the network address of the destination node or group address for multicast messages.", HFILL }},
{ &hf_lwm_src_endp,
{ "Source Endpoint", "lwm.src_endp", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies the source endpoint identifier.", HFILL }},
{ &hf_lwm_dst_endp,
{ "Destination Endpoint", "lwm.dst_endp", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies the destination endpoint identifier.", HFILL }},
/*Multicast header*/
{ &hf_lwm_multi_nmrad,
{ "Non-member Radius", "lwm.multi_nmrad", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies remaining radius (number of hops) for Non-members of multicast group.", HFILL }},
{ &hf_lwm_multi_mnmrad,
{ "Maximum Non-member Radius", "lwm.multi_mnmrad", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies maximum radius (number of hops) for Non-members of multicast group.", HFILL }},
{ &hf_lwm_multi_mrad,
{ "Member Radius", "lwm.multi_mrad", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies remaining radius (number of hops) for Members of multicast group.", HFILL }},
{ &hf_lwm_multi_mmrad,
{ "Maximum Member Radius", "lwm.multi_mmrad", FT_UINT8, BASE_DEC, NULL, 0x0,
"Specifies maximum radius (number of hops) for Members of multicast group.", HFILL }},
/*MIC, security*/
{ &hf_lwm_mic,
{ "Message Integrity Code", "lwm.mic", FT_UINT32, BASE_HEX, NULL, 0x0,
"Specifies Message Integrity Code (MIC).", HFILL }},
/*----------------------------------*/
/* */
/* Command Frames Specific Fields */
/* */
/*----------------------------------*/
{ &hf_lwm_cmd,
{ "Command ID", "lwm.cmd", FT_UINT8, BASE_HEX, VALS(lwm_cmd_names), 0x0,
"It contains Command ID value.", HFILL }},
/* Command Frame - Ack */
{ &hf_lwm_cmd_seq,
{ "Sequence number", "lwm.cmd.seq", FT_UINT8, BASE_DEC, NULL, 0x0,
"It contains a network sequence number of a frame that is being acknowledged.", HFILL }},
{ &hf_lwm_cmd_cm,
{ "Control Message", "lwm.cmd.cm", FT_UINT8, BASE_HEX, NULL, 0x0,
"It contains an arbitrary value that can be set on the sending side.", HFILL }},
/* Part of Command Frames - Route Request, Route Reply*/
{ &hf_lwm_cmd_route_src,
{ "Source address", "lwm.cmd.route_src", FT_UINT16, BASE_HEX, NULL, 0x0,
"It contains a source network address from the frame that cannot be routed", HFILL }},
{ &hf_lwm_cmd_route_dst,
{ "Destination Address", "lwm.cmd.route_dst", FT_UINT16, BASE_HEX, NULL, 0x0,
"It contains a destination network address from the frame that cannot be routed", HFILL }},
{ &hf_lwm_cmd_route_multi,
{ "Multicast", "lwm.cmd.multi", FT_UINT8, BASE_HEX, VALS(lwm_cmd_multi_names), 0x0,
"If it set to 0, Destination Address field contains a network address. If it set to 1, Destination Address field contains a group ID.", HFILL }},
/* Part of Command Frame - Route Request */
{ &hf_lwm_cmd_linkquality,
{ "Link Quality", "lwm.cmd.linkq", FT_UINT8, BASE_DEC, NULL, 0x0,
"It contains a link quality value of the potential route accumulated over all hops towards the destination.", HFILL }},
/* Part of Command Frame - Route Reply */
{ &hf_lwm_cmd_forwlinkquality,
{ "Forward Link Quality", "lwm.cmd.flinkq", FT_UINT8, BASE_DEC, NULL, 0x0,
"It contains a value of the Link Quality field from the corresponding Route Request Command Frame.", HFILL }},
{ &hf_lwm_cmd_revlinkquality,
{ "Reverse Link Quality", "lwm.cmd.rlinkq", FT_UINT8, BASE_DEC, NULL, 0x0,
"It contains a link quality value of the discovered route accumulated over all hops towards the originator.", HFILL }},
};
/* Subtrees */
static gint *ett[] = {
&ett_lwm,
&ett_lwm_fcf,
&ett_lwm_multi_tree,
&ett_lwm_cmd_tree
};
static ei_register_info ei[] = {
{ &ei_lwm_mal_error, { "lwm.malformed_error", PI_MALFORMED, PI_ERROR, "Malformed Packet", EXPFILL }},
{ &ei_lwm_n_src_broad, { "lwm.not_src_broadcast", PI_COMMENTS_GROUP, PI_NOTE, "Source address can not be broadcast address !", EXPFILL }},
{ &ei_lwm_mismatch_endp, { "lwm.mismatch_endp", PI_COMMENTS_GROUP, PI_WARN, "Stack command Endpoints mismatch (should be 0, both)!", EXPFILL }},
{ &ei_lwm_empty_payload, { "lwm.empty_payload", PI_COMMENTS_GROUP, PI_WARN, "Empty LwMesh Payload!", EXPFILL }},
{ &ei_lwm_no_decryption_key, { "lwm.no_decryption_key", PI_PROTOCOL, PI_NOTE, "No encryption key set - can't decrypt", EXPFILL }},
{ &ei_lwm_decryption_failed, { "lwm.decryption_failed", PI_PROTOCOL, PI_WARN, "Decryption Failed", EXPFILL }},
};
module_t *lw_module;
expert_module_t* expert_lwm;
/* Register protocol name and description. */
proto_lwm = proto_register_protocol("Lightweight Mesh (v1.1.1)", "LwMesh", "lwm");
/* Register header fields and subtrees. */
proto_register_field_array(proto_lwm, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_lwm = expert_register_protocol(proto_lwm);
expert_register_field_array(expert_lwm, ei, array_length(ei));
lw_module = prefs_register_protocol(proto_lwm,proto_reg_handoff_lwm);
/* Register preferences for a decryption key */
/* TODO: Implement a UAT for multiple keys, and with more advanced key management. */
prefs_register_string_preference(lw_module, "lwmes_key", "Lw Decryption key",
"128-bit decryption key in hexadecimal format", (const char **)&lwmes_key_str);
/* Register dissector with Wireshark. */
lwm_handle = register_dissector("lwm", dissect_lwm, proto_lwm);
} /* proto_register_lwm */
/*FUNCTION:------------------------------------------------------
* NAME
* proto_reg_handoff_lwm
* DESCRIPTION
* Registers the lwm dissector with Wireshark.
* Will be called during Wireshark startup, and whenever
* preferences are changed.
* PARAMETERS
* none
* RETURNS
* void
*---------------------------------------------------------------
*/
void proto_reg_handoff_lwm(void)
{
static gboolean initialized = FALSE;
GByteArray *bytes;
gboolean res;
if (!initialized) {
/* Register our dissector with IEEE 802.15.4 */
dissector_add_for_decode_as(IEEE802154_PROTOABBREV_WPAN_PANID, lwm_handle);
heur_dissector_add(IEEE802154_PROTOABBREV_WPAN, dissect_lwm_heur, "Lightweight Mesh over IEEE 802.15.4", "lwm_wlan", proto_lwm, HEURISTIC_ENABLE);
initialized = TRUE;
}
/* Convert key to raw bytes */
bytes = g_byte_array_new();
res = hex_str_to_bytes(lwmes_key_str, bytes, FALSE);
lwmes_key_valid = (res && bytes->len >= IEEE802154_CIPHER_SIZE);
if (lwmes_key_valid) {
memcpy(lwmes_key, bytes->data, IEEE802154_CIPHER_SIZE);
}
g_byte_array_free(bytes, TRUE);
} /* proto_reg_handoff_lwm */
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/