From e9d4e32f8f6c44da84a0cd7479986a15dac5fead Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 9 Dec 2009 22:41:29 +0100 Subject: [PATCH] Replace GHashTable with tr1::unordered_map in tcp_portmon This means portmon no longer depends on glib. --- ChangeLog | 4 + configure.ac.in | 2 +- src/Makefile.am | 2 +- src/libtcp-portmon.c | 715 ------------------------------------------ src/libtcp-portmon.cc | 532 +++++++++++++++++++++++++++++++ src/libtcp-portmon.h | 148 ++------- src/tcp-portmon.c | 8 +- 7 files changed, 564 insertions(+), 847 deletions(-) delete mode 100644 src/libtcp-portmon.c create mode 100644 src/libtcp-portmon.cc diff --git a/ChangeLog b/ChangeLog index 7c4143bc..dbe133d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2009-12-09 + * Replaced GHashTable with tr1::unordered_map in tcp_portmon. + This means portmon no longer depends on glib. + 2009-12-01 * Reenable top objects for all target OS's diff --git a/configure.ac.in b/configure.ac.in index 29971e7f..edf4605f 100644 --- a/configure.ac.in +++ b/configure.ac.in @@ -30,6 +30,7 @@ dnl dnl Tools dnl AC_PROG_CC +AC_PROG_CXX AC_PROG_LD AC_PROG_INSTALL AC_PROG_LIBTOOL @@ -551,7 +552,6 @@ if test x"$want_portmon" = xyes; then if test x"$PORT_MONITORS_MISSING" = xyes; then AC_MSG_ERROR([missing a needed network header for port monitoring]) fi - WANT_GLIB=yes AC_DEFINE(TCP_PORT_MONITOR, 1, [Define if you want tcp port monitoring support]) fi fi diff --git a/src/Makefile.am b/src/Makefile.am index 0cc4f7ac..9fd43646 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,7 +70,7 @@ solaris = solaris.c freebsd = freebsd.c freebsd.h bsdapm.c bsdapm.h netbsd = netbsd.c netbsd.h openbsd = openbsd.c openbsd.h bsdapm.c bsdapm.h -port_monitors = libtcp-portmon.c libtcp-portmon.h \ +port_monitors = libtcp-portmon.cc libtcp-portmon.h \ tcp-portmon.c tcp-portmon.h x11 = x11.c x11.h fonts.c fonts.h hddtemp = hddtemp.c hddtemp.h diff --git a/src/libtcp-portmon.c b/src/libtcp-portmon.c deleted file mode 100644 index bedcae5f..00000000 --- a/src/libtcp-portmon.c +++ /dev/null @@ -1,715 +0,0 @@ -/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- - * vim: ts=4 sw=4 noet ai cindent syntax=c - * - * libtcp-portmon.c: tcp port monitoring library. - * - * Copyright (C) 2005-2007 Philip Kovacs pkovacs@users.sourceforge.net - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - * USA. - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "libtcp-portmon.h" -#include - -/* ------------------------------------------------------------------- - * IMPLEMENTATION INTERFACE - * - * Implementation-specific interface begins here. Clients should not - * manipulate these structures directly, nor call the defined helper - * functions. Use the "Client interface" functions defined at bottom. - * ------------------------------------------------------------------- */ - -/* ----------------------------------- - * Copy a tcp_connection_t - * - * Returns 0 on success, -1 otherwise. - * ----------------------------------- */ -int copy_tcp_connection(tcp_connection_t *p_dest_connection, - const tcp_connection_t *p_source_connection) -{ - if (!p_dest_connection || !p_source_connection) { - return -1; - } - - p_dest_connection->local_addr = p_source_connection->local_addr; - p_dest_connection->local_port = p_source_connection->local_port; - p_dest_connection->remote_addr = p_source_connection->remote_addr; - p_dest_connection->remote_port = p_source_connection->remote_port; - p_dest_connection->age = p_source_connection->age; - - return 0; -} - -/* ------------------------------------------- - * Port monitor utility functions implementing - * tcp_port_monitor_function_ptr_t - * ------------------------------------------- */ -void destroy_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void) -{ - tcp_connection_node_t *p_node, *p_temp; - - if (!p_monitor || p_void) { /* p_void should be NULL in this context */ - return; - } - - /* destroy the monitor's peek array */ - free(p_monitor->p_peek); - - /* destroy the monitor's connection list */ - for (p_node = p_monitor->connection_list.p_head; p_node != NULL; ) { - /* p_temp is for the next iteration */ - p_temp = p_node->p_next; - - free(p_node); - - p_node = p_temp; - } - - /* destroy the monitor's hash */ - g_hash_table_destroy(p_monitor->hash); - p_monitor->hash = NULL; - - /* destroy the monitor */ - free(p_monitor); - p_monitor = NULL; -} - -void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void) -{ - /* Run through the monitor's connections and decrement the age variable. - * If the age goes negative, we remove the connection from the monitor. - * Function takes O(n) time on the number of connections. */ - - tcp_connection_node_t *p_node, *p_temp; - tcp_connection_t *p_conn; - - if (!p_monitor || p_void) { /* p_void should be NULL in this context */ - return; - } - - if (!p_monitor->p_peek) { - return; - } - - for (p_node = p_monitor->connection_list.p_head; p_node; ) { - if (--p_node->connection.age >= 0) { - p_node = p_node->p_next; - continue; - } - - /* connection on p_node is old. remove connection from the hash. */ - p_conn = &p_node->connection; -#ifdef HASH_DEBUG - fprintf(stderr, "monitor hash removal of connection [%s]", p_conn->key); - if (!g_hash_table_remove(p_monitor->hash, - (gconstpointer) p_conn->key)) { - fprintf(stderr, " - ERROR NOT FOUND\n"); - return; - } - fprintf(stderr, " - OK\n"); -#else - if (!g_hash_table_remove(p_monitor->hash, - (gconstpointer) p_conn)) { - return; - } -#endif - - /* splice p_node out of the connection_list */ - if (p_node->p_prev != NULL) { - p_node->p_prev->p_next = p_node->p_next; - } - if (p_node->p_next != NULL) { - p_node->p_next->p_prev = p_node->p_prev; - } - - /* correct the list head and tail if necessary */ - if (p_monitor->connection_list.p_head == p_node) { - p_monitor->connection_list.p_head = p_node->p_next; - } - if (p_monitor->connection_list.p_tail == p_node) { - p_monitor->connection_list.p_tail = p_node->p_prev; - } - - /* p_temp is for the next iteration */ - p_temp = p_node->p_next; - - /* destroy the node */ - free(p_node); - - p_node = p_temp; - } -} - -void rebuild_tcp_port_monitor_peek_table(tcp_port_monitor_t *p_monitor, - void *p_void) -{ - /* Run through the monitor's connections and rebuild the peek table of - * connection pointers. This is done so peeking into the monitor can be - * done in O(1) time instead of O(n) time for each peek. */ - - tcp_connection_node_t *p_node; - int i = 0; - - if (!p_monitor || p_void) { /* p_void should be NULL in this context */ - return; - } - - /* zero out the peek array */ - memset(p_monitor->p_peek, 0, p_monitor->max_port_monitor_connections * - sizeof(tcp_connection_t *)); - - for (p_node = p_monitor->connection_list.p_head; p_node != NULL; - p_node = p_node->p_next, i++) { - p_monitor->p_peek[i] = &p_node->connection; - } -} - -void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor, - void *p_void) -{ - /* The monitor gets to look at each connection to see if it falls within - * the monitor's port range of interest. Connections of interest are first - * looked up in the hash to see if they are already there. If they are, we - * reset the age of the connection so it is not deleted. If the connection - * is not in the hash, we add it, but only if we haven't exceeded the - * maximum connection limit for the monitor. - * The function takes O(1) time. */ - - tcp_connection_node_t *p_node; - tcp_connection_t *p_connection, *p_conn_hash; - - if (!p_monitor || !p_void) { - return; - } - - /* This p_connection is on caller's stack and not the heap. - * If we are interested, we will create a copy of the connection - * (on the heap) and add it to our list. */ - p_connection = (tcp_connection_t *) p_void; - - /* inspect the local port number of the connection to see if we're - * interested. */ - if ((p_monitor->port_range_begin <= p_connection->local_port) - && (p_connection->local_port <= p_monitor->port_range_end)) { - /* the connection is in the range of the monitor. */ - - /* first check the hash to see if the connection is already there. */ - if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash, - (gconstpointer) p_connection))) { - /* it's already in the hash. reset the age of the connection. */ - p_conn_hash->age = TCP_CONNECTION_STARTING_AGE; - - return; - } - - /* Connection is not yet in the hash. - * Add it if max_connections not exceeded. */ - if (g_hash_table_size(p_monitor->hash) - >= p_monitor->max_port_monitor_connections) { - return; - } - - /* create a new connection node */ - if ((p_node = (tcp_connection_node_t *) - calloc(1, sizeof(tcp_connection_node_t))) == NULL) { - return; - } - - /* copy the connection data */ - if (copy_tcp_connection(&p_node->connection, p_connection) != 0) { - /* error copying the connection data. deallocate p_node to - * avoid leaks and return. */ - free(p_node); - return; - } - - p_node->connection.age = TCP_CONNECTION_STARTING_AGE; - p_node->p_next = NULL; - - /* insert it into the monitor's hash table */ -#ifdef HASH_DEBUG - fprintf(stderr, "monitor hash insert of connection [%s]\n", - p_node->connection.key); -#endif - g_hash_table_insert(p_monitor->hash, - (gpointer) &p_node->connection, (gpointer) &p_node->connection); - - /* append the node to the monitor's connection list */ - if (p_monitor->connection_list.p_tail == NULL) { - /* assume p_head is NULL too */ - p_monitor->connection_list.p_head = p_node; - p_monitor->connection_list.p_tail = p_node; - p_node->p_prev = NULL; - } else { - p_monitor->connection_list.p_tail->p_next = p_node; - p_node->p_prev = p_monitor->connection_list.p_tail; - p_monitor->connection_list.p_tail = p_node; - } - } -} - -/* ------------------------------------------------------------------------ - * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in - * the collection. - * ------------------------------------------------------------------------ */ -void for_each_tcp_port_monitor_in_collection( - tcp_port_monitor_collection_t *p_collection, - tcp_port_monitor_function_ptr_t p_function, void *p_function_args) -{ - tcp_port_monitor_node_t *p_current_node, *p_next_node; - - if (!p_collection || !p_function) { - return; - } - - /* for each monitor in the collection */ - for (p_current_node = p_collection->monitor_list.p_head; - p_current_node != NULL; p_current_node = p_next_node) { - p_next_node = p_current_node->p_next; /* do this first! */ - - if (p_current_node->p_monitor) { - /* apply the function with the given arguments */ - p_function(p_current_node->p_monitor, p_function_args); - } - } -} - -static const unsigned char prefix_4on6[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff -}; - -union sockaddr_in46 { - struct sockaddr_in sa4; - struct sockaddr_in6 sa6; - struct sockaddr sa; -}; - -/* checks whether the address is a IPv4-mapped IPv6 address */ -static int is_4on6(const struct in6_addr *addr) -{ - return ! memcmp(&addr->s6_addr, prefix_4on6, sizeof(prefix_4on6)); -} - - -/* converts the address to appropriate textual representation (IPv6, IPv4 or fqdn) */ -static void print_host(char *p_buffer, size_t buffer_size, const struct in6_addr *addr, int fqdn) -{ - union sockaddr_in46 sa; - socklen_t slen; - - memset(&sa, 0, sizeof(sa)); - - if(is_4on6(addr)) { - sa.sa4.sin_family = AF_INET; - memcpy(&sa.sa4.sin_addr.s_addr, &addr->s6_addr[12], 4); - slen = sizeof(sa.sa4); - } else { - sa.sa6.sin6_family = AF_INET6; - memcpy(&sa.sa6.sin6_addr, addr, sizeof(struct in6_addr)); - slen = sizeof(sa.sa6); - } - - getnameinfo(&sa.sa, slen, p_buffer, buffer_size, NULL, 0, fqdn?0:NI_NUMERICHOST); -} - -/* converts the textual representation of an IPv4 or IPv6 address to struct in6_addr */ -static void string_to_addr(struct in6_addr *addr, const char *p_buffer) -{ - size_t i; - - if(strlen(p_buffer) < 32) { //IPv4 address - i = sizeof(prefix_4on6); - memcpy(addr->s6_addr, prefix_4on6, i); - } else { - i = 0; - } - - for( ; i < sizeof(addr->s6_addr); i+=4, p_buffer+=8) { - sscanf(p_buffer, "%8x", (unsigned *)&addr->s6_addr[i]); - } -} - -/* hash function for tcp_connections */ -static guint tcp_connection_hash(gconstpointer A) -{ - const tcp_connection_t *a = (const tcp_connection_t *) A; - guint hash = 0; - size_t i; - - hash = hash*47 + a->local_port; - hash = hash*47 + a->remote_port; - for(i = 0; i < sizeof(a->local_addr.s6_addr); ++i) - hash = hash*47 + a->local_addr.s6_addr[i]; - for(i = 0; i < sizeof(a->remote_addr.s6_addr); ++i) - hash = hash*47 + a->remote_addr.s6_addr[i]; - - return hash; -} - -/* comparison function for tcp_connections */ -static gboolean tcp_connection_equal(gconstpointer A, gconstpointer B) -{ - const tcp_connection_t *a = (const tcp_connection_t *) A; - const tcp_connection_t *b = (const tcp_connection_t *) B; - - return a->local_port == b->local_port && a->remote_port == b->remote_port && - ! memcmp(&a->local_addr, &b->local_addr, sizeof(a->local_addr)) && - ! memcmp(&a->remote_addr.s6_addr, &b->remote_addr, sizeof(a->remote_addr)); -} - -/* adds connections from file to the collection */ -static void process_file(tcp_port_monitor_collection_t *p_collection, const char *file) -{ - FILE *fp; - char buf[256]; - char local_addr[40]; - char remote_addr[40]; - tcp_connection_t conn; - unsigned long inode, uid, state; - - if ((fp = fopen(file, "r")) == NULL) { - return; - } - - /* ignore field name line */ - fgets(buf, 255, fp); - - /* read all tcp connections */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - - if (sscanf(buf, - "%*d: %39[0-9a-fA-F]:%hx %39[0-9a-fA-F]:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu", - local_addr, &conn.local_port, - remote_addr, &conn.remote_port, - (unsigned long *) &state, (unsigned long *) &uid, - (unsigned long *) &inode) != 7) { - fprintf(stderr, "/proc/net/tcp: bad file format\n"); - } - /** TCP_ESTABLISHED equals 1, but is not (always??) included **/ - //if ((inode == 0) || (state != TCP_ESTABLISHED)) { - if((inode == 0) || (state != 1)) { - continue; - } - - string_to_addr(&conn.local_addr, local_addr); - string_to_addr(&conn.remote_addr, remote_addr); - - /* show the connection to each port monitor. */ - for_each_tcp_port_monitor_in_collection(p_collection, - &show_connection_to_tcp_port_monitor, (void *) &conn); - } - - fclose(fp); -} - -/* ---------------------------------------------------------------------- - * CLIENT INTERFACE - * - * Clients should call only those functions below this line. - * ---------------------------------------------------------------------- */ - -/* ---------------------------------- - * Client operations on port monitors - * ---------------------------------- */ - -/* Clients should first try to "find_tcp_port_monitor" before creating one - * so that there are no redundant monitors. */ -tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin, - in_port_t port_range_end, tcp_port_monitor_args_t *p_creation_args) -{ - tcp_port_monitor_t *p_monitor; - - /* create the monitor */ - p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t)); - if (!p_monitor) { - return NULL; - } - - p_monitor->max_port_monitor_connections = - p_creation_args->max_port_monitor_connections; - - /* build the monitor key for the collection hash */ - g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end); - - /* create the monitor's connection hash */ - if ((p_monitor->hash = g_hash_table_new(tcp_connection_hash, tcp_connection_equal)) == NULL) { - /* we failed to create the hash, so destroy the monitor completely - * so we don't leak */ - destroy_tcp_port_monitor(p_monitor, NULL); - return NULL; - } - - /* create the monitor's peek array */ - if ((p_monitor->p_peek = (tcp_connection_t **) - calloc(p_monitor->max_port_monitor_connections, - sizeof(tcp_connection_t *))) == NULL) { - /* we failed to create the peek array, - * so destroy the monitor completely, again, so we don't leak */ - destroy_tcp_port_monitor(p_monitor, NULL); - return NULL; - } - - p_monitor->port_range_begin = port_range_begin; - p_monitor->port_range_end = port_range_end; - - p_monitor->connection_list.p_head = NULL; - p_monitor->connection_list.p_tail = NULL; - - return p_monitor; -} - -/* Clients use this function to get connection data from the indicated - * port monitor. - * The requested monitor value is copied into a client-supplied char buffer. - * Returns 0 on success, -1 otherwise. */ -int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, - int connection_index, char *p_buffer, size_t buffer_size) -{ - struct sockaddr_in sa; - - if (!p_monitor || !p_buffer || connection_index < 0) { - return -1; - } - - memset(p_buffer, 0, buffer_size); - memset(&sa, 0, sizeof(sa)); - - sa.sin_family = AF_INET; - - /* if the connection index is out of range, we simply return with no error, - * having first cleared the client-supplied buffer. */ - if ((item != COUNT) && (connection_index - > (int) g_hash_table_size(p_monitor->hash) - 1)) { - return 0; - } - - switch (item) { - - case COUNT: - - snprintf(p_buffer, buffer_size, "%d", - g_hash_table_size(p_monitor->hash)); - break; - - case REMOTEIP: - - print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 0); - break; - - case REMOTEHOST: - - print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 1); - break; - - case REMOTEPORT: - - snprintf(p_buffer, buffer_size, "%d", - p_monitor->p_peek[connection_index]->remote_port); - break; - - case REMOTESERVICE: - - sa.sin_port=htons(p_monitor->p_peek[connection_index]->remote_port); - getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); - break; - - case LOCALIP: - - print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 0); - break; - - case LOCALHOST: - - print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 1); - break; - - case LOCALPORT: - - snprintf(p_buffer, buffer_size, "%d", - p_monitor->p_peek[connection_index]->local_port); - break; - - case LOCALSERVICE: - - sa.sin_port=htons(p_monitor->p_peek[connection_index]->local_port); - getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); - break; - - default: - return -1; - } - - return 0; -} - -/* -------------------------------- - * Client operations on collections - * -------------------------------- */ - -/* Create a monitor collection. Do this one first. */ -tcp_port_monitor_collection_t *create_tcp_port_monitor_collection(void) -{ - tcp_port_monitor_collection_t *p_collection; - - p_collection = (tcp_port_monitor_collection_t *) - calloc(1, sizeof(tcp_port_monitor_collection_t)); - if (!p_collection) { - return NULL; - } - - /* create the collection's monitor hash */ - if ((p_collection->hash = g_hash_table_new(g_str_hash, g_str_equal)) - == NULL) { - /* we failed to create the hash, - * so destroy the monitor completely so we don't leak */ - destroy_tcp_port_monitor_collection(p_collection); - return NULL; - } - - p_collection->monitor_list.p_head = NULL; - p_collection->monitor_list.p_tail = NULL; - - return p_collection; -} - -/* Destroy the monitor collection (and the monitors inside). - * Do this one last. */ -void destroy_tcp_port_monitor_collection( - tcp_port_monitor_collection_t *p_collection) -{ - tcp_port_monitor_node_t *p_current_node, *p_next_node; - - if (!p_collection) { - return; - } - - /* destroy the monitors */ - for_each_tcp_port_monitor_in_collection(p_collection, - &destroy_tcp_port_monitor, NULL); - - /* next destroy the empty monitor nodes */ - for (p_current_node = p_collection->monitor_list.p_head; - p_current_node != NULL; p_current_node = p_next_node) { - p_next_node = p_current_node->p_next; /* do this first! */ - - free(p_current_node); - } - - /* destroy the collection's hash */ - g_hash_table_destroy(p_collection->hash); - p_collection->hash = NULL; - - free(p_collection); - p_collection = NULL; -} - -/* Updates the tcp statistics for all monitors within a collection */ -void update_tcp_port_monitor_collection( - tcp_port_monitor_collection_t *p_collection) -{ - if (!p_collection) { - return; - } - - process_file(p_collection, "/proc/net/tcp"); - process_file(p_collection, "/proc/net/tcp6"); - - /* age the connections in all port monitors. */ - for_each_tcp_port_monitor_in_collection(p_collection, - &age_tcp_port_monitor, NULL); - - /* rebuild the connection peek tables of all monitors - * so clients can peek in O(1) time */ - for_each_tcp_port_monitor_in_collection(p_collection, - &rebuild_tcp_port_monitor_peek_table, NULL); -} - -/* After clients create a monitor, use this to add it to the collection. - * Returns 0 on success, -1 otherwise. */ -int insert_tcp_port_monitor_into_collection( - tcp_port_monitor_collection_t *p_collection, - tcp_port_monitor_t *p_monitor) -{ - tcp_port_monitor_node_t *p_node; - - if (!p_collection || !p_monitor) { - return -1; - } - - /* create a container node for this monitor */ - p_node = (tcp_port_monitor_node_t *) - calloc(1, sizeof(tcp_port_monitor_node_t)); - if (!p_node) { - return -1; - } - - /* populate the node */ - p_node->p_monitor = p_monitor; - p_node->p_next = NULL; - - /* add a pointer to this monitor to the collection's hash */ -#ifdef HASH_DEBUG - fprintf(stderr, "collection hash insert of monitor [%s]\n", p_monitor->key); -#endif - g_hash_table_insert(p_collection->hash, (gpointer) p_monitor->key, - (gpointer) p_monitor); - - /* tail of the container gets this node */ - if (!p_collection->monitor_list.p_tail) { - p_collection->monitor_list.p_tail = p_node; - } else { - /* p_next of the tail better be NULL */ - if (p_collection->monitor_list.p_tail->p_next != NULL) { - return -1; - } - - /* splice node onto tail */ - p_collection->monitor_list.p_tail->p_next = p_node; - p_collection->monitor_list.p_tail = p_node; - } - - /* if this was the first element added */ - if (!p_collection->monitor_list.p_head) { - p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail; - } - - return 0; -} - -/* Clients need a way to find monitors */ -tcp_port_monitor_t *find_tcp_port_monitor( - const tcp_port_monitor_collection_t *p_collection, - in_port_t port_range_begin, in_port_t port_range_end) -{ - tcp_port_monitor_t *p_monitor; - gchar key[12]; - - if (!p_collection) { - return NULL; - } - - /* is monitor in hash? */ - g_sprintf(key, ":%04X :%04X", port_range_begin, port_range_end); - p_monitor = g_hash_table_lookup(p_collection->hash, (gconstpointer) key); - return p_monitor; -} diff --git a/src/libtcp-portmon.cc b/src/libtcp-portmon.cc new file mode 100644 index 00000000..63f2a972 --- /dev/null +++ b/src/libtcp-portmon.cc @@ -0,0 +1,532 @@ +/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- + * vim: ts=4 sw=4 noet ai cindent syntax=cpp + * + * libtcp-portmon.c: tcp port monitoring library. + * + * Copyright (C) 2005-2007 Philip Kovacs pkovacs@users.sourceforge.net + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "libtcp-portmon.h" + +#include +#include +#include +#include + +/* ------------------------------------------------------------------- + * IMPLEMENTATION INTERFACE + * + * Implementation-specific interface begins here. Clients should not + * manipulate these structures directly, nor call the defined helper + * functions. Use the "Client interface" functions defined at bottom. + * ------------------------------------------------------------------- */ + +namespace { + /* ------------------------------------------------------------------------ + * A single tcp connection + * ------------------------------------------------------------------------ */ + struct tcp_connection_t { + /* connection's key in monitor hash */ + struct in6_addr local_addr; + struct in6_addr remote_addr; + in_port_t local_port; + in_port_t remote_port; + }; + + /* hash function for tcp connections */ + struct tcp_connection_hash { + size_t operator()(const tcp_connection_t &a) const + { + size_t hash = 0; + size_t i; + + hash = hash*47 + a.local_port; + hash = hash*47 + a.remote_port; + for(i = 0; i < sizeof(a.local_addr.s6_addr); ++i) + hash = hash*47 + a.local_addr.s6_addr[i]; + for(i = 0; i < sizeof(a.remote_addr.s6_addr); ++i) + hash = hash*47 + a.remote_addr.s6_addr[i]; + + return hash; + } + }; + + /* comparison function for tcp connections */ + bool operator==(const tcp_connection_t &a, const tcp_connection_t &b) + { + return a.local_port == b.local_port && a.remote_port == b.remote_port && + ! std::memcmp(&a.local_addr, &b.local_addr, sizeof(a.local_addr)) && + ! std::memcmp(&a.remote_addr.s6_addr, &b.remote_addr, sizeof(a.remote_addr)); + } + + /* ------------------------------------------------------------------------ + * A hash table containing tcp connection + * + * The second parameter provides the mechanism for removing connections if + * they are not seen again in subsequent update cycles. + * ------------------------------------------------------------------------ */ + typedef std::tr1::unordered_map connection_hash_t; + + /* start and end of port monitor range. Set start=end to monitor a single port */ + typedef std::pair port_range_t; + + /* hash function for port ranges */ + struct port_range_hash { + size_t operator()(const port_range_t &a) const + { + return a.first*47 + a.second; + } + }; + + typedef std::tr1::unordered_map monitor_hash_t; + +} + +/* -------------- + * A port monitor + * -------------- */ +struct _tcp_port_monitor_t { + /* hash table of pointers into connection list */ + connection_hash_t hash; + /* array of connection pointers for O(1) peeking + * these point into the hash table*/ + std::vector p_peek; + + _tcp_port_monitor_t(int max_connections) + : hash(), p_peek(max_connections, NULL) + { } + + _tcp_port_monitor_t(const _tcp_port_monitor_t &other) + : hash(other.hash), p_peek(other.p_peek.size(), NULL) + { + // we must rebuild the peek table because the pointers are no longer valid + rebuild_peek_table(); + } + + void rebuild_peek_table() + { + /* Run through the monitor's connections and rebuild the peek table of + * connection pointers. This is done so peeking into the monitor can be + * done in O(1) time instead of O(n) time for each peek. */ + + /* zero out the peek array */ + std::fill(p_peek.begin(), p_peek.end(), static_cast(NULL)); + + size_t i = 0; + for (connection_hash_t::iterator j = hash.begin(); j != hash.end(); ++j, ++i ) { + p_peek[i] = &j->first; + } + } + +private: + // we don't need this atm + const _tcp_port_monitor_t& operator=(const _tcp_port_monitor_t &); +}; + +/* ----------------------------- + * A tcp port monitor collection + * ----------------------------- */ +struct _tcp_port_monitor_collection_t { + /* hash table of monitors */ + monitor_hash_t hash; +}; + +namespace { + /* --------------------------------------- + * A port monitor utility function typedef + * --------------------------------------- */ + typedef void (*tcp_port_monitor_function_ptr_t)(monitor_hash_t::value_type &monitor, + void *p_void); + + void age_tcp_port_monitor(monitor_hash_t::value_type &monitor, void *p_void) + { + /* Run through the monitor's connections and decrement the age variable. + * If the age goes negative, we remove the connection from the monitor. + * Function takes O(n) time on the number of connections. */ + + if (p_void) { /* p_void should be NULL in this context */ + return; + } + + for (connection_hash_t::iterator i = monitor.second.hash.begin(); + i != monitor.second.hash.end(); ) { + + if (--i->second >= 0) + ++i; + else { + /* connection is old. remove connection from the hash. */ + /* erase shouldn't invalidate iterators */ + monitor.second.hash.erase(i++); + } + } + } + + void rebuild_tcp_port_monitor_peek_table(monitor_hash_t::value_type &monitor, + void *p_void) + { + if (p_void) { /* p_void should be NULL in this context */ + return; + } + + monitor.second.rebuild_peek_table(); + } + + void show_connection_to_tcp_port_monitor(monitor_hash_t::value_type &monitor, + void *p_void) + { + /* The monitor gets to look at each connection to see if it falls within + * the monitor's port range of interest. Connections of interest are first + * looked up in the hash to see if they are already there. If they are, we + * reset the age of the connection so it is not deleted. If the connection + * is not in the hash, we add it, but only if we haven't exceeded the + * maximum connection limit for the monitor. + * The function takes O(1) time. */ + + tcp_connection_t *p_connection; + + if (!p_void) { + return; + } + + /* This p_connection is on caller's stack and not the heap. + * If we are interested, we will create a copy of the connection + * (on the heap) and add it to our list. */ + p_connection = (tcp_connection_t *) p_void; + + /* inspect the local port number of the connection to see if we're + * interested. */ + if ((monitor.first.first <= p_connection->local_port) + && (p_connection->local_port <= monitor.first.second)) { + /* the connection is in the range of the monitor. */ + + /* first check the hash to see if the connection is already there. */ + connection_hash_t::iterator i = monitor.second.hash.find(*p_connection); + if (i != monitor.second.hash.end()) { + /* it's already in the hash. reset the age of the connection. */ + i->second = TCP_CONNECTION_STARTING_AGE; + + return; + } + + /* Connection is not yet in the hash. + * Add it if max_connections not exceeded. */ + if (monitor.second.hash.size() < monitor.second.p_peek.size()) { + + monitor.second.hash.insert(connection_hash_t::value_type(*p_connection, + TCP_CONNECTION_STARTING_AGE)); + } + } + } + + /* ------------------------------------------------------------------------ + * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in + * the collection. + * ------------------------------------------------------------------------ */ + void for_each_tcp_port_monitor_in_collection( + tcp_port_monitor_collection_t *p_collection, + tcp_port_monitor_function_ptr_t p_function, void *p_function_args) + { + if (!p_collection || !p_function) { + return; + } + + /* for each monitor in the collection */ + for (monitor_hash_t::iterator i = p_collection->hash.begin(); + i != p_collection->hash.end(); ++i) { + /* apply the function with the given arguments */ + p_function(*i, p_function_args); + } + } + + const unsigned char prefix_4on6[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff + }; + + union sockaddr_in46 { + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + struct sockaddr sa; + }; + + /* checks whether the address is a IPv4-mapped IPv6 address */ + bool is_4on6(const struct in6_addr *addr) + { + return ! std::memcmp(&addr->s6_addr, prefix_4on6, sizeof(prefix_4on6)); + } + + + /* converts the address to appropriate textual representation (IPv6, IPv4 or fqdn) */ + void print_host(char *p_buffer, size_t buffer_size, const struct in6_addr *addr, int fqdn) + { + union sockaddr_in46 sa; + socklen_t slen; + + std::memset(&sa, 0, sizeof(sa)); + + if(is_4on6(addr)) { + sa.sa4.sin_family = AF_INET; + std::memcpy(&sa.sa4.sin_addr.s_addr, &addr->s6_addr[12], 4); + slen = sizeof(sa.sa4); + } else { + sa.sa6.sin6_family = AF_INET6; + std::memcpy(&sa.sa6.sin6_addr, addr, sizeof(struct in6_addr)); + slen = sizeof(sa.sa6); + } + + getnameinfo(&sa.sa, slen, p_buffer, buffer_size, NULL, 0, fqdn?0:NI_NUMERICHOST); + } + + /* converts the textual representation of an IPv4 or IPv6 address to struct in6_addr */ + void string_to_addr(struct in6_addr *addr, const char *p_buffer) + { + size_t i; + + if(std::strlen(p_buffer) < 32) { //IPv4 address + i = sizeof(prefix_4on6); + std::memcpy(addr->s6_addr, prefix_4on6, i); + } else { + i = 0; + } + + for( ; i < sizeof(addr->s6_addr); i+=4, p_buffer+=8) { + std::sscanf(p_buffer, "%8x", (unsigned *)&addr->s6_addr[i]); + } + } + + /* adds connections from file to the collection */ + void process_file(tcp_port_monitor_collection_t *p_collection, const char *file) + { + std::FILE *fp; + char buf[256]; + char local_addr[40]; + char remote_addr[40]; + tcp_connection_t conn; + unsigned long inode, uid, state; + + if ((fp = std::fopen(file, "r")) == NULL) { + return; + } + + /* ignore field name line */ + if(std::fgets(buf, 255, fp) == NULL) { + std::fclose(fp); + return; + } + + /* read all tcp connections */ + while (std::fgets(buf, sizeof(buf), fp) != NULL) { + + if (std::sscanf(buf, + "%*d: %39[0-9a-fA-F]:%hx %39[0-9a-fA-F]:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu", + local_addr, &conn.local_port, + remote_addr, &conn.remote_port, + (unsigned long *) &state, (unsigned long *) &uid, + (unsigned long *) &inode) != 7) { + std::fprintf(stderr, "%s: bad file format\n", file); + } + /** TCP_ESTABLISHED equals 1, but is not (always??) included **/ + //if ((inode == 0) || (state != TCP_ESTABLISHED)) { + if((inode == 0) || (state != 1)) { + continue; + } + + string_to_addr(&conn.local_addr, local_addr); + string_to_addr(&conn.remote_addr, remote_addr); + + /* show the connection to each port monitor. */ + for_each_tcp_port_monitor_in_collection(p_collection, + &show_connection_to_tcp_port_monitor, (void *) &conn); + } + + std::fclose(fp); + } +} + +/* ---------------------------------------------------------------------- + * CLIENT INTERFACE + * + * Clients should call only those functions below this line. + * ---------------------------------------------------------------------- */ + +/* ---------------------------------- + * Client operations on port monitors + * ---------------------------------- */ + +/* Clients use this function to get connection data from the indicated + * port monitor. + * The requested monitor value is copied into a client-supplied char buffer. + * Returns 0 on success, -1 otherwise. */ +int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, + int connection_index, char *p_buffer, size_t buffer_size) +{ + struct sockaddr_in sa; + + if (!p_monitor || !p_buffer || connection_index < 0) { + return -1; + } + + std::memset(p_buffer, 0, buffer_size); + std::memset(&sa, 0, sizeof(sa)); + + sa.sin_family = AF_INET; + + /* if the connection index is out of range, we simply return with no error, + * having first cleared the client-supplied buffer. */ + if ((item != COUNT) && (connection_index >= p_monitor->hash.size())) { + return 0; + } + + switch (item) { + + case COUNT: + + std::snprintf(p_buffer, buffer_size, "%zd", p_monitor->hash.size()); + break; + + case REMOTEIP: + + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 0); + break; + + case REMOTEHOST: + + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 1); + break; + + case REMOTEPORT: + + std::snprintf(p_buffer, buffer_size, "%d", + p_monitor->p_peek[connection_index]->remote_port); + break; + + case REMOTESERVICE: + + sa.sin_port=htons(p_monitor->p_peek[connection_index]->remote_port); + getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); + break; + + case LOCALIP: + + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 0); + break; + + case LOCALHOST: + + print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 1); + break; + + case LOCALPORT: + + std::snprintf(p_buffer, buffer_size, "%d", + p_monitor->p_peek[connection_index]->local_port); + break; + + case LOCALSERVICE: + + sa.sin_port=htons(p_monitor->p_peek[connection_index]->local_port); + getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), NULL, 0, p_buffer, buffer_size, NI_NUMERICHOST); + break; + + default: + return -1; + } + + return 0; +} + +/* -------------------------------- + * Client operations on collections + * -------------------------------- */ + +/* Create a monitor collection. Do this one first. */ +tcp_port_monitor_collection_t *create_tcp_port_monitor_collection(void) +{ + return new tcp_port_monitor_collection_t(); +} + +/* Destroy the monitor collection (and the monitors inside). + * Do this one last. */ +void destroy_tcp_port_monitor_collection( + tcp_port_monitor_collection_t *p_collection) +{ + delete p_collection; +} + +/* Updates the tcp statistics for all monitors within a collection */ +void update_tcp_port_monitor_collection( + tcp_port_monitor_collection_t *p_collection) +{ + if (!p_collection) { + return; + } + + process_file(p_collection, "/proc/net/tcp"); + process_file(p_collection, "/proc/net/tcp6"); + + /* age the connections in all port monitors. */ + for_each_tcp_port_monitor_in_collection(p_collection, + &age_tcp_port_monitor, NULL); + + /* rebuild the connection peek tables of all monitors + * so clients can peek in O(1) time */ + for_each_tcp_port_monitor_in_collection(p_collection, + &rebuild_tcp_port_monitor_peek_table, NULL); +} + +/* Creation of reduntant monitors is silently ignored */ +int insert_new_tcp_port_monitor_into_collection( + tcp_port_monitor_collection_t *p_collection, in_port_t port_range_begin, + in_port_t port_range_end, tcp_port_monitor_args_t *p_creation_args) +{ + + if (!p_collection) { + return -1; + } + + p_collection->hash.insert( monitor_hash_t::value_type( + port_range_t(port_range_begin, port_range_end), + tcp_port_monitor_t(p_creation_args->max_port_monitor_connections) + ) ); + + return 0; +} + +/* Clients need a way to find monitors */ +tcp_port_monitor_t *find_tcp_port_monitor( + tcp_port_monitor_collection_t *p_collection, + in_port_t port_range_begin, in_port_t port_range_end) +{ + if (!p_collection) { + return NULL; + } + + /* is monitor in hash? */ + monitor_hash_t::iterator i = p_collection->hash.find( + port_range_t(port_range_begin, port_range_end) ); + + return i == p_collection->hash.end() ? NULL : &i->second; +} diff --git a/src/libtcp-portmon.h b/src/libtcp-portmon.h index 3465e3f3..a4a5d71e 100644 --- a/src/libtcp-portmon.h +++ b/src/libtcp-portmon.h @@ -1,4 +1,5 @@ /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- + * vim: ts=4 sw=4 noet ai cindent syntax=c * * libtcp-portmon.h: tcp port monitoring library. * @@ -29,13 +30,11 @@ #include #include -#include #include -#include -#include -#include -#include +#ifdef __cplusplus +extern "C" { +#endif /* connection deleted if unseen again after this # of refreshes */ #define TCP_CONNECTION_STARTING_AGE 1 @@ -63,118 +62,21 @@ enum tcp_port_monitor_peekables { LOCALSERVICE }; -/* ------------------------------------------------------------------------ - * A single tcp connection - * - * The age variable provides the mechanism for removing connections if they - * are not seen again in subsequent update cycles. - * ------------------------------------------------------------------------ */ -typedef struct _tcp_connection_t { - /* connection's key in monitor hash */ - struct in6_addr local_addr; - struct in6_addr remote_addr; - in_port_t local_port; - in_port_t remote_port; - int age; -} tcp_connection_t; - -/* ---------------------------------- - * Copy a connection - * - * Returns 0 on success, -1 otherwise - * ---------------------------------- */ -int copy_tcp_connection(tcp_connection_t *p_dest_connection, - const tcp_connection_t *p_source_connection); - -/* ------------------------------------------------------------------- - * A tcp connection node/list - * - * Connections within each monitor are stored in a double-linked list. - * ------------------------------------------------------------------- */ -typedef struct _tcp_connection_node_t { - tcp_connection_t connection; - struct _tcp_connection_node_t *p_prev; - struct _tcp_connection_node_t *p_next; -} tcp_connection_node_t; - -typedef struct _tcp_connection_list_t { - tcp_connection_node_t *p_head; - tcp_connection_node_t *p_tail; -} tcp_connection_list_t; - -/* -------------- +/* ------------------------------------------------------------ * A port monitor - * -------------- */ -typedef struct _tcp_port_monitor_t { - /* monitor's key in collection hash */ - gchar key[TCP_PORT_MONITOR_HASH_KEY_SIZE]; - /* start of monitor port range */ - in_port_t port_range_begin; - /* begin = end to monitor a single port */ - in_port_t port_range_end; - /* list of connections for this monitor */ - tcp_connection_list_t connection_list; - /* hash table of pointers into connection list */ - GHashTable *hash; - /* array of connection pointers for O(1) peeking */ - tcp_connection_t **p_peek; - /* max number of connections */ - unsigned int max_port_monitor_connections; -} tcp_port_monitor_t; + * + * The definition of the struct is hidden because it contains + * C++-specific stuff and we want to #include this from C code. + * ------------------------------------------------------------ */ +typedef struct _tcp_port_monitor_t tcp_port_monitor_t; -/* ------------------------ - * A port monitor node/list - * ------------------------ */ -typedef struct _tcp_port_monitor_node_t { - tcp_port_monitor_t *p_monitor; - struct _tcp_port_monitor_node_t *p_next; -} tcp_port_monitor_node_t; - -typedef struct __tcp_port_monitor_list_t { - tcp_port_monitor_node_t *p_head; - tcp_port_monitor_node_t *p_tail; -} tcp_port_monitor_list_t; - -/* --------------------------------------- - * A port monitor utility function typedef - * --------------------------------------- */ -typedef void (*tcp_port_monitor_function_ptr_t)(tcp_port_monitor_t *p_monitor, - void *p_void); - -/* ------------------------------------------- - * Port monitor utility functions implementing - * tcp_port_monitor_function_ptr_t - * ------------------------------------------- */ -void destroy_tcp_port_monitor(tcp_port_monitor_t *p_monitor, - void *p_void /* (use NULL for this function) */); - -void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, - void *p_void /* (use NULL for this function) */); - -void rebuild_tcp_port_monitor_peek_table(tcp_port_monitor_t *p_monitor, - void *p_void /* (use NULL for this function) */); - -void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor, - void *p_connection /* (client should cast) */); - -/* ----------------------------- +/* ------------------------------------------------------------ * A tcp port monitor collection - * ----------------------------- */ -typedef struct _tcp_port_monitor_collection_t { - /* list of monitors for this collection */ - tcp_port_monitor_list_t monitor_list; - /* hash table of pointers into collection's monitor list */ - GHashTable *hash; -} tcp_port_monitor_collection_t; - -/* -------------------------------------------------------- - * Apply a tcp_port_monitor_function_ptr_t function to each - * port monitor in the collection. - * -------------------------------------------------------- */ -void for_each_tcp_port_monitor_in_collection( - tcp_port_monitor_collection_t *p_collection, - tcp_port_monitor_function_ptr_t p_function, - void *p_function_args /* (for user arguments) */); + * + * The definition of the struct is hidden because it contains + * C++-specific stuff and we want to #include this from C code. + * ------------------------------------------------------------ */ +typedef struct _tcp_port_monitor_collection_t tcp_port_monitor_collection_t; /* ---------------------------------------------------------------------- * CLIENT INTERFACE @@ -192,11 +94,6 @@ typedef struct _tcp_port_monitor_args_t { * Client operations on port monitors * ---------------------------------- */ -/* Clients should first try to "find_tcp_port_monitor" before creating one, - * so that there are no redundant monitors. */ -tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin, - in_port_t port_range_end, tcp_port_monitor_args_t *p_creation_args); - /* Clients use this function to get connection data from * the indicated port monitor. * The requested monitor value is copied into a client-supplied char buffer. @@ -227,14 +124,19 @@ void destroy_tcp_port_monitor_collection( void update_tcp_port_monitor_collection( tcp_port_monitor_collection_t *p_collection); -/* After clients create a monitor, use this to add it to the collection. +/* Creation of reduntant monitors is silently ignored * Returns 0 on success, -1 otherwise. */ -int insert_tcp_port_monitor_into_collection( - tcp_port_monitor_collection_t *p_collection, tcp_port_monitor_t *p_monitor); +int insert_new_tcp_port_monitor_into_collection( + tcp_port_monitor_collection_t *p_collection, in_port_t port_range_begin, + in_port_t port_range_end, tcp_port_monitor_args_t *p_creation_args); /* Clients need a way to find monitors */ tcp_port_monitor_t *find_tcp_port_monitor( - const tcp_port_monitor_collection_t *p_collection, + tcp_port_monitor_collection_t *p_collection, in_port_t port_range_begin, in_port_t port_range_end); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/tcp-portmon.c b/src/tcp-portmon.c index b9c7cdbb..9ed1c340 100644 --- a/src/tcp-portmon.c +++ b/src/tcp-portmon.c @@ -98,14 +98,8 @@ int tcp_portmon_init(struct text_object *obj, const char *arg) /* if a port monitor for this port does not exist, * create one and add it to the collection */ if (find_tcp_port_monitor(pmc, port_begin, port_end) == NULL) { - tcp_port_monitor_t *p_monitor = create_tcp_port_monitor(port_begin, - port_end, &pma); - - if (!p_monitor) { - CRIT_ERR(NULL, NULL, "tcp_portmon: unable to create port monitor"); - } /* add the newly created monitor to the collection */ - if (insert_tcp_port_monitor_into_collection(pmc, p_monitor) != 0) { + if (insert_new_tcp_port_monitor_into_collection(pmc, port_begin, port_end, &pma) != 0) { CRIT_ERR(NULL, NULL, "tcp_portmon: unable to add port monitor to " "collection"); }