diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-ltp.c | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-ltp.c')
-rw-r--r-- | epan/dissectors/packet-ltp.c | 1940 |
1 files changed, 1940 insertions, 0 deletions
diff --git a/epan/dissectors/packet-ltp.c b/epan/dissectors/packet-ltp.c new file mode 100644 index 00000000..57653435 --- /dev/null +++ b/epan/dissectors/packet-ltp.c @@ -0,0 +1,1940 @@ +/* packet-ltp.c + * Routines for LTP dissection + * Copyright 2009, Mithun Roy <mithunroy13@gmail.com> + * Copyright 2017, Krishnamurthy Mayya <krishnamurthymayya@gmail.com> + Revision: Minor modifications to Header and Trailer extensions + by correcting the offset handling. + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * Modifications were made to this file under designation MFS-33289-1 and + * are Copyright 2015 United States Government as represented by NASA + * Marshall Space Flight Center. All Rights Reserved. + * + * Released under the GNU GPL with NASA legal approval granted 2016-06-10. + * + * The subject software is provided "AS IS" WITHOUT ANY WARRANTY of any kind, + * either expressed, implied or statutory and this agreement does not, + * in any manner, constitute an endorsement by government agency of any + * results, designs or products resulting from use of the subject software. + * See the Agreement for the specific language governing permissions and + * limitations. + */ + +/* + * Licklider Transmission Protocol - RFC 5326. + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/expert.h> +#include <epan/conversation.h> +#include <epan/conversation_table.h> +#include <epan/conversation_filter.h> +#include <epan/proto_data.h> +#include <epan/reassemble.h> +#include <epan/stats_tree.h> +#include <epan/to_str.h> +#include <wsutil/wmem/wmem_map.h> +#include <wsutil/wmem/wmem_interval_tree.h> + +void proto_register_ltp(void); +void proto_reg_handoff_ltp(void); + +static dissector_handle_t ltp_handle; + +#define LTP_MIN_DATA_BUFFER 5 + +/// Unique session identifier +typedef struct { + /// Session originator + guint64 orig_eng_id; + /// Session number + guint64 sess_num; +} ltp_session_id_t; + +/** Function to match the GHashFunc signature. + */ +static guint +ltp_session_id_hash(gconstpointer ptr) +{ + const ltp_session_id_t *obj = ptr; + return ( + g_int64_hash(&(obj->orig_eng_id)) + ^ g_int64_hash(&(obj->sess_num)) + ); +} + +/** Function to match the GEqualFunc signature. + */ +static gboolean +ltp_session_id_equal(gconstpointer a, gconstpointer b) +{ + const ltp_session_id_t *aobj = a; + const ltp_session_id_t *bobj = b; + return ( + (aobj->orig_eng_id == bobj->orig_eng_id) + && (aobj->sess_num == bobj->sess_num) + ); +} + +/// Reassembly function +static gpointer +ltp_session_new_key(const packet_info *pinfo _U_, const guint32 id _U_, + const void *data) +{ + const ltp_session_id_t *obj = data; + ltp_session_id_t *key = g_slice_new(ltp_session_id_t); + + key->orig_eng_id = obj->orig_eng_id; + key->sess_num = obj->sess_num; + + return (gpointer)key; +} + +/// Reassembly function +static void +ltp_session_free_key(gpointer ptr) +{ + ltp_session_id_t *key = (ltp_session_id_t *)ptr; + g_slice_free(ltp_session_id_t, key); +} + +typedef struct { + guint32 frame_num; + nstime_t abs_ts; +} ltp_frame_info_t; + +static ltp_frame_info_t * +ltp_frame_info_new(const packet_info *pinfo) +{ + ltp_frame_info_t *obj = wmem_new(wmem_file_scope(), ltp_frame_info_t); + obj->frame_num = pinfo->num; + obj->abs_ts = pinfo->abs_ts; + return obj; +} + +/** Function to match the GCompareFunc signature. + */ +static gint +ltp_frame_info_find_pinfo(gconstpointer a, gconstpointer b) +{ + const ltp_frame_info_t *aobj = a; + const packet_info *bobj = b; + if (aobj->frame_num < bobj->num) return -1; + if (aobj->frame_num > bobj->num) return 1; + return 0; +} + +/// A session is an LTP conversation +typedef struct { + /** Map from first-seen segment data ranges to data frame info (ltp_frame_info_t*) */ + wmem_itree_t *data_segs; + /** Map from report ID (guint64) to tree (wmem_itree_t*) of + * first-seen segment data ranges to data frame info (ltp_frame_info_t*) */ + wmem_map_t *rpt_segs; + /** Set after seeing EORP */ + guint64 *red_size; + /** Set after seeing EOB */ + guint64 *block_size; + + /** Map from checkpoint ID (guint64) to wmem_list_t of frame info (ltp_frame_info_t*) */ + wmem_map_t *checkpoints; + /** Map from checkpoint ID (guint64) to wmem_list_t of frame info (ltp_frame_info_t*) */ + wmem_map_t *chkp_acks; + /** Map from report ID (guint64) to wmem_list_t of frame info (ltp_frame_info_t*) */ + wmem_map_t *reports; + /** Map from report ID (guint64) to wmem_list_t of frame info (ltp_frame_info_t*) */ + wmem_map_t *rpt_acks; + /** Map from report ID (guint64) to wmem_list_t of frame info (ltp_frame_info_t*) */ + wmem_map_t *rpt_datas; +} ltp_session_data_t; + +/** Add a cross-reference value source. + * @param map The map to add to. + * @param ref_num The cross-reference value. + * @param pinfo The source frame of the value. + */ +static void +ltp_ref_src(wmem_map_t *map, guint64 ref_num, const packet_info *pinfo) +{ + wmem_list_t *found = wmem_map_lookup(map, &ref_num); + if (!found) + { + guint64 *key = wmem_new(wmem_file_scope(), guint64); + *key = ref_num; + found = wmem_list_new(wmem_file_scope()); + wmem_map_insert(map, key, found); + } + + if (wmem_list_find_custom(found, pinfo, ltp_frame_info_find_pinfo)) + { + return; + } + ltp_frame_info_t *val = ltp_frame_info_new(pinfo); + wmem_list_append(found, val); +} + +/** Show cross-reference value sources as tree items. + * @param map The map to search in. + * @param ref_num The cross-reference value. + * @param pinfo The frame using the reference (to avoid duplicates). + * @param tree The tree to show references under. + * @param hf_ref The field index to add source frame numbers. + * @param hf_time The field index to report time differences. + */ +static void +ltp_ref_use(wmem_map_t *map, guint64 ref_num, const packet_info *pinfo, proto_tree *tree, int hf_ref, int hf_time) +{ + const wmem_list_t *found = wmem_map_lookup(map, &ref_num); + if (!found) + { + return; + } + + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame_refd = wmem_list_frame_data(it); + if (frame_refd->frame_num == pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(tree, hf_ref, NULL, 0, 0, frame_refd->frame_num) + ); + + if (hf_time >= 0) + { + nstime_t td; + nstime_delta(&td, &(pinfo->abs_ts), &(frame_refd->abs_ts)); + PROTO_ITEM_SET_GENERATED( + proto_tree_add_time(tree, hf_time, NULL, 0, 0, &td) + ); + } + } +} + +/// Tap info for single segment +typedef struct { + /// Associated session context (optional) + ltp_session_data_t *session; + /// Segment type + guint8 seg_type; + /// Session ID + ltp_session_id_t sess_id; + /// Text form of session name, scoped to file + const char *sess_name; + /// Full segment size + guint seg_size; + /// If non-zero, the size of the contained block + guint block_size; + /// For red data segment or report, is this original + gboolean corr_orig; +} ltp_tap_info_t; + +/* For reassembling LTP segments */ +static reassembly_table ltp_reassembly_table; + +/* Initialize the protocol and registered fields */ +static int proto_ltp = -1; + +static int ltp_tap = -1; + +static gboolean ltp_reassemble_block = TRUE; +static gboolean ltp_analyze_sequence = TRUE; + +/* LTP Header variables */ +static int hf_ltp_version = -1; +static int hf_ltp_type = -1; +static int hf_ltp_session_name = -1; +static int hf_ltp_session_orig = -1; +static int hf_ltp_session_no = -1; +static int hf_ltp_hdr_extn_cnt = -1; +static int hf_ltp_trl_extn_cnt = -1; + +/* LTP Data Segment variable */ +static int hf_ltp_data_clid = -1; +static int hf_ltp_data_offset = -1; +static int hf_ltp_data_length = -1; +static int hf_ltp_data_chkp = -1; +static int hf_ltp_data_chkp_rpt = -1; +static int hf_ltp_data_rpt = -1; +static int hf_ltp_data_rpt_ref = -1; +static int hf_ltp_data_rpt_time = -1; +static int hf_ltp_data_sda_clid = -1; +static int hf_ltp_partial_packet = -1; +static int hf_ltp_data_clidata = -1; +static int hf_ltp_data_retrans = -1; +static int hf_ltp_data_clm_rpt = -1; +static int hf_ltp_block_red_size = -1; +static int hf_ltp_block_green_size = -1; +static int hf_ltp_block_bundle_size = -1; +static int hf_ltp_block_bundle_cnt = -1; + +/* LTP Report Segment variable */ +static int hf_ltp_rpt_sno = -1; +static int hf_ltp_rpt_sno_ack = -1; +static int hf_ltp_rpt_sno_data = -1; +static int hf_ltp_rpt_chkp = -1; +static int hf_ltp_rpt_chkp_ref = -1; +static int hf_ltp_rpt_chkp_time = -1; +static int hf_ltp_rpt_ub = -1; +static int hf_ltp_rpt_lb = -1; +static int hf_ltp_rpt_len = -1; +static int hf_ltp_rpt_retrans = -1; +static int hf_ltp_rpt_clm_cnt = -1; +static int hf_ltp_rpt_clm_off = -1; +static int hf_ltp_rpt_clm_len = -1; +static int hf_ltp_rpt_clm_fst = -1; +static int hf_ltp_rpt_clm_lst = -1; +static int hf_ltp_rpt_clm_ref = -1; +static int hf_ltp_rpt_gap = -1; +static int hf_ltp_rpt_gap_ref = -1; +static int hf_ltp_rpt_gap_total = -1; + +/* LTP Report Ack Segment Variable */ +static int hf_ltp_rpt_ack_sno = -1; +static int hf_ltp_rpt_ack_dupe = -1; +static int hf_ltp_rpt_ack_ref = -1; +static int hf_ltp_rpt_ack_time = -1; +static int hf_ltp_cancel_ack = -1; + +/* LTP Session Management Segment Variable */ +static int hf_ltp_cancel_code = -1; + +/* LTP Header Extension Segment */ +static int hf_ltp_hdr_extn_tag = -1; +static int hf_ltp_hdr_extn_len = -1; +static int hf_ltp_hdr_extn_val = -1; + +/* LTP Trailer Extension Segment */ +static int hf_ltp_trl_extn_tag = -1; +static int hf_ltp_trl_extn_len = -1; +static int hf_ltp_trl_extn_val = -1; + +/*LTP reassembly */ +static int hf_ltp_fragments = -1; +static int hf_ltp_fragment = -1; +static int hf_ltp_fragment_overlap = -1; +static int hf_ltp_fragment_overlap_conflicts = -1; +static int hf_ltp_fragment_multiple_tails = -1; +static int hf_ltp_fragment_too_long_fragment = -1; +static int hf_ltp_fragment_error = -1; +static int hf_ltp_fragment_count = -1; +static int hf_ltp_reassembled_in = -1; +static int hf_ltp_reassembled_length = -1; + +static expert_field ei_ltp_mal_reception_claim = EI_INIT; +static expert_field ei_ltp_sdnv_length = EI_INIT; +static expert_field ei_ltp_sno_larger_than_ccsds = EI_INIT; +static expert_field ei_ltp_report_async = EI_INIT; + +static dissector_handle_t bundle_handle; + +static const value_string ltp_type_codes[] = { + {0x0, "Red data, NOT {Checkpoint, EORP or EOB}"}, + {0x1, "Red data, Checkpoint, NOT {EORP or EOB}"}, + {0x2, "Red data, Checkpoint, EORP, NOT EOB"}, + {0x3, "Red data, Checkpoint, EORP, EOB"}, + {0x4, "Green data, NOT EOB"}, + {0x5, "Green data, undefined"}, + {0x6, "Green data, undefined"}, + {0x7, "Green data, EOB"}, + {0x8, "Report segment"}, + {0x9, "Report-acknowledgment segment"}, + {0xa, "Control segment, undefined"}, + {0xb, "Control segment, undefined"}, + {0xc, "Cancel segment from block sender"}, + {0xd, "Cancel-acknowledgment segment to block sender"}, + {0xe, "Cancel segment from block receiver"}, + {0xf, "Cancel-acknowledgment segment to block receiver"}, + {0,NULL} +}; + +static const value_string ltp_type_col_info[] = { + {0x0, "Red data"}, + {0x1, "Red data"}, + {0x2, "Red data"}, + {0x3, "Red data"}, + {0x4, "Green data"}, + {0x5, "Green data"}, + {0x6, "Green data"}, + {0x7, "Green data"}, + {0x8, "Report segment"}, + {0x9, "Report ack segment"}, + {0xa, "Control segment"}, + {0xb, "Control segment"}, + {0xc, "Cancel segment"}, + {0xd, "Cancel ack segment"}, + {0xe, "Cancel segment"}, + {0xf, "Cancel ack segment"}, + {0, NULL} +}; + +static const value_string ltp_cancel_codes[] = { + {0x00, "Client service canceled session"}, + {0x01, "Unreachable client service"}, + {0x02, "Retransmission limit exceeded"}, + {0x03, "Miscolored segment"}, + {0x04, "A system error"}, + {0x05, "Exceeded the Retransmission-Cycles limit"}, + {0, NULL} +}; + +static const value_string extn_tag_codes[] = { + {0x00, "LTP authentication extension"}, + {0x01, "LTP cookie extension"}, + {0, NULL} +}; + +static const val64_string client_service_id_info[] = { + {0x01, "Bundle Protocol"}, + {0x02, "CCSDS LTP Service Data Aggregation"}, + {0, NULL} +}; + +#define LTP_PORT 1113 + +/* Initialize the subtree pointers */ +static gint ett_ltp = -1; +static gint ett_ltp_hdr = -1; +static gint ett_hdr_session = -1; +static gint ett_hdr_extn = -1; +static gint ett_frame_ref = -1; +static gint ett_data_segm = -1; +static gint ett_block = -1; +static gint ett_rpt_segm = -1; +static gint ett_rpt_clm = -1; +static gint ett_rpt_gap = -1; +static gint ett_rpt_ack_segm = -1; +static gint ett_session_mgmt = -1; +static gint ett_trl_extn = -1; +static gint ett_ltp_fragment = -1; +static gint ett_ltp_fragments = -1; + +static const fragment_items ltp_frag_items = { + /*Fragment subtrees*/ + &ett_ltp_fragment, + &ett_ltp_fragments, + /*Fragment Fields*/ + &hf_ltp_fragments, + &hf_ltp_fragment, + &hf_ltp_fragment_overlap, + &hf_ltp_fragment_overlap_conflicts, + &hf_ltp_fragment_multiple_tails, + &hf_ltp_fragment_too_long_fragment, + &hf_ltp_fragment_error, + &hf_ltp_fragment_count, + /*Reassembled in field*/ + &hf_ltp_reassembled_in, + /*Reassembled length field*/ + &hf_ltp_reassembled_length, + /* Reassembled data field */ + NULL, + /*Tag*/ + "LTP fragments" +}; + +static proto_item * +add_sdnv64_to_tree(proto_tree *tree, tvbuff_t *tvb, packet_info* pinfo, int offset, int hf_sdnv, guint64 *retval, gint *lenretval) +{ + proto_item *ti; + ti = proto_tree_add_item_ret_varint(tree, hf_sdnv, tvb, offset, -1, ENC_VARINT_SDNV, retval, lenretval); + + if (*lenretval <= 0) { + expert_add_info(pinfo, ti, &ei_ltp_sdnv_length); + } + return ti; +} + +/// Summary of a data segment tree item +typedef struct { + /// Data segment packet info + packet_info *pinfo; + /// Tree of the data segment + proto_tree *ltp_data_tree; + /// The first offset of this segment + guint64 data_fst; + /// The last offset of this segment + guint64 data_lst; +} ltp_data_seg_info_t; + +static void +ltp_data_seg_find_report(gpointer key _U_, gpointer value, gpointer user_data) +{ + wmem_itree_t *rpt_clms = value; + const ltp_data_seg_info_t *data_seg = user_data; + if (!(data_seg->data_fst <= data_seg->data_lst)) + { + return; + } + + wmem_list_t *found = wmem_itree_find_intervals(rpt_clms, wmem_packet_scope(), data_seg->data_fst, data_seg->data_lst); + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame = wmem_list_frame_data(it); + // report must be after this data segment + if (frame->frame_num < data_seg->pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(data_seg->ltp_data_tree, hf_ltp_data_clm_rpt, NULL, 0, 0, frame->frame_num) + ); + } + +} + +static int +dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int frame_offset,int ltp_type, + int *data_len, ltp_tap_info_t *tap) +{ + ltp_session_data_t *session = tap->session; + guint64 client_id; + guint64 data_offset; + guint64 data_length; + guint64 chkp_sno = 0; + guint64 rpt_sno = 0; + guint64 sda_client_id = 0; + + unsigned segment_size = 0; + + int sdnv_length; + + proto_tree *ltp_data_tree; + proto_item *ti; + + fragment_head *frag_msg = NULL; + + tvbuff_t *new_tvb = NULL; + + /* Create a subtree for data segment and add the other fields under it */ + ltp_data_tree = proto_tree_add_subtree(ltp_tree, tvb, frame_offset, tvb_captured_length_remaining(tvb, frame_offset), ett_data_segm, NULL, "Data Segment"); + + /* Client ID - 0 = Bundle Protocol, 1 = CCSDS LTP Service Data Aggregation */ + add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_clid, &client_id, &sdnv_length); + frame_offset += sdnv_length; + segment_size += sdnv_length; + + /* data segment offset */ + add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_offset, &data_offset, &sdnv_length); + if (sdnv_length > 0) { + frame_offset += sdnv_length; + segment_size += sdnv_length; + } else { + return 0; + } + + /* data segment length */ + add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_length, &data_length, &sdnv_length); + if (sdnv_length > 0) { + frame_offset += sdnv_length; + segment_size += sdnv_length; + + /* add in the data length also */ + segment_size += (unsigned int) data_length; + } else { + return 0; + } + *data_len = (int) data_length; + + const guint64 data_fst = data_offset; + const guint64 data_lst = data_offset + data_length - 1; + gboolean newdata = TRUE; + if (ltp_analyze_sequence && session) + { + if (data_fst <= data_lst) + { + wmem_list_t *found = wmem_itree_find_intervals(session->data_segs, wmem_packet_scope(), data_fst, data_lst); + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame = wmem_list_frame_data(it); + if (frame->frame_num == pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(ltp_data_tree, hf_ltp_data_retrans, NULL, 0, 0, frame->frame_num) + ); + newdata = false; + } + + if (newdata) + { + ltp_frame_info_t *val = ltp_frame_info_new(pinfo); + wmem_itree_insert(session->data_segs, data_fst, data_lst, val); + } + } + + ltp_data_seg_info_t data_seg_info; + data_seg_info.pinfo = pinfo; + data_seg_info.ltp_data_tree = ltp_data_tree; + data_seg_info.data_fst = data_fst; + data_seg_info.data_lst = data_lst; + wmem_map_foreach(session->rpt_segs, ltp_data_seg_find_report, &data_seg_info); + + } + tap->corr_orig = newdata; + + if (ltp_type != 0 && ltp_type < 4) + { + /* checkpoint serial number - 32 bits per CCSDS */ + ti = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_chkp, &chkp_sno, &sdnv_length); + if (sdnv_length > 0) { + frame_offset += sdnv_length; + segment_size += sdnv_length; + + if (chkp_sno > 4294967295U) { + /* just a warning - continue processing */ + expert_add_info(pinfo, ti, &ei_ltp_sno_larger_than_ccsds); + } + } else { + return 0; + } + if (ltp_analyze_sequence && session) + { + proto_tree *tree_chkp_sno = proto_item_add_subtree(ti, ett_frame_ref); + ltp_ref_src(session->checkpoints, chkp_sno, pinfo); + ltp_ref_use(session->chkp_acks, chkp_sno, pinfo, tree_chkp_sno, hf_ltp_data_chkp_rpt, -1); + } + + /* report serial number - 32 bits per CCSDS */ + ti = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_rpt, &rpt_sno, &sdnv_length); + if (sdnv_length > 0) { + frame_offset += sdnv_length; + segment_size += sdnv_length; + + if (rpt_sno > 4294967295U) { + /* just a warning - continue processing */ + expert_add_info(pinfo, ti, &ei_ltp_sno_larger_than_ccsds); + } + } else { + return 0; + } + if (ltp_analyze_sequence && session) + { + ltp_ref_src(session->rpt_datas, rpt_sno, pinfo); + ltp_ref_use(session->reports, rpt_sno, pinfo, proto_item_add_subtree(ti, ett_frame_ref), hf_ltp_data_rpt_ref, hf_ltp_data_rpt_time); + } + } + const gboolean is_green = (ltp_type >= 4) && (ltp_type <= 7); + const gboolean is_eorp = (ltp_type == 2) || (ltp_type == 3); + const gboolean is_eob = (ltp_type == 3) || (ltp_type == 7); + if (session) + { + if ((is_green && (data_offset == 0)) && !(session->red_size)) + { + session->red_size = wmem_new(wmem_file_scope(), guint64); + *(session->red_size) = 0; + } + if (is_eorp && !(session->red_size)) + { + session->red_size = wmem_new(wmem_file_scope(), guint64); + *(session->red_size) = data_offset + data_length; + } + if (is_eob && !(session->block_size)) + { + session->block_size = wmem_new(wmem_file_scope(), guint64); + *(session->block_size) = data_offset + data_length; + } + } + + proto_tree_add_item(ltp_data_tree, hf_ltp_data_clidata, tvb, frame_offset, -1, ENC_NA); + + if (segment_size >= tvb_captured_length(tvb)) { + /* did not capture the entire packet */ + /* XXX: expert info instead? Distinguish between segment_size + * >= reported_length (i.e., bogus reported data_length) and + * too short capture? */ + proto_tree_add_string(ltp_data_tree, hf_ltp_partial_packet, tvb, 0, 0, "<increase capture size?>"); + /* data_len is subtracted from the return value to set the + * header length, so report the number of data bytes available. + */ + *data_len = tvb_captured_length_remaining(tvb, frame_offset); + return tvb_captured_length(tvb); + } + + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, + "range %" G_GINT64_MODIFIER "u-%" G_GINT64_MODIFIER "u", + data_fst, data_lst); + + if (ltp_reassemble_block) + { + frag_msg = fragment_add_check( + <p_reassembly_table, + tvb, frame_offset, pinfo, 0, &(tap->sess_id), + (guint32)data_offset, (guint32)data_length, !is_eob + ); + } + if(frag_msg) + { + /* Checking if the segment is completely reassembled */ + if(!(frag_msg->flags & FD_PARTIAL_REASSEMBLY)) + { + /* if the segment has not been fragmented, then no reassembly is needed */ + if(is_eob && data_offset == 0) + { + new_tvb = tvb_new_subset_length(tvb, frame_offset, (int) data_length); + } + else + { + new_tvb = process_reassembled_data(tvb, frame_offset, pinfo, "Reassembled LTP Block", + frag_msg, <p_frag_items,NULL, ltp_tree); + + } + } + } + + if(new_tvb) + { + guint64 data_count = 0; + int parse_length = tvb_reported_length(new_tvb); + int parse_offset = 0; + proto_tree *root_tree = proto_tree_get_parent_tree(ltp_tree); + + /* Data associated with the full block, not just this segment */ + proto_tree *block_tree = proto_tree_add_subtree_format(ltp_tree, new_tvb, 0, -1, ett_block, NULL, + "Block, size: %d bytes", parse_length); + tap->block_size = parse_length; + + if (session && session->red_size && session->block_size) + { + guint64 red_size = *(session->red_size); + guint64 green_size = *(session->block_size) - *(session->red_size); + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(block_tree, hf_ltp_block_red_size, new_tvb, 0, (gint)red_size, red_size) + ); + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(block_tree, hf_ltp_block_green_size, new_tvb, (gint)red_size, (gint)green_size, green_size) + ); + } + + while(parse_offset < parse_length) + { + int bundle_size; + tvbuff_t *datatvb; + + if (client_id == 2) { + add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset+parse_offset, hf_ltp_data_sda_clid, &sda_client_id, &sdnv_length); + parse_offset += sdnv_length; + if (parse_offset == parse_length) { + col_set_str(pinfo->cinfo, COL_INFO, "CCSDS LTP SDA Protocol Error"); + return 0; /* Give up*/ + } + } + + datatvb = tvb_new_subset_remaining(new_tvb, parse_offset); + bundle_size = call_dissector(bundle_handle, datatvb, pinfo, root_tree); + if(bundle_size == 0) { /*Couldn't parse bundle*/ + col_set_str(pinfo->cinfo, COL_INFO, "Dissection Failed"); + return 0; /*Give up*/ + } + proto_tree_add_uint64(block_tree, hf_ltp_block_bundle_size, datatvb, 0, bundle_size, bundle_size); + + parse_offset += bundle_size; + data_count++; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(block_tree, hf_ltp_block_bundle_cnt, new_tvb, 0, parse_offset, data_count) + ); + } + else + { + if(ltp_reassemble_block && frag_msg && (frag_msg->flags & FD_DEFRAGMENTED)) + { + col_append_frame_number(pinfo, COL_INFO, " [Reassembled in #%d] ",frag_msg->reassembled_in); + } + else if (!newdata) + { + col_append_str(pinfo->cinfo, COL_INFO, " [Retransmission] "); + } + else if (ltp_reassemble_block) + { + col_append_str(pinfo->cinfo, COL_INFO, " [Unfinished LTP Block] "); + } + } + + return segment_size; +} + + +static void +ltp_check_reception_gap(proto_tree *ltp_rpt_tree, packet_info *pinfo, + ltp_session_data_t *session, guint64 prec_lst, guint64 next_fst, + int *gap_count, guint64 *gap_total) { + const guint64 gap_len = next_fst - (prec_lst + 1); + if (gap_len <= 0) { + return; + } + proto_item *gap_item = proto_tree_add_uint64_format(ltp_rpt_tree, hf_ltp_rpt_gap, NULL, 0, 0, gap_len, + "Reception gap: %" PRIu64 "-%" PRIu64 " (%" PRIu64 " bytes)", + prec_lst + 1, next_fst - 1, gap_len + ); + PROTO_ITEM_SET_GENERATED(gap_item); + *gap_count += 1; + *gap_total += gap_len; + + if (ltp_analyze_sequence && session) + { + proto_tree *gap_tree = proto_item_add_subtree(gap_item, ett_rpt_gap); + + const guint64 gap_fst = prec_lst + 1; + const guint64 gap_lst = next_fst - 1; + wmem_list_t *found = wmem_itree_find_intervals(session->data_segs, wmem_packet_scope(), gap_fst, gap_lst); + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame = wmem_list_frame_data(it); + if (frame->frame_num > pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(gap_tree, hf_ltp_rpt_gap_ref, NULL, 0, 0, frame->frame_num) + ); + } + } +} + + +static int +dissect_report_segment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ltp_tree, int frame_offset, ltp_tap_info_t *tap) { + ltp_session_data_t *session = tap->session; + gint64 rpt_sno; + gint64 chkp_sno; + guint64 upper_bound; + guint64 lower_bound; + guint64 rcpt_clm_cnt; + guint64 offset; + guint64 length; + guint64 clm_fst, clm_lst; + + int rpt_sno_size; + int chkp_sno_size; + int upper_bound_size; + int lower_bound_size; + int rcpt_clm_cnt_size; + int offset_size; + int length_size; + + int segment_offset = 0; + int gap_count = 0; + guint64 gap_total = 0; + + proto_item *ltp_rpt_item; + proto_item *ltp_rpt_clm_cnt; + proto_item *ltp_rpt_clm_item; + proto_item *item_rpt_sno, *item_chkp_sno; + + proto_tree *ltp_rpt_tree; + proto_tree *ltp_rpt_clm_tree; + + /* Create the subtree for report segment under the main LTP tree and all the report segment fields under it */ + ltp_rpt_tree = proto_tree_add_subtree(ltp_tree, tvb, frame_offset, -1, ett_rpt_segm, <p_rpt_item, "Report Segment"); + + /* Extract the report segment info */ + item_rpt_sno = add_sdnv64_to_tree(ltp_rpt_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_sno, &rpt_sno, &rpt_sno_size); + segment_offset += rpt_sno_size; + if (ltp_analyze_sequence && session) + { + proto_tree *tree_rpt_sno = proto_item_add_subtree(item_rpt_sno, ett_frame_ref); + ltp_ref_src(session->reports, rpt_sno, pinfo); + ltp_ref_use(session->rpt_acks, rpt_sno, pinfo, tree_rpt_sno, hf_ltp_rpt_sno_ack, -1); + ltp_ref_use(session->rpt_datas, rpt_sno, pinfo, tree_rpt_sno, hf_ltp_rpt_sno_data, -1); + } + + item_chkp_sno = add_sdnv64_to_tree(ltp_rpt_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_chkp, &chkp_sno, &chkp_sno_size); + segment_offset += chkp_sno_size; + if (ltp_analyze_sequence && session) + { + proto_tree *tree_chkp_sno = proto_item_add_subtree(item_chkp_sno, ett_frame_ref); + ltp_ref_src(session->chkp_acks, chkp_sno, pinfo); + ltp_ref_use(session->checkpoints, chkp_sno, pinfo, tree_chkp_sno, hf_ltp_rpt_chkp_ref, hf_ltp_rpt_chkp_time); + + if (chkp_sno == 0) + { + expert_add_info(pinfo, item_chkp_sno, &ei_ltp_report_async); + } + } + + add_sdnv64_to_tree(ltp_rpt_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_ub, &upper_bound, &upper_bound_size); + segment_offset += upper_bound_size; + + add_sdnv64_to_tree(ltp_rpt_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_lb, &lower_bound, &lower_bound_size); + segment_offset += lower_bound_size; + + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_len, tvb, 0, 0, upper_bound - lower_bound) + ); + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, + "range %" G_GINT64_MODIFIER "u-%" G_GINT64_MODIFIER "u", + lower_bound, upper_bound-1); + + gboolean newdata = TRUE; + if (ltp_analyze_sequence && session) + { + const guint64 data_fst = lower_bound; + const guint64 data_lst = upper_bound - 1; + + // All segments for a single report ID + wmem_itree_t *rpt = wmem_map_lookup(session->rpt_segs, &rpt_sno); + if (!rpt) + { + guint64 *key = wmem_new(wmem_file_scope(), guint64); + *key = rpt_sno; + rpt = wmem_itree_new(wmem_file_scope()); + wmem_map_insert(session->rpt_segs, key, rpt); + } + + if (data_fst <= data_lst) { + wmem_list_t *found = wmem_itree_find_intervals(rpt, wmem_packet_scope(), data_fst, data_lst); + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame = wmem_list_frame_data(it); + if (frame->frame_num == pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(ltp_rpt_tree, hf_ltp_rpt_retrans, NULL, 0, 0, frame->frame_num) + ); + newdata = false; + } + + if (newdata) + { + ltp_frame_info_t *val = ltp_frame_info_new(pinfo); + wmem_itree_insert(rpt, data_fst, data_lst, val); + } + } + } + tap->corr_orig = newdata; + + ltp_rpt_clm_cnt = add_sdnv64_to_tree(ltp_rpt_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_clm_cnt, &rcpt_clm_cnt, &rcpt_clm_cnt_size); + segment_offset += rcpt_clm_cnt_size; + /* Each reception claim is at least 2 bytes, so if the count is larger than the + * max number of claims we can possibly squeeze into the remaining tvbuff, then + * the packet is malformed. + */ + if (rcpt_clm_cnt > (guint64)tvb_captured_length_remaining(tvb, frame_offset + segment_offset) / 2) { + expert_add_info_format(pinfo, ltp_rpt_clm_cnt, &ei_ltp_mal_reception_claim, + "Reception claim count impossibly large: %" G_GINT64_MODIFIER "d > %d", rcpt_clm_cnt, + tvb_captured_length_remaining(tvb, frame_offset + segment_offset) / 2); + return 0; + } + + clm_lst = lower_bound - 1; + + /* There can be multiple reception claims in the same report segment */ + for(guint64 ix = 0; ix < rcpt_clm_cnt; ix++){ + /* Peek at the offset to see if there is a preceeding gap */ + tvb_get_varint(tvb, frame_offset + segment_offset, FT_VARINT_MAX_LEN, &offset, ENC_VARINT_SDNV); + clm_fst = lower_bound + offset; + ltp_check_reception_gap(ltp_rpt_tree, pinfo, session, clm_lst, clm_fst, &gap_count, &gap_total); + + ltp_rpt_clm_tree = proto_tree_add_subtree(ltp_rpt_tree, tvb, frame_offset + segment_offset, -1, ett_rpt_clm, <p_rpt_clm_item, "Reception claim"); + + add_sdnv64_to_tree(ltp_rpt_clm_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_clm_off, &offset, &offset_size); + segment_offset += offset_size; + + add_sdnv64_to_tree(ltp_rpt_clm_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_clm_len, &length, &length_size); + segment_offset += length_size; + + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(ltp_rpt_clm_tree, hf_ltp_rpt_clm_fst, tvb, 0, 0, clm_fst) + ); + clm_lst = clm_fst + length - 1; + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(ltp_rpt_clm_tree, hf_ltp_rpt_clm_lst, tvb, 0, 0, clm_lst) + ); + + proto_item_append_text(ltp_rpt_clm_item, + ": %" PRIu64 "-%" PRIu64 " (%" PRIu64 " bytes)", + clm_fst, clm_lst, length + ); + proto_item_set_end(ltp_rpt_clm_item, tvb, frame_offset + segment_offset); + + if (ltp_analyze_sequence && session && (clm_fst <= clm_lst)) + { + wmem_list_t *found = wmem_itree_find_intervals(session->data_segs, wmem_packet_scope(), clm_fst, clm_lst); + for (wmem_list_frame_t *it = wmem_list_head(found); it != NULL; + it = wmem_list_frame_next(it)) + { + const ltp_frame_info_t *frame = wmem_list_frame_data(it); + if (frame->frame_num > pinfo->num) + { + continue; + } + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint(ltp_rpt_clm_tree, hf_ltp_rpt_clm_ref, NULL, 0, 0, frame->frame_num) + ); + } + } + } + proto_item_set_end(ltp_rpt_item, tvb, frame_offset + segment_offset); + + ltp_check_reception_gap(ltp_rpt_tree, pinfo, session, clm_lst, upper_bound, &gap_count, &gap_total); + PROTO_ITEM_SET_GENERATED( + proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_gap_total, NULL, 0, 0, gap_total) + ); + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "gaps: %d, gap total: %"PRIu64, gap_count, gap_total); + + return segment_offset; +} + + +static int +dissect_report_ack_segment(proto_tree *ltp_tree, tvbuff_t *tvb, packet_info *pinfo, int frame_offset, ltp_tap_info_t *tap){ + ltp_session_data_t *session = tap->session; + gint64 rpt_sno; + int rpt_sno_size; + int segment_offset = 0; + + proto_item *ltp_rpt_ack_item, *item_rpt_sno; + proto_tree *ltp_rpt_ack_tree; + + /* Creating tree for the report ack segment */ + ltp_rpt_ack_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, -1, + ett_rpt_ack_segm, <p_rpt_ack_item, "Report Ack Segment"); + + /* Extracing receipt serial number info */ + item_rpt_sno = add_sdnv64_to_tree(ltp_rpt_ack_tree, tvb, pinfo, frame_offset + segment_offset, hf_ltp_rpt_ack_sno, &rpt_sno, &rpt_sno_size); + segment_offset += rpt_sno_size; + + proto_item_set_end(ltp_rpt_ack_item, tvb, frame_offset + segment_offset); + + if (ltp_analyze_sequence && session) + { + proto_tree *tree_rpt_sno = proto_item_add_subtree(item_rpt_sno, ett_frame_ref); + ltp_ref_src(session->rpt_acks, rpt_sno, pinfo); + ltp_ref_use(session->rpt_acks, rpt_sno, pinfo, tree_rpt_sno, hf_ltp_rpt_ack_dupe, -1); + ltp_ref_use(session->reports, rpt_sno, pinfo, tree_rpt_sno, hf_ltp_rpt_ack_ref, hf_ltp_rpt_ack_time); + } + + return segment_offset; +} + + +static int +dissect_cancel_segment(proto_tree * ltp_tree, tvbuff_t *tvb, int frame_offset, ltp_tap_info_t *tap _U_){ + guint8 reason_code; + + proto_tree *ltp_cancel_tree; + + /* The cancel segment has only one byte, which contains the reason code. */ + reason_code = tvb_get_guint8(tvb,frame_offset); + + /* Creating tree for the cancel segment */ + ltp_cancel_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, 1, ett_session_mgmt, NULL, "Cancel Segment"); + + proto_tree_add_uint_format_value(ltp_cancel_tree, hf_ltp_cancel_code, tvb, frame_offset, 1, reason_code, + "%x (%s)", reason_code, val_to_str_const(reason_code,ltp_cancel_codes,"Reserved")); + return 1; +} + + +static int +dissect_header_extn(proto_tree *ltp_tree, tvbuff_t *tvb, packet_info *pinfo, int frame_offset,int hdr_extn_cnt){ + gint64 length; + int length_size; + + int extn_offset = 0; + + proto_item *ltp_hdr_extn_item; + proto_tree *ltp_hdr_extn_tree; + + ltp_hdr_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, -1, ett_hdr_extn, <p_hdr_extn_item, "Header Extension"); + + for(int ix = 0; ix < hdr_extn_cnt; ix++){ + /* From RFC-5326, the total length of the Header Extension Tree will be length of the following: + a) Extension type length (1 byte) + b) The length of the 'length' field (as defined by the SDNV which handles dynamic size) + c) The length of the value field which is the decoded length */ + proto_tree_add_item(ltp_hdr_extn_tree, hf_ltp_hdr_extn_tag, tvb, frame_offset + extn_offset, 1, ENC_NA); + extn_offset += 1; + + add_sdnv64_to_tree(ltp_hdr_extn_tree, tvb, pinfo, frame_offset + extn_offset, hf_ltp_hdr_extn_len, &length, &length_size); + extn_offset += length_size; + + proto_tree_add_item(ltp_hdr_extn_tree, hf_ltp_hdr_extn_val, tvb, frame_offset + extn_offset, (int)length, ENC_NA); + extn_offset += (int)length; + } + + proto_item_set_end(ltp_hdr_extn_item, tvb, frame_offset + extn_offset); + return extn_offset; +} + +static int +dissect_trailer_extn(proto_tree *ltp_tree, tvbuff_t *tvb, packet_info *pinfo, int frame_offset,int trl_extn_cnt){ + gint64 length; + int length_size; + + int extn_offset = 0; + + proto_item *ltp_trl_extn_item; + proto_tree *ltp_trl_extn_tree; + + ltp_trl_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, -1, ett_trl_extn, <p_trl_extn_item, "Trailer Extension"); + + for(int ix = 0; ix < trl_extn_cnt; ix++){ + proto_tree_add_item(ltp_trl_extn_tree, hf_ltp_trl_extn_tag, tvb, frame_offset + extn_offset, 1, ENC_NA); + frame_offset += 1; + + add_sdnv64_to_tree(ltp_trl_extn_tree, tvb, pinfo, frame_offset + extn_offset, hf_ltp_hdr_extn_len, &length, &length_size); + frame_offset += length_size; + + proto_tree_add_item(ltp_trl_extn_tree, hf_ltp_trl_extn_val, tvb, frame_offset + extn_offset, (int)length, ENC_NA); + frame_offset += (int)length; + } + + proto_item_set_end(ltp_trl_extn_item, tvb, frame_offset + extn_offset); + return extn_offset; +} + + +static int +dissect_ltp_segment(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti = NULL; + proto_tree *ltp_tree = NULL; + int frame_offset = offset; + int segment_offset = 0; + int data_len = 0; + + gint ltp_type; + guint64 bitsval; + gint hdr_extn_cnt; + gint trl_extn_cnt; + + ltp_session_id_t sess_id; + int engine_id_size; + int session_num_size; + const char *sess_name; + ltp_session_data_t *session = NULL; + + proto_tree *ltp_header_tree = NULL; + proto_item *ltp_header_item = NULL; + proto_tree *ltp_session_tree = NULL; + proto_item *ltp_session_item = NULL; + + /* Check that there's enough data */ + if(tvb_captured_length(tvb) < LTP_MIN_DATA_BUFFER){ + return 0; + } + + /* Extract all the header info from the packet */ + ti = proto_tree_add_item(tree, proto_ltp, tvb, offset, -1, ENC_NA); + ltp_tree = proto_item_add_subtree(ti, ett_ltp); + + ltp_tap_info_t *tap = wmem_new0(wmem_packet_scope(), ltp_tap_info_t); + + /* Adding Header Subtree */ + ltp_header_tree = proto_tree_add_subtree(ltp_tree, tvb, frame_offset, 0, ett_ltp_hdr, NULL, "LTP Header"); + ltp_header_item = proto_tree_get_parent(ltp_header_tree); + + proto_tree_add_bits_ret_val(ltp_header_tree, hf_ltp_version, tvb, frame_offset, 4, &bitsval, ENC_BIG_ENDIAN); + proto_tree_add_bits_ret_val(ltp_header_tree, hf_ltp_type, tvb, frame_offset+4, 4, &bitsval, ENC_BIG_ENDIAN); + ltp_type = (int)bitsval; + tap->seg_type = ltp_type; + frame_offset++; + + /* Adding the session id subtree */ + ltp_session_tree = proto_tree_add_subtree(ltp_header_tree, tvb, frame_offset, 0, ett_hdr_session, NULL, "Session ID"); + ltp_session_item = proto_tree_get_parent(ltp_session_tree); + + add_sdnv64_to_tree(ltp_session_tree, tvb, pinfo, frame_offset, hf_ltp_session_orig, &sess_id.orig_eng_id, &engine_id_size); + frame_offset += engine_id_size; + + add_sdnv64_to_tree(ltp_session_tree, tvb, pinfo, frame_offset, hf_ltp_session_no, &sess_id.sess_num, &session_num_size); + frame_offset += session_num_size; + + proto_item_set_end(ltp_session_item, tvb, frame_offset); + tap->sess_id = sess_id; + + sess_name = wmem_strdup_printf( + wmem_file_scope(), + "%" PRId64 "/%" PRIu64, + sess_id.orig_eng_id, sess_id.sess_num + ); + tap->sess_name = sess_name; + PROTO_ITEM_SET_GENERATED( + proto_tree_add_string(ltp_session_tree, hf_ltp_session_name, tvb, + frame_offset - engine_id_size - session_num_size, + engine_id_size + session_num_size, sess_name) + ); + proto_item_append_text(ltp_session_item,": %s", sess_name); + proto_item_append_text(ti,", Session: %s", sess_name); + p_add_proto_data(pinfo->pool, pinfo, proto_ltp, pinfo->curr_layer_num, (void *)sess_name); + + if (ltp_analyze_sequence) + { + // LTP sessions exist independently of network addresses and transport ports + conversation_element_t *conv_key = wmem_alloc_array(pinfo->pool, conversation_element_t, 3); + conv_key[0].type = CE_UINT64; + conv_key[0].uint64_val = sess_id.orig_eng_id; + conv_key[1].type = CE_UINT64; + conv_key[1].uint64_val = sess_id.sess_num; + conv_key[2].type = CE_CONVERSATION_TYPE; + conv_key[2].conversation_type_val = CONVERSATION_LTP; + + pinfo->use_conv_addr_port_endpoints = FALSE; + pinfo->conv_addr_port_endpoints = NULL; + pinfo->conv_elements = conv_key; + conversation_t *convo = find_or_create_conversation(pinfo); + + session = conversation_get_proto_data(convo, proto_ltp); + if (!session) + { + session = wmem_new0(wmem_file_scope(), ltp_session_data_t); + session->data_segs = wmem_itree_new(wmem_file_scope()); + session->rpt_segs = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + session->checkpoints = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + session->chkp_acks = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + session->reports = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + session->rpt_acks = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + session->rpt_datas = wmem_map_new(wmem_file_scope(), g_int64_hash, g_int64_equal); + + conversation_add_proto_data(convo, proto_ltp, session); + } + } + tap->session = session; + + /* Adding Extension count to the header tree */ + proto_tree_add_bits_ret_val(ltp_header_tree, hf_ltp_hdr_extn_cnt, tvb, 8*frame_offset, 4, &bitsval, ENC_BIG_ENDIAN); + hdr_extn_cnt = (int)bitsval; + proto_tree_add_bits_ret_val(ltp_header_tree, hf_ltp_trl_extn_cnt, tvb, 8*frame_offset+4, 4, &bitsval, ENC_BIG_ENDIAN); + trl_extn_cnt = (int)bitsval; + frame_offset++; + + proto_item_set_end(ltp_header_item, tvb, frame_offset); + + col_add_fstr(pinfo->cinfo, COL_INFO, "Session %s, %s", sess_name, val_to_str_const(ltp_type,ltp_type_col_info,"Protocol Error")); + + /* Check if there are any header extensions */ + if(hdr_extn_cnt > 0) + { + int hdr_extn_offset = dissect_header_extn(ltp_tree, tvb, pinfo, frame_offset,hdr_extn_cnt); + frame_offset += hdr_extn_offset; + } + + /* Call sub routines to handle the segment content*/ + if((ltp_type >= 0) && (ltp_type < 8)){ + segment_offset = dissect_data_segment(ltp_tree,tvb,pinfo,frame_offset,ltp_type, &data_len, tap); + if(segment_offset == 0){ + col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error"); + return 0; + } + } + else if(ltp_type == 8){ + segment_offset = dissect_report_segment(tvb, pinfo, ltp_tree,frame_offset, tap); + if(segment_offset == 0){ + col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error"); + return 0; + } + } + else if(ltp_type == 9){ + segment_offset = dissect_report_ack_segment(ltp_tree,tvb, pinfo, frame_offset, tap); + if(segment_offset == 0){ + col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error"); + return 0; + } + } + else if(ltp_type == 12 || ltp_type == 14){ + segment_offset = dissect_cancel_segment(ltp_tree,tvb,frame_offset, tap); + if(segment_offset == 0){ + col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error"); + return 0; + } + } + else if(ltp_type == 13 || ltp_type == 15){ + proto_tree_add_string(ltp_tree, hf_ltp_cancel_ack, tvb, 0, 0, "(No Data)"); + } + frame_offset += segment_offset; + + /* Check to see if there are any trailer extensions */ + const int trl_start = frame_offset; + if(trl_extn_cnt > 0) + { + int trl_length = dissect_trailer_extn(ltp_tree, tvb, pinfo, frame_offset,trl_extn_cnt); + frame_offset += trl_length; + } + + const int frame_len = frame_offset - offset; + proto_item_set_len(ti, trl_start - data_len); + proto_tree_set_appendix(ltp_tree, tvb, trl_start, frame_offset - trl_start); + tap->seg_size = frame_len; + tap_queue_packet(ltp_tap, pinfo, tap); + + /* Return the amount of data this dissector was able to dissect */ + return frame_len; +} + +static int +dissect_ltp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + const int packet_len = tvb_reported_length(tvb); + int offset = 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "LTP"); + + while (offset < packet_len) + { + const int sublen = dissect_ltp_segment(tvb, offset, pinfo, tree); + if (sublen == 0) + { + break; + } + offset += sublen; + } + return offset; +} + +/// Conversation address for the session receiver +static const char *const ltp_conv_receiver = "receiver"; +/// Assigned during proto_register_ltp() +static address ltp_addr_receiver = ADDRESS_INIT_NONE; + +static const char * +ltp_conv_get_filter_type(conv_item_t *conv _U_, conv_filter_type_e filter) +{ + switch (conv->dst_address.type) + { + case AT_STRINGZ: + switch (filter) + { + case CONV_FT_SRC_ADDRESS: + case CONV_FT_DST_ADDRESS: + case CONV_FT_ANY_ADDRESS: + return "ltp.session.name"; + default: + break; + } + break; + default: + break; + } + + return CONV_FILTER_INVALID; +} + +static ct_dissector_info_t ltp_ct_dissector_info = { + <p_conv_get_filter_type +}; + +static tap_packet_status +ltp_conv_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data, tap_flags_t flags _U_) +{ + conv_hash_t *hash = (conv_hash_t*) tapdata; + ltp_tap_info_t *ltp = (ltp_tap_info_t *)data; + address *src = wmem_new0(pinfo->pool, address); + address *dst = wmem_new0(pinfo->pool, address); + + address *diraddr, *othaddr; + switch (ltp->seg_type) { + case 0x8: + case 0xd: + case 0xe: + // report, cancel ack to sender, cancel from receiver + diraddr = dst; + othaddr = src; + break; + default: + diraddr = src; + othaddr = dst; + break; + } + set_address(diraddr, AT_STRINGZ, (int) strlen(ltp->sess_name) + 1, ltp->sess_name); + copy_address_shallow(othaddr, <p_addr_receiver); + + add_conversation_table_data(hash, src, dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, + <p_ct_dissector_info, ENDPOINT_NONE); + + return TAP_PACKET_REDRAW; +} + +static const char * +ltp_endp_get_filter_type(endpoint_item_t *host, conv_filter_type_e filter) +{ + switch (filter) + { + case CONV_FT_SRC_ADDRESS: + case CONV_FT_DST_ADDRESS: + case CONV_FT_ANY_ADDRESS: + if (host->myaddress.type == AT_NUMERIC) + { + return "ltp.session.orig"; + } + break; + default: + break; + } + + return CONV_FILTER_INVALID; +} + +static et_dissector_info_t ltp_endp_dissector_info = { + <p_endp_get_filter_type +}; + +static tap_packet_status +ltp_endp_packet(void *tapdata _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *data _U_, tap_flags_t flags _U_) +{ + conv_hash_t *hash = (conv_hash_t*) tapdata; + ltp_tap_info_t *ltp = (ltp_tap_info_t *)data; + address *diraddr = wmem_new0(pinfo->pool, address); + + set_address(diraddr, AT_NUMERIC, (int) sizeof(ltp->sess_id.orig_eng_id), &(ltp->sess_id.orig_eng_id)); + gboolean sender; + switch (ltp->seg_type) { + case 0x8: + case 0xd: + case 0xe: + // report, cancel ack to sender, cancel from receiver + sender = FALSE; + break; + default: + sender = TRUE; + break; + } + + add_endpoint_table_data(hash, diraddr, 0, sender, 1, pinfo->fd->pkt_len, + <p_endp_dissector_info, ENDPOINT_NONE); + + return TAP_PACKET_REDRAW; +} + +static gboolean +ltp_filter_valid(packet_info *pinfo, void *user_data _U_) +{ + return proto_is_frame_protocol(pinfo->layers, "ltp"); +} + +static gchar* +ltp_build_filter(packet_info *pinfo, void *user_data _U_) +{ + gchar *result = NULL; + int layer_num = 1; + for (wmem_list_frame_t *protos = wmem_list_head(pinfo->layers); + protos != NULL; protos = wmem_list_frame_next(protos), ++layer_num) + { + const int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + if (proto_id != proto_ltp) + { + continue; + } + const char *sess_name = p_get_proto_data(pinfo->pool, pinfo, proto_ltp, layer_num); + if (!sess_name) + { + continue; + } + + gchar *filter = g_strdup_printf( + "ltp.session.name == \"%s\"", + sess_name + ); + + if (result) + { + gchar *oldresult = result; + result = g_strjoin(" || ", oldresult, filter, NULL); + g_free(oldresult); + g_free(filter); + } + else + { + result = filter; + } + } + + return result; +} + +static const gchar* st_str_segs = "Segment Size (by Type)"; +static const gchar* st_str_red = "Red Data"; +static const gchar* st_str_corr_orig = "Original"; +static const gchar* st_str_corr_ret = "Retransmission seen"; +static const gchar* st_str_green = "Green Data"; +static const gchar* st_str_rpt = "Report"; +static const gchar* st_str_canc_src = "Cancel by Sender"; +static const gchar* st_str_canc_dst = "Cancel by Receiver"; +static const gchar* st_str_ack = "Report/Cancel Ack"; +static const gchar* st_str_engs = "Segment Addr (by Engine ID)"; +static const gchar* st_str_blks = "Block Size (by Engine ID)"; +static int st_node_segs = -1; +static int st_node_red = -1; +static int st_node_green = -1; +static int st_node_rpt = -1; +static int st_node_engs = -1; +static int st_node_blks = -1; + +static void +ltp_stats_tree_init(stats_tree *st) +{ + st_node_segs = stats_tree_create_node(st, st_str_segs, 0, STAT_DT_INT, FALSE); + st_node_red = stats_tree_create_node(st, st_str_red, st_node_segs, STAT_DT_INT, TRUE); + stats_tree_create_node(st, st_str_corr_orig, st_node_red, STAT_DT_INT, FALSE); + stats_tree_create_node(st, st_str_corr_ret, st_node_red, STAT_DT_INT, FALSE); + st_node_green = stats_tree_create_node(st, st_str_green, st_node_segs, STAT_DT_INT, FALSE); + st_node_rpt = stats_tree_create_node(st, st_str_rpt, st_node_segs, STAT_DT_INT, TRUE); + stats_tree_create_node(st, st_str_corr_orig, st_node_rpt, STAT_DT_INT, FALSE); + stats_tree_create_node(st, st_str_corr_ret, st_node_rpt, STAT_DT_INT, FALSE); + stats_tree_create_node(st, st_str_canc_src, st_node_segs, STAT_DT_INT, FALSE); + stats_tree_create_node(st, st_str_canc_dst, st_node_segs, STAT_DT_INT, FALSE); + stats_tree_create_node(st, st_str_ack, st_node_segs, STAT_DT_INT, FALSE); + + st_node_engs = stats_tree_create_pivot(st, st_str_engs, 0); + st_node_blks = stats_tree_create_pivot(st, st_str_blks, 0); +} + +static tap_packet_status +ltp_stats_tree_packet(stats_tree *st, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *p, tap_flags_t flags _U_) +{ + const ltp_tap_info_t *tap = (const ltp_tap_info_t *)p; + + tick_stat_node(st, st_str_segs, 0, FALSE); + + switch (tap->seg_type) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + avg_stat_node_add_value_int(st, st_str_red, 0, FALSE, tap->seg_size); + avg_stat_node_add_value_int(st, tap->corr_orig ? st_str_corr_orig : st_str_corr_ret, st_node_red, TRUE, tap->seg_size); + break; + case 0x4: + case 0x7: + avg_stat_node_add_value_int(st, st_str_green, 0, FALSE, tap->seg_size); + break; + case 0x8: + avg_stat_node_add_value_int(st, st_str_rpt, 0, FALSE, tap->seg_size); + avg_stat_node_add_value_int(st, tap->corr_orig ? st_str_corr_orig : st_str_corr_ret, st_node_rpt, TRUE, tap->seg_size); + break; + case 0xc: + avg_stat_node_add_value_int(st, st_str_canc_src, 0, FALSE, tap->seg_size); + break; + case 0xe: + avg_stat_node_add_value_int(st, st_str_canc_dst, 0, FALSE, tap->seg_size); + break; + case 0x9: + case 0xd: + case 0xf: + avg_stat_node_add_value_int(st, st_str_ack, 0, FALSE, tap->seg_size); + break; + } + + tick_stat_node(st, st_str_engs, 0, TRUE); + const char *eng_id = wmem_strdup_printf(pinfo->pool, "%" PRIu64, tap->sess_id.orig_eng_id); + int st_eng_id = tick_stat_node(st, eng_id, st_node_engs, TRUE); + if (tap->block_size > 0) + { + avg_stat_node_add_value_int(st, st_str_blks, 0, TRUE, tap->block_size); + avg_stat_node_add_value_int(st, eng_id, st_node_blks, FALSE, tap->block_size); + } + + const address *eng_addr = NULL; + switch (tap->seg_type) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x7: + case 0x9: + case 0xc: // cancel from sender + case 0xf: + eng_addr = &(pinfo->src); + break; + case 0x8: // report + case 0xd: + case 0xe: // cancel from receiver + eng_addr = &(pinfo->dst); + break; + } + const gchar *eng_addr_str = eng_addr ? address_to_display(pinfo->pool, eng_addr) : NULL; + if (eng_addr_str) + { + tick_stat_node(st, eng_addr_str, st_eng_id, FALSE); + } + + return TAP_PACKET_REDRAW; +} + +/* Register the protocol with Wireshark */ +void +proto_register_ltp(void) +{ + static hf_register_info hf[] = { + {&hf_ltp_version, + {"LTP Version","ltp.version", + FT_UINT8,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_type, + {"LTP Type","ltp.type", + FT_UINT8,BASE_HEX,VALS(ltp_type_codes), 0x0, NULL, HFILL} + }, + {&hf_ltp_session_orig, + {"Session originator","ltp.session.orig", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_session_no, + {"Session number","ltp.session.number", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_session_name, + {"Session Name","ltp.session.name", + FT_STRING,BASE_NONE,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_hdr_extn_cnt, + {"Header Extension Count","ltp.hdr.extn.cnt", + FT_UINT8,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_trl_extn_cnt, + {"Trailer Extension Count","ltp.trl.extn.cnt", + FT_UINT8,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_clid, + {"Client service ID","ltp.data.client.id", + FT_UINT64,BASE_DEC | BASE_VAL64_STRING, VALS64(client_service_id_info), 0x0, NULL, HFILL} + }, + {&hf_ltp_data_offset, + {"Offset","ltp.data.offset", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_length, + {"Length","ltp.data.length", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_chkp, + {"Checkpoint serial number","ltp.data.chkp", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_chkp_rpt, + {"Checkpoint report segment in frame","ltp.data.chkp.rpt", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_rpt, + {"Report serial number","ltp.data.rpt", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_rpt_ref, + {"Response to report segment in frame","ltp.data.rpt.ref", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_rpt_time, + {"Time since report","ltp.data.rpt.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_clidata, + {"Client service data","ltp.data.data", + FT_BYTES,BASE_NONE,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_retrans, + {"Retransmission of data in frame","ltp.data.retrans", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_PREV), 0x0, NULL, HFILL} + }, + {&hf_ltp_data_clm_rpt, + {"Claimed in report segment in frame","ltp.data.clm_rpt", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_NONE), 0x0, NULL, HFILL} + }, + {&hf_ltp_block_red_size, + {"Red part size", "ltp.block.red_size", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_block_green_size, + {"Green part size", "ltp.block.green_size", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_block_bundle_size, + {"Bundle size", "ltp.block.bundle_size", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, + "The dissected bundle is below in the protocol tree", HFILL} + }, + {&hf_ltp_block_bundle_cnt, + {"Bundles within the block", "ltp.block.bundle_cnt", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_sno, + {"Report serial number","ltp.rpt.sno", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_sno_ack, + {"Report ack segment in frame","ltp.rpt.sno.ack", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_sno_data, + {"Responding data segment in frame","ltp.rpt.sno.data", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_chkp, + {"Checkpoint serial number","ltp.rpt.chkp", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_chkp_ref, + {"Checkpoint data segment in frame","ltp.rpt.chkp.ref", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_chkp_time, + {"Time since checkpoint","ltp.rpt.chkp.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_ub, + {"Upper bound","ltp.rpt.ub", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_lb, + {"Lower bound","ltp.rpt.lb", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_len, + {"Report bound length","ltp.rpt.bound_len", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_retrans, + {"Retransmission of report in frame","ltp.rpt.retrans", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RETRANS_PREV), 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_cnt, + {"Reception claim count","ltp.rpt.clm.cnt", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_off, + {"Offset","ltp.rpt.clm.off", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_len, + {"Length","ltp.rpt.clm.len", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_fst, + {"First block index","ltp.rpt.clm.first", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_lst, + {"Last block index","ltp.rpt.clm.last", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_clm_ref, + {"Data segment in frame","ltp.rpt.clm.ref", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_NONE), 0x0, + "Which previous data segment is this an ACK for", HFILL} + }, + {&hf_ltp_rpt_gap, + {"Reception gap","ltp.rpt.gap", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_gap_ref, + {"Data segment in frame","ltp.rpt.gap.ref", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_NONE), 0x0, + "Which previous data segment is this an NACK for", HFILL} + }, + {&hf_ltp_rpt_gap_total, + {"Total gap length","ltp.rpt.gap_total", + FT_UINT64,BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_ack_sno, + {"Report serial number","ltp.rpt.ack.sno", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_ack_dupe, + {"Same ack report number in frame","ltp.rpt.ack.sno.dupe", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_NONE), 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_ack_ref, + {"Report segment in frame","ltp.rpt.ack.sno.ref", + FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL} + }, + {&hf_ltp_rpt_ack_time, + {"Time since report","ltp.rpt.ack.sno.time", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_cancel_code, + {"Cancel code","ltp.cancel.code", + FT_UINT8,BASE_HEX,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_hdr_extn_tag, + {"Extension tag","ltp.hdr.extn.tag", + FT_UINT8,BASE_HEX,VALS(extn_tag_codes), 0x0, NULL, HFILL} + }, + {&hf_ltp_hdr_extn_len, + {"Length","ltp.hdr.extn.len", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_hdr_extn_val, + {"Value","ltp.hdr.extn.val", + FT_BYTES,BASE_NONE,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_trl_extn_tag, + {"Extension tag","ltp.trl.extn.tag", + FT_UINT8,BASE_HEX,VALS(extn_tag_codes), 0x0, NULL, HFILL} + }, + {&hf_ltp_trl_extn_len, + {"Length","ltp.trl.extn.len", + FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_trl_extn_val, + {"Value","ltp.trl.extn.val", + FT_BYTES,BASE_NONE,NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragments, + {"LTP Fragments", "ltp.fragments", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment, + {"LTP Fragment", "ltp.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_overlap, + {"LTP fragment overlap", "ltp.fragment.overlap", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_overlap_conflicts, + {"LTP fragment overlapping with conflicting data", + "ltp.fragment.overlap.conflicts", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_multiple_tails, + {"LTP has multiple tails", "ltp.fragment.multiple_tails", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_too_long_fragment, + {"LTP fragment too long", "ltp.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_error, + {"LTP defragmentation error", "ltp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_fragment_count, + {"LTP fragment count", "ltp.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_reassembled_in, + {"LTP reassembled in", "ltp.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_reassembled_length, + {"LTP reassembled length", "ltp.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_data_sda_clid, + {"Client service ID", "ltp.data.sda.client.id", + FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_partial_packet, + {"<partial packet>", "ltp.partial_packet", + FT_STRINGZPAD, BASE_NONE, NULL, 0x0, NULL, HFILL} + }, + {&hf_ltp_cancel_ack, + {"<Cancel Ack>", "ltp.cancel_ack", + FT_STRINGZPAD, BASE_NONE, NULL, 0x0, NULL, HFILL} + } + }; + +/* Setup protocol subtree array */ + static gint *ett[] = { + &ett_ltp, + &ett_ltp_hdr, + &ett_hdr_session, + &ett_hdr_extn, + &ett_frame_ref, + &ett_data_segm, + &ett_block, + &ett_rpt_segm, + &ett_rpt_clm, + &ett_rpt_gap, + &ett_rpt_ack_segm, + &ett_session_mgmt, + &ett_trl_extn, + &ett_ltp_fragment, + &ett_ltp_fragments + }; + + static ei_register_info ei[] = { + { &ei_ltp_mal_reception_claim, { "ltp.mal_reception_claim", PI_MALFORMED, PI_ERROR, "Reception claim count impossibly large", EXPFILL }}, + { &ei_ltp_sdnv_length, { "ltp.sdnv_length_invalid", PI_PROTOCOL, PI_ERROR, "SDNV length error", EXPFILL }}, + { &ei_ltp_sno_larger_than_ccsds, { "ltp.serial_number_too_large", PI_PROTOCOL, PI_WARN, "Serial number larger than CCSDS specification", EXPFILL }}, + { &ei_ltp_report_async, { "ltp.report_async", PI_SEQUENCE, PI_CHAT, "Report segment not sent in response to a data checkpoint", EXPFILL }} + }; + + expert_module_t* expert_ltp; + + /* Register the protocol name and description */ + proto_ltp = proto_register_protocol("Licklider Transmission Protocol", "LTP", "ltp"); + + module_t *module_ltp = prefs_register_protocol(proto_ltp, NULL); + prefs_register_bool_preference( + module_ltp, + "analyze_sequence", + "Analyze segment sequences", + "Whether the dissector should analyze the sequencing and " + "cross-references of the segments within each session.", + <p_analyze_sequence + ); + prefs_register_bool_preference( + module_ltp, + "reassemble_block", + "Reassemble block segments", + "Whether the dissector should combine block segments " + "together into a full block.", + <p_reassemble_block + ); + + proto_register_field_array(proto_ltp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_ltp = expert_register_protocol(proto_ltp); + expert_register_field_array(expert_ltp, ei, array_length(ei)); + + ltp_handle = register_dissector("ltp", dissect_ltp, proto_ltp); + + set_address(<p_addr_receiver, AT_STRINGZ, (int) strlen(ltp_conv_receiver) + 1, ltp_conv_receiver); + register_conversation_table(proto_ltp, TRUE, ltp_conv_packet, ltp_endp_packet); + register_conversation_filter("ltp", "LTP", ltp_filter_valid, ltp_build_filter, NULL); + ltp_tap = register_tap("ltp"); + + static const reassembly_table_functions ltp_session_reassembly_table_functions = { + ltp_session_id_hash, + ltp_session_id_equal, + ltp_session_new_key, + ltp_session_new_key, + ltp_session_free_key, + ltp_session_free_key + }; + reassembly_table_register(<p_reassembly_table, + <p_session_reassembly_table_functions); +} + +void +proto_reg_handoff_ltp(void) +{ + bundle_handle = find_dissector_add_dependency("bundle", proto_ltp); + + dissector_add_uint_with_preference("udp.port", LTP_PORT, ltp_handle); + dissector_add_uint_with_preference("dccp.port", LTP_PORT, ltp_handle); + + stats_tree_register("ltp", "ltp", "LTP", ST_SORT_COL_COUNT, ltp_stats_tree_packet, ltp_stats_tree_init, NULL); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ |