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 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "packet_info.h"
18 #include "conversation_table.h"
19 #include "addr_resolv.h"
20 #include "address_types.h"
22 #include "stat_tap_ui.h"
25 gboolean hide_ports; /* hide TCP / UDP port columns */
26 int proto_id; /* protocol id (0-indexed) */
27 tap_packet_cb conv_func; /* function to be called for new incoming packets for conversation*/
28 tap_packet_cb host_func; /* function to be called for new incoming packets for hostlist */
29 conv_gui_init_cb conv_gui_init; /* GUI specific function to initialize conversation */
30 host_gui_init_cb host_gui_init; /* GUI specific function to initialize hostlist */
33 gboolean get_conversation_hide_ports(register_ct_t* ct)
35 return ct->hide_ports;
38 int get_conversation_proto_id(register_ct_t* ct)
46 tap_packet_cb get_conversation_packet_func(register_ct_t* ct)
51 tap_packet_cb get_hostlist_packet_func(register_ct_t* ct)
56 static wmem_tree_t *registered_ct_tables = NULL;
59 dissector_conversation_init(const char *opt_arg, void* userdata)
61 register_ct_t *table = (register_ct_t*)userdata;
62 GString *cmd_str = g_string_new("conv,");
63 const char *filter=NULL;
65 g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
66 if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
67 if (opt_arg[cmd_str->len] == ',') {
68 filter = opt_arg + cmd_str->len + 1;
71 g_string_free(cmd_str, TRUE);
73 if (table->conv_gui_init)
74 table->conv_gui_init(table, filter);
78 dissector_hostlist_init(const char *opt_arg, void* userdata)
80 register_ct_t *table = (register_ct_t*)userdata;
81 GString *cmd_str = g_string_new("");
82 const char *filter=NULL;
84 g_string_printf(cmd_str, "%s,%s", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
85 if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
86 if (opt_arg[cmd_str->len] == ',') {
87 filter = opt_arg + cmd_str->len + 1;
93 g_string_free(cmd_str, TRUE);
95 if (table->host_gui_init)
96 table->host_gui_init(table, filter);
98 /** get conversation from protocol ID
100 * @param proto_id protocol ID
101 * @return tap function handler of conversation
103 register_ct_t* get_conversation_by_proto_id(int proto_id)
105 return (register_ct_t*)wmem_tree_lookup_string(registered_ct_tables, proto_get_protocol_short_name(find_protocol_by_id(proto_id)), 0);
109 register_conversation_table(const int proto_id, gboolean hide_ports, tap_packet_cb conv_packet_func, tap_packet_cb hostlist_func)
111 register_ct_t *table;
113 table = wmem_new(wmem_epan_scope(), register_ct_t);
115 table->hide_ports = hide_ports;
116 table->proto_id = proto_id;
117 table->conv_func = conv_packet_func;
118 table->host_func = hostlist_func;
119 table->conv_gui_init = NULL;
120 table->host_gui_init = NULL;
122 if (registered_ct_tables == NULL)
123 registered_ct_tables = wmem_tree_new(wmem_epan_scope());
125 wmem_tree_insert_string(registered_ct_tables, proto_get_protocol_short_name(find_protocol_by_id(proto_id)), table, 0);
128 /* Set GUI fields for register_ct list */
130 set_conv_gui_data(const void *key _U_, void *value, void *userdata)
132 GString *conv_cmd_str = g_string_new("conv,");
134 register_ct_t *table = (register_ct_t*)value;
136 table->conv_gui_init = (conv_gui_init_cb)userdata;
138 g_string_append(conv_cmd_str, proto_get_protocol_filter_name(table->proto_id));
139 ui_info.group = REGISTER_STAT_GROUP_CONVERSATION_LIST;
140 ui_info.title = NULL; /* construct this from the protocol info? */
141 ui_info.cli_string = g_string_free(conv_cmd_str, FALSE);
142 ui_info.tap_init_cb = dissector_conversation_init;
144 ui_info.params = NULL;
145 register_stat_tap_ui(&ui_info, table);
146 g_free((char*)ui_info.cli_string);
150 void conversation_table_set_gui_info(conv_gui_init_cb init_cb)
152 wmem_tree_foreach(registered_ct_tables, set_conv_gui_data, (void*)init_cb);
156 set_host_gui_data(const void *key _U_, void *value, void *userdata)
159 register_ct_t *table = (register_ct_t*)value;
161 table->host_gui_init = (host_gui_init_cb)userdata;
163 ui_info.group = REGISTER_STAT_GROUP_ENDPOINT_LIST;
164 ui_info.title = NULL; /* construct this from the protocol info? */
165 ui_info.cli_string = g_strdup_printf("%s,%s", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
166 ui_info.tap_init_cb = dissector_hostlist_init;
168 ui_info.params = NULL;
169 register_stat_tap_ui(&ui_info, table);
170 g_free((char*)ui_info.cli_string);
174 void hostlist_table_set_gui_info(host_gui_init_cb init_cb)
176 wmem_tree_foreach(registered_ct_tables, set_host_gui_data, (void*)init_cb);
179 void conversation_table_iterate_tables(wmem_foreach_func func, void* user_data)
181 wmem_tree_foreach(registered_ct_tables, func, user_data);
184 guint conversation_table_get_num(void)
186 return wmem_tree_count(registered_ct_tables);
189 /** Compute the hash value for two given address/port pairs.
190 * (Parameter type is gconstpointer for GHashTable compatibility.)
192 * @param v Conversation Key. MUST point to a conv_key_t struct.
193 * @return Computed key hash.
196 conversation_hash(gconstpointer v)
198 const conv_key_t *key = (const conv_key_t *)v;
202 hash_val = add_address_to_hash(hash_val, &key->addr1);
203 hash_val += key->port1;
204 hash_val = add_address_to_hash(hash_val, &key->addr2);
205 hash_val += key->port2;
206 hash_val ^= key->conv_id;
211 /** Compare two conversation keys for an exact match.
212 * (Parameter types are gconstpointer for GHashTable compatibility.)
214 * @param key1 First conversation. MUST point to a conv_key_t struct.
215 * @param key2 Second conversation. MUST point to a conv_key_t struct.
216 * @return TRUE if conversations are equal, FALSE otherwise.
219 conversation_equal(gconstpointer key1, gconstpointer key2)
221 const conv_key_t *ck1 = (const conv_key_t *)key1;
222 const conv_key_t *ck2 = (const conv_key_t *)key2;
224 if (ck1->conv_id == ck2->conv_id)
226 if (ck1->port1 == ck2->port1 &&
227 ck1->port2 == ck2->port2 &&
228 addresses_equal(&ck1->addr1, &ck2->addr1) &&
229 addresses_equal(&ck1->addr2, &ck2->addr2)) {
233 if (ck1->port2 == ck2->port1 &&
234 ck1->port1 == ck2->port2 &&
235 addresses_equal(&ck1->addr2, &ck2->addr1) &&
236 addresses_equal(&ck1->addr1, &ck2->addr2)) {
242 * The addresses, ports, or conversation IDs don't match.
248 reset_conversation_table_data(conv_hash_t *ch)
254 if (ch->conv_array != NULL) {
256 for(i = 0; i < ch->conv_array->len; i++){
257 conv_item_t *conv = &g_array_index(ch->conv_array, conv_item_t, i);
258 free_address(&conv->src_address);
259 free_address(&conv->dst_address);
262 g_array_free(ch->conv_array, TRUE);
265 if (ch->hashtable != NULL) {
266 g_hash_table_destroy(ch->hashtable);
273 void reset_hostlist_table_data(conv_hash_t *ch)
279 if (ch->conv_array != NULL) {
281 for(i = 0; i < ch->conv_array->len; i++){
282 hostlist_talker_t *host = &g_array_index(ch->conv_array, hostlist_talker_t, i);
283 free_address(&host->myaddress);
286 g_array_free(ch->conv_array, TRUE);
289 if (ch->hashtable != NULL) {
290 g_hash_table_destroy(ch->hashtable);
297 char *get_conversation_address(wmem_allocator_t *allocator, address *addr, gboolean resolve_names)
300 return address_to_display(allocator, addr);
302 return address_to_str(allocator, addr);
306 char *get_conversation_port(wmem_allocator_t *allocator, guint32 port, endpoint_type etype, gboolean resolve_names)
309 if(!resolve_names) etype = ENDPOINT_NONE;
313 return tcp_port_to_display(allocator, port);
315 return udp_port_to_display(allocator, port);
317 return sctp_port_to_display(allocator, port);
319 return wmem_strdup_printf(allocator, "%d", port);
323 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
324 a endpoint_type and a name_type (FN_...)
325 return a string for the filter name.
327 Some addresses, like AT_ETHER may actually be any of multiple types
328 of protocols, either ethernet, tokenring, fddi, wlan etc so we must be
329 more specific there; that's why we need specific_addr_type.
332 conversation_get_filter_name(conv_item_t *conv_item, conv_filter_type_e filter_type)
335 if ((conv_item == NULL) || (conv_item->dissector_info == NULL) || (conv_item->dissector_info->get_filter_type == NULL)) {
336 return CONV_FILTER_INVALID;
339 return conv_item->dissector_info->get_filter_type(conv_item, filter_type);
343 hostlist_get_filter_name(hostlist_talker_t *host, conv_filter_type_e filter_type)
346 if ((host == NULL) || (host->dissector_info == NULL) || (host->dissector_info->get_filter_type == NULL)) {
347 return CONV_FILTER_INVALID;
350 return host->dissector_info->get_filter_type(host, filter_type);
353 /* Convert a port number into a string or NULL */
355 ct_port_to_str(endpoint_type etype, guint32 port)
362 return g_strdup_printf("%d", port);
369 static int usb_address_type = -1;
371 char *get_conversation_filter(conv_item_t *conv_item, conv_direction_e direction)
373 char *sport, *dport, *src_addr, *dst_addr;
376 /* XXX - Hack until we find something better */
377 if (usb_address_type == -1)
378 usb_address_type = address_type_get_by_name("AT_USB");
380 sport = ct_port_to_str(conv_item->etype, conv_item->src_port);
381 dport = ct_port_to_str(conv_item->etype, conv_item->dst_port);
382 src_addr = address_to_str(NULL, &conv_item->src_address);
383 dst_addr = address_to_str(NULL, &conv_item->dst_address);
385 if (conv_item->src_address.type == AT_STRINGZ || conv_item->src_address.type == usb_address_type) {
388 new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
389 wmem_free(NULL, src_addr);
392 if (conv_item->dst_address.type == AT_STRINGZ || conv_item->dst_address.type == usb_address_type) {
395 new_addr = wmem_strdup_printf(NULL, "\"%s\"", dst_addr);
396 wmem_free(NULL, dst_addr);
401 case CONV_DIR_A_TO_FROM_B:
403 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
404 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
407 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
410 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
413 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
418 case CONV_DIR_A_TO_B:
420 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
421 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
424 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
427 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
430 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
435 case CONV_DIR_A_FROM_B:
437 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
438 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
441 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
444 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
447 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
452 case CONV_DIR_A_TO_FROM_ANY:
454 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
455 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
458 sport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
463 case CONV_DIR_A_TO_ANY:
465 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
466 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
469 sport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
474 case CONV_DIR_A_FROM_ANY:
476 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
477 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
480 sport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
485 case CONV_DIR_ANY_TO_FROM_B:
487 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
488 conversation_get_filter_name(conv_item, CONV_FT_ANY_ADDRESS),
491 dport?conversation_get_filter_name(conv_item, CONV_FT_ANY_PORT):"",
496 case CONV_DIR_ANY_FROM_B:
498 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
499 conversation_get_filter_name(conv_item, CONV_FT_SRC_ADDRESS),
502 dport?conversation_get_filter_name(conv_item, CONV_FT_SRC_PORT):"",
507 case CONV_DIR_ANY_TO_B:
509 str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
510 conversation_get_filter_name(conv_item, CONV_FT_DST_ADDRESS),
513 dport?conversation_get_filter_name(conv_item, CONV_FT_DST_PORT):"",
519 str = wmem_strdup(NULL, "INVALID");
524 wmem_free(NULL, src_addr);
525 wmem_free(NULL, dst_addr);
529 char *get_hostlist_filter(hostlist_talker_t *host)
531 char *sport, *src_addr;
534 /* XXX - Hack until we find something better */
535 if (usb_address_type == -1)
536 usb_address_type = address_type_get_by_name("AT_USB");
538 sport = ct_port_to_str(host->etype, host->port);
539 src_addr = address_to_str(NULL, &host->myaddress);
540 if (host->myaddress.type == AT_STRINGZ || host->myaddress.type == usb_address_type) {
543 new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
544 wmem_free(NULL, src_addr);
548 str = g_strdup_printf("%s==%s%s%s%s%s",
549 hostlist_get_filter_name(host, CONV_FT_ANY_ADDRESS),
552 sport?hostlist_get_filter_name(host, CONV_FT_ANY_PORT):"",
557 wmem_free(NULL, src_addr);
562 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,
563 nstime_t *ts, nstime_t *abs_ts, ct_dissector_info_t *ct_info, endpoint_type etype)
565 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, etype);
569 add_conversation_table_data_with_conv_id(
580 ct_dissector_info_t *ct_info,
583 const address *addr1, *addr2;
584 guint32 port1, port2;
585 conv_item_t *conv_item = NULL;
586 unsigned int conversation_idx = 0;
588 if (src_port > dst_port) {
593 } else if (src_port < dst_port) {
598 } else if (cmp_address(src, dst) < 0) {
610 /* if we don't have any entries at all yet */
611 if (ch->conv_array == NULL) {
612 ch->conv_array = g_array_sized_new(FALSE, FALSE, sizeof(conv_item_t), 10000);
614 ch->hashtable = g_hash_table_new_full(conversation_hash,
615 conversation_equal, /* key_equal_func */
616 g_free, /* key_destroy_func */
617 NULL); /* value_destroy_func */
620 /* try to find it among the existing known conversations */
621 conv_key_t existing_key;
622 gpointer conversation_idx_hash_val;
624 existing_key.addr1 = *addr1;
625 existing_key.addr2 = *addr2;
626 existing_key.port1 = port1;
627 existing_key.port2 = port2;
628 existing_key.conv_id = conv_id;
629 if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &conversation_idx_hash_val)) {
630 conv_item = &g_array_index(ch->conv_array, conv_item_t, GPOINTER_TO_UINT(conversation_idx_hash_val));
634 /* if we still don't know what conversation this is it has to be a new one
635 and we have to allocate it and append it to the end of the list */
636 if (conv_item == NULL) {
638 conv_item_t new_conv_item;
640 copy_address(&new_conv_item.src_address, addr1);
641 copy_address(&new_conv_item.dst_address, addr2);
642 new_conv_item.dissector_info = ct_info;
643 new_conv_item.etype = etype;
644 new_conv_item.src_port = port1;
645 new_conv_item.dst_port = port2;
646 new_conv_item.conv_id = conv_id;
647 new_conv_item.rx_frames = 0;
648 new_conv_item.tx_frames = 0;
649 new_conv_item.rx_bytes = 0;
650 new_conv_item.tx_bytes = 0;
653 memcpy(&new_conv_item.start_time, ts, sizeof(new_conv_item.start_time));
654 memcpy(&new_conv_item.stop_time, ts, sizeof(new_conv_item.stop_time));
655 memcpy(&new_conv_item.start_abs_time, abs_ts, sizeof(new_conv_item.start_abs_time));
657 nstime_set_unset(&new_conv_item.start_abs_time);
658 nstime_set_unset(&new_conv_item.start_time);
659 nstime_set_unset(&new_conv_item.stop_time);
661 g_array_append_val(ch->conv_array, new_conv_item);
662 conversation_idx = ch->conv_array->len - 1;
663 conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
665 /* ct->conversations address is not a constant but src/dst_address.data are */
666 new_key = g_new(conv_key_t, 1);
667 set_address(&new_key->addr1, conv_item->src_address.type, conv_item->src_address.len, conv_item->src_address.data);
668 set_address(&new_key->addr2, conv_item->dst_address.type, conv_item->dst_address.len, conv_item->dst_address.data);
669 new_key->port1 = port1;
670 new_key->port2 = port2;
671 new_key->conv_id = conv_id;
672 g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(conversation_idx));
675 /* update the conversation struct */
676 if ( (!cmp_address(src, addr1)) && (!cmp_address(dst, addr2)) && (src_port==port1) && (dst_port==port2) ) {
677 conv_item->tx_frames += num_frames;
678 conv_item->tx_bytes += num_bytes;
680 conv_item->rx_frames += num_frames;
681 conv_item->rx_bytes += num_bytes;
685 if (nstime_cmp(ts, &conv_item->stop_time) > 0) {
686 memcpy(&conv_item->stop_time, ts, sizeof(conv_item->stop_time));
687 } else if (nstime_cmp(ts, &conv_item->start_time) < 0) {
688 memcpy(&conv_item->start_time, ts, sizeof(conv_item->start_time));
689 memcpy(&conv_item->start_abs_time, abs_ts, sizeof(conv_item->start_abs_time));
695 * Compute the hash value for a given address/port pairs if the match
699 host_hash(gconstpointer v)
701 const host_key_t *key = (const host_key_t *)v;
705 hash_val = add_address_to_hash(hash_val, &key->myaddress);
706 hash_val += key->port;
711 * Compare two host keys for an exact match.
714 host_match(gconstpointer v, gconstpointer w)
716 const host_key_t *v1 = (const host_key_t *)v;
717 const host_key_t *v2 = (const host_key_t *)w;
719 if (v1->port == v2->port &&
720 addresses_equal(&v1->myaddress, &v2->myaddress)) {
724 * The addresses or the ports don't match.
730 add_hostlist_table_data(conv_hash_t *ch, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, hostlist_dissector_info_t *host_info, endpoint_type etype)
732 hostlist_talker_t *talker=NULL;
735 /* XXX should be optimized to allocate n extra entries at a time
736 instead of just one */
737 /* if we don't have any entries at all yet */
738 if(ch->conv_array==NULL){
739 ch->conv_array=g_array_sized_new(FALSE, FALSE, sizeof(hostlist_talker_t), 10000);
740 ch->hashtable = g_hash_table_new_full(host_hash,
741 host_match, /* key_equal_func */
742 g_free, /* key_destroy_func */
743 NULL); /* value_destroy_func */
746 /* try to find it among the existing known conversations */
747 host_key_t existing_key;
748 gpointer talker_idx_hash_val;
750 copy_address_shallow(&existing_key.myaddress, addr);
751 existing_key.port = port;
753 if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &talker_idx_hash_val)) {
754 talker = &g_array_index(ch->conv_array, hostlist_talker_t, GPOINTER_TO_UINT(talker_idx_hash_val));
758 /* if we still don't know what talker this is it has to be a new one
759 and we have to allocate it and append it to the end of the list */
762 hostlist_talker_t host;
764 copy_address(&host.myaddress, addr);
765 host.dissector_info = host_info;
772 host.modified = TRUE;
774 g_array_append_val(ch->conv_array, host);
775 talker_idx= ch->conv_array->len - 1;
776 talker=&g_array_index(ch->conv_array, hostlist_talker_t, talker_idx);
778 /* hl->hosts address is not a constant but address.data is */
779 new_key = g_new(host_key_t,1);
780 set_address(&new_key->myaddress, talker->myaddress.type, talker->myaddress.len, talker->myaddress.data);
781 new_key->port = port;
782 g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(talker_idx));
785 /* if this is a new talker we need to initialize the struct */
786 talker->modified = TRUE;
788 /* update the talker struct */
790 talker->tx_frames+=num_frames;
791 talker->tx_bytes+=num_bytes;
793 talker->rx_frames+=num_frames;
794 talker->rx_bytes+=num_bytes;
804 * indent-tabs-mode: nil
807 * ex: set shiftwidth=4 tabstop=8 expandtab:
808 * :indentSize=4:tabSize=8:noTabs=true: