2 * Flow sequence analysis
4 * Some code from 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
15 #include "sequence_analysis.h"
17 #include "addr_resolv.h"
19 #include "color_filters.h"
20 #include "column-info.h"
22 #include "wmem/wmem.h"
24 #define NODE_OVERFLOW MAX_NUM_NODES+1
26 struct register_analysis {
27 const char* name; /* Name (used for lookup) */
28 const char* ui_name; /* Name used for UI */
29 int proto_id; /* protocol id (0-indexed) */
30 const char* tap_listen_str; /* string used in register_tap_listener (NULL to use protocol name) */
32 tap_packet_cb analysis_func; /* function to be called for new incoming packets for sequence analysis */
35 static wmem_tree_t *registered_seq_analysis = NULL;
38 register_seq_analysis(const char* name, const char* ui_name, const int proto_id, const char* tap_listener, guint tap_flags, tap_packet_cb tap_func)
40 register_analysis_t* analysis;
42 DISSECTOR_ASSERT(tap_func);
44 analysis = wmem_new0(wmem_epan_scope(), register_analysis_t);
46 analysis->name = name;
47 analysis->ui_name = ui_name;
48 analysis->proto_id = proto_id;
49 if (tap_listener != NULL)
50 analysis->tap_listen_str = tap_listener;
52 analysis->tap_listen_str = proto_get_protocol_filter_name(proto_id);
53 analysis->tap_flags = tap_flags;
54 analysis->analysis_func = tap_func;
56 if (registered_seq_analysis == NULL)
57 registered_seq_analysis = wmem_tree_new(wmem_epan_scope());
59 wmem_tree_insert_string(registered_seq_analysis, name, analysis, 0);
62 const char* sequence_analysis_get_name(register_analysis_t* analysis)
64 return analysis->name;
67 const char* sequence_analysis_get_ui_name(register_analysis_t* analysis)
69 return analysis->ui_name;
72 const char* sequence_analysis_get_tap_listener_name(register_analysis_t* analysis)
74 return analysis->tap_listen_str;
77 tap_packet_cb sequence_analysis_get_packet_func(register_analysis_t* analysis)
79 return analysis->analysis_func;
82 guint sequence_analysis_get_tap_flags(register_analysis_t* analysis)
84 return analysis->tap_flags;
88 register_analysis_t* sequence_analysis_find_by_name(const char* name)
90 return (register_analysis_t*)wmem_tree_lookup_string(registered_seq_analysis, name, 0);
93 void sequence_analysis_table_iterate_tables(wmem_foreach_func func, gpointer user_data)
95 wmem_tree_foreach(registered_seq_analysis, func, user_data);
98 seq_analysis_item_t* sequence_analysis_create_sai_with_addresses(packet_info *pinfo, seq_analysis_info_t *sainfo)
100 seq_analysis_item_t *sai = NULL;
101 char time_str[COL_MAX_LEN];
103 if (sainfo->any_addr) {
104 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
105 sai = g_new0(seq_analysis_item_t, 1);
106 copy_address(&(sai->src_addr),&(pinfo->net_src));
107 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 gchar *protocol = NULL;
139 const gchar *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 = g_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 /* SEQ_ANALYSIS_DEBUG("adding new item"); */
168 sainfo->items = g_queue_new();
169 sainfo->ht= g_hash_table_new(g_int_hash, g_int_equal);
173 void sequence_analysis_info_free(seq_analysis_info_t *sainfo)
177 /* SEQ_ANALYSIS_DEBUG("%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(gpointer 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);
199 /* compare two list entries by packet no */
201 sequence_analysis_sort_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_)
203 const seq_analysis_item_t *entry_a = (const seq_analysis_item_t *)a;
204 const seq_analysis_item_t *entry_b = (const seq_analysis_item_t *)b;
206 if(entry_a->frame_number < entry_b->frame_number)
209 if(entry_a->frame_number > entry_b->frame_number)
217 sequence_analysis_list_sort(seq_analysis_info_t *sainfo)
220 g_queue_sort(sainfo->items, sequence_analysis_sort_compare, NULL);
224 sequence_analysis_list_free(seq_analysis_info_t *sainfo)
227 /* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
229 /* free the graph data items */
231 if (sainfo->items != NULL)
232 g_queue_free_full(sainfo->items, sequence_analysis_item_free);
233 sainfo->items = g_queue_new();
235 if (NULL != sainfo->ht) {
236 g_hash_table_remove_all(sainfo->ht);
240 sequence_analysis_free_nodes(sainfo);
243 /* Return the index array if the node is in the array. Return -1 if there is room in the array
244 * and Return -2 if the array is full
246 /****************************************************************************/
247 static guint add_or_get_node(seq_analysis_info_t *sainfo, address *node) {
250 if (node->type == AT_NONE) return NODE_OVERFLOW;
252 for (i=0; i<MAX_NUM_NODES && i < sainfo->num_nodes ; i++) {
253 if ( cmp_address(&(sainfo->nodes[i]), node) == 0 ) return i; /* it is in the array */
256 if (i >= MAX_NUM_NODES) {
257 return NODE_OVERFLOW;
260 copy_address(&(sainfo->nodes[i]), node);
265 struct sainfo_counter {
266 seq_analysis_info_t *sainfo;
270 static void sequence_analysis_get_nodes_item_proc(gpointer data, gpointer user_data)
272 seq_analysis_item_t *gai = (seq_analysis_item_t *)data;
273 struct sainfo_counter *sc = (struct sainfo_counter *)user_data;
276 gai->src_node = add_or_get_node(sc->sainfo, &(gai->src_addr));
277 gai->dst_node = add_or_get_node(sc->sainfo, &(gai->dst_addr));
281 /* Get the nodes from the list */
282 /****************************************************************************/
284 sequence_analysis_get_nodes(seq_analysis_info_t *sainfo)
286 struct sainfo_counter sc = {sainfo, 0};
288 /* Fill the node array */
289 g_queue_foreach(sainfo->items, sequence_analysis_get_nodes_item_proc, &sc);
294 /* Free the node address list */
295 /****************************************************************************/
297 sequence_analysis_free_nodes(seq_analysis_info_t *sainfo)
301 for (i=0; i<MAX_NUM_NODES; i++) {
302 free_address(&sainfo->nodes[i]);
304 sainfo->num_nodes = 0;
307 /* Writing analysis to file */
308 /****************************************************************************/
310 #define NODE_CHARS_WIDTH 20
311 #define CONV_TIME_HEADER "Conv.| Time "
312 #define TIME_HEADER "|Time "
313 #define CONV_TIME_EMPTY_HEADER " | "
314 #define TIME_EMPTY_HEADER "| "
315 #define CONV_TIME_HEADER_LENGTH 16
316 #define TIME_HEADER_LENGTH 10
318 /****************************************************************************/
319 /* Adds trailing characters to complete the requested length. */
320 /****************************************************************************/
322 static void enlarge_string(GString *gstr, guint32 length, char pad) {
326 for (i = gstr->len; i < length; i++) {
327 g_string_append_c(gstr, pad);
331 /****************************************************************************/
332 /* overwrites the characters in a string, between positions p1 and p2, with */
333 /* the characters of text_to_insert */
334 /* NB: it does not check that p1 and p2 fit into string */
335 /****************************************************************************/
337 static void overwrite (GString *gstr, char *text_to_insert, guint32 p1, guint32 p2) {
341 gchar *ins_str = NULL;
355 ins_len = g_utf8_strlen(text_to_insert, -1);
358 } else if (len < ins_len) {
359 ins_str = g_utf8_substring(text_to_insert, 0, len);
362 if (!ins_str) ins_str = g_strdup(text_to_insert);
367 g_string_erase(gstr, pos, len);
369 g_string_insert(gstr, pos, ins_str);
375 sequence_analysis_dump_to_file(FILE *of, seq_analysis_info_t *sainfo, unsigned int first_node)
377 guint32 i, display_items, display_nodes;
378 guint32 start_position, end_position, item_width, header_length;
379 seq_analysis_item_t *sai;
380 guint16 first_conv_num = 0;
381 gboolean several_convs = FALSE;
382 gboolean first_packet = TRUE;
384 GString *label_string, *empty_line, *separator_line, *tmp_str, *tmp_str2;
385 const char *empty_header;
386 char src_port[8], dst_port[8];
391 if (sainfo->items != NULL)
392 list = g_queue_peek_nth_link(sainfo->items, 0);
396 sai = (seq_analysis_item_t *)list->data;
397 list = g_list_next(list);
404 first_conv_num = sai->conv_num;
405 first_packet = FALSE;
407 else if (sai->conv_num != first_conv_num) {
408 several_convs = TRUE;
412 /* if not items to display */
413 if (display_items == 0) {
417 label_string = g_string_new("");
418 empty_line = g_string_new("");
419 separator_line = g_string_new("");
420 tmp_str = g_string_new("");
421 tmp_str2 = g_string_new("");
423 display_nodes = sainfo->num_nodes;
425 /* Write the conv. and time headers */
427 fprintf(of, CONV_TIME_HEADER);
428 empty_header = CONV_TIME_EMPTY_HEADER;
429 header_length = CONV_TIME_HEADER_LENGTH;
432 fprintf(of, TIME_HEADER);
433 empty_header = TIME_EMPTY_HEADER;
434 header_length = TIME_HEADER_LENGTH;
437 /* Write the node names on top */
438 for (i=0; i<display_nodes; i+=2) {
439 /* print the node identifiers */
440 addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
441 g_string_printf(label_string, "| %s", addr_str);
442 wmem_free(NULL, addr_str);
443 enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
444 fprintf(of, "%s", label_string->str);
445 g_string_printf(label_string, "| ");
446 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
447 g_string_append(empty_line, label_string->str);
450 fprintf(of, "|\n%s", empty_header);
451 g_string_printf(label_string, "| ");
452 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
453 fprintf(of, "%s", label_string->str);
455 /* Write the node names on top */
456 for (i=1; i<display_nodes; i+=2) {
457 /* print the node identifiers */
458 addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
459 g_string_printf(label_string, "| %s", addr_str);
460 wmem_free(NULL, addr_str);
461 if (label_string->len < NODE_CHARS_WIDTH)
463 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
464 g_string_append(label_string, "| ");
466 enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
467 fprintf(of, "%s", label_string->str);
468 g_string_printf(label_string, "| ");
469 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
470 g_string_append(empty_line, label_string->str);
475 g_string_append_c(empty_line, '|');
477 enlarge_string(separator_line, (guint32) empty_line->len + header_length, '-');
483 list = g_queue_peek_nth_link(sainfo->items, 0);
486 sai = (seq_analysis_item_t *)list->data;
487 list = g_list_next(list);
492 start_position = (sai->src_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
494 end_position = (sai->dst_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
496 if (start_position > end_position) {
497 item_width = start_position-end_position;
499 else if (start_position < end_position) {
500 item_width = end_position-start_position;
502 else{ /* same origin and destination address */
503 end_position = start_position+NODE_CHARS_WIDTH;
504 item_width = NODE_CHARS_WIDTH;
507 /* separator between conversations */
508 if (sai->conv_num != first_conv_num) {
509 fprintf(of, "%s\n", separator_line->str);
510 first_conv_num = sai->conv_num;
513 /* write the conversation number */
515 g_string_printf(label_string, "%i", sai->conv_num);
516 enlarge_string(label_string, 5, ' ');
517 fprintf(of, "%s", label_string->str);
520 if (sai->time_str != NULL) {
521 g_string_printf(label_string, "|%s", sai->time_str);
522 enlarge_string(label_string, 10, ' ');
523 fprintf(of, "%s", label_string->str);
526 /* write the frame label */
528 g_string_printf(tmp_str, "%s", empty_line->str);
529 overwrite(tmp_str, sai->frame_label,
533 fprintf(of, "%s", tmp_str->str);
535 /* write the comments */
536 fprintf(of, "%s\n", sai->comment);
538 /* write the arrow and frame label*/
539 fprintf(of, "%s", empty_header);
541 g_string_printf(tmp_str, "%s", empty_line->str);
543 g_string_truncate(tmp_str2, 0);
545 if (start_position<end_position) {
546 enlarge_string(tmp_str2, item_width-2, '-');
547 g_string_append_c(tmp_str2, '>');
550 g_string_printf(tmp_str2, "<");
551 enlarge_string(tmp_str2, item_width-1, '-');
554 overwrite(tmp_str, tmp_str2->str,
559 g_snprintf(src_port, sizeof(src_port), "(%i)", sai->port_src);
560 g_snprintf(dst_port, sizeof(dst_port), "(%i)", sai->port_dst);
562 if (start_position<end_position) {
563 overwrite(tmp_str, src_port, start_position-9, start_position-1);
564 overwrite(tmp_str, dst_port, end_position+1, end_position+9);
567 overwrite(tmp_str, src_port, start_position+1, start_position+9);
568 overwrite(tmp_str, dst_port, end_position-9, end_position+1);
571 fprintf(of, "%s\n", tmp_str->str);
574 g_string_free(label_string, TRUE);
575 g_string_free(empty_line, TRUE);
576 g_string_free(separator_line, TRUE);
577 g_string_free(tmp_str, TRUE);
578 g_string_free(tmp_str2, TRUE);
587 * indent-tabs-mode: nil
590 * ex: set shiftwidth=4 tabstop=8 expandtab:
591 * :indentSize=4:tabSize=8:noTabs=true: