2 * Routines for the TRANSUM response time analyzer post-dissector
3 * By Paul Offord <paul.offord@advance7.com>
4 * Copyright 2016 Advance Seven Limited
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
13 /* ToDo: Test handling of multiple SMB2 messages within a packet */
14 /* ToDo: Rework the Summarizer code (future release) */
18 #include <epan/proto.h>
19 #include <epan/packet.h>
20 #include <epan/prefs.h>
21 #include <wsutil/ws_printf.h>
22 #include "packet-transum.h"
23 #include "preferences.h"
24 #include "extractors.h"
27 void proto_register_transum(void);
28 void proto_reg_handoff_transum(void);
30 static dissector_handle_t transum_handle;
32 #define CAPTURE_CLIENT 0
33 #define CAPTURE_INTERMEDIATE 1
34 #define CAPTURE_SERVICE 2
36 #define RTE_TIME_SEC 1
37 #define RTE_TIME_MSEC 1000
38 #define RTE_TIME_USEC 1000000
40 /* The following are the field ids for the protocol values used by TRANSUM.
41 Make sure they line up with ehf_of_interest order */
42 HF_OF_INTEREST_INFO hf_of_interest[HF_INTEREST_END_OF_LIST] = {
46 { -1, "tcp.analysis.retransmission" },
47 { -1, "tcp.analysis.keep_alive" },
48 { -1, "tcp.flags.syn" },
49 { -1, "tcp.flags.ack" },
50 { -1, "tcp.flags.reset" },
51 { -1, "tcp.flags.urg" },
53 { -1, "tcp.srcport" },
54 { -1, "tcp.dstport" },
58 { -1, "udp.srcport" },
59 { -1, "udp.dstport" },
63 { -1, "tls.record.content_type" },
71 { -1, "smb2.msg_id" },
75 { -1, "dcerpc.pkt_type" },
76 { -1, "dcerpc.cn_call_id" },
77 { -1, "dcerpc.cn_ctx_id" },
83 static range_t *tcp_svc_port_range_values;
85 static range_t *udp_svc_port_range_values;
87 TSUM_PREFERENCES preferences;
90 static wmem_map_t *detected_tcp_svc; /* this array is used to track services detected during the syn/syn-ack process */
92 static wmem_map_t *dcerpc_req_pkt_type; /* used to indicate if a DCE-RPC pkt_type is a request */
94 static wmem_map_t *dcerpc_streams = NULL; /* used to record TCP stream numbers that are carrying DCE-RPC data */
97 This array contains calls and returns that have no TRUE context_id
98 This is needed to overcome an apparent bug in Wireshark where
99 the field name of context id in parameters is the same as context id
102 static wmem_map_t *dcerpc_context_zero;
105 The rrpd_list holds information about all of the APDU Request-Response Pairs seen in the trace.
107 static wmem_list_t *rrpd_list = NULL;
110 output_rrpd is a hash of pointers to RRPDs on the rrpd_list. The index is the frame number. This hash is
111 used during Wireshark's second scan. As each packet is processed, TRANSUM uses the packet's frame number to index into
112 this hash to determine if we have RTE data for this particular packet, and if so the write_rte function is called.
114 static wmem_map_t *output_rrpd;
117 The temp_rsp_rrpd_list holds RRPDs for APDUs where we have not yet seen the header information and so we can't
118 fully qualify the identification of the RRPD (the identification being ip_proto:stream_no:session_id:msg_id).
119 This only occurs when a) we are using one of the decode_based calculations (such as SMB2), and b) when we have
120 TCP Reassembly enabled. Once we receive a header packet for an APDU we migrate the entry from this array to the
123 static wmem_list_t *temp_rsp_rrpd_list = NULL; /* Reuse these for speed and efficient memory use - issue a warning if we run out */
125 /* Optimisation data - the following is used for various optimisation measures */
126 static int highest_tcp_stream_no;
127 static int highest_udp_stream_no;
128 wmem_map_t *tcp_stream_exceptions;
131 static gint ett_transum = -1;
132 static gint ett_transum_header = -1;
133 static gint ett_transum_data = -1;
135 static int proto_transum = -1;
137 static int hf_tsum_status = -1;
138 //static int hf_tsum_time_units = -1;
139 static int hf_tsum_req_first_seg = -1;
140 static int hf_tsum_req_last_seg = -1;
141 static int hf_tsum_rsp_first_seg = -1;
142 static int hf_tsum_rsp_last_seg = -1;
143 static int hf_tsum_apdu_rsp_time = -1;
144 static int hf_tsum_service_time = -1;
145 static int hf_tsum_req_spread = -1;
146 static int hf_tsum_rsp_spread = -1;
147 static int hf_tsum_clip_filter = -1;
148 static int hf_tsum_calculation = -1;
149 static int hf_tsum_summary = -1;
150 static int hf_tsum_req_search = -1;
151 static int hf_tsum_rsp_search = -1;
153 static const enum_val_t capture_position_vals[] = {
154 { "TRACE_CAP_CLIENT", "Client", TRACE_CAP_CLIENT },
155 { "TRACE_CAP_INTERMEDIATE", "Intermediate", TRACE_CAP_INTERMEDIATE },
156 { "TRACE_CAP_SERVICE", "Service", TRACE_CAP_SERVICE },
160 static const value_string rrdp_calculation_vals[] = {
161 { RTE_CALC_GTCP, "Generic TCP" },
162 { RTE_CALC_SYN, "SYN and SYN/ACK" },
163 { RTE_CALC_DCERPC, "DCE-RPC" },
164 { RTE_CALC_SMB2, "SMB2" },
165 { RTE_CALC_GUDP, "Generic UDP" },
166 { RTE_CALC_DNS, "DNS" },
171 /*static const enum_val_t time_multiplier_vals[] = {
172 { "RTE_TIME_SEC", "seconds", RTE_TIME_SEC },
173 { "RTE_TIME_MSEC", "milliseconds", RTE_TIME_MSEC },
174 { "RTE_TIME_USEC", "microseconds", RTE_TIME_USEC },
178 void add_detected_tcp_svc(guint16 port)
180 wmem_map_insert(detected_tcp_svc, GUINT_TO_POINTER(port), GUINT_TO_POINTER(port));
184 static void init_dcerpc_data(void)
186 wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(0), GUINT_TO_POINTER(1));
187 wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(11), GUINT_TO_POINTER(1));
188 wmem_map_insert(dcerpc_req_pkt_type, GUINT_TO_POINTER(14), GUINT_TO_POINTER(1));
190 wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(11), GUINT_TO_POINTER(11));
191 wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(12), GUINT_TO_POINTER(12));
192 wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(14), GUINT_TO_POINTER(14));
193 wmem_map_insert(dcerpc_context_zero, GUINT_TO_POINTER(15), GUINT_TO_POINTER(15));
196 static void register_dcerpc_stream(guint32 stream_no)
198 wmem_map_insert(dcerpc_streams, GUINT_TO_POINTER(stream_no), GUINT_TO_POINTER(1));
201 /* This function should be called before any change to RTE data. */
202 static void null_output_rrpd_entries(RRPD *in_rrpd)
204 wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_first_frame));
205 wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_last_frame));
206 wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_first_frame));
207 wmem_map_remove(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_last_frame));
210 /* This function should be called after any change to RTE data. */
211 static void update_output_rrpd(RRPD *in_rrpd)
213 if (preferences.rte_on_first_req)
214 wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_first_frame), in_rrpd);
216 if (preferences.rte_on_last_req)
217 wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->req_last_frame), in_rrpd);
219 if (preferences.rte_on_first_rsp)
220 wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_first_frame), in_rrpd);
222 if (preferences.rte_on_last_rsp)
223 wmem_map_insert(output_rrpd, GUINT_TO_POINTER(in_rrpd->rsp_last_frame), in_rrpd);
226 /* Return the index of the RRPD that has been appended */
227 static RRPD* append_to_rrpd_list(RRPD *in_rrpd)
229 RRPD *next_rrpd = (RRPD*)wmem_memdup(wmem_file_scope(), in_rrpd, sizeof(RRPD));
231 update_output_rrpd(next_rrpd);
233 wmem_list_append(rrpd_list, next_rrpd);
238 static RRPD *find_latest_rrpd_dcerpc(RRPD *in_rrpd)
241 wmem_list_frame_t* i;
243 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
245 rrpd = (RRPD*)wmem_list_frame_data(i);
247 if (rrpd->calculation != RTE_CALC_DCERPC && rrpd->calculation != RTE_CALC_SYN)
250 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
251 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
253 /* if we can match on session_id and msg_id must be a retransmission of the last request packet or the response */
254 /* this logic works whether or not we are using reassembly */
255 if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
258 /* If this is a retransmission, we assume it relates to this rrpd_list entry.
259 This is a bit of a kludge and not ideal but a compromise.*/
260 /* ToDo: look at using TCP sequence number to allocate a retransmission to the correct APDU */
261 if (in_rrpd->is_retrans)
264 if (preferences.reassembly)
268 /* if the input rrpd is for c2s and the one we have found already has response information, then the
269 in_rrpd represents a new RR Pair. */
270 if (rrpd->rsp_first_frame)
273 /* 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. */
277 else /* The in_rrpd relates to a packet going s2c */
279 /* When reassembly is enabled, multi-packet response information is actually migrated from the temp_rsp_rrpd_list
280 to the rrpd_list and so we won't come through here. */
284 else /* we are not using reassembly */
289 /* if we have a message id this is a new Request APDU */
293 return rrpd; /* add this packet to the matching stream */
296 else /* this packet is going s2c */
298 if (!in_rrpd->msg_id && rrpd->rsp_first_frame)
299 /* we need to add this frame to the response APDU of the most recent rrpd_list entry that has already had response packets */
303 } /* this is the end of the 5-tuple check */
306 in_rrpd->req_search_total++;
308 in_rrpd->rsp_search_total++;
309 } /* end of the for loop */
314 static RRPD *find_latest_rrpd_dns(RRPD *in_rrpd)
317 wmem_list_frame_t* i;
319 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
321 rrpd = (RRPD*)wmem_list_frame_data(i);
323 if (rrpd->calculation != RTE_CALC_DNS)
326 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
327 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
329 if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
331 if (in_rrpd->c2s && rrpd->rsp_first_frame)
332 return NULL; /* this is new */
336 } /* this is the end of the 5-tuple check */
339 in_rrpd->req_search_total++;
341 in_rrpd->rsp_search_total++;
342 } /* this is the end of the for loop */
347 static RRPD *find_latest_rrpd_gtcp(RRPD *in_rrpd)
350 wmem_list_frame_t* i;
352 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
354 rrpd = (RRPD*)wmem_list_frame_data(i);
356 if (rrpd->calculation != RTE_CALC_GTCP && rrpd->calculation != RTE_CALC_SYN)
359 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
360 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
362 if (in_rrpd->c2s && rrpd->rsp_first_frame)
363 return NULL; /* this is new */
366 } /* this is the end of the 5-tuple check */
369 in_rrpd->req_search_total++;
371 in_rrpd->rsp_search_total++;
372 } /* this is the end of the for loop */
377 static RRPD *find_latest_rrpd_gudp(RRPD *in_rrpd)
380 wmem_list_frame_t* i;
382 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
384 rrpd = (RRPD*)wmem_list_frame_data(i);
386 if (rrpd->calculation != RTE_CALC_GUDP)
389 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
390 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
392 if (in_rrpd->c2s && rrpd->rsp_first_frame)
393 return NULL; /* this is new */
396 } /* this is the end of the 5-tuple check */
399 in_rrpd->req_search_total++;
401 in_rrpd->rsp_search_total++;
402 } /* this is the end of the for loop */
407 static RRPD *find_latest_rrpd_smb2(RRPD *in_rrpd)
410 wmem_list_frame_t* i;
412 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
414 rrpd = (RRPD*)wmem_list_frame_data(i);
416 if (rrpd->calculation != RTE_CALC_SMB2 && rrpd->calculation != RTE_CALC_SYN)
419 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
420 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
422 /* if we can match on session_id and msg_id must be a retransmission of the last request packet or the response */
423 /* this logic works whether or not we are using reassembly */
424 if (rrpd->session_id == in_rrpd->session_id && rrpd->msg_id == in_rrpd->msg_id)
427 /* If this is a retransmission, we assume it relates to this rrpd_list entry.
428 This is a bit of a kludge and not ideal but a compromise.*/
429 /* ToDo: look at using TCP sequence number to allocate a retransmission to the correct APDU */
430 if (in_rrpd->is_retrans)
433 if (preferences.reassembly)
437 /* if the input rrpd is for c2s and the one we have found already has response information, then the
438 in_rrpd represents a new RR Pair. */
439 if (rrpd->rsp_first_frame)
442 /* 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. */
446 else /* The in_rrpd relates to a packet going s2c */
448 /* When reassembly is enabled, multi-packet response information is actually migrated from the temp_rsp_rrpd_list
449 to the rrpd_list and so we won't come through here. */
453 else /* we are not using reassembly */
458 /* if we have a message id this is a new Request APDU */
462 return rrpd; /* add this packet to the matching stream */
465 else /* this packet is going s2c */
467 if (!in_rrpd->msg_id && rrpd->rsp_first_frame)
468 /* we need to add this frame to the response APDU of the most recent rrpd_list entry that has already had response packets */
472 } /* this is the end of the 5-tuple check */
475 in_rrpd->req_search_total++;
477 in_rrpd->rsp_search_total++;
478 } /* end of the for loop */
483 static RRPD *find_latest_rrpd_syn(RRPD *in_rrpd)
486 wmem_list_frame_t* i;
488 for (i = wmem_list_tail(rrpd_list); i != NULL; i = wmem_list_frame_prev(i))
490 rrpd = (RRPD*)wmem_list_frame_data(i);
492 if (rrpd->calculation != RTE_CALC_SYN)
495 /* if the input 5-tuple doesn't match the rrpd_list_entry 5-tuple -> go find the next list entry */
496 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
499 } /* this is the end of the 5-tuple check */
502 in_rrpd->req_search_total++;
504 in_rrpd->rsp_search_total++;
505 } /* this is the end of the for loop */
510 static RRPD *find_latest_rrpd(RRPD *in_rrpd)
512 /* Optimisation Code */
513 if (in_rrpd->ip_proto == IP_PROTO_TCP && (int)in_rrpd->stream_no > highest_tcp_stream_no)
515 highest_tcp_stream_no = in_rrpd->stream_no;
518 else if (in_rrpd->ip_proto == IP_PROTO_UDP && (int)in_rrpd->stream_no > highest_udp_stream_no)
520 highest_udp_stream_no = in_rrpd->stream_no;
523 /* End of Optimisation Code */
525 switch (in_rrpd->calculation)
527 case RTE_CALC_DCERPC:
528 return find_latest_rrpd_dcerpc(in_rrpd);
532 return find_latest_rrpd_dns(in_rrpd);
536 return find_latest_rrpd_gtcp(in_rrpd);
540 return find_latest_rrpd_gudp(in_rrpd);
544 return find_latest_rrpd_smb2(in_rrpd);
548 return find_latest_rrpd_syn(in_rrpd);
555 static void update_rrpd_list_entry(RRPD *match, RRPD *in_rrpd)
557 null_output_rrpd_entries(match);
559 if (preferences.debug_enabled)
561 match->req_search_total += in_rrpd->req_search_total;
562 match->rsp_search_total += in_rrpd->rsp_search_total;
567 match->req_last_frame = in_rrpd->req_last_frame;
568 match->req_last_rtime = in_rrpd->req_last_rtime;
571 match->session_id = in_rrpd->session_id;
572 match->msg_id = in_rrpd->msg_id;
577 if (!match->rsp_first_frame)
579 match->rsp_first_frame = in_rrpd->rsp_first_frame;
580 match->rsp_first_rtime = in_rrpd->rsp_first_rtime;
582 match->rsp_last_frame = in_rrpd->rsp_last_frame;
583 match->rsp_last_rtime = in_rrpd->rsp_last_rtime;
586 update_output_rrpd(match);
590 This function processes a sub-packet that is going from client-to-service.
592 static void update_rrpd_list_entry_req(RRPD *in_rrpd)
596 match = find_latest_rrpd(in_rrpd);
599 update_rrpd_list_entry(match, in_rrpd);
601 append_to_rrpd_list(in_rrpd);
605 This function inserts an RRPD into the temp_rsp_rrpd_list. If this is
606 successful return a pointer to the entry, else return NULL.
608 static RRPD* insert_into_temp_rsp_rrpd_list(RRPD *in_rrpd)
610 RRPD *rrpd = (RRPD*)wmem_memdup(wmem_file_scope(), in_rrpd, sizeof(RRPD));
612 wmem_list_append(temp_rsp_rrpd_list, rrpd);
617 static RRPD* find_temp_rsp_rrpd(RRPD *in_rrpd)
619 wmem_list_frame_t *i;
622 for (i = wmem_list_head(temp_rsp_rrpd_list); i; i = wmem_list_frame_next(i))
624 rrpd = (RRPD*)wmem_list_frame_data(i);
625 if (rrpd->ip_proto == in_rrpd->ip_proto && rrpd->stream_no == in_rrpd->stream_no)
632 static void update_temp_rsp_rrpd(RRPD *temp_list, RRPD *in_rrpd)
634 temp_list->rsp_last_frame = in_rrpd->rsp_last_frame;
635 temp_list->rsp_last_rtime = in_rrpd->rsp_last_rtime;
638 /* This function migrates an entry from the temp_rsp_rrpd_list to the main rrpd_list. */
639 static void migrate_temp_rsp_rrpd(RRPD *main_list, RRPD *temp_list)
641 update_rrpd_list_entry(main_list, temp_list);
643 wmem_list_remove(temp_rsp_rrpd_list, temp_list);
646 static void update_rrpd_list_entry_rsp(RRPD *in_rrpd)
648 RRPD *match, *temp_list;
650 if (in_rrpd->decode_based)
652 if (preferences.reassembly)
656 /* If we have a msg_id in the input RRPD we must have header information. */
657 temp_list = find_temp_rsp_rrpd(in_rrpd);
659 if (temp_list != NULL)
661 update_temp_rsp_rrpd(temp_list, in_rrpd);
663 /* Migrate the temp_rsp_rrpd_list entry to the main rrpd_list */
664 match = find_latest_rrpd(in_rrpd);
666 migrate_temp_rsp_rrpd(match, temp_list);
670 match = find_latest_rrpd(in_rrpd);
671 /* There isn't an entry in the temp_rsp_rrpd_list so update the master rrpd_list entry */
673 update_rrpd_list_entry(match, in_rrpd);
678 /* Update an existing entry to the temp_rsp_rrpd_list or add a new one. */
679 temp_list = find_temp_rsp_rrpd(in_rrpd);
681 if (temp_list != NULL)
682 update_temp_rsp_rrpd(temp_list, in_rrpd);
685 /* If this is a retransmission we need to add it to the last completed rrpd_list entry for this stream */
686 if (in_rrpd->is_retrans)
688 match = find_latest_rrpd(in_rrpd);
691 update_rrpd_list_entry(match, in_rrpd);
693 insert_into_temp_rsp_rrpd_list(in_rrpd);
696 /* As it's not a retransmission, just create a new entry on the temp list */
697 insert_into_temp_rsp_rrpd_list(in_rrpd);
703 /* Reassembly isn't set and so just go ahead and use the list function */
704 match = find_latest_rrpd(in_rrpd);
706 update_rrpd_list_entry(match, in_rrpd);
711 /* if this isn't decode_based then just go ahead and update the RTE data */
712 match = find_latest_rrpd(in_rrpd);
714 update_rrpd_list_entry(match, in_rrpd);
722 This function updates the RTE data of an RRPD on the rrpd_list. The
723 frame_no values in the input RRPD double up as a mask. If the frame_no
724 is > 0 then the frame_no value and rtime values are updated. If the
725 frame_no is 0 then that particular frame_no and rtime value is not updated.
727 static void update_rrpd_rte_data(RRPD *in_rrpd)
730 update_rrpd_list_entry_req(in_rrpd);
732 update_rrpd_list_entry_rsp(in_rrpd);
735 gboolean is_dcerpc_context_zero(guint32 pkt_type)
737 return (wmem_map_lookup(dcerpc_context_zero, GUINT_TO_POINTER(pkt_type)) != NULL);
740 gboolean is_dcerpc_req_pkt_type(guint32 pkt_type)
742 return (wmem_map_lookup(dcerpc_req_pkt_type, GUINT_TO_POINTER(pkt_type)) != NULL);
745 static gboolean is_dcerpc_stream(guint32 stream_no)
747 return (wmem_map_lookup(dcerpc_streams, GUINT_TO_POINTER(stream_no)) != NULL);
751 This function initialises the global variables and populates the
752 [tcp|udp]_svc_ports tables with information from the preference settings
754 static void init_globals(void)
756 if (!proto_is_protocol_enabled(find_protocol_by_id(proto_transum)))
759 highest_tcp_stream_no = -1;
760 highest_udp_stream_no = -1;
762 /* Create and initialise some dynamic memory areas */
763 tcp_stream_exceptions = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
764 detected_tcp_svc = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
765 rrpd_list = wmem_list_new(wmem_file_scope());
766 temp_rsp_rrpd_list = wmem_list_new(wmem_file_scope());
768 /* Indicate what fields we're interested in. */
769 GArray *wanted_fields = g_array_sized_new(FALSE, FALSE, (guint)sizeof(int), HF_INTEREST_END_OF_LIST);
770 for (int i = 0; i < HF_INTEREST_END_OF_LIST; i++)
772 if (hf_of_interest[i].hf != -1)
773 g_array_append_val(wanted_fields, hf_of_interest[i].hf);
775 g_warning("TRANSUM: unknown field %s", hf_of_interest[i].proto_name);
777 set_postdissector_wanted_hfids(transum_handle, wanted_fields);
779 preferences.tcp_svc_ports = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
780 preferences.udp_svc_ports = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
782 /* use the range values to populate the tcp_svc_ports list*/
783 for (guint i = 0; i < tcp_svc_port_range_values->nranges; i++)
785 for (guint32 j = tcp_svc_port_range_values->ranges[i].low; j <= tcp_svc_port_range_values->ranges[i].high; j++)
787 wmem_map_insert(preferences.tcp_svc_ports, GUINT_TO_POINTER(j), GUINT_TO_POINTER(RTE_CALC_GTCP));
791 /* use the range values to populate the udp_svc_ports list*/
792 for (guint i = 0; i < udp_svc_port_range_values->nranges; i++)
794 for (guint32 j = udp_svc_port_range_values->ranges[i].low; j <= udp_svc_port_range_values->ranges[i].high; j++)
796 wmem_map_insert(preferences.udp_svc_ports, GUINT_TO_POINTER(j), GUINT_TO_POINTER(RTE_CALC_GUDP));
800 /* create arrays to hold some DCE-RPC values */
801 dcerpc_context_zero = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
802 dcerpc_req_pkt_type = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
803 dcerpc_streams = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
806 wmem_map_insert(preferences.tcp_svc_ports, GUINT_TO_POINTER(445), GUINT_TO_POINTER(RTE_CALC_SMB2));
807 wmem_map_insert(preferences.udp_svc_ports, GUINT_TO_POINTER(53), GUINT_TO_POINTER(RTE_CALC_DNS));
810 /* Undo capture file-specific initializations. */
811 static void cleanup_globals(void)
813 /* Clear the list of wanted fields as it will be reinitialized. */
814 set_postdissector_wanted_hfids(transum_handle, NULL);
817 /* This function adds the RTE data to the tree. The summary ptr is currently
818 not used but will be used for summariser information once this feature has
819 been ported from the LUA code. */
820 static void write_rte(RRPD *in_rrpd, tvbuff_t *tvb, proto_tree *tree, char *summary)
824 nstime_t rte_reqspread;
825 nstime_t rte_rspspread;
826 proto_tree *rte_tree;
828 wmem_strbuf_t *temp_string = wmem_strbuf_new(wmem_packet_scope(), "");
830 if (in_rrpd->req_first_frame)
832 pi = proto_tree_add_item(tree, proto_transum, tvb, 0, -1, ENC_NA);
833 rte_tree = proto_item_add_subtree(pi, ett_transum);
835 nstime_delta(&rte_reqspread, &(in_rrpd->req_last_rtime), &(in_rrpd->req_first_rtime));
836 if (in_rrpd->rsp_first_frame)
838 /* calculate the RTE times */
839 nstime_delta(&rte_art, &(in_rrpd->rsp_last_rtime), &(in_rrpd->req_first_rtime));
840 nstime_delta(&rte_st, &(in_rrpd->rsp_first_rtime), &(in_rrpd->req_last_rtime));
841 nstime_delta(&rte_rspspread, &(in_rrpd->rsp_last_rtime), &(in_rrpd->rsp_first_rtime));
843 pi = proto_tree_add_string(rte_tree, hf_tsum_status, tvb, 0, 0, "OK");
847 pi = proto_tree_add_string(rte_tree, hf_tsum_status, tvb, 0, 0, "Response missing");
849 proto_item_set_generated(pi);
852 pi = proto_tree_add_uint(rte_tree, hf_tsum_req_first_seg, tvb, 0, 0, in_rrpd->req_first_frame);
853 proto_item_set_generated(pi);
854 pi = proto_tree_add_uint(rte_tree, hf_tsum_req_last_seg, tvb, 0, 0, in_rrpd->req_last_frame);
855 proto_item_set_generated(pi);
857 if (in_rrpd->rsp_first_frame)
859 pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_first_seg, tvb, 0, 0, in_rrpd->rsp_first_frame);
860 proto_item_set_generated(pi);
861 pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_last_seg, tvb, 0, 0, in_rrpd->rsp_last_frame);
862 proto_item_set_generated(pi);
864 pi = proto_tree_add_time(rte_tree, hf_tsum_apdu_rsp_time, tvb, 0, 0, &rte_art);
865 proto_item_set_generated(pi);
866 pi = proto_tree_add_time(rte_tree, hf_tsum_service_time, tvb, 0, 0, &rte_st);
867 proto_item_set_generated(pi);
870 pi = proto_tree_add_time(rte_tree, hf_tsum_req_spread, tvb, 0, 0, &rte_reqspread);
871 proto_item_set_generated(pi);
873 if (in_rrpd->rsp_first_frame)
875 pi = proto_tree_add_time(rte_tree, hf_tsum_rsp_spread, tvb, 0, 0, &rte_rspspread);
876 proto_item_set_generated(pi);
879 if (in_rrpd->ip_proto == IP_PROTO_TCP)
880 wmem_strbuf_append_printf(temp_string, "tcp.stream==%d", in_rrpd->stream_no);
881 else if (in_rrpd->ip_proto == IP_PROTO_UDP)
882 wmem_strbuf_append_printf(temp_string, "udp.stream==%d", in_rrpd->stream_no);
884 if (in_rrpd->rsp_first_frame)
885 wmem_strbuf_append_printf(temp_string, " && frame.number>=%d && frame.number<=%d", in_rrpd->req_first_frame, in_rrpd->rsp_last_frame);
887 wmem_strbuf_append_printf(temp_string, " && frame.number>=%d && frame.number<=%d", in_rrpd->req_first_frame, in_rrpd->req_last_frame);
889 if (in_rrpd->calculation == RTE_CALC_GTCP)
890 wmem_strbuf_append_printf(temp_string, " && tcp.len>0");
892 pi = proto_tree_add_string(rte_tree, hf_tsum_clip_filter, tvb, 0, 0, wmem_strbuf_get_str(temp_string));
893 proto_item_set_generated(pi);
895 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"));
896 proto_item_set_generated(pi);
898 if (in_rrpd->rsp_first_frame)
900 if (preferences.summarisers_enabled)
904 pi = proto_tree_add_string(tree, hf_tsum_summary, tvb, 0, 0, summary);
905 proto_item_set_generated(pi);
910 if (preferences.debug_enabled)
912 pi = proto_tree_add_uint(rte_tree, hf_tsum_req_search, tvb, 0, 0, in_rrpd->req_search_total);
913 proto_item_set_generated(pi);
914 pi = proto_tree_add_uint(rte_tree, hf_tsum_rsp_search, tvb, 0, 0, in_rrpd->rsp_search_total);
915 proto_item_set_generated(pi);
921 This function sets initial values in the current_pkt structure and checks
922 the xxx_svc_port arrays to see if they contain a match for the source or
923 destination port. This function also adds tcp_svc_ports entries when it
924 discovers DCE-RPC traffic.
926 Returns the number of sub-packets to be processed.
928 static void set_proto_values(packet_info *pinfo, proto_tree *tree, PKT_INFO* pkt_info, PKT_INFO* subpackets)
930 guint32 field_uint[MAX_RETURNED_ELEMENTS]; /* An extracted field array for unsigned integers */
931 size_t field_value_count; /* How many entries are there in the extracted field array */
933 pkt_info->frame_number = pinfo->fd->num; /* easy access to frame number */
934 pkt_info->relative_time = pinfo->rel_ts;
936 int number_sub_pkts_of_interest = 0; /* default */
938 if (pinfo->ptype == PT_TCP)
939 pkt_info->rrpd.ip_proto = IP_PROTO_TCP;
940 else if (pinfo->ptype == PT_UDP)
941 pkt_info->rrpd.ip_proto = IP_PROTO_UDP;
943 if (pkt_info->rrpd.ip_proto == IP_PROTO_TCP)
945 number_sub_pkts_of_interest = decode_gtcp(pinfo, tree, pkt_info);
946 /* decode_gtcp may return 0 but we need to keep processing because we
947 calculate RTE figures for all SYNs and also we may detect DCE-RPC later
948 (even though we don't currently have an entry in the tcp_svc_ports list). */
950 /* Optimisation code */
951 if (pkt_info->len || pkt_info->tcp_flags_syn)
953 if (pkt_info->ssl_content_type == 21) /* this is an SSL Alert */
955 pkt_info->pkt_of_interest = FALSE;
959 if ((int)pkt_info->rrpd.stream_no > highest_tcp_stream_no && !pkt_info->rrpd.c2s)
961 /* first packet on the stream is s2c and so add to exception list */
962 if (wmem_map_lookup(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no)) == NULL)
963 wmem_map_insert(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no), GUINT_TO_POINTER(1));
966 if (wmem_map_lookup(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no)) != NULL)
968 if (pkt_info->rrpd.c2s)
969 wmem_map_remove(tcp_stream_exceptions, GUINT_TO_POINTER(pkt_info->rrpd.stream_no));
971 pkt_info->pkt_of_interest = FALSE;
974 /* End of Optimisation Code */
976 if (pkt_info->tcp_retran)
978 /* we may not want to continue with this packet if it's a retransmission */
980 /* If this is a server-side trace we need to ignore client-to-service TCP retransmissions
981 the rationale being that if we saw the original in the trace the service process saw it too */
982 if (pkt_info->rrpd.c2s && preferences.capture_position == CAPTURE_SERVICE)
984 pkt_info->pkt_of_interest = FALSE;
988 /* If this is a client-side trace we need to ignore service-to-client TCP retransmissions
989 the rationale being that if we saw the original in the trace the client process saw it too */
990 else if (!pkt_info->rrpd.c2s && preferences.capture_position == CAPTURE_CLIENT)
992 pkt_info->pkt_of_interest = FALSE;
997 /* We are not interested in TCP Keep-Alive */
998 if (pkt_info->tcp_keep_alive)
1000 pkt_info->pkt_of_interest = FALSE;
1004 if (pkt_info->len == 1)
1006 if (preferences.orphan_ka_discard && pkt_info->tcp_flags_ack && pkt_info->rrpd.c2s)
1008 pkt_info->pkt_of_interest = FALSE;
1009 return; /* It's a KEEP-ALIVE -> stop processing this packet */
1014 if (pkt_info->tcp_flags_syn)
1015 number_sub_pkts_of_interest = decode_syn(pinfo, tree, pkt_info);
1017 if (pkt_info->len > 0)
1020 if (pkt_info->dstport == 445 || pkt_info->srcport == 445)
1021 number_sub_pkts_of_interest = decode_smb(pinfo, tree, pkt_info, subpackets);
1025 /* check if DCE-RPC */
1026 /* We need to set RTE_CALC_DCERPC even when we don't have header info. */
1027 if (is_dcerpc_stream(pkt_info->rrpd.stream_no))
1029 pkt_info->rrpd.calculation = RTE_CALC_DCERPC;
1030 pkt_info->rrpd.decode_based = TRUE;
1031 pkt_info->pkt_of_interest = TRUE;
1034 if (!extract_uint(tree, hf_of_interest[HF_INTEREST_DCERPC_VER].hf, field_uint, &field_value_count))
1036 if (field_value_count)
1038 if (pkt_info->rrpd.calculation != RTE_CALC_DCERPC)
1039 register_dcerpc_stream(pkt_info->rrpd.stream_no);
1041 number_sub_pkts_of_interest = decode_dcerpc(pinfo, tree, pkt_info);
1048 else if (pkt_info->rrpd.ip_proto == IP_PROTO_UDP)
1051 number_sub_pkts_of_interest = decode_gudp(pinfo, tree, pkt_info);
1053 if (pkt_info->srcport == 53 || pkt_info->dstport == 53)
1054 number_sub_pkts_of_interest = decode_dns(pinfo, tree, pkt_info);
1057 /* Set appropriate RTE values in the sub-packets */
1058 for (int i = 0; (i < number_sub_pkts_of_interest) && (i < MAX_SUBPKTS_PER_PACKET); i++)
1060 if (pkt_info->rrpd.c2s)
1062 subpackets[i].rrpd.req_first_frame = pkt_info->frame_number;
1063 subpackets[i].rrpd.req_first_rtime = pkt_info->relative_time;
1064 subpackets[i].rrpd.req_last_frame = pkt_info->frame_number;
1065 subpackets[i].rrpd.req_last_rtime = pkt_info->relative_time;
1067 subpackets[i].frame_number = pkt_info->frame_number; /* this acts as a switch later */
1071 subpackets[i].rrpd.rsp_first_frame = pkt_info->frame_number;
1072 subpackets[i].rrpd.rsp_first_rtime = pkt_info->relative_time;
1073 subpackets[i].rrpd.rsp_last_frame = pkt_info->frame_number;
1074 subpackets[i].rrpd.rsp_last_rtime = pkt_info->relative_time;
1076 subpackets[i].frame_number = pkt_info->frame_number; /* this acts as a switch later */
1083 * This function is called for each packet
1084 * Wireshark scans all the packets once and then once again as they are displayed
1085 * The pinfo.visited boolean is set to FALSE; on the first scan
1087 static int dissect_transum(tvbuff_t *buffer, packet_info *pinfo, proto_tree *tree, void *data _U_)
1089 /* if (there is RTE info associated with this packet we need to output it */
1090 if (PINFO_FD_VISITED(pinfo))
1092 RRPD *rrpd = (RRPD*)wmem_map_lookup(output_rrpd, GUINT_TO_POINTER(pinfo->num));
1098 /* Add the RTE data to the protocol decode tree if we output_flag is set */
1099 write_rte(rrpd, buffer, tree, NULL);
1105 PKT_INFO *sub_packet = wmem_alloc0_array(wmem_packet_scope(), PKT_INFO, MAX_SUBPKTS_PER_PACKET);
1107 set_proto_values(pinfo, tree, &sub_packet[0], sub_packet);
1109 if (sub_packet[0].pkt_of_interest)
1111 /* Loop to process each sub_packet and update the related RTE data */
1112 for (int i = 0; i < MAX_SUBPKTS_PER_PACKET; i++)
1114 if (!sub_packet[i].frame_number)
1117 update_rrpd_rte_data(&(sub_packet[i].rrpd));
1126 proto_register_transum(void)
1128 module_t *transum_module;
1130 static hf_register_info hf[] = {
1132 { "RTE Status", "transum.status",
1133 FT_STRING, BASE_NONE, NULL, 0x0,
1134 "Indication of completeness of the RTE information", HFILL } },
1136 { &hf_tsum_time_units,
1137 { "RTE Time Units", "transum.time_units",
1138 FT_STRING, BASE_NONE, NULL, 0x0,
1139 "Time units used (s, ms or us) for the RTE values", HFILL }
1142 { &hf_tsum_req_first_seg,
1143 { "Req First Seg", "transum.firstreq",
1144 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1145 "First Segment of an APDU Request", HFILL }
1148 { &hf_tsum_req_last_seg,
1149 { "Req Last Seg", "transum.lastreq",
1150 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1151 "Last Segment of an APDU Request", HFILL }
1154 { &hf_tsum_rsp_first_seg,
1155 { "Rsp First Seg", "transum.firstrsp",
1156 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1157 "First Segment of an APDU Response", HFILL }
1160 { &hf_tsum_rsp_last_seg,
1161 { "Rsp Last Seg", "transum.lastrsp",
1162 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1163 "Last Segment of an APDU Response", HFILL }
1166 { &hf_tsum_apdu_rsp_time,
1167 { "APDU Rsp Time", "transum.art",
1168 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1169 "RTE APDU Response Time", HFILL }
1172 { &hf_tsum_service_time,
1173 { "Service Time", "transum.st",
1174 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1175 "RTE Service Time", HFILL }
1178 { &hf_tsum_req_spread,
1179 { "Req Spread", "transum.reqspread",
1180 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1181 "RTE Request Spread", HFILL }
1184 { &hf_tsum_rsp_spread,
1185 { "Rsp Spread", "transum.rspspread",
1186 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1187 "RTE Response Spread", HFILL }
1190 { &hf_tsum_clip_filter,
1191 { "Trace clip filter", "transum.clip_filter",
1192 FT_STRING, BASE_NONE, NULL, 0x0,
1193 "Filter expression to select the APDU Reqest-Response pair", HFILL }
1196 { &hf_tsum_calculation,
1197 { "Calculation", "transum.calculation",
1198 FT_STRING, BASE_NONE, NULL, 0x0,
1199 "Basis of the RTE calculation", HFILL }
1203 { "Summary", "transum.summary",
1204 FT_STRING, BASE_NONE, NULL, 0x0,
1205 "Summarizer information", HFILL }
1208 { &hf_tsum_req_search,
1209 { "Req Search Count", "transum.req_search",
1210 FT_UINT32, BASE_DEC, NULL, 0x0,
1211 "rrpd_list search total for the request packets", HFILL }
1214 { &hf_tsum_rsp_search,
1215 { "Rsp Search Counts", "transum.rsp_search",
1216 FT_UINT32, BASE_DEC, NULL, 0x0,
1217 "rrpd_list search total for the response packets", HFILL }
1222 /* Setup protocol subtree array */
1223 static gint *ett[] = {
1225 &ett_transum_header,
1229 proto_transum = proto_register_protocol("TRANSUM RTE Data", "TRANSUM", "transum");
1231 /* Due to performance concerns of the dissector, it's disabled by default */
1232 proto_disable_by_default(proto_transum);
1235 /* Set User Preferences defaults */
1236 preferences.capture_position = TRACE_CAP_CLIENT;
1237 preferences.reassembly = TRUE;
1239 range_convert_str(wmem_epan_scope(), &tcp_svc_port_range_values, "25, 80, 443, 1433", MAX_TCP_PORT);
1240 range_convert_str(wmem_epan_scope(), &udp_svc_port_range_values, "137-139", MAX_UDP_PORT);
1242 preferences.orphan_ka_discard = FALSE;
1243 preferences.time_multiplier = RTE_TIME_SEC;
1244 preferences.rte_on_first_req = FALSE;
1245 preferences.rte_on_last_req = TRUE;
1246 preferences.rte_on_first_rsp = FALSE;
1247 preferences.rte_on_last_rsp = FALSE;
1249 preferences.debug_enabled = FALSE;
1251 /* no start registering stuff */
1252 proto_register_field_array(proto_transum, hf, array_length(hf));
1253 proto_register_subtree_array(ett, array_length(ett));
1255 transum_module = prefs_register_protocol(proto_transum, NULL); /* ToDo: We need to rethink the NULL pointer so that a preference change causes a rescan */
1257 /* Register the preferences */
1258 prefs_register_obsolete_preference(transum_module, "tsumenabled");
1260 prefs_register_enum_preference(transum_module,
1263 "Position of the capture unit that produced this trace. This setting affects the way TRANSUM handles TCP Retransmissions. See the manual for details.",
1264 &preferences.capture_position,
1265 capture_position_vals,
1268 prefs_register_bool_preference(transum_module,
1270 "Subdissector reassembly enabled",
1271 "Set this to match to the TCP subdissector reassembly setting",
1272 &preferences.reassembly);
1274 prefs_register_range_preference(transum_module,
1276 "Output RTE data for these TCP service ports",
1277 "Add and remove ports numbers separated by commas\nRanges are supported e.g. 25,80,2000-3000,5432",
1278 &tcp_svc_port_range_values,
1281 prefs_register_range_preference(transum_module,
1283 "Output RTE data for these UDP service ports",
1284 "Add and remove ports numbers separated by commas\nRanges are supported e.g. 123,137-139,520-521,2049",
1285 &udp_svc_port_range_values,
1288 prefs_register_bool_preference(transum_module,
1289 "orphan_ka_discard",
1290 "Discard orphaned TCP Keep-Alives",
1291 "Set this to discard any packet in the direction client to service,\nwith a 1-byte payload of 0x00 and the ACK flag set",
1292 &preferences.orphan_ka_discard);
1294 /* removed from this release
1295 prefs_register_enum_preference(transum_module,
1297 "Time units for RTE values",
1298 "Unit of time used for APDU Response Time, Service Time and Spread Time values.",
1299 &preferences.time_multiplier,
1300 time_multiplier_vals,
1304 prefs_register_bool_preference(transum_module,
1306 "Add RTE data to the first request segment",
1307 "RTE data will be added to the first request packet",
1308 &preferences.rte_on_first_req);
1310 prefs_register_bool_preference(transum_module,
1312 "Add RTE data to the last request segment",
1313 "RTE data will be added to the last request packet",
1314 &preferences.rte_on_last_req);
1316 prefs_register_bool_preference(transum_module,
1318 "Add RTE data to the first response segment",
1319 "RTE data will be added to the first response packet",
1320 &preferences.rte_on_first_rsp);
1322 prefs_register_bool_preference(transum_module,
1324 "Add RTE data to the last response segment",
1325 "RTE data will be added to the last response packet",
1326 &preferences.rte_on_last_rsp);
1328 prefs_register_bool_preference(transum_module,
1330 "Enable debug info",
1331 "Set this only to troubleshoot problems",
1332 &preferences.debug_enabled);
1334 transum_handle = register_dissector("transum", dissect_transum, proto_transum);
1336 register_init_routine(init_globals);
1337 register_cleanup_routine(cleanup_globals);
1339 register_postdissector(transum_handle);
1341 output_rrpd = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
1344 void proto_reg_handoff_transum(void)
1346 /* Get the field id for each field we will need */
1347 for (int i = 0; i < HF_INTEREST_END_OF_LIST; i++)
1349 hf_of_interest[i].hf = proto_registrar_get_id_byname(hf_of_interest[i].proto_name);
1354 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1359 * indent-tabs-mode: nil
1362 * vi: set shiftwidth=4 tabstop=8 expandtab:
1363 * :indentSize=4:tabSize=8:noTabs=true: