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 <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
James Ko 2019-03-26 13:48:42 -07:00 committed by Anders Broman
parent 43b85f102c
commit dfbf88397e
1 changed files with 262 additions and 26 deletions

View File

@ -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 <jck@exegin.com>
* Copyright 2019 Exegin Technologies Limited
*
* Routines for 6LoWPAN packet disassembly
* By Owen Kirby <osk@exegin.com>
* 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",