wireshark/plugins/epan/transum/packet-transum.c

1367 lines
47 KiB
C

/* packet-transum.c
* Routines for the TRANSUM response time analyzer post-dissector
* By Paul Offord <paul.offord@advance7.com>
* Copyright 2016 Advance Seven Limited
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/* ToDo: Test handling of multiple SMB2 messages within a packet */
/* ToDo: Rework the Summarizer code (future release) */
#include "config.h"
#define WS_LOG_DOMAIN "transum"
#include <epan/proto.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/ws_printf.h>
#include "packet-transum.h"
#include "preferences.h"
#include "extractors.h"
#include "decoders.h"
#include <wsutil/wslog.h>
void proto_register_transum(void);
void proto_reg_handoff_transum(void);
static dissector_handle_t transum_handle;
#define CAPTURE_CLIENT 0
#define CAPTURE_INTERMEDIATE 1
#define CAPTURE_SERVICE 2
#define RTE_TIME_SEC 1
#define RTE_TIME_MSEC 1000
#define RTE_TIME_USEC 1000000
/* The following are the field ids for the protocol values used by TRANSUM.
Make sure they line up with ehf_of_interest order */
HF_OF_INTEREST_INFO hf_of_interest[HF_INTEREST_END_OF_LIST] = {
{ -1, "ip.proto" },
{ -1, "ipv6.nxt" },
{ -1, "tcp.analysis.retransmission" },
{ -1, "tcp.analysis.keep_alive" },
{ -1, "tcp.flags.syn" },
{ -1, "tcp.flags.ack" },
{ -1, "tcp.flags.reset" },
{ -1, "tcp.flags.urg" },
{ -1, "tcp.seq" },
{ -1, "tcp.srcport" },
{ -1, "tcp.dstport" },
{ -1, "tcp.stream" },
{ -1, "tcp.len" },
{ -1, "udp.srcport" },
{ -1, "udp.dstport" },
{ -1, "udp.stream" },
{ -1, "udp.length" },
{ -1, "tls.record.content_type" },
{ -1, "tds.type" },
{ -1, "tds.length" },
{ -1, "smb.mid" },
{ -1, "smb2.sesid" },
{ -1, "smb2.msg_id" },
{ -1, "smb2.cmd" },
{ -1, "dcerpc.ver" },
{ -1, "dcerpc.pkt_type" },
{ -1, "dcerpc.cn_call_id" },
{ -1, "dcerpc.cn_ctx_id" },
{ -1, "dns.id"},
};
static range_t *tcp_svc_port_range_values;
static range_t *udp_svc_port_range_values;
TSUM_PREFERENCES preferences;
static wmem_map_t *detected_tcp_svc; /* this array is used to track services detected during the syn/syn-ack process */
static wmem_map_t *dcerpc_req_pkt_type; /* used to indicate if a DCE-RPC pkt_type is a request */
static wmem_map_t *dcerpc_streams = NULL; /* used to record TCP stream numbers that are carrying DCE-RPC data */
/*
This array contains calls and returns that have no TRUE context_id
This is needed to overcome an apparent bug in Wireshark where
the field name of context id in parameters is the same as context id
in a message header
*/
static wmem_map_t *dcerpc_context_zero;
/*
The rrpd_list holds information about all of the APDU Request-Response Pairs seen in the trace.
*/
static wmem_list_t *rrpd_list = NULL;
/*
output_rrpd is a hash of pointers to RRPDs on the rrpd_list. The index is the frame number. This hash is
used during Wireshark's second scan. As each packet is processed, TRANSUM uses the packet's frame number to index into
this hash to determine if we have RTE data for this particular packet, and if so the write_rte function is called.
*/
static wmem_map_t *output_rrpd;
/*
The temp_rsp_rrpd_list holds RRPDs for APDUs where we have not yet seen the header information and so we can't
fully qualify the identification of the RRPD (the identification being ip_proto:stream_no:session_id:msg_id).
This only occurs when a) we are using one of the decode_based calculations (such as SMB2), and b) when we have
TCP Reassembly enabled. Once we receive a header packet for an APDU we migrate the entry from this array to the
main rrpd_list.
*/
static wmem_list_t *temp_rsp_rrpd_list = NULL; /* Reuse these for speed and efficient memory use - issue a warning if we run out */
/* Optimisation data - the following is used for various optimisation measures */
static int highest_tcp_stream_no;
static int highest_udp_stream_no;
wmem_map_t *tcp_stream_exceptions;
static gint ett_transum = -1;
static gint ett_transum_header = -1;
static gint ett_transum_data = -1;
static int proto_transum = -1;
static int hf_tsum_status = -1;
//static int hf_tsum_time_units = -1;
static int hf_tsum_req_first_seg = -1;
static int hf_tsum_req_last_seg = -1;
static int hf_tsum_rsp_first_seg = -1;
static int hf_tsum_rsp_last_seg = -1;
static int hf_tsum_apdu_rsp_time = -1;
static int hf_tsum_service_time = -1;
static int hf_tsum_req_spread = -1;
static int hf_tsum_rsp_spread = -1;
static int hf_tsum_clip_filter = -1;
static int hf_tsum_calculation = -1;
static int hf_tsum_summary = -1;
static int hf_tsum_req_search = -1;
static int hf_tsum_rsp_search = -1;
static const enum_val_t capture_position_vals[] = {
{ "TRACE_CAP_CLIENT", "Client", TRACE_CAP_CLIENT },
{ "TRACE_CAP_INTERMEDIATE", "Intermediate", TRACE_CAP_INTERMEDIATE },
{ "TRACE_CAP_SERVICE", "Service", TRACE_CAP_SERVICE },
{ NULL, NULL, 0}
};
static const value_string rrdp_calculation_vals[] = {
{ RTE_CALC_GTCP, "Generic TCP" },
{ RTE_CALC_SYN, "SYN and SYN/ACK" },
{ RTE_CALC_DCERPC, "DCE-RPC" },
{ RTE_CALC_SMB2, "SMB2" },
{ RTE_CALC_GUDP, "Generic UDP" },
{ RTE_CALC_DNS, "DNS" },
{ 0, NULL }
};
/*static const enum_val_t time_multiplier_vals[] = {
{ "RTE_TIME_SEC", "seconds", RTE_TIME_SEC },
{ "RTE_TIME_MSEC", "milliseconds", RTE_TIME_MSEC },
{ "RTE_TIME_USEC", "microseconds", RTE_TIME_USEC },
{ NULL, NULL, 0}
};*/
void add_detected_tcp_svc(guint16 port)
{
wmem_map_insert(detected_tcp_svc, GUINT_TO_POINTER(port), GUINT_TO_POINTER(port));
}
static void init_dcerpc_data(void)
{
wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(0), GUINT_TO_POINTER(1));
wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(11), GUINT_TO_POINTER(1));
wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(14), GUINT_TO_POINTER(1));
wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(11), GUINT_TO_POINTER(11));
wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(12), GUINT_TO_POINTER(12));
wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(14), GUINT_TO_POINTER(14));
wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(15), GUINT_TO_POINTER(15));
}
static void register_dcerpc_stream(guint32 stream_no)
{
wmem_map_insert(dcerpc_streams, GUINT_TO_POINTER(stream_no), GUINT_TO_POINTER(1));
}
/* This function should be called before any change to RTE data. */
static void null_output_rrpd_entries(RRPD *in_rrpd)
{
wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_first_frame));
wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_last_frame));
wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_first_frame));
wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_last_frame));
}
/* This function should be called after any change to RTE data. */
static void update_output_rrpd(RRPD *in_rrpd)
{
if (preferences.rte_on_first_req)
wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_first_frame), in_rrpd);
if (preferences.rte_on_last_req)
wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_last_frame), in_rrpd);
if (preferences.rte_on_first_rsp)
wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_first_frame), in_rrpd);
if (preferences.rte_on_last_rsp)
wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_last_frame), in_rrpd);
}
/* Return the index of the RRPD that has been appended */
static RRPD* append_to_rrpd_list(RRPD *in_rrpd)
{
RRPD *next_rrpd = (RRPD*)wmem_memdup(wmem_file_scope(), in_rrpd, sizeof(RRPD));
update_output_rrpd(next_rrpd);
wmem_list_append(rrpd_list, next_rrpd);
return next_rrpd;
}
static RRPD *find_latest_rrpd_dcerpc(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_DCERPC && rrpd->calculation != RTE_CALC_SYN)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
/* if we can match on session_id and msg_id must be a retransmission of the last request packet or the response */
/* this logic works whether or not we are using reassembly */
if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
return rrpd;
/* If this is a retransmission, we assume it relates to this rrpd_list entry.
This is a bit of a kludge and not ideal but a compromise.*/
/* ToDo: look at using TCP sequence number to allocate a retransmission to the correct APDU */
if (in_rrpd->is_retrans)
return rrpd;
if (preferences.reassembly)
{
if (in_rrpd->c2s)
{
/* if the input rrpd is for c2s and the one we have found already has response information, then the
in_rrpd represents a new RR Pair. */
if (rrpd->rsp_first_frame)
return NULL;
/* If the current rrpd_list entry doesn't have a msg_id then we assume we are mid Request APDU and so we have a match. */
if (!rrpd->msg_id)
return rrpd;
}
else /* The in_rrpd relates to a packet going s2c */
{
/* When reassembly is enabled, multi-packet response information is actually migrated from the temp_rsp_rrpd_list
to the rrpd_list and so we won't come through here. */
;
}
}
else /* we are not using reassembly */
{
if (in_rrpd->c2s)
{
if (in_rrpd->msg_id)
/* if we have a message id this is a new Request APDU */
return NULL;
else /* No msg_id */
{
return rrpd; /* add this packet to the matching stream */
}
}
else /* this packet is going s2c */
{
if (!in_rrpd->msg_id && rrpd->rsp_first_frame)
/* we need to add this frame to the response APDU of the most recent rrpd_list entry that has already had response packets */
return rrpd;
}
}
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd_dns(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_DNS)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
{
if (in_rrpd->c2s && rrpd->rsp_first_frame)
return NULL; /* this is new */
else
return rrpd;
}
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* this is the end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd_gtcp(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_GTCP && rrpd->calculation != RTE_CALC_SYN)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
if (in_rrpd->c2s && rrpd->rsp_first_frame)
return NULL; /* this is new */
else
return rrpd;
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* this is the end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd_gudp(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_GUDP)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
if (in_rrpd->c2s && rrpd->rsp_first_frame)
return NULL; /* this is new */
else
return rrpd;
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* this is the end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd_smb2(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_SMB2 && rrpd->calculation != RTE_CALC_SYN)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
/* if we can match on session_id and msg_id must be a retransmission of the last request packet or the response */
/* this logic works whether or not we are using reassembly */
if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
return rrpd;
/* If this is a retransmission, we assume it relates to this rrpd_list entry.
This is a bit of a kludge and not ideal but a compromise.*/
/* ToDo: look at using TCP sequence number to allocate a retransmission to the correct APDU */
if (in_rrpd->is_retrans)
return rrpd;
if (preferences.reassembly)
{
if (in_rrpd->c2s)
{
/* if the input rrpd is for c2s and the one we have found already has response information, then the
in_rrpd represents a new RR Pair. */
if (rrpd->rsp_first_frame)
return NULL;
/* If the current rrpd_list entry doesn't have a msg_id then we assume we are mid Request APDU and so we have a match. */
if (!rrpd->msg_id)
return rrpd;
}
else /* The in_rrpd relates to a packet going s2c */
{
/* When reassembly is enabled, multi-packet response information is actually migrated from the temp_rsp_rrpd_list
to the rrpd_list and so we won't come through here. */
;
}
}
else /* we are not using reassembly */
{
if (in_rrpd->c2s)
{
if (in_rrpd->msg_id)
/* if we have a message id this is a new Request APDU */
return NULL;
else /* No msg_id */
{
return rrpd; /* add this packet to the matching stream */
}
}
else /* this packet is going s2c */
{
if (!in_rrpd->msg_id && rrpd->rsp_first_frame)
/* we need to add this frame to the response APDU of the most recent rrpd_list entry that has already had response packets */
return rrpd;
}
}
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd_syn(RRPD *in_rrpd)
{
RRPD *rrpd;
wmem_list_frame_t* i;
for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->calculation != RTE_CALC_SYN)
continue;
/* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
{
return rrpd;
} /* this is the end of the 5-tuple check */
if (in_rrpd->c2s)
in_rrpd->req_search_total++;
else
in_rrpd->rsp_search_total++;
} /* this is the end of the for loop */
return NULL;
}
static RRPD *find_latest_rrpd(RRPD *in_rrpd)
{
/* Optimisation Code */
if (in_rrpd->ip_proto == IP_PROTO_TCP && (int)in_rrpd->stream_no > highest_tcp_stream_no)
{
highest_tcp_stream_no = in_rrpd->stream_no;
return NULL;
}
else if (in_rrpd->ip_proto == IP_PROTO_UDP && (int)in_rrpd->stream_no > highest_udp_stream_no)
{
highest_udp_stream_no = in_rrpd->stream_no;
return NULL;
}
/* End of Optimisation Code */
switch (in_rrpd->calculation)
{
case RTE_CALC_DCERPC:
return find_latest_rrpd_dcerpc(in_rrpd);
break;
case RTE_CALC_DNS:
return find_latest_rrpd_dns(in_rrpd);
break;
case RTE_CALC_GTCP:
return find_latest_rrpd_gtcp(in_rrpd);
break;
case RTE_CALC_GUDP:
return find_latest_rrpd_gudp(in_rrpd);
break;
case RTE_CALC_SMB2:
return find_latest_rrpd_smb2(in_rrpd);
break;
case RTE_CALC_SYN:
return find_latest_rrpd_syn(in_rrpd);
break;
}
return NULL;
}
static void update_rrpd_list_entry(RRPD *match, RRPD *in_rrpd)
{
null_output_rrpd_entries(match);
if (preferences.debug_enabled)
{
match->req_search_total += in_rrpd->req_search_total;
match->rsp_search_total += in_rrpd->rsp_search_total;
}
if (in_rrpd->c2s)
{
match->req_last_frame = in_rrpd->req_last_frame;
match->req_last_rtime = in_rrpd->req_last_rtime;
if (in_rrpd->msg_id)
{
match->session_id = in_rrpd->session_id;
match->msg_id = in_rrpd->msg_id;
}
}
else
{
if (!match->rsp_first_frame)
{
match->rsp_first_frame = in_rrpd->rsp_first_frame;
match->rsp_first_rtime = in_rrpd->rsp_first_rtime;
}
match->rsp_last_frame = in_rrpd->rsp_last_frame;
match->rsp_last_rtime = in_rrpd->rsp_last_rtime;
}
update_output_rrpd(match);
}
/*
This function processes a sub-packet that is going from client-to-service.
*/
static void update_rrpd_list_entry_req(RRPD *in_rrpd)
{
RRPD *match;
match = find_latest_rrpd(in_rrpd);
if (match != NULL)
update_rrpd_list_entry(match, in_rrpd);
else
append_to_rrpd_list(in_rrpd);
}
/*
This function inserts an RRPD into the temp_rsp_rrpd_list. If this is
successful return a pointer to the entry, else return NULL.
*/
static RRPD* insert_into_temp_rsp_rrpd_list(RRPD *in_rrpd)
{
RRPD *rrpd = (RRPD*)wmem_memdup(wmem_file_scope(), in_rrpd, sizeof(RRPD));
wmem_list_append(temp_rsp_rrpd_list, rrpd);
return rrpd;
}
static RRPD* find_temp_rsp_rrpd(RRPD *in_rrpd)
{
wmem_list_frame_t *i;
RRPD* rrpd;
for (i = wmem_list_head(temp_rsp_rrpd_list); i; i = wmem_list_frame_next(i))
{
rrpd = (RRPD*)wmem_list_frame_data(i);
if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
return rrpd;
}
return NULL;
}
static void update_temp_rsp_rrpd(RRPD *temp_list, RRPD *in_rrpd)
{
temp_list->rsp_last_frame = in_rrpd->rsp_last_frame;
temp_list->rsp_last_rtime = in_rrpd->rsp_last_rtime;
}
/* This function migrates an entry from the temp_rsp_rrpd_list to the main rrpd_list. */
static void migrate_temp_rsp_rrpd(RRPD *main_list, RRPD *temp_list)
{
update_rrpd_list_entry(main_list, temp_list);
wmem_list_remove(temp_rsp_rrpd_list, temp_list);
}
static void update_rrpd_list_entry_rsp(RRPD *in_rrpd)
{
RRPD *match, *temp_list;
if (in_rrpd->decode_based)
{
if (preferences.reassembly)
{
if (in_rrpd->msg_id)
{
/* If we have a msg_id in the input RRPD we must have header information. */
temp_list = find_temp_rsp_rrpd(in_rrpd);
if (temp_list != NULL)
{
update_temp_rsp_rrpd(temp_list, in_rrpd);
/* Migrate the temp_rsp_rrpd_list entry to the main rrpd_list */
match = find_latest_rrpd(in_rrpd);
if (match != NULL)
migrate_temp_rsp_rrpd(match, temp_list);
}
else
{
match = find_latest_rrpd(in_rrpd);
/* There isn't an entry in the temp_rsp_rrpd_list so update the master rrpd_list entry */
if (match != NULL)
update_rrpd_list_entry(match, in_rrpd);
}
}
else
{
/* Update an existing entry to the temp_rsp_rrpd_list or add a new one. */
temp_list = find_temp_rsp_rrpd(in_rrpd);
if (temp_list != NULL)
update_temp_rsp_rrpd(temp_list, in_rrpd);
else
{
/* If this is a retransmission we need to add it to the last completed rrpd_list entry for this stream */
if (in_rrpd->is_retrans)
{
match = find_latest_rrpd(in_rrpd);
if (match != NULL)
update_rrpd_list_entry(match, in_rrpd);
else
insert_into_temp_rsp_rrpd_list(in_rrpd);
}
else
/* As it's not a retransmission, just create a new entry on the temp list */
insert_into_temp_rsp_rrpd_list(in_rrpd);
}
}
}
else
{
/* Reassembly isn't set and so just go ahead and use the list function */
match = find_latest_rrpd(in_rrpd);
if (match != NULL)
update_rrpd_list_entry(match, in_rrpd);
}
}
else
{
/* if this isn't decode_based then just go ahead and update the RTE data */
match = find_latest_rrpd(in_rrpd);
if (match != NULL)
update_rrpd_list_entry(match, in_rrpd);
}
return;
}
/*
This function updates the RTE data of an RRPD on the rrpd_list. The
frame_no values in the input RRPD double up as a mask. If the frame_no
is > 0 then the frame_no value and rtime values are updated. If the
frame_no is 0 then that particular frame_no and rtime value is not updated.
*/
static void update_rrpd_rte_data(RRPD *in_rrpd)
{
if (in_rrpd->c2s)
update_rrpd_list_entry_req(in_rrpd);
else
update_rrpd_list_entry_rsp(in_rrpd);
}
gboolean is_dcerpc_context_zero(guint32 pkt_type)
{
return (wmem_map_lookup(dcerpc_context_zero, GUINT_TO_POINTER(pkt_type)) != NULL);
}
gboolean is_dcerpc_req_pkt_type(guint32 pkt_type)
{
return (wmem_map_lookup(dcerpc_req_pkt_type, GUINT_TO_POINTER(pkt_type)) != NULL);
}
static gboolean is_dcerpc_stream(guint32 stream_no)
{
return (wmem_map_lookup(dcerpc_streams, GUINT_TO_POINTER(stream_no)) != NULL);
}
/*
This function initialises the global variables and populates the
[tcp|udp]_svc_ports tables with information from the preference settings
*/
static void init_globals(void)
{
if (!proto_is_protocol_enabled(find_protocol_by_id(proto_transum)))
return;
highest_tcp_stream_no = -1;
highest_udp_stream_no = -1;
/* Create and initialise some dynamic memory areas */
tcp_stream_exceptions = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
detected_tcp_svc = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
rrpd_list = wmem_list_new(wmem_file_scope());
temp_rsp_rrpd_list = wmem_list_new(wmem_file_scope());
/* Indicate what fields we're interested in. */
GArray *wanted_fields = g_array_sized_new(FALSE, FALSE, (guint)sizeof(int), HF_INTEREST_END_OF_LIST);
for (int i = 0; i < HF_INTEREST_END_OF_LIST; i++)
{
if (hf_of_interest[i].hf != -1)
g_array_append_val(wanted_fields, hf_of_interest[i].hf);
else
ws_warning("TRANSUM: unknown field %s", hf_of_interest[i].proto_name);
}
set_postdissector_wanted_hfids(transum_handle, wanted_fields);
preferences.tcp_svc_ports = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
preferences.udp_svc_ports = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
/* use the range values to populate the tcp_svc_ports list*/
for (guint i = 0; i < tcp_svc_port_range_values->nranges; i++)
{
for (guint32 j = tcp_svc_port_range_values->ranges[i].low; j <= tcp_svc_port_range_values->ranges[i].high; j++)
{
wmem_map_insert(preferences.tcp_svc_ports, GUINT_TO_POINTER(j), GUINT_TO_POINTER(RTE_CALC_GTCP));
}
}
/* use the range values to populate the udp_svc_ports list*/
for (guint i = 0; i < udp_svc_port_range_values->nranges; i++)
{
for (guint32 j = udp_svc_port_range_values->ranges[i].low; j <= udp_svc_port_range_values->ranges[i].high; j++)
{
wmem_map_insert(preferences.udp_svc_ports, GUINT_TO_POINTER(j), GUINT_TO_POINTER(RTE_CALC_GUDP));
}
}
/* create arrays to hold some DCE-RPC values */
dcerpc_context_zero = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
dcerpc_req_pkt_type = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
dcerpc_streams = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
init_dcerpc_data();
wmem_map_insert(preferences.tcp_svc_ports, GUINT_TO_POINTER(445), GUINT_TO_POINTER(RTE_CALC_SMB2));
wmem_map_insert(preferences.udp_svc_ports, GUINT_TO_POINTER(53), GUINT_TO_POINTER(RTE_CALC_DNS));
}
/* Undo capture file-specific initializations. */
static void cleanup_globals(void)
{
/* Clear the list of wanted fields as it will be reinitialized. */
set_postdissector_wanted_hfids(transum_handle, NULL);
}
/* This function adds the RTE data to the tree. The summary ptr is currently
not used but will be used for summariser information once this feature has
been ported from the LUA code. */
static void write_rte(RRPD *in_rrpd, tvbuff_t *tvb, proto_tree *tree, char *summary)
{
nstime_t rte_art;
nstime_t rte_st;
nstime_t rte_reqspread;
nstime_t rte_rspspread;
proto_tree *rte_tree;
proto_item *pi;
wmem_strbuf_t *temp_string = wmem_strbuf_new(wmem_packet_scope(), "");
if (in_rrpd->req_first_frame)
{
pi = proto_tree_add_item(tree, proto_transum, tvb, 0, -1, ENC_NA);
rte_tree = proto_item_add_subtree(pi, ett_transum);
nstime_delta(&rte_reqspread, &(in_rrpd->req_last_rtime), &(in_rrpd->req_first_rtime));
if (in_rrpd->rsp_first_frame)
{
/* calculate the RTE times */
nstime_delta(&rte_art, &(in_rrpd->rsp_last_rtime), &(in_rrpd->req_first_rtime));
nstime_delta(&rte_st, &(in_rrpd->rsp_first_rtime), &(in_rrpd->req_last_rtime));
nstime_delta(&rte_rspspread, &(in_rrpd->rsp_last_rtime), &(in_rrpd->rsp_first_rtime));
pi = proto_tree_add_string(rte_tree, hf_tsum_status, tvb, 0, 0, "OK");
}
else
{
pi = proto_tree_add_string(rte_tree, hf_tsum_status, tvb, 0, 0, "Response missing");
}
proto_item_set_generated(pi);
pi = proto_tree_add_uint(rte_tree, hf_tsum_req_first_seg, tvb, 0, 0, in_rrpd->req_first_frame);
proto_item_set_generated(pi);
pi = proto_tree_add_uint(rte_tree, hf_tsum_req_last_seg, tvb, 0, 0, in_rrpd->req_last_frame);
proto_item_set_generated(pi);
if (in_rrpd->rsp_first_frame)
{
pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_first_seg, tvb, 0, 0, in_rrpd->rsp_first_frame);
proto_item_set_generated(pi);
pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_last_seg, tvb, 0, 0, in_rrpd->rsp_last_frame);
proto_item_set_generated(pi);
pi = proto_tree_add_time(rte_tree, hf_tsum_apdu_rsp_time, tvb, 0, 0, &rte_art);
proto_item_set_generated(pi);
pi = proto_tree_add_time(rte_tree, hf_tsum_service_time, tvb, 0, 0, &rte_st);
proto_item_set_generated(pi);
}
pi = proto_tree_add_time(rte_tree, hf_tsum_req_spread, tvb, 0, 0, &rte_reqspread);
proto_item_set_generated(pi);
if (in_rrpd->rsp_first_frame)
{
pi = proto_tree_add_time(rte_tree, hf_tsum_rsp_spread, tvb, 0, 0, &rte_rspspread);
proto_item_set_generated(pi);
}
if (in_rrpd->ip_proto == IP_PROTO_TCP)
wmem_strbuf_append_printf(temp_string, "tcp.stream==%d", in_rrpd->stream_no);
else if (in_rrpd->ip_proto == IP_PROTO_UDP)
wmem_strbuf_append_printf(temp_string, "udp.stream==%d", in_rrpd->stream_no);
if (in_rrpd->rsp_first_frame)
wmem_strbuf_append_printf(temp_string, " && frame.number>=%d && frame.number<=%d", in_rrpd->req_first_frame, in_rrpd->rsp_last_frame);
else
wmem_strbuf_append_printf(temp_string, " && frame.number>=%d && frame.number<=%d", in_rrpd->req_first_frame, in_rrpd->req_last_frame);
if (in_rrpd->calculation == RTE_CALC_GTCP)
wmem_strbuf_append_printf(temp_string, " && tcp.len>0");
pi = proto_tree_add_string(rte_tree, hf_tsum_clip_filter, tvb, 0, 0, wmem_strbuf_get_str(temp_string));
proto_item_set_generated(pi);
pi = proto_tree_add_string(rte_tree, hf_tsum_calculation, tvb, 0, 0, val_to_str(in_rrpd->calculation, rrdp_calculation_vals, "Unknown calculation type: %d"));
proto_item_set_generated(pi);
if (in_rrpd->rsp_first_frame)
{
if (preferences.summarisers_enabled)
{
if (summary)
{
pi = proto_tree_add_string(tree, hf_tsum_summary, tvb, 0, 0, summary);
proto_item_set_generated(pi);
}
}
}
if (preferences.debug_enabled)
{
pi = proto_tree_add_uint(rte_tree, hf_tsum_req_search, tvb, 0, 0, in_rrpd->req_search_total);
proto_item_set_generated(pi);
pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_search, tvb, 0, 0, in_rrpd->rsp_search_total);
proto_item_set_generated(pi);
}
}
}
/*
This function sets initial values in the current_pkt structure and checks
the xxx_svc_port arrays to see if they contain a match for the source or
destination port. This function also adds tcp_svc_ports entries when it
discovers DCE-RPC traffic.
Returns the number of sub-packets to be processed.
*/
static void set_proto_values(packet_info *pinfo, proto_tree *tree, PKT_INFO* pkt_info, PKT_INFO* subpackets)
{
guint32 field_uint[MAX_RETURNED_ELEMENTS]; /* An extracted field array for unsigned integers */
size_t field_value_count; /* How many entries are there in the extracted field array */
pkt_info->frame_number = pinfo->fd->num; /* easy access to frame number */
pkt_info->relative_time = pinfo->rel_ts;
int number_sub_pkts_of_interest = 0; /* default */
if (pinfo->ptype == PT_TCP)
pkt_info->rrpd.ip_proto = IP_PROTO_TCP;
else if (pinfo->ptype == PT_UDP)
pkt_info->rrpd.ip_proto = IP_PROTO_UDP;
if (pkt_info->rrpd.ip_proto == IP_PROTO_TCP)
{
number_sub_pkts_of_interest = decode_gtcp(pinfo, tree, pkt_info);
/* decode_gtcp may return 0 but we need to keep processing because we
calculate RTE figures for all SYNs and also we may detect DCE-RPC later
(even though we don't currently have an entry in the tcp_svc_ports list). */
/* Optimisation code */
if (pkt_info->len || pkt_info->tcp_flags_syn)
{
if (pkt_info->ssl_content_type == 21) /* this is an SSL Alert */
{
pkt_info->pkt_of_interest = FALSE;
return;
}
if ((int)pkt_info->rrpd.stream_no > highest_tcp_stream_no && !pkt_info->rrpd.c2s)
{
/* first packet on the stream is s2c and so add to exception list */
if (wmem_map_lookup(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no)) == NULL)
wmem_map_insert(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no), GUINT_TO_POINTER(1));
}
if (wmem_map_lookup(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no)) != NULL)
{
if (pkt_info->rrpd.c2s)
wmem_map_remove(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no));
else
pkt_info->pkt_of_interest = FALSE;
}
}
/* End of Optimisation Code */
if (pkt_info->tcp_retran)
{
/* we may not want to continue with this packet if it's a retransmission */
/* If this is a server-side trace we need to ignore client-to-service TCP retransmissions
the rationale being that if we saw the original in the trace the service process saw it too */
if (pkt_info->rrpd.c2s && preferences.capture_position == CAPTURE_SERVICE)
{
pkt_info->pkt_of_interest = FALSE;
return;
}
/* If this is a client-side trace we need to ignore service-to-client TCP retransmissions
the rationale being that if we saw the original in the trace the client process saw it too */
else if (!pkt_info->rrpd.c2s && preferences.capture_position == CAPTURE_CLIENT)
{
pkt_info->pkt_of_interest = FALSE;
return;
}
}
/* We are not interested in TCP Keep-Alive */
if (pkt_info->tcp_keep_alive)
{
pkt_info->pkt_of_interest = FALSE;
return;
}
if (pkt_info->len == 1)
{
if (preferences.orphan_ka_discard && pkt_info->tcp_flags_ack && pkt_info->rrpd.c2s)
{
pkt_info->pkt_of_interest = FALSE;
return; /* It's a KEEP-ALIVE -> stop processing this packet */
}
}
/* check if SYN */
if (pkt_info->tcp_flags_syn)
number_sub_pkts_of_interest = decode_syn(pinfo, tree, pkt_info);
if (pkt_info->len > 0)
{
/* check if SMB2 */
if (pkt_info->dstport == 445 || pkt_info->srcport == 445)
number_sub_pkts_of_interest = decode_smb(pinfo, tree, pkt_info, subpackets);
else
{
/* check if DCE-RPC */
/* We need to set RTE_CALC_DCERPC even when we don't have header info. */
if (is_dcerpc_stream(pkt_info->rrpd.stream_no))
{
pkt_info->rrpd.calculation = RTE_CALC_DCERPC;
pkt_info->rrpd.decode_based = TRUE;
pkt_info->pkt_of_interest = TRUE;
}
if (!extract_uint(tree, hf_of_interest[HF_INTEREST_DCERPC_VER].hf, field_uint, &field_value_count))
{
if (field_value_count)
{
if (pkt_info->rrpd.calculation != RTE_CALC_DCERPC)
register_dcerpc_stream(pkt_info->rrpd.stream_no);
number_sub_pkts_of_interest = decode_dcerpc(pinfo, tree, pkt_info);
}
}
}
}
}
else if (pkt_info->rrpd.ip_proto == IP_PROTO_UDP)
{
/* It's UDP */
number_sub_pkts_of_interest = decode_gudp(pinfo, tree, pkt_info);
if (pkt_info->srcport == 53 || pkt_info->dstport == 53)
number_sub_pkts_of_interest = decode_dns(pinfo, tree, pkt_info);
}
/* Set appropriate RTE values in the sub-packets */
for (int i = 0; (i < number_sub_pkts_of_interest) && (i < MAX_SUBPKTS_PER_PACKET); i++)
{
if (pkt_info->rrpd.c2s)
{
subpackets[i].rrpd.req_first_frame = pkt_info->frame_number;
subpackets[i].rrpd.req_first_rtime = pkt_info->relative_time;
subpackets[i].rrpd.req_last_frame = pkt_info->frame_number;
subpackets[i].rrpd.req_last_rtime = pkt_info->relative_time;
subpackets[i].frame_number = pkt_info->frame_number; /* this acts as a switch later */
}
else
{
subpackets[i].rrpd.rsp_first_frame = pkt_info->frame_number;
subpackets[i].rrpd.rsp_first_rtime = pkt_info->relative_time;
subpackets[i].rrpd.rsp_last_frame = pkt_info->frame_number;
subpackets[i].rrpd.rsp_last_rtime = pkt_info->relative_time;
subpackets[i].frame_number = pkt_info->frame_number; /* this acts as a switch later */
}
}
}
/*
* This function is called for each packet
* Wireshark scans all the packets once and then once again as they are displayed
* The pinfo.visited boolean is set to FALSE; on the first scan
*/
static int dissect_transum(tvbuff_t *buffer, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
/* if (there is RTE info associated with this packet we need to output it */
if (PINFO_FD_VISITED(pinfo))
{
RRPD *rrpd = (RRPD*)wmem_map_lookup(output_rrpd, GUINT_TO_POINTER(pinfo->num));
if (rrpd)
{
if (tree)
{
/* Add the RTE data to the protocol decode tree if we output_flag is set */
write_rte(rrpd, buffer, tree, NULL);
}
}
}
else
{
PKT_INFO *sub_packet = wmem_alloc0_array(wmem_packet_scope(), PKT_INFO, MAX_SUBPKTS_PER_PACKET);
set_proto_values(pinfo, tree, &sub_packet[0], sub_packet);
if (sub_packet[0].pkt_of_interest)
{
/* Loop to process each sub_packet and update the related RTE data */
for (int i = 0; i < MAX_SUBPKTS_PER_PACKET; i++)
{
if (!sub_packet[i].frame_number)
break;
update_rrpd_rte_data(&(sub_packet[i].rrpd));
}
}
}
return 0;
}
void
proto_register_transum(void)
{
module_t *transum_module;
static hf_register_info hf[] = {
{ &hf_tsum_status,
{ "RTE Status", "transum.status",
FT_STRING, BASE_NONE, NULL, 0x0,
"Indication of completeness of the RTE information", HFILL } },
#if 0
{ &hf_tsum_time_units,
{ "RTE Time Units", "transum.time_units",
FT_STRING, BASE_NONE, NULL, 0x0,
"Time units used (s, ms or us) for the RTE values", HFILL }
},
#endif
{ &hf_tsum_req_first_seg,
{ "Req First Seg", "transum.firstreq",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"First Segment of an APDU Request", HFILL }
},
{ &hf_tsum_req_last_seg,
{ "Req Last Seg", "transum.lastreq",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Last Segment of an APDU Request", HFILL }
},
{ &hf_tsum_rsp_first_seg,
{ "Rsp First Seg", "transum.firstrsp",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"First Segment of an APDU Response", HFILL }
},
{ &hf_tsum_rsp_last_seg,
{ "Rsp Last Seg", "transum.lastrsp",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Last Segment of an APDU Response", HFILL }
},
{ &hf_tsum_apdu_rsp_time,
{ "APDU Rsp Time", "transum.art",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"RTE APDU Response Time", HFILL }
},
{ &hf_tsum_service_time,
{ "Service Time", "transum.st",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"RTE Service Time", HFILL }
},
{ &hf_tsum_req_spread,
{ "Req Spread", "transum.reqspread",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"RTE Request Spread", HFILL }
},
{ &hf_tsum_rsp_spread,
{ "Rsp Spread", "transum.rspspread",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
"RTE Response Spread", HFILL }
},
{ &hf_tsum_clip_filter,
{ "Trace clip filter", "transum.clip_filter",
FT_STRING, BASE_NONE, NULL, 0x0,
"Filter expression to select the APDU Request-Response pair", HFILL }
},
{ &hf_tsum_calculation,
{ "Calculation", "transum.calculation",
FT_STRING, BASE_NONE, NULL, 0x0,
"Basis of the RTE calculation", HFILL }
},
{ &hf_tsum_summary,
{ "Summary", "transum.summary",
FT_STRING, BASE_NONE, NULL, 0x0,
"Summarizer information", HFILL }
},
{ &hf_tsum_req_search,
{ "Req Search Count", "transum.req_search",
FT_UINT32, BASE_DEC, NULL, 0x0,
"rrpd_list search total for the request packets", HFILL }
},
{ &hf_tsum_rsp_search,
{ "Rsp Search Counts", "transum.rsp_search",
FT_UINT32, BASE_DEC, NULL, 0x0,
"rrpd_list search total for the response packets", HFILL }
}
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_transum,
&ett_transum_header,
&ett_transum_data
};
proto_transum = proto_register_protocol("TRANSUM RTE Data", "TRANSUM", "transum");
/* Due to performance concerns of the dissector, it's disabled by default */
proto_disable_by_default(proto_transum);
/* Set User Preferences defaults */
preferences.capture_position = TRACE_CAP_CLIENT;
preferences.reassembly = TRUE;
range_convert_str(wmem_epan_scope(), &tcp_svc_port_range_values, "25, 80, 443, 1433", MAX_TCP_PORT);
range_convert_str(wmem_epan_scope(), &udp_svc_port_range_values, "137-139", MAX_UDP_PORT);
preferences.orphan_ka_discard = FALSE;
preferences.time_multiplier = RTE_TIME_SEC;
preferences.rte_on_first_req = FALSE;
preferences.rte_on_last_req = TRUE;
preferences.rte_on_first_rsp = FALSE;
preferences.rte_on_last_rsp = FALSE;
preferences.debug_enabled = FALSE;
/* no start registering stuff */
proto_register_field_array(proto_transum, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
transum_module = prefs_register_protocol(proto_transum, NULL); /* ToDo: We need to rethink the NULL pointer so that a preference change causes a rescan */
/* Register the preferences */
prefs_register_obsolete_preference(transum_module, "tsumenabled");
prefs_register_enum_preference(transum_module,
"capture_position",
"Capture position",
"Position of the capture unit that produced this trace. This setting affects the way TRANSUM handles TCP Retransmissions. See the manual for details.",
&preferences.capture_position,
capture_position_vals,
FALSE);
prefs_register_bool_preference(transum_module,
"reassembly",
"Subdissector reassembly enabled",
"Set this to match to the TCP subdissector reassembly setting",
&preferences.reassembly);
prefs_register_range_preference(transum_module,
"tcp_port_ranges",
"Output RTE data for these TCP service ports",
"Add and remove ports numbers separated by commas\nRanges are supported e.g. 25,80,2000-3000,5432",
&tcp_svc_port_range_values,
65536);
prefs_register_range_preference(transum_module,
"udp_port_ranges",
"Output RTE data for these UDP service ports",
"Add and remove ports numbers separated by commas\nRanges are supported e.g. 123,137-139,520-521,2049",
&udp_svc_port_range_values,
65536);
prefs_register_bool_preference(transum_module,
"orphan_ka_discard",
"Discard orphaned TCP Keep-Alives",
"Set this to discard any packet in the direction client to service,\nwith a 1-byte payload of 0x00 and the ACK flag set",
&preferences.orphan_ka_discard);
/* removed from this release
prefs_register_enum_preference(transum_module,
"time_multiplier",
"Time units for RTE values",
"Unit of time used for APDU Response Time, Service Time and Spread Time values.",
&preferences.time_multiplier,
time_multiplier_vals,
FALSE);
*/
prefs_register_bool_preference(transum_module,
"rte_on_first_req",
"Add RTE data to the first request segment",
"RTE data will be added to the first request packet",
&preferences.rte_on_first_req);
prefs_register_bool_preference(transum_module,
"rte_on_last_req",
"Add RTE data to the last request segment",
"RTE data will be added to the last request packet",
&preferences.rte_on_last_req);
prefs_register_bool_preference(transum_module,
"rte_on_first_rsp",
"Add RTE data to the first response segment",
"RTE data will be added to the first response packet",
&preferences.rte_on_first_rsp);
prefs_register_bool_preference(transum_module,
"rte_on_last_rsp",
"Add RTE data to the last response segment",
"RTE data will be added to the last response packet",
&preferences.rte_on_last_rsp);
prefs_register_bool_preference(transum_module,
"debug_enabled",
"Enable debug info",
"Set this only to troubleshoot problems",
&preferences.debug_enabled);
transum_handle = register_dissector("transum", dissect_transum, proto_transum);
register_init_routine(init_globals);
register_cleanup_routine(cleanup_globals);
register_postdissector(transum_handle);
output_rrpd = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
}
void proto_reg_handoff_transum(void)
{
/* Get the field id for each field we will need */
for (int i = 0; i < HF_INTEREST_END_OF_LIST; i++)
{
hf_of_interest[i].hf = proto_registrar_get_id_byname(hf_of_interest[i].proto_name);
}
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/