1 /* conversations_table.c
2 * conversations_table 2003 Ronnie Sahlberg
3 * Helper routines common to all endpoint conversations tap.
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "packet_info.h"
30 #include "conversation_table.h"
31 #include "addr_resolv.h"
34 #include "stat_cmd_args.h"
36 GList *cmd_string_list_ = NULL;
39 gboolean hide_ports; /* hide TCP / UDP port columns */
40 int proto_id; /* protocol id (0-indexed) */
41 tap_packet_cb packet_func; /* function to be called for new incoming packets */
42 conv_gui_init_cb gui_init_cb; /* GUI specific function to initialize conversation */
45 gboolean get_conversation_hide_ports(register_ct_t* ct)
47 return ct->hide_ports;
50 int get_conversation_proto_id(register_ct_t* ct)
58 tap_packet_cb get_conversation_packet_func(register_ct_t* ct)
60 return ct->packet_func;
63 static GSList *registered_ct_tables = NULL;
66 dissector_conversation_init(const char *opt_arg, void* userdata)
68 register_ct_t *table = (register_ct_t*)userdata;
69 GString *cmd_str = g_string_new("conv,");
70 const char *filter=NULL;
72 g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
73 if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
74 filter = opt_arg + cmd_str->len;
78 g_string_free(cmd_str, TRUE);
80 if (table->gui_init_cb)
81 table->gui_init_cb(table, filter);
84 /** get conversation from protocol ID
86 * @param proto_id protocol ID
87 * @return tap function handler of conversation
89 register_ct_t* get_conversation_by_proto_id(int proto_id)
94 for(ct = registered_ct_tables; ct != NULL; ct = g_slist_next(ct)){
95 table = (register_ct_t*)ct->data;
96 if ((table) && (table->proto_id == proto_id))
104 insert_sorted_by_table_name(gconstpointer aparam, gconstpointer bparam)
106 const register_ct_t *a = (register_ct_t *)aparam;
107 const register_ct_t *b = (register_ct_t *)bparam;
109 return g_ascii_strcasecmp(proto_get_protocol_short_name(find_protocol_by_id(a->proto_id)), proto_get_protocol_short_name(find_protocol_by_id(b->proto_id)));
113 register_conversation_table(const int proto_id, gboolean hide_ports, tap_packet_cb packet_func)
115 register_ct_t *table;
116 GString *cmd_str = g_string_new("conv,");
118 table = g_new(register_ct_t,1);
120 table->hide_ports = hide_ports;
121 table->proto_id = proto_id;
122 table->packet_func = packet_func;
123 table->gui_init_cb = NULL;
125 registered_ct_tables = g_slist_insert_sorted(registered_ct_tables, table, insert_sorted_by_table_name);
127 g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
128 cmd_string_list_ = g_list_append(cmd_string_list_, cmd_str->str);
129 register_stat_cmd_arg(cmd_str->str, dissector_conversation_init, table);
130 g_string_free(cmd_str, FALSE);
133 /* Set GUI fields for register_ct list */
135 set_gui_data(gpointer data, gpointer user_data)
137 register_ct_t *table = (register_ct_t*)data;
138 table->gui_init_cb = (conv_gui_init_cb)user_data;
141 void conversation_table_set_gui_info(conv_gui_init_cb init_cb)
143 g_slist_foreach(registered_ct_tables, set_gui_data, init_cb);
146 void conversation_table_iterate_tables(GFunc func, gpointer user_data)
148 g_slist_foreach(registered_ct_tables, func, user_data);
151 guint conversation_table_get_num(void)
153 return g_slist_length(registered_ct_tables);
157 register_ct_t *get_conversation_table_by_num(guint table_num)
159 return (register_ct_t *) g_slist_nth_data(registered_ct_tables, table_num);
162 /** Compute the hash value for two given address/port pairs.
163 * (Parameter type is gconstpointer for GHashTable compatibility.)
165 * @param key Conversation. MUST point to a conv_key_t struct.
166 * @return Computed key hash.
169 conversation_hash(gconstpointer v)
171 const conv_key_t *key = (const conv_key_t *)v;
175 ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
176 hash_val += key->port1;
177 ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
178 hash_val += key->port2;
179 hash_val ^= key->conv_id;
184 /** Compare two conversation keys for an exact match.
185 * (Parameter types are gconstpointer for GHashTable compatibility.)
187 * @param key1 First conversation. MUST point to a conv_key_t struct.
188 * @param key2 Second conversation. MUST point to a conv_key_t struct.
189 * @return TRUE if conversations are equal, FALSE otherwise.
192 conversation_equal(gconstpointer key1, gconstpointer key2)
194 const conv_key_t *ck1 = (const conv_key_t *)key1;
195 const conv_key_t *ck2 = (const conv_key_t *)key2;
197 if (ck1->conv_id == ck2->conv_id)
199 if (ck1->port1 == ck2->port1 &&
200 ck1->port2 == ck2->port2 &&
201 ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr1) &&
202 ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr2)) {
206 if (ck1->port2 == ck2->port1 &&
207 ck1->port1 == ck2->port2 &&
208 ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr1) &&
209 ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr2)) {
215 * The addresses, ports, or conversation IDs don't match.
221 reset_conversation_table_data(conv_hash_t *ch)
227 if (ch->conv_array != NULL) {
229 for(i = 0; i < ch->conv_array->len; i++){
230 conv_item_t *conv = &g_array_index(ch->conv_array, conv_item_t, i);
231 g_free((gpointer)conv->src_address.data);
232 g_free((gpointer)conv->dst_address.data);
235 g_array_free(ch->conv_array, TRUE);
238 if (ch->hashtable != NULL) {
239 g_hash_table_destroy(ch->hashtable);
246 const char *get_conversation_address(address *addr, gboolean resolve_names)
249 return ep_address_to_display(addr);
251 return ep_address_to_str(addr);
255 const char *get_conversation_port(guint32 port, port_type ptype, gboolean resolve_names)
258 if(!resolve_names) ptype = PT_NONE;
262 return ep_tcp_port_to_display(port);
264 return ep_udp_port_to_display(port);
266 return ep_sctp_port_to_display(port);
268 return ep_strdup_printf("%d", port);
272 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
273 a port_type and a name_type (FN_...)
274 return a string for the filter name.
276 Some addresses, like AT_ETHER may actually be any of multiple types
277 of protocols, either ethernet, tokenring, fddi, wlan etc so we must be
278 more specific there; that's why we need specific_addr_type.
281 conversation_get_filter_name(conv_item_t *conv_item, conv_filter_type_e filter_type)
284 if ((conv_item == NULL) || (conv_item->dissector_info == NULL) || (conv_item->dissector_info->get_filter_type == NULL)) {
285 return CONV_FILTER_INVALID;
288 return conv_item->dissector_info->get_filter_type(conv_item, filter_type);
291 /* Convert a port number into a string or NULL */
293 ct_port_to_str(port_type ptype, guint32 port)
300 return g_strdup_printf("%d", port);
307 const char *get_conversation_filter(conv_item_t *conv_item, conv_direction_e direction)
310 const char *str = "INVALID";
312 sport = ct_port_to_str(conv_item->ptype, conv_item->src_port);
313 dport = ct_port_to_str(conv_item->ptype, conv_item->dst_port);
316 case CONV_DIR_A_TO_FROM_B:
318 str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
319 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
320 ep_address_to_str(&conv_item->src_address),
322 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
325 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
326 ep_address_to_str(&conv_item->dst_address),
328 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
333 case CONV_DIR_A_TO_B:
335 str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
336 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
337 ep_address_to_str(&conv_item->src_address),
339 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
342 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
343 ep_address_to_str(&conv_item->dst_address),
345 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
350 case CONV_DIR_A_FROM_B:
352 str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
353 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
354 ep_address_to_str(&conv_item->src_address),
356 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
359 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
360 ep_address_to_str(&conv_item->dst_address),
362 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
367 case CONV_DIR_A_TO_FROM_ANY:
369 str = ep_strdup_printf("%s==%s%s%s%s%s",
370 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
371 ep_address_to_str(&conv_item->src_address),
373 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
378 case CONV_DIR_A_TO_ANY:
380 str = ep_strdup_printf("%s==%s%s%s%s%s",
381 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
382 ep_address_to_str(&conv_item->src_address),
384 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
389 case CONV_DIR_A_FROM_ANY:
391 str = ep_strdup_printf("%s==%s%s%s%s%s",
392 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
393 ep_address_to_str(&conv_item->src_address),
395 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
400 case CONV_DIR_ANY_TO_FROM_B:
402 str = ep_strdup_printf("%s==%s%s%s%s%s",
403 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
404 ep_address_to_str(&conv_item->dst_address),
406 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
411 case CONV_DIR_ANY_FROM_B:
413 str = ep_strdup_printf("%s==%s%s%s%s%s",
414 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
415 ep_address_to_str(&conv_item->dst_address),
417 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
422 case CONV_DIR_ANY_TO_B:
424 str = ep_strdup_printf("%s==%s%s%s%s%s",
425 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
426 ep_address_to_str(&conv_item->dst_address),
428 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
442 add_conversation_table_data(conv_hash_t *ch, const address *src, const address *dst, guint32 src_port, guint32 dst_port, int num_frames, int num_bytes,
443 nstime_t *ts, nstime_t *abs_ts, ct_dissector_info_t *ct_info, port_type ptype)
445 add_conversation_table_data_with_conv_id(ch, src, dst, src_port, dst_port, CONV_ID_UNSET, num_frames, num_bytes, ts, abs_ts, ct_info, ptype);
449 add_conversation_table_data_with_conv_id(
460 ct_dissector_info_t *ct_info,
463 const address *addr1, *addr2;
464 guint32 port1, port2;
465 conv_item_t *conv_item = NULL;
466 unsigned int conversation_idx = 0;
468 if (src_port > dst_port) {
473 } else if (src_port < dst_port) {
478 } else if (CMP_ADDRESS(src, dst) < 0) {
490 /* if we dont have any entries at all yet */
491 if (ch->conv_array == NULL) {
492 ch->conv_array = g_array_sized_new(FALSE, FALSE, sizeof(conv_item_t), 10000);
494 ch->hashtable = g_hash_table_new_full(conversation_hash,
495 conversation_equal, /* key_equal_func */
496 g_free, /* key_destroy_func */
497 NULL); /* value_destroy_func */
500 /* try to find it among the existing known conversations */
501 conv_key_t existing_key;
503 existing_key.addr1 = *addr1;
504 existing_key.addr2 = *addr2;
505 existing_key.port1 = port1;
506 existing_key.port2 = port2;
507 existing_key.conv_id = conv_id;
508 if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, (gpointer *) &conversation_idx)) {
509 conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
513 /* if we still dont know what conversation this is it has to be a new one
514 and we have to allocate it and append it to the end of the list */
515 if (conv_item == NULL) {
517 conv_item_t new_conv_item;
519 COPY_ADDRESS(&new_conv_item.src_address, addr1);
520 COPY_ADDRESS(&new_conv_item.dst_address, addr2);
521 new_conv_item.dissector_info = ct_info;
522 new_conv_item.ptype = ptype;
523 new_conv_item.src_port = port1;
524 new_conv_item.dst_port = port2;
525 new_conv_item.conv_id = conv_id;
526 new_conv_item.rx_frames = 0;
527 new_conv_item.tx_frames = 0;
528 new_conv_item.rx_bytes = 0;
529 new_conv_item.tx_bytes = 0;
530 new_conv_item.modified = TRUE;
533 memcpy(&new_conv_item.start_time, ts, sizeof(new_conv_item.start_time));
534 memcpy(&new_conv_item.stop_time, ts, sizeof(new_conv_item.stop_time));
535 memcpy(&new_conv_item.start_abs_time, abs_ts, sizeof(new_conv_item.start_abs_time));
537 nstime_set_unset(&new_conv_item.start_abs_time);
538 nstime_set_unset(&new_conv_item.start_time);
539 nstime_set_unset(&new_conv_item.stop_time);
541 g_array_append_val(ch->conv_array, new_conv_item);
542 conversation_idx = ch->conv_array->len - 1;
543 conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
545 /* ct->conversations address is not a constant but src/dst_address.data are */
546 new_key = g_new(conv_key_t, 1);
547 SET_ADDRESS(&new_key->addr1, conv_item->src_address.type, conv_item->src_address.len, conv_item->src_address.data);
548 SET_ADDRESS(&new_key->addr2, conv_item->dst_address.type, conv_item->dst_address.len, conv_item->dst_address.data);
549 new_key->port1 = port1;
550 new_key->port2 = port2;
551 new_key->conv_id = conv_id;
552 g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(conversation_idx));
555 /* update the conversation struct */
556 conv_item->modified = TRUE;
557 if ( (!CMP_ADDRESS(src, addr1)) && (!CMP_ADDRESS(dst, addr2)) && (src_port==port1) && (dst_port==port2) ) {
558 conv_item->tx_frames += num_frames;
559 conv_item->tx_bytes += num_bytes;
561 conv_item->rx_frames += num_frames;
562 conv_item->rx_bytes += num_bytes;
566 if (nstime_cmp(ts, &conv_item->stop_time) > 0) {
567 memcpy(&conv_item->stop_time, ts, sizeof(conv_item->stop_time));
568 } else if (nstime_cmp(ts, &conv_item->start_time) < 0) {
569 memcpy(&conv_item->start_time, ts, sizeof(conv_item->start_time));
570 memcpy(&conv_item->start_abs_time, abs_ts, sizeof(conv_item->start_abs_time));
581 * indent-tabs-mode: nil
584 * ex: set shiftwidth=4 tabstop=8 expandtab:
585 * :indentSize=4:tabSize=8:noTabs=true: