* 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;
+}
+
/* -----------------------------------------------------------------------------
* Open-addressed hash implementation requires that we supply two hash functions
* and a match function to compare two hash elements for identity.
p_monitor1 = (tcp_port_monitor_t *)p_data1;
p_monitor2 = (tcp_port_monitor_t *)p_data2;
- return (p_monitor1->port_range_begin == p_monitor1->port_range_begin &&
- p_monitor2->port_range_end == p_monitor2->port_range_end);
+ return (p_monitor1->port_range_begin == p_monitor2->port_range_begin &&
+ p_monitor1->port_range_end == p_monitor2->port_range_end);
}
/* ---------------------------------------------------------------------------
#ifdef HASH_DEBUG
fprintf(stderr,"--- num vacated is %d, vacated factor is %.3f\n", p_monitor->hash.vacated, vacated_load );
#endif
- if ( vacated_load <= TCP_CONNECIION_HASH_MAX_VACATED_PCT )
+ if ( vacated_load <= TCP_CONNECTION_HASH_MAX_VACATED_RATIO )
{
/* hash is fine and needs no rebalancing */
return;
void * p_void
)
{
- /* Run through the monitori's connections and rebuild the 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. */
return;
/* zero out the peek array */
- memset( p_monitor->p_peek, 0, TCP_CONNECTION_HASH_SIZE * sizeof(tcp_connection_t *) );
+ memset( p_monitor->p_peek, 0, p_monitor->hash.positions * sizeof(tcp_connection_t *) );
for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next, i++ )
{
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. */
tcp_connection_t *p_connection = (tcp_connection_t *)p_void;
/* inspect the local port number of the connection to see if we're interested. */
/* it's already in the hash. reset the age of the connection. */
if ( p_connection != NULL )
{
- p_connection->age = TCP_CONNECIION_STARTING_AGE;
+ p_connection->age = TCP_CONNECTION_STARTING_AGE;
}
return;
* if our load factor cap is now exceeded. The benefit of limiting connections in this way
* is that the hash will continue to function at an average (1) speed by keeping the load
* load factor down. Of course the downside is that each port monitor has a strict maximum
- * connection limit. Future versions should probably allow the client to set the hash size
- * and load limits and/or provide for automatic resizing of hashes. */
+ * connection limit. */
- if ( (double)p_monitor->hash.size / (double)p_monitor->hash.positions >= TCP_CONNECTION_HASH_MAX_LOAD_PCT )
+ if ( (double)p_monitor->hash.size / (double)p_monitor->hash.positions >= TCP_CONNECTION_HASH_MAX_LOAD_RATIO )
{
/* hash exceeds our load limit is now "full" */
return;
if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
return;
- p_node->connection = *p_connection; /* bitwise copy of the struct */
- p_node->connection.age = TCP_CONNECIION_STARTING_AGE;
+ /* 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 */
}
+/* ----------------------------------------------------------------------------------------
+ * Calculate an efficient hash size based on the desired number of elements and load factor.
+ * ---------------------------------------------------------------------------------------- */
+int calc_efficient_hash_size(
+ int min_elements,
+ int max_hash_size,
+ double max_load_factor
+ )
+{
+ double min_size, hash_size, log_base_2;
+
+ /* the size of the hash will the smallest power of two such that the minimum number
+ of desired elements does not exceed the maximum load factor. */
+
+ min_size = (double)min_elements / max_load_factor; /* starting point */
+
+ /* now adjust size up to nearest power of two */
+ log_base_2 = (double) (int) ( log(min_size) / log(2) ) ; /* lop off fractional portion of log */
+
+ hash_size = pow(2,log_base_2) >= min_size ? min_size : pow(2,(double)++log_base_2);
+
+ /* respect the maximum */
+ hash_size = hash_size <= max_hash_size ? hash_size : max_hash_size;
+
+ /*
+ fprintf(stderr,"hash size is %d, based on %d min_elements and %.02f max load, %d maximum\n",
+ (int)hash_size, min_elements, max_load_factor, max_hash_size);
+ */
+ return hash_size;
+}
+
/* ----------------------------------------------------------------------
* CLIENT INTERFACE
*
tcp_port_monitor_t * create_tcp_port_monitor(
in_port_t port_range_begin,
in_port_t port_range_end,
- int hash_size
+ tcp_port_monitor_args_t * p_creation_args
)
{
tcp_port_monitor_t * p_monitor;
/* create the monitor's connection hash */
if ( hash_create( &p_monitor->hash,
- hash_size > 0 ? hash_size : TCP_CONNECTION_HASH_SIZE,
+ p_creation_args && p_creation_args->min_port_monitor_connections > 0 ?
+ calc_efficient_hash_size( p_creation_args->min_port_monitor_connections,
+ TCP_CONNECTION_HASH_SIZE_MAX,
+ TCP_CONNECTION_HASH_MAX_LOAD_RATIO ) :
+ TCP_CONNECTION_HASH_SIZE_DEFAULT,
&connection_hash_function_1, &connection_hash_function_2,
&connection_match_function, NULL ) != 0 )
{
}
/* create the monitor's peek array */
- if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( TCP_CONNECTION_HASH_SIZE, sizeof(tcp_connection_t *))) == NULL )
+ if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( p_monitor->hash.positions, 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);
The requested monitor value is copied into a client-supplied char buffer.
Returns 0 on success, -1 otherwise. */
int peek_tcp_port_monitor(
- tcp_port_monitor_t * p_monitor,
+ const tcp_port_monitor_t * p_monitor,
int item,
int connection_index,
char * p_buffer,
/* Create a monitor collection. Do this one first. */
tcp_port_monitor_collection_t * create_tcp_port_monitor_collection(
- int hash_size
+ tcp_port_monitor_collection_args_t * p_creation_args
)
{
tcp_port_monitor_collection_t * p_collection;
/* create the collection's monitor hash */
if ( hash_create( &p_collection->hash,
- hash_size > 0 ? hash_size : TCP_MONITOR_HASH_SIZE,
+ p_creation_args && p_creation_args->min_port_monitors > 0 ?
+ calc_efficient_hash_size( p_creation_args->min_port_monitors,
+ TCP_MONITOR_HASH_SIZE_MAX,
+ TCP_MONITOR_HASH_MAX_LOAD_RATIO ) :
+ TCP_MONITOR_HASH_SIZE_DEFAULT,
&monitor_hash_function_1, &monitor_hash_function_2,
&monitor_match_function, NULL ) != 0 )
{
FILE *fp;
char buf[256];
tcp_connection_t conn;
- unsigned long state;
+ unsigned long inode,uid,state;
if ( !p_collection )
return;
/* read all tcp connections */
while (fgets (buf, sizeof (buf), fp) != NULL) {
- if ( sscanf (buf, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
- (unsigned long *)&conn.local_addr, (unsigned long *)&conn.local_port,
- (unsigned long *)&conn.remote_addr, (unsigned long *)&conn.remote_port,
- (unsigned long *)&state, (unsigned long *)&conn.uid, (unsigned long *)&conn.inode) != 7 )
+ if ( sscanf (buf, "%*d: %lx:%hx %lx:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
+ (unsigned long *)&conn.local_addr, &conn.local_port,
+ (unsigned long *)&conn.remote_addr, &conn.remote_port,
+ (unsigned long *)&state, (unsigned long *)&uid, (unsigned long *)&inode) != 7 )
fprintf( stderr, "/proc/net/tcp: bad file format\n" );
- if ((conn.inode == 0) || (state != TCP_ESTABLISHED)) continue;
+ if ((inode == 0) || (state != TCP_ESTABLISHED)) continue;
/* show the connection to each port monitor. */
for_each_tcp_port_monitor_in_collection(
/* Clients need a way to find monitors */
tcp_port_monitor_t * find_tcp_port_monitor(
- tcp_port_monitor_collection_t * p_collection,
+ const tcp_port_monitor_collection_t * p_collection,
in_port_t port_range_begin,
in_port_t port_range_end
)