1 /* -------------------------------------------------------------------------
2 * libtcp-portmon.c: tcp port monitoring library.
4 * Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * --------------------------------------------------------------------------- */
23 #include "libtcp-portmon.h"
25 /* -------------------------------------------------------------------
26 * IMPLEMENTATION INTERFACE
28 * Implementation-specific interface begins here. Clients should not
29 * manipulate these structures directly, nor call the defined helper
30 * functions. Use the "Client interface" functions defined at bottom.
31 * ------------------------------------------------------------------- */
33 /* ----------------------------------
34 * Copy a tcp_connection_t
36 * Returns 0 on success, -1 otherwise.
37 * ----------------------------------*/
38 int copy_tcp_connection(
39 tcp_connection_t * p_dest_connection,
40 const tcp_connection_t * p_source_connection
43 if ( !p_dest_connection || !p_source_connection )
46 p_dest_connection->local_addr = p_source_connection->local_addr;
47 p_dest_connection->local_port = p_source_connection->local_port;
48 p_dest_connection->remote_addr = p_source_connection->remote_addr;
49 p_dest_connection->remote_port = p_source_connection->remote_port;
50 p_dest_connection->age = p_source_connection->age;
55 /* -----------------------------------------------------------------------------
56 * Open-addressed hash implementation requires that we supply two hash functions
57 * and a match function to compare two hash elements for identity.
58 * ----------------------------------------------------------------------------- */
60 /* --------------------------------------------------
61 * Functions to hash the connections within a monitor
62 * --------------------------------------------------*/
64 #define CONNECTION_HASH_KEY_LEN 17
66 /* ----------------------------------------------------------------------------------
67 * First connection hash function: DJB with a 65521 prime modulus to govern the range.
68 * ----------------------------------------------------------------------------------*/
69 int connection_hash_function_1( const void *p_data )
71 tcp_connection_t *p_conn;
72 char key[CONNECTION_HASH_KEY_LEN];
73 unsigned int hash = 5381;
79 memset(key,0,sizeof(key));
81 /* p_data is a pointer to tcp_connection_t */
82 p_conn = (tcp_connection_t *)p_data;
84 /* key is a hex representation of the connection */
85 snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X",
86 p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
88 fprintf(stderr,"--- key=[%s]\n",key);
91 for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
93 hash = ((hash << 5) + hash) + (key[i]);
96 return (hash & 0x7FFFFFFF) % 65521;
99 /* -------------------------------------------------------------------------
100 * Second connection hash function: DEK, modified to return odd numbers only,
101 * as required for open-address hashing using double-hash probing.
102 * Also uses a 65521 prime modulus to govern the range.
103 * -------------------------------------------------------------------------*/
104 int connection_hash_function_2( const void *p_data )
106 tcp_connection_t *p_conn;
107 char key[CONNECTION_HASH_KEY_LEN];
108 unsigned int hash = CONNECTION_HASH_KEY_LEN-1;
114 memset(key,0,sizeof(key));
116 /* p_data is a pointer to a tcp_connection_t */
117 p_conn = (tcp_connection_t *)p_data;
119 /* key is a hex representation of the connection */
120 snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X",
121 p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
123 for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
125 hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
127 return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
130 /* -------------------------------------------------------------------------
131 * Connection Match function returns non-zero if hash elements are identical.
132 * -------------------------------------------------------------------------*/
133 int connection_match_function( const void *p_data1, const void *p_data2 )
135 tcp_connection_t *p_conn1, *p_conn2;
137 if ( !p_data1 || !p_data2 )
140 /* p_data1, p_data2 are pointers to tcp_connection_t */
141 p_conn1 = (tcp_connection_t *)p_data1;
142 p_conn2 = (tcp_connection_t *)p_data2;
144 return (p_conn1->local_addr == p_conn2->local_addr &&
145 p_conn1->local_port == p_conn2->local_port &&
146 p_conn1->remote_addr == p_conn2->remote_addr &&
147 p_conn1->remote_port == p_conn2->remote_port );
150 /* --------------------------------------------------
151 * Functions to hash the monitors within a collection
152 * --------------------------------------------------*/
154 #define MONITOR_HASH_KEY_LEN 9
156 /* -------------------------------------------------------------------------------
157 * First monitor hash function: DJB with a 65521 prime modulus to govern the range.
158 * -------------------------------------------------------------------------------*/
159 int monitor_hash_function_1( const void *p_data )
161 tcp_port_monitor_t *p_monitor;
162 char key[MONITOR_HASH_KEY_LEN];
163 unsigned int hash = 5381;
169 memset(key,0,sizeof(key));
171 /* p_data is a pointer to tcp_port_monitor_t */
172 p_monitor = (tcp_port_monitor_t *)p_data;
174 /* key is a hex representation of the starting port concatenated to the ending port */
175 snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
176 p_monitor->port_range_begin, p_monitor->port_range_end );
178 fprintf(stderr,"--- key=[%s]\n",key);
181 for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
183 hash = ((hash << 5) + hash) + (key[i]);
186 return (hash & 0x7FFFFFFF) % 65521;
189 /* -----------------------------------------------------------------------
190 * Second monitor hash function: DEK, modified to return odd numbers only,
191 * as required for open-address hashing using double-hash probing.
192 * Also uses a 65521 prime modulus to govern the range.
193 * -----------------------------------------------------------------------*/
194 int monitor_hash_function_2( const void *p_data )
196 tcp_port_monitor_t *p_monitor;
197 char key[MONITOR_HASH_KEY_LEN];
198 unsigned int hash = MONITOR_HASH_KEY_LEN-1;
204 memset(key,0,sizeof(key));
206 /* p_data is a pointer to a tcp_port_monitor_t */
207 p_monitor = (tcp_port_monitor_t *)p_data;
209 /* key is a hex representation of the starting port concatenated to the ending port */
210 snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
211 p_monitor->port_range_begin, p_monitor->port_range_end );
213 for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
215 hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
217 return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
220 /* ----------------------------------------------------------------------
221 * Monitor match function returns non-zero if hash elements are identical.
222 * ----------------------------------------------------------------------*/
223 int monitor_match_function( const void *p_data1, const void *p_data2 )
225 tcp_port_monitor_t *p_monitor1, *p_monitor2;
227 if ( !p_data1 || !p_data2 )
230 /* p_data1, p_data2 are pointers to tcp_connection_t */
231 p_monitor1 = (tcp_port_monitor_t *)p_data1;
232 p_monitor2 = (tcp_port_monitor_t *)p_data2;
234 return (p_monitor1->port_range_begin == p_monitor1->port_range_begin &&
235 p_monitor2->port_range_end == p_monitor2->port_range_end);
238 /* ---------------------------------------------------------------------------
239 * Port monitor utility functions implementing tcp_port_monitor_function_ptr_t
240 * ---------------------------------------------------------------------------*/
241 void destroy_tcp_port_monitor(
242 tcp_port_monitor_t * p_monitor,
246 tcp_connection_node_t *p_node, *p_temp;
248 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
251 /* destroy the monitor's hash */
252 hash_destroy(&p_monitor->hash);
254 /* destroy the monitor's peek array */
255 free( p_monitor->p_peek );
257 /* destroy the monitor's connection list */
258 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; )
260 /* p_temp is for the next iteration */
261 p_temp = p_node->p_next;
268 /* destroy the monitor */
273 void age_tcp_port_monitor(
274 tcp_port_monitor_t * p_monitor,
278 /* Run through the monitor's connections and decrement the age variable.
279 * If the age goes negative, we remove the connection from the monitor.
280 * Function takes O(n) time on the number of connections. */
282 tcp_connection_node_t *p_node, *p_temp;
283 tcp_connection_t *p_conn;
286 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
289 if ( !p_monitor->p_peek )
292 for ( p_node = p_monitor->connection_list.p_head; p_node != NULL; )
294 if ( --p_node->connection.age >= 0 ) {
295 p_node = p_node->p_next;
299 /* connection on p_node is old. remove connection from the hash. */
300 p_conn = &p_node->connection;
301 p_cast = (void *)p_conn;
302 if ( hash_remove( &p_monitor->hash, &p_cast ) != 0 ) {
304 fprintf(stderr, "--- hash_remove error\n");
309 /* splice p_node out of the connection_list */
310 if ( p_node->p_prev != NULL )
311 p_node->p_prev->p_next = p_node->p_next;
312 if ( p_node->p_next != NULL )
313 p_node->p_next->p_prev = p_node->p_prev;
315 /* correct the list head and tail if necessary */
316 if ( p_monitor->connection_list.p_head == p_node )
317 p_monitor->connection_list.p_head = p_node->p_next;
318 if ( p_monitor->connection_list.p_tail == p_node )
319 p_monitor->connection_list.p_tail = p_node->p_prev;
321 /* p_temp is for the next iteration */
322 p_temp = p_node->p_next;
324 /* destroy the node */
331 void maintain_tcp_port_monitor_hash(
332 tcp_port_monitor_t * p_monitor,
336 /* Check the number of vacated slots in the hash. If it exceeds our maximum
337 * threshold (should be about 1/4 of the hash table), then the hash table
338 * performance degrades from O(1) toward O(n) as the number of vacated slots
339 * climbs. This is avoided by clearing the hash and reinserting the entries.
340 * The benefit of open-addressing hashing does come with this price --
341 * you must rebalance it occasionally. */
343 tcp_connection_node_t *p_node;
346 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
349 vacated_load = (double)p_monitor->hash.vacated / (double)p_monitor->hash.positions;
351 fprintf(stderr,"--- num vacated is %d, vacated factor is %.3f\n", p_monitor->hash.vacated, vacated_load );
353 if ( vacated_load <= TCP_CONNECTION_HASH_MAX_VACATED_RATIO )
355 /* hash is fine and needs no rebalancing */
360 fprintf(stderr,"--- rebuilding hash\n");
363 /* rebuild the hash */
364 memset( p_monitor->hash.pp_table, 0, p_monitor->hash.positions * sizeof(void **));
365 p_monitor->hash.size = 0;
366 p_monitor->hash.vacated = 0;
368 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next )
370 if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
373 fprintf(stderr,"--- hash_insert error\n");
380 void rebuild_tcp_port_monitor_peek_table(
381 tcp_port_monitor_t * p_monitor,
385 /* Run through the monitor's connections and rebuild the peek table
386 * of connection pointers. This is done so peeking into the monitor
387 * can be done in O(1) time instead of O(n) time for each peek. */
389 tcp_connection_node_t *p_node;
392 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
395 /* zero out the peek array */
396 memset( p_monitor->p_peek, 0, p_monitor->hash.positions * sizeof(tcp_connection_t *) );
398 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next, i++ )
400 p_monitor->p_peek[i] = &p_node->connection;
404 void show_connection_to_tcp_port_monitor(
405 tcp_port_monitor_t * p_monitor,
409 /* The monitor gets to look at each connection to see if it falls within
410 * the monitor's port range of interest. Connections of interest are first
411 * looked up in the hash to see if they are already there. If they are, we
412 * reset the age of the connection so it is not deleted. If the connection
413 * is not in the hash, we add it, but only if the hash is not saturated.
414 * The function takes O(1) time. */
416 tcp_connection_node_t *p_node;
419 if ( !p_monitor || !p_void )
422 /* This p_connection is on caller's stack and not the heap. If we are interested,
423 * we will create a copy of the connection (on the heap) and add it to our list. */
424 tcp_connection_t *p_connection = (tcp_connection_t *)p_void;
426 /* inspect the local port number of the connection to see if we're interested. */
427 if ( (p_monitor->port_range_begin <= p_connection->local_port) &&
428 (p_connection->local_port <= p_monitor->port_range_end) )
430 /* the connection is in the range of the monitor. */
432 /* first check the hash to see if the connection is already there. */
433 p_cast = (void *)p_connection;
434 if ( hash_lookup( &p_monitor->hash, &p_cast ) == 0 )
436 p_connection = (tcp_connection_t *)p_cast;
437 /* it's already in the hash. reset the age of the connection. */
438 if ( p_connection != NULL )
440 p_connection->age = TCP_CONNECTION_STARTING_AGE;
446 /* Connection is not yet in the hash. We will try to add it, but only if the hash is not
447 * yet saturated. We assume the hash is saturated (and therefore ignore this connection)
448 * if our load factor cap is now exceeded. The benefit of limiting connections in this way
449 * is that the hash will continue to function at an average (1) speed by keeping the load
450 * load factor down. Of course the downside is that each port monitor has a strict maximum
451 * connection limit. */
453 if ( (double)p_monitor->hash.size / (double)p_monitor->hash.positions >= TCP_CONNECTION_HASH_MAX_LOAD_RATIO )
455 /* hash exceeds our load limit is now "full" */
459 /* create a new connection node */
460 if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
463 /* copy the connection data */
464 if ( copy_tcp_connection( &p_node->connection, p_connection ) != 0 )
466 /* error copying the connection data. deallocate p_node to avoid leaks and return. */
471 p_node->connection.age = TCP_CONNECTION_STARTING_AGE;
472 p_node->p_next = NULL;
474 /* insert it into the monitor's hash table */
475 if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
477 /* error inserting into hash. delete the connection node we just created, so no leaks. */
479 fprintf(stderr, "--- hash_insert error\n");
485 /* append the node to the monitor's connection list */
486 if ( p_monitor->connection_list.p_tail == NULL ) /* assume p_head is NULL too */
488 p_monitor->connection_list.p_head = p_node;
489 p_monitor->connection_list.p_tail = p_node;
490 p_node->p_prev = NULL;
494 p_monitor->connection_list.p_tail->p_next = p_node;
495 p_node->p_prev = p_monitor->connection_list.p_tail;
496 p_monitor->connection_list.p_tail = p_node;
501 /* ---------------------------------------------------------------------------------------
502 * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in the collection.
503 * ---------------------------------------------------------------------------------------*/
504 void for_each_tcp_port_monitor_in_collection(
505 tcp_port_monitor_collection_t * p_collection,
506 tcp_port_monitor_function_ptr_t p_function,
507 void * p_function_args
510 tcp_port_monitor_node_t * p_current_node, * p_next_node;
512 if ( !p_collection || !p_function )
515 /* for each monitor in the collection */
516 for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
518 p_next_node = p_current_node->p_next; /* do this first! */
520 if ( p_current_node->p_monitor )
522 /* apply the function with the given arguments */
523 (*p_function)( p_current_node->p_monitor, p_function_args );
526 p_current_node = p_next_node;
531 /* ----------------------------------------------------------------------------------------
532 * Calculate an efficient hash size based on the desired number of elements and load factor.
533 * ---------------------------------------------------------------------------------------- */
534 int calc_efficient_hash_size(
537 double max_load_factor
540 double min_size, hash_size, log_base_2;
542 /* the size of the hash will the smallest power of two such that the minimum number
543 of desired elements does not exceed the maximum load factor. */
545 min_size = (double)min_elements / max_load_factor; /* starting point */
547 /* now adjust size up to nearest power of two */
548 log_base_2 = (double) (int) ( log(min_size) / log(2) ) ; /* lop off fractional portion of log */
550 hash_size = pow(2,log_base_2) >= min_size ? min_size : pow(2,(double)++log_base_2);
552 /* respect the maximum */
553 hash_size = hash_size <= max_hash_size ? hash_size : max_hash_size;
556 fprintf(stderr,"hash size is %d, based on %d min_elements and %.02f max load, %d maximum\n",
557 (int)hash_size, min_elements, max_load_factor, max_hash_size);
563 /* ----------------------------------------------------------------------
566 * Clients should call only those functions below this line.
567 * ---------------------------------------------------------------------- */
569 /* ----------------------------------
570 * Client operations on port monitors
571 * ---------------------------------- */
573 /* Clients should first try to "find_tcp_port_monitor" before creating one
574 so that there are no redundant monitors. */
575 tcp_port_monitor_t * create_tcp_port_monitor(
576 in_port_t port_range_begin,
577 in_port_t port_range_end,
578 tcp_port_monitor_args_t * p_creation_args
581 tcp_port_monitor_t * p_monitor;
583 /* create the monitor */
584 p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t) );
588 /* create the monitor's connection hash */
589 if ( hash_create( &p_monitor->hash,
590 p_creation_args && p_creation_args->min_port_monitor_connections > 0 ?
591 calc_efficient_hash_size( p_creation_args->min_port_monitor_connections,
592 TCP_CONNECTION_HASH_SIZE_MAX,
593 TCP_CONNECTION_HASH_MAX_LOAD_RATIO ) :
594 TCP_CONNECTION_HASH_SIZE_DEFAULT,
595 &connection_hash_function_1, &connection_hash_function_2,
596 &connection_match_function, NULL ) != 0 )
598 /* we failed to create the hash, so destroy the monitor completely so we don't leak */
599 destroy_tcp_port_monitor(p_monitor,NULL);
603 /* create the monitor's peek array */
604 if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( p_monitor->hash.positions, sizeof(tcp_connection_t *))) == NULL )
606 /* we failed to create the peek array, so destroy the monitor completely, again, so we don't leak */
607 destroy_tcp_port_monitor(p_monitor,NULL);
611 p_monitor->port_range_begin = port_range_begin;
612 p_monitor->port_range_end = port_range_end;
614 p_monitor->connection_list.p_head = NULL;
615 p_monitor->connection_list.p_tail = NULL;
620 /* Clients use this function to get connection data from the indicated port monitor.
621 The requested monitor value is copied into a client-supplied char buffer.
622 Returns 0 on success, -1 otherwise. */
623 int peek_tcp_port_monitor(
624 const tcp_port_monitor_t * p_monitor,
626 int connection_index,
631 struct hostent *p_hostent;
632 struct servent *p_servent;
635 if ( !p_monitor || !p_buffer || connection_index < 0 )
638 memset(p_buffer, 0, buffer_size);
639 memset(&net, 0, sizeof(net));
641 /* if the connection index is out of range, we simply return with no error
642 * having first cleared the client-supplied buffer. */
643 if ( (item!=COUNT) && (connection_index > p_monitor->hash.size - 1) )
650 snprintf( p_buffer, buffer_size, "%d" , p_monitor->hash.size );
655 net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
656 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
661 p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->remote_addr, sizeof(in_addr_t), AF_INET);
662 /* if no host name found, just use ip address. */
663 if ( !p_hostent || !p_hostent->h_name )
665 net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
666 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
669 snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
674 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );
679 net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
680 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
685 p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->local_addr, sizeof(in_addr_t), AF_INET);
686 /* if no host name found, just use ip address. */
687 if ( !p_hostent || !p_hostent->h_name )
689 net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
690 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
693 snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
698 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
703 p_servent = getservbyport( htons(p_monitor->p_peek[ connection_index ]->local_port ), "tcp" );
704 /* if no service name found for the port, just use the port number. */
705 if ( !p_servent || !p_servent->s_name )
707 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
710 snprintf( p_buffer, buffer_size, "%s", p_servent->s_name );
720 /* --------------------------------
721 * Client operations on collections
722 * -------------------------------- */
724 /* Create a monitor collection. Do this one first. */
725 tcp_port_monitor_collection_t * create_tcp_port_monitor_collection(
726 tcp_port_monitor_collection_args_t * p_creation_args
729 tcp_port_monitor_collection_t * p_collection;
731 p_collection = (tcp_port_monitor_collection_t *) calloc( 1, sizeof( tcp_port_monitor_collection_t ) );
735 /* create the collection's monitor hash */
736 if ( hash_create( &p_collection->hash,
737 p_creation_args && p_creation_args->min_port_monitors > 0 ?
738 calc_efficient_hash_size( p_creation_args->min_port_monitors,
739 TCP_MONITOR_HASH_SIZE_MAX,
740 TCP_MONITOR_HASH_MAX_LOAD_RATIO ) :
741 TCP_MONITOR_HASH_SIZE_DEFAULT,
742 &monitor_hash_function_1, &monitor_hash_function_2,
743 &monitor_match_function, NULL ) != 0 )
745 /* we failed to create the hash, so destroy the monitor completely so we don't leak */
746 destroy_tcp_port_monitor_collection(p_collection);
750 p_collection->monitor_list.p_head = NULL;
751 p_collection->monitor_list.p_tail = NULL;
756 /* Destroy the monitor collection (and the monitors inside). Do this one last. */
757 void destroy_tcp_port_monitor_collection(
758 tcp_port_monitor_collection_t * p_collection
761 tcp_port_monitor_node_t * p_current_node, * p_next_node;
766 /* destroy the collection's hash */
767 hash_destroy( &p_collection->hash );
769 /* destroy the monitors */
770 for_each_tcp_port_monitor_in_collection(
772 &destroy_tcp_port_monitor,
776 /* next destroy the empty monitor nodes */
777 for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
779 p_next_node = p_current_node->p_next; /* do this first! */
781 free( p_current_node );
782 p_current_node = p_next_node;
785 free( p_collection );
789 /* Updates the tcp statistics for all monitors within a collection */
790 void update_tcp_port_monitor_collection(
791 tcp_port_monitor_collection_t * p_collection
796 tcp_connection_t conn;
797 unsigned long inode,uid,state;
802 /* age the connections in all port monitors. */
803 for_each_tcp_port_monitor_in_collection(
805 &age_tcp_port_monitor,
809 /* read tcp data from /proc/net/tcp */
810 if ( ( fp = fopen("/proc/net/tcp", "r" ) ) == NULL )
813 /* ignore field name line */
816 /* read all tcp connections */
817 while (fgets (buf, sizeof (buf), fp) != NULL) {
819 if ( sscanf (buf, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
820 (unsigned long *)&conn.local_addr, (unsigned long *)&conn.local_port,
821 (unsigned long *)&conn.remote_addr, (unsigned long *)&conn.remote_port,
822 (unsigned long *)&state, (unsigned long *)&uid, (unsigned long *)&inode) != 7 )
824 fprintf( stderr, "/proc/net/tcp: bad file format\n" );
826 if ((inode == 0) || (state != TCP_ESTABLISHED)) continue;
828 /* show the connection to each port monitor. */
829 for_each_tcp_port_monitor_in_collection(
831 &show_connection_to_tcp_port_monitor,
838 /* check the health of the monitor hashes and rebuild them if nedded */
839 for_each_tcp_port_monitor_in_collection(
841 &maintain_tcp_port_monitor_hash,
845 /* rebuild the connection peek tables of all monitors so clients can peek in O(1) time */
846 for_each_tcp_port_monitor_in_collection(
848 &rebuild_tcp_port_monitor_peek_table,
853 /* After clients create a monitor, use this to add it to the collection.
854 Returns 0 on success, -1 otherwise. */
855 int insert_tcp_port_monitor_into_collection(
856 tcp_port_monitor_collection_t * p_collection,
857 tcp_port_monitor_t * p_monitor
860 tcp_port_monitor_node_t * p_node;
862 if ( !p_collection || !p_monitor )
865 /* create a container node for this monitor */
866 p_node = (tcp_port_monitor_node_t *) calloc( 1, sizeof(tcp_port_monitor_node_t) );
870 /* populate the node */
871 p_node->p_monitor = p_monitor;
872 p_node->p_next = NULL;
874 /* add a pointer to this monitor to the collection's hash */
875 if ( hash_insert( &p_collection->hash, (void *)p_monitor ) != 0 )
877 /* error inserting into hash. destroy the monitor's container node so no leaks */
882 /* tail of the container gets this node */
883 if ( !p_collection->monitor_list.p_tail )
884 p_collection->monitor_list.p_tail = p_node;
887 /* p_next of the tail better be NULL */
888 if ( p_collection->monitor_list.p_tail->p_next != NULL )
891 /* splice node onto tail */
892 p_collection->monitor_list.p_tail->p_next = p_node;
893 p_collection->monitor_list.p_tail = p_node;
896 /* if this was the first element added */
897 if ( !p_collection->monitor_list.p_head )
898 p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail;
903 /* Clients need a way to find monitors */
904 tcp_port_monitor_t * find_tcp_port_monitor(
905 const tcp_port_monitor_collection_t * p_collection,
906 in_port_t port_range_begin,
907 in_port_t port_range_end
910 tcp_port_monitor_t monitor,*p_monitor;
916 /* need a monitor object to use for searching the hash */
917 monitor.port_range_begin = port_range_begin;
918 monitor.port_range_end = port_range_end;
919 p_monitor = &monitor;
920 p_cast = (void *)p_monitor;
922 /* simple hash table lookup */
923 if ( hash_lookup( &p_collection->hash, &p_cast ) == 0 )
925 /* found the monitor and p_cast now points to it */
926 p_monitor = (tcp_port_monitor_t *)p_cast;
930 return NULL; /* monitor not found */