1941 lines
60 KiB
C
1941 lines
60 KiB
C
/* 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;
|
|
|
|
static int ltp_tap;
|
|
|
|
static gboolean ltp_reassemble_block = TRUE;
|
|
static gboolean ltp_analyze_sequence = TRUE;
|
|
|
|
/* LTP Header variables */
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
|
|
/* LTP Header Extension Segment */
|
|
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;
|
|
static int hf_ltp_trl_extn_len;
|
|
static int hf_ltp_trl_extn_val;
|
|
|
|
/*LTP reassembly */
|
|
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;
|
|
|
|
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;
|
|
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*/
|
|
&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:
|
|
*/
|