diff options
Diffstat (limited to 'epan/conversation.c')
-rw-r--r-- | epan/conversation.c | 910 |
1 files changed, 399 insertions, 511 deletions
diff --git a/epan/conversation.c b/epan/conversation.c index aef2d5f53a..8fc9bba215 100644 --- a/epan/conversation.c +++ b/epan/conversation.c @@ -18,9 +18,18 @@ #include "to_str.h" #include "conversation.h" -// To do: -// - Switch the "id" routines (conversation_new_by_id, etc) to elements. -// - Switch the rest of the routines to elements. +// The conversation database is a map of maps that contain conversation_t's. +// Top-level map keys are strings that describe each conversation type. +// Second-level map keys are conversation_element_t arrays. +// { +// "uint,endpoint": { +// [ { type: CE_ADDR, addr_val: 10.20.30.40}, { type: CE_PORT, uint_val: 80 } ... ]: <conversation_t> +// [ { type: CE_ADDR, addr_val: 1.1.1.1}, { type: CE_PORT, uint_val: 53 } ... ]: <conversation_t> +// } +// } +// Instead of using strings as keys we could bit-shift conversation endpoint types +// into a guint64, e.g. 0x0000000102010200 for CE_ADDRESS,CE_PORT,CE_ADDRESS,CE_PORT,CE_ENDPOINT. +// We could also use this to prepend a type+length indicator for element arrays. /* define DEBUG_CONVERSATION for pretty debug printing */ /* #define DEBUG_CONVERSATION */ @@ -30,21 +39,33 @@ int _debug_conversation_indent = 0; #endif +/* + * We could use an element list here, but this is effectively a parameter list + * for find_conversation and is more compact. + */ struct endpoint { address addr1; address addr2; - endpoint_type etype; guint32 port1; guint32 port2; + endpoint_type etype; }; -struct conversation_key { - // XXX Replace these with conversation_element_t's. - address addr1; - address addr2; - endpoint_type etype; - guint32 port1; - guint32 port2; +/* Element offsets for address+port conversations */ +enum { + ADDR1_IDX, + PORT1_IDX, + ADDR2_IDX, + PORT2_IDX, + ENDP_EXACT_IDX, + EXACT_IDX_COUNT, + PORT2_NO_ADDR2_IDX = ADDR2_IDX, + ENDP_NO_ADDR2_IDX = PORT2_IDX, + ENDP_NO_PORT2_IDX = PORT2_IDX, + ENDP_NO_ADDR2_PORT2_IDX = ADDR2_IDX, + NO_ADDR2_IDX_COUNT = ENDP_EXACT_IDX, + NO_PORT2_IDX_COUNT = ENDP_EXACT_IDX, + NO_ADDR2_PORT2_IDX_COUNT = PORT2_IDX, }; /* @@ -84,11 +105,12 @@ static guint32 new_index; */ static address null_address_ = ADDRESS_INIT_NONE; -static conversation_t *conversation_lookup_hashtable(wmem_map_t *conversation_hashtable, const guint32 frame_num, conversation_key_t conv_key); /* Element count including the terminating CE_ENDPOINT */ -#define MAX_CONVERSATION_ELEMENTS 10 // Arbitrary. -static size_t conversation_element_count(conversation_element_t *elements) { +#define MAX_CONVERSATION_ELEMENTS 8 // Arbitrary. +static size_t +conversation_element_count(conversation_element_t *elements) +{ size_t count = 0; while (elements[count].type != CE_ENDPOINT) { count++; @@ -100,11 +122,24 @@ static size_t conversation_element_count(conversation_element_t *elements) { return count; } +static endpoint_type +conversation_endpoint_type(conversation_element_t *elements) +{ + size_t count = 0; + while (elements[count].type != CE_ENDPOINT) { + count++; + DISSECTOR_ASSERT(count < MAX_CONVERSATION_ELEMENTS); + } + return elements[count].endpoint_type_val; +} + /* Create a string based on element types. */ -static char* conversation_element_list_name(wmem_allocator_t *allocator, conversation_element_t *elements) { +static char* +conversation_element_list_name(wmem_allocator_t *allocator, conversation_element_t *elements) { const char *type_names[] = { "endpoint", "address", + "port", "string", "uint", "uint64", @@ -120,6 +155,82 @@ static char* conversation_element_list_name(wmem_allocator_t *allocator, convers return wmem_strbuf_finalize(conv_hash_group); } +#if 0 // debugging +static char* conversation_element_list_values(conversation_element_t *elements) { + const char *type_names[] = { + "endpoint", + "address", + "port", + "string", + "uint", + "uint64", + }; + char *sep = ""; + GString *value_str = g_string_new(""); + size_t element_count = conversation_element_count(elements); + for (size_t i = 0; i < element_count; i++) { + conversation_element_t *cur_el = &elements[i]; + g_string_append_printf(value_str, "%s%s=", sep, type_names[cur_el->type]); + sep = ","; + switch (cur_el->type) { + case CE_ENDPOINT: + g_string_append_printf(value_str, "%d", cur_el->endpoint_type_val); + break; + case CE_ADDRESS: + { + char *as = address_to_str(NULL, &cur_el->addr_val); + g_string_append(value_str, as); + g_free(as); + } + break; + case CE_PORT: + g_string_append_printf(value_str, "%u", cur_el->port_val); + break; + case CE_STRING: + g_string_append(value_str, cur_el->str_val); + break; + case CE_UINT: + g_string_append_printf(value_str, "%u", cur_el->uint_val); + break; + case CE_UINT64: + g_string_append_printf(value_str, "%" G_GUINT64_FORMAT, cur_el->uint64_val); + break; + } + } + return g_string_free(value_str, FALSE); +} +#endif + +static bool +is_no_addr2_key(conversation_element_t *key) +{ + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT + && key[PORT2_NO_ADDR2_IDX].type == CE_PORT && key[ENDP_NO_ADDR2_IDX].type == CE_ENDPOINT) { + return true; + } + return false; +} + +static bool +is_no_port2_key(conversation_element_t *key) +{ + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT + && key[ADDR2_IDX].type == CE_ADDRESS && key[ENDP_NO_PORT2_IDX].type == CE_ENDPOINT) { + return true; + } + return false; +} + +static bool +is_no_addr2_port2_key(conversation_element_t *key) +{ + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT + && key[ENDP_NO_ADDR2_PORT2_IDX].type == CE_ENDPOINT) { + return true; + } + return false; +} + /* * Creates a new conversation with known endpoints based on a conversation * created with the CONVERSATION_TEMPLATE option while keeping the @@ -137,12 +248,12 @@ static char* conversation_element_list_name(wmem_allocator_t *allocator, convers static conversation_t * conversation_create_from_template(conversation_t *conversation, const address *addr2, const guint32 port2) { + endpoint_type etype = conversation_endpoint_type(conversation->key_ptr); /* * Add a new conversation and keep the conversation template only if the * CONVERSATION_TEMPLATE bit is set for a connection oriented protocol. */ - if (conversation->options & CONVERSATION_TEMPLATE && - conversation->key_ptr->etype != ENDPOINT_UDP) + if (conversation->options & CONVERSATION_TEMPLATE && etype != ENDPOINT_UDP) { /* * Set up a new options mask where the conversation template bit and the @@ -154,8 +265,8 @@ conversation_create_from_template(conversation_t *conversation, const address *a /* * Are both the NO_ADDR2 and NO_PORT2 wildcards set in the options mask? */ - if (conversation->options & NO_ADDR2 && - conversation->options & NO_PORT2) + if (conversation->options & NO_ADDR2 && conversation->options & NO_PORT2 + && is_no_addr2_port2_key(conversation->key_ptr)) { /* * The conversation template was created without knowledge of both @@ -164,11 +275,11 @@ conversation_create_from_template(conversation_t *conversation, const address *a */ new_conversation_from_template = conversation_new(conversation->setup_frame, - &conversation->key_ptr->addr1, addr2, - conversation->key_ptr->etype, conversation->key_ptr->port1, + &conversation->key_ptr[ADDR1_IDX].addr_val, addr2, + etype, conversation->key_ptr[PORT1_IDX].port_val, port2, options); } - else if (conversation->options & NO_PORT2) + else if (conversation->options & NO_PORT2 && is_no_port2_key(conversation->key_ptr)) { /* * The conversation template was created without knowledge of port 2 @@ -176,11 +287,11 @@ conversation_create_from_template(conversation_t *conversation, const address *a */ new_conversation_from_template = conversation_new(conversation->setup_frame, - &conversation->key_ptr->addr1, &conversation->key_ptr->addr2, - conversation->key_ptr->etype, conversation->key_ptr->port1, + &conversation->key_ptr[ADDR1_IDX].addr_val, &conversation->key_ptr[ADDR2_IDX].addr_val, + etype, conversation->key_ptr[PORT1_IDX].port_val, port2, options); } - else if (conversation->options & NO_ADDR2) + else if (conversation->options & NO_ADDR2 && is_no_addr2_key(conversation->key_ptr)) { /* * The conversation template was created without knowledge of address @@ -188,9 +299,9 @@ conversation_create_from_template(conversation_t *conversation, const address *a */ new_conversation_from_template = conversation_new(conversation->setup_frame, - &conversation->key_ptr->addr1, addr2, - conversation->key_ptr->etype, conversation->key_ptr->port1, - conversation->key_ptr->port2, options); + &conversation->key_ptr[ADDR1_IDX].addr_val, addr2, + etype, conversation->key_ptr[PORT1_IDX].port_val, + conversation->key_ptr[PORT2_NO_ADDR2_IDX].port_val, options); } else { @@ -217,282 +328,6 @@ conversation_create_from_template(conversation_t *conversation, const address *a } /* - * Compute the hash value for two given address/port pairs if the match - * is to be exact. - */ -/* https://web.archive.org/web/20070615045827/http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx#existing - * (formerly at http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx#existing) - * One-at-a-Time hash - */ -guint -conversation_hash_exact(gconstpointer v) -{ - const conversation_key_t key = (const conversation_key_t)v; - guint hash_val; - address tmp_addr; - - hash_val = 0; - tmp_addr.len = 4; - - hash_val = add_address_to_hash(hash_val, &key->addr1); - - tmp_addr.data = &key->port1; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - hash_val = add_address_to_hash(hash_val, &key->addr2); - - tmp_addr.data = &key->port2; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - hash_val += ( hash_val << 3 ); - hash_val ^= ( hash_val >> 11 ); - hash_val += ( hash_val << 15 ); - - return hash_val; -} - -/* - * Compare two conversation keys for an exact match. - */ -static gint -conversation_match_exact(gconstpointer v, gconstpointer w) -{ - const conversation_key_t v1 = (const conversation_key_t)v; - const conversation_key_t v2 = (const conversation_key_t)w; - - if (v1->etype != v2->etype) - return 0; /* different types of port */ - - /* - * Are the first and second port 1 values the same, the first and - * second port 2 values the same, the first and second address - * 1 values the same, and the first and second address 2 values - * the same? - */ - if (v1->port1 == v2->port1 && - v1->port2 == v2->port2 && - addresses_equal(&v1->addr1, &v2->addr1) && - addresses_equal(&v1->addr2, &v2->addr2)) { - /* - * Yes. It's the same conversation, and the two - * address/port pairs are going in the same direction. - */ - return 1; - } - - /* - * Is the first port 2 the same as the second port 1, the first - * port 1 the same as the second port 2, the first address 2 - * the same as the second address 1, and the first address 1 - * the same as the second address 2? - */ - if (v1->port2 == v2->port1 && - v1->port1 == v2->port2 && - addresses_equal(&v1->addr2, &v2->addr1) && - addresses_equal(&v1->addr1, &v2->addr2)) { - /* - * Yes. It's the same conversation, and the two - * address/port pairs are going in opposite directions. - */ - return 1; - } - - /* - * The addresses or the ports don't match. - */ - return 0; -} - -/* - * Compute the hash value for two given address/port pairs if the match - * has a wildcard address 2. - */ -static guint -conversation_hash_no_addr2(gconstpointer v) -{ - const conversation_key_t key = (const conversation_key_t)v; - guint hash_val; - address tmp_addr; - - hash_val = 0; - tmp_addr.len = 4; - - hash_val = add_address_to_hash(hash_val, &key->addr1); - - tmp_addr.data = &key->port1; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - tmp_addr.data = &key->port2; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - hash_val += ( hash_val << 3 ); - hash_val ^= ( hash_val >> 11 ); - hash_val += ( hash_val << 15 ); - - return hash_val; -} - -/* - * Compare two conversation keys, except for the address 2 value. - * We don't check both directions of the conversation - the routine - * doing the hash lookup has to do two searches, as the hash key - * will be different for the two directions. - */ -static gint -conversation_match_no_addr2(gconstpointer v, gconstpointer w) -{ - const conversation_key_t v1 = (const conversation_key_t)v; - const conversation_key_t v2 = (const conversation_key_t)w; - - if (v1->etype != v2->etype) - return 0; /* different types of port */ - - /* - * Are the first and second port 1 values the same, the first and - * second port 2 valuess the same, and the first and second - * address 1 values the same? - */ - if (v1->port1 == v2->port1 && - v1->port2 == v2->port2 && - addresses_equal(&v1->addr1, &v2->addr1)) { - /* - * Yes. It's the same conversation, and the two - * address/port pairs are going in the same direction. - */ - return 1; - } - - /* - * The addresses or the ports don't match. - */ - return 0; -} - -/* - * Compute the hash value for two given address/port pairs if the match - * has a wildcard port 2. - */ -static guint -conversation_hash_no_port2(gconstpointer v) -{ - const conversation_key_t key = (const conversation_key_t)v; - guint hash_val; - address tmp_addr; - - hash_val = 0; - tmp_addr.len = 4; - - hash_val = add_address_to_hash(hash_val, &key->addr1); - - tmp_addr.data = &key->port1; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - hash_val = add_address_to_hash(hash_val, &key->addr2); - - hash_val += ( hash_val << 3 ); - hash_val ^= ( hash_val >> 11 ); - hash_val += ( hash_val << 15 ); - - return hash_val; -} - -/* - * Compare two conversation keys, except for the port 2 value. - * We don't check both directions of the conversation - the routine - * doing the hash lookup has to do two searches, as the hash key - * will be different for the two directions. - */ -static gint -conversation_match_no_port2(gconstpointer v, gconstpointer w) -{ - const conversation_key_t v1 = (const conversation_key_t)v; - const conversation_key_t v2 = (const conversation_key_t)w; - - if (v1->etype != v2->etype) - return 0; /* different types of port */ - - /* - * Are the first and second port 1 values the same, the first and - * second address 1 values the same, and the first and second - * address 2 values the same? - */ - if (v1->port1 == v2->port1 && - addresses_equal(&v1->addr1, &v2->addr1) && - addresses_equal(&v1->addr2, &v2->addr2)) { - /* - * Yes. It's the same conversation, and the two - * address/port pairs are going in the same direction. - */ - return 1; - } - - /* - * The addresses or the ports don't match. - */ - return 0; -} - -/* - * Compute the hash value for two given address/port pairs if the match - * has a wildcard address 2 and port 2. - */ -static guint -conversation_hash_no_addr2_or_port2(gconstpointer v) -{ - const conversation_key_t key = (const conversation_key_t)v; - guint hash_val; - address tmp_addr; - - hash_val = 0; - tmp_addr.len = 4; - - hash_val = add_address_to_hash(hash_val, &key->addr1); - - tmp_addr.data = &key->port1; - hash_val = add_address_to_hash(hash_val, &tmp_addr); - - hash_val += ( hash_val << 3 ); - hash_val ^= ( hash_val >> 11 ); - hash_val += ( hash_val << 15 ); - - return hash_val; -} - -/* - * Compare the address 1 and port 1 in the two conversation keys. - * We don't check both directions of the conversation - the routine - * doing the hash lookup has to do two searches, as the hash key - * will be different for the two directions. - */ -static gint -conversation_match_no_addr2_or_port2(gconstpointer v, gconstpointer w) -{ - const conversation_key_t v1 = (const conversation_key_t)v; - const conversation_key_t v2 = (const conversation_key_t)w; - - if (v1->etype != v2->etype) - return 0; /* different types of port */ - - /* - * Are the first and second port 1 values the same and the first - * and second address 1 values the same? - */ - if (v1->port1 == v2->port1 && - addresses_equal(&v1->addr1, &v2->addr1)) { - /* - * Yes. It's the same conversation, and the two - * address/port pairs are going in the same direction. - */ - return 1; - } - - /* - * The addresses or the ports don't match. - */ - return 0; -} - -/* * Compute the hash value for two given element lists if the match * is to be exact. */ @@ -513,6 +348,11 @@ conversation_hash_element_list(gconstpointer v) case CE_ADDRESS: hash_val = add_address_to_hash(hash_val, &element->addr_val); break; + case CE_PORT: + tmp_addr.len = (int) sizeof(element->port_val); + tmp_addr.data = &element->port_val; + hash_val = add_address_to_hash(hash_val, &tmp_addr); + break; case CE_STRING: tmp_addr.len = (int) strlen(element->str_val); tmp_addr.data = element->str_val; @@ -566,6 +406,11 @@ conversation_match_element_list(gconstpointer v1, gconstpointer v2) return FALSE; } break; + case CE_PORT: + if (element1->port_val != element2->port_val) { + return FALSE; + } + break; case CE_STRING: if (strcmp(element1->str_val, element2->str_val)) { return FALSE; @@ -611,21 +456,59 @@ conversation_init(void) * pointed to by conversation data structures that were freed * above. */ - conversation_hashtable_exact_addr_port = - wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), conversation_hash_exact, - conversation_match_exact); - conversation_hashtable_no_addr2 = - wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), conversation_hash_no_addr2, - conversation_match_no_addr2); - conversation_hashtable_no_port2 = - wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), conversation_hash_no_port2, - conversation_match_no_port2); - conversation_hashtable_no_addr2_or_port2 = - wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), conversation_hash_no_addr2_or_port2, - conversation_match_no_addr2_or_port2); - - conversation_hashtable_element_list = - wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal); + conversation_hashtable_element_list = wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal); + + conversation_element_t exact_elements[EXACT_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_PORT, .port_val = 0 }, + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_PORT, .port_val = 0 }, + { CE_ENDPOINT, .endpoint_type_val = ENDPOINT_NONE } + }; + char *exact_map_key = conversation_element_list_name(wmem_epan_scope(), exact_elements); + conversation_hashtable_exact_addr_port = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), + conversation_hash_element_list, + conversation_match_element_list); + wmem_map_insert(conversation_hashtable_element_list, wmem_strdup(wmem_epan_scope(), exact_map_key), + conversation_hashtable_exact_addr_port); + + conversation_element_t no_addr2_elements[NO_ADDR2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_PORT, .port_val = 0 }, + { CE_PORT, .port_val = 0 }, + { CE_ENDPOINT, .endpoint_type_val = ENDPOINT_NONE } + }; + char *no_addr2_map_key = conversation_element_list_name(wmem_epan_scope(), no_addr2_elements); + conversation_hashtable_no_addr2 = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), + conversation_hash_element_list, + conversation_match_element_list); + wmem_map_insert(conversation_hashtable_element_list, wmem_strdup(wmem_epan_scope(), no_addr2_map_key), + conversation_hashtable_no_addr2); + + conversation_element_t no_port2_elements[NO_PORT2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_PORT, .port_val = 0 }, + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_ENDPOINT, .endpoint_type_val = ENDPOINT_NONE } + }; + char *no_port2_map_key = conversation_element_list_name(wmem_epan_scope(), no_port2_elements); + conversation_hashtable_no_port2 = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), + conversation_hash_element_list, + conversation_match_element_list); + wmem_map_insert(conversation_hashtable_element_list, wmem_strdup(wmem_epan_scope(), no_port2_map_key), + conversation_hashtable_no_port2); + + conversation_element_t no_addr2_or_port2_elements[NO_ADDR2_PORT2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = ADDRESS_INIT_NONE }, + { CE_PORT, .port_val = 0 }, + { CE_ENDPOINT, .endpoint_type_val = ENDPOINT_NONE } + }; + char *no_addr2_or_port2_map_key = conversation_element_list_name(wmem_epan_scope(), no_addr2_or_port2_elements); + conversation_hashtable_no_addr2_or_port2 = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), + conversation_hash_element_list, + conversation_match_element_list); + wmem_map_insert(conversation_hashtable_element_list, wmem_strdup(wmem_epan_scope(), no_addr2_or_port2_map_key), + conversation_hashtable_no_addr2_or_port2); conversation_element_t id_elements[2] = { { CE_UINT, .uint_val = 0 }, @@ -770,48 +653,6 @@ conversation_remove_from_hashtable(wmem_map_t *hashtable, conversation_t *conv) } } -#if 0 // debugging -static char* conversation_element_list_values(conversation_element_t *elements) { - const char *type_names[] = { - "endpoint", - "address", - "string", - "uint", - "uint64", - }; - char *sep = ""; - GString *value_str = g_string_new(""); - size_t element_count = conversation_element_count(elements); - for (size_t i = 0; i < element_count; i++) { - conversation_element_t *cur_el = &elements[i]; - g_string_append_printf(value_str, "%s%s=", sep, type_names[cur_el->type]); - sep = ","; - switch (cur_el->type) { - case CE_ADDRESS: - { - char *as = address_to_str(NULL, &cur_el->addr_val); - g_string_append(value_str, as); - g_free(as); - } - break; - case CE_ENDPOINT: - g_string_append_printf(value_str, "%d", cur_el->endpoint_type_val); - break; - case CE_STRING: - g_string_append(value_str, cur_el->str_val); - break; - case CE_UINT: - g_string_append_printf(value_str, "%u", cur_el->uint_val); - break; - case CE_UINT64: - g_string_append_printf(value_str, "%" G_GUINT64_FORMAT, cur_el->uint64_val); - break; - } - } - return g_string_free(value_str, FALSE); -} -#endif - conversation_t *conversation_new_full(const guint32 setup_frame, conversation_element_t *elements) { DISSECTOR_ASSERT(elements); @@ -840,8 +681,7 @@ conversation_t *conversation_new_full(const guint32 setup_frame, conversation_el new_index++; - // XXX Overloading conversation_key_t this way is terrible and we shouldn't do it. - conversation->key_ptr = (conversation_key_t) conv_key; + conversation->key_ptr = conv_key; conversation_insert_into_hashtable(el_list_map, conversation); return conversation; } @@ -863,8 +703,7 @@ conversation_new(const guint32 setup_frame, const address *addr1, const address "A conversation template may not be constructed without wildcard options"); */ wmem_map_t* hashtable; - conversation_t *conversation=NULL; - conversation_key_t new_key; + conversation_t *conversation = NULL; /* * Verify that the correct options are used, if any. */ @@ -959,34 +798,59 @@ conversation_new(const guint32 setup_frame, const address *addr1, const address } #endif + // Always allocate an "exact"-sized key in case we call conversation_set_port2 + // or conversation_set_addr2 later. + conversation_element_t *new_key = wmem_alloc(wmem_file_scope(), sizeof(conversation_element_t) * EXACT_IDX_COUNT); + size_t addr2_idx = 0; + size_t port2_idx = 0; + size_t endp_idx; + + new_key[ADDR1_IDX].type = CE_ADDRESS; + if (addr1 != NULL) { + copy_address_wmem(wmem_file_scope(), &new_key[ADDR1_IDX].addr_val, addr1); + } else { + clear_address(&new_key[ADDR1_IDX].addr_val); + } + new_key[PORT1_IDX].type = CE_PORT; + new_key[PORT1_IDX].port_val = port1; + if (options & NO_ADDR2) { if (options & (NO_PORT2|NO_PORT2_FORCE)) { hashtable = conversation_hashtable_no_addr2_or_port2; + endp_idx = ENDP_NO_ADDR2_PORT2_IDX; } else { hashtable = conversation_hashtable_no_addr2; + port2_idx = PORT2_NO_ADDR2_IDX; + endp_idx = ENDP_NO_ADDR2_IDX; } } else { + addr2_idx = ADDR2_IDX; if (options & (NO_PORT2|NO_PORT2_FORCE)) { hashtable = conversation_hashtable_no_port2; + endp_idx = ENDP_NO_PORT2_IDX; } else { hashtable = conversation_hashtable_exact_addr_port; + port2_idx = PORT2_IDX; + endp_idx = ENDP_EXACT_IDX; } } - new_key = wmem_new0(wmem_file_scope(), struct conversation_key); - if (addr1 != NULL) { - copy_address_wmem(wmem_file_scope(), &new_key->addr1, addr1); - } else { - clear_address(&new_key->addr1); + if (addr2_idx) { + new_key[addr2_idx].type = CE_ADDRESS; + if (addr2 != NULL) { + copy_address_wmem(wmem_file_scope(), &new_key[addr2_idx].addr_val, addr2); + } else { + clear_address(&new_key[addr2_idx].addr_val); + } } - if (addr2 != NULL) { - copy_address_wmem(wmem_file_scope(), &new_key->addr2, addr2); - } else { - clear_address(&new_key->addr2); + + if (port2_idx) { + new_key[port2_idx].type = CE_PORT; + new_key[port2_idx].port_val = port2; } - new_key->etype = etype; - new_key->port1 = port1; - new_key->port2 = port2; + + new_key[endp_idx].type = CE_ENDPOINT; + new_key[endp_idx].endpoint_type_val = etype; conversation = wmem_new0(wmem_file_scope(), conversation_t); @@ -1020,15 +884,14 @@ conversation_new_by_id(const guint32 setup_frame, const endpoint_type etype, con elements[0].uint_val = id; elements[1].type = CE_ENDPOINT; elements[1].endpoint_type_val = etype; - // XXX Overloading conversation_key_t this way is terrible and we shouldn't do it. - conversation->key_ptr = (conversation_key_t) elements; + conversation->key_ptr = elements; conversation_insert_into_hashtable(conversation_hashtable_id, conversation); return conversation; } /* - * Set the port 2 value in a key. Remove the original from table, + * Set the port 2 value in a key. Remove the original from table, * update the options and port values, insert the updated key. */ void @@ -1051,11 +914,21 @@ conversation_set_port2(conversation_t *conv, const guint32 port) } else { conversation_remove_from_hashtable(conversation_hashtable_no_port2, conv); } + + // Shift our endpoint element over and set our port. We assume that conv->key_ptr + // was created with conversation_new and that we have enough element slots. conv->options &= ~NO_PORT2; - conv->key_ptr->port2 = port; if (conv->options & NO_ADDR2) { + // addr1,port1,endp -> addr1,port1,port2,endp + conv->key_ptr[ENDP_NO_ADDR2_IDX] = conv->key_ptr[ENDP_NO_ADDR2_PORT2_IDX]; + conv->key_ptr[PORT2_NO_ADDR2_IDX].type = CE_PORT; + conv->key_ptr[PORT2_NO_ADDR2_IDX].port_val = port; conversation_insert_into_hashtable(conversation_hashtable_no_addr2, conv); } else { + // addr1,port1,addr2,endp -> addr1,port1,addr2,port2,endp + conv->key_ptr[ENDP_EXACT_IDX] = conv->key_ptr[ENDP_NO_PORT2_IDX]; + conv->key_ptr[PORT2_IDX].type = CE_PORT; + conv->key_ptr[PORT2_IDX].port_val = port; conversation_insert_into_hashtable(conversation_hashtable_exact_addr_port, conv); } DENDENT(); @@ -1088,17 +961,29 @@ conversation_set_addr2(conversation_t *conv, const address *addr) } else { conversation_remove_from_hashtable(conversation_hashtable_no_port2, conv); } + + // Shift our endpoint and, if needed, our port element over and set our address. + // We assume that conv->key_ptr was created with conversation_new and that we have + // enough element slots. conv->options &= ~NO_ADDR2; - copy_address_wmem(wmem_file_scope(), &conv->key_ptr->addr2, addr); + wmem_map_t *hashtable; if (conv->options & NO_PORT2) { - conversation_insert_into_hashtable(conversation_hashtable_no_port2, conv); + // addr1,port1,endp -> addr1,port1,addr2,endp + conv->key_ptr[ENDP_NO_PORT2_IDX] = conv->key_ptr[ENDP_NO_ADDR2_PORT2_IDX]; + hashtable = conversation_hashtable_no_port2; } else { - conversation_insert_into_hashtable(conversation_hashtable_exact_addr_port, conv); + // addr1,port1,port2,endp -> addr1,port1,addr2,port2,endp + conv->key_ptr[ENDP_EXACT_IDX] = conv->key_ptr[ENDP_NO_ADDR2_IDX]; + conv->key_ptr[PORT2_IDX] = conv->key_ptr[PORT2_NO_ADDR2_IDX]; + hashtable = conversation_hashtable_exact_addr_port; } + conv->key_ptr[ADDR2_IDX].type = CE_ADDRESS; + copy_address_wmem(wmem_file_scope(), &conv->key_ptr[ADDR2_IDX].addr_val, addr); + conversation_insert_into_hashtable(hashtable, conv); DENDENT(); } -static conversation_t *conversation_lookup_hashtable(wmem_map_t *conversation_hashtable, const guint32 frame_num, conversation_key_t conv_key) +static conversation_t *conversation_lookup_hashtable(wmem_map_t *conversation_hashtable, const guint32 frame_num, conversation_element_t *conv_key) { conversation_t* convo = NULL; conversation_t* match = NULL; @@ -1137,7 +1022,7 @@ conversation_t *find_conversation_full(const guint32 frame_num, conversation_ele return NULL; } - return conversation_lookup_hashtable(el_list_map, frame_num, (conversation_key_t)elements); + return conversation_lookup_hashtable(el_list_map, frame_num, elements); } /* @@ -1145,30 +1030,67 @@ conversation_t *find_conversation_full(const guint32 frame_num, conversation_ele * {addr1, port1, addr2, port2} and set up before frame_num. */ static conversation_t * -conversation_lookup_addr_port(wmem_map_t *hashtable, const guint32 frame_num, const address *addr1, const address *addr2, - const endpoint_type etype, const guint32 port1, const guint32 port2) +conversation_lookup_exact(const guint32 frame_num, const address *addr1, const guint32 port1, + const address *addr2, const guint32 port2, const endpoint_type etype) { - struct conversation_key key; + conversation_element_t key[EXACT_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = *addr1 }, + { CE_PORT, .port_val = port1 }, + { CE_ADDRESS, .addr_val = *addr2 }, + { CE_PORT, .port_val = port2 }, + { CE_ENDPOINT, .endpoint_type_val = etype }, + }; + return conversation_lookup_hashtable(conversation_hashtable_exact_addr_port, frame_num, key); +} - /* - * We don't make a copy of the address data, we just copy the - * pointer to it, as "key" disappears when we return. - */ - if (addr1 != NULL) { - key.addr1 = *addr1; - } else { - clear_address(&key.addr1); - } - if (addr2 != NULL) { - key.addr2 = *addr2; - } else { - clear_address(&key.addr2); - } - key.etype = etype; - key.port1 = port1; - key.port2 = port2; +/* + * Search a particular hash table for a conversation with the specified + * {addr1, port1, port2} and set up before frame_num. + */ +static conversation_t * +conversation_lookup_no_addr2(const guint32 frame_num, const address *addr1, const guint32 port1, + const guint32 port2, const endpoint_type etype) +{ + conversation_element_t key[NO_ADDR2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = *addr1 }, + { CE_PORT, .port_val = port1 }, + { CE_PORT, .port_val = port2 }, + { CE_ENDPOINT, .endpoint_type_val = etype }, + }; + return conversation_lookup_hashtable(conversation_hashtable_no_addr2, frame_num, key); +} + +/* + * Search a particular hash table for a conversation with the specified + * {addr1, port1, addr2} and set up before frame_num. + */ +static conversation_t * +conversation_lookup_no_port2(const guint32 frame_num, const address *addr1, const guint32 port1, + const address *addr2, const endpoint_type etype) +{ + conversation_element_t key[NO_PORT2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = *addr1 }, + { CE_PORT, .port_val = port1 }, + { CE_ADDRESS, .addr_val = *addr2 }, + { CE_ENDPOINT, .endpoint_type_val = etype }, + }; + return conversation_lookup_hashtable(conversation_hashtable_no_port2, frame_num, key); +} - return conversation_lookup_hashtable(hashtable, frame_num, &key); +/* + * Search a particular hash table for a conversation with the specified + * {addr1, port1, addr2} and set up before frame_num. + */ +static conversation_t * +conversation_lookup_no_addr2_or_port2(const guint32 frame_num, const address *addr1, const guint32 port1, + const endpoint_type etype) +{ + conversation_element_t key[NO_ADDR2_PORT2_IDX_COUNT] = { + { CE_ADDRESS, .addr_val = *addr1 }, + { CE_PORT, .port_val = port1 }, + { CE_ENDPOINT, .endpoint_type_val = etype }, + }; + return conversation_lookup_hashtable(conversation_hashtable_no_addr2_or_port2, frame_num, key); } /* @@ -1229,18 +1151,12 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address */ DPRINT(("trying exact match: %s:%d -> %s:%d", addr_a_str, port_a, addr_b_str, port_b)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_exact_addr_port, - frame_num, addr_a, addr_b, etype, - port_a, port_b); + conversation = conversation_lookup_exact(frame_num, addr_a, port_a, addr_b, port_b, etype); /* Didn't work, try the other direction */ if (conversation == NULL) { DPRINT(("trying exact match: %s:%d -> %s:%d", addr_b_str, port_b, addr_a_str, port_a)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_exact_addr_port, - frame_num, addr_b, addr_a, etype, - port_b, port_a); + conversation = conversation_lookup_exact(frame_num, addr_b, port_b, addr_a, port_a, etype); } if ((conversation == NULL) && (addr_a->type == AT_FC)) { /* In Fibre channel, OXID & RXID are never swapped as @@ -1248,10 +1164,7 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address */ DPRINT(("trying exact match: %s:%d -> %s:%d", addr_b_str, port_a, addr_a_str, port_b)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_exact_addr_port, - frame_num, addr_b, addr_a, etype, - port_a, port_b); + conversation = conversation_lookup_exact(frame_num, addr_b, port_a, addr_a, port_b, etype); } DPRINT(("exact match %sfound",conversation?"":"not ")); if (conversation != NULL) @@ -1274,19 +1187,14 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address */ DPRINT(("trying wildcarded match: %s:%d -> *:%d", addr_a_str, port_a, port_b)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2, - frame_num, addr_a, addr_b, etype, port_a, port_b); + conversation = conversation_lookup_no_addr2(frame_num, addr_a, port_a, port_b, etype); if ((conversation == NULL) && (addr_a->type == AT_FC)) { /* In Fibre channel, OXID & RXID are never swapped as * TCP/UDP ports are in TCP/IP. */ DPRINT(("trying wildcarded match: %s:%d -> *:%d", addr_b_str, port_a, port_b)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2, - frame_num, addr_b, addr_a, etype, - port_a, port_b); + conversation = conversation_lookup_no_addr2(frame_num, addr_b, port_a, port_b, etype); } if (conversation != NULL) { /* @@ -1329,9 +1237,7 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address if (!(options & NO_ADDR_B)) { DPRINT(("trying wildcarded match: %s:%d -> *:%d", addr_b_str, port_b, port_a)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2, - frame_num, addr_b, addr_a, etype, port_b, port_a); + conversation = conversation_lookup_no_addr2(frame_num, addr_b, port_b, port_a, etype); if (conversation != NULL) { /* * If this is for a connection-oriented @@ -1374,17 +1280,13 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address */ DPRINT(("trying wildcarded match: %s:%d -> %s:*", addr_a_str, port_a, addr_b_str)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_port2, - frame_num, addr_a, addr_b, etype, port_a, port_b); + conversation = conversation_lookup_no_port2(frame_num, addr_a, port_a, addr_b, etype); if ((conversation == NULL) && (addr_a->type == AT_FC)) { /* In Fibre channel, OXID & RXID are never swapped as * TCP/UDP ports are in TCP/IP */ DPRINT(("trying wildcarded match: %s:%d -> %s:*", addr_b_str, port_a, addr_a_str)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_port2, - frame_num, addr_b, addr_a, etype, port_a, port_b); + conversation = conversation_lookup_no_port2(frame_num, addr_b, port_a, addr_a, etype); } if (conversation != NULL) { /* @@ -1427,9 +1329,7 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address if (!(options & NO_PORT_B)) { DPRINT(("trying wildcarded match: %s:%d -> %s:*", addr_b_str, port_b, addr_a_str)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_port2, - frame_num, addr_b, addr_a, etype, port_b, port_a); + conversation = conversation_lookup_no_port2(frame_num, addr_b, port_b, addr_a, etype); if (conversation != NULL) { /* * If this is for a connection-oriented @@ -1466,9 +1366,7 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address * (Neither "addr_b" nor "port_b" take part in this lookup.) */ DPRINT(("trying wildcarded match: %s:%d -> *:*", addr_a_str, port_a)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2_or_port2, - frame_num, addr_a, addr_b, etype, port_a, port_b); + conversation = conversation_lookup_no_addr2_or_port2(frame_num, addr_a, port_a, etype); if (conversation != NULL) { /* * If this is for a connection-oriented protocol: @@ -1521,15 +1419,11 @@ find_conversation(const guint32 frame_num, const address *addr_a, const address if ((addr_a != NULL) && (addr_a->type == AT_FC)) { DPRINT(("trying wildcarded match: %s:%d -> *:*", addr_b_str, port_a)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2_or_port2, - frame_num, addr_b, addr_a, etype, port_a, port_b); + conversation = conversation_lookup_no_addr2_or_port2(frame_num, addr_b, port_a, etype); } else { DPRINT(("trying wildcarded match: %s:%d -> *:*", addr_b_str, port_b)); - conversation = - conversation_lookup_addr_port(conversation_hashtable_no_addr2_or_port2, - frame_num, addr_b, addr_a, etype, port_b, port_a); + conversation = conversation_lookup_no_addr2_or_port2(frame_num, addr_b, port_b, etype); } if (conversation != NULL) { /* @@ -1578,7 +1472,7 @@ find_conversation_by_id(const guint32 frame, const endpoint_type etype, const gu { CE_ENDPOINT, .endpoint_type_val = etype } }; - return conversation_lookup_hashtable(conversation_hashtable_id, frame, (conversation_key_t)elements); + return conversation_lookup_hashtable(conversation_hashtable_id, frame, elements); } void @@ -1762,7 +1656,7 @@ try_conversation_dissector_by_id(const endpoint_type etype, const guint32 id, tv conversation_t * find_conversation_pinfo(packet_info *pinfo, const guint options) { - conversation_t *conv=NULL; + conversation_t *conv = NULL; DINSTR(gchar *src_str = address_to_str(NULL, &pinfo->src)); DINSTR(gchar *dst_str = address_to_str(NULL, &pinfo->dst)); @@ -1780,7 +1674,7 @@ find_conversation_pinfo(packet_info *pinfo, const guint options) pinfo->conv_endpoint->etype, pinfo->conv_endpoint->port1, pinfo->conv_endpoint->port2, 0)) != NULL) { DPRINT(("found previous conversation for frame #%u (last_frame=%d)", - pinfo->num, conv->last_frame)); + pinfo->num, conv->last_frame)); if (pinfo->num > conv->last_frame) { conv->last_frame = pinfo->num; } @@ -1859,62 +1753,49 @@ conversation_create_endpoint(struct _packet_info *pinfo, address* addr1, address endpoint_type etype, guint32 port1, guint32 port2) { pinfo->conv_endpoint = wmem_new0(pinfo->pool, struct endpoint); - pinfo->use_endpoint = TRUE; - if (addr1 != NULL) + if (addr1 != NULL) { copy_address_wmem(pinfo->pool, &pinfo->conv_endpoint->addr1, addr1); - - if (addr2 != NULL) + } + if (addr2 != NULL) { copy_address_wmem(pinfo->pool, &pinfo->conv_endpoint->addr2, addr2); + } pinfo->conv_endpoint->etype = etype; pinfo->conv_endpoint->port1 = port1; pinfo->conv_endpoint->port2 = port2; + + pinfo->use_endpoint = TRUE; } void -conversation_create_endpoint_by_id(struct _packet_info *pinfo, - endpoint_type etype, guint32 id) +conversation_create_endpoint_by_id(struct _packet_info *pinfo, endpoint_type etype, guint32 id) { - /* Force the lack of a address or port B */ - conversation_create_endpoint(pinfo, &null_address_, &null_address_, etype, id, 0); + pinfo->conv_elements = wmem_alloc0(pinfo->pool, sizeof(conversation_element_t) * 2); + pinfo->conv_elements[0].type = CE_UINT; + pinfo->conv_elements[1].uint_val = id; + pinfo->conv_elements[1].type = CE_ENDPOINT; + pinfo->conv_elements[1].endpoint_type_val = etype; + + pinfo->use_endpoint = TRUE; } guint32 conversation_get_endpoint_by_id(struct _packet_info *pinfo, endpoint_type etype, const guint options) { - if (pinfo->conv_endpoint == NULL) + if (pinfo->conv_endpoint == NULL) { return 0; + } - if ((pinfo->conv_endpoint->etype != etype) && - ((options & USE_LAST_ENDPOINT) != USE_LAST_ENDPOINT)) + if (pinfo->conv_elements[0].type != CE_UINT || pinfo->conv_elements[1].type != CE_ENDPOINT) { return 0; + } - return pinfo->conv_endpoint->port1; -} - -wmem_map_t * -get_conversation_hashtable_exact(void) -{ - return conversation_hashtable_exact_addr_port; -} - -wmem_map_t * -get_conversation_hashtable_no_addr2(void) -{ - return conversation_hashtable_no_addr2; -} - -wmem_map_t * -get_conversation_hashtable_no_port2(void) -{ - return conversation_hashtable_no_port2; -} + if ((pinfo->conv_elements[1].endpoint_type_val != etype) && ((options & USE_LAST_ENDPOINT) != USE_LAST_ENDPOINT)) { + return 0; + } -wmem_map_t * -get_conversation_hashtable_no_addr2_or_port2(void) -{ - return conversation_hashtable_no_addr2_or_port2; + return pinfo->conv_elements[0].uint_val; } wmem_map_t * @@ -1923,28 +1804,50 @@ get_conversation_hashtables(void) return conversation_hashtable_element_list; } -address* -conversation_key_addr1(const conversation_key_t key) +const address* +conversation_key_addr1(const conversation_element_t *key) { - return &key->addr1; + const address *addr = &null_address_; + if (key[ADDR1_IDX].type == CE_ADDRESS) { + addr = &key[ADDR1_IDX].addr_val; + } + return addr; } -address* -conversation_key_addr2(const conversation_key_t key) +guint32 +conversation_key_port1(const conversation_element_t * key) { - return &key->addr2; + guint32 port = 0; + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT) { + port = key[PORT1_IDX].port_val; + } + return port; } -guint32 -conversation_key_port1(const conversation_key_t key) +const address* +conversation_key_addr2(const conversation_element_t * key) { - return key->port1; + const address *addr = &null_address_; + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT && key[ADDR2_IDX].type == CE_ADDRESS) { + addr = &key[ADDR2_IDX].addr_val; + } + return addr; } guint32 -conversation_key_port2(const conversation_key_t key) +conversation_key_port2(const conversation_element_t * key) { - return key->port2; + guint32 port = 0; + if (key[ADDR1_IDX].type == CE_ADDRESS && key[PORT1_IDX].type == CE_PORT) { + if (key[ADDR2_IDX].type == CE_ADDRESS && key[PORT2_IDX].type == CE_PORT) { + // Exact + port = key[PORT2_IDX].port_val; + } else if (key[PORT2_NO_ADDR2_IDX].type == CE_PORT) { + // No addr 2 + port = key[PORT2_NO_ADDR2_IDX].port_val; + } + } + return port; } WS_DLL_PUBLIC @@ -1984,21 +1887,6 @@ endpoint_type conversation_pt_to_endpoint_type(port_type pt) return ENDPOINT_NONE; } -gchar* -conversation_get_html_hash(const conversation_key_t key) -{ - gchar *hash, *addr1, *addr2; - - addr1 = address_to_str(NULL, &key->addr1); - addr2 = address_to_str(NULL, &key->addr2); - hash = wmem_strdup_printf(NULL, "<tr><td>%s</td><td>%d</td><td>%s</td><td>%d</td></tr>\n", - addr1, key->port1, addr2, key->port2); - wmem_free(NULL, addr1); - wmem_free(NULL, addr2); - - return hash; -} - /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * |