aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-ltp.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-ltp.c')
-rw-r--r--epan/dissectors/packet-ltp.c1590
1 files changed, 1242 insertions, 348 deletions
diff --git a/epan/dissectors/packet-ltp.c b/epan/dissectors/packet-ltp.c
index 2cb32a9b9b..df798b6028 100644
--- a/epan/dissectors/packet-ltp.c
+++ b/epan/dissectors/packet-ltp.c
@@ -34,83 +34,307 @@
#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 "packet-bpv6.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
-#define LTP_MAX_HDR_EXTN 16
-#define LTP_MAX_TRL_EXTN 16
+
+/// 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 proto_ltp;
+
+static int ltp_tap;
+
+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_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;
+static int hf_ltp_version;
+static int hf_ltp_type;
+static int hf_ltp_session_name;
+static int hf_ltp_session_orig;
+static int hf_ltp_session_no;
+static int hf_ltp_hdr_extn_cnt;
+static int hf_ltp_trl_extn_cnt;
/* 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_rpt = -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_clid;
+static int hf_ltp_data_offset;
+static int hf_ltp_data_length;
+static int hf_ltp_data_chkp;
+static int hf_ltp_data_chkp_rpt;
+static int hf_ltp_data_rpt;
+static int hf_ltp_data_rpt_ref;
+static int hf_ltp_data_rpt_time;
+static int hf_ltp_data_sda_clid;
+static int hf_ltp_partial_packet;
+static int hf_ltp_data_clidata;
+static int hf_ltp_data_retrans;
+static int hf_ltp_data_clm_rpt;
+static int hf_ltp_block_red_size;
+static int hf_ltp_block_green_size;
+static int hf_ltp_block_bundle_size;
+static int hf_ltp_block_bundle_cnt;
/* LTP Report Segment variable */
-static int hf_ltp_rpt_sno = -1;
-static int hf_ltp_rpt_chkp = -1;
-static int hf_ltp_rpt_ub = -1;
-static int hf_ltp_rpt_lb = -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_sno;
+static int hf_ltp_rpt_sno_ack;
+static int hf_ltp_rpt_sno_data;
+static int hf_ltp_rpt_chkp;
+static int hf_ltp_rpt_chkp_ref;
+static int hf_ltp_rpt_chkp_time;
+static int hf_ltp_rpt_ub;
+static int hf_ltp_rpt_lb;
+static int hf_ltp_rpt_len;
+static int hf_ltp_rpt_retrans;
+static int hf_ltp_rpt_clm_cnt;
+static int hf_ltp_rpt_clm_off;
+static int hf_ltp_rpt_clm_len;
+static int hf_ltp_rpt_clm_fst;
+static int hf_ltp_rpt_clm_lst;
+static int hf_ltp_rpt_clm_ref;
+static int hf_ltp_rpt_gap;
+static int hf_ltp_rpt_gap_ref;
+static int hf_ltp_rpt_gap_total;
/* LTP Report Ack Segment Variable */
-static int hf_ltp_rpt_ack_sno = -1;
-static int hf_ltp_cancel_ack = -1;
+static int hf_ltp_rpt_ack_sno;
+static int hf_ltp_rpt_ack_dupe;
+static int hf_ltp_rpt_ack_ref;
+static int hf_ltp_rpt_ack_time;
+static int hf_ltp_cancel_ack;
/* LTP Session Management Segment Variable */
-static int hf_ltp_cancel_code = -1;
+static int hf_ltp_cancel_code;
/* 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;
+static int hf_ltp_hdr_extn_tag;
+static int hf_ltp_hdr_extn_len;
+static int hf_ltp_hdr_extn_val;
/* 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;
+static int hf_ltp_trl_extn_tag;
+static int hf_ltp_trl_extn_len;
+static int hf_ltp_trl_extn_val;
/*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_neg_reception_claim_count = EI_INIT;
-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 int hf_ltp_fragments;
+static int hf_ltp_fragment;
+static int hf_ltp_fragment_overlap;
+static int hf_ltp_fragment_overlap_conflicts;
+static int hf_ltp_fragment_multiple_tails;
+static int hf_ltp_fragment_too_long_fragment;
+static int hf_ltp_fragment_error;
+static int hf_ltp_fragment_count;
+static int hf_ltp_reassembled_in;
+static int hf_ltp_reassembled_length;
+
+static expert_field ei_ltp_mal_reception_claim;
+static expert_field ei_ltp_sdnv_length;
+static expert_field ei_ltp_sno_larger_than_ccsds;
+static expert_field ei_ltp_report_async;
static dissector_handle_t bundle_handle;
@@ -170,7 +394,7 @@ static const value_string extn_tag_codes[] = {
{0, NULL}
};
-static const value_string client_service_id_info[] = {
+static const val64_string client_service_id_info[] = {
{0x01, "Bundle Protocol"},
{0x02, "CCSDS LTP Service Data Aggregation"},
{0, NULL}
@@ -179,19 +403,21 @@ static const value_string client_service_id_info[] = {
#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_data_segm = -1;
-static gint ett_data_data_segm = -1;
-static gint ett_rpt_segm = -1;
-static gint ett_rpt_clm = -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 gint ett_ltp;
+static gint ett_ltp_hdr;
+static gint ett_hdr_session;
+static gint ett_hdr_extn;
+static gint ett_frame_ref;
+static gint ett_data_segm;
+static gint ett_block;
+static gint ett_rpt_segm;
+static gint ett_rpt_clm;
+static gint ett_rpt_gap;
+static gint ett_rpt_ack_segm;
+static gint ett_session_mgmt;
+static gint ett_trl_extn;
+static gint ett_ltp_fragment;
+static gint ett_ltp_fragments;
static const fragment_items ltp_frag_items = {
/*Fragment subtrees*/
@@ -216,29 +442,62 @@ static const fragment_items ltp_frag_items = {
"LTP fragments"
};
-static int
-add_sdnv64_to_tree(proto_tree *tree, tvbuff_t *tvb, packet_info* pinfo, int offset, int hf_sdnv, guint64 *value, proto_item** item_ret)
+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)
{
- int sdnv_status;
- int sdnv_length;
- guint64 sdnv_value;
- proto_item* ti;
+ proto_item *ti;
+ ti = proto_tree_add_item_ret_varint(tree, hf_sdnv, tvb, offset, -1, ENC_VARINT_SDNV, retval, lenretval);
- sdnv_status = evaluate_sdnv64(tvb, offset, &sdnv_length, &sdnv_value);
- ti = proto_tree_add_uint64(tree, hf_sdnv, tvb, offset, sdnv_length, sdnv_value);
+ if (*lenretval <= 0) {
+ expert_add_info(pinfo, ti, &ei_ltp_sdnv_length);
+ }
+ return ti;
+}
- *value = sdnv_value;
- if (NULL != *item_ret) *item_ret = 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;
+ }
- if (!sdnv_status) {
- expert_add_info(pinfo, ti, &ei_ltp_sdnv_length);
+ 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)
+ );
}
- return sdnv_length;
+
}
static int
-dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int frame_offset,int ltp_type, guint64 session_num)
+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;
@@ -249,35 +508,25 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
unsigned segment_size = 0;
int sdnv_length;
- int sdnv_status;
proto_tree *ltp_data_tree;
proto_item *ti;
fragment_head *frag_msg = NULL;
- gboolean more_frags = TRUE;
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 */
- sdnv_status = evaluate_sdnv64(tvb, frame_offset, &sdnv_length, &client_id);
- ti = proto_tree_add_uint64_format_value(ltp_data_tree, hf_ltp_data_clid, tvb, frame_offset, sdnv_length, client_id,
- "%" G_GINT64_MODIFIER "u (%s)", client_id,
- val_to_str_const((const guint32) client_id, client_service_id_info, "Invalid"));
- if (!sdnv_status) {
- expert_add_info(pinfo, ti, &ei_ltp_sdnv_length);
- return 0;
- }
+ 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 */
- if ((sdnv_length = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_offset, &data_offset, &ti)) > 0) {
+ 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 {
@@ -285,7 +534,8 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
}
/* data segment length */
- if ((sdnv_length = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_length, &data_length, &ti)) > 0) {
+ 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;
@@ -294,12 +544,52 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
} 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;
- more_frags = FALSE;
if (ltp_type != 0 && ltp_type < 4)
{
/* checkpoint serial number - 32 bits per CCSDS */
- if ((sdnv_length = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_chkp, &chkp_sno, &ti)) > 0) {
+ 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;
@@ -310,9 +600,16 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
} 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 */
- if ((sdnv_length = add_sdnv64_to_tree(ltp_data_tree, tvb, pinfo, frame_offset, hf_ltp_data_rpt, &rpt_sno, &ti)) > 0) {
+ 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;
@@ -323,36 +620,75 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
} else {
return 0;
}
-
- } else if (ltp_type != 7) {
- more_frags = TRUE;
-
+ 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);
}
- frag_msg = fragment_add_check(&ltp_reassembly_table,
- tvb, frame_offset, pinfo, (guint32)session_num, NULL,
- (guint32)data_offset, (guint32)data_length, more_frags);
+ 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(
+ &ltp_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(!more_frags && data_offset == 0)
+ if(is_eob && data_offset == 0)
{
- new_tvb = tvb_new_subset_remaining(tvb, frame_offset);
+ new_tvb = tvb_new_subset_length(tvb, frame_offset, (int) data_length);
}
else
{
- new_tvb = process_reassembled_data(tvb, frame_offset, pinfo, "Reassembled LTP Segment",
- frag_msg, &ltp_frag_items,NULL, ltp_data_tree);
+ new_tvb = process_reassembled_data(tvb, frame_offset, pinfo, "Reassembled LTP Block",
+ frag_msg, &ltp_frag_items,NULL, ltp_tree);
}
}
@@ -360,33 +696,35 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
if(new_tvb)
{
- int data_count = 1;
-
- int parse_length;
+ guint64 data_count = 0;
+ int parse_length = tvb_reported_length(new_tvb);
int parse_offset = 0;
- parse_length = tvb_captured_length(new_tvb);
+ 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;
- int sda_header_size;
- proto_tree *ltp_data_data_tree;
tvbuff_t *datatvb;
- ltp_data_data_tree = proto_tree_add_subtree_format(ltp_data_tree, tvb,frame_offset, 0,
- ett_data_data_segm, NULL, "Data[%d]",data_count);
- sda_header_size = 0;
if (client_id == 2) {
- sdnv_status = evaluate_sdnv64(tvb, frame_offset+parse_offset, &sdnv_length, &sda_client_id);
- ti = proto_tree_add_uint64_format_value(ltp_data_data_tree, hf_ltp_data_sda_clid, tvb, frame_offset+parse_offset, sdnv_length, sda_client_id,
- "%" G_GINT64_MODIFIER "u (%s)", sda_client_id, val_to_str_const((const guint32) sda_client_id, client_service_id_info, "Invalid"));
-
- if (!sdnv_status) {
- expert_add_info(pinfo, ti, &ei_ltp_sdnv_length);
- return 0;
- }
-
- sda_header_size = sdnv_length;
-
+ 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");
@@ -394,46 +732,91 @@ dissect_data_segment(proto_tree *ltp_tree, tvbuff_t *tvb,packet_info *pinfo,int
}
}
- datatvb = tvb_new_subset_length_caplen(new_tvb, parse_offset, (int)parse_length - parse_offset, tvb_captured_length(new_tvb));
- bundle_size = call_dissector(bundle_handle, datatvb, pinfo, ltp_data_data_tree);
+ 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*/
}
-
- /* update the length of the data set */
- proto_item_set_len(ltp_data_data_tree, bundle_size+sda_header_size);
+ 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(frag_msg && more_frags)
+ 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);
+ col_append_frame_number(pinfo, COL_INFO, " [Reassembled in #%d] ",frag_msg->reassembled_in);
}
- else
+ else if (!newdata)
{
- col_append_str(pinfo->cinfo, COL_INFO, "[Unfinished LTP Segment] ");
+ 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) {
+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;
- int rcpt_clm_cnt;
+ guint64 rcpt_clm_cnt;
guint64 offset;
guint64 length;
+ guint64 clm_fst, clm_lst;
int rpt_sno_size;
int chkp_sno_size;
@@ -444,10 +827,13 @@ dissect_report_segment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ltp_tree,
int length_size;
int segment_offset = 0;
- int i;
+ 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;
@@ -456,92 +842,191 @@ dissect_report_segment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ltp_tree,
ltp_rpt_tree = proto_tree_add_subtree(ltp_tree, tvb, frame_offset, -1, ett_rpt_segm, &ltp_rpt_item, "Report Segment");
/* Extract the report segment info */
- rpt_sno = evaluate_sdnv_64(tvb, frame_offset, &rpt_sno_size);
- proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_sno, tvb, frame_offset + segment_offset, rpt_sno_size, (guint64)rpt_sno);
+ 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);
+ }
- chkp_sno = evaluate_sdnv_64(tvb, frame_offset + segment_offset, &chkp_sno_size);
- proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_chkp, tvb, frame_offset + segment_offset, chkp_sno_size, (guint64)chkp_sno);
+ 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);
- upper_bound = evaluate_sdnv(tvb, frame_offset + segment_offset, &upper_bound_size);
- proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_ub, tvb, frame_offset + segment_offset, upper_bound_size, (guint64)upper_bound);
+ 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;
- lower_bound = evaluate_sdnv(tvb, frame_offset + segment_offset, &lower_bound_size);
- proto_tree_add_uint64(ltp_rpt_tree, hf_ltp_rpt_lb, tvb, frame_offset + segment_offset, lower_bound_size, (guint64)lower_bound);
+ 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;
- rcpt_clm_cnt = evaluate_sdnv(tvb, frame_offset + segment_offset, &rcpt_clm_cnt_size);
- if (rcpt_clm_cnt < 0){
- proto_item_set_end(ltp_rpt_item, tvb, frame_offset + segment_offset);
- expert_add_info_format(pinfo, ltp_tree, &ei_ltp_neg_reception_claim_count,
- "Negative reception claim count: %d", rcpt_clm_cnt);
- return 0;
+ 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 > tvb_captured_length_remaining(tvb, frame_offset + segment_offset) / 2) {
- proto_item_set_end(ltp_rpt_item, tvb, frame_offset + segment_offset);
- expert_add_info_format(pinfo, ltp_tree, &ei_ltp_mal_reception_claim,
- "Reception claim count impossibly large: %d > %d", rcpt_clm_cnt,
+ 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;
}
- proto_tree_add_uint(ltp_rpt_tree, hf_ltp_rpt_clm_cnt, tvb, frame_offset + segment_offset, rcpt_clm_cnt_size, rcpt_clm_cnt);
- segment_offset += rcpt_clm_cnt_size;
- ltp_rpt_clm_tree = proto_tree_add_subtree(ltp_rpt_tree, tvb, frame_offset + segment_offset, -1, ett_rpt_clm, &ltp_rpt_clm_item, "Reception claims");
+ clm_lst = lower_bound - 1;
/* There can be multiple reception claims in the same report segment */
- for(i = 0; i<rcpt_clm_cnt; i++){
- offset = evaluate_sdnv(tvb,frame_offset + segment_offset, &offset_size);
- proto_tree_add_uint64_format(ltp_rpt_clm_tree, hf_ltp_rpt_clm_off, tvb, frame_offset + segment_offset, offset_size, offset,
- "Offset[%d] : %"G_GINT64_MODIFIER"d", i, offset);
+ for(guint64 ix = 0; ix < rcpt_clm_cnt; ix++){
+ /* Peek at the offset to see if there is a preceding 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, &ltp_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;
- length = evaluate_sdnv(tvb,frame_offset + segment_offset, &length_size);
- proto_tree_add_uint64_format(ltp_rpt_clm_tree, hf_ltp_rpt_clm_len, tvb, frame_offset + segment_offset, length_size, length,
- "Length[%d] : %"G_GINT64_MODIFIER"d",i, length);
+ 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_clm_item, tvb, frame_offset + segment_offset);
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,int frame_offset){
+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, &ltp_rpt_ack_item, "Report Ack Segment");
+
/* Extracing receipt serial number info */
- rpt_sno = evaluate_sdnv_64(tvb,frame_offset, &rpt_sno_size);
- /* XXX - verify that this does not overflow */
+ 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;
- if((unsigned)(frame_offset + segment_offset) > tvb_captured_length(tvb)){
- return 0;
- }
+ proto_item_set_end(ltp_rpt_ack_item, tvb, frame_offset + segment_offset);
- /* Creating tree for the report ack segment */
- ltp_rpt_ack_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, segment_offset,
- ett_rpt_ack_segm, NULL, "Report Ack Segment");
+ 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);
+ }
- proto_tree_add_uint64(ltp_rpt_ack_tree, hf_ltp_rpt_ack_sno, tvb, frame_offset,rpt_sno_size, (guint64)rpt_sno);
return segment_offset;
}
static int
-dissect_cancel_segment(proto_tree * ltp_tree, tvbuff_t *tvb,int frame_offset){
+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;
@@ -559,235 +1044,213 @@ dissect_cancel_segment(proto_tree * ltp_tree, tvbuff_t *tvb,int frame_offset){
static int
-dissect_header_extn(proto_tree *ltp_tree, tvbuff_t *tvb,int frame_offset,int hdr_extn_cnt){
- guint8 extn_type[LTP_MAX_HDR_EXTN];
- gint64 length[LTP_MAX_HDR_EXTN];
-
- int length_size[LTP_MAX_HDR_EXTN];
+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 i;
int extn_offset = 0;
+ proto_item *ltp_hdr_extn_item;
proto_tree *ltp_hdr_extn_tree;
- /* There can be more than one header extensions */
- for(i = 0; i < hdr_extn_cnt; i++){
- extn_type[i] = tvb_get_guint8(tvb,frame_offset);
- extn_offset++;
+ ltp_hdr_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, -1, ett_hdr_extn, &ltp_hdr_extn_item, "Header Extension");
- length[i] = evaluate_sdnv_64(tvb,frame_offset+1,&length_size[i]);
- if((guint64)(frame_offset + extn_offset + length_size[i] + length[i]) >= (guint64)tvb_captured_length(tvb)){
- return 0;
- }
-
- extn_offset += length_size[i];
+ 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 */
- extn_offset += (int)length[i];
- }
- ltp_hdr_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, extn_offset, ett_hdr_extn, NULL, "Header Extension");
-
- for(i = 0; i < hdr_extn_cnt; i++){
- proto_tree_add_uint_format_value(ltp_hdr_extn_tree, hf_ltp_hdr_extn_tag, tvb, frame_offset, 1, extn_type[i], "%x (%s)", extn_type[i], val_to_str_const(extn_type[i],extn_tag_codes,"Unassigned/Reserved"));
- frame_offset += 1;
+ proto_tree_add_item(ltp_hdr_extn_tree, hf_ltp_hdr_extn_tag, tvb, frame_offset + extn_offset, 1, ENC_NA);
+ extn_offset += 1;
- proto_tree_add_uint64_format(ltp_hdr_extn_tree, hf_ltp_hdr_extn_len, tvb, frame_offset, length_size[i],length[i], "Length [%d]: %"G_GINT64_MODIFIER"d",i+1,length[i]);
- frame_offset += length_size[i];
+ 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, (int)length[i], ENC_NA);
- frame_offset += (int)length[i];
+ 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,int frame_offset,int trl_extn_cnt){
- guint8 extn_type[LTP_MAX_TRL_EXTN];
- gint64 length[LTP_MAX_TRL_EXTN];
-
- int length_size[LTP_MAX_TRL_EXTN];
+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 i;
int extn_offset = 0;
+ proto_item *ltp_trl_extn_item;
proto_tree *ltp_trl_extn_tree;
- DISSECTOR_ASSERT(trl_extn_cnt < LTP_MAX_TRL_EXTN);
-
- for(i = 0; i < trl_extn_cnt; i++){
- extn_type[i] = tvb_get_guint8(tvb,frame_offset);
- extn_offset++;
-
- if((unsigned)(frame_offset + extn_offset) >= tvb_captured_length(tvb)){
- return 0;
- }
-
- length[i] = evaluate_sdnv_64(tvb,frame_offset+1,&length_size[i]);
- extn_offset += length_size[i];
-
- if((guint64)(frame_offset + extn_offset + length_size[i] + length[i]) >= tvb_captured_length(tvb)){
- return 0;
- }
-
- /* From RFC-5326, the total length of the Trailer 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 */
- extn_offset += (int)length[i];
- }
-
- ltp_trl_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, extn_offset, ett_trl_extn, NULL, "Trailer Extension");
+ ltp_trl_extn_tree = proto_tree_add_subtree(ltp_tree, tvb,frame_offset, -1, ett_trl_extn, &ltp_trl_extn_item, "Trailer Extension");
- for(i = 0; i < trl_extn_cnt; i++){
- proto_tree_add_uint_format_value(ltp_trl_extn_tree, hf_ltp_trl_extn_tag, tvb, frame_offset, 1, extn_type[i], "%x (%s)", extn_type[i], val_to_str_const(extn_type[i],extn_tag_codes,"Unassigned/Reserved"));
+ 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;
- proto_tree_add_uint64_format(ltp_trl_extn_tree, hf_ltp_trl_extn_len, tvb, frame_offset, length_size[i], length[i], "Length [%d]: %"G_GINT64_MODIFIER"d",i+1,length[i]);
- frame_offset += length_size[i];
+ 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, (int)length[i], ENC_NA);
- frame_offset += (int)length[i];
+ 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(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+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;
- int header_offset;
+ int frame_offset = offset;
int segment_offset = 0;
+ int data_len = 0;
- guint8 ltp_hdr;
gint ltp_type;
- guint8 ltp_extn_cnt;
+ guint64 bitsval;
gint hdr_extn_cnt;
gint trl_extn_cnt;
- gint64 engine_id;
- guint64 session_num;
+ 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;
}
- frame_offset = 0;
- header_offset = 0;
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "LTP Segment");
/* Extract all the header info from the packet */
- ltp_hdr = tvb_get_guint8(tvb, frame_offset);
- header_offset++;
-
- engine_id = evaluate_sdnv_64(tvb,frame_offset + header_offset,&engine_id_size);
- header_offset += engine_id_size;
- if((unsigned)header_offset >= tvb_captured_length(tvb)){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
-
- session_num = evaluate_sdnv_64(tvb,frame_offset + header_offset,&session_num_size);
- header_offset += session_num_size;
- if((unsigned)header_offset >= tvb_captured_length(tvb)){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
-
- ti = proto_tree_add_item(tree, proto_ltp, tvb, 0, -1, ENC_NA);
+ ti = proto_tree_add_item(tree, proto_ltp, tvb, offset, -1, ENC_NA);
ltp_tree = proto_item_add_subtree(ti, ett_ltp);
- /* Adding Header Subtree */
- ltp_header_tree = proto_tree_add_subtree(ltp_tree, tvb, frame_offset, header_offset+1, ett_ltp_hdr, NULL, "LTP Header");
+ ltp_tap_info_t *tap = wmem_new0(wmem_packet_scope(), ltp_tap_info_t);
- proto_tree_add_uint(ltp_header_tree,hf_ltp_version,tvb,frame_offset,1,hi_nibble(ltp_hdr));
- ltp_type = lo_nibble(ltp_hdr);
- proto_tree_add_uint_format_value(ltp_header_tree,hf_ltp_type,tvb,frame_offset,1,ltp_type,"%x (%s)",
- ltp_type,val_to_str_const(ltp_type,ltp_type_codes,"Invalid"));
+ /* 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, header_offset+1, ett_hdr_session, NULL, "Session ID");
-
- proto_tree_add_uint64(ltp_session_tree,hf_ltp_session_orig,tvb,frame_offset,engine_id_size,(guint64)engine_id);
- frame_offset+=engine_id_size;
- proto_tree_add_uint64(ltp_session_tree,hf_ltp_session_no, tvb, frame_offset,session_num_size,session_num);
- frame_offset+=session_num_size;
+ /* 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 */
- ltp_extn_cnt = tvb_get_guint8(tvb,frame_offset);
- hdr_extn_cnt = hi_nibble(ltp_extn_cnt);
- trl_extn_cnt = lo_nibble(ltp_extn_cnt);
-
- proto_tree_add_uint(ltp_header_tree,hf_ltp_hdr_extn_cnt,tvb,frame_offset,1,hdr_extn_cnt);
- proto_tree_add_uint(ltp_header_tree,hf_ltp_trl_extn_cnt,tvb,frame_offset,1,trl_extn_cnt);
+ 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++;
- col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(ltp_type,ltp_type_col_info,"Protocol Error"));
+ proto_item_set_end(ltp_header_item, tvb, frame_offset);
- /* Check if there are any header extensions */
- if(hdr_extn_cnt > 0){
- int hdr_extn_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"));
- if((unsigned)frame_offset >= tvb_captured_length(tvb)){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
-
- hdr_extn_offset = dissect_header_extn(ltp_tree, tvb, frame_offset,hdr_extn_cnt);
- if(hdr_extn_offset == 0){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
+ /* 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;
}
- if((unsigned)frame_offset > tvb_captured_length(tvb)){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
- else if((unsigned)frame_offset == tvb_captured_length(tvb)){
- if(ltp_type != 13 && ltp_type != 15){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
- }
- }
-
/* 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,session_num);
+ 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);
+ 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,frame_offset);
+ 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);
+ 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;
@@ -797,19 +1260,315 @@ dissect_ltp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
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 */
- if(trl_extn_cnt > 0){
- if((unsigned)frame_offset >= tvb_captured_length(tvb)){
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
+ 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;
}
- if(0 == dissect_trailer_extn(ltp_tree, tvb, frame_offset,trl_extn_cnt)) {
- col_set_str(pinfo->cinfo, COL_INFO, "Protocol Error");
- return 0;
+ break;
+ default:
+ break;
+ }
+
+ return CONV_FILTER_INVALID;
+}
+
+static ct_dissector_info_t ltp_ct_dissector_info = {
+ &ltp_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, &ltp_addr_receiver);
+
+ add_conversation_table_data(hash, src, dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts,
+ &ltp_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 the amount of data this dissector was able to dissect */
- return tvb_captured_length(tvb);
+
+ return CONV_FILTER_INVALID;
+}
+
+static et_dissector_info_t ltp_endp_dissector_info = {
+ &ltp_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,
+ &ltp_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 */
@@ -823,7 +1582,7 @@ proto_register_ltp(void)
},
{&hf_ltp_type,
{"LTP Type","ltp.type",
- FT_UINT8,BASE_HEX,NULL, 0x0, NULL, HFILL}
+ FT_UINT8,BASE_HEX,VALS(ltp_type_codes), 0x0, NULL, HFILL}
},
{&hf_ltp_session_orig,
{"Session originator","ltp.session.orig",
@@ -833,6 +1592,10 @@ proto_register_ltp(void)
{"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}
@@ -843,7 +1606,7 @@ proto_register_ltp(void)
},
{&hf_ltp_data_clid,
{"Client service ID","ltp.data.client.id",
- FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL}
+ FT_UINT64,BASE_DEC | BASE_VAL64_STRING, VALS64(client_service_id_info), 0x0, NULL, HFILL}
},
{&hf_ltp_data_offset,
{"Offset","ltp.data.offset",
@@ -851,61 +1614,158 @@ proto_register_ltp(void)
},
{&hf_ltp_data_length,
{"Length","ltp.data.length",
- FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL}
+ 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}
},
-#if 0
+ {&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}
},
-#endif
+ {&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,NULL, 0x0, NULL, HFILL}
+ 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,NULL, 0x0, NULL, HFILL}
+ 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_UINT8,BASE_DEC,NULL, 0x0, NULL, HFILL}
+ FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL}
},
{&hf_ltp_rpt_clm_off,
{"Offset","ltp.rpt.clm.off",
- FT_UINT64,BASE_DEC,NULL, 0x0, NULL, HFILL}
+ 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,NULL, 0x0, NULL, HFILL}
+ FT_UINT8,BASE_HEX,VALS(extn_tag_codes), 0x0, NULL, HFILL}
},
{&hf_ltp_hdr_extn_len,
{"Length","ltp.hdr.extn.len",
@@ -917,7 +1777,7 @@ proto_register_ltp(void)
},
{&hf_ltp_trl_extn_tag,
{"Extension tag","ltp.trl.extn.tag",
- FT_UINT8,BASE_HEX,NULL, 0x0, NULL, HFILL}
+ FT_UINT8,BASE_HEX,VALS(extn_tag_codes), 0x0, NULL, HFILL}
},
{&hf_ltp_trl_extn_len,
{"Length","ltp.trl.extn.len",
@@ -988,10 +1848,12 @@ proto_register_ltp(void)
&ett_ltp_hdr,
&ett_hdr_session,
&ett_hdr_extn,
+ &ett_frame_ref,
&ett_data_segm,
- &ett_data_data_segm,
+ &ett_block,
&ett_rpt_segm,
&ett_rpt_clm,
+ &ett_rpt_gap,
&ett_rpt_ack_segm,
&ett_session_mgmt,
&ett_trl_extn,
@@ -1000,10 +1862,10 @@ proto_register_ltp(void)
};
static ei_register_info ei[] = {
- { &ei_ltp_neg_reception_claim_count, { "ltp.neg_reception_claim_count", PI_UNDECODED, PI_ERROR, "Negative reception claim count", EXPFILL }},
{ &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_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;
@@ -1011,25 +1873,57 @@ proto_register_ltp(void)
/* 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.",
+ &ltp_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.",
+ &ltp_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(&ltp_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(&ltp_reassembly_table,
- &addresses_reassembly_table_functions);
+ &ltp_session_reassembly_table_functions);
}
void
proto_reg_handoff_ltp(void)
{
- dissector_handle_t ltp_handle;
-
- ltp_handle = create_dissector_handle(dissect_ltp, proto_ltp);
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);
}
/*