2 * Flow sequence analysis
4 * Some code from gtk/flow_graph.c
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
14 #define WS_LOG_DOMAIN LOG_DOMAIN_EPAN
16 #include "sequence_analysis.h"
18 #include "addr_resolv.h"
20 #include "color_filters.h"
21 #include <epan/column.h>
23 #include <epan/wmem_scopes.h>
25 #define NODE_OVERFLOW MAX_NUM_NODES+1
27 struct register_analysis
{
28 const char* name
; /* Name (used for lookup) */
29 const char* ui_name
; /* Name used for UI */
30 int proto_id
; /* protocol id (0-indexed) */
31 const char* tap_listen_str
; /* string used in register_tap_listener (NULL to use protocol name) */
33 tap_packet_cb analysis_func
; /* function to be called for new incoming packets for sequence analysis */
36 static wmem_tree_t
*registered_seq_analysis
;
39 register_seq_analysis(const char* name
, const char* ui_name
, const int proto_id
, const char* tap_listener
, unsigned tap_flags
, tap_packet_cb tap_func
)
41 register_analysis_t
* analysis
;
43 DISSECTOR_ASSERT(tap_func
);
45 analysis
= wmem_new0(wmem_epan_scope(), register_analysis_t
);
47 analysis
->name
= name
;
48 analysis
->ui_name
= ui_name
;
49 analysis
->proto_id
= proto_id
;
50 if (tap_listener
!= NULL
)
51 analysis
->tap_listen_str
= tap_listener
;
53 analysis
->tap_listen_str
= proto_get_protocol_filter_name(proto_id
);
54 analysis
->tap_flags
= tap_flags
;
55 analysis
->analysis_func
= tap_func
;
57 if (registered_seq_analysis
== NULL
)
58 registered_seq_analysis
= wmem_tree_new(wmem_epan_scope());
60 wmem_tree_insert_string(registered_seq_analysis
, name
, analysis
, 0);
63 const char* sequence_analysis_get_name(register_analysis_t
* analysis
)
65 return analysis
->name
;
68 const char* sequence_analysis_get_ui_name(register_analysis_t
* analysis
)
70 return analysis
->ui_name
;
73 const char* sequence_analysis_get_tap_listener_name(register_analysis_t
* analysis
)
75 return analysis
->tap_listen_str
;
78 tap_packet_cb
sequence_analysis_get_packet_func(register_analysis_t
* analysis
)
80 return analysis
->analysis_func
;
83 unsigned sequence_analysis_get_tap_flags(register_analysis_t
* analysis
)
85 return analysis
->tap_flags
;
89 register_analysis_t
* sequence_analysis_find_by_name(const char* name
)
91 return (register_analysis_t
*)wmem_tree_lookup_string(registered_seq_analysis
, name
, 0);
94 void sequence_analysis_table_iterate_tables(wmem_foreach_func func
, void *user_data
)
96 wmem_tree_foreach(registered_seq_analysis
, func
, user_data
);
99 seq_analysis_item_t
* sequence_analysis_create_sai_with_addresses(packet_info
*pinfo
, seq_analysis_info_t
*sainfo
)
101 seq_analysis_item_t
*sai
= NULL
;
102 char time_str
[COL_MAX_LEN
];
104 if (!sainfo
->any_addr
) {
105 if (pinfo
->net_src
.type
!=AT_NONE
&& pinfo
->net_dst
.type
!=AT_NONE
) {
106 sai
= g_new0(seq_analysis_item_t
, 1);
107 copy_address(&(sai
->src_addr
),&(pinfo
->net_src
));
108 copy_address(&(sai
->dst_addr
),&(pinfo
->net_dst
));
111 if (pinfo
->src
.type
!=AT_NONE
&& pinfo
->dst
.type
!=AT_NONE
) {
112 sai
= g_new0(seq_analysis_item_t
, 1);
113 copy_address(&(sai
->src_addr
),&(pinfo
->src
));
114 copy_address(&(sai
->dst_addr
),&(pinfo
->dst
));
119 /* Fill in the timestamps */
120 set_fd_time(pinfo
->epan
, pinfo
->fd
, time_str
);
121 sai
->time_str
= g_strdup(time_str
);
127 void sequence_analysis_use_color_filter(packet_info
*pinfo
, seq_analysis_item_t
*sai
)
129 if (pinfo
->fd
->color_filter
) {
130 sai
->bg_color
= color_t_to_rgb(&pinfo
->fd
->color_filter
->bg_color
);
131 sai
->fg_color
= color_t_to_rgb(&pinfo
->fd
->color_filter
->fg_color
);
132 sai
->has_color_filter
= true;
136 void sequence_analysis_use_col_info_as_label_comment(packet_info
*pinfo
, seq_analysis_item_t
*sai
)
138 const char *protocol
= NULL
;
139 const char *colinfo
= NULL
;
142 colinfo
= col_get_text(pinfo
->cinfo
, COL_INFO
);
143 protocol
= col_get_text(pinfo
->cinfo
, COL_PROTOCOL
);
146 if (colinfo
!= NULL
) {
147 sai
->frame_label
= g_strdup(colinfo
);
148 if (protocol
!= NULL
) {
149 sai
->comment
= ws_strdup_printf("%s: %s", protocol
, colinfo
);
151 sai
->comment
= g_strdup(colinfo
);
154 /* This will probably never happen...*/
155 if (protocol
!= NULL
) {
156 sai
->frame_label
= g_strdup(protocol
);
157 sai
->comment
= g_strdup(protocol
);
162 seq_analysis_info_t
*
163 sequence_analysis_info_new(void)
165 seq_analysis_info_t
*sainfo
= g_new0(seq_analysis_info_t
, 1);
167 ws_noisy("adding new item");
168 sainfo
->items
= g_queue_new();
169 sainfo
->ht
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
173 void sequence_analysis_info_free(seq_analysis_info_t
*sainfo
)
177 ws_noisy("%d items", g_queue_get_length(sainfo
->items
));
178 sequence_analysis_list_free(sainfo
);
180 g_queue_free(sainfo
->items
);
181 if (sainfo
->ht
!= NULL
)
182 g_hash_table_destroy(sainfo
->ht
);
187 static void sequence_analysis_item_free(void *data
)
189 seq_analysis_item_t
*seq_item
= (seq_analysis_item_t
*)data
;
190 g_free(seq_item
->frame_label
);
191 g_free(seq_item
->time_str
);
192 g_free(seq_item
->comment
);
193 free_address(&seq_item
->src_addr
);
194 free_address(&seq_item
->dst_addr
);
195 if (seq_item
->info_ptr
) {
197 /* XXX: If seq_item->info_type is GA_INFO_TYPE_RTP, then we need
198 * to free the data, but rtpstream_info_free_* is in libui and
200 if (seq_item
->info_type
== GA_INFO_TYPE_RTP
) {
201 rtpstream_info_free_data((rtpstream_info_t
*)seq_item
->info_ptr
);
204 g_free(seq_item
->info_ptr
);
210 /* compare two list entries by packet no */
212 sequence_analysis_sort_compare(const void *a
, const void *b
, void *user_data _U_
)
214 const seq_analysis_item_t
*entry_a
= (const seq_analysis_item_t
*)a
;
215 const seq_analysis_item_t
*entry_b
= (const seq_analysis_item_t
*)b
;
217 if(entry_a
->frame_number
< entry_b
->frame_number
)
220 if(entry_a
->frame_number
> entry_b
->frame_number
)
228 sequence_analysis_list_sort(seq_analysis_info_t
*sainfo
)
231 g_queue_sort(sainfo
->items
, sequence_analysis_sort_compare
, NULL
);
235 sequence_analysis_list_free(seq_analysis_info_t
*sainfo
)
238 ws_noisy("%d items", g_queue_get_length(sainfo
->items
));
240 /* free the graph data items */
242 if (sainfo
->items
!= NULL
)
243 g_queue_free_full(sainfo
->items
, sequence_analysis_item_free
);
244 sainfo
->items
= g_queue_new();
246 if (NULL
!= sainfo
->ht
) {
247 g_hash_table_remove_all(sainfo
->ht
);
251 sequence_analysis_free_nodes(sainfo
);
254 /* Return the index array if the node is in the array. Return -1 if there is room in the array
255 * and Return -2 if the array is full
257 /****************************************************************************/
258 static unsigned add_or_get_node(seq_analysis_info_t
*sainfo
, address
*node
) {
261 if (node
->type
== AT_NONE
) return NODE_OVERFLOW
;
263 for (i
=0; i
<MAX_NUM_NODES
&& i
< sainfo
->num_nodes
; i
++) {
264 if ( cmp_address(&(sainfo
->nodes
[i
]), node
) == 0 ) return i
; /* it is in the array */
267 if (i
>= MAX_NUM_NODES
) {
268 return NODE_OVERFLOW
;
271 copy_address(&(sainfo
->nodes
[i
]), node
);
276 /* Same as add_or_get_node() but invoked for conversations where the same address is both used
278 * The occurrence number is tracking how many times this address was seen,
279 * value is 0 for the first occurrence, 1 for the second, and we never go higher to not have
280 * too many Y-Axis in the diagram.
282 static unsigned add_or_get_node_local(seq_analysis_info_t
*sainfo
, address
*node
, uint8_t occurrence
) {
285 if (node
->type
== AT_NONE
) return NODE_OVERFLOW
;
287 for (i
=0; i
<MAX_NUM_NODES
&& i
< sainfo
->num_nodes
; i
++) {
288 if ( cmp_address(&(sainfo
->nodes
[i
]), node
) == 0 ) {
290 /* address is matching, go further by checking the occurrence indication */
291 if(sainfo
->occurrence
[i
]==occurrence
) {
297 if (i
>= MAX_NUM_NODES
) {
298 return NODE_OVERFLOW
;
300 else { /* insert a new entry */
302 copy_address(&(sainfo
->nodes
[i
]), node
);
303 sainfo
->occurrence
[i
] = occurrence
;
308 struct sainfo_counter
{
309 seq_analysis_info_t
*sainfo
;
313 static void sequence_analysis_get_nodes_item_proc(void *data
, void *user_data
)
315 seq_analysis_item_t
*gai
= (seq_analysis_item_t
*)data
;
316 struct sainfo_counter
*sc
= (struct sainfo_counter
*)user_data
;
320 /* when both addresses are the same, look at the ports indications */
321 if( addresses_equal(&(gai
->src_addr
), &(gai
->dst_addr
)) ) {
322 if(gai
->port_src
< gai
->port_dst
) {
323 gai
->src_node
= add_or_get_node_local(sc
->sainfo
, &(gai
->src_addr
), 0 );
324 gai
->dst_node
= add_or_get_node_local(sc
->sainfo
, &(gai
->dst_addr
), 1 );
327 gai
->src_node
= add_or_get_node_local(sc
->sainfo
, &(gai
->src_addr
), 1 );
328 gai
->dst_node
= add_or_get_node_local(sc
->sainfo
, &(gai
->dst_addr
), 0 );
332 gai
->src_node
= add_or_get_node(sc
->sainfo
, &(gai
->src_addr
));
333 gai
->dst_node
= add_or_get_node(sc
->sainfo
, &(gai
->dst_addr
));
338 /* Get the nodes from the list */
339 /****************************************************************************/
341 sequence_analysis_get_nodes(seq_analysis_info_t
*sainfo
)
343 struct sainfo_counter sc
= {sainfo
, 0};
345 /* Fill the node array */
346 g_queue_foreach(sainfo
->items
, sequence_analysis_get_nodes_item_proc
, &sc
);
351 /* Free the node address list */
352 /****************************************************************************/
354 sequence_analysis_free_nodes(seq_analysis_info_t
*sainfo
)
358 for (i
=0; i
<MAX_NUM_NODES
; i
++) {
359 free_address(&sainfo
->nodes
[i
]);
361 sainfo
->num_nodes
= 0;
364 /* Writing analysis to file */
365 /****************************************************************************/
367 #define NODE_CHARS_WIDTH 20
368 #define CONV_TIME_HEADER "Conv.| Time "
369 #define TIME_HEADER "|Time "
370 #define CONV_TIME_EMPTY_HEADER " | "
371 #define TIME_EMPTY_HEADER "| "
372 #define CONV_TIME_HEADER_LENGTH 16
373 #define TIME_HEADER_LENGTH 10
375 /****************************************************************************/
376 /* Adds trailing characters to complete the requested length. */
377 /****************************************************************************/
379 static void enlarge_string(GString
*gstr
, uint32_t length
, char pad
) {
383 for (i
= gstr
->len
; i
< length
; i
++) {
384 g_string_append_c(gstr
, pad
);
388 /****************************************************************************/
389 /* overwrites the characters in a string, between positions p1 and p2, with */
390 /* the characters of text_to_insert */
391 /* NB: it does not check that p1 and p2 fit into string */
392 /****************************************************************************/
394 static void overwrite (GString
*gstr
, char *text_to_insert
, uint32_t p1
, uint32_t p2
) {
398 char *ins_str
= NULL
;
412 ins_len
= g_utf8_strlen(text_to_insert
, -1);
415 } else if (len
< ins_len
) {
416 ins_str
= g_utf8_substring(text_to_insert
, 0, len
);
419 if (!ins_str
) ins_str
= g_strdup(text_to_insert
);
424 g_string_erase(gstr
, pos
, len
);
426 g_string_insert(gstr
, pos
, ins_str
);
432 sequence_analysis_dump_to_file(FILE *of
, seq_analysis_info_t
*sainfo
, unsigned int first_node
)
434 uint32_t i
, display_items
, display_nodes
;
435 uint32_t start_position
, end_position
, item_width
, header_length
;
436 seq_analysis_item_t
*sai
;
437 uint16_t first_conv_num
= 0;
438 bool several_convs
= false;
439 bool first_packet
= true;
441 GString
*label_string
, *empty_line
, *separator_line
, *tmp_str
, *tmp_str2
;
442 const char *empty_header
;
443 char src_port
[8], dst_port
[8];
448 if (sainfo
->items
!= NULL
)
449 list
= g_queue_peek_nth_link(sainfo
->items
, 0);
453 sai
= (seq_analysis_item_t
*)list
->data
;
454 list
= g_list_next(list
);
461 first_conv_num
= sai
->conv_num
;
462 first_packet
= false;
464 else if (sai
->conv_num
!= first_conv_num
) {
465 several_convs
= true;
469 /* if not items to display */
470 if (display_items
== 0) {
474 label_string
= g_string_new("");
475 empty_line
= g_string_new("");
476 separator_line
= g_string_new("");
477 tmp_str
= g_string_new("");
478 tmp_str2
= g_string_new("");
480 display_nodes
= sainfo
->num_nodes
;
482 /* Write the conv. and time headers */
484 fprintf(of
, CONV_TIME_HEADER
);
485 empty_header
= CONV_TIME_EMPTY_HEADER
;
486 header_length
= CONV_TIME_HEADER_LENGTH
;
489 fprintf(of
, TIME_HEADER
);
490 empty_header
= TIME_EMPTY_HEADER
;
491 header_length
= TIME_HEADER_LENGTH
;
494 /* Write the node names on top */
495 for (i
=0; i
<display_nodes
; i
+=2) {
496 /* print the node identifiers */
497 addr_str
= address_to_display(NULL
, &(sainfo
->nodes
[i
+first_node
]));
498 g_string_printf(label_string
, "| %s", addr_str
);
499 wmem_free(NULL
, addr_str
);
500 enlarge_string(label_string
, NODE_CHARS_WIDTH
*2, ' ');
501 fprintf(of
, "%s", label_string
->str
);
502 g_string_printf(label_string
, "| ");
503 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
504 g_string_append(empty_line
, label_string
->str
);
507 fprintf(of
, "|\n%s", empty_header
);
508 g_string_printf(label_string
, "| ");
509 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
510 fprintf(of
, "%s", label_string
->str
);
512 /* Write the node names on top */
513 for (i
=1; i
<display_nodes
; i
+=2) {
514 /* print the node identifiers */
515 addr_str
= address_to_display(NULL
, &(sainfo
->nodes
[i
+first_node
]));
516 g_string_printf(label_string
, "| %s", addr_str
);
517 wmem_free(NULL
, addr_str
);
518 if (label_string
->len
< NODE_CHARS_WIDTH
)
520 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
521 g_string_append(label_string
, "| ");
523 enlarge_string(label_string
, NODE_CHARS_WIDTH
*2, ' ');
524 fprintf(of
, "%s", label_string
->str
);
525 g_string_printf(label_string
, "| ");
526 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
527 g_string_append(empty_line
, label_string
->str
);
532 g_string_append_c(empty_line
, '|');
534 enlarge_string(separator_line
, (uint32_t) empty_line
->len
+ header_length
, '-');
540 list
= g_queue_peek_nth_link(sainfo
->items
, 0);
543 sai
= (seq_analysis_item_t
*)list
->data
;
544 list
= g_list_next(list
);
549 start_position
= (sai
->src_node
-first_node
)*NODE_CHARS_WIDTH
+NODE_CHARS_WIDTH
/2;
551 end_position
= (sai
->dst_node
-first_node
)*NODE_CHARS_WIDTH
+NODE_CHARS_WIDTH
/2;
553 if (start_position
> end_position
) {
554 item_width
= start_position
-end_position
;
556 else if (start_position
< end_position
) {
557 item_width
= end_position
-start_position
;
559 else{ /* same origin and destination address */
560 end_position
= start_position
+NODE_CHARS_WIDTH
;
561 item_width
= NODE_CHARS_WIDTH
;
564 /* separator between conversations */
565 if (sai
->conv_num
!= first_conv_num
) {
566 fprintf(of
, "%s\n", separator_line
->str
);
567 first_conv_num
= sai
->conv_num
;
570 /* write the conversation number */
572 g_string_printf(label_string
, "%i", sai
->conv_num
);
573 enlarge_string(label_string
, 5, ' ');
574 fprintf(of
, "%s", label_string
->str
);
577 if (sai
->time_str
!= NULL
) {
578 g_string_printf(label_string
, "|%s", sai
->time_str
);
579 enlarge_string(label_string
, 10, ' ');
580 fprintf(of
, "%s", label_string
->str
);
583 /* write the frame label */
585 g_string_printf(tmp_str
, "%s", empty_line
->str
);
586 overwrite(tmp_str
, sai
->frame_label
,
590 fprintf(of
, "%s", tmp_str
->str
);
592 /* write the comments */
593 fprintf(of
, "%s\n", sai
->comment
);
595 /* write the arrow and frame label*/
596 fprintf(of
, "%s", empty_header
);
598 g_string_printf(tmp_str
, "%s", empty_line
->str
);
600 g_string_truncate(tmp_str2
, 0);
602 if (start_position
<end_position
) {
603 enlarge_string(tmp_str2
, item_width
-2, '-');
604 g_string_append_c(tmp_str2
, '>');
607 g_string_printf(tmp_str2
, "<");
608 enlarge_string(tmp_str2
, item_width
-1, '-');
611 overwrite(tmp_str
, tmp_str2
->str
,
616 snprintf(src_port
, sizeof(src_port
), "(%i)", sai
->port_src
);
617 snprintf(dst_port
, sizeof(dst_port
), "(%i)", sai
->port_dst
);
619 if (start_position
<end_position
) {
620 overwrite(tmp_str
, src_port
, start_position
-9, start_position
-1);
621 overwrite(tmp_str
, dst_port
, end_position
+1, end_position
+9);
624 overwrite(tmp_str
, src_port
, start_position
+1, start_position
+9);
625 overwrite(tmp_str
, dst_port
, end_position
-9, end_position
+1);
628 fprintf(of
, "%s\n", tmp_str
->str
);
631 g_string_free(label_string
, TRUE
);
632 g_string_free(empty_line
, TRUE
);
633 g_string_free(separator_line
, TRUE
);
634 g_string_free(tmp_str
, TRUE
);
635 g_string_free(tmp_str2
, TRUE
);
644 * indent-tabs-mode: nil
647 * ex: set shiftwidth=4 tabstop=8 expandtab:
648 * :indentSize=4:tabSize=8:noTabs=true: