Background:
I've written a dissector for reading a protocol called TC. The subset of this I'm implementing has two headers - a primary header and a segment header. The primary header is 40 bits long, and the segment header is 8 bits long. I will get into more detail as to how the headers are broken down shortly. After parsing the segment header, my intent is to pass the remainder of the packet into the standard ethernet dissector. This means that the protocol is a link layer protocol. The order of headers is as follows: Primary Header, Segment Header, Ethernet Header, IPv4 header. I've configured this protocol to take WTAP_ENCAP_USER10 and have a pcap configured to meet that type that contains two packets meeting its criteria.
The display in wireshark recognizes the protocol and mostly has the correct breakdown of the fields, but with some errors in reading and has a problem handing off to ethernet. For the rest of this question, I'll provide a breakdown of the fields of the two headers and how I've approached dissection+handoff, then talk about the result and what's wrong.
Detail of Protocol: Primary header
The primary header is comprised of eight fields in the following order:
- 2 bit version number -
- 1 bit bypass flag
- 1 bit command flag
- 2 bit spare field
- 10 bit spacecraft_id field - outside of code, I'll call this id for short
- 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
- 10 bit length field
- 8 bit sequence number field
Items 2,3,5, and 6 all have value_string mappings for values to semantic values.
Detail of Protocol: Segment header
The segment header only has two fields, in the following order:
- A 2 bit sequence flag field
- A 6 bit map_id field - I'll call this map for short.
"dissect_tc" Method
FIrst, I'll show most of the dissect_tc method.
static int
dissect_tc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
//I ASSUME OFFSET IS IN OCTETS
int offset = 0;
proto_item *tc_packet;
proto_tree *tc_tree = NULL;
proto_item *primary_header = NULL;
proto_tree *primary_header_tree;
proto_item *segment_header = NULL;
proto_tree *segment_header_tree;
guint32 first_word;
guint16 second_word;
gint tc_length;
gint length = 0;
gint reported_length;
...
/* Note - both headers together are 48 bits, so I split the amount I need to process
into two words - the first 32, and the second 16. This means that the second word
actually runs across both headers - it includes the sequence number of
the primary header and the entirety of the segment header. */
first_word = tvb_get_ntohl(tvb, 0);
second_word = tvb_get_ntohs(tvb, 32);
reported_length = tvb_reported_length_remaining(tvb, 0);
tc_length = TC_PRIMARY_HEADER_LENGTH + TC_SEGMENT_HEADER_LENGTH;
...(set length to tc_length, unless reported_len < tc_length, in which case reported.)
tc_packet = proto_tree_add_item(tree, proto_tc_subset, tvb, 0, length, ENC_BIG_ENDIAN);
tc_tree = proto_item_add_subtree(tc_packet, ett_tc);
/* build the tc primary header tree */
primary_header = proto_tree_add_item(tc_tree, proto_tc_subset, tvb, offset, TC_PRIMARY_HEADER_LENGTH, ENC_NA);
primary_header_tree = proto_item_add_subtree(primary_header, ett_tc_primary_header);
proto_tree_add_uint(primary_header_tree, hf_tc_version_number, tvb, offset, 2, first_word);
proto_tree_add_boolean(primary_header_tree, hf_tc_bypass_flag, tvb, offset, 1, first_word);
proto_tree_add_boolean(primary_header_tree, hf_tc_ctrl_cmd_flag, tvb, offset, 1, first_word);
proto_tree_add_uint(primary_header_tree, hf_tc_rsvd_spare, tvb, offset, 2, first_word);
proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 10, first_word);
proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 6, first_word);
proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 10, first_word);
proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 8, second_word);
offset+=5;
proto_item_set_end(primary_header, tvb, offset);
/* build the segment header tree */
segment_header=proto_tree_add_item(tc_tree, proto_tc_subset, tvb, offset, TC_SEGMENT_HEADER_LENGTH, ENC_BIG_ENDIAN);
segment_header_tree=proto_item_add_subtree(segment_header, ett_tc_segment_header);
proto_tree_add_item(segment_header_tree, hf_tc_seq_flags, tvb, offset, 2, second_word);
proto_tree_add_item(segment_header_tree, hf_tc_map_id, tvb, offset, 6, second_word);
offset += 1;
proto_item_set_end(segment_header, tvb, offset);
//Handing off to ethernet dissector.
//NOTE: some useful find_dissector args: eth, ip, ipv6, mpls.
call_dissector(find_dissector("eth"), tvb_new_subset_remaining(tvb, offset), pinfo, tree);
return(offset);
"register_tc" method
For the sake of some concision, assume any value_fields passed into VALS() are correctly defined. They are defined elsewhere in the dissector.
void
proto_register_tc(void)
{
static hf_register_info hf[] = {
/* primary tc header flags */
{ &hf_tc_version_number,
{ "Version", "tc.version",
FT_UINT32, BASE_DEC, NULL, 0xc0000000,
NULL, HFILL }
},
{ &hf_tc_bypass_flag,
{ "Bypass Flag", "tc.bypass",
FT_BOOLEAN, 32, VALS(tc_primary_header_bypass_flags), 0x20000000,
NULL, HFILL }
},
{ &hf_tc_ctrl_cmd_flag,
{ "Control Command Flag", "tc.cmd_flag",
FT_BOOLEAN, 32, VALS(tc_primary_header_ctrl_cmd_flags), 0x10000000,
NULL, HFILL }
},
{ &hf_tc_rsvd_spare,
{ "RSVD Spare", "tc.rsvd",
FT_UINT32, BASE_DEC, VALS(tc_primary_header_rsvd), 0x0c000000,
NULL, HFILL }
},
//...(I continue instantiating fields, sliding the bitmask along the 32 bit value)
//I'll show the code near the end of this method now.
/* tc segment header */
{ &hf_tc_seq_flags,
{ "Sequence Flags", "tc.seq_flags",
FT_UINT8, BASE_DEC, NULL, 0xc0,
NULL, HFILL }
},
{ &hf_tc_map_id,
{ "MAP ID", "tc.map_id",
FT_UINT8, BASE_DEC, NULL, 0x3f,
NULL, HFILL }
}
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_tc,
&ett_tc_primary_header,
&ett_tc_segment_header
};
...
/* Register the protocol name and description */
proto_tc_subset = proto_register_protocol("TC_Subset", "TC_Subset", "tc_subset");
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_tc_subset, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_tc = expert_register_protocol(proto_tc_subset);
expert_register_field_array(expert_tc, ei, array_length(ei));
register_dissector ( "tc_subset", dissect_tc, proto_tc_subset );
Problems+Questions
Problem 1: In the wireshark display, the main and two subtrees of my dissection show up as "TC Subset" with two subtrees of "TC Subset" instead of "TC Subset" with two subtrees of "Primary Header" and "Segment Header".
Question 1: I have a feeling that's because I passed in "proto_tc_subset" when instantiating the two subtrees. How do I correctly define them as primary and segment headers so that they display as I would like?
++++++++++++++++++++++++++++++++++++++++++++++++
Problem 2: In the wireshark display, the packets I am analyzing have a "1" for the bypass flag. This is being displayed as '(null)' in the Wireshark display. This is the value_field for that field - its structure is similar to the one for the command flag, though that is 0, and that one works as expected. Removing the VALS() call with this value_string and rebuilding shows that the value is interpreted as "True" by default, so the "1" is being read correctly with the bitmask.
//'Enum' equivalent describing what the bypass flag means
static const value_string tc_primary_header_bypass_flags[] = {
{ 0, "Operating in Type-A TC Mode, with retransmissions and ACKs" },
{ 1, "Operating in Bypass Mode - no retransissions or ACKs" },
{ 0, NULL }
};
Question 2 : What did I do wrong for this to constantly read out as (null), and how do I fix it?
++++++++++++++++++++++++++++++++++++++++++++++++
Problem 3: In the segment header, I get an error that says:
Expert Info (Warning/Malformed): Trying to fetch an unsigned integer with length 6 when attempting to read the map_id. It seems to be reading it correctly, though.
Question 3: Why does Wireshark think this is malformed, and how do I read the value without generating a warning?
++++++++++++++++++++++++++++++++++++++++++++++++
Problem 4 (Final Problem): After the map_ID, I have a critical failure. It says that there is a dissector bug in my protocol; specifically, the packet.c file generated from my dissector has a failed assertion "handle != ((void *) 0)".
Question 4: I assume that that error has to do with my attempt to hand off the remainder of the packet to the ethernet protocol dissector. How am I supposed to properly do that handoff?