diff options
author | Jaap Keuter <jaap.keuter@xs4all.nl> | 2009-10-03 10:06:19 +0000 |
---|---|---|
committer | Jaap Keuter <jaap.keuter@xs4all.nl> | 2009-10-03 10:06:19 +0000 |
commit | 07a1f3875e70c7af3a7b07610b199daa176defa8 (patch) | |
tree | ce298ef814a2f5f61c1273ee1974a77fc7f1b309 /epan/dissectors | |
parent | 3138f91c9a2a9f8052e9616601aefd5fc74c521a (diff) |
From Owen Kirby:
The attached patch adds a dissector for IPv6 over IEEE 802.15.4 (aka 6LoWPAN).
The protocol is specified in RFC 4944. This dissector also processes the
6LoWPAN draft header compression scheme in draft-ietf-6lowpan-hc-05.
svn path=/trunk/; revision=30268
Diffstat (limited to 'epan/dissectors')
-rw-r--r-- | epan/dissectors/Makefile.common | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-6lowpan.c | 2111 | ||||
-rw-r--r-- | epan/dissectors/packet-6lowpan.h | 161 |
3 files changed, 2274 insertions, 0 deletions
diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common index 7cfc5fab36..621d7fb8de 100644 --- a/epan/dissectors/Makefile.common +++ b/epan/dissectors/Makefile.common @@ -187,6 +187,7 @@ DISSECTOR_SRC = \ packet-3com-njack.c \ packet-3com-xns.c \ packet-3g-a11.c \ + packet-6lowpan.c \ packet-9p.c \ packet-aarp.c \ packet-acap.c \ @@ -938,6 +939,7 @@ DISSECTOR_SRC = \ # DISSECTOR_INCLUDES = \ $(PIDL_DISSECTOR_INCLUDES) \ + packet-6lowpan.h \ packet-acn.h \ packet-acp133.h \ packet-acse.h \ diff --git a/epan/dissectors/packet-6lowpan.c b/epan/dissectors/packet-6lowpan.c new file mode 100644 index 0000000000..2a3d8c7f01 --- /dev/null +++ b/epan/dissectors/packet-6lowpan.c @@ -0,0 +1,2111 @@ +/* packet-6lowpan.c + * Routines for 6LoWPAN packet disassembly + * By Owen Kirby <osk@exegin.com> + * Copyright 2009 Owen Kirby + * + * $Id$ + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <glib.h> +#include <epan/packet.h> +#include <epan/expert.h> +#include <epan/reassemble.h> +#include <epan/ipproto.h> +#include <epan/in_cksum.h> +#include "epan/dissectors/packet-ipv6.h" +#include "epan/dissectors/packet-ieee802154.h" +#include "packet-6lowpan.h" + +/* Protocol fields handles. */ +static int proto_6lowpan = -1; +static int hf_6lowpan_pattern = -1; +static int hf_6lowpan_nhc_pattern = -1; + +/* Header compression fields. */ +static int hf_6lowpan_hc1_source_prefix = -1; +static int hf_6lowpan_hc1_source_ifc = -1; +static int hf_6lowpan_hc1_dest_prefix = -1; +static int hf_6lowpan_hc1_dest_ifc = -1; +static int hf_6lowpan_hc1_class = -1; +static int hf_6lowpan_hc1_next = -1; +static int hf_6lowpan_hc1_more = -1; +static int hf_6lowpan_hc2_udp_src = -1; +static int hf_6lowpan_hc2_udp_dst = -1; +static int hf_6lowpan_hc2_udp_len = -1; + +/* IPHC header field. */ +static int hf_6lowpan_iphc_flag_tf = -1; +static int hf_6lowpan_iphc_flag_nhdr = -1; +static int hf_6lowpan_iphc_flag_hlim = -1; +static int hf_6lowpan_iphc_flag_cid = -1; +static int hf_6lowpan_iphc_flag_sac = -1; +static int hf_6lowpan_iphc_flag_sam = -1; +static int hf_6lowpan_iphc_flag_mcast = -1; +static int hf_6lowpan_iphc_flag_dac = -1; +static int hf_6lowpan_iphc_flag_dam = -1; +static int hf_6lowpan_iphc_sci = -1; +static int hf_6lowpan_iphc_dci = -1; + +/* NHC IPv6 extension header fields. */ +static int hf_6lowpan_nhc_ext_eid = -1; +static int hf_6lowpan_nhc_ext_nh = -1; +static int hf_6lowpan_nhc_ext_next = -1; +static int hf_6lowpan_nhc_ext_length = -1; + +/* NHC UDP compression header fields. */ +static int hf_6lowpan_nhc_udp_checksum = -1; +static int hf_6lowpan_nhc_udp_src = -1; +static int hf_6lowpan_nhc_udp_dst = -1; + +/* Inline IPv6 header fields. */ +static int hf_6lowpan_traffic_class = -1; +static int hf_6lowpan_flow_label = -1; +static int hf_6lowpan_ecn = -1; +static int hf_6lowpan_dscp = -1; +static int hf_6lowpan_next_header = -1; +static int hf_6lowpan_hop_limit = -1; +static int hf_6lowpan_source = -1; +static int hf_6lowpan_dest = -1; + +/* Inline UDP header fields. */ +static int hf_6lowpan_udp_src = -1; +static int hf_6lowpan_udp_dst = -1; +static int hf_6lowpan_udp_len = -1; +static int hf_6lowpan_udp_checksum = -1; + +/* Broadcast header fields. */ +static int hf_6lowpan_bcast_seqnum = -1; + +/* Mesh header fields. */ +static int hf_6lowpan_mesh_v = -1; +static int hf_6lowpan_mesh_f = -1; +static int hf_6lowpan_mesh_hops = -1; +static int hf_6lowpan_mesh_orig16 = -1; +static int hf_6lowpan_mesh_orig64 = -1; +static int hf_6lowpan_mesh_dest16 = -1; +static int hf_6lowpan_mesh_dest64 = -1; + +/* Fragmentation header fields. */ +static int hf_6lowpan_frag_dgram_size = -1; +static int hf_6lowpan_frag_dgram_tag = -1; +static int hf_6lowpan_frag_dgram_offset = -1; + +/* Protocol tree handles. */ +static gint ett_6lowpan = -1; +static gint ett_6lowpan_hc1 = -1; +static gint ett_6lowpan_hc2_udp = -1; +static gint ett_6lowpan_iphc = -1; +static gint ett_6lowpan_nhc_ext = -1; +static gint ett_6lowpan_nhc_udp = -1; +static gint ett_6lowpan_bcast = -1; +static gint ett_6lowpan_mesh = -1; +static gint ett_6lowpan_mesh_flags = -1; +static gint ett_6lowpan_frag = -1; +static gint ett_6lopwan_traffic_class = -1; + +/* Dissector prototypes */ +static void proto_init_6lowpan (void); +static gboolean dissect_6lowpan_heur (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static void dissect_6lowpan (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static tvbuff_t * dissect_6lowpan_ipv6 (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static tvbuff_t * dissect_6lowpan_hc1 (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static tvbuff_t * dissect_6lowpan_bc0 (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static tvbuff_t * dissect_6lowpan_iphc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static struct lowpan_nhdr * dissect_6lowpan_iphc_nhc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset); +static tvbuff_t * dissect_6lowpan_mesh (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static tvbuff_t * dissect_6lowpan_frag (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean first); +static tvbuff_t * dissect_6lowpan_unknown (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); + +/* Helper functions. */ +static gboolean lowpan_dlsrc_to_ifcid (packet_info *pinfo, guint8 *ifcid); +static gboolean lowpan_dldst_to_ifcid (packet_info *pinfo, guint8 *ifcid); +static void lowpan_addr16_to_ifcid (guint16 addr, guint16 pan, guint8 *ifcid); +static tvbuff_t * lowpan_reassemble_ipv6 (struct ip6_hdr * ipv6, struct lowpan_nhdr * nhdr_list); +static guint8 lowpan_parse_nhc_proto (tvbuff_t *tvb, gint offset); + +/* Subdissector handles. */ +static dissector_handle_t data_handle; +static dissector_handle_t ipv6_handle; + +/* Value Strings */ +static const value_string lowpan_patterns [] = { + { LOWPAN_PATTERN_NALP, "Not a LoWPAN frame" }, + { LOWPAN_PATTERN_IPV6, "Uncompressed IPv6" }, + { LOWPAN_PATTERN_HC1, "Header compression" }, + { LOWPAN_PATTERN_BC0, "Broadcast" }, + { LOWPAN_PATTERN_IPHC, "IP header compression" }, + { LOWPAN_PATTERN_ESC, "Escape" }, + { LOWPAN_PATTERN_MESH, "Mesh" }, + { LOWPAN_PATTERN_FRAG1, "First fragment" }, + { LOWPAN_PATTERN_FRAGN, "Fragment" }, + { 0, NULL } +}; +static const true_false_string lowpan_compression = { + "Compressed", + "Inline" +}; +static const value_string lowpan_hc1_next [] = { + { LOWPAN_HC1_NEXT_NONE, "Inline" }, + { LOWPAN_HC1_NEXT_UDP, "UDP" }, + { LOWPAN_HC1_NEXT_ICMP, "ICMP" }, + { LOWPAN_HC1_NEXT_TCP, "TCP" }, + { 0, NULL } +}; +static const value_string lowpan_iphc_traffic [] = { + { LOWPAN_IPHC_FLOW_CLASS_LABEL, "Traffic class and flow label inline" }, + { LOWPAN_IPHC_FLOW_ECN_LABEL, "ECN and flow label inline" }, + { LOWPAN_IPHC_FLOW_CLASS, "Traffic class inline" }, + { LOWPAN_IPHC_FLOW_COMPRESSED, "Version, traffic class, and flow label compressed" }, + { 0, NULL } +}; +static const value_string lowpan_iphc_hop_limit [] = { + { LOWPAN_IPHC_HLIM_INLINE, "Inline" }, + { LOWPAN_IPHC_HLIM_1, "1" }, + { LOWPAN_IPHC_HLIM_64, "64" }, + { LOWPAN_IPHC_HLIM_255, "255" }, + { 0, NULL } +}; +static const true_false_string lowpan_iphc_addr_compression = { + "Stateful", + "Stateless" +}; +static const value_string lowpan_iphc_addr_modes [] = { + { LOWPAN_IPHC_ADDR_FULL_INLINE, "Inline" }, + { LOWPAN_IPHC_ADDR_64BIT_INLINE,"64-bits inline" }, + { LOWPAN_IPHC_ADDR_16BIT_INLINE,"16-bits inline" }, + { LOWPAN_IPHC_ADDR_COMPRESSED, "Compressed" }, + { 0, NULL } +}; +static const value_string lowpan_nhc_patterns [] = { + { LOWPAN_NHC_PATTERN_EXT, "IPv6 extension header" }, + { LOWPAN_NHC_PATTERN_UDP, "UDP compression header" }, + { 0, NULL } +}; +static const value_string lowpan_nhc_eid [] = { + { LOWPAN_NHC_EID_HOP_BY_HOP, "IPv6 hop-by-hop options" }, + { LOWPAN_NHC_EID_ROUTING, "IPv6 routing" }, + { LOWPAN_NHC_EID_FRAGMENT, "IPv6 fragment" }, + { LOWPAN_NHC_EID_DEST_OPTIONS, "IPv6 destination options" }, + { LOWPAN_NHC_EID_MOBILITY, "IPv6 mobility header" }, + { LOWPAN_NHC_EID_IPV6, "IPv6 header" }, + { 0, NULL } +}; +/* Reassembly Data */ +static int hf_6lowpan_fragments = -1; +static int hf_6lowpan_fragment = -1; +static int hf_6lowpan_fragment_overlap = -1; +static int hf_6lowpan_fragment_overlap_conflicts = -1; +static int hf_6lowpan_fragment_multiple_tails = -1; +static int hf_6lowpan_fragment_too_long_fragment = -1; +static int hf_6lowpan_fragment_error = -1; +static int hf_6lowpan_reassembled_in = -1; +static gint ett_6lowpan_fragment = -1; +static gint ett_6lowpan_fragments = -1; + +static const fragment_items lowpan_frag_items = { + /* Fragment subtrees */ + &ett_6lowpan_fragment, + &ett_6lowpan_fragments, + /* Fragment fields */ + &hf_6lowpan_fragments, + &hf_6lowpan_fragment, + &hf_6lowpan_fragment_overlap, + &hf_6lowpan_fragment_overlap_conflicts, + &hf_6lowpan_fragment_multiple_tails, + &hf_6lowpan_fragment_too_long_fragment, + &hf_6lowpan_fragment_error, + /* Reassembled in field */ + &hf_6lowpan_reassembled_in, + /* Tag */ + "6LoWPAN fragments" +}; + +static GHashTable *lowpan_fragment_table = NULL; +static GHashTable *lowpan_reassembled_table = NULL; + +/* Link-Local prefix used by 6LoWPAN. */ +static const guint8 lowpan_llprefix[8] = { + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Helper macro to convert a bit offset/length into a byte count. */ +#define BITS_TO_BYTE_LEN(bitoff, bitlen) ((bitlen)?(((bitlen) + ((bitoff)&0x07) + 7) >> 3):(0)) + +/* Structure for rebuilding UDP datagrams. */ +struct udp_hdr { + guint16 src_port; + guint16 dst_port; + guint16 length; + guint16 checksum; +}; + +/* Structure used to store decompressed header chains until reaseembly. */ +struct lowpan_nhdr { + /* List Linking */ + struct lowpan_nhdr *next; + /* Next Header */ + guint8 proto; + guint length; + guint reported; + guint8 hdr[]; +}; + +/*FUNCTION:------------------------------------------------------ + * NAME + * lowpan_addr16_to_ifcid + * DESCRIPTION + * Converts a short address to in interface identifier as + * per rfc 4944 section 6. + * PARAMETERS + * addr ; 16-bit short address. + * pan ; 16-bit PAN identifier. + * ifcid ; interface identifier (output). + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +static void +lowpan_addr16_to_ifcid(guint16 addr, guint16 pan, guint8 *ifcid) +{ + /* Build an EUI-64 from the short address and PAN identifier. */ + ifcid[0] = (pan >> 8) & 0xff; + ifcid[1] = (pan >> 0) & 0xff; + ifcid[2] = 0x00; + ifcid[3] = 0xff; + ifcid[4] = 0xfe; + ifcid[5] = 0x00; + ifcid[6] = (addr >> 8) & 0xff; + ifcid[7] = (addr >> 0) & 0xff; + + /* Clear the universal/local bit. */ + ifcid[0] &= ~(0x02); +} /* lowpan_addr16_to_ifcid */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * lowpan_dlsrc_to_ifcid + * DESCRIPTION + * Finds an interface identifier from the data-link source + * addressing. + * PARAMETERS + * pinfo ; packet information. + * ifcid ; interface identifier (output). + * RETURNS + * gboolean ; TRUE if an interface identifier could + * be found. + *--------------------------------------------------------------- + */ +static gboolean +lowpan_dlsrc_to_ifcid(packet_info *pinfo, guint8 *ifcid) +{ + ieee802154_packet * packet = pinfo->private_data; + + /* Check the link-layer address field. */ + if (pinfo->dl_src.type == AT_EUI64) { + memcpy(ifcid, pinfo->dl_src.data, LOWPAN_IFC_ID_LEN); + return TRUE; + } + + /* Sanity-Check to ensure the parent dissector was IEEE 802.15.4 */ + if (!pinfo->layer_names) return FALSE; + if (!pinfo->layer_names->str) return FALSE; + if (strstr(pinfo->layer_names->str, "wpan")) return FALSE; + + if (packet->src_addr_mode == IEEE802154_FCF_ADDR_EXT) { + guint64 addr; + addr = pntoh64(&packet->src.addr64); + memcpy(ifcid, &addr, LOWPAN_IFC_ID_LEN); + return TRUE; + } + if (packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) { + lowpan_addr16_to_ifcid(packet->src.addr16, packet->src_pan, ifcid); + return TRUE; + } + + /* Failed to find a link-layer source address. */ + return FALSE; +} /* lowpan_dlsrc_to_ifcid */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * lowpan_dldst_to_ifcid + * DESCRIPTION + * Finds an interface identifier from the data-link destination + * addressing. + * PARAMETERS + * pinfo ; packet information. + * ifcid ; interface identifier (output). + * RETURNS + * gboolean ; TRUE if an interface identifier could + * be found. + *--------------------------------------------------------------- + */ +static gboolean +lowpan_dldst_to_ifcid(packet_info *pinfo, guint8 *ifcid) +{ + ieee802154_packet * packet = pinfo->private_data; + + /* Check the link-layer address field. */ + if (pinfo->dl_dst.type == AT_EUI64) { + memcpy(ifcid, pinfo->dl_dst.data, LOWPAN_IFC_ID_LEN); + return TRUE; + } + + /* Sanity-Check to ensure the parent dissector was IEEE 802.15.4 */ + if (!pinfo->layer_names) return FALSE; + if (!pinfo->layer_names->str) return FALSE; + if (strstr(pinfo->layer_names->str, "wpan")) return FALSE; + + if (packet->dst_addr_mode == IEEE802154_FCF_ADDR_EXT) { + guint64 addr; + addr = pntoh64(&packet->dst.addr64); + memcpy(ifcid, &addr, LOWPAN_IFC_ID_LEN); + return TRUE; + } + if (packet->src_addr_mode == IEEE802154_FCF_ADDR_SHORT) { + lowpan_addr16_to_ifcid(packet->dst.addr16, packet->dst_pan, ifcid); + return TRUE; + } + + /* Failed to find a link-layer source address. */ + return FALSE; +} /* lowpan_dldst_to_ifcid */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * lowpan_reassemble_ipv6 + * DESCRIPTION + * Helper function to rebuild an IPv6 packet from the IPv6 + * header structure, and a list of next header structures. + * PARAMETERS + * ipv6 ; IPv6 Header. + * nhdr_list ; Next header list. + * RETURNS + * tvbuff_t * ; Reassembled IPv6 packet. + *--------------------------------------------------------------- + */ +static tvbuff_t * +lowpan_reassemble_ipv6(struct ip6_hdr *ipv6, struct lowpan_nhdr *nhdr_list) +{ + gint length = 0; + gint reported = 0; + guint8 * buffer; + guint8 * cursor; + struct lowpan_nhdr *nhdr; + + /* Compute the real and reported lengths. */ + for (nhdr = nhdr_list; nhdr; nhdr = nhdr->next) { + length += nhdr->length; + reported += nhdr->reported; + } + ipv6->ip6_plen = g_ntohs(reported); + + /* Allocate a buffer for the packet and copy in the IPv6 header. */ + buffer = g_malloc(length + sizeof(struct ip6_hdr)); + memcpy(buffer, ipv6, sizeof(struct ip6_hdr)); + cursor = buffer + sizeof(struct ip6_hdr); + + /* Add the next headers into the buffer. */ + for (nhdr = nhdr_list; nhdr; nhdr = nhdr->next) { + memcpy(cursor, nhdr->hdr, nhdr->length); + cursor += nhdr->length; + }; + + /* Return the reassembed packet. */ + return tvb_new_real_data(buffer, length + sizeof(struct ip6_hdr), reported + sizeof(struct ip6_hdr)); +} /* lowpan_reassemble_ipv6 */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * lowpan_parse_nhc_proto + * DESCRIPTION + * Parses the start of an 6LoWPAN NHC header to determine the + * next header protocol identifier. Will return IP_PROTO_NONE + * if no valid protocol could be determined. + * PARAMETERS + * tvb ; packet buffer. + * offset ; offset of the NHC. + * RETURNS + * guint8 ; IP_PROTO_* of the next header's protocol. + *--------------------------------------------------------------- + */ +static guint8 +lowpan_parse_nhc_proto(tvbuff_t *tvb, gint offset) +{ + /* Ensure that at least one byte exists. */ + if (!tvb_bytes_exist(tvb, offset, sizeof(guint8))) return IP_PROTO_NONE; + + /* Check for IPv6 extension headers. */ + if (tvb_get_bits8(tvb, offset<<3, LOWPAN_NHC_PATTERN_EXT_BITS) == LOWPAN_NHC_PATTERN_EXT) { + guint8 eid = (tvb_get_guint8(tvb, offset) & LOWPAN_NHC_EXT_EID) >> LOWPAN_NHC_EXT_EID_OFFSET; + switch (eid) { + case LOWPAN_NHC_EID_HOP_BY_HOP: + return IP_PROTO_HOPOPTS; + case LOWPAN_NHC_EID_ROUTING: + return IP_PROTO_ROUTING; + case LOWPAN_NHC_EID_FRAGMENT: + return IP_PROTO_FRAGMENT; + case LOWPAN_NHC_EID_DEST_OPTIONS: + return IP_PROTO_DSTOPTS; + case LOWPAN_NHC_EID_MOBILITY: + return IP_PROTO_MIPV6; + case LOWPAN_NHC_EID_IPV6: + /* I don't understand this option. */ + default: + /* Unknown protocol type. */ + return IP_PROTO_NONE; + }; + } + /* Check for compressed UDP headers. */ + if (tvb_get_bits8(tvb, offset<<3, LOWPAN_NHC_PATTERN_UDP_BITS) == LOWPAN_NHC_PATTERN_UDP) { + return IP_PROTO_UDP; + } + /* Unknown header type. */ + return IP_PROTO_NONE; +} /* lowpan_parse_nhc_proto */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_heur + * DESCRIPTION + * Heuristic dissector for 6LoWPAN. Checks if the pattern is + * a valid 6LoWPAN type, and not NALP. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; protocol display tree. + * RETURNS + * boolean ; TRUE if the tvbuff was dissected as a + * 6LoWPAN packet. If this returns FALSE, + * then no dissection will be attempted. + *--------------------------------------------------------------- + */ +static gboolean +dissect_6lowpan_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Check for valid patterns. */ + do { + /* Parse patterns until we find a match. */ + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_IPV6_BITS) == LOWPAN_PATTERN_IPV6) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_HC1_BITS) == LOWPAN_PATTERN_HC1) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_BC0_BITS) == LOWPAN_PATTERN_BC0) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_IPHC_BITS) == LOWPAN_PATTERN_IPHC) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_MESH_BITS) == LOWPAN_PATTERN_MESH) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAG1) break; + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAGN) break; + + /* If we get here, then we couldn't match to any pattern. */ + return FALSE; + } while(0); + + /* If we get here, then we found a matching pattern. */ + dissect_6lowpan(tvb, pinfo, tree); + return TRUE; +} /* dissect_6lowpan_heur */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan + * DESCRIPTION + * Dissector routine for 6LoWPAN packets. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; protocol display tree. + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +static void +dissect_6lowpan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_tree *volatile lowpan_tree = NULL; + proto_item *volatile lowpan_root = NULL; + tvbuff_t * next = tvb; + + /* Create the protocol tree. */ + if (tree) { + lowpan_root = proto_tree_add_protocol_format(tree, proto_6lowpan, tvb, 0, tvb_length(tvb), "6LoWPAN"); + lowpan_tree = proto_item_add_subtree(lowpan_root, ett_6lowpan); + } + /* Add the protocol name. */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "6LoWPAN"); + + /* Dissect headers in a loop until we find the end of the buffer. */ + while (next) { + /* Parse patterns until we find a match. */ + if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_IPV6_BITS) == LOWPAN_PATTERN_IPV6) { + next = dissect_6lowpan_ipv6(next, pinfo, lowpan_tree); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_HC1_BITS) == LOWPAN_PATTERN_HC1) { + next = dissect_6lowpan_hc1(next, pinfo, lowpan_tree); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_BC0_BITS) == LOWPAN_PATTERN_BC0) { + next = dissect_6lowpan_bc0(next, pinfo, lowpan_tree); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_IPHC_BITS) == LOWPAN_PATTERN_IPHC) { + next = dissect_6lowpan_iphc(next, pinfo, lowpan_tree); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_MESH_BITS) == LOWPAN_PATTERN_MESH) { + next = dissect_6lowpan_mesh(next, pinfo, lowpan_tree); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAG1) { + next = dissect_6lowpan_frag(next, pinfo, lowpan_tree, TRUE); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAGN) { + next = dissect_6lowpan_frag(next, pinfo, lowpan_tree, FALSE); + } + else { + next = dissect_6lowpan_unknown(next, pinfo, lowpan_tree); + } + } /* while */ +} /* dissect_6lowpan */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_ipv6 + * DESCRIPTION + * Dissector routine for an uncompressed IPv6 header type. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * offset ; offset to the start of the header. + * RETURNS + * tvbuff_t * ; The remaining payload to be parsed. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_ipv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + tvbuff_t * ipv6_tvb; + + /* Get and display the pattern. */ + if (tree) { + proto_tree_add_bits_item(tree, hf_6lowpan_pattern, tvb, 0, LOWPAN_PATTERN_IPV6_BITS, FALSE); + } + + /* Create a tvbuff subset for the ipv6 datagram. */ + ipv6_tvb = tvb_new_subset(tvb, sizeof(guint8), -1, tvb_reported_length(tvb) - sizeof(guint8)); + call_dissector(ipv6_handle, ipv6_tvb, pinfo, proto_tree_get_root(tree)); + + /* No data remaining, we gave it all to the IPv6 dissector. */ + return NULL; +} /* dissect_6lowpan_ipv6 */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_hc1 + * DESCRIPTION + * Dissector routine for a 6LoWPAN HC1 header. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * RETURNS + * tvbuff_t * ; The remaining payload to be parsed. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_hc1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset = 0; + gint bit_offset; + int i; + guint8 hc1_encoding; + guint8 hc_udp_encoding = 0; + guint8 next_header; + proto_tree * hc_tree; + proto_item * ti; + tvbuff_t * ipv6_tvb; + /* IPv6 header. */ + guint8 ipv6_class; + struct ip6_hdr ipv6; + struct lowpan_nhdr *nhdr_list; + + /*===================================================== + * Parse HC Encoding Flags + *===================================================== + */ + /* Create a tree for the HC1 Header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint16), "HC1 Encoding"); + hc_tree = proto_item_add_subtree(ti, ett_6lowpan_hc1); + } + + /* Get and display the pattern. */ + if (tree) { + proto_tree_add_bits_item(hc_tree, hf_6lowpan_pattern, tvb, 0, LOWPAN_PATTERN_HC1_BITS, FALSE); + } + offset += sizeof(guint8); + + /* Get and display the HC1 encoding bits. */ + hc1_encoding = tvb_get_guint8(tvb, offset); + next_header = ((hc1_encoding & LOWPAN_HC1_NEXT) >> 1); + if (tree) { + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_source_prefix, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_SOURCE_PREFIX); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_source_ifc, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_SOURCE_IFC); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_dest_prefix, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_DEST_PREFIX); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_dest_ifc, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_DEST_IFC); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_class, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_TRAFFIC_CLASS); + proto_tree_add_uint(hc_tree, hf_6lowpan_hc1_next, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_NEXT); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc1_more, tvb, offset, sizeof(guint8), hc1_encoding & LOWPAN_HC1_MORE); + } + offset += sizeof(guint8); + + /* Get and display the HC2 encoding bits, if present. */ + if (hc1_encoding & LOWPAN_HC1_MORE) { + if (next_header == LOWPAN_HC1_NEXT_UDP) { + hc_udp_encoding = tvb_get_guint8(tvb, offset); + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint8), "HC_UDP Encoding"); + hc_tree = proto_item_add_subtree(ti, ett_6lowpan_hc2_udp); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc2_udp_src, tvb, offset, sizeof(guint8), hc_udp_encoding & LOWPAN_HC2_UDP_SRCPORT); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc2_udp_dst, tvb, offset, sizeof(guint8), hc_udp_encoding & LOWPAN_HC2_UDP_DSTPORT); + proto_tree_add_boolean(hc_tree, hf_6lowpan_hc2_udp_len, tvb, offset, sizeof(guint8), hc_udp_encoding & LOWPAN_HC2_UDP_LENGTH); + } + offset += sizeof(guint8); + } + else { + /* HC1 states there are more bits, but an illegal next header was defined. */ + expert_add_info_format(pinfo, NULL, PI_MALFORMED, PI_ERROR, "HC1 more bits expected for illegal next header type."); + return NULL; + } + } + + /*===================================================== + * Parse Uncompressed IPv6 Header Fields + *===================================================== + */ + /* + * And now all hell breaks loose. After the header encoding fields, we are + * left with an assortment of optional fields from the IPv6 header, + * depending on which fields are present or not, the headers may not be + * aligned to an octet boundary. + * + * From now on we have to parse the uncompressed fields relative to a bit + * offset. + */ + bit_offset = offset << 3; + + /* Parse the traffic class and flow label. */ + ipv6_class = 0; + ipv6.ip6_flow = 0; + if (!(hc1_encoding & LOWPAN_HC1_TRAFFIC_CLASS)) { + /* Parse the traffic class. */ + ipv6_class = tvb_get_bits8(tvb, bit_offset, LOWPAN_IPV6_TRAFFIC_CLASS_BITS); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_traffic_class, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_IPV6_TRAFFIC_CLASS_BITS), ipv6_class); + } + bit_offset += LOWPAN_IPV6_TRAFFIC_CLASS_BITS; + + /* Parse the flow label. */ + ipv6.ip6_flow = tvb_get_bits32(tvb, bit_offset, LOWPAN_IPV6_FLOW_LABEL_BITS, FALSE); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_flow_label, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_IPV6_FLOW_LABEL_BITS), ipv6.ip6_flow); + } + bit_offset += LOWPAN_IPV6_FLOW_LABEL_BITS; + } + ipv6.ip6_flow = g_ntohl(ipv6.ip6_flow | (ipv6_class << LOWPAN_IPV6_FLOW_LABEL_BITS)); + ipv6.ip6_vfc = ((0x6 << 4) | (ipv6_class >> 4)); + + /* Parse the IPv6 next header field. */ + if (next_header == LOWPAN_HC1_NEXT_UDP) { + ipv6.ip6_nxt = IP_PROTO_UDP; + } + else if (next_header == LOWPAN_HC1_NEXT_ICMP) { + ipv6.ip6_nxt = IP_PROTO_ICMPV6; + } + else if (next_header == LOWPAN_HC1_NEXT_TCP) { + ipv6.ip6_nxt = IP_PROTO_TCP; + } + else { + /* Parse the next header field. */ + ipv6.ip6_nxt = tvb_get_bits8(tvb, bit_offset, LOWPAN_IPV6_NEXT_HEADER_BITS); + if (tree) { + proto_tree_add_uint_format(tree, hf_6lowpan_next_header, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_IPV6_NEXT_HEADER_BITS), ipv6.ip6_nxt, + "Next header: %s (0x%02x)", ipprotostr(ipv6.ip6_nxt), ipv6.ip6_nxt); + } + bit_offset += LOWPAN_IPV6_NEXT_HEADER_BITS; + } + + ipv6.ip6_hops = tvb_get_bits8(tvb, bit_offset, LOWPAN_IPV6_HOP_LIMIT_BITS); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_hop_limit, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_IPV6_HOP_LIMIT_BITS), ipv6.ip6_hops); + } + bit_offset += LOWPAN_IPV6_HOP_LIMIT_BITS; + + /*===================================================== + * Parse/Decompress IPv6 Source Address + *===================================================== + */ + offset = bit_offset; + if (!(hc1_encoding & LOWPAN_HC1_SOURCE_PREFIX)) { + for (i=0; i<8; i++, bit_offset += 8) { + ipv6.ip6_src.bytes[i] = tvb_get_bits8(tvb, bit_offset, 8); + } + } + else { + memcpy(ipv6.ip6_src.bytes, lowpan_llprefix, sizeof(lowpan_llprefix)); + } + if (!(hc1_encoding & LOWPAN_HC1_SOURCE_IFC)) { + for (i=8; i<16; i++, bit_offset += 8) { + ipv6.ip6_src.bytes[i] = tvb_get_bits8(tvb, bit_offset, 8); + } + } + /* Try to recover the source interface identifier from the packet info. */ + else if (pinfo->src.type == AT_EUI64) { + memcpy(&ipv6.ip6_src.bytes[8], pinfo->src.data, 8); + } + /* Try to recover the source interface identifier from the link layer. */ + else { + lowpan_dlsrc_to_ifcid(pinfo, &ipv6.ip6_src.bytes[8]); + } + /* Display the source address. */ + if (tree) { + proto_tree_add_ipv6(tree, hf_6lowpan_source, tvb, offset>>3, + BITS_TO_BYTE_LEN(offset, (bit_offset-offset)), (guint8 *)&ipv6.ip6_src); + } + + /*===================================================== + * Parse/Decompress IPv6 Destination Address + *===================================================== + */ + offset = bit_offset; + if (!(hc1_encoding & LOWPAN_HC1_DEST_PREFIX)) { + for (i=0; i<8; i++, bit_offset += 8) { + ipv6.ip6_dst.bytes[i] = tvb_get_bits8(tvb, bit_offset, 8); + } + } + else { + memcpy(ipv6.ip6_dst.bytes, lowpan_llprefix, sizeof(lowpan_llprefix)); + } + if (!(hc1_encoding & LOWPAN_HC1_DEST_IFC)) { + for (i=8; i<16; i++, bit_offset += 8) { + ipv6.ip6_dst.bytes[i] = tvb_get_bits8(tvb, bit_offset, 8); + } + } + /* Try to recover the destination interface identifier from the packet info. */ + else if (pinfo->dst.type == AT_EUI64) { + memcpy(&ipv6.ip6_dst.bytes[8], pinfo->dst.data, 8); + } + /* Try to recover the source interface identifier from the link layer. */ + else { + lowpan_dldst_to_ifcid(pinfo, &ipv6.ip6_dst.bytes[8]); + } + /* Display the destination address. */ + if (tree) { + proto_tree_add_ipv6(tree, hf_6lowpan_dest, tvb, offset>>3, + BITS_TO_BYTE_LEN(offset, (bit_offset-offset)), (guint8 *)&ipv6.ip6_dst); + } + + /*===================================================== + * Parse and Reconstruct the UDP Header + *===================================================== + */ + if ((hc1_encoding & LOWPAN_HC1_MORE) && (next_header == LOWPAN_HC1_NEXT_UDP)) { + struct udp_hdr udp; + gint length; + + /* Parse the source port. */ + offset = bit_offset; + if (hc_udp_encoding & LOWPAN_HC2_UDP_SRCPORT) { + udp.src_port = tvb_get_bits8(tvb, bit_offset, LOWPAN_UDP_PORT_COMPRESSED_BITS) + LOWPAN_PORT_12BIT_OFFSET; + bit_offset += LOWPAN_UDP_PORT_COMPRESSED_BITS; + } + else { + udp.src_port = tvb_get_bits16(tvb, bit_offset, LOWPAN_UDP_PORT_BITS, FALSE); + bit_offset += LOWPAN_UDP_PORT_BITS; + } + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_src, tvb, offset>>3, + BITS_TO_BYTE_LEN(offset, (bit_offset-offset)), udp.src_port); + } + udp.src_port = g_ntohs(udp.src_port); + + /* Parse the destination port. */ + offset = bit_offset; + if (hc_udp_encoding & LOWPAN_HC2_UDP_DSTPORT) { + udp.dst_port = tvb_get_bits8(tvb, bit_offset, LOWPAN_UDP_PORT_COMPRESSED_BITS) + LOWPAN_PORT_12BIT_OFFSET; + bit_offset += LOWPAN_UDP_PORT_COMPRESSED_BITS; + } + else { + udp.dst_port = tvb_get_bits16(tvb, bit_offset, LOWPAN_UDP_PORT_BITS, FALSE); + bit_offset += LOWPAN_UDP_PORT_BITS; + } + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_dst, tvb, offset>>3, + BITS_TO_BYTE_LEN(offset, (bit_offset-offset)), udp.dst_port); + } + udp.dst_port = g_ntohs(udp.dst_port); + + /* Parse the length, if present. */ + if (!(hc1_encoding & LOWPAN_HC2_UDP_LENGTH)) { + udp.length = tvb_get_bits16(tvb, bit_offset, LOWPAN_UDP_LENGTH_BITS, FALSE); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_len, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_UDP_LENGTH_BITS), udp.length); + + } + bit_offset += LOWPAN_UDP_LENGTH_BITS; + } + /* Compute the length from the tvbuff size. */ + else { + udp.length = tvb_reported_length(tvb); + udp.length -= BITS_TO_BYTE_LEN(0, bit_offset + LOWPAN_UDP_CHECKSUM_BITS); + udp.length += sizeof(struct udp_hdr); + } + udp.length = g_ntohs(udp.length); + + /* Parse the checksum. */ + udp.checksum = tvb_get_bits16(tvb, bit_offset, LOWPAN_UDP_CHECKSUM_BITS, FALSE); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_checksum, tvb, bit_offset>>3, + BITS_TO_BYTE_LEN(bit_offset, LOWPAN_UDP_CHECKSUM_BITS), udp.checksum); + } + bit_offset += LOWPAN_UDP_CHECKSUM_BITS; + udp.checksum = g_ntohs(udp.checksum); + + /* Construct the next header for the UDP datagram. */ + offset = BITS_TO_BYTE_LEN(0, bit_offset); + length = tvb_length_remaining(tvb, offset); + nhdr_list = ep_alloc(sizeof(struct lowpan_nhdr) + sizeof(struct udp_hdr) + length); + nhdr_list->next = NULL; + nhdr_list->proto = IP_PROTO_UDP; + nhdr_list->length = length + sizeof(struct udp_hdr); + nhdr_list->reported = g_ntohs(udp.length); + + /* Copy the UDP header into the buffer. */ + memcpy(nhdr_list->hdr, &udp, sizeof(struct udp_hdr)); + tvb_memcpy(tvb, nhdr_list->hdr + sizeof(struct udp_hdr), offset, length); + } + /*===================================================== + * Reconstruct the IPv6 Packet + *===================================================== + */ + else { + offset = BITS_TO_BYTE_LEN(0, bit_offset); + nhdr_list = ep_alloc(sizeof(struct lowpan_nhdr) + tvb_length_remaining(tvb, offset)); + nhdr_list->next = NULL; + nhdr_list->proto = ipv6.ip6_nxt; + nhdr_list->length = tvb_length_remaining(tvb, offset); + nhdr_list->reported = tvb_reported_length_remaining(tvb, offset); + tvb_memcpy(tvb, nhdr_list->hdr, offset, nhdr_list->length); + } + + /* Link the reassembled tvbuff together. */ + ipv6_tvb = lowpan_reassemble_ipv6(&ipv6, nhdr_list); + tvb_set_child_real_data_tvbuff(tvb, ipv6_tvb); + add_new_data_source(pinfo, ipv6_tvb, "6LoWPAN header decompression"); + + /* Pass the reassembled packet to the IPv6 dissector. */ + call_dissector(ipv6_handle, ipv6_tvb, pinfo, proto_tree_get_root(tree)); + return NULL; +} /* dissect_6lowpan_hc1 */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_iphc + * DESCRIPTION + * Dissector routine for a 6LoWPAN IPHC header. + * + * This header is still in the draft phase, but is expected + * to replace HC1. + * + * See draft-ietf-6lowpan-hc-05.txt + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * RETURNS + * tvbuff_t * ; The remaining payload to be parsed. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_iphc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset = 0; + gint length; + proto_tree * iphc_tree; + proto_item * ti = NULL; + proto_item * ti_sam = NULL; + proto_item * ti_dam = NULL; + gboolean addr_err; + /* IPHC header fields. */ + guint16 iphc_flags; + guint8 iphc_traffic; + guint8 iphc_hop_limit; + guint8 iphc_src_mode; + guint8 iphc_dst_mode; + guint8 iphc_ctx = 0; + /* IPv6 header */ + guint8 ipv6_class = 0; + struct ip6_hdr ipv6; + tvbuff_t * ipv6_tvb; + /* Next header chain */ + struct lowpan_nhdr *nhdr_list; + + /* Create a tree for the IPHC header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint16), "IPHC Header"); + iphc_tree = proto_item_add_subtree(ti, ett_6lowpan_iphc); + + /* Display the pattern. */ + proto_tree_add_bits_item(iphc_tree, hf_6lowpan_pattern, tvb, 0, LOWPAN_PATTERN_IPHC_BITS, FALSE); + } + + /*===================================================== + * Parse IPHC Header flags. + *===================================================== + */ + iphc_flags = tvb_get_ntohs(tvb, offset); + iphc_traffic = (iphc_flags & LOWPAN_IPHC_FLAG_FLOW) >> LOWPAN_IPHC_FLAG_OFFSET_FLOW; + iphc_hop_limit = (iphc_flags & LOWPAN_IPHC_FLAG_HLIM) >> LOWPAN_IPHC_FLAG_OFFSET_HLIM; + iphc_src_mode = (iphc_flags & LOWPAN_IPHC_FLAG_SRC_MODE) >> LOWPAN_IPHC_FLAG_OFFSET_SRC_MODE; + iphc_dst_mode = (iphc_flags & LOWPAN_IPHC_FLAG_DST_MODE) >> LOWPAN_IPHC_FLAG_OFFSET_DST_MODE; + if (tree) { + proto_tree_add_uint (iphc_tree, hf_6lowpan_iphc_flag_tf, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_FLOW); + proto_tree_add_boolean (iphc_tree, hf_6lowpan_iphc_flag_nhdr, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_NHDR); + proto_tree_add_uint (iphc_tree, hf_6lowpan_iphc_flag_hlim, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_HLIM); + proto_tree_add_boolean (iphc_tree, hf_6lowpan_iphc_flag_cid, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_CONTEXT_ID); + proto_tree_add_boolean (iphc_tree, hf_6lowpan_iphc_flag_sac, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_SRC_COMP); + ti_sam = proto_tree_add_uint(iphc_tree, hf_6lowpan_iphc_flag_sam, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_SRC_MODE); + proto_tree_add_boolean (iphc_tree, hf_6lowpan_iphc_flag_mcast, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_MCAST_COMP); + proto_tree_add_boolean (iphc_tree, hf_6lowpan_iphc_flag_dac, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_DST_COMP); + ti_dam = proto_tree_add_uint(iphc_tree, hf_6lowpan_iphc_flag_dam, tvb, offset, sizeof(guint16), iphc_flags & LOWPAN_IPHC_FLAG_DST_MODE); + } + offset += sizeof(guint16); + + /* Display the context identifier extension, if present. */ + if (iphc_flags & LOWPAN_IPHC_FLAG_CONTEXT_ID) { + iphc_ctx = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint(iphc_tree, hf_6lowpan_iphc_sci, tvb, offset, sizeof(guint8), iphc_ctx & LOWPAN_IPHC_FLAG_SCI); + proto_tree_add_uint(iphc_tree, hf_6lowpan_iphc_dci, tvb, offset, sizeof(guint8), iphc_ctx & LOWPAN_IPHC_FLAG_DCI); + } + offset += sizeof(guint8); + } + + /*===================================================== + * Parse Traffic Class and Flow Label + *===================================================== + */ + offset <<= 3; + /* Parse the ECN field. */ + if (iphc_traffic != LOWPAN_IPHC_FLOW_COMPRESSED) { + ipv6_class |= tvb_get_bits8(tvb, offset, LOWPAN_IPHC_ECN_BITS); + offset += LOWPAN_IPHC_ECN_BITS; + } + /* Parse the DSCP field. */ + if ((iphc_traffic == LOWPAN_IPHC_FLOW_CLASS_LABEL) || (iphc_traffic == LOWPAN_IPHC_FLOW_CLASS)) { + ipv6_class |= (tvb_get_bits8(tvb, offset, LOWPAN_IPHC_DSCP_BITS) << LOWPAN_IPHC_ECN_BITS); + offset += LOWPAN_IPHC_DSCP_BITS; + } + /* Display the traffic class field. */ + if (tree) { + /* Create a tree for the traffic class. */ + proto_tree * tf_tree; + ti = proto_tree_add_uint(tree, hf_6lowpan_traffic_class, tvb, offset>>3, sizeof(guint8), ipv6_class); + tf_tree = proto_item_add_subtree(ti, ett_6lopwan_traffic_class); + + /* Add the ECN and DSCP fields. */ + proto_tree_add_uint(tf_tree, hf_6lowpan_ecn, tvb, offset>>3, sizeof(guint8), ipv6_class & LOWPAN_IPHC_TRAFFIC_ECN); + proto_tree_add_uint(tf_tree, hf_6lowpan_dscp, tvb, offset>>3, sizeof(guint8), ipv6_class & LOWPAN_IPHC_TRAFFIC_DSCP); + } + + /* Parse and display the traffic label. */ + if ((iphc_traffic == LOWPAN_IPHC_FLOW_CLASS_LABEL) || (iphc_traffic == LOWPAN_IPHC_FLOW_ECN_LABEL)) { + /* Pad to 4-bits past the start of the byte. */ + offset += ((4 - offset) & 0x7); + ipv6.ip6_flow = tvb_get_bits32(tvb, offset, LOWPAN_IPHC_LABEL_BITS, FALSE); + if (tree) { + proto_tree_add_bits_item(tree, hf_6lowpan_flow_label, tvb, offset, LOWPAN_IPHC_LABEL_BITS, FALSE); + } + offset += LOWPAN_IPHC_LABEL_BITS; + } + else ipv6.ip6_flow = 0; + + /* Rebuild the IPv6 flow label and traffic class fields. */ + ipv6.ip6_flow = g_ntohl(ipv6.ip6_flow) | (ipv6_class << LOWPAN_IPV6_FLOW_LABEL_BITS); + ipv6.ip6_vfc = (0x6 << 4) | (ipv6_class >> 4); + + /* Convert back to byte offsets. */ + offset >>= 3; + + /*===================================================== + * Parse Next Header and Hop Limit + *===================================================== + */ + /* Get the next header field, if present. */ + if (!(iphc_flags & LOWPAN_IPHC_FLAG_NHDR)) { + ipv6.ip6_nxt = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint_format(tree, hf_6lowpan_next_header, tvb, offset, sizeof(guint8), ipv6.ip6_nxt, + "Next header: %s (0x%02x)", ipprotostr(ipv6.ip6_nxt), ipv6.ip6_nxt); + } + offset += sizeof(guint8); + } + + /* Get the hop limit field, if present. */ + if (iphc_hop_limit == LOWPAN_IPHC_HLIM_1) { + ipv6.ip6_hlim = 1; + } + else if (iphc_hop_limit == LOWPAN_IPHC_HLIM_64) { + ipv6.ip6_hlim = 64; + } + else if (iphc_hop_limit == LOWPAN_IPHC_HLIM_255) { + ipv6.ip6_hlim = 255; + } + else { + ipv6.ip6_hlim = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_hop_limit, tvb, offset, sizeof(guint8), ipv6.ip6_hlim); + } + offset += sizeof(guint8); + } + + /*===================================================== + * Parse and decompress the source address. + *===================================================== + */ + addr_err = FALSE; + length = 0; + memset(&ipv6.ip6_src, 0, sizeof(ipv6.ip6_src)); + /*----------------------- + * Stateless compression + *----------------------- + */ + if (!(iphc_flags & LOWPAN_IPHC_FLAG_SRC_COMP)) { + /* Load the link-local prefix. */ + ipv6.ip6_src.bytes[0] = 0xff; + ipv6.ip6_src.bytes[1] = 0xfe; + /* Full Address inline. */ + if (iphc_src_mode == LOWPAN_IPHC_ADDR_FULL_INLINE) { + length = sizeof(ipv6.ip6_src); + tvb_memcpy(tvb, &ipv6.ip6_src.bytes[sizeof(ipv6.ip6_src) - length], offset, length); + } + /* Partial address inline. */ + else if (iphc_src_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) { + length = sizeof(guint64); + tvb_memcpy(tvb, &ipv6.ip6_src.bytes[sizeof(ipv6.ip6_src) - length], offset, length); + } + else if (iphc_src_mode == LOWPAN_IPHC_ADDR_16BIT_INLINE) { + length = sizeof(guint16); + tvb_memcpy(tvb, &ipv6.ip6_src.bytes[sizeof(ipv6.ip6_src) - length], offset, length); + } + /* Try to recover the source interface identifier from the link layer. */ + else { + lowpan_dlsrc_to_ifcid(pinfo, &ipv6.ip6_src.bytes[8]); + } + } + /*----------------------- + * Stateful compression + *----------------------- + */ + else { + /* + * TODO: Stateful address recovery. + * For now, just set the address to 0 and ignore the context bits. + */ + addr_err = TRUE; + if (iphc_src_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) length = sizeof(guint64); + else if (iphc_src_mode == LOWPAN_IPHC_ADDR_16BIT_INLINE) length = sizeof(guint16); + else if (iphc_src_mode == LOWPAN_IPHC_ADDR_COMPRESSED) length = 0; + else { + /* Illegal source address compression mode. */ + expert_add_info_format(pinfo, ti_sam, PI_MALFORMED, PI_ERROR, "Illegal source address mode"); + return NULL; + } + } + + /* Display the source IPv6 address. */ + if (tree) { + ti = proto_tree_add_ipv6(tree, hf_6lowpan_source, tvb, offset, length, (guint8 *)&ipv6.ip6_src); + } + if (addr_err) { + expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_WARN, "Failed to recover source IPv6 address"); + } + offset += length; + + /*===================================================== + * Parse and decompress the destination address. + *===================================================== + */ + addr_err = FALSE; + length = 0; + memset(&ipv6.ip6_dst, 0, sizeof(ipv6.ip6_dst)); + /*--------------------------------- + * Stateless unicast compression + *--------------------------------- + */ + if (!(iphc_flags & LOWPAN_IPHC_FLAG_DST_COMP) && !(iphc_flags & LOWPAN_IPHC_FLAG_MCAST_COMP)) { + /* Load the link-local prefix. */ + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = 0xfe; + /* Full Address inline. */ + if (iphc_dst_mode == LOWPAN_IPHC_ADDR_FULL_INLINE) { + length = sizeof(ipv6.ip6_dst); + tvb_memcpy(tvb, &ipv6.ip6_dst.bytes[sizeof(ipv6.ip6_dst) - length], offset, length); + } + /* Partial address inline. */ + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) { + length = sizeof(guint64); + tvb_memcpy(tvb, &ipv6.ip6_dst.bytes[sizeof(ipv6.ip6_dst) - length], offset, length); + } + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_16BIT_INLINE) { + length = sizeof(guint16); + tvb_memcpy(tvb, &ipv6.ip6_dst.bytes[sizeof(ipv6.ip6_dst) - length], offset, length); + } + /* Try to recover the source interface identifier from the link layer. */ + else { + lowpan_dldst_to_ifcid(pinfo, &ipv6.ip6_dst.bytes[8]); + } + } + /*--------------------------------- + * Stateless multicast compression + *--------------------------------- + */ + else if (!(iphc_flags & LOWPAN_IPHC_FLAG_DST_COMP) && (iphc_flags & LOWPAN_IPHC_FLAG_MCAST_COMP)) { + if (iphc_dst_mode == LOWPAN_IPHC_ADDR_FULL_INLINE) { + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[11] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[12] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[13] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[14] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[15] = tvb_get_guint8(tvb, offset + (length++)); + } + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) { + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[13] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[14] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[15] = tvb_get_guint8(tvb, offset + (length++)); + } + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_16BIT_INLINE) { + guint8 temp = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = (temp >> 4); + ipv6.ip6_dst.bytes[14] = (temp & 0xf); + ipv6.ip6_dst.bytes[15] = tvb_get_guint8(tvb, offset + (length++)); + } + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_COMPRESSED) { + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = 0x02; + ipv6.ip6_dst.bytes[15] = tvb_get_guint8(tvb, offset + (length++)); + } + } + /*--------------------------------- + * Stateful unicast compression + *--------------------------------- + */ + else if ((iphc_flags & LOWPAN_IPHC_FLAG_DST_COMP) && !(iphc_flags & LOWPAN_IPHC_FLAG_MCAST_COMP)) { + /* TODO: Stateful address recovery. */ + addr_err = TRUE; + if (iphc_dst_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) length = sizeof(guint64); + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_16BIT_INLINE) length = sizeof(guint16); + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_COMPRESSED) length = 0; + else { + /* Illegal destination address compression mode. */ + expert_add_info_format(pinfo, ti_dam, PI_MALFORMED, PI_ERROR, "Illegal destination address mode"); + return NULL; + } + } + /*--------------------------------- + * Stateful multicast compression + *--------------------------------- + */ + else { + /* If full inline, it's neither stateful, nor compressed. */ + if (iphc_dst_mode == LOWPAN_IPHC_ADDR_FULL_INLINE) { + length = sizeof(ipv6.ip6_dst); + tvb_memcpy(tvb, &ipv6.ip6_dst, offset, length); + } + /* If 64-bit inline, then there are 48-bits inline, and the + * remainder is derived from context. */ + else if (iphc_dst_mode == LOWPAN_IPHC_ADDR_64BIT_INLINE) { + ipv6.ip6_dst.bytes[0] = 0xff; + ipv6.ip6_dst.bytes[1] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[2] = tvb_get_guint8(tvb, offset + (length++)); + /* TODO: Recover the stuff derived from context. */ + addr_err = TRUE; + ipv6.ip6_dst.bytes[12] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[13] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[14] = tvb_get_guint8(tvb, offset + (length++)); + ipv6.ip6_dst.bytes[15] = tvb_get_guint8(tvb, offset + (length++)); + } + else { + /* Illegal destination address compression mode. */ + addr_err = TRUE; + expert_add_info_format(pinfo, ti_dam, PI_MALFORMED, PI_ERROR, "Illegal destination address mode"); + return NULL; + } + + } + + /* Display the destination IPv6 address. */ + if (tree) { + ti = proto_tree_add_ipv6(tree, hf_6lowpan_dest, tvb, offset, length, (guint8 *)&ipv6.ip6_dst); + } + if (addr_err) { + expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_WARN, "Failed to recover destination IPv6 address"); + } + offset += length; + + /*===================================================== + * Decompress extension headers. + *===================================================== + */ + /* Parse the list of extension headers. */ + if (iphc_flags & LOWPAN_IPHC_FLAG_NHDR) { + /* Parse the next header protocol identifer. */ + ipv6.ip6_nxt = lowpan_parse_nhc_proto(tvb, offset); + + /* Parse the 6LoWPAN NHC fields. */ + nhdr_list = dissect_6lowpan_iphc_nhc(tvb, pinfo, tree, offset); + } + /* Create an extension header for the remaining payload. */ + else { + nhdr_list = ep_alloc(sizeof(struct lowpan_nhdr) + tvb_length_remaining(tvb, offset)); + nhdr_list->next = NULL; + nhdr_list->proto = ipv6.ip6_nxt; + nhdr_list->length = tvb_length_remaining(tvb, offset); + nhdr_list->reported = tvb_reported_length_remaining(tvb, offset); + tvb_memcpy(tvb, nhdr_list->hdr, offset, nhdr_list->length); + } + + /*===================================================== + * Rebuild the IPv6 packet. + *===================================================== + */ + /* Reassemble the IPv6 packet. */ + ipv6_tvb = lowpan_reassemble_ipv6(&ipv6, nhdr_list); + tvb_set_child_real_data_tvbuff(tvb, ipv6_tvb); + add_new_data_source(pinfo, ipv6_tvb, "6LoWPAN header decompression"); + + /* Pass the reassembled packe to the IPv6 dissector. */ + call_dissector(ipv6_handle, ipv6_tvb, pinfo, proto_tree_get_root(tree)); + return NULL; +} /* dissect_6lowpan_iphc */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_iphc_nhc + * DESCRIPTION + * Dissector routine for a 6LoWPAN IPHC next header structure(s). + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * offset ; packet buffer offset. + * RETURNS + * lowpan_nhdr * ; List of ep_alloc'd next header structures. + *--------------------------------------------------------------- + */ +static struct lowpan_nhdr * +dissect_6lowpan_iphc_nhc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset) +{ + gint length; + proto_item * ti = NULL; + proto_tree * nhc_tree = NULL; + struct lowpan_nhdr *nhdr; + + /* IPv6 Extension Header. */ + if (tvb_get_bits8(tvb, offset<<3, LOWPAN_NHC_PATTERN_EXT_BITS) == LOWPAN_NHC_PATTERN_EXT) { + struct ip6_ext ipv6_ext; + guint8 ext_flags; + guint8 ext_len; + guint8 ext_proto; + + /* Parse the IPv6 extension header protocol. */ + ext_proto = lowpan_parse_nhc_proto(tvb, offset); + + /* Create a tree for the IPv6 extnesion header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint16), "IPv6 extension header"); + nhc_tree = proto_item_add_subtree(ti, ett_6lowpan_nhc_ext); + /* Display the NHC-UDP pattern. */ + proto_tree_add_bits_item(nhc_tree, hf_6lowpan_nhc_pattern, tvb, offset<<3, LOWPAN_NHC_PATTERN_EXT_BITS, FALSE); + } + + /* Get and display the extension header compression flags. */ + ext_flags = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint(nhc_tree, hf_6lowpan_nhc_ext_eid, tvb, offset, sizeof(guint8), ext_flags & LOWPAN_NHC_EXT_EID); + proto_tree_add_boolean(nhc_tree, hf_6lowpan_nhc_ext_nh, tvb, offset, sizeof(guint8), ext_flags & LOWPAN_NHC_EXT_NHDR); + } + offset += sizeof(guint8); + + /* Get and display the next header field, if present. */ + if (!(ext_flags & LOWPAN_NHC_EXT_NHDR)) { + ipv6_ext.ip6e_nxt = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint_format(nhc_tree, hf_6lowpan_nhc_ext_next, tvb, offset, sizeof(guint8), ipv6_ext.ip6e_nxt, + "Next header: %s (0x%02x)", ipprotostr(ipv6_ext.ip6e_nxt), ipv6_ext.ip6e_nxt); + proto_item_set_end(ti, tvb, offset+sizeof(guint8)); + } + offset += sizeof(guint8); + } + + /* Get and display the extension header length. */ + ext_len = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_uint(nhc_tree, hf_6lowpan_nhc_ext_length, tvb, offset, sizeof(guint8), ext_len); + } + offset += sizeof(guint8); + + /* Compute the length of the extension header padded to an 8-byte alignment. */ + length = sizeof(struct ip6_ext) + ext_len; + length += ((length + 7) & 0x7); + + /* Create the next header structure for the IPv6 extension header. */ + nhdr = ep_alloc0(sizeof(struct lowpan_nhdr) + sizeof(struct ip6_ext) + length); + nhdr->next = NULL; + nhdr->proto = ext_proto; + nhdr->length = length + sizeof(struct ip6_ext); + nhdr->reported = length + sizeof(struct ip6_ext); + + /* Add the IPv6 extension header to the buffer. */ + if (ext_flags & LOWPAN_NHC_EXT_NHDR) { + ipv6_ext.ip6e_nxt = lowpan_parse_nhc_proto(tvb, offset+ext_len); + } + ipv6_ext.ip6e_len = nhdr->reported>>3; /* Convert to units of 8 bytes. */ + memcpy(nhdr->hdr, &ipv6_ext, sizeof(struct ip6_ext)); + + /* + * If the extension header was truncated, display the remainder using + * the data dissector, and end NHC dissection here. + */ + if (!tvb_bytes_exist(tvb, offset, ext_len)) { + /* Call the data dissector for the remainder. */ + call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, nhc_tree); + + /* Copy the remainder, and truncate the real buffer length. */ + nhdr->length = tvb_length_remaining(tvb, offset) + sizeof(struct ip6_ext); + tvb_memcpy(tvb, nhdr->hdr + sizeof(struct ip6_ext), offset, tvb_length_remaining(tvb, offset)); + + /* There is nothing more we can do. */ + return nhdr; + } + + /* Display the extension header using the data dissector. */ + call_dissector(data_handle, tvb_new_subset(tvb, offset, ext_len, ext_len), pinfo, nhc_tree); + + /* Copy the extension header into the struct. */ + tvb_memcpy(tvb, nhdr->hdr + sizeof(struct ip6_ext), offset, ext_len); + offset += ext_len; + + if (!(ext_flags & LOWPAN_NHC_EXT_NHDR)) { + /* Create another next header structure for the remaining payload. */ + nhdr->next = ep_alloc(sizeof(struct lowpan_nhdr) + tvb_length_remaining(tvb, offset)); + nhdr->next->next = NULL; + nhdr->next->proto = ipv6_ext.ip6e_nxt; + nhdr->next->length = tvb_length_remaining(tvb, offset); + nhdr->next->reported = tvb_reported_length_remaining(tvb, offset); + tvb_memcpy(tvb, nhdr->next->hdr, offset, length); + } + else { + /* + * There are more LOWPAN_NHC structures to parse. Call ourself agian + * recursively to parse them and build the linked list. + */ + nhdr->next = dissect_6lowpan_iphc_nhc(tvb, pinfo, tree, offset); + } + + /* Done. */ + return nhdr; + } + /*===================================================== + * UDP Header + *===================================================== + */ + if (tvb_get_bits8(tvb, offset<<3, LOWPAN_NHC_PATTERN_UDP_BITS) == LOWPAN_NHC_PATTERN_UDP) { + struct udp_hdr udp; + gint src_bitlen; + gint dst_bitlen; + guint8 udp_flags; + + /* Create a tree for the UDP header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint8), "UDP header compression"); + nhc_tree = proto_item_add_subtree(ti, ett_6lowpan_nhc_udp); + /* Display the NHC-UDP pattern. */ + proto_tree_add_bits_item(nhc_tree, hf_6lowpan_nhc_pattern, tvb, offset<<3, LOWPAN_NHC_PATTERN_UDP_BITS, FALSE); + } + + /* Get and display the UDP header compression options */ + udp_flags = tvb_get_guint8(tvb, offset); + if (tree) { + proto_tree_add_boolean(nhc_tree, hf_6lowpan_nhc_udp_checksum, tvb, offset, sizeof(guint8), udp_flags & LOWPAN_NHC_UDP_CHECKSUM); + proto_tree_add_boolean(nhc_tree, hf_6lowpan_nhc_udp_src, tvb, offset, sizeof(guint8), udp_flags & LOWPAN_NHC_UDP_SRCPORT); + proto_tree_add_boolean(nhc_tree, hf_6lowpan_nhc_udp_dst, tvb, offset, sizeof(guint8), udp_flags & LOWPAN_NHC_UDP_DSTPORT); + } + offset += sizeof(guint8); + + /* Get and display the ports. */ + switch (udp_flags & (LOWPAN_NHC_UDP_SRCPORT | LOWPAN_NHC_UDP_DSTPORT)) { + case (LOWPAN_NHC_UDP_SRCPORT | LOWPAN_NHC_UDP_DSTPORT): + udp.src_port = LOWPAN_PORT_12BIT_OFFSET; + udp.dst_port = LOWPAN_PORT_12BIT_OFFSET; + src_bitlen = 4; + dst_bitlen = 4; + break; + + case LOWPAN_NHC_UDP_SRCPORT: + udp.src_port = LOWPAN_PORT_8BIT_OFFSET; + udp.dst_port = 0; + src_bitlen = 8; + dst_bitlen = 16; + break; + + case LOWPAN_NHC_UDP_DSTPORT: + udp.src_port = 0; + udp.dst_port = LOWPAN_PORT_8BIT_OFFSET; + src_bitlen = 16; + dst_bitlen = 8; + break; + + default: + udp.src_port = 0; + udp.dst_port = 0; + src_bitlen = 16; + dst_bitlen = 16; + break; + } /* switch */ + + /* Source port */ + udp.src_port += tvb_get_bits16(tvb, offset<<3, src_bitlen, FALSE); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_src, tvb, offset, BITS_TO_BYTE_LEN(offset<<3, src_bitlen), udp.src_port); + } + /* Destination port */ + udp.dst_port += tvb_get_bits16(tvb, (offset<<3)+src_bitlen, dst_bitlen, FALSE); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_dst, tvb, offset, BITS_TO_BYTE_LEN((offset<<3)+src_bitlen, dst_bitlen), udp.dst_port); + } + offset += ((src_bitlen + dst_bitlen)>>3); + udp.src_port = g_ntohs(udp.src_port); + udp.dst_port = g_ntohs(udp.dst_port); + + /* Get and display the checksum. */ + if (!(udp_flags & LOWPAN_NHC_UDP_CHECKSUM)) { + /* Parse the checksum. */ + udp.checksum = tvb_get_ntohs(tvb, offset); + if (tree) { + proto_tree_add_uint(tree, hf_6lowpan_udp_checksum, tvb, offset, sizeof(guint16), udp.checksum); + } + offset += sizeof(guint16); + udp.checksum = g_ntohs(udp.checksum); + } + else { + udp.checksum = 0; + } + + /* Compute the datagram length. */ + length = tvb_reported_length_remaining(tvb, offset); + udp.length = g_ntohs(length + sizeof(struct udp_hdr)); + + /* + * Although rfc768 (udp) allows a packet to be sent with a checksum of + * 0 to mean that no checksum was computed, apparently IPv6 specifically + * disallows sending UDP datagrams without checksums. Likewise, 6LoWPAN + * requires that we recompute the checksum. + * + * Although, if the datagram is incomplete, then leave the checsum at 0. + */ + if ((udp_flags & LOWPAN_NHC_UDP_CHECKSUM) && tvb_bytes_exist(tvb, offset, length)) { + vec_t cksum_vec[3]; + struct { + struct e_in6_addr src; + struct e_in6_addr dst; + guint8 zero; + guint8 proto; + guint16 length; + } cksum_phdr; + guint16 cksum; + + /* Fill in the pseudo-header. */ + memcpy(&cksum_phdr.src, pinfo->src.data, sizeof(struct e_in6_addr)); + memcpy(&cksum_phdr.dst, pinfo->dst.data, sizeof(struct e_in6_addr)); + cksum_phdr.zero = 0; + cksum_phdr.proto = IP_PROTO_UDP; + cksum_phdr.length = udp.length; + + /* Compute the checksum. */ + cksum_vec[0].ptr = (const guint8 *)&cksum_phdr; + cksum_vec[0].len = sizeof(cksum_phdr); + cksum_vec[1].ptr = (const guint8 *)&udp; + cksum_vec[1].len = sizeof(struct udp_hdr); + cksum_vec[2].ptr = tvb_get_ptr(tvb, offset, length); + cksum_vec[2].len = length; + cksum = in_cksum(cksum_vec, 3); + udp.checksum = g_ntohs((cksum)?(cksum):(~cksum)); + } + + /* Create the next header structure for the UDP datagram. */ + nhdr = ep_alloc(sizeof(struct lowpan_nhdr) + sizeof(struct udp_hdr) + tvb_length_remaining(tvb, offset)); + nhdr->next = NULL; + nhdr->proto = IP_PROTO_UDP; + nhdr->length = tvb_length_remaining(tvb, offset) + sizeof(struct udp_hdr); + nhdr->reported = g_ntohs(udp.length); + + /* Copy the UDP header into the buffer. */ + memcpy(nhdr->hdr, &udp, sizeof(struct udp_hdr)); + tvb_memcpy(tvb, nhdr->hdr + sizeof(struct udp_hdr), offset, length); + return nhdr; + } + /*===================================================== + * Unknown Next Header Type + *===================================================== + */ + return NULL; +} /* dissect_6lowpan_iphc_nhc */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_bc0 + * DESCRIPTION + * Dissector routine for a 6LoWPAN broadcast header. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * RETURNS + * tvbuff_t * ; The remaining payload to be parsed. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_bc0(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) +{ + guint8 seqnum; + proto_tree * bcast_tree; + proto_item * ti; + + /* Create a tree for the broadcast header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, 0, sizeof(guint16), "Broadcast Header"); + bcast_tree = proto_item_add_subtree(ti, ett_6lowpan_bcast); + } + + /* Get and display the pattern. */ + if (tree) { + proto_tree_add_bits_item(bcast_tree, hf_6lowpan_pattern, tvb, 0, LOWPAN_PATTERN_BC0_BITS, FALSE); + } + + /* Get and display the sequence number. */ + seqnum = tvb_get_guint8(tvb, sizeof(guint8)); + if (tree) { + proto_tree_add_uint(bcast_tree, hf_6lowpan_bcast_seqnum, tvb, sizeof(guint8), sizeof(guint8), seqnum); + } + + /* Return the remaining buffer. */ + return tvb_new_subset(tvb, sizeof(guint16), -1, -1); +} /* dissect_6lowpan_bc0 */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_mesh + * DESCRIPTION + * Dissector routine for a 6LoWPAN mesh header. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * offset ; offset to the start of the header. + * RETURNS + * tvbuff_t * ; The remaining payload to be parsed. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_mesh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset = 0; + guint8 mesh_header; + proto_tree * mesh_tree = NULL; + proto_item * ti = NULL; + guint16 src_pan = IEEE802154_BCAST_PAN; + guint16 dst_pan = IEEE802154_BCAST_PAN; + const guint8 * src_ifcid; + const guint8 * dst_ifcid; + + /* + * If 16-bit addresses are used, we need to consult the MAC layer to + * retrieve the PAN identifiers used if we want to reconstruct the + * interface identifier. + */ + if (pinfo->layer_names && pinfo->layer_names->str) { + /* Ensure the MAC layer is IEEE 802.15.4 */ + if (strstr(pinfo->layer_names->str, "wpan") != NULL) { + ieee802154_packet * packet = pinfo->private_data; + if (packet->src_addr_mode != IEEE802154_FCF_ADDR_NONE) src_pan = packet->src_pan; + if (packet->dst_addr_mode != IEEE802154_FCF_ADDR_NONE) dst_pan = packet->dst_pan; + } + } + + /* Create a tree for the mesh header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, offset, 0, "Mesh Header"); + mesh_tree = proto_item_add_subtree(ti, ett_6lowpan_mesh); + } + + /* Get and display the mesh flags. */ + mesh_header = tvb_get_guint8(tvb, offset); + if (tree) { + proto_item * flag_item; + proto_tree * flag_tree; + + /* Create the mesh header subtree. */ + flag_item = proto_tree_add_text(mesh_tree, tvb, offset, sizeof(guint8), "Flags"); + flag_tree = proto_item_add_subtree(flag_item, ett_6lowpan_mesh); + + /* Add the mesh header fields. */ + proto_tree_add_bits_item(flag_tree, hf_6lowpan_pattern, tvb, offset * 8, LOWPAN_PATTERN_MESH_BITS, FALSE); + proto_tree_add_boolean(flag_tree, hf_6lowpan_mesh_v, tvb, offset, sizeof(guint8), mesh_header & LOWPAN_MESH_HEADER_V); + proto_tree_add_boolean(flag_tree, hf_6lowpan_mesh_f, tvb, offset, sizeof(guint8), mesh_header & LOWPAN_MESH_HEADER_F); + proto_tree_add_uint(flag_tree, hf_6lowpan_mesh_hops, tvb, offset, sizeof(guint8), mesh_header & LOWPAN_MESH_HEADER_HOPS); + } + offset += sizeof(guint8); + + /* Get and display the originator address. */ + if (mesh_header & LOWPAN_MESH_HEADER_V) { + guint64 addr64 = tvb_get_ntoh64(tvb, offset); + if (tree) { + proto_tree_add_uint64(mesh_tree, hf_6lowpan_mesh_orig64, tvb, offset, sizeof(guint64), addr64); + } + src_ifcid = tvb_get_ptr(tvb, offset, sizeof(guint64)); + offset += sizeof(guint64); + } + else { + guint16 addr16 = tvb_get_ntohs(tvb, offset); + if (tree) { + proto_tree_add_uint(mesh_tree, hf_6lowpan_mesh_orig16, tvb, offset, sizeof(guint16), addr16); + } + src_ifcid = ep_alloc(sizeof(guint64)); + lowpan_addr16_to_ifcid(addr16, src_pan, (guint8 *)src_ifcid); + offset += sizeof(guint16); + } + SET_ADDRESS(&pinfo->src, AT_EUI64, sizeof(guint64), src_ifcid); + SET_ADDRESS(&pinfo->net_src, AT_EUI64, sizeof(guint64), src_ifcid); + + /* Get and display the destination address. */ + if (mesh_header & LOWPAN_MESH_HEADER_F) { + guint64 addr64 = tvb_get_ntoh64(tvb, offset); + if (tree) { + proto_tree_add_uint64(mesh_tree, hf_6lowpan_mesh_dest64, tvb, offset, sizeof(guint64), addr64); + } + dst_ifcid = tvb_get_ptr(tvb, offset, sizeof(guint64)); + offset += sizeof(guint64); + } + else { + guint16 addr16 = tvb_get_ntohs(tvb, offset); + if (tree) { + proto_tree_add_uint(mesh_tree, hf_6lowpan_mesh_dest16, tvb, offset, sizeof(guint16), addr16); + } + dst_ifcid = ep_alloc(sizeof(guint64)); + lowpan_addr16_to_ifcid(addr16, dst_pan, (guint8 *)dst_ifcid); + offset += sizeof(guint16); + } + SET_ADDRESS(&pinfo->dst, AT_EUI64, sizeof(guint64), dst_ifcid); + SET_ADDRESS(&pinfo->net_dst, AT_EUI64, sizeof(guint64), dst_ifcid); + + /* Adjust the mesh header length. */ + if (tree) { + proto_item_set_end(ti, tvb, offset); + } + + /* Return the remaining buffer. */ + return tvb_new_subset(tvb, offset, -1, -1); +} /* dissect_6lowpan_mesh */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_frag + * DESCRIPTION + * Dissector routine for a 6LoWPAN FRAG headers. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * first ; TRUE if dispatch was FRAG1, FALSE if FRAGN. + * RETURNS + * tvbuff_t * ; reassembled/next buffer. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_frag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean first) +{ + gint offset = 0; + gint frag_size; + guint16 dgram_size; + guint16 dgram_tag; + guint8 dgram_offset = 0; + proto_tree * frag_tree = NULL; + proto_item * ti = NULL; + /* Reassembly parameters. */ + tvbuff_t * new_tvb = NULL; + fragment_data * frag_data = NULL; + gboolean save_fragmented; + + /* Create a tree for the fragmentation header. */ + if (tree) { + ti = proto_tree_add_text(tree, tvb, offset, 0, "Fragmentation Header"); + frag_tree = proto_item_add_subtree(ti, ett_6lowpan_frag); + } + + /* Get and display the pattern and datagram size. */ + dgram_size = tvb_get_bits16(tvb, (offset * 8) + LOWPAN_PATTERN_FRAG_BITS, LOWPAN_FRAG_DGRAM_SIZE_BITS, FALSE); + if (tree) { + proto_tree_add_bits_item(frag_tree, hf_6lowpan_pattern, tvb, offset * 8, LOWPAN_PATTERN_FRAG_BITS, FALSE); + proto_tree_add_uint(frag_tree, hf_6lowpan_frag_dgram_size, tvb, offset, sizeof(guint16), dgram_size); + } + offset += sizeof(guint16); + + /* Get and display the datagram tag. */ + dgram_tag = tvb_get_ntohs(tvb, offset); + if (tree) { + proto_tree_add_uint(frag_tree, hf_6lowpan_frag_dgram_tag, tvb, offset, sizeof(guint16), dgram_tag); + } + offset += sizeof(guint16); + + if (!first) { + dgram_offset = tvb_get_guint8(tvb, offset) * 8; + if (tree) { + proto_tree_add_uint(frag_tree, hf_6lowpan_frag_dgram_offset, tvb, offset, sizeof(guint8), dgram_offset); + } + offset += sizeof(guint8); + } + + /* Adjust the fragmentation header length. */ + frag_size = tvb_length_remaining(tvb, offset); + if (tree) { + proto_item_set_end(ti, tvb, offset); + } + + /* Add this datagram to the fragment table. */ + save_fragmented = pinfo->fragmented; + pinfo->fragmented = TRUE; + frag_data = fragment_add_check(tvb, offset, pinfo, dgram_tag, + lowpan_fragment_table, lowpan_reassembled_table, + dgram_offset, frag_size, ((dgram_offset + frag_size) < dgram_size)); + + /* Attempt reassembly. */ + new_tvb = process_reassembled_data(tvb, offset, pinfo, + "Reassembled Message", frag_data, &lowpan_frag_items, + NULL, tree); + + /* If reassembly was successful, then return the completed datagram. */ + if (new_tvb) { + return new_tvb; + } + /* + * Otherwise, we were unable to reassemble the packet. If this is the + * first fragment, we can still return the remainder for more processing. + */ + else if (first) { + return tvb_new_subset(tvb, offset, -1, dgram_size); + } + /* + * If reassembly failed, and this is not the first fragment, then display + * the payload fragment using the data dissector. + */ + else { + tvbuff_t * data_tvb = tvb_new_subset(tvb, offset, -1, dgram_size); + call_dissector(data_handle, data_tvb, pinfo, proto_tree_get_root(tree)); + return NULL; + } +} /* dissect_6lowpan_frag */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_unknown + * DESCRIPTION + * Dissector routine for 6LoWPAN packets after encountering + * an unknown header. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_unknown(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + tvbuff_t * data_tvb; + + /* Get and display the pattern. */ + if (tree) { + proto_tree_add_bits_item(tree, hf_6lowpan_pattern, tvb, 0, 8, FALSE); + } + + /* Create a tvbuff subset for the remaining data. */ + data_tvb = tvb_new_subset(tvb, sizeof(guint8), -1, tvb_reported_length(tvb) - sizeof(guint8)); + call_dissector(data_handle, data_tvb, pinfo, proto_tree_get_root(tree)); + + /* No data remaining, we gave it all to the data dissector. */ + return NULL; +} /* dissect_6lowpan_unknown */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * proto_register_6lowpan + * DESCRIPTION + * Protocol registration routine for 6LoWPAN. Called during + * Wireshark initialization. + * PARAMETERS + * none ; + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +void +proto_register_6lowpan(void) +{ + static hf_register_info hf[] = { + /* Common 6LoWPAN fields. */ + { &hf_6lowpan_pattern, + { "Pattern", "6lowpan.pattern", FT_UINT8, BASE_HEX, VALS(lowpan_patterns), 0x0, NULL, HFILL }}, + { &hf_6lowpan_nhc_pattern, + { "Pattern", "6lowpan.nhc.pattern", FT_UINT8, BASE_HEX, VALS(lowpan_nhc_patterns), 0x0, NULL, HFILL }}, + + /* HC1 header fields. */ + { &hf_6lowpan_hc1_source_prefix, + { "Source prefix", "6lowpan.hc1.src_prefix", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC1_SOURCE_PREFIX, NULL, HFILL }}, + { &hf_6lowpan_hc1_source_ifc, + { "Source interface", "6lowpan.hc1.src_ifc", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC1_SOURCE_IFC, NULL, HFILL }}, + { &hf_6lowpan_hc1_dest_prefix, + { "Destination prefix", "6lowpan.hc1.dst_prefix", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC1_DEST_PREFIX, NULL, HFILL }}, + { &hf_6lowpan_hc1_dest_ifc, + { "Destination interface", "6lowpan.hc1.dst_ifc", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC1_DEST_IFC, NULL, HFILL }}, + { &hf_6lowpan_hc1_class, + { "Traffic class and flow label", "6lowpan.hc1.class", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC1_TRAFFIC_CLASS, NULL, HFILL }}, + { &hf_6lowpan_hc1_next, + { "Next header", "6lowpan.hc1.next", FT_UINT8, BASE_HEX, VALS(lowpan_hc1_next), LOWPAN_HC1_NEXT, NULL, HFILL }}, + { &hf_6lowpan_hc1_more, + { "More HC bits", "6lowpan.hc1.more", FT_BOOLEAN, 8, NULL, LOWPAN_HC1_MORE, NULL, HFILL }}, + + /* HC_UDP header fields. */ + { &hf_6lowpan_hc2_udp_src, + { "Source port", "6lowpan.hc2.udp.src", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC2_UDP_SRCPORT, NULL, HFILL }}, + { &hf_6lowpan_hc2_udp_dst, + { "Destination port", "6lowpan.hc2.udp.dst", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC2_UDP_DSTPORT, NULL, HFILL }}, + { &hf_6lowpan_hc2_udp_len, + { "Length", "6lowpan.hc2.udp.length", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_HC2_UDP_LENGTH, NULL, HFILL }}, + + /* IPHC header fields. */ + { &hf_6lowpan_iphc_flag_tf, + { "Traffic class and flow lable", "6lowpan.iphc.tf", FT_UINT16, BASE_HEX, VALS(lowpan_iphc_traffic), LOWPAN_IPHC_FLAG_FLOW, "traffic class and flow control encoding", HFILL }}, + { &hf_6lowpan_iphc_flag_nhdr, + { "Next header", "6lowpan.iphc.nh", FT_BOOLEAN, 16, TFS(&lowpan_compression), LOWPAN_IPHC_FLAG_NHDR, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_hlim, + { "Hop limit", "6lowpan.iphc.hlim", FT_UINT16, BASE_HEX, VALS(lowpan_iphc_hop_limit), LOWPAN_IPHC_FLAG_HLIM, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_cid, + { "Context identifier extension", "6lowpan.iphc.cid", FT_BOOLEAN, 16, NULL, LOWPAN_IPHC_FLAG_CONTEXT_ID, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_sac, + { "Source address compression", "6lowpan.iphc.sac", FT_BOOLEAN, 16, TFS(&lowpan_iphc_addr_compression), LOWPAN_IPHC_FLAG_SRC_COMP, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_sam, + { "Source address mode", "6lowpan.iphc.sac", FT_UINT16, BASE_HEX, VALS(lowpan_iphc_addr_modes), LOWPAN_IPHC_FLAG_SRC_MODE, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_mcast, + { "Multicast address compression", "6lowpan.iphc.m", FT_BOOLEAN, 16, NULL, LOWPAN_IPHC_FLAG_MCAST_COMP, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_dac, + { "Destination address compression","6lowpan.iphc.dac", FT_BOOLEAN, 16, TFS(&lowpan_iphc_addr_compression), LOWPAN_IPHC_FLAG_DST_COMP, NULL, HFILL }}, + { &hf_6lowpan_iphc_flag_dam, + { "Destination address mode", "6lowpan.iphc.dam", FT_UINT16, BASE_HEX, VALS(lowpan_iphc_addr_modes), LOWPAN_IPHC_FLAG_DST_MODE, NULL, HFILL }}, + { &hf_6lowpan_iphc_sci, + { "Source context identifier", "6lowpan.iphc.sci", FT_UINT8, BASE_HEX, NULL, LOWPAN_IPHC_FLAG_SCI, NULL, HFILL }}, + { &hf_6lowpan_iphc_dci, + { "Destination context identifier", "6lowpan.iphc.dci", FT_UINT8, BASE_HEX, NULL, LOWPAN_IPHC_FLAG_DCI, NULL, HFILL }}, + + /* NHC IPv6 extnesion header fields. */ + { &hf_6lowpan_nhc_ext_eid, + { "Header ID", "6lowpan.nhc.ext.eid", FT_UINT8, BASE_HEX, VALS(lowpan_nhc_eid), LOWPAN_NHC_EXT_EID, NULL, HFILL }}, + { &hf_6lowpan_nhc_ext_nh, + { "Next header", "6lowpan.nhc.ext.nh", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_NHC_EXT_NHDR, NULL, HFILL }}, + { &hf_6lowpan_nhc_ext_next, + { "Next header", "6lowpan.nhc.ext.next", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_nhc_ext_length, + { "Header length", "6lowpan.nhc.ext.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + /* NHC UDP header fields. */ + { &hf_6lowpan_nhc_udp_checksum, + { "Checksum", "6lowpan.nhc.udp.checksum", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_NHC_UDP_CHECKSUM, NULL, HFILL }}, + { &hf_6lowpan_nhc_udp_src, + { "Source port", "6lowpan.nhc.udp.src", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_NHC_UDP_SRCPORT, NULL, HFILL }}, + { &hf_6lowpan_nhc_udp_dst, + { "Destination port", "6lowpan.nhc.udp.dst", FT_BOOLEAN, 8, TFS(&lowpan_compression), LOWPAN_NHC_UDP_DSTPORT, NULL, HFILL }}, + + /* Uncompressed IPv6 fields. */ + { &hf_6lowpan_traffic_class, + { "Traffic class", "6lowpan.class", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_flow_label, + { "Flow label", "6lowpan.flow", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_ecn, + { "ECN", "6lowpan.ecn", FT_UINT8, BASE_HEX, NULL, LOWPAN_IPHC_TRAFFIC_ECN, NULL, HFILL }}, + { &hf_6lowpan_dscp, + { "DSCP", "6lowpan.dscp", FT_UINT8, BASE_HEX, NULL, LOWPAN_IPHC_TRAFFIC_DSCP, NULL, HFILL }}, + { &hf_6lowpan_next_header, + { "Next header", "6lowpan.next", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_hop_limit, + { "Hop limit", "6lowpan.hops", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_source, + { "Source", "6lowpan.src", FT_IPv6, BASE_NONE, NULL, 0x0, "Source IPv6 address", HFILL }}, + { &hf_6lowpan_dest, + { "Destination", "6lowpan.dst", FT_IPv6, BASE_NONE, NULL, 0x0, "Destination IPv6 address", HFILL }}, + + /* Uncompressed UDP fields. */ + { &hf_6lowpan_udp_src, + { "Source port", "6lowpan.udp.src", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_udp_dst, + { "Destination port", "6lowpan.udp.dst", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_udp_len, + { "UDP length", "6lowpan.udp.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_udp_checksum, + { "UDP checksum", "6lowpan.udp.checksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + /* Broadcast header fields. */ + { &hf_6lowpan_bcast_seqnum, + { "Sequence number", "6lowpan.bcast.seqnum", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + /* Mesh header fields. */ + { &hf_6lowpan_mesh_v, + { "V", "6lowpan.mesh.v", FT_BOOLEAN, 8, NULL, LOWPAN_MESH_HEADER_V, "extended originator address present", HFILL }}, + { &hf_6lowpan_mesh_f, + { "D", "6lowpan.mesh.f", FT_BOOLEAN, 8, NULL, LOWPAN_MESH_HEADER_F, "extended destination address present", HFILL }}, + { &hf_6lowpan_mesh_hops, + { "Hops left", "6lowpan.mesh.hops", FT_UINT8, BASE_DEC, NULL, LOWPAN_MESH_HEADER_HOPS, NULL, HFILL }}, + { &hf_6lowpan_mesh_orig16, + { "Originator", "6lowpan.mesh.orig16", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_mesh_orig64, + { "Originator", "6lowpan.mesh.orig64", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_mesh_dest16, + { "Destination", "6lowpan.mesh.dest16", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_mesh_dest64, + { "Destination", "6lowpan.mesh.dest64", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + /* Fragmentation header fields. */ + { &hf_6lowpan_frag_dgram_size, + { "Datagram size", "6lowpan.frag.size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_frag_dgram_tag, + { "Datagram tag", "6lowpan.frag.tag", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_frag_dgram_offset, + { "Datagram offset", "6lowpan.frag.offset", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + + /* Reassembly fields. */ + { &hf_6lowpan_fragments, + { "Message fragments", "6lowpan.fragments", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment, + { "Message fragment", "6lowpan.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment_overlap, + { "Message fragment overlap", "6lowpan.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment_overlap_conflicts, + { "Message fragment overlapping with conflicting data", "6lowpan.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment_multiple_tails, + { "Message has multiple tail fragments", "6lowpan.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment_too_long_fragment, + { "Message fragment too long", "6lowpan.fragment.too_long_fragment", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_fragment_error, + { "Message defragmentation error", "6lowpan.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }}, + { &hf_6lowpan_reassembled_in, + { "Reassembled in", "6lowpan.reassembled.in",FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }} + }; + + static gint *ett[] = { + &ett_6lowpan, + &ett_6lowpan_hc1, + &ett_6lowpan_hc2_udp, + &ett_6lowpan_iphc, + &ett_6lowpan_nhc_ext, + &ett_6lowpan_nhc_udp, + &ett_6lowpan_bcast, + &ett_6lowpan_mesh, + &ett_6lowpan_mesh_flags, + &ett_6lowpan_frag, + &ett_6lopwan_traffic_class, + /* Reassembly subtrees. */ + &ett_6lowpan_fragment, + &ett_6lowpan_fragments + }; + + proto_6lowpan = proto_register_protocol("IPv6 over IEEE 802.15.4", "6LoWPAN", "6lowpan"); + proto_register_field_array(proto_6lowpan, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + /* Register the dissector with wireshark. */ + register_dissector("6lowpan", dissect_6lowpan, proto_6lowpan); + + /* Register the dissector init function */ + register_init_routine(proto_init_6lowpan); +} /* proto_register_6lowpan */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * proto_init_6lowpan + * DESCRIPTION + * 6LoWPAN initialization function. + * PARAMETERS + * none ; + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +static void +proto_init_6lowpan(void) +{ + fragment_table_init(&lowpan_fragment_table); + reassembled_table_init(&lowpan_reassembled_table); +} /* proto_init_6lowpan */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * proto_reg_handoff_6lowpan + * DESCRIPTION + * Protocol handoff routine for 6LoWPAN. Called after all + * protocols have been loaded, and whenever any preferences + * are changed. + * PARAMETERS + * none ; + * RETURNS + * void ; + *--------------------------------------------------------------- + */ +void +proto_reg_handoff_6lowpan(void) +{ + static gboolean init = FALSE; + + if (!init) { + data_handle = find_dissector("data"); + ipv6_handle = find_dissector("ipv6"); + init = TRUE; + } + + /* Register the 6LoWPAN dissector with IEEE 802.15.4 */ + heur_dissector_add("wpan", dissect_6lowpan_heur, proto_6lowpan); +} /* proto_reg_handoff_6lowpan */ diff --git a/epan/dissectors/packet-6lowpan.h b/epan/dissectors/packet-6lowpan.h new file mode 100644 index 0000000000..497b89efeb --- /dev/null +++ b/epan/dissectors/packet-6lowpan.h @@ -0,0 +1,161 @@ +/* packet-6lowpan.h + * Definitions for 6lowpan packet disassembly structures and routines + * By Owen Kirby <osk@exegin.com> + * Copyright 2009 Owen Kirby + * + * $Id$ + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* 6LoWPAN Patterns */ +#define LOWPAN_PATTERN_NALP 0x00 +#define LOWPAN_PATTERN_NALP_BITS 2 +#define LOWPAN_PATTERN_IPV6 0x41 +#define LOWPAN_PATTERN_IPV6_BITS 8 +#define LOWPAN_PATTERN_HC1 0x42 /* Deprecated - replaced with IPHC. */ +#define LOWPAN_PATTERN_HC1_BITS 8 +#define LOWPAN_PATTERN_BC0 0x50 +#define LOWPAN_PATTERN_BC0_BITS 8 +#define LOWPAN_PATTERN_IPHC 0x03 /* See draft-ietf-6lowpan-hc-05.txt */ +#define LOWPAN_PATTERN_IPHC_BITS 3 +#define LOWPAN_PATTERN_ESC 0x7f +#define LOWPAN_PATTERN_ESC_BITS 8 +#define LOWPAN_PATTERN_MESH 0x02 +#define LOWPAN_PATTERN_MESH_BITS 2 +#define LOWPAN_PATTERN_FRAG1 0x18 +#define LOWPAN_PATTERN_FRAGN 0x1c +#define LOWPAN_PATTERN_FRAG_BITS 5 + +/* 6LoWPAN HC1 Header */ +#define LOWPAN_HC1_SOURCE_PREFIX 0x80 +#define LOWPAN_HC1_SOURCE_IFC 0x40 +#define LOWPAN_HC1_DEST_PREFIX 0x20 +#define LOWPAN_HC1_DEST_IFC 0x10 +#define LOWPAN_HC1_TRAFFIC_CLASS 0x08 +#define LOWPAN_HC1_NEXT 0x06 +#define LOWPAN_HC1_MORE 0x01 + +/* IPv6 header field lengths (in bits) */ +#define LOWPAN_IPV6_TRAFFIC_CLASS_BITS 8 +#define LOWPAN_IPV6_FLOW_LABEL_BITS 20 +#define LOWPAN_IPV6_NEXT_HEADER_BITS 8 +#define LOWPAN_IPV6_HOP_LIMIT_BITS 8 +#define LOWPAN_IPV6_PREFIX_BITS 64 +#define LOWPAN_IPV6_INTERFACE_BITS 64 + +/* HC_UDP header field lengths (in bits). */ +#define LOWPAN_UDP_PORT_BITS 16 +#define LOWPAN_UDP_PORT_COMPRESSED_BITS 4 +#define LOWPAN_UDP_LENGTH_BITS 16 +#define LOWPAN_UDP_CHECKSUM_BITS 16 + +/* HC1 Next Header compression modes. */ +#define LOWPAN_HC1_NEXT_NONE 0x00 +#define LOWPAN_HC1_NEXT_UDP 0x01 +#define LOWPAN_HC1_NEXT_ICMP 0x02 +#define LOWPAN_HC1_NEXT_TCP 0x03 + +/* HC_UDP Header */ +#define LOWPAN_HC2_UDP_SRCPORT 0x80 +#define LOWPAN_HC2_UDP_DSTPORT 0x40 +#define LOWPAN_HC2_UDP_LENGTH 0x20 +#define LOWPAN_HC2_UDP_RESERVED 0x1f + +/* IPHC Base flags */ +#define LOWPAN_IPHC_FLAG_FLOW 0x1800 +#define LOWPAN_IPHC_FLAG_NHDR 0x0400 +#define LOWPAN_IPHC_FLAG_HLIM 0x0300 +#define LOWPAN_IPHC_FLAG_CONTEXT_ID 0x0080 +#define LOWPAN_IPHC_FLAG_SRC_COMP 0x0040 +#define LOWPAN_IPHC_FLAG_SRC_MODE 0x0030 +#define LOWPAN_IPHC_FLAG_MCAST_COMP 0x0008 +#define LOWPAN_IPHC_FLAG_DST_COMP 0x0004 +#define LOWPAN_IPHC_FLAG_DST_MODE 0x0003 +#define LOWPAN_IPHC_FLAG_SCI 0xf0 +#define LOWPAN_IPHC_FLAG_DCI 0x0f +/* Offsets for extracting integer fields. */ +#define LOWPAN_IPHC_FLAG_OFFSET_FLOW 11 +#define LOWPAN_IPHC_FLAG_OFFSET_HLIM 8 +#define LOWPAN_IPHC_FLAG_OFFSET_SRC_MODE 4 +#define LOWPAN_IPHC_FLAG_OFFSET_DST_MODE 0 + +/* IPHC Flow encoding values. */ +#define LOWPAN_IPHC_FLOW_CLASS_LABEL 0x0 +#define LOWPAN_IPHC_FLOW_ECN_LABEL 0x1 +#define LOWPAN_IPHC_FLOW_CLASS 0x2 +#define LOWPAN_IPHC_FLOW_COMPRESSED 0x3 + +/* IPHC Hop limit encoding. */ +#define LOWPAN_IPHC_HLIM_INLINE 0x0 +#define LOWPAN_IPHC_HLIM_1 0x1 +#define LOWPAN_IPHC_HLIM_64 0x2 +#define LOWPAN_IPHC_HLIM_255 0x3 + +/* IPHC address modes. */ +#define LOWPAN_IPHC_ADDR_FULL_INLINE 0x0 +#define LOWPAN_IPHC_ADDR_64BIT_INLINE 0x1 +#define LOWPAN_IPHC_ADDR_16BIT_INLINE 0x2 +#define LOWPAN_IPHC_ADDR_COMPRESSED 0x3 + +/* IPHC Traffic class and flow label field sizes (in bits) */ +#define LOWPAN_IPHC_ECN_BITS 2 +#define LOWPAN_IPHC_DSCP_BITS 6 +#define LOWPAN_IPHC_LABEL_BITS 20 +/* Bitmasks for the reconstructed traffic class field. */ +#define LOWPAN_IPHC_TRAFFIC_ECN 0x03 +#define LOWPAN_IPHC_TRAFFIC_DSCP 0xfc + +/* NHC Patterns. */ +#define LOWPAN_NHC_PATTERN_EXT 0x0e +#define LOWPAN_NHC_PATTERN_EXT_BITS 4 +#define LOWPAN_NHC_PATTERN_UDP 0x1e +#define LOWPAN_NHC_PATTERN_UDP_BITS 5 + +/* NHC Extension header fields. */ +#define LOWPAN_NHC_EXT_EID 0x0e +#define LOWPAN_NHC_EXT_EID_OFFSET 1 +#define LOWPAN_NHC_EXT_NHDR 0x01 + +#define LOWPAN_NHC_EID_HOP_BY_HOP 0x00 +#define LOWPAN_NHC_EID_ROUTING 0x01 +#define LOWPAN_NHC_EID_FRAGMENT 0x02 +#define LOWPAN_NHC_EID_DEST_OPTIONS 0x03 +#define LOWPAN_NHC_EID_MOBILITY 0x04 +#define LOWPAN_NHC_EID_IPV6 0x07 + +/* NHC UDP fields. */ +#define LOWPAN_NHC_UDP_CHECKSUM 0x04 +#define LOWPAN_NHC_UDP_SRCPORT 0x02 +#define LOWPAN_NHC_UDP_DSTPORT 0x01 + +/* 6LoWPAN Mesh Header */ +#define LOWPAN_MESH_HEADER_V 0x20 +#define LOWPAN_MESH_HEADER_F 0x10 +#define LOWPAN_MESH_HEADER_HOPS 0x0f + +/* 6LoWPAN First Fragment Header */ +#define LOWPAN_FRAG_DGRAM_SIZE_BITS 11 + +/* Compressed port number offset. */ +#define LOWPAN_PORT_8BIT_OFFSET 0xf000 +#define LOWPAN_PORT_12BIT_OFFSET (LOWPAN_PORT_8BIT_OFFSET | 0xb0) + +/* 6LoWPAN interface identifier length. */ +#define LOWPAN_IFC_ID_LEN 8 |