Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Debugging Dissector Read Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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?

Debugging Dissector Read Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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?

Debugging Dissector Read Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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?

Debugging Dissector Read Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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?

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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, FT_UINT16, BASE_DEC, NULL, 0xc0,
0x00c0,
            NULL, HFILL }
        },
        { &hf_tc_map_id,
            { "MAP ID",           "tc.map_id",
            FT_UINT8, FT_UINT16, BASE_DEC, NULL, 0x3f,
0x003f,
            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?

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 10, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 6, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 10, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 8, 1, second_word);
    offset+=5;
offset+=1;
    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, 1, second_word);
    proto_tree_add_item(segment_header_tree, hf_tc_map_id, tvb, offset, 6, 1, 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_UINT16, BASE_DEC, NULL, 0x00c0,
            NULL, HFILL }
        },
        { &hf_tc_map_id,
            { "MAP ID",           "tc.map_id",
            FT_UINT16, BASE_DEC, NULL, 0x003f,
            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?

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
    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, 1, second_word);
    proto_tree_add_item(segment_header_tree, hf_tc_map_id, tvb, offset, 1, 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_UINT16, BASE_DEC, NULL, 0x00c0,
            NULL, HFILL }
        },
        { &hf_tc_map_id,
            { "MAP ID",           "tc.map_id",
            FT_UINT16, BASE_DEC, NULL, 0x003f,
            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 3 (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 3: 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?handoff? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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);
tvb_get_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_ntohs(tvb, 32);
tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
    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, 1, second_word);
    proto_tree_add_item(segment_header_tree, hf_tc_map_id, tvb, offset, 1, 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_UINT16, BASE_DEC, NULL, 0x00c0,
            NULL, HFILL }
        },
        { &hf_tc_map_id,
            { "MAP ID",           "tc.map_id",
            FT_UINT16, BASE_DEC, NULL, 0x003f,
            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 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
    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, 1, second_word);
    proto_tree_add_item(segment_header_tree, hf_tc_map_id, tvb, offset, 1, second_word);
     //Similar process with adding items + updating offset += 1;
value afterwards as needed

    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 
      //I continue instantiating fields, in this manner, 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_UINT16, BASE_DEC, NULL, 0x00c0,
            NULL, HFILL }
        },
        { &hf_tc_map_id,
            { "MAP ID",           "tc.map_id",
            FT_UINT16, BASE_DEC, NULL, 0x003f,
            NULL, HFILL }
        }
    };
bitmask


    /* 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 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
    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);

     //Similar process with adding items + updating offset value afterwards as needed

    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 }
        },

      //I continue in this manner, sliding along the bitmask


    /* 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 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. 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_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
    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);

     //Similar process with adding items + updating offset value afterwards as needed

    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 }
        },

      //I continue in this manner, sliding along the bitmask


    /* 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 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

EDIT: As problems have been solved, I've reduced the amount of code shown to make it easier to pinpoint potential issues. Thanks to Pascal Quentin, Guy Harris, and grahamb for all their help so far.

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for short
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. A 6 bit map_id field - I'll call this map for short.

"dissect_tc" Method

FIrst, I'll show most some 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_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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, 1, first_word);
    proto_tree_add_uint(primary_header_tree, hf_tc_spacecraft_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_virtual_chan_id, tvb, offset, 2, first_word);
    offset+=1;
    proto_tree_add_uint(primary_header_tree, hf_tc_frame_length, tvb, offset, 2, first_word);
    offset+=2
    proto_tree_add_uint(primary_header_tree, hf_tc_sequence_number, tvb, offset, 1, second_word);
    offset+=1;
//Continue adding items in a similar manner, updating offset as needed until it is 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);

     //Similar process with adding items + updating offset value afterwards as needed
needed, offset is 6

    proto_item_set_end(segment_header, tvb, offset);

    //Handing off to ethernet dissector.
    //NOTE: some useful find_dissector args: eth, eth_(with/no/maybe)fcs, ip, ipv6, mpls.
    call_dissector(find_dissector("eth"), call_dissector(find_dissector("ip"), 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 data */
        { &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 }
        },

      //I 
      /*I continue in this manner, sliding along the bitmask
defining all fields for both primary and segment headers
         in this singular hf array. Maybe this is supposed to be two arrays, one for each? */


    /* 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 It has TC Subset because I passed in "proto_tc_subset" when instantiating the two subtrees. How do I correctly define them variables such as primary and segment headers so that they display "proto_tc_subset_primary" and "proto_tc_subset_segment" to pass those variables in instead? The desired result is as follows: In Wireshark, when examining the the protocol display, where > is a non-expanded item and V indicates an expanded item, instead of

>Frame 1
V TC_Subset
   >TC_Subset
   >TC_Subset

I would like?

like the output to look like this.

>Frame 1
V TC_Subset
   >Primary Header
   >Segment Header

++++++++++++++++++++++++++++++++++++++++++++++++

PROBLEM 2 SOLVED - use TFS for boolean

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. 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.

Question 2 : What did I do wrong for this to constantly read out as (null), and how do I fix it?

++++++++++++++++++++++++++++++++++++++++++++++++

PROBLEM 3 SOLVED - incorrect dissector calling as seen in answer below

Problem 3 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.

Debugging Dissector Read and Dissector Handoff Issue

EDIT: As problems have been solved, I've reduced the amount of code shown to make it easier to pinpoint potential issues. Thanks to Pascal Quentin, Guy Harris, and grahamb for all their help so far.

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:

  1. 2 bit version number -
  2. 1 bit bypass flag
  3. 1 bit command flag
  4. 2 bit spare field
  5. 10 bit spacecraft_id field - outside of code, I'll call this id for short
  6. 6 bit virtual_channel_id field - outside of code, I'll call this vid for shortfield
  7. 10 bit length field
  8. 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:

  1. A 2 bit sequence flag field
  2. A 6 bit map_id field - I'll call this map for short.

"dissect_tc" Method

FIrst, I'll show some 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_guint32(tvb, 0, ENC_BIG_ENDIAN);
    second_word = tvb_get_guint16(tvb, 4, ENC_BIG_ENDIAN);

    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.)
correctly)

    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);
    //Continue adding items in a similar manner, updating offset as needed until it is 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);

     //Similar process with adding items + updating offset value afterwards as needed, offset is 6
needed

    proto_item_set_end(segment_header, tvb, offset);

    //Handing off to ethernet dissector.
    //NOTE: some useful find_dissector args: eth_(with/no/maybe)fcs, ip, ipv6, mpls.
    call_dissector(find_dissector("ip"), call_dissector(find_dissector("eth_maybefcs"), 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.dissector. A lot of code isn't shown here.

void
proto_register_tc(void)
{
    static hf_register_info hf[] = {

            /* tc header data */
        { &hf_tc_version_number,
            { "Version",           "tc.version",
            FT_UINT32, BASE_DEC, NULL, 0xc0000000,
            NULL, HFILL }
        },

      /*I continue in this manner, defining all fields for both primary and segment headers
         in this singular hf array. Maybe this is supposed to be two arrays, one for each? */


    /* 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: It has TC Subset because I passed in "proto_tc_subset" when instantiating the two subtrees. How do I correctly define variables such as "proto_tc_subset_primary" and "proto_tc_subset_segment" to pass those variables in instead? The desired result is as follows: In Wireshark, when examining the the protocol display, where > is a non-expanded item and V indicates an expanded item, instead of

>Frame 1
V TC_Subset
   >TC_Subset
   >TC_Subset

I would like the output to look like this.

>Frame 1
V TC_Subset
   >Primary Header
   >Segment Header

++++++++++++++++++++++++++++++++++++++++++++++++

PROBLEM 2 SOLVED - use TFS for boolean

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. 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.

Question 2 : What did I do wrong for this to constantly read out as (null), and how do I fix it?

++++++++++++++++++++++++++++++++++++++++++++++++

PROBLEM 3 SOLVED - incorrect dissector calling as seen in answer below

Problem 3 (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 3: 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? I've tried using "ip" there instead and it'll just give the rest as a big heap of undissected data, and removing the statement eliminates the error, so it's definitely the handoff.