1 /* -------------------------------------------------------------------------
2 * libtcp-portmon.c: tcp port monitoring library.
4 * Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * --------------------------------------------------------------------------- */
21 #include "libtcp-portmon.h"
23 /* -------------------------------------------------------------------
24 * IMPLEMENTATION INTERFACE
26 * Implementation-specific interface begins here. Clients should not
27 * manipulate these structures directly, nor call the defined helper
28 * functions. Use the "Client interface" functions defined at bottom.
29 * ------------------------------------------------------------------- */
31 /* -----------------------------------------------------------------------------
32 * Open-addressed hash implementation requires that we supply two hash functions
33 * and a match function to compare two hash elements for identity.
34 * ----------------------------------------------------------------------------- */
36 /* --------------------------------------------------
37 * Functions to hash the connections within a monitor
38 * --------------------------------------------------*/
40 #define CONNECTION_HASH_KEY_LEN 17
42 /* ----------------------------------------------------------------------------------
43 * First connection hash function: DJB with a 65521 prime modulus to govern the range.
44 * ----------------------------------------------------------------------------------*/
45 int connection_hash_function_1( const void *p_data )
47 tcp_connection_t *p_conn;
48 char key[CONNECTION_HASH_KEY_LEN];
49 unsigned int hash = 5381;
55 memset(key,0,sizeof(key));
57 /* p_data is a pointer to tcp_connection_t */
58 p_conn = (tcp_connection_t *)p_data;
60 /* key is a hex representation of the connection */
61 snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X",
62 p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
64 fprintf(stderr,"--- key=[%s]\n",key);
67 for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
69 hash = ((hash << 5) + hash) + (key[i]);
72 return (hash & 0x7FFFFFFF) % 65521;
75 /* -------------------------------------------------------------------------
76 * Second connection hash function: DEK, modified to return odd numbers only,
77 * as required for open-address hashing using double-hash probing.
78 * Also uses a 65521 prime modulus to govern the range.
79 * -------------------------------------------------------------------------*/
80 int connection_hash_function_2( const void *p_data )
82 tcp_connection_t *p_conn;
83 char key[CONNECTION_HASH_KEY_LEN];
84 unsigned int hash = CONNECTION_HASH_KEY_LEN-1;
90 memset(key,0,sizeof(key));
92 /* p_data is a pointer to a tcp_connection_t */
93 p_conn = (tcp_connection_t *)p_data;
95 /* key is a hex representation of the connection */
96 snprintf(key, CONNECTION_HASH_KEY_LEN, "%08X%04X%04X",
97 p_conn->remote_addr, p_conn->remote_port, p_conn->local_port);
99 for(i = 0; i < CONNECTION_HASH_KEY_LEN-1; i++)
101 hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
103 return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
106 /* -------------------------------------------------------------------------
107 * Connection Match function returns non-zero if hash elements are identical.
108 * -------------------------------------------------------------------------*/
109 int connection_match_function( const void *p_data1, const void *p_data2 )
111 tcp_connection_t *p_conn1, *p_conn2;
113 if ( !p_data1 || !p_data2 )
116 /* p_data1, p_data2 are pointers to tcp_connection_t */
117 p_conn1 = (tcp_connection_t *)p_data1;
118 p_conn2 = (tcp_connection_t *)p_data2;
120 return (p_conn1->local_addr == p_conn2->local_addr &&
121 p_conn1->local_port == p_conn2->local_port &&
122 p_conn1->remote_addr == p_conn2->remote_addr &&
123 p_conn1->remote_port == p_conn2->remote_port );
126 /* --------------------------------------------------
127 * Functions to hash the monitors within a collection
128 * --------------------------------------------------*/
130 #define MONITOR_HASH_KEY_LEN 9
132 /* -------------------------------------------------------------------------------
133 * First monitor hash function: DJB with a 65521 prime modulus to govern the range.
134 * -------------------------------------------------------------------------------*/
135 int monitor_hash_function_1( const void *p_data )
137 tcp_port_monitor_t *p_monitor;
138 char key[MONITOR_HASH_KEY_LEN];
139 unsigned int hash = 5381;
145 memset(key,0,sizeof(key));
147 /* p_data is a pointer to tcp_port_monitor_t */
148 p_monitor = (tcp_port_monitor_t *)p_data;
150 /* key is a hex representation of the starting port concatenated to the ending port */
151 snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
152 p_monitor->port_range_begin, p_monitor->port_range_end );
154 fprintf(stderr,"--- key=[%s]\n",key);
157 for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
159 hash = ((hash << 5) + hash) + (key[i]);
162 return (hash & 0x7FFFFFFF) % 65521;
165 /* -----------------------------------------------------------------------
166 * Second monitor hash function: DEK, modified to return odd numbers only,
167 * as required for open-address hashing using double-hash probing.
168 * Also uses a 65521 prime modulus to govern the range.
169 * -----------------------------------------------------------------------*/
170 int monitor_hash_function_2( const void *p_data )
172 tcp_port_monitor_t *p_monitor;
173 char key[MONITOR_HASH_KEY_LEN];
174 unsigned int hash = MONITOR_HASH_KEY_LEN-1;
180 memset(key,0,sizeof(key));
182 /* p_data is a pointer to a tcp_port_monitor_t */
183 p_monitor = (tcp_port_monitor_t *)p_data;
185 /* key is a hex representation of the starting port concatenated to the ending port */
186 snprintf(key, MONITOR_HASH_KEY_LEN, "%04X%04X",
187 p_monitor->port_range_begin, p_monitor->port_range_end );
189 for(i = 0; i < MONITOR_HASH_KEY_LEN-1; i++)
191 hash = ((hash << 5) ^ (hash >> 27)) ^ (key[i]);
193 return (( hash & 0x7FFFFFFF ) % 65521 ) | 0x00000001;
196 /* ----------------------------------------------------------------------
197 * Monitor match function returns non-zero if hash elements are identical.
198 * ----------------------------------------------------------------------*/
199 int monitor_match_function( const void *p_data1, const void *p_data2 )
201 tcp_port_monitor_t *p_monitor1, *p_monitor2;
203 if ( !p_data1 || !p_data2 )
206 /* p_data1, p_data2 are pointers to tcp_connection_t */
207 p_monitor1 = (tcp_port_monitor_t *)p_data1;
208 p_monitor2 = (tcp_port_monitor_t *)p_data2;
210 return (p_monitor1->port_range_begin == p_monitor1->port_range_begin &&
211 p_monitor2->port_range_end == p_monitor2->port_range_end);
214 /* ---------------------------------------------------------------------------
215 * Port monitor utility functions implementing tcp_port_monitor_function_ptr_t
216 * ---------------------------------------------------------------------------*/
217 void destroy_tcp_port_monitor(
218 tcp_port_monitor_t * p_monitor,
222 tcp_connection_node_t *p_node, *p_temp;
224 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
227 /* destroy the monitor's hash */
228 hash_destroy(&p_monitor->hash);
230 /* destroy the monitor's peek array */
231 free( p_monitor->p_peek );
233 /* destroy the monitor's connection list */
234 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; )
236 /* p_temp is for the next iteration */
237 p_temp = p_node->p_next;
244 /* destroy the monitor */
249 void age_tcp_port_monitor(
250 tcp_port_monitor_t * p_monitor,
254 /* Run through the monitor's connections and decrement the age variable.
255 * If the age goes negative, we remove the connection from the monitor.
256 * Function takes O(n) time on the number of connections. */
258 tcp_connection_node_t *p_node, *p_temp;
259 tcp_connection_t *p_conn;
262 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
265 if ( !p_monitor->p_peek )
268 for ( p_node = p_monitor->connection_list.p_head; p_node != NULL; )
270 if ( --p_node->connection.age >= 0 ) {
271 p_node = p_node->p_next;
275 /* connection on p_node is old. remove connection from the hash. */
276 p_conn = &p_node->connection;
277 p_cast = (void *)p_conn;
278 if ( hash_remove( &p_monitor->hash, &p_cast ) != 0 ) {
280 fprintf(stderr, "--- hash_remove error\n");
285 /* splice p_node out of the connection_list */
286 if ( p_node->p_prev != NULL )
287 p_node->p_prev->p_next = p_node->p_next;
288 if ( p_node->p_next != NULL )
289 p_node->p_next->p_prev = p_node->p_prev;
291 /* correct the list head and tail if necessary */
292 if ( p_monitor->connection_list.p_head == p_node )
293 p_monitor->connection_list.p_head = p_node->p_next;
294 if ( p_monitor->connection_list.p_tail == p_node )
295 p_monitor->connection_list.p_tail = p_node->p_prev;
297 /* p_temp is for the next iteration */
298 p_temp = p_node->p_next;
300 /* destroy the node */
307 void maintain_tcp_port_monitor_hash(
308 tcp_port_monitor_t * p_monitor,
312 /* Check the number of vacated slots in the hash. If it exceeds our maximum
313 * threshold (should be about 1/4 of the hash table), then the hash table
314 * performance degrades from O(1) toward O(n) as the number of vacated slots
315 * climbs. This is avoided by clearing the hash and reinserting the entries.
316 * The benefit of open-addressing hashing does come with this price --
317 * you must rebalance it occasionally. */
319 tcp_connection_node_t *p_node;
322 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
325 vacated_load = (double)p_monitor->hash.vacated / (double)p_monitor->hash.positions;
327 fprintf(stderr,"--- num vacated is %d, vacated factor is %.3f\n", p_monitor->hash.vacated, vacated_load );
329 if ( vacated_load <= TCP_CONNECIION_HASH_MAX_VACATED_PCT )
331 /* hash is fine and needs no rebalancing */
336 fprintf(stderr,"--- rebuilding hash\n");
339 /* rebuild the hash */
340 memset( p_monitor->hash.pp_table, 0, p_monitor->hash.positions * sizeof(void **));
341 p_monitor->hash.size = 0;
342 p_monitor->hash.vacated = 0;
344 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next )
346 if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
349 fprintf(stderr,"--- hash_insert error\n");
356 void rebuild_tcp_port_monitor_peek_table(
357 tcp_port_monitor_t * p_monitor,
361 /* Run through the monitori's connections and rebuild the peek table
362 * of connection pointers. This is done so peeking into the monitor
363 * can be done in O(1) time instead of O(n) time for each peek. */
365 tcp_connection_node_t *p_node;
368 if ( !p_monitor || p_void ) /* p_void should be NULL in this context */
371 /* zero out the peek array */
372 memset( p_monitor->p_peek, 0, TCP_CONNECTION_HASH_SIZE * sizeof(tcp_connection_t *) );
374 for ( p_node=p_monitor->connection_list.p_head; p_node!=NULL; p_node=p_node->p_next, i++ )
376 p_monitor->p_peek[i] = &p_node->connection;
380 void show_connection_to_tcp_port_monitor(
381 tcp_port_monitor_t * p_monitor,
385 /* The monitor gets to look at each connection to see if it falls within
386 * the monitor's port range of interest. Connections of interest are first
387 * looked up in the hash to see if they are already there. If they are, we
388 * reset the age of the connection so it is not deleted. If the connection
389 * is not in the hash, we add it, but only if the hash is not saturated.
390 * The function takes O(1) time. */
392 tcp_connection_node_t *p_node;
395 if ( !p_monitor || !p_void )
398 tcp_connection_t *p_connection = (tcp_connection_t *)p_void;
400 /* inspect the local port number of the connection to see if we're interested. */
401 if ( (p_monitor->port_range_begin <= p_connection->local_port) &&
402 (p_connection->local_port <= p_monitor->port_range_end) )
404 /* the connection is in the range of the monitor. */
406 /* first check the hash to see if the connection is already there. */
407 p_cast = (void *)p_connection;
408 if ( hash_lookup( &p_monitor->hash, &p_cast ) == 0 )
410 p_connection = (tcp_connection_t *)p_cast;
411 /* it's already in the hash. reset the age of the connection. */
412 if ( p_connection != NULL )
414 p_connection->age = TCP_CONNECIION_STARTING_AGE;
420 /* Connection is not yet in the hash. We will try to add it, but only if the hash is not
421 * yet saturated. We assume the hash is saturated (and therefore ignore this connection)
422 * if our load factor cap is now exceeded. The benefit of limiting connections in this way
423 * is that the hash will continue to function at an average (1) speed by keeping the load
424 * load factor down. Of course the downside is that each port monitor has a strict maximum
425 * connection limit. Future versions should probably allow the client to set the hash size
426 * and load limits and/or provide for automatic resizing of hashes. */
428 if ( p_monitor->hash.size / p_monitor->hash.positions > TCP_CONNECTION_HASH_MAX_LOAD_PCT )
430 /* hash exceeds our load limit is now "full" */
434 /* create a new connection node */
435 if ( (p_node = (tcp_connection_node_t *) calloc(1, sizeof(tcp_connection_node_t))) == NULL )
438 p_node->connection = *p_connection; /* bitwise copy of the struct */
439 p_node->connection.age = TCP_CONNECIION_STARTING_AGE;
440 p_node->p_next = NULL;
442 /* insert it into the monitor's hash table */
443 if ( hash_insert( &p_monitor->hash, (void *)&p_node->connection ) != 0 )
445 /* error inserting into hash. delete the connection node we just created, so no leaks. */
447 fprintf(stderr, "--- hash_insert error\n");
453 /* append the node to the monitor's connection list */
454 if ( p_monitor->connection_list.p_tail == NULL ) /* assume p_head is NULL too */
456 p_monitor->connection_list.p_head = p_node;
457 p_monitor->connection_list.p_tail = p_node;
458 p_node->p_prev = NULL;
462 p_monitor->connection_list.p_tail->p_next = p_node;
463 p_node->p_prev = p_monitor->connection_list.p_tail;
464 p_monitor->connection_list.p_tail = p_node;
469 /* ---------------------------------------------------------------------------------------
470 * Apply a tcp_port_monitor_function_ptr_t function to each port monitor in the collection.
471 * ---------------------------------------------------------------------------------------*/
472 void for_each_tcp_port_monitor_in_collection(
473 tcp_port_monitor_collection_t * p_collection,
474 tcp_port_monitor_function_ptr_t p_function,
475 void * p_function_args
478 tcp_port_monitor_node_t * p_current_node, * p_next_node;
480 if ( !p_collection || !p_function )
483 /* for each monitor in the collection */
484 for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
486 p_next_node = p_current_node->p_next; /* do this first! */
488 if ( p_current_node->p_monitor )
490 /* apply the function with the given arguments */
491 (*p_function)( p_current_node->p_monitor, p_function_args );
494 p_current_node = p_next_node;
500 /* ----------------------------------------------------------------------
503 * Clients should call only those functions below this line.
504 * ---------------------------------------------------------------------- */
506 /* ----------------------------------
507 * Client operations on port monitors
508 * ---------------------------------- */
510 /* Clients should first try to "find_tcp_port_monitor" before creating one
511 so that there are no redundant monitors. */
512 tcp_port_monitor_t * create_tcp_port_monitor(
513 in_port_t port_range_begin,
514 in_port_t port_range_end
517 tcp_port_monitor_t * p_monitor;
519 /* create the monitor */
520 p_monitor = (tcp_port_monitor_t *) calloc(1, sizeof(tcp_port_monitor_t) );
524 /* create the monitor's connection hash */
525 if ( hash_create( &p_monitor->hash, TCP_CONNECTION_HASH_SIZE,
526 &connection_hash_function_1, &connection_hash_function_2,
527 &connection_match_function, NULL ) != 0 )
529 /* we failed to create the hash, so destroy the monitor completely so we don't leak */
530 destroy_tcp_port_monitor(p_monitor,NULL);
534 /* create the monitor's peek array */
535 if ( (p_monitor->p_peek = (tcp_connection_t **) calloc( TCP_CONNECTION_HASH_SIZE, sizeof(tcp_connection_t *))) == NULL )
537 /* we failed to create the peek array, so destroy the monitor completely, again, so we don't leak */
538 destroy_tcp_port_monitor(p_monitor,NULL);
542 p_monitor->port_range_begin = port_range_begin;
543 p_monitor->port_range_end = port_range_end;
545 p_monitor->connection_list.p_head = NULL;
546 p_monitor->connection_list.p_tail = NULL;
551 /* Clients use this function to get connection data from the indicated port monitor.
552 The requested monitor value is copied into a client-supplied char buffer.
553 Returns 0 on success, -1 otherwise. */
554 int peek_tcp_port_monitor(
555 tcp_port_monitor_t * p_monitor,
557 int connection_index,
562 struct hostent *p_hostent;
563 struct servent *p_servent;
566 if ( !p_monitor || !p_buffer || connection_index < 0 )
569 memset(p_buffer, 0, buffer_size);
570 memset(&net, 0, sizeof(net));
572 /* if the connection index is out of range, we simply return with no error
573 * having first cleared the client-supplied buffer. */
574 if ( (item!=COUNT) && (connection_index > p_monitor->hash.size - 1) )
581 snprintf( p_buffer, buffer_size, "%d" , p_monitor->hash.size );
586 net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
587 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
592 p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->remote_addr, sizeof(in_addr_t), AF_INET);
593 /* if no host name found, just use ip address. */
594 if ( !p_hostent || !p_hostent->h_name )
596 net.s_addr = p_monitor->p_peek[ connection_index ]->remote_addr;
597 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
600 snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
605 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->remote_port );
610 net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
611 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
616 p_hostent = gethostbyaddr( &p_monitor->p_peek[ connection_index ]->local_addr, sizeof(in_addr_t), AF_INET);
617 /* if no host name found, just use ip address. */
618 if ( !p_hostent || !p_hostent->h_name )
620 net.s_addr = p_monitor->p_peek[ connection_index ]->local_addr;
621 snprintf( p_buffer, buffer_size, "%s", inet_ntoa( net ) );
624 snprintf( p_buffer, buffer_size, "%s", p_hostent->h_name );
629 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
634 p_servent = getservbyport( htons(p_monitor->p_peek[ connection_index ]->local_port ), "tcp" );
635 /* if no service name found for the port, just use the port number. */
636 if ( !p_servent || !p_servent->s_name )
638 snprintf( p_buffer, buffer_size, "%d", p_monitor->p_peek[ connection_index ]->local_port );
641 snprintf( p_buffer, buffer_size, "%s", p_servent->s_name );
651 /* --------------------------------
652 * Client operations on collections
653 * -------------------------------- */
655 /* Create a monitor collection. Do this one first. */
656 tcp_port_monitor_collection_t * create_tcp_port_monitor_collection( void )
658 tcp_port_monitor_collection_t * p_collection;
660 p_collection = (tcp_port_monitor_collection_t *) calloc( 1, sizeof( tcp_port_monitor_collection_t ) );
664 /* create the collection's monitor hash */
665 if ( hash_create( &p_collection->hash, TCP_MONITOR_HASH_SIZE,
666 &monitor_hash_function_1, &monitor_hash_function_2,
667 &monitor_match_function, NULL ) != 0 )
669 /* we failed to create the hash, so destroy the monitor completely so we don't leak */
670 destroy_tcp_port_monitor_collection(p_collection);
674 p_collection->monitor_list.p_head = NULL;
675 p_collection->monitor_list.p_tail = NULL;
680 /* Destroy the monitor collection (and the monitors inside). Do this one last. */
681 void destroy_tcp_port_monitor_collection(
682 tcp_port_monitor_collection_t * p_collection
685 tcp_port_monitor_node_t * p_current_node, * p_next_node;
690 /* destroy the collection's hash */
691 hash_destroy( &p_collection->hash );
693 /* destroy the monitors */
694 for_each_tcp_port_monitor_in_collection(
696 &destroy_tcp_port_monitor,
700 /* next destroy the empty monitor nodes */
701 for ( p_current_node = p_collection->monitor_list.p_head; p_current_node != NULL; )
703 p_next_node = p_current_node->p_next; /* do this first! */
705 free( p_current_node );
706 p_current_node = p_next_node;
709 free( p_collection );
713 /* Updates the tcp statistics for all monitors within a collection */
714 void update_tcp_port_monitor_collection(
715 tcp_port_monitor_collection_t * p_collection
720 tcp_connection_t conn;
726 /* age the connections in all port monitors. */
727 for_each_tcp_port_monitor_in_collection(
729 &age_tcp_port_monitor,
733 /* read tcp data from /proc/net/tcp */
734 if ( ( fp = fopen("/proc/net/tcp", "r" ) ) == NULL )
737 /* ignore field name line */
740 /* read all tcp connections */
741 while (fgets (buf, sizeof (buf), fp) != NULL) {
743 if ( sscanf (buf, "%*d: %lx:%lx %lx:%lx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
744 (unsigned long *)&conn.local_addr, (unsigned long *)&conn.local_port,
745 (unsigned long *)&conn.remote_addr, (unsigned long *)&conn.remote_port,
746 (unsigned long *)&state, (unsigned long *)&conn.uid, (unsigned long *)&conn.inode) != 7 )
748 fprintf( stderr, "/proc/net/tcp: bad file format\n" );
750 if ((conn.inode == 0) || (state != TCP_ESTABLISHED)) continue;
752 /* show the connection to each port monitor. */
753 for_each_tcp_port_monitor_in_collection(
755 &show_connection_to_tcp_port_monitor,
762 /* check the health of the monitor hashes and rebuild them if nedded */
763 for_each_tcp_port_monitor_in_collection(
765 &maintain_tcp_port_monitor_hash,
769 /* rebuild the connection peek tables of all monitors so clients can peek in O(1) time */
770 for_each_tcp_port_monitor_in_collection(
772 &rebuild_tcp_port_monitor_peek_table,
777 /* After clients create a monitor, use this to add it to the collection.
778 Returns 0 on success, -1 otherwise. */
779 int insert_tcp_port_monitor_into_collection(
780 tcp_port_monitor_collection_t * p_collection,
781 tcp_port_monitor_t * p_monitor
784 tcp_port_monitor_node_t * p_node;
786 if ( !p_collection || !p_monitor )
789 /* create a container node for this monitor */
790 p_node = (tcp_port_monitor_node_t *) calloc( 1, sizeof(tcp_port_monitor_node_t) );
794 /* populate the node */
795 p_node->p_monitor = p_monitor;
796 p_node->p_next = NULL;
798 /* add a pointer to this monitor to the collection's hash */
799 if ( hash_insert( &p_collection->hash, (void *)p_monitor ) != 0 )
801 /* error inserting into hash. destroy the monitor's container node so no leaks */
806 /* tail of the container gets this node */
807 if ( !p_collection->monitor_list.p_tail )
808 p_collection->monitor_list.p_tail = p_node;
811 /* p_next of the tail better be NULL */
812 if ( p_collection->monitor_list.p_tail->p_next != NULL )
815 /* splice node onto tail */
816 p_collection->monitor_list.p_tail->p_next = p_node;
817 p_collection->monitor_list.p_tail = p_node;
820 /* if this was the first element added */
821 if ( !p_collection->monitor_list.p_head )
822 p_collection->monitor_list.p_head = p_collection->monitor_list.p_tail;
827 /* Clients need a way to find monitors */
828 tcp_port_monitor_t * find_tcp_port_monitor(
829 tcp_port_monitor_collection_t * p_collection,
830 in_port_t port_range_begin,
831 in_port_t port_range_end
834 tcp_port_monitor_t monitor,*p_monitor;
840 /* need a monitor object to use for searching the hash */
841 monitor.port_range_begin = port_range_begin;
842 monitor.port_range_end = port_range_end;
843 p_monitor = &monitor;
844 p_cast = (void *)p_monitor;
846 /* simple hash table lookup */
847 if ( hash_lookup( &p_collection->hash, &p_cast ) == 0 )
849 /* found the monitor and p_cast now points to it */
850 p_monitor = (tcp_port_monitor_t *)p_cast;
854 return NULL; /* monitor not found */