From dfbf88397ef5b523579afb2ac335d5bef45a0298 Mon Sep 17 00:00:00 2001 From: James Ko Date: Tue, 26 Mar 2019 13:48:42 -0700 Subject: [PATCH] Add 6LoWPAN Selective Fragment Recovery Reassemble fragments with RFRAG Dispatch type and header. Change-Id: Ifa30289069fda13fadc090fa5b78c0fcbfbae39e Reviewed-on: https://code.wireshark.org/review/32594 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu Reviewed-by: Anders Broman --- epan/dissectors/packet-6lowpan.c | 288 ++++++++++++++++++++++++++++--- 1 file changed, 262 insertions(+), 26 deletions(-) diff --git a/epan/dissectors/packet-6lowpan.c b/epan/dissectors/packet-6lowpan.c index 6f993add3c..aa0c813f55 100644 --- a/epan/dissectors/packet-6lowpan.c +++ b/epan/dissectors/packet-6lowpan.c @@ -1,4 +1,10 @@ /* packet-6lowpan.c + * + * Add Selective Fragment Recovery per + * https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-02 + * By James Ko + * Copyright 2019 Exegin Technologies Limited + * * Routines for 6LoWPAN packet disassembly * By Owen Kirby * Copyright 2009 Owen Kirby @@ -47,6 +53,12 @@ void proto_reg_handoff_6lowpan(void); #define LOWPAN_PATTERN_FRAG1 0x18 #define LOWPAN_PATTERN_FRAGN 0x1c #define LOWPAN_PATTERN_FRAG_BITS 5 +#define LOWPAN_PATTERN_RFRAG 0x74 +#define LOWPAN_PATTERN_RFRAG_ACK 0x75 +#define LOWPAN_PATTERN_RFRAG_BITS 7 + +#define LOWPAN_RFRAG_SEQUENCE_BITS 5 +#define LOWPAN_RFRAG_FRAG_SZ_BITS 10 /* RFC8025 and RFC8138 */ #define LOWPAN_PATTERN_PAGING_DISPATCH 0xf @@ -334,6 +346,16 @@ static int hf_6lowpan_frag_dgram_size = -1; static int hf_6lowpan_frag_dgram_tag = -1; static int hf_6lowpan_frag_dgram_offset = -1; +/* Recoverable Fragmentation header fields. */ +static int hf_6lowpan_rfrag_congestion = -1; +static int hf_6lowpan_rfrag_ack_requested = -1; +static int hf_6lowpan_rfrag_dgram_tag = -1; +static int hf_6lowpan_rfrag_sequence = -1; +static int hf_6lowpan_rfrag_size = -1; +static int hf_6lowpan_rfrag_dgram_size = -1; +static int hf_6lowpan_rfrag_offset = -1; +static int hf_6lowpan_rfrag_ack_bitmap = -1; + /* Protocol tree handles. */ static gint ett_6lowpan = -1; static gint ett_6lowpan_hc1 = -1; @@ -368,6 +390,8 @@ static const value_string lowpan_patterns [] = { { LOWPAN_PATTERN_MESH, "Mesh" }, { LOWPAN_PATTERN_FRAG1, "First fragment" }, { LOWPAN_PATTERN_FRAGN, "Fragment" }, + { LOWPAN_PATTERN_RFRAG, "Recoverable Fragment" }, + { LOWPAN_PATTERN_RFRAG_ACK, "Recoverable Fragment ACK" }, { 0, NULL } }; static const true_false_string lowpan_compression = { @@ -586,6 +610,8 @@ static tvbuff_t * dissect_6lowpan_iphc (tvbuff_t *tvb, packet_info *pin static struct lowpan_nhdr * dissect_6lowpan_iphc_nhc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, gint dgram_size, const guint8 *siid, const guint8 *diid); static tvbuff_t * dissect_6lowpan_mesh (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 *siid, guint8 *diid); +static tvbuff_t * dissect_6lowpan_rfrag (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const guint8 *siid, const guint8 *diid); +static tvbuff_t * dissect_6lowpan_rfrag_ack (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static tvbuff_t * dissect_6lowpan_frag_first (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const guint8 *siid, const guint8 *diid); static tvbuff_t * dissect_6lowpan_frag_middle (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static void dissect_6lowpan_unknown (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); @@ -1139,6 +1165,8 @@ dissect_6lowpan_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * if ((mesh & LOWPAN_MESH_HEADER_HOPS) == LOWPAN_MESH_HEADER_HOPS) offset++; continue; } + if (tvb_get_bits8(tvb, offset*8, LOWPAN_PATTERN_RFRAG_BITS) == LOWPAN_PATTERN_RFRAG) break; + if (tvb_get_bits8(tvb, offset*8, LOWPAN_PATTERN_RFRAG_BITS) == LOWPAN_PATTERN_RFRAG_ACK) break; if (tvb_get_bits8(tvb, offset*8, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAG1) { /* First fragment headers must be followed by another valid header. */ offset += 4; @@ -1202,6 +1230,15 @@ dissect_6lowpan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data } /* After the mesh and broadcast headers, process dispatch codes recursively. */ + /* Recoverable Fragmentation headers.*/ + if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_RFRAG_BITS) == LOWPAN_PATTERN_RFRAG) { + next = dissect_6lowpan_rfrag(next, pinfo, lowpan_tree, src_iid, dst_iid); + if (!next) return tvb_captured_length(tvb); + } + else if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_RFRAG_BITS) == LOWPAN_PATTERN_RFRAG_ACK) { + next = dissect_6lowpan_rfrag_ack(next, pinfo, lowpan_tree); + if (!next) return tvb_captured_length(tvb); + } /* Fragmentation headers.*/ if (tvb_get_bits8(next, 0, LOWPAN_PATTERN_FRAG_BITS) == LOWPAN_PATTERN_FRAG1) { next = dissect_6lowpan_frag_first(next, pinfo, lowpan_tree, src_iid, dst_iid); @@ -2760,6 +2797,203 @@ dissect_6lowpan_mesh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8 return tvb_new_subset_remaining(tvb, offset); } /* dissect_6lowpan_mesh */ +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_frag_headers + * DESCRIPTION + * Dissector routine for headers in the first fragment. + * The first fragment can contain an uncompressed IPv6, HC1 or IPHC fragment. + * PARAMETERS + * tvb ; fragment buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * siid ; Source Interface ID. + * diid ; Destination Interface ID. + * RETURNS + * tvbuff_t * ; buffer containing the uncompressed IPv6 headers + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_frag_headers(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *length_item, const guint8 *siid, const guint8 *diid) +{ + tvbuff_t *frag_tvb = NULL; + + /* The first fragment can contain an uncompressed IPv6, HC1 or IPHC fragment. */ + if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_IPV6_BITS) == LOWPAN_PATTERN_IPV6) { + frag_tvb = dissect_6lowpan_ipv6(tvb, pinfo, tree); + } + else if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_HC1_BITS) == LOWPAN_PATTERN_HC1) { + /* Check if the datagram size is sane. */ + if (tvb_reported_length(tvb) < IPv6_HDR_SIZE) { + expert_add_info_format(pinfo, length_item, &ei_6lowpan_bad_ipv6_header_length, + "Length is less than IPv6 header length %u", IPv6_HDR_SIZE); + } + frag_tvb = dissect_6lowpan_hc1(tvb, pinfo, tree, tvb_reported_length(tvb), siid, diid); + } + else if (tvb_get_bits8(tvb, 0, LOWPAN_PATTERN_IPHC_BITS) == LOWPAN_PATTERN_IPHC) { + /* Check if the datagram size is sane. */ + if (tvb_reported_length(tvb) < IPv6_HDR_SIZE) { + expert_add_info_format(pinfo, length_item, &ei_6lowpan_bad_ipv6_header_length, + "Length is less than IPv6 header length %u", IPv6_HDR_SIZE); + } + frag_tvb = dissect_6lowpan_iphc(tvb, pinfo, tree, tvb_reported_length(tvb), siid, diid); + } + /* Unknown 6LoWPAN dispatch type */ + else { + dissect_6lowpan_unknown(tvb, pinfo, tree); + } + return frag_tvb; +} /* dissect_6lowpan_frag_headers */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_rfrag + * DESCRIPTION + * Dissector routine for a 6LoWPAN Recoverable Fragment headers. + * + * If reassembly could be completed, this should return an + * uncompressed IPv6 packet. If reassembly had to be delayed + * for more packets, this will return NULL. + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * siid ; Source Interface ID. + * diid ; Destination Interface ID. + * RETURNS + * tvbuff_t * ; reassembled IPv6 packet. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_rfrag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const guint8 *siid, const guint8 *diid) +{ + gint offset = 0; + guint32 frag_size; + guint32 dgram_tag; + proto_tree * frag_tree; + proto_item * ti; + proto_item * length_item; + /* Reassembly parameters. */ + tvbuff_t * new_tvb; + tvbuff_t * frag_tvb; + fragment_head * frag_data; + gboolean save_fragmented; + guint16 sequence; + guint32 frag_offset; + + /* Create a tree for the fragmentation header. */ + frag_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_6lowpan_frag, &ti, "RFRAG Header"); + + /* Get and display the pattern and explicit congestion bit. */ + proto_tree_add_bits_item(frag_tree, hf_6lowpan_pattern, tvb, offset * 8, LOWPAN_PATTERN_RFRAG_BITS, ENC_BIG_ENDIAN); + proto_tree_add_item(frag_tree, hf_6lowpan_rfrag_congestion, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Get and display the datagram tag. */ + proto_tree_add_item_ret_uint(frag_tree, hf_6lowpan_rfrag_dgram_tag, tvb, offset, 1, ENC_BIG_ENDIAN, &dgram_tag); + offset += 1; + + proto_tree_add_item(frag_tree, hf_6lowpan_rfrag_ack_requested, tvb, offset, 2, ENC_BIG_ENDIAN); + sequence = tvb_get_bits16(tvb, (offset * 8) + 1, LOWPAN_RFRAG_SEQUENCE_BITS, ENC_BIG_ENDIAN); + proto_tree_add_item(frag_tree, hf_6lowpan_rfrag_sequence, tvb, offset, 2, ENC_BIG_ENDIAN); + + frag_size = tvb_get_bits16(tvb, (offset * 8) + 1 + LOWPAN_RFRAG_SEQUENCE_BITS, LOWPAN_RFRAG_FRAG_SZ_BITS, ENC_BIG_ENDIAN); + length_item = proto_tree_add_uint(frag_tree, hf_6lowpan_rfrag_size, tvb, offset * 8, 2, frag_size); + offset += 2; + + if (sequence) { + proto_tree_add_item_ret_uint(frag_tree, hf_6lowpan_rfrag_offset, tvb, offset, 2, ENC_BIG_ENDIAN, &frag_offset); + } + else { + proto_tree_add_item_ret_uint(frag_tree, hf_6lowpan_rfrag_dgram_size, tvb, offset, 2, ENC_BIG_ENDIAN, &frag_offset); + } + offset += 2; + + /* Adjust the fragmentation header length. */ + proto_item_set_end(ti, tvb, offset); + + frag_tvb = tvb_new_subset_length(tvb, offset, frag_size); + if (sequence == 0) { + dissect_6lowpan_frag_headers(frag_tvb, pinfo, tree, length_item, siid, diid); + } + + /* Add this datagram to the fragment table. */ + save_fragmented = pinfo->fragmented; + pinfo->fragmented = TRUE; + guint32 frag_id = lowpan_reassembly_id(pinfo, dgram_tag); + if (sequence == 0) { + frag_data = fragment_add_check(&lowpan_reassembly_table, + frag_tvb, 0, pinfo, frag_id, NULL, + 0, frag_size, TRUE); + fragment_set_tot_len(&lowpan_reassembly_table, pinfo, frag_id, NULL, frag_offset); + } + else { + guint32 dgram_size = fragment_get_tot_len(&lowpan_reassembly_table, pinfo, frag_id, NULL); + frag_data = fragment_add_check(&lowpan_reassembly_table, + frag_tvb, 0, pinfo, frag_id, NULL, + frag_offset, frag_size, (frag_offset+frag_size) < dgram_size); + } + + /* Attempt reassembly. */ + new_tvb = process_reassembled_data(frag_tvb, 0, pinfo, + "Reassembled 6LoWPAN", frag_data, &lowpan_frag_items, + NULL, tree); + + pinfo->fragmented = save_fragmented; + + if (new_tvb) { + /* Reassembly was successful; return the completed datagram. */ + return new_tvb; + } else { + /* Reassembly was unsuccessful; show this fragment. This may + just mean that we don't yet have all the fragments, so + we should not just continue dissecting. */ + call_data_dissector(frag_tvb, pinfo, proto_tree_get_root(tree)); + return NULL; + } +} /* dissect_6lowpan_rfrag */ + +/*FUNCTION:------------------------------------------------------ + * NAME + * dissect_6lowpan_rfrag_ack + * DESCRIPTION + * Dissector routine for a 6LoWPAN ACK Dispatch type and header + * PARAMETERS + * tvb ; packet buffer. + * pinfo ; packet info. + * tree ; 6LoWPAN display tree. + * RETURNS + * tvbuff_t * ; reassembled IPv6 packet. + *--------------------------------------------------------------- + */ +static tvbuff_t * +dissect_6lowpan_rfrag_ack(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset = 0; + proto_tree * frag_tree; + proto_item * ti; + (void)pinfo; + + /* Create a tree for the fragmentation header. */ + frag_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_6lowpan_frag, &ti, "RFRAG ACK Header"); + + /* Get and display the pattern and explicit congestion bit. */ + proto_tree_add_bits_item(frag_tree, hf_6lowpan_pattern, tvb, offset * 8, LOWPAN_PATTERN_RFRAG_BITS, ENC_BIG_ENDIAN); + proto_tree_add_item(frag_tree, hf_6lowpan_rfrag_congestion, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Get and display the datagram tag. */ + proto_tree_add_item(frag_tree, hf_6lowpan_rfrag_dgram_tag, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + proto_tree_add_bits_item(frag_tree, hf_6lowpan_rfrag_ack_bitmap, tvb, offset * 8, 32, ENC_BIG_ENDIAN); + offset += 4; + + /* TODO: Match ACK bits to original fragments? */ + + return tvb_new_subset_remaining(tvb, offset); +} /* dissect_6lowpan_rfrag_ack */ + /*FUNCTION:------------------------------------------------------ * NAME * dissect_6lowpan_frag_first @@ -2812,33 +3046,9 @@ dissect_6lowpan_frag_first(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* Adjust the fragmentation header length. */ proto_item_set_end(ti, tvb, offset); - /* The first fragment can contain an uncompressed IPv6, HC1 or IPHC fragment. */ - frag_tvb = tvb_new_subset_remaining(tvb, offset); - if (tvb_get_bits8(frag_tvb, 0, LOWPAN_PATTERN_IPV6_BITS) == LOWPAN_PATTERN_IPV6) { - frag_tvb = dissect_6lowpan_ipv6(frag_tvb, pinfo, tree); - } - else if (tvb_get_bits8(frag_tvb, 0, LOWPAN_PATTERN_HC1_BITS) == LOWPAN_PATTERN_HC1) { - /* Check if the datagram size is sane. */ - if (dgram_size < IPv6_HDR_SIZE) { - expert_add_info_format(pinfo, length_item, &ei_6lowpan_bad_ipv6_header_length, - "Length is less than IPv6 header length %u", IPv6_HDR_SIZE); - } - frag_tvb = dissect_6lowpan_hc1(frag_tvb, pinfo, tree, dgram_size, siid, diid); - } - else if (tvb_get_bits8(frag_tvb, 0, LOWPAN_PATTERN_IPHC_BITS) == LOWPAN_PATTERN_IPHC) { - /* Check if the datagram size is sane. */ - if (dgram_size < IPv6_HDR_SIZE) { - expert_add_info_format(pinfo, length_item, &ei_6lowpan_bad_ipv6_header_length, - "Length is less than IPv6 header length %u", IPv6_HDR_SIZE); - } - frag_tvb = dissect_6lowpan_iphc(frag_tvb, pinfo, tree, dgram_size, siid, diid); - } - /* Unknown 6LoWPAN dispatch type */ - else { - dissect_6lowpan_unknown(frag_tvb, pinfo, tree); - return NULL; - } + frag_tvb = tvb_new_subset_length(tvb, offset, dgram_size); + frag_tvb = dissect_6lowpan_frag_headers(frag_tvb, pinfo, tree, length_item, siid, diid); /* Check call to dissect_6lowpan_xxx was successful */ if (frag_tvb == NULL) { return NULL; @@ -3219,6 +3429,32 @@ proto_register_6lowpan(void) { "Datagram offset", "6lowpan.frag.offset", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + /* Recoverable Fragmentation header fields. */ + { &hf_6lowpan_rfrag_congestion, + { "Congestion", "6lowpan.rfrag.congestion", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, NULL, HFILL }}, + { &hf_6lowpan_rfrag_ack_requested, + { "Ack requested", "6lowpan.rfrag.ack_requested", + FT_BOOLEAN, 16, TFS(&tfs_yes_no), 0x8000, NULL, HFILL }}, + { &hf_6lowpan_rfrag_dgram_tag, + { "Datagram tag", "6lowpan.rfrag.tag", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_rfrag_sequence, + { "Fragment sequence", "6lowpan.rfrag.sequence", + FT_UINT16, BASE_DEC, NULL, 0x7C00, NULL, HFILL }}, + { &hf_6lowpan_rfrag_size, + { "Fragment size", "6lowpan.rfrag.size", + FT_UINT16, BASE_DEC, NULL, 0x03FF, NULL, HFILL }}, + { &hf_6lowpan_rfrag_dgram_size, + { "Datagram size", "6lowpan.rfrag.datagram_size", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_rfrag_offset, + { "Fragment offset", "6lowpan.rfrag.offset", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_6lowpan_rfrag_ack_bitmap, + { "Fragment ACK bitmask", "6lowpan.rfrag.ack_bitmask", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + /* Reassembly fields. */ { &hf_6lowpan_fragments, { "Message fragments", "6lowpan.fragments",