X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Flibtcp-portmon.c;h=5881cc902ad79ba9d3575b4cd47a3e9e9c928aab;hb=6aef55aa00257f61eecf4dde7f9710429b3b082e;hp=f9b801bb3878abbce10592c3cd03b34d0ba69129;hpb=5a1d151a58affa053c3f4a496deef7aef3d77f9c;p=monky diff --git a/src/libtcp-portmon.c b/src/libtcp-portmon.c index f9b801b..5881cc9 100644 --- a/src/libtcp-portmon.c +++ b/src/libtcp-portmon.c @@ -2,6 +2,8 @@ * libtcp-portmon.c: tcp port monitoring library. * * Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net + * + * $Id$ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +30,28 @@ * 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. @@ -207,8 +231,8 @@ int monitor_match_function( const void *p_data1, const void *p_data2 ) 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); } /* --------------------------------------------------------------------------- @@ -326,7 +350,7 @@ void maintain_tcp_port_monitor_hash( #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; @@ -358,7 +382,7 @@ void rebuild_tcp_port_monitor_peek_table( 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. */ @@ -369,7 +393,7 @@ void rebuild_tcp_port_monitor_peek_table( 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++ ) { @@ -395,6 +419,8 @@ void show_connection_to_tcp_port_monitor( 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. */ @@ -411,7 +437,7 @@ void show_connection_to_tcp_port_monitor( /* 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; @@ -422,10 +448,9 @@ void show_connection_to_tcp_port_monitor( * 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; @@ -435,8 +460,15 @@ void show_connection_to_tcp_port_monitor( 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 */ @@ -496,7 +528,38 @@ void for_each_tcp_port_monitor_in_collection( } +/* ---------------------------------------------------------------------------------------- + * 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 * @@ -511,7 +574,8 @@ void for_each_tcp_port_monitor_in_collection( 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 + in_port_t port_range_end, + tcp_port_monitor_args_t * p_creation_args ) { tcp_port_monitor_t * p_monitor; @@ -522,7 +586,12 @@ tcp_port_monitor_t * create_tcp_port_monitor( return NULL; /* create the monitor's connection hash */ - if ( hash_create( &p_monitor->hash, TCP_CONNECTION_HASH_SIZE, + if ( hash_create( &p_monitor->hash, + 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 ) { @@ -532,7 +601,7 @@ tcp_port_monitor_t * create_tcp_port_monitor( } /* 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); @@ -552,7 +621,7 @@ tcp_port_monitor_t * create_tcp_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( - tcp_port_monitor_t * p_monitor, + const tcp_port_monitor_t * p_monitor, int item, int connection_index, char * p_buffer, @@ -653,7 +722,9 @@ int peek_tcp_port_monitor( * -------------------------------- */ /* Create a monitor collection. Do this one first. */ -tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( void ) +tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( + tcp_port_monitor_collection_args_t * p_creation_args + ) { tcp_port_monitor_collection_t * p_collection; @@ -662,7 +733,12 @@ tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( void ) return NULL; /* create the collection's monitor hash */ - if ( hash_create( &p_collection->hash, TCP_MONITOR_HASH_SIZE, + if ( hash_create( &p_collection->hash, + 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 ) { @@ -718,7 +794,7 @@ void update_tcp_port_monitor_collection( FILE *fp; char buf[256]; tcp_connection_t conn; - unsigned long state; + unsigned long inode,uid,state; if ( !p_collection ) return; @@ -740,14 +816,14 @@ void update_tcp_port_monitor_collection( /* 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( @@ -826,7 +902,7 @@ int insert_tcp_port_monitor_into_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 )