diff options
author | Anders Broman <anders.broman@ericsson.com> | 2009-07-03 11:45:42 +0000 |
---|---|---|
committer | Anders Broman <anders.broman@ericsson.com> | 2009-07-03 11:45:42 +0000 |
commit | 24b17483b718522b382ce9165328aa2ec48a1d84 (patch) | |
tree | af0024171b7887069a014f15ad31b9da3626323d /epan/dissectors/packet-ndmp.c | |
parent | 9518d53ab4e7c3bb8a41077660bbe4e800ffd2b1 (diff) |
From Chris Costa:
NDMP fragmentation fix.
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3395
svn path=/trunk/; revision=28935
Diffstat (limited to 'epan/dissectors/packet-ndmp.c')
-rw-r--r-- | epan/dissectors/packet-ndmp.c | 406 |
1 files changed, 366 insertions, 40 deletions
diff --git a/epan/dissectors/packet-ndmp.c b/epan/dissectors/packet-ndmp.c index 0a49ca57b7..a441e3fe7e 100644 --- a/epan/dissectors/packet-ndmp.c +++ b/epan/dissectors/packet-ndmp.c @@ -224,6 +224,15 @@ static int hf_ndmp_data_bytes_processed = -1; static int hf_ndmp_data_est_bytes_remain = -1; static int hf_ndmp_data_est_time_remain = -1; +static int hf_ndmp_fragments = -1; +static int hf_ndmp_fragment = -1; +static int hf_ndmp_fragment_overlap = -1; +static int hf_ndmp_fragment_overlap_conflicts = -1; +static int hf_ndmp_fragment_multiple_tails = -1; +static int hf_ndmp_fragment_too_long_fragment = -1; +static int hf_ndmp_fragment_error = -1; +static int hf_ndmp_reassembled_in = -1; + static gint ett_ndmp = -1; static gint ett_ndmp_fraghdr = -1; static gint ett_ndmp_header = -1; @@ -242,6 +251,29 @@ static gint ett_ndmp_file_name = -1; static gint ett_ndmp_file_stats = -1; static gint ett_ndmp_file_invalids = -1; static gint ett_ndmp_state_invalids = -1; +static gint ett_ndmp_fragment = -1; +static gint ett_ndmp_fragments = -1; + +static const fragment_items ndmp_frag_items = { + /* Fragment subtrees */ + &ett_ndmp_fragment, + &ett_ndmp_fragments, + /* Fragment fields */ + &hf_ndmp_fragments, + &hf_ndmp_fragment, + &hf_ndmp_fragment_overlap, + &hf_ndmp_fragment_overlap_conflicts, + &hf_ndmp_fragment_multiple_tails, + &hf_ndmp_fragment_too_long_fragment, + &hf_ndmp_fragment_error, + /* Reassembled in field */ + &hf_ndmp_reassembled_in, + /* Tag */ + "NDMP fragments" +}; + +static GHashTable *ndmp_fragment_table = NULL; +static GHashTable *ndmp_reassembled_table = NULL; /* XXX someone should start adding the new stuff from v3, v4 and v5*/ #define NDMP_PROTOCOL_UNKNOWN 0 @@ -259,6 +291,11 @@ static enum_val_t ndmp_protocol_versions[] = { static gint ndmp_default_protocol_version = NDMP_PROTOCOL_V4; +typedef struct _ndmp_frag_info { + guint32 first_seq; + guint16 offset; +} ndmp_frag_info; + typedef struct _ndmp_task_data_t { guint32 request_frame; guint32 response_frame; @@ -270,6 +307,8 @@ typedef struct _ndmp_conv_data_t { guint8 version; emem_tree_t *tasks; /* indexed by Sequence# */ emem_tree_t *itl; /* indexed by packet# */ + emem_tree_t *fragsA; /* indexed by Sequence# */ + emem_tree_t *fragsB; ndmp_task_data_t *task; conversation_t *conversation; } ndmp_conv_data_t; @@ -493,6 +532,82 @@ static const value_string msg_vals[] = { {0, NULL} }; +gboolean +check_ndmp_rm(tvbuff_t *tvb, packet_info *pinfo) +{ + guint len; + guint32 tmp; + + /* verify that the tcp port is 10000, ndmp always runs on port 10000*/ + if ((pinfo->srcport!=TCP_PORT_NDMP)&&(pinfo->destport!=TCP_PORT_NDMP)) { + return FALSE; + } + + /* check that the header looks sane */ + len=tvb_length(tvb); + /* check the record marker that it looks sane. + * It has to be >=24 bytes or (arbitrary limit) <1Mbyte + */ + if(len>=4){ + tmp=(tvb_get_ntohl(tvb, 0)&RPC_RM_FRAGLEN); + if( (tmp<24)||(tmp>1000000) ){ + return FALSE; + } + } + + return TRUE; +} + +gboolean +check_ndmp_hdr(tvbuff_t *tvb, packet_info *pinfo) +{ + guint len; + guint32 tmp; + + len=tvb_length(tvb); + + /* If the length is less than 24, it isn't a valid + header */ + if (len<24){ + return FALSE; + } + + /* check the timestamp, timestamps are valid if they + * (arbitrary) lie between 1980-jan-1 and 2030-jan-1 + */ + if(len>=8){ + tmp=tvb_get_ntohl(tvb, 4); + if( (tmp<0x12ceec50)||(tmp>0x70dc1ed0) ){ + return FALSE; + } + } + + /* check the type */ + if(len>=12){ + tmp=tvb_get_ntohl(tvb, 8); + if( tmp>1 ){ + return FALSE; + } + } + + /* check message */ + if(len>=16){ + tmp=tvb_get_ntohl(tvb, 12); + if( (tmp>0xa09) || (tmp==0) ){ + return FALSE; + } + } + + /* check error */ + if(len>=24){ + tmp=tvb_get_ntohl(tvb, 20); + if( (tmp>0x17) ){ + return FALSE; + } + } + + return TRUE; +} static int dissect_connect_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, @@ -520,7 +635,7 @@ dissect_error(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE); if(err && check_col(pinfo->cinfo, COL_INFO)) { col_append_fstr(pinfo->cinfo, COL_INFO, - " NDMP Error:%s", + " NDMP Error:%s ", val_to_str(err, error_vals, "Unknown NDMP error code %#x")); } @@ -2646,7 +2761,6 @@ dissect_data_get_state_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, return offset; } - typedef struct _ndmp_command { guint32 cmd; int (*request) (tvbuff_t *tvb, int offset, packet_info *pinfo, @@ -2807,7 +2921,7 @@ dissect_ndmp_header(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *p offset=dissect_error(tvb, offset, pinfo, tree, nh->seq); if (check_col(pinfo->cinfo, COL_INFO)){ - col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s", + col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s ", val_to_str(nh->msg, msg_vals, "Unknown Message (0x%02x)"), val_to_str(nh->type, msg_type_vals, "Unknown Type (0x%02x)") ); @@ -2870,12 +2984,22 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) guint32 ndmp_rm; struct ndmp_header nh; guint32 size; + guint32 seq, len, nxt, frag_num; + gint nbytes; + int direction; + struct tcpinfo *tcpinfo; + ndmp_frag_info* nfi; proto_item *ndmp_item = NULL; proto_tree *ndmp_tree = NULL; proto_item *hdr_item = NULL; proto_tree *hdr_tree = NULL; + emem_tree_t *frags; conversation_t *conversation; proto_item *vers_item; + gboolean save_fragmented, save_writable; + gboolean do_frag = TRUE; + tvbuff_t* new_tvb = NULL; + fragment_data *frag_msg = NULL; top_tree=tree; /* scsi should open its expansions on the top level */ @@ -2889,6 +3013,7 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); } + ndmp_conv_data=conversation_get_proto_data(conversation, proto_ndmp); if(!ndmp_conv_data){ ndmp_conv_data=se_alloc(sizeof(ndmp_conv_data_t)); @@ -2896,66 +3021,223 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ndmp_conv_data->tasks=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "NDMP tasks"); ndmp_conv_data->itl=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "NDMP itl"); ndmp_conv_data->conversation=conversation; + ndmp_conv_data->fragsA=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "NDMP fragsA"); + ndmp_conv_data->fragsB=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "NDMP fragsB"); conversation_add_proto_data(conversation, proto_ndmp, ndmp_conv_data); } - /* size of this NDMP PDU */ - size = tvb_length_remaining(tvb, offset); - if (size < 28) { - /* too short to be NDMP */ - return; - } - /* - * Read the NDMP header, if we have it. + * Read the NDMP record marker, if we have it. */ ndmp_rm=tvb_get_ntohl(tvb, offset); - nh.seq = tvb_get_ntohl(tvb, offset+4); - nh.time = tvb_get_ntohl(tvb, offset+8); - nh.type = tvb_get_ntohl(tvb, offset+12); - nh.msg = tvb_get_ntohl(tvb, offset+16); - nh.rep_seq = tvb_get_ntohl(tvb, offset+20); - nh.err = tvb_get_ntohl(tvb, offset+24); + /* Reassemble if desegmentation and reassembly are enabled, otherwise + * just pass through and use the data in tvb for dissection */ + if (ndmp_defragment && ndmp_desegment) + { + + /* Initialize the tables, if neccesary */ + if (ndmp_fragment_table == NULL) + { + fragment_table_init(&ndmp_fragment_table); + reassembled_table_init(&ndmp_reassembled_table); + } + + /* + * Determine the direction of the flow, so we can use the correct fragment tree + */ + direction=CMP_ADDRESS(&pinfo->src, &pinfo->dst); + if(direction==0) { + direction= (pinfo->srcport > pinfo->destport) ? 1 : -1; + } + if(direction>=0){ + frags = ndmp_conv_data->fragsA; + } else { + frags = ndmp_conv_data->fragsB; + } - /* - * Check if this is the last fragment. - */ - if (!(ndmp_rm & RPC_RM_LASTFRAG)) { /* - * This isn't the last fragment. - * If we're doing reassembly, just return - * TRUE to indicate that this looks like - * the beginning of an NDMP message, - * and let them do reassembly. + * Figure out the tcp seq and pdu length. Fragment tree is indexed based on seq; */ - if (ndmp_defragment) + if (pinfo == NULL || pinfo->private_data == NULL) { + return; + } + tcpinfo = pinfo->private_data; + + if (tcpinfo == NULL) { return; + } + seq = tcpinfo->seq; + len = (ndmp_rm & RPC_RM_FRAGLEN) + 4; + nxt = seq + len; + + /* + * In case there are multiple PDUs in the same frame, advance the tcp seq + * so that they can be distinguished from one another + */ + tcpinfo->seq = nxt; + + nfi = se_tree_lookup32(frags, seq); + + if (!nfi) + { + frag_num = 0; + + /* + * If nfi doesn't exist, then there are no fragments before this one. + * If there are fragments after this one, create the entry in the frag + * tree so the next fragment can find it. + */ + if (!(ndmp_rm & RPC_RM_LASTFRAG)) + { + nfi=se_alloc(sizeof(ndmp_frag_info)); + nfi->first_seq = seq; + nfi->offset = 1; + se_tree_insert32(frags, nxt, (void *)nfi); + } + /* + * If this is both the first and the last fragment, then there + * is no reason to even engage the reassembly routines. Just + * create the new_tvb directly from tvb. + */ + else + { + do_frag = FALSE; + new_tvb = tvb_new_subset(tvb, 4, -1, -1); + } + } + else + { + /* + * An entry was found, so we know the offset of this fragment + */ + frag_num = nfi->offset; + seq = nfi->first_seq; + + /* + * If this isn't the last frag, add another entry so the next fragment can find it. + */ + if (!(ndmp_rm & RPC_RM_LASTFRAG)) + { + nfi=se_alloc(sizeof(ndmp_frag_info)); + nfi->first_seq = seq; + nfi->offset = frag_num+1; + se_tree_insert32(frags, nxt, (void *)nfi); + } + } + + save_fragmented = pinfo->fragmented; + + /* If fragmentation is neccessary */ + if (do_frag) + { + pinfo->fragmented = TRUE; + + frag_msg = fragment_add_seq_check(tvb, 4, pinfo, + seq, + ndmp_fragment_table, + ndmp_reassembled_table, + frag_num, + tvb_length_remaining(tvb, offset)-4, + !(ndmp_rm & RPC_RM_LASTFRAG)); + + new_tvb = process_reassembled_data(tvb, 4, pinfo, "Reassembled NDMP", frag_msg, &ndmp_frag_items, NULL, tree); + } + + /* + * Check if this is the last fragment. + */ + if (!(ndmp_rm & RPC_RM_LASTFRAG)) { + /* + * Update the column info. + */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "NDMP"); + + if (check_col(pinfo->cinfo, COL_INFO)) { + col_clear(pinfo->cinfo, COL_INFO); + col_append_fstr(pinfo->cinfo, COL_INFO, "[NDMP fragment] "); + } + + /* + * Add the record marker information to the tree + */ + if (tree) { + ndmp_item = proto_tree_add_item(tree, proto_ndmp, tvb, 0, -1, FALSE); + ndmp_tree = proto_item_add_subtree(ndmp_item, ett_ndmp); + } + hdr_item = proto_tree_add_text(ndmp_tree, tvb, 0, 4, + "Fragment header: %s%u %s", + (ndmp_rm & RPC_RM_LASTFRAG) ? "Last fragment, " : "", + ndmp_rm & RPC_RM_FRAGLEN, plurality(ndmp_rm & RPC_RM_FRAGLEN, "byte", "bytes")); + hdr_tree = proto_item_add_subtree(hdr_item, ett_ndmp_fraghdr); + proto_tree_add_boolean(hdr_tree, hf_ndmp_lastfrag, tvb, 0, 4, ndmp_rm); + proto_tree_add_uint(hdr_tree, hf_ndmp_fraglen, tvb, 0, 4, ndmp_rm); + + /* + * Decode the remaining bytes as generic NDMP fragment data + */ + nbytes = tvb_reported_length_remaining(tvb, 4); + proto_tree_add_text(ndmp_tree, tvb, 4, nbytes, "NDMP fragment data (%u byte%s)", nbytes, plurality(nbytes, "", "s")); + + return; + } + } + else + { + new_tvb = tvb_new_subset(tvb, 4, -1, -1); + } + + + /* size of this NDMP PDU */ + size = tvb_length_remaining(new_tvb, offset); + if (size < 24) { + /* too short to be NDMP */ + return; } + /* + * If it doesn't look like a valid NDMP header at this point, there is + * no reason to move forward + */ + if (!check_ndmp_hdr(new_tvb, pinfo)) + { + return; + } + + nh.seq = tvb_get_ntohl(new_tvb, offset); + nh.time = tvb_get_ntohl(new_tvb, offset+4); + nh.type = tvb_get_ntohl(new_tvb, offset+8); + nh.msg = tvb_get_ntohl(new_tvb, offset+12); + nh.rep_seq = tvb_get_ntohl(new_tvb, offset+16); + nh.err = tvb_get_ntohl(new_tvb, offset+20); + + /* When the last fragment is small and the final frame contains + * multiple fragments, the column becomes unwritable. + * Temporarily change that so that the correct header can be + * applied */ + save_writable = col_get_writable(pinfo->cinfo); + col_set_writable(pinfo->cinfo, TRUE); + if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, "NDMP"); if (check_col(pinfo->cinfo, COL_INFO)) { col_clear(pinfo->cinfo, COL_INFO); } - if (tree) { - ndmp_item = proto_tree_add_item(tree, proto_ndmp, - tvb, 0, -1, FALSE); + ndmp_item = proto_tree_add_item(tree, proto_ndmp, tvb, 0, -1, FALSE); ndmp_tree = proto_item_add_subtree(ndmp_item, ett_ndmp); } - /* ndmp version (and autodetection) */ if(ndmp_conv_data->version!=NDMP_PROTOCOL_UNKNOWN){ - vers_item=proto_tree_add_uint(ndmp_tree, hf_ndmp_version, tvb, offset, 0, ndmp_conv_data->version); + vers_item=proto_tree_add_uint(ndmp_tree, hf_ndmp_version, new_tvb, offset, 0, ndmp_conv_data->version); } else { - vers_item=proto_tree_add_uint_format(ndmp_tree, hf_ndmp_version, tvb, offset, 0, ndmp_default_protocol_version, "Unknown NDMP version, using default:%d", ndmp_default_protocol_version); + vers_item=proto_tree_add_uint_format(ndmp_tree, hf_ndmp_version, new_tvb, offset, 0, ndmp_default_protocol_version, "Unknown NDMP version, using default:%d", ndmp_default_protocol_version); } PROTO_ITEM_SET_GENERATED(vers_item); - /* request response matching */ ndmp_conv_data->task=NULL; switch(nh.type){ @@ -2972,7 +3254,8 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) } if(ndmp_conv_data->task && ndmp_conv_data->task->response_frame){ proto_item *it; - it=proto_tree_add_uint(ndmp_tree, hf_ndmp_response_frame, tvb, 0, 0, ndmp_conv_data->task->response_frame); + it=proto_tree_add_uint(ndmp_tree, hf_ndmp_response_frame, new_tvb, 0, 0, ndmp_conv_data->task->response_frame); + PROTO_ITEM_SET_GENERATED(it); } break; @@ -2989,17 +3272,18 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) proto_item *it; nstime_t delta_ts; - it=proto_tree_add_uint(ndmp_tree, hf_ndmp_request_frame, tvb, 0, 0, ndmp_conv_data->task->request_frame); + it=proto_tree_add_uint(ndmp_tree, hf_ndmp_request_frame, new_tvb, 0, 0, ndmp_conv_data->task->request_frame); + PROTO_ITEM_SET_GENERATED(it); nstime_delta(&delta_ts, &pinfo->fd->abs_ts, &ndmp_conv_data->task->ndmp_time); - it=proto_tree_add_time(ndmp_tree, hf_ndmp_time, tvb, 0, 0, &delta_ts); + it=proto_tree_add_time(ndmp_tree, hf_ndmp_time, new_tvb, 0, 0, &delta_ts); PROTO_ITEM_SET_GENERATED(it); } break; } - + /* Add the record marker information to the tree */ hdr_item = proto_tree_add_text(ndmp_tree, tvb, 0, 4, "Fragment header: %s%u %s", (ndmp_rm & RPC_RM_LASTFRAG) ? "Last fragment, " : "", @@ -3013,7 +3297,12 @@ dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) * are implementations which pad some additional data after * the PDU. We MUST use size. */ - dissect_ndmp_cmd(tvb, offset+4, pinfo, ndmp_tree, &nh); + dissect_ndmp_cmd(new_tvb, offset, pinfo, ndmp_tree, &nh); + + /* restore saved variabled */ + pinfo->fragmented = save_fragmented; + col_set_writable(pinfo->cinfo, save_writable); + return; } @@ -3092,7 +3381,16 @@ check_if_ndmp(tvbuff_t *tvb, packet_info *pinfo) static int dissect_ndmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { - if(!check_if_ndmp(tvb, pinfo)) { + /* If we are doing defragmentation, don't check more than the record mark here, + * because if this is a continuation of a fragmented NDMP PDU there won't be a + * NDMP header after the RM */ + if(ndmp_defragment && !check_ndmp_rm(tvb, pinfo)) { + return 0; + } + + /* If we aren't doing both defragmentation and reassembly, check for the entire + * NDMP header before proceeding */ + if(!(ndmp_defragment && ndmp_desegment) && !check_if_ndmp(tvb, pinfo)) { return 0; } @@ -3783,6 +4081,32 @@ proto_register_ndmp(void) { &hf_ndmp_fraglen, { "Fragment Length", "ndmp.fraglen", FT_UINT32, BASE_DEC, NULL, RPC_RM_FRAGLEN, NULL, HFILL }}, + {&hf_ndmp_fragments, { + "NDMP fragments", "ndmp.fragments", FT_NONE, BASE_NONE, + NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment, + {"NDMP fragment", "ndmp.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment_overlap, + {"NDMP fragment overlap", "ndmp.fragment.overlap", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment_overlap_conflicts, + {"NDMP fragment overlapping with conflicting data", + "msg.fragment.overlap.conflicts", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment_multiple_tails, + {"NDMP has multiple tail fragments", + "msg.fragment.multiple_tails", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment_too_long_fragment, + {"NDMP fragment too long", "ndmp.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_fragment_error, + {"NDMP defragmentation error", "ndmp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_ndmp_reassembled_in, + {"Reassembled in", "ndmp.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, }; static gint *ett[] = { @@ -3804,6 +4128,8 @@ proto_register_ndmp(void) &ett_ndmp_file_stats, &ett_ndmp_file_invalids, &ett_ndmp_state_invalids, + &ett_ndmp_fragment, + &ett_ndmp_fragments, }; module_t *ndmp_module; |