epan: Allow conversations based on arbitrary element lists.

Add conversation_new_full and find_conversation_full, which take
arbitrary element lists instead of fixed addresses and ports.

Update the comments in conversation.h to be more Doxygen-conformant.
Update README.dissector.

Use the new functionality to add initial conversation support to the
Falco Bridge dissector.
This commit is contained in:
Gerald Combs 2022-05-10 17:03:31 -07:00 committed by A Wireshark GitLab Utility
parent 96cf14ea01
commit be929e162d
7 changed files with 469 additions and 51 deletions

View File

@ -2434,12 +2434,12 @@ ServerA:1000 to ClientA:2000 and the packet from ClientA:2000 to ServerA:1000.
2.2.1 Conversation Routines
There are seven routines that you will use to work with a conversation:
conversation_new, find_conversation, find_or_create_conversation,
There are nine routines that you will use to work with a conversation:
conversation_new, conversation_new_full, find_conversation,
find_conversation_full, find_or_create_conversation,
conversation_add_proto_data, conversation_get_proto_data,
conversation_delete_proto_data, and conversation_set_dissector.
2.2.1.1 The conversation_init function.
This is an internal routine for the conversation code. As such you
@ -2475,7 +2475,7 @@ Where:
guint32 setup_frame = The lowest numbered frame for this conversation
address* addr1 = first data packet address
address* addr2 = second data packet address
port_type ptype = port type, this is defined in packet.h
endpoint_type etype = endpoint type, defined in conversation.h
guint32 port1 = first data packet port
guint32 port2 = second data packet port
guint options = conversation options, NO_ADDR2 and/or NO_PORT2
@ -2500,7 +2500,25 @@ packet indicates that, later in the capture, a conversation will be
created using certain addresses and ports, in the case where the packet
doesn't specify the addresses and ports of both sides.
2.2.1.3 The find_conversation function.
2.2.1.3 The conversation_new_full function.
This routine will create a new conversation based upon an arbritrary
lists of elements. Elements can be addresses, strings, unsigned
integers, or unsigned 64-bit integers. Unlike conversation_new, element
lists are matched strictly; wildcards aren't (yet) supported.
The conversation_new_full prototype:
conversation_t *conversation_new(const guint32 setup_frame,
conversation_element_t *elements);
Where:
guint32 setup_frame = The lowest numbered frame for
this conversation
conversation_element_t *elements = An array of data types and
values which identify this conversation. The array MUST be
terminated with a CE_ENDPOINT element.
2.2.1.4 The find_conversation function.
Call this routine to look up a conversation. If no conversation is found,
the routine will return a NULL value.
@ -2549,7 +2567,24 @@ NO_ADDR_B|NO_PORT_B, the "addr_b" address will be treated as matching
any "wildcarded" address and the "port_b" port will be treated as
matching any "wildcarded" port.
2.2.1.4 The find_conversation_pinfo function.
2.2.1.5 The find_conversation_full function.
Call this routine to look up a conversation based on an element list. If
no conversation is found, the routine will return a NULL value.
The find_conversation_full prototype:
conversation_t *find_conversation_full(guint32 frame_num,
conversation_element_t *elements);
Where:
guint32 setup_frame = The lowest numbered frame for
this conversation
conversation_element_t *elements = An array of data types and
values which identify this conversation. The array MUST be
terminated with a CE_ENDPOINT element.
2.2.1.6 The find_conversation_pinfo function.
This convenience function will find an existing conversation (by calling
find_conversation())
@ -2563,9 +2598,11 @@ Where:
const guint options = conversation options, NO_ADDR_B and/or NO_PORT_B
The frame number and the addresses necessary for find_conversation() are
taken from the pinfo structure (as is commonly done).
taken from the addresses and ports in the pinfo structure,
pinfo->conv_endpoint if pinfo->use_endpoint is set, or
pinfo->conv_elements if it is set.
2.2.1.5 The find_or_create_conversation function.
2.2.1.7 The find_or_create_conversation function.
This convenience function will find an existing conversation (by calling
find_conversation()) and, if a conversation does not already exist, create a
@ -2582,8 +2619,7 @@ The frame number and the addresses necessary for find_conversation() and
conversation_new() are taken from the pinfo structure (as is commonly done)
and no 'options' are used.
2.2.1.6 The conversation_add_proto_data function.
2.2.1.8 The conversation_add_proto_data function.
Once you have created a conversation with conversation_new, you can
associate data with it using this function.
@ -2609,7 +2645,7 @@ Using the protocol number allows several dissectors to
associate data with a given conversation.
2.2.1.7 The conversation_get_proto_data function.
2.2.1.9 The conversation_get_proto_data function.
After you have located a conversation with find_conversation, you can use
this function to retrieve any data associated with it.
@ -2628,7 +2664,7 @@ typically in the proto_register_XXXX portion of a dissector. The function
returns a pointer to the data requested, or NULL if no data was found.
2.2.1.8 The conversation_delete_proto_data function.
2.2.1.10 The conversation_delete_proto_data function.
After you are finished with a conversation, you can remove your association
with this function. Please note that ONLY the conversation entry is
@ -2647,7 +2683,7 @@ Where:
is a unique protocol number created with proto_register_protocol,
typically in the proto_register_XXXX portion of a dissector.
2.2.1.9 The conversation_set_dissector function
2.2.1.11 The conversation_set_dissector function
This function sets the protocol dissector to be invoked whenever
conversation parameters (addresses, port_types, ports, etc) are matched

View File

@ -11,11 +11,17 @@
#include "config.h"
#include <string.h>
#include <glib.h>
#include "packet.h"
#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.
/* define DEBUG_CONVERSATION for pretty debug printing */
/* #define DEBUG_CONVERSATION */
#include "conversation_debug.h"
@ -34,7 +40,7 @@ struct endpoint {
};
struct conversation_key {
struct conversation_key *next;
// XXX Replace these with conversation_element_t's.
address addr1;
address addr2;
endpoint_type etype;
@ -45,7 +51,7 @@ struct conversation_key {
/*
* Hash table for conversations with no wildcards.
*/
static wmem_map_t *conversation_hashtable_exact = NULL;
static wmem_map_t *conversation_hashtable_exact_addr_port = NULL;
/*
* Hash table for conversations with one wildcard address.
@ -62,6 +68,10 @@ static wmem_map_t *conversation_hashtable_no_port2 = NULL;
*/
static wmem_map_t *conversation_hashtable_no_addr2_or_port2 = NULL;
/*
* Hash table of hash tables for conversations identified by element lists.
*/
static wmem_map_t *conversation_hashtable_element_list = NULL;
static guint32 new_index;
@ -443,6 +453,111 @@ conversation_match_no_addr2_or_port2(gconstpointer v, gconstpointer w)
return 0;
}
/*
* Compute the hash value for two given element lists 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_element_list(gconstpointer v)
{
const conversation_element_t *element = (const conversation_element_t*)v;
guint hash_val = 0;
for (;;) {
// XXX We could use a hash_arbitrary_bytes routine. Abuse add_address_to_hash in the mean time.
address tmp_addr;
switch (element->type) {
case CE_ADDRESS:
hash_val = add_address_to_hash(hash_val, &element->addr_val);
break;
case CE_STRING:
tmp_addr.len = (int) strlen(element->str_val);
tmp_addr.data = element->str_val;
hash_val = add_address_to_hash(hash_val, &tmp_addr);
break;
case CE_UINT:
tmp_addr.len = (int) sizeof(element->uint_val);
tmp_addr.data = &element->uint_val;
hash_val = add_address_to_hash(hash_val, &tmp_addr);
break;
case CE_UINT64:
tmp_addr.len = (int) sizeof(element->uint64_val);
tmp_addr.data = &element->uint64_val;
hash_val = add_address_to_hash(hash_val, &tmp_addr);
break;
case CE_ENDPOINT:
tmp_addr.len = (int) sizeof(element->endpoint_type_val);
tmp_addr.data = &element->endpoint_type_val;
hash_val = add_address_to_hash(hash_val, &tmp_addr);
goto done;
break;
}
element++;
}
done:
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 gboolean
conversation_match_element_list(gconstpointer v1, gconstpointer v2)
{
const conversation_element_t *element1 = (const conversation_element_t*)v1;
const conversation_element_t *element2 = (const conversation_element_t*)v2;
for (;;) {
if (element1->type != element2->type) {
return FALSE;
}
switch (element1->type) {
case CE_ADDRESS:
if (!addresses_equal(&element1->addr_val, &element2->addr_val)) {
return FALSE;
}
break;
case CE_STRING:
if (strcmp(element1->str_val, element2->str_val)) {
return FALSE;
}
break;
case CE_UINT:
if (element1->uint_val != element2->uint_val) {
return FALSE;
}
break;
case CE_UINT64:
if (element1->uint64_val != element2->uint64_val) {
return FALSE;
}
break;
case CE_ENDPOINT:
if (element1->endpoint_type_val != element2->endpoint_type_val) {
return FALSE;
}
goto done;
break;
}
element1++;
element2++;
}
done:
// Everything matched so far.
return TRUE;
}
/**
* Create a new hash tables for conversations.
*/
@ -457,7 +572,7 @@ conversation_init(void)
* pointed to by conversation data structures that were freed
* above.
*/
conversation_hashtable_exact =
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 =
@ -470,6 +585,8 @@ conversation_init(void)
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);
}
/**
@ -501,6 +618,7 @@ conversation_insert_into_hashtable(wmem_map_t *hashtable, conversation_t *conv)
/* New entry */
conv->next = NULL;
conv->last = conv;
wmem_map_insert(hashtable, conv->key_ptr, conv);
DPRINT(("created a new conversation chain"));
}
@ -602,6 +720,117 @@ conversation_remove_from_hashtable(wmem_map_t *hashtable, conversation_t *conv)
}
}
/* Element count including the terminating CE_ENDPOINT */
#define MAX_CONVERSATION_ELEMENTS 10 // Arbitrary.
static size_t conversation_element_count(conversation_element_t *elements) {
size_t count = 0;
while (elements[count].type != CE_ENDPOINT) {
count++;
DISSECTOR_ASSERT(count < MAX_CONVERSATION_ELEMENTS);
}
count++;
// Keying on the endpoint type alone isn't very useful.
DISSECTOR_ASSERT(count > 1);
return count;
}
/* Create a string based on element types. Must be g_freed. */
static char* conversation_element_list_name(conversation_element_t *elements) {
const char *type_names[] = {
"endpoint",
"address",
"string",
"uint",
"uint64",
};
char *sep = "";
GString *conv_hash_group = 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(conv_hash_group, "%s%s", sep, type_names[cur_el->type]);
sep = ",";
}
return g_string_free(conv_hash_group, FALSE);
}
#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);
char *el_list_map_key = conversation_element_list_name(elements);
wmem_map_t *el_list_map = (wmem_map_t *) wmem_map_lookup(conversation_hashtable_element_list, el_list_map_key);
if (!el_list_map) {
el_list_map = 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_file_scope(), el_list_map_key), el_list_map);
}
g_free(el_list_map_key);
size_t element_count = conversation_element_count(elements);
conversation_element_t *conv_key = wmem_memdup(wmem_file_scope(), elements, sizeof(conversation_element_t) * element_count);
for (size_t i = 0; i < element_count; i++) {
if (conv_key[i].type == CE_ADDRESS) {
copy_address_wmem(wmem_file_scope(), &conv_key[i].addr_val, &elements[i].addr_val);
} else if (conv_key[i].type == CE_STRING) {
conv_key[i].str_val = wmem_strdup(wmem_file_scope(), elements[i].str_val);
}
}
conversation_t *conversation = wmem_new0(wmem_file_scope(), conversation_t);
conversation->conv_index = new_index;
conversation->setup_frame = conversation->last_frame = setup_frame;
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_insert_into_hashtable(el_list_map, conversation);
return conversation;
}
/*
* Given two address/port pairs for a packet, create a new conversation
* to contain packets between those address/port pairs.
@ -721,11 +950,11 @@ conversation_new(const guint32 setup_frame, const address *addr1, const address
if (options & (NO_PORT2|NO_PORT2_FORCE)) {
hashtable = conversation_hashtable_no_port2;
} else {
hashtable = conversation_hashtable_exact;
hashtable = conversation_hashtable_exact_addr_port;
}
}
new_key = wmem_new(wmem_file_scope(), struct conversation_key);
new_key = wmem_new0(wmem_file_scope(), struct conversation_key);
if (addr1 != NULL) {
copy_address_wmem(wmem_file_scope(), &new_key->addr1, addr1);
} else {
@ -797,7 +1026,7 @@ conversation_set_port2(conversation_t *conv, const guint32 port)
if (conv->options & NO_ADDR2) {
conversation_insert_into_hashtable(conversation_hashtable_no_addr2, conv);
} else {
conversation_insert_into_hashtable(conversation_hashtable_exact, conv);
conversation_insert_into_hashtable(conversation_hashtable_exact_addr_port, conv);
}
DENDENT();
}
@ -834,11 +1063,48 @@ conversation_set_addr2(conversation_t *conv, const address *addr)
if (conv->options & NO_PORT2) {
conversation_insert_into_hashtable(conversation_hashtable_no_port2, conv);
} else {
conversation_insert_into_hashtable(conversation_hashtable_exact, conv);
conversation_insert_into_hashtable(conversation_hashtable_exact_addr_port, conv);
}
DENDENT();
}
conversation_t *find_conversation_full(const guint32 frame_num, conversation_element_t *elements)
{
char *el_list_map_key = conversation_element_list_name(elements);
wmem_map_t *el_list_map = (wmem_map_t *) wmem_map_lookup(conversation_hashtable_element_list, el_list_map_key);
g_free(el_list_map_key);
if (!el_list_map) {
return NULL;
}
conversation_t* convo=NULL;
conversation_t* match=NULL;
conversation_t* chain_head=NULL;
chain_head = (conversation_t *)wmem_map_lookup(el_list_map, elements);
if (chain_head && (chain_head->setup_frame <= frame_num)) {
match = chain_head;
if (chain_head->last && (chain_head->last->setup_frame <= frame_num))
return chain_head->last;
if (chain_head->latest_found && (chain_head->latest_found->setup_frame <= frame_num))
match = chain_head->latest_found;
for (convo = match; convo && convo->setup_frame <= frame_num; convo = convo->next) {
if (convo->setup_frame > match->setup_frame) {
match = convo;
}
}
}
if (match) {
chain_head->latest_found = match;
}
return match;
}
/*
* Search a particular hash table for a conversation with the specified
* {addr1, port1, addr2, port2} and set up before frame_num.
@ -875,10 +1141,10 @@ conversation_lookup_hashtable(wmem_map_t *hashtable, const guint32 frame_num, co
if (chain_head && (chain_head->setup_frame <= frame_num)) {
match = chain_head;
if ((chain_head->last)&&(chain_head->last->setup_frame<=frame_num))
if (chain_head->last && (chain_head->last->setup_frame <= frame_num))
return chain_head->last;
if ((chain_head->latest_found)&&(chain_head->latest_found->setup_frame<=frame_num))
if (chain_head->latest_found && (chain_head->latest_found->setup_frame <= frame_num))
match = chain_head->latest_found;
for (convo = match; convo && convo->setup_frame <= frame_num; convo = convo->next) {
@ -894,7 +1160,6 @@ conversation_lookup_hashtable(wmem_map_t *hashtable, const guint32 frame_num, co
return match;
}
/*
* Given two address/port pairs for a packet, search for a conversation
* containing packets between those address/port pairs. Returns NULL if
@ -950,7 +1215,7 @@ 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_hashtable(conversation_hashtable_exact,
conversation_lookup_hashtable(conversation_hashtable_exact_addr_port,
frame_num, addr_a, addr_b, etype,
port_a, port_b);
/* Didn't work, try the other direction */
@ -958,7 +1223,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_b, addr_a_str, port_a));
conversation =
conversation_lookup_hashtable(conversation_hashtable_exact,
conversation_lookup_hashtable(conversation_hashtable_exact_addr_port,
frame_num, addr_b, addr_a, etype,
port_b, port_a);
}
@ -969,7 +1234,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_hashtable(conversation_hashtable_exact,
conversation_lookup_hashtable(conversation_hashtable_exact_addr_port,
frame_num, addr_b, addr_a, etype,
port_a, port_b);
}
@ -1480,6 +1745,14 @@ find_conversation_pinfo(packet_info *pinfo, const guint options)
conv->last_frame = pinfo->num;
}
}
} else if (pinfo->conv_elements) {
if ((conv = find_conversation_full(pinfo->num, pinfo->conv_elements)) != NULL) {
DPRINT(("found previous conversation elements for frame #%u (last_frame=%d)",
pinfo->num, conv->last_frame));
if (pinfo->num > conv->last_frame) {
conv->last_frame = pinfo->num;
}
}
} else {
if ((conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport,
@ -1584,7 +1857,7 @@ conversation_get_endpoint_by_id(struct _packet_info *pinfo, endpoint_type etype,
wmem_map_t *
get_conversation_hashtable_exact(void)
{
return conversation_hashtable_exact;
return conversation_hashtable_exact_addr_port;
}
wmem_map_t *

View File

@ -21,9 +21,13 @@ extern "C" {
#endif /* __cplusplus */
/**
*@file
* @file
* The conversation API lets you correlate packets based on values in a
* packet, typically address+port tuples. You can search for conversations
* based on their value tuples and attach data to them.
*/
/*
/**
* Flags to pass to "conversation_new()" to indicate that the address 2
* and/or port 2 values for the conversation should be wildcards.
* The CONVERSATION_TEMPLATE option tells that any of the other supplied
@ -39,15 +43,15 @@ extern "C" {
#define NO_PORT2_FORCE 0x04
#define CONVERSATION_TEMPLATE 0x08
/*
/**
* Flags to pass to "find_conversation()" to indicate that the address B
* and/or port B search arguments are wildcards.
*/
#define NO_ADDR_B 0x01
#define NO_PORT_B 0x02
/* Flags to handle endpoints */
#define USE_LAST_ENDPOINT 0x08 /* Use last endpoint created, regardless of type */
/** Flags to handle endpoints */
#define USE_LAST_ENDPOINT 0x08 /**< Use last endpoint created, regardless of type */
/* Types of port numbers Wireshark knows about. */
typedef enum {
@ -82,15 +86,18 @@ typedef enum {
ENDPOINT_DVBBBF, /* DVB Base Band Frame ISI/PLP_ID */
ENDPOINT_IWARP_MPA, /* iWarp MPA */
ENDPOINT_BT_UTP, /* BitTorrent uTP Connection ID */
ENDPOINT_LOG, /* Logging source */
} endpoint_type;
/**
* Data structure representing a conversation.
* Key used for identifying a conversation.
*/
struct conversation_key;
typedef struct conversation_key* conversation_key_t;
/**
* Data structure representing a conversation.
*/
typedef struct conversation {
struct conversation *next; /** pointer to next conversation on hash chain */
struct conversation *last; /** pointer to the last conversation on hash chain */
@ -124,18 +131,70 @@ extern void conversation_init(void);
*/
extern void conversation_epan_reset(void);
/*
/**
* Conversation element type.
*/
typedef enum {
CE_ENDPOINT,
CE_ADDRESS,
CE_STRING,
CE_UINT,
CE_UINT64,
} conversation_element_type;
/**
* Elements used to identify conversations for *_full routines and pinfo->conv_elements.
* Arrays must be terminated with an element .type set to CE_ENDPOINT.
*/
typedef struct conversation_element {
conversation_element_type type;
union {
endpoint_type endpoint_type_val;
address addr_val;
const char *str_val;
unsigned int uint_val;
uint64_t uint64_val;
};
} conversation_element_t;
/**
* Create a new conversation identified by a list of elements.
* @param setup_frame The first frame in the conversation.
* @param elements An array of element types and values. Must not be NULL. Must be terminated with a CE_ENDPOINT element.
* @return The new conversation.
*/
WS_DLL_PUBLIC WS_RETNONNULL conversation_t *conversation_new_full(const guint32 setup_frame, conversation_element_t *elements);
/**
* Given two address/port pairs for a packet, create a new conversation
* to contain packets between those address/port pairs.
* identified by address/port pairs.
*
* The options field is used to specify whether the address 2 value
* and/or port 2 value are not given and any value is acceptable
* when searching for this conversation.
*
* @param setup_frame The first frame in the conversation.
* @param addr1 The first address in the identifying tuple.
* @param addr2 The second address in the identifying tuple.
* @param etype The endpoint type.
* @param port1 The first port in the identifying tuple.
* @param port2 The second port in the identifying tuple.
* @param options NO_ADDR2, NO_PORT2, NO_PORT2_FORCE, or CONVERSATION_TEMPLATE.
* Options except for NO_PORT2 and NO_PORT2_FORCE can be ORed.
* @return The new conversation.
*/
WS_DLL_PUBLIC conversation_t *conversation_new(const guint32 setup_frame, const address *addr1, const address *addr2,
WS_DLL_PUBLIC WS_RETNONNULL conversation_t *conversation_new(const guint32 setup_frame, const address *addr1, const address *addr2,
const endpoint_type etype, const guint32 port1, const guint32 port2, const guint options);
WS_DLL_PUBLIC conversation_t *conversation_new_by_id(const guint32 setup_frame, const endpoint_type etype, const guint32 id, const guint options);
WS_DLL_PUBLIC WS_RETNONNULL conversation_t *conversation_new_by_id(const guint32 setup_frame, const endpoint_type etype, const guint32 id, const guint options);
/**
* Search for a conversation based on the structure and values of an element list.
* @param frame_num Frame number. Must be greater than or equal to the conversation's initial frame number.
* @param elements An array of element types and values. Must not be NULL. Must be terminated with a CE_ENDPOINT element.
* @return The matching conversation if found, otherwise NULL.
*/
WS_DLL_PUBLIC conversation_t *find_conversation_full(const guint32 frame_num, conversation_element_t *elements);
/**
* Given two address/port pairs for a packet, search for a conversation
@ -172,6 +231,15 @@ WS_DLL_PUBLIC conversation_t *conversation_new_by_id(const guint32 setup_frame,
* a pointer to the matched conversation;
*
* otherwise, we found no matching conversation, and return NULL.
*
* @param frame_num Frame number. Must be greater than or equal to the conversation's initial frame number.
* @param addr1 The first address in the identifying tuple.
* @param addr2 The second address in the identifying tuple.
* @param etype The endpoint type.
* @param port1 The first port in the identifying tuple.
* @param port2 The second port in the identifying tuple.
* @param options Wildcard options as described above.
* @return The matching conversation if found, otherwise NULL.
*/
WS_DLL_PUBLIC conversation_t *find_conversation(const guint32 frame_num, const address *addr_a, const address *addr_b,
const endpoint_type etype, const guint32 port_a, const guint32 port_b, const guint options);
@ -183,19 +251,23 @@ WS_DLL_PUBLIC conversation_t *find_conversation_by_id(const guint32 frame, const
*/
WS_DLL_PUBLIC conversation_t *find_conversation_pinfo(packet_info *pinfo, const guint options);
/** A helper function that calls find_conversation() and, if a conversation is
* not found, calls conversation_new().
* The frame number and addresses are taken from pinfo.
* No options are used, though we could extend this API to include an options
* parameter.
/**
* A helper function that calls find_conversation() and, if a conversation is
* not found, calls conversation_new().
* The frame number and addresses are taken from pinfo.
* No options are used, though we could extend this API to include an options
* parameter.
*
* @param pinfo Packet info.
* @return The existing or new conversation.
*/
WS_DLL_PUBLIC conversation_t *find_or_create_conversation(packet_info *pinfo);
WS_DLL_PUBLIC WS_RETNONNULL conversation_t *find_or_create_conversation(packet_info *pinfo);
/** A helper function that calls find_conversation_by_id() and, if a
* conversation is not found, calls conversation_new_by_id().
* The frame number is taken from pinfo.
*/
WS_DLL_PUBLIC conversation_t *find_or_create_conversation_by_id(packet_info *pinfo, const endpoint_type etype, const guint32 id);
WS_DLL_PUBLIC WS_RETNONNULL conversation_t *find_or_create_conversation_by_id(packet_info *pinfo, const endpoint_type etype, const guint32 id);
/** Associate data with a conversation.
* @param conv Conversation. Must not be NULL.

View File

@ -664,6 +664,7 @@ dissect_file(epan_dissect_t *edt, wtap_rec *rec,
edt->pi.ptype = PT_NONE;
edt->pi.use_endpoint = FALSE;
edt->pi.conv_endpoint = NULL;
edt->pi.conv_elements = NULL;
edt->pi.p2p_dir = P2P_DIR_UNKNOWN;
edt->pi.link_dir = LINK_DIR_UNKNOWN;
edt->pi.layers = wmem_list_new(edt->pi.pool);

View File

@ -16,6 +16,7 @@
#include "address.h"
struct endpoint;
struct conversation_element;
/** @file
* Dissected packet data and metadata.
@ -72,6 +73,7 @@ typedef struct _packet_info {
const char *match_string; /**< matched string for calling subdissector from table */
gboolean use_endpoint; /**< TRUE if endpoint member should be used for conversations */
struct endpoint* conv_endpoint; /**< Data that can be used for conversations */
struct conversation_element *conv_elements; /**< Conversation identifier; alternative to conv_endpoint */
guint16 can_desegment; /**< >0 if this segment could be desegmented.
A dissector that can offer this API (e.g.
TCP) sets can_desegment=2, then

View File

@ -182,6 +182,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
conversation_key_port2@Base 2.5.0
conversation_new@Base 1.9.1
conversation_new_by_id@Base 2.5.0
conversation_new_full@Base 3.7.1
conversation_pt_to_endpoint_type@Base 2.5.0
conversation_set_dissector@Base 1.9.1
conversation_set_dissector_from_frame_number@Base 2.0.0
@ -673,6 +674,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
find_capture_dissector@Base 2.3.0
find_conversation@Base 1.9.1
find_conversation_by_id@Base 2.5.0
find_conversation_full@Base 3.7.1
find_conversation_pinfo@Base 2.5.0
find_depend_dissector_list@Base 2.1.0
find_dissector@Base 1.9.1

View File

@ -31,6 +31,7 @@
#include <epan/packet.h>
#include <epan/proto.h>
#include <epan/proto_data.h>
#include <epan/conversation.h>
#include <epan/conversation_filter.h>
#include <epan/tap.h>
#include <epan/stat_tap_ui.h>
@ -183,7 +184,6 @@ configure_plugin(bridge_info* bi, char* config _U_)
if (addr_fields) {
bi->hf_id_to_addr_id = (int *)wmem_alloc(wmem_epan_scope(), bi->visible_fields * sizeof(int));
memset(bi->hf_id_to_addr_id, -1, bi->visible_fields);
bi->hf_v4 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info));
bi->hf_v4_ids = (int*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(int));
bi->hf_v6 = (hf_register_info*)wmem_alloc(wmem_epan_scope(), addr_fields * sizeof(hf_register_info));
@ -295,8 +295,9 @@ configure_plugin(bridge_info* bi, char* config _U_)
}
};
*ri_v6 = finfo_v6;
addr_fld_cnt++;
} else {
bi->hf_id_to_addr_id[fld_cnt] = -1;
}
fld_cnt++;
}
@ -482,6 +483,7 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* da
bridge_info* bi = p_get_proto_data(pinfo->pool, pinfo, proto_falco_bridge, PROTO_DATA_BRIDGE_HANDLE);
guint plen = tvb_captured_length(tvb);
const char *source_name = get_sinsp_source_name(bi->ssi);
wmem_array_t *conversation_elements = wmem_array_new(pinfo->pool, sizeof(conversation_element_t));
col_set_str(pinfo->cinfo, COL_PROTOCOL, source_name);
/* Clear out stuff in the info column */
@ -508,11 +510,16 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* da
for (uint32_t fld_idx = 0; fld_idx < bi->visible_fields; fld_idx++) {
sinsp_field_extract_t *sfe = &sinsp_fields[fld_idx];
header_field_info* hfinfo = &(bi->hf[fld_idx].hfinfo);
conversation_element_t conv_el = {0};
if (!sfe->is_present) {
continue;
}
if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) {
conv_vals_cnt++;
}
if (sfe->type == SFT_STRINGZ && hfinfo->type == FT_STRINGZ) {
proto_item *pi = proto_tree_add_string(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_str);
if (bi->field_flags[fld_idx] & BFF_INFO) {
@ -530,10 +537,6 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* da
PROTO_DATA_CONVINFO_USER_BASE + conv_vals_cnt, cvalptr);
}
if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) {
conv_vals_cnt++;
}
int addr_fld_idx = bi->hf_id_to_addr_id[fld_idx];
if (addr_fld_idx >= 0) {
ws_in4_addr v4_addr;
@ -552,21 +555,50 @@ dissect_sinsp_span(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* da
if (addr_item) {
proto_item_set_generated(addr_item);
}
if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) {
conv_el.type = CE_ADDRESS;
copy_address(&conv_el.addr_val, &pinfo->net_src);
}
} else {
if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) {
conv_el.type = CE_STRING;
conv_el.str_val = wmem_strdup(pinfo->pool, sfe->res_str);
}
}
}
else if (sfe->type == SFT_UINT64 && hfinfo->type == FT_UINT64) {
proto_tree_add_uint64(fb_tree, bi->hf_ids[fld_idx], tvb, 0, plen, sfe->res_u64);
if ((bi->field_flags[fld_idx] & BFF_CONVERSATION) != 0) {
conv_el.type = CE_UINT64;
conv_el.uint64_val = sfe->res_u64;
}
}
else {
REPORT_DISSECTOR_BUG("Field %s has an unrecognized or mismatched type %u != %u",
hfinfo->abbrev, sfe->type, hfinfo->type);
}
if (conv_el.type != CE_ENDPOINT) {
wmem_array_append_one(conversation_elements, conv_el);
}
}
if (!rc) {
REPORT_DISSECTOR_BUG("Falco plugin %s extract error", get_sinsp_source_name(bi->ssi));
}
unsigned num_conv_els = wmem_array_get_count(conversation_elements);
if (num_conv_els > 0) {
conversation_element_t conv_el;
conv_el.type = CE_ENDPOINT;
conv_el.endpoint_type_val = ENDPOINT_LOG;
wmem_array_append_one(conversation_elements, conv_el);
pinfo->conv_elements = (conversation_element_t *) wmem_array_get_raw(conversation_elements);
conversation_t *conv = find_conversation_pinfo(pinfo, 0);
if (!conv) {
conversation_new_full(pinfo->fd->num, pinfo->conv_elements);
}
}
return plen;
}