1 /* tap-sequence-analysis.c
2 * Flow sequence analysis
6 * Some code from from gtk/flow_graph.c
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include "tap-sequence-analysis.h"
33 #include "epan/addr_resolv.h"
34 #include "epan/column-utils.h"
35 #include "epan/packet.h"
37 #include "epan/dissectors/packet-tcp.h"
39 #include "ui/alert_box.h"
41 #include <wsutil/file_util.h>
43 #define NODE_OVERFLOW MAX_NUM_NODES+1
45 #define NODE_CHARS_WIDTH 20
46 #define CONV_TIME_HEADER "Conv.| Time "
47 #define TIME_HEADER "|Time "
48 #define CONV_TIME_EMPTY_HEADER " | "
49 #define TIME_EMPTY_HEADER "| "
50 #define CONV_TIME_HEADER_LENGTH 16
51 #define TIME_HEADER_LENGTH 10
53 /****************************************************************************/
54 /* whenever a frame packet is seen by the tap listener */
55 /* Add a new frame into the graph */
57 seq_analysis_frame_packet( void *ptr
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *dummy _U_
)
59 seq_analysis_info_t
*sainfo
= (seq_analysis_info_t
*) ptr
;
61 if ((sainfo
->all_packets
)||(pinfo
->fd
->flags
.passed_dfilter
==1)){
63 gchar
*protocol
= NULL
;
64 gchar
*colinfo
= NULL
;
65 seq_analysis_item_t
*sai
= NULL
;
67 if (sainfo
->any_addr
) {
68 if (pinfo
->net_src
.type
!=AT_NONE
&& pinfo
->net_dst
.type
!=AT_NONE
) {
69 sai
= (seq_analysis_item_t
*)g_malloc0(sizeof(seq_analysis_item_t
));
70 COPY_ADDRESS(&(sai
->src_addr
),&(pinfo
->net_src
));
71 COPY_ADDRESS(&(sai
->dst_addr
),&(pinfo
->net_dst
));
75 if (pinfo
->src
.type
!=AT_NONE
&& pinfo
->dst
.type
!=AT_NONE
) {
76 sai
= (seq_analysis_item_t
*)g_malloc0(sizeof(seq_analysis_item_t
));
77 COPY_ADDRESS(&(sai
->src_addr
),&(pinfo
->src
));
78 COPY_ADDRESS(&(sai
->dst_addr
),&(pinfo
->dst
));
82 if (!sai
) return FALSE
;
86 sai
->port_src
=pinfo
->srcport
;
87 sai
->port_dst
=pinfo
->destport
;
90 if (pinfo
->cinfo
->col_first
[COL_INFO
]>=0){
92 for (i
= pinfo
->cinfo
->col_first
[COL_INFO
]; i
<= pinfo
->cinfo
->col_last
[COL_INFO
]; i
++) {
93 if (pinfo
->cinfo
->fmt_matx
[i
][COL_INFO
]) {
94 colinfo
= g_strdup(pinfo
->cinfo
->col_data
[i
]);
95 /* break; ? or g_free(colinfo); before g_strdup() */
100 if (pinfo
->cinfo
->col_first
[COL_PROTOCOL
]>=0){
102 for (i
= pinfo
->cinfo
->col_first
[COL_PROTOCOL
]; i
<= pinfo
->cinfo
->col_last
[COL_PROTOCOL
]; i
++) {
103 if (pinfo
->cinfo
->fmt_matx
[i
][COL_PROTOCOL
]) {
104 protocol
= g_strdup(pinfo
->cinfo
->col_data
[i
]);
105 /* break; ? or g_free(protocol); before g_strdup() */
111 if (colinfo
!= NULL
) {
112 if (protocol
!= NULL
) {
113 sai
->frame_label
= g_strdup(colinfo
);
114 sai
->comment
= g_strdup_printf("%s: %s", protocol
, colinfo
);
116 sai
->frame_label
= g_strdup(colinfo
);
117 sai
->comment
= g_strdup(colinfo
);
120 /* This will probably never happen...*/
121 if (protocol
!= NULL
) {
122 sai
->frame_label
= g_strdup(protocol
);
123 sai
->comment
= g_strdup(protocol
);
134 sainfo
->list
= g_list_prepend(sainfo
->list
, sai
);
140 /****************************************************************************/
141 /* whenever a TCP packet is seen by the tap listener */
142 /* Add a new tcp frame into the graph */
144 seq_analysis_tcp_packet( void *ptr _U_
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *tcp_info
)
146 seq_analysis_info_t
*sainfo
= (seq_analysis_info_t
*) ptr
;
147 const struct tcpheader
*tcph
= (struct tcpheader
*)tcp_info
;
149 if ((sainfo
->all_packets
)||(pinfo
->fd
->flags
.passed_dfilter
==1)){
150 /* copied from packet-tcp */
151 static const gchar
*fstr
[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
153 gboolean flags_found
= FALSE
;
155 seq_analysis_item_t
*sai
;
157 sai
= (seq_analysis_item_t
*)g_malloc0(sizeof(seq_analysis_item_t
));
159 if (sainfo
->any_addr
) {
160 COPY_ADDRESS(&(sai
->src_addr
),&(pinfo
->net_src
));
161 COPY_ADDRESS(&(sai
->dst_addr
),&(pinfo
->net_dst
));
163 COPY_ADDRESS(&(sai
->src_addr
),&(pinfo
->src
));
164 COPY_ADDRESS(&(sai
->dst_addr
),&(pinfo
->dst
));
166 sai
->port_src
=pinfo
->srcport
;
167 sai
->port_dst
=pinfo
->destport
;
170 for (i
= 0; i
< 8; i
++) {
172 if (tcph
->th_flags
& bpos
) {
174 g_strlcat(flags
, ", ", sizeof(flags
));
176 g_strlcat(flags
, fstr
[i
], sizeof(flags
));
180 if (flags
[0] == '\0') {
181 g_snprintf (flags
, sizeof(flags
), "<None>");
184 if ((tcph
->th_have_seglen
)&&(tcph
->th_seglen
!=0)){
185 sai
->frame_label
= g_strdup_printf("%s - Len: %u",flags
, tcph
->th_seglen
);
188 sai
->frame_label
= g_strdup(flags
);
191 if (tcph
->th_flags
& TH_ACK
)
192 sai
->comment
= g_strdup_printf("Seq = %u Ack = %u",tcph
->th_seq
, tcph
->th_ack
);
194 sai
->comment
= g_strdup_printf("Seq = %u",tcph
->th_seq
);
200 sainfo
->list
= g_list_prepend(sainfo
->list
, sai
);
207 sequence_analysis_list_get(capture_file
*cf
, seq_analysis_info_t
*sainfo
)
210 gchar time_str
[COL_MAX_LEN
];
212 if (!cf
|| !sainfo
) return;
214 switch (sainfo
->type
) {
215 case SEQ_ANALYSIS_ANY
:
216 register_tap_listener("frame", sainfo
, NULL
,
219 seq_analysis_frame_packet
,
223 case SEQ_ANALYSIS_TCP
:
224 register_tap_listener("tcp", sainfo
, NULL
,
227 seq_analysis_tcp_packet
,
231 case SEQ_ANALYSIS_VOIP
:
238 cf_retap_packets(cf
);
239 sainfo
->list
= g_list_reverse(sainfo
->list
);
240 remove_tap_listener(sainfo
);
242 /* Fill in the timestamps */
243 list
= g_list_first(sainfo
->list
);
246 seq_analysis_item_t
*seq_item
= (seq_analysis_item_t
*)list
->data
;
247 set_fd_time(cf
->epan
, seq_item
->fd
, time_str
);
248 seq_item
->time_str
= g_strdup(time_str
);
249 list
= g_list_next(list
);
254 sequence_analysis_list_free(seq_analysis_info_t
*sainfo
)
261 /* free the graph data items */
262 list
= g_list_first(sainfo
->list
);
265 seq_analysis_item_t
*seq_item
= (seq_analysis_item_t
*)list
->data
;
266 g_free(seq_item
->frame_label
);
267 g_free(seq_item
->time_str
);
268 g_free(seq_item
->comment
);
270 list
= g_list_next(list
);
272 g_list_free(sainfo
->list
);
276 for (i
=0; i
<MAX_NUM_NODES
; i
++) {
277 sainfo
->nodes
[i
].type
= AT_NONE
;
278 sainfo
->nodes
[i
].len
= 0;
279 g_free((void *)sainfo
->nodes
[i
].data
);
280 sainfo
->nodes
[i
].data
= NULL
;
282 sainfo
->num_nodes
= 0;
285 /****************************************************************************/
286 /* Adds trailing characters to complete the requested length. */
287 /****************************************************************************/
289 static void enlarge_string(GString
*gstr
, guint32 length
, char pad
) {
293 for (i
= gstr
->len
; i
< length
; i
++) {
294 g_string_append_c(gstr
, pad
);
298 /****************************************************************************/
299 /* overwrites the characters in a string, between positions p1 and p2, with */
300 /* the characters of text_to_insert */
301 /* NB: it does not check that p1 and p2 fit into string */
302 /****************************************************************************/
304 static void overwrite (GString
*gstr
, char *text_to_insert
, guint32 p1
, guint32 p2
) {
308 gchar
*ins_str
= NULL
;
322 ins_len
= g_utf8_strlen(text_to_insert
, -1);
325 } else if (len
< ins_len
) {
326 #if GLIB_CHECK_VERSION(2,30,0)
327 ins_str
= g_utf8_substring(text_to_insert
, 0, len
);
329 gchar
*end
= g_utf8_offset_to_pointer(text_to_insert
, len
);
330 ins_str
= g_strndup(text_to_insert
, end
- text_to_insert
);
334 if (!ins_str
) ins_str
= g_strdup(text_to_insert
);
339 g_string_erase(gstr
, pos
, len
);
341 g_string_insert(gstr
, pos
, ins_str
);
345 /* Return the index array if the node is in the array. Return -1 if there is room in the array
346 * and Return -2 if the array is full
348 /****************************************************************************/
349 static gint
add_or_get_node(seq_analysis_info_t
*sainfo
, address
*node
) {
352 if (node
->type
== AT_NONE
) return NODE_OVERFLOW
;
354 for (i
=0; i
<MAX_NUM_NODES
&& i
< sainfo
->num_nodes
; i
++) {
355 if ( CMP_ADDRESS(&(sainfo
->nodes
[i
]), node
) == 0 ) return i
; /* it is in the array */
358 if (i
== MAX_NUM_NODES
) {
359 return NODE_OVERFLOW
;
362 COPY_ADDRESS(&(sainfo
->nodes
[i
]), node
);
367 /* Get the nodes from the list */
368 /****************************************************************************/
370 sequence_analysis_get_nodes(seq_analysis_info_t
*sainfo
)
373 seq_analysis_item_t
*gai
;
376 /* fill the node array */
377 list
= g_list_first(sainfo
->list
);
380 gai
= (seq_analysis_item_t
*)list
->data
;
383 #if 0 /* inverse is always false ? */
384 if (!user_data
->dlg
.inverse
) {
386 gai
->src_node
= (guint16
)add_or_get_node(sainfo
, &(gai
->src_addr
));
387 gai
->dst_node
= (guint16
)add_or_get_node(sainfo
, &(gai
->dst_addr
));
388 #if 0 /* inverse is always false ? */
390 gai
->dst_node
= (guint16
)add_or_get_node(sainfo
, &(gai
->src_addr
));
391 gai
->src_node
= (guint16
)add_or_get_node(sainfo
, &(gai
->dst_addr
));
395 list
= g_list_next(list
);
400 /****************************************************************************/
402 sequence_analysis_dump_to_file(const char *pathname
, seq_analysis_info_t
*sainfo
, capture_file
*cf
, unsigned int first_node
)
404 guint32 i
, display_items
, display_nodes
;
405 guint32 start_position
, end_position
, item_width
, header_length
;
406 seq_analysis_item_t
*sai
;
407 guint16 first_conv_num
= 0;
408 gboolean several_convs
= FALSE
;
409 gboolean first_packet
= TRUE
;
411 GString
*label_string
, *empty_line
, *separator_line
, *tmp_str
, *tmp_str2
;
412 const char *empty_header
;
413 char src_port
[8], dst_port
[8];
419 of
= ws_fopen(pathname
, "w");
421 open_failure_alert_box(pathname
, errno
, TRUE
);
425 time_str
= (gchar
*)g_malloc(COL_MAX_LEN
);
426 label_string
= g_string_new("");
427 empty_line
= g_string_new("");
428 separator_line
= g_string_new("");
429 tmp_str
= g_string_new("");
430 tmp_str2
= g_string_new("");
433 list
= g_list_first(sainfo
->list
);
436 sai
= (seq_analysis_item_t
*)list
->data
;
437 list
= g_list_next(list
);
444 first_conv_num
= sai
->conv_num
;
445 first_packet
= FALSE
;
447 else if (sai
->conv_num
!= first_conv_num
) {
448 several_convs
= TRUE
;
452 /* if not items to display */
453 if (display_items
== 0)
456 display_nodes
= sainfo
->num_nodes
;
458 /* Write the conv. and time headers */
460 fprintf(of
, CONV_TIME_HEADER
);
461 empty_header
= CONV_TIME_EMPTY_HEADER
;
462 header_length
= CONV_TIME_HEADER_LENGTH
;
465 fprintf(of
, TIME_HEADER
);
466 empty_header
= TIME_EMPTY_HEADER
;
467 header_length
= TIME_HEADER_LENGTH
;
470 /* Write the node names on top */
471 for (i
=0; i
<display_nodes
; i
+=2) {
472 /* print the node identifiers */
473 g_string_printf(label_string
, "| %s",
474 get_addr_name(&(sainfo
->nodes
[i
+first_node
])));
475 enlarge_string(label_string
, NODE_CHARS_WIDTH
*2, ' ');
476 fprintf(of
, "%s", label_string
->str
);
477 g_string_printf(label_string
, "| ");
478 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
479 g_string_append(empty_line
, label_string
->str
);
482 fprintf(of
, "|\n%s", empty_header
);
483 g_string_printf(label_string
, "| ");
484 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
485 fprintf(of
, "%s", label_string
->str
);
487 /* Write the node names on top */
488 for (i
=1; i
<display_nodes
; i
+=2) {
489 /* print the node identifiers */
490 g_string_printf(label_string
, "| %s",
491 get_addr_name(&(sainfo
->nodes
[i
+first_node
])));
492 if (label_string
->len
< NODE_CHARS_WIDTH
)
494 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
495 g_string_append(label_string
, "| ");
497 enlarge_string(label_string
, NODE_CHARS_WIDTH
*2, ' ');
498 fprintf(of
, "%s", label_string
->str
);
499 g_string_printf(label_string
, "| ");
500 enlarge_string(label_string
, NODE_CHARS_WIDTH
, ' ');
501 g_string_append(empty_line
, label_string
->str
);
506 g_string_append_c(empty_line
, '|');
508 enlarge_string(separator_line
, (guint32
) empty_line
->len
+ header_length
, '-');
514 list
= g_list_first(sainfo
->list
);
517 sai
= (seq_analysis_item_t
*)list
->data
;
518 list
= g_list_next(list
);
523 start_position
= (sai
->src_node
-first_node
)*NODE_CHARS_WIDTH
+NODE_CHARS_WIDTH
/2;
525 end_position
= (sai
->dst_node
-first_node
)*NODE_CHARS_WIDTH
+NODE_CHARS_WIDTH
/2;
527 if (start_position
> end_position
) {
528 item_width
= start_position
-end_position
;
530 else if (start_position
< end_position
) {
531 item_width
= end_position
-start_position
;
533 else{ /* same origin and destination address */
534 end_position
= start_position
+NODE_CHARS_WIDTH
;
535 item_width
= NODE_CHARS_WIDTH
;
538 /* separator between conversations */
539 if (sai
->conv_num
!= first_conv_num
) {
540 fprintf(of
, "%s\n", separator_line
->str
);
541 first_conv_num
= sai
->conv_num
;
544 /* write the conversation number */
546 g_string_printf(label_string
, "%i", sai
->conv_num
);
547 enlarge_string(label_string
, 5, ' ');
548 fprintf(of
, "%s", label_string
->str
);
553 g_string_printf(label_string
, "|%.3f", nstime_to_sec(&sai
->fd
->rel_ts
));
555 /* Write the time, using the same format as in the time col */
556 set_fd_time(cf
->epan
, sai
->fd
, time_str
);
557 g_string_printf(label_string
, "|%s", time_str
);
558 enlarge_string(label_string
, 10, ' ');
559 fprintf(of
, "%s", label_string
->str
);
561 /* write the frame label */
563 g_string_printf(tmp_str
, "%s", empty_line
->str
);
564 overwrite(tmp_str
, sai
->frame_label
,
568 fprintf(of
, "%s", tmp_str
->str
);
570 /* write the comments */
571 fprintf(of
, "%s\n", sai
->comment
);
573 /* write the arrow and frame label*/
574 fprintf(of
, "%s", empty_header
);
576 g_string_printf(tmp_str
, "%s", empty_line
->str
);
578 g_string_truncate(tmp_str2
, 0);
580 if (start_position
<end_position
) {
581 enlarge_string(tmp_str2
, item_width
-2, '-');
582 g_string_append_c(tmp_str2
, '>');
585 g_string_printf(tmp_str2
, "<");
586 enlarge_string(tmp_str2
, item_width
-1, '-');
589 overwrite(tmp_str
, tmp_str2
->str
,
594 g_snprintf(src_port
, sizeof(src_port
), "(%i)", sai
->port_src
);
595 g_snprintf(dst_port
, sizeof(dst_port
), "(%i)", sai
->port_dst
);
597 if (start_position
<end_position
) {
598 overwrite(tmp_str
, src_port
, start_position
-9, start_position
-1);
599 overwrite(tmp_str
, dst_port
, end_position
+1, end_position
+9);
602 overwrite(tmp_str
, src_port
, start_position
+1, start_position
+9);
603 overwrite(tmp_str
, dst_port
, end_position
-9, end_position
+1);
606 fprintf(of
, "%s\n", tmp_str
->str
);
610 g_string_free(label_string
, TRUE
);
611 g_string_free(empty_line
, TRUE
);
612 g_string_free(separator_line
, TRUE
);
613 g_string_free(tmp_str
, TRUE
);
614 g_string_free(tmp_str2
, TRUE
);
627 * indent-tabs-mode: nil
630 * ex: set shiftwidth=4 tabstop=8 expandtab:
631 * :indentSize=4:tabSize=8:noTabs=true: