epan: Convert remaining conversation code to elements.

Convert the address+port conversation code to element lists. Make our
conversation keys element lists. Document more of the conversation API.

Update the Conversation Hash Table dialog to use the new API.

Describe an alternative key type and data structure at the top of
conversation.c.
This commit is contained in:
Gerald Combs 2022-05-24 12:33:33 -07:00
parent 729b4d3b69
commit 5cd591129f
5 changed files with 481 additions and 605 deletions

File diff suppressed because it is too large Load Diff

View File

@ -91,10 +91,32 @@ typedef enum {
} endpoint_type;
/**
* Key used for identifying a conversation.
* Conversation element type.
*/
struct conversation_key;
typedef struct conversation_key* conversation_key_t;
typedef enum {
CE_ENDPOINT,
CE_ADDRESS,
CE_PORT,
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;
unsigned int port_val;
const char *str_val;
unsigned int uint_val;
uint64_t uint64_val;
};
} conversation_element_t;
/**
* Data structure representing a conversation.
@ -110,17 +132,16 @@ typedef struct conversation {
wmem_tree_t *data_list; /** list of data associated with conversation */
wmem_tree_t *dissector_tree; /** tree containing protocol dissector client associated with conversation */
guint options; /** wildcard flags */
conversation_key_t key_ptr; /** pointer to the key for this conversation */
conversation_element_t *key_ptr; /** Keys are conversation element arrays terminated with a CE_ENDPOINT */
} conversation_t;
struct endpoint;
typedef struct endpoint* endpoint_t;
WS_DLL_PUBLIC address* conversation_key_addr1(const conversation_key_t key);
WS_DLL_PUBLIC address* conversation_key_addr2(const conversation_key_t key);
WS_DLL_PUBLIC guint32 conversation_key_port1(const conversation_key_t key);
WS_DLL_PUBLIC guint32 conversation_key_port2(const conversation_key_t key);
WS_DLL_PUBLIC const address* conversation_key_addr1(const conversation_element_t *key);
WS_DLL_PUBLIC guint32 conversation_key_port1(const conversation_element_t *key);
WS_DLL_PUBLIC const address* conversation_key_addr2(const conversation_element_t *key);
WS_DLL_PUBLIC guint32 conversation_key_port2(const conversation_element_t *key);
/**
* Create a new hash tables for conversations.
@ -132,32 +153,6 @@ 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.
@ -297,12 +292,36 @@ WS_DLL_PUBLIC void conversation_set_dissector_from_frame_number(conversation_t *
WS_DLL_PUBLIC dissector_handle_t conversation_get_dissector(conversation_t *conversation, const guint32 frame_num);
/**
* Save address+port information in the current packet info which can be matched by
* find_conversation_pinfo. Supports wildcarding.
* @param pinfo Packet info.
* @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.
*/
WS_DLL_PUBLIC void conversation_create_endpoint(struct _packet_info *pinfo, address* addr1, address* addr2,
endpoint_type etype, guint32 port1, guint32 port2);
/**
* Save ID information in the current packet info which can be matched by
* conversation_get_endpoint_by_id. Does not support wildcarding.
* @param pinfo Packet info.
* @param etype The endpoint type.
* @param id A unique ID.
*/
WS_DLL_PUBLIC void conversation_create_endpoint_by_id(struct _packet_info *pinfo,
endpoint_type etype, guint32 id);
/**
* @brief conversation_get_endpoint_by_id
* @param pinfo Packet info.
* @param etype The endpoint type.
* @param options USE_LAST_ENDPOINT or 0.
* @return The endpoint ID if successful, or 0 on failure.
*/
WS_DLL_PUBLIC guint32 conversation_get_endpoint_by_id(struct _packet_info *pinfo,
endpoint_type etype, const guint options);
@ -325,18 +344,20 @@ WS_DLL_PUBLIC gboolean try_conversation_dissector_by_id(const endpoint_type etyp
/* These routines are used to set undefined values for a conversation */
/**
* Set the second port in a conversation created with conversation_new.
* @param conv Conversation. Must be created with conversation_new.
* @param port The second port to set.
*/
WS_DLL_PUBLIC void conversation_set_port2(conversation_t *conv, const guint32 port);
/**
* Set the second address in a conversation created with conversation_new.
* @param conv Conversation. Must be created with conversation_new.
* @param port The second address to set.
*/
WS_DLL_PUBLIC void conversation_set_addr2(conversation_t *conv, const address *addr);
WS_DLL_PUBLIC wmem_map_t *get_conversation_hashtable_exact(void);
WS_DLL_PUBLIC wmem_map_t *get_conversation_hashtable_no_addr2(void);
WS_DLL_PUBLIC wmem_map_t * get_conversation_hashtable_no_port2(void);
WS_DLL_PUBLIC wmem_map_t *get_conversation_hashtable_no_addr2_or_port2(void);
/**
* @brief Get a hash table of conversation hash table.
*
@ -353,9 +374,6 @@ WS_DLL_PUBLIC endpoint_type conversation_pt_to_endpoint_type(port_type pt);
WS_DLL_PUBLIC guint conversation_hash_exact(gconstpointer v);
/* Provide a wmem_alloced (NULL scope) hash string using HTML tags */
WS_DLL_PUBLIC gchar *conversation_get_html_hash(const conversation_key_t key);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -591,6 +591,7 @@ dissect_record(epan_dissect_t *edt, int file_type_subtype,
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.src_win_scale = -1; /* unknown Rcv.Wind.Shift */

View File

@ -72,8 +72,8 @@ typedef struct _packet_info {
guint32 match_uint; /**< matched uint for calling subdissector from table */
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 */
struct endpoint* conv_endpoint; /**< Data that can be used for address+port conversations, including wildcarding */
struct conversation_element *conv_elements; /**< Arbritrary conversation identifier; can't be wildcarded */
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

@ -33,6 +33,7 @@ fill_named_table(gpointer key, gpointer value _U_, gpointer user_data)
if (html_table->isEmpty()) {
html_table->append("<tr>");
int addr_count = 1;
int port_count = 1;
int string_count = 1;
int uint_count = 1;
int uint64_count = 1;
@ -42,6 +43,9 @@ fill_named_table(gpointer key, gpointer value _U_, gpointer user_data)
case CE_ADDRESS:
title = QString("Address %1").arg(addr_count++);
break;
case CE_PORT:
title = QString("Port %1").arg(port_count++);
break;
case CE_STRING:
title = QString("String %1").arg(string_count++);
break;
@ -70,6 +74,9 @@ title_done:
case CE_ADDRESS:
val = address_to_qstring(&cur_el->addr_val);
break;
case CE_PORT:
val = QString::number(cur_el->port_val);
break;
case CE_STRING:
val = cur_el->str_val;
break;
@ -102,12 +109,7 @@ ConversationHashTablesDialog::ConversationHashTablesDialog(QWidget *parent) :
QString html;
html += "<h3>Conversation Hash Tables</h3>\n";
html += hashTableToHtmlTable("conversation_hashtable_exact", get_conversation_hashtable_exact());
html += hashTableToHtmlTable("conversation_hashtable_no_addr2", get_conversation_hashtable_no_addr2());
html += hashTableToHtmlTable("conversation_hashtable_no_port2", get_conversation_hashtable_no_port2());
html += hashTableToHtmlTable("conversation_hashtable_no_addr2_or_port2", get_conversation_hashtable_no_addr2_or_port2());
html += "<h2>Conversation Hash Tables</h2>\n";
wmem_map_t *conversation_tables = get_conversation_hashtables();
wmem_list_t *table_names = wmem_map_get_keys(NULL, conversation_tables);
@ -116,7 +118,12 @@ ConversationHashTablesDialog::ConversationHashTablesDialog(QWidget *parent) :
const char *table_name = static_cast<const char *>(wmem_list_frame_data(cur_frame));
wmem_map_t *table = static_cast<wmem_map_t *>(wmem_map_lookup(conversation_tables, table_name));
html += QString("<p>%1, %2 entries</p>\n").arg(table_name).arg(wmem_map_size(table));
if (!table) {
html += QString("<h3>%1, Error: table not found</h3>\n").arg(table_name);
continue;
}
html += QString("<h3>%1, %2 entries</h3>\n").arg(table_name).arg(wmem_map_size(table));
QString html_table;
html += "<table>\n";
wmem_map_foreach(table, fill_named_table, &html_table);
@ -131,41 +138,3 @@ ConversationHashTablesDialog::~ConversationHashTablesDialog()
{
delete ui;
}
static void
populate_html_table(gpointer data, gpointer user_data)
{
const conversation_key_t conv_key = (conversation_key_t)data;
QString* html_table = (QString*)user_data;
gchar* tmp = conversation_get_html_hash(conv_key);
// XXX Add a column for the hash value.
(*html_table) += QString(tmp);
wmem_free(NULL, tmp);
}
const QString ConversationHashTablesDialog::hashTableToHtmlTable(const QString table_name, wmem_map_t *hash_table)
{
wmem_list_t *conversation_keys = NULL;
guint num_keys = 0;
if (hash_table)
{
conversation_keys = wmem_map_get_keys(NULL, hash_table);
num_keys = wmem_list_count(conversation_keys);
}
QString html_table = QString("<p>%1, %2 entries</p>").arg(table_name).arg(num_keys);
if (num_keys > 0)
{
int one_em = fontMetrics().height();
html_table += QString("<table cellpadding=\"%1\">\n").arg(one_em / 4);
html_table += "<tr><th align=\"left\">Address 1</th><th align=\"left\">Port 1</th><th align=\"left\">Address 2</th><th align=\"left\">Port 2</th></tr>\n";
wmem_list_foreach(conversation_keys, populate_html_table, (void*)&html_table);
html_table += "</table>\n";
}
if (conversation_keys)
wmem_destroy_list(conversation_keys);
return html_table;
}