/* 
 * $Id: idrp_rt_util.c,v 1.6 1995/08/01 20:26:42 sjr Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

#include "include.h"
#include "iso.h"
#include "idrp.h"
#ifdef	BSD_IDRP	
#include "/sys/sys/malloc.h"
#else
#include <malloc.h>
#endif	/*BSD_IDRP */

/* Routing table code for IDRP */

/* This idrp routing table code attempts to look like the bgp-4 */
/* code in gated.  Since we are doing simular functions, this  */

/* imply better maintenance or simply common confusion.  */
/* idrp specific mainteance routines: */

/* hash table routines
 *  idrp_rid_hash(peer,id) - Sue
 *  - routine to hash the route id and
 *    return an idrpRoute entry
 *
 *  idrp_add_hash_entry(peer,id,p_rt) - Sue
 *  - routine to add new entry to hash table
 *  - give route_id and idrpRoute structure
 *
 *  idrp_free_hash_entry(peer,id) - Sue
 *   - routine to release hash entry from
 *     hash table given id, and relink
 *     the hash table list 
 *
 * idrp_release_hash_tbl(peer) - sue
 *  -  routine to release all of hash table
 *     entries and return 
 *
 * idrp_init_hash_tbl(peer) - sue 
 * - routine to initialize hash table
 *
 *  %% idrpRoute structure handling routines
 * 
 * 1) idrp_alloc_idrpRoute(peer,attr_rec,family)  (Sue's/joint) 
 *     idrpPeer *peer - peer structure pointer which
 *                      route is associated with 
 *     attr_rec - attribute record for route
 *     family - AF.ISO, AF.INET
 *     return pointer to idrpRoute structure 
 *
 * 2) idrp_free_idrpRoute(p_rt)
 *    p_rt - idrp route structure
 * 
 *
 * 3) idrp_free_idrpRoute_entry(id,route_entry_list,circular)
 *     id - id of route
 *     idrpRoute_list
 *     circular - whether list is circular or not
 * 	
 *	- searches list for route_entry_list 
 * 
 * 4) idrp_add_idrpRoute_entry(id,route_entry_list) - 
 *     id - id of route
 *     idrpRoute_list
 *     circular - circular list or not
 *		- creates a new route_id entry and links to list
 * 
 *  
 * 5) idrp_free_outlist  - free an idrpRoute from outlist and relink
 *			outlist around the idrp Route
 * 6) idrp_copy_routelist(res,rt_chg)
 *	- copy the route list for a duplicate attribute 
 */


idrp_rid_hash *
idrp_rid_hash_get(peer,id)
idrpPeer *peer;
u_int id;
{
idrp_rid_hash *p_rid;

	trace_tf(idrp_trace_options, TR_NORMAL,0,("tracing hash table additions and subtractions id %d peer %s",id,peer->name));

	p_rid = &(peer->hash_table[IDRP_ROUTE_ID_HASH(id)]); 

	/* walk the hash table list */
	while (p_rid)
		{
		if (p_rid->route_id == id)
			{
			/* Not sure if we need this test -- depends on whether or not the
			 * peer's hash table is cleared when its session goes down and back
			 * up again.  If it's not cleared, we need this test.  Watch for the
			 * trace messages and if they don't appear (or if we're sure that this
			 * won't happen) then delete this test.
			 */

			IDRP_STATUS_BIT_TEST(p_rid, IDRP_RID_ROUTE_GONE)
				{	
			        trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp_rid_hash() -- route id (%d) matched p_rid = %x, but IDRP_RID_ROUTE_GONE (%x,%x,%x,%x) was set",
					 id,p_rid,p_rid->p_idrp_rt[0],p_rid->p_idrp_rt[1],p_rid->p_idrp_rt[2],p_rid->p_idrp_rt[3])); 
				return((idrp_rid_hash *) 0);
				}
			else
				{
				return (p_rid);
				}
			}
		p_rid = p_rid->next;
		}

	return ((idrp_rid_hash *) 0);
} 
			

/* idrp_add_hash_entry
 * - add entry to peer hash table for route_id_in
 */

idrp_rid_hash *
idrp_add_hash_entry(peer,id,p_route)
idrpPeer *peer;
u_int id;
idrpRoute *p_route[];
{
idrp_rid_hash	*p_rid = 0;
idrp_rid_hash	*p_rid_last = 0;
int		i;


	trace_tf(idrp_trace_options, TR_NORMAL,0,(" Add hash table entry for peer %s id %d ",peer->name,id));
	p_rid = &(peer->hash_table[IDRP_ROUTE_ID_HASH(id)]);

	/* if not the initial entry - then
	 * allocate a entry for this hash entry
	 */

	IDRP_STATUS_BIT_TEST(p_rid,IDRP_RID_EMPTY) 
		{
		/* the empty bit is set - so re-use the
		 * the attribute
		 */
		
		trace_tf(idrp_trace_options, TR_NORMAL,0,("adding rid %d and main entry empty status %x",id,p_rid->status));
		IDRP_STATUS_BIT_CLEAR(p_rid, IDRP_RID_EMPTY);

		/* assert that we should have a nulled out 
		 * status field or we are in big trouble
		 */
		if (p_rid->status == IDRP_RID_ROUTE_GONE)
			{
			int i;
			NLRI_FAMILY_LOOP(i)
				{
				if (p_rid->p_idrp_rt[i] != (idrpRoute *) NULL)
					{
					/* halt */

					assert(p_rid->status == 0);
					}
				}
			trace_log_tf(idrp_trace_options, 0, LOG_ERR,("peer %s - (%d) hash id %d has IDRP_RID_ROUTE_GONE (%x) ",
					peer->name,p_rid->route_id,id,p_rid)); 
			p_rid->status = IDRP_RID_NORMAL;  	
			}	
		}
	else
		{	
		trace_tf(idrp_trace_options, TR_NORMAL,0,("adding rid %d and main entry full",id));

		/* try to link to the head of the list
		 * minus the first one so the topo
		 * stuff floats to the first things
		 * checked
		 */

		p_rid_last = p_rid;
		p_rid = (idrp_rid_hash *) idrp_local_mem_fit(sizeof(idrp_rid_hash));
		bzero(p_rid,sizeof(idrp_rid_hash));	
		p_rid->next  = p_rid_last->next;
		p_rid_last->next = p_rid;
		}

	p_rid->route_id = id;

	NLRI_FAMILY_LOOP(i)
		{
		p_rid->p_idrp_rt[i] = p_route[i]; 
		} NLRI_FAMILY_LOOP_END
	return(p_rid);
}

void 
idrp_free_hash_entry(peer,id)
idrpPeer *peer;
u_int id;
{
idrp_rid_hash *p_rid = 0;
idrp_rid_hash *p_rid_last = 0;
idrp_rid_hash *p_rid_succ;

	trace_tf(idrp_trace_options, TR_NORMAL,0,("Freeing the hash table entry for id %x peer %s",id,peer->name));
	 
	p_rid = &(peer->hash_table[IDRP_ROUTE_ID_HASH(id)]);

	/* walk the hash table list looking for id */

	while (p_rid)
		{
		if (p_rid->route_id == id) 
			{
			if (p_rid_last)
				{
				/* not first on list 
				 * link over this id
				 */ 
				trace_tf(idrp_trace_options, TR_NORMAL,0,(" entry for id %d peer %s not first on list freed",id,peer->name)); 
	
				p_rid_last->next = p_rid->next;
				IDRP_MEM_FIT_FREE(p_rid);
				}	
			else
				{
				/* first on list:  If there's a successor, copy it into 1st entry and delete
				 * the (old) successor entry.  Otherwise (singleton entry) zero the entry and
				 * reset its status to EMPTY.
				 */

				/* We can just use p_rid here to indicate the first entry in the chain -- the
				 * conditions to get here require it.  Do an assert() to make sure...
				 */

				assert( &(peer->hash_table[IDRP_ROUTE_ID_HASH(id)]) == p_rid );

				if (p_rid->next)
					{
					trace_tf(idrp_trace_options, TR_NORMAL,0,(" entry for id %d peer %s has successor",id,peer->name,p_rid->route_id)); 

					/* we need to hold onto the address of the successor, so that we can free() it.
					 */

					p_rid_succ = p_rid->next; 

					/* move the successor into the first position 
					 */

					bcopy((caddr_t) p_rid_succ, (caddr_t) p_rid ,sizeof(idrp_rid_hash));

					/* now free the successor
					 */
					IDRP_MEM_FIT_FREE(p_rid_succ);
					}
				else 
					{

					/* Singleton entry, so reset status so that we can re-use the entry later.
					 */

					trace_tf(idrp_trace_options, TR_NORMAL,0,(" singleton entry for id %d peer %s",id,peer->name)); 

					bzero((caddr_t) p_rid ,sizeof(idrp_rid_hash));
					p_rid->status = IDRP_RID_EMPTY;
					}
				}
			return;
			}
		p_rid_last = p_rid;
		p_rid = p_rid->next;
		}
	trace_tf(idrp_trace_options, TR_NORMAL,0,("hash entry route id %d for peer %s tried to be freed, but not there ", id,peer->name));

	return;	
}
			
 
void 
idrp_release_hash_tbl(peer)
idrpPeer *peer;
{
idrp_rid_hash	*p_rid;
idrp_rid_hash	*p_rid_last;
int		i,j;


	trace_tf(idrp_trace_options, TR_NORMAL,0,("Free whole hash table peer %s",peer->name));

	for (j = 0; j < IDRP_HASH_TABLE_SIZE; j++)
		{
		if ((peer->hash_table[j].status & IDRP_RID_EMPTY) == 0)
			{	
			/* if the hash entry is non-empty... */

			/* ...first  walk hash chain freeing up the task table...
			 */

			p_rid = peer->hash_table[j].next;
			while (p_rid)
				{
				idrpRoute *p_irt;
				/* clean out any idrp_routes still  associated with this 
				 * chain
				 */

				NLRI_FAMILY_LOOP(i)
					{
					for (p_irt = p_rid->p_idrp_rt[i]; p_irt; p_irt = p_irt->p_next_nlri)
						{
						/* zero the route id recieved
						 * - set the RID bad bit 
						 */

						p_irt->route_id_in = 0;
						IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_RID_BAD);
						}	
					} NLRI_FAMILY_LOOP_END;	
				p_rid_last = p_rid;
				p_rid = p_rid->next;
				IDRP_MEM_FIT_FREE(p_rid_last);
				}

			/* ...then zero and reset the head 
			 */
			}
		bzero((caddr_t) &(peer->hash_table[j]) ,sizeof(idrp_rid_hash));
		peer->hash_table[j].status = IDRP_RID_EMPTY;
		}

	return;
}

void 
idrp_reinit_hash_tbl(peer)
idrpPeer	*peer;
{
	idrp_release_hash_tbl(peer);
}		

void 
idrp_init_hash_tbl(peer)
idrpPeer	*peer;
{
int	i;

	trace_tf(idrp_trace_options, TR_NORMAL,0,("init hash table peer %s",peer->name));

	for (i = 0; i < IDRP_HASH_TABLE_SIZE; i++)
		{
		peer->hash_table[i].status = IDRP_RID_EMPTY;
		}
}
		
			
/* %% idrpRoute handling routines */

		
/*
 * idrp_alloc_idrpRoute(peer,attr_rec,family)  (Sue's/joint) 
 *     idrpPeer *peer - peer structure pointer which
 *                      route is associated with 
 *     attr_rec - attribute record for route
 *     family - AF.ISO, AF.INET
 *     return pointer to idrpRoute structure 
 */
idrpRoute *
idrp_alloc_idrpRoute(peer,attr_rec,family,route_id_in)
idrpPeer *peer;
idrp_attribute_record *attr_rec;
int family;
int route_id_in;
{
idrpRoute	*p_route;	/* idrp route pointer */

/* allocate the buffer for the idrp route information */
/* this buffer goes in the protocol specific portion  */
/* of the gated route_entry   */
 

        p_route = (idrpRoute *) idrp_local_mem_fit(sizeof(idrpRoute));
	if (p_route == ((idrpRoute *) attr_rec))
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0,("gated giving us same memory twice - %%%% retry "));
       		 p_route = (idrpRoute *) idrp_local_mem_fit(sizeof(idrpRoute));
		}
	bzero(p_route,sizeof(idrpRoute));
        p_route->peer=peer;
        p_route->p_attr = attr_rec;
        p_route->family = family;
	p_route->route_id_in = route_id_in;
        return(p_route);
}


void 
idrp_free_idrpRoute(p_rt)
idrpRoute *p_rt;
{
	/* assumes the idrpRoute is unlinked from all structures */

	IDRP_MEM_FIT_FREE(p_rt);

}
/* %% idrpRoute_entry handling routines */


/* 
 *  idrp_free_idrpRoute_entry(id,route_entry_list,circular)
 *     id - id of route
 *     idrpRoute_list
 * 	
 *	- searches list for route_entry_list 
 * 	and frees it
 */

void
idrp_free_idrpRoute_entry(p_ent,p_att)
idrpRoute_entry *p_ent;		/* entry to delete */
idrp_attribute_record	*p_att; /* idrp attribute record */
{
idrpRoute_entry	*p_rtl;		/* pointer for walking route list */ 

	assert (p_att->route_id_list);

	/*  check to see if head - will happen most of time */ 

	if (p_ent == p_att->route_id_list)
		{
		/* it is at the head of the list
		 * - reset the head and free the entry
		 */
		
		p_att->route_id_list = p_ent->p_next;
		}
	else
		{
		/* search for the entry and reset
		 * list
		 */

		for (p_rtl = p_att->route_id_list->p_next; p_rtl; p_rtl = p_rtl->p_next)
			{
			if (p_rtl->p_next == p_ent)
				{
				/* found the entry
				 * - skip over it
				 */

				p_rtl->p_next = p_ent->p_next;
				break;
				}
			}
		}

	IDRP_MEM_FIT_FREE(p_ent);

}


/*
 * free_idrpRoute_ent(id,peer,p_rt_list)
 *
 *  free the idrp route entry off a linked list 
 */

idrpRoute_entry *
free_idrpRoute_ent(id,peer,p_rt_list)
int 		id;
idrpPeer	*peer;
idrpRoute_entry	*p_rt_list;
{
idrpRoute_entry	*p_rt;		/* pointer for walking route list */ 
idrpRoute_entry	*p_rt_last;	/* pointer for walking route list */ 

	p_rt = p_rt_list->p_next;
	p_rt_last = p_rt_list; 

	/* non-circular list - try for just one */

	if (p_rt == 0)  
		{
		/* one entry on linked list and not there */

		trace_tf (idrp_trace_options, TR_NORMAL,0,("no match found list %x route id %d",p_rt_list,id));
		return (p_rt_list);
		}

	/* walk through list looking for id */

	while (p_rt)
		{
		if (p_rt->route_id == id && p_rt->peer == peer)
			{
			/* found the node to free */
			/* relink list */			
	
			p_rt_last->p_next = p_rt->p_next;
			IDRP_MEM_FIT_FREE(p_rt);
			return (p_rt_list);	
			}
		p_rt_last = p_rt;
		p_rt = p_rt->p_next;
		}

	/* if get here, no match found */

	trace_tf (idrp_trace_options, TR_NORMAL,0,("no match found list %x route id %d",p_rt_list,id));
	return (p_rt_list);
}

/*
 * circular_free_idrpRoute(id,p_rt_list)
 *
 *  free the idrp route entry off a linked list 
 */

idrpRoute_entry *
circular_free_idrpRoute(id,p_rt_list)
int id;
idrpRoute_entry	*p_rt_list;	/* pointer to route list */ 
{
idrpRoute_entry	*p_rt;		/* pointer for walking route list */ 
idrpRoute_entry	*p_rt_last;	/* pointer for walking route list */ 

	p_rt = p_rt_list->p_next;
	p_rt_last = p_rt_list; 

	/* walk through circular list until we get to begining */

	while (p_rt != p_rt_list)
		{
		/* check to see if ID found */
		if (p_rt->route_id == id)
			{
			p_rt_last->p_next = p_rt->p_next;
			IDRP_MEM_FIT_FREE(p_rt);
			return (p_rt_list);
			}
		p_rt_last = p_rt;
		p_rt = p_rt->p_next;
		}
	
	/* no id found */
	trace_tf (idrp_trace_options, TR_NORMAL,0,("no match found for route_id circular list %x %d",p_rt_list,id));
	return (p_rt_list);
		
}

/* 
 * 6.) idrp_add_idrpRoute_entry(id,route_entry_list) - 
 *     id - id of route
 *     idrpRoute_list
 */

idrpRoute_entry *
idrp_add_idrpRoute_entry(id,p_rtl)
u_int id;
idrpRoute_entry *p_rtl;	/* pointer to route list */
{
idrpRoute_entry *p_rt;

	/* walk to end of the linked list (not circular) */ 

	p_rt = p_rtl;
	while (p_rt->p_next)
		{
		p_rt = p_rt->p_next;
		}
	p_rt->p_next = (idrpRoute_entry *) idrp_local_mem_fit(sizeof(idrpRoute_entry));
	bzero(p_rt->p_next,sizeof(idrpRoute_entry));
	p_rt->p_next->route_id = id;
	return(p_rt->p_next);
}

int
idrp_rt_within_RD(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
	if (p_idrp_rt->route_id_in == IDRP_LOCAL_ROUTE_ID)
		{
		return(TRUE);
		}

	if (p_idrp_rt->route_id_in == IDRP_EXT_INFO_ROUTE_ID)
		{
		if (p_idrp_rt->p_attr->local_mask & IDRP_LOCAL_EXT_INFO)
			{
			return (TRUE);
			}
#ifdef IDRP_ASPATH
		if (p_idrp_rt->p_attr->idrp_local_proto == RTPROTO_BGP)
			{
			return(idrp_bgp_within_rd(p_idrp_rt));
			}
#endif /* IDRP_ASPATH */
		}

	return(FALSE);
}

 /*
 * idrp_free_outlist - relink an idrpRoute's Route_out list
 *		    around a route
 * 
 */

void 
idrp_free_outlist(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
int		i;

	/* when the doubly linked list
	 * comes for the route_out comes
	 * please turn this
	 * into duplicate routine
	 * 
	 * skh/jgs -  reason for duplicate routine
	 *   is for speed - no calls to 2nd routine
	 *   within a routine.
	 * jgs - HUH??   
	 */
  

	PEER_CNT_LOOP(i)
                {
		idrp_rem_relink_outlist(p_idrp_rt,i);	
		} PEER_CNT_LOOP_END;
}


void
idrp_rem_relink_outlist(p_idrp_rt,peer_id)
idrpRoute	*p_idrp_rt;
int		peer_id;
{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp_rem_relink_outlist(): nlri %s p_idrp_rt %x peer %d",
		iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt, peer_id));

	if (p_idrp_rt->route_out[peer_id].p_next == (idrpRoute *) NULL)
		{
		/* only one route on the output route id */

		if (p_idrp_rt->route_out[peer_id].route_id != 0)
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0,("Circular route_out list is broken nlri %s %x",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt));
			p_idrp_rt->route_out[peer_id].route_id = 0;
			}
		}
	else
		{
		if (p_idrp_rt->route_out[peer_id].p_next != p_idrp_rt)
			{
			idrpRoute *p_pred = p_idrp_rt->route_out[peer_id].p_back; /* predecessor route */
			idrpRoute *p_succ = p_idrp_rt->route_out[peer_id].p_next; /* successor route */

			p_pred->route_out[peer_id].p_next = p_succ;
			p_succ->route_out[peer_id].p_back = p_pred;
			}
		p_idrp_rt->route_out[peer_id].p_next = (idrpRoute *) NULL;
		p_idrp_rt->route_out[peer_id].p_back = (idrpRoute *) NULL;
		p_idrp_rt->route_out[peer_id].route_id = 0;
		}
}


void 
idrp_relink_repl_outlist(p_idrp_rt,p_idrp_rt_old)
idrpRoute	*p_idrp_rt;
idrpRoute	*p_idrp_rt_old;
{
int	i;

	PEER_CNT_LOOP(i)
		{
		if (p_idrp_rt_old->route_out[i].p_next != (idrpRoute *) NULL)
			{
			if (p_idrp_rt_old->route_out[i].p_next == p_idrp_rt_old)
				{
				/* singleton on list
				 */

				p_idrp_rt->route_out[i].p_next = p_idrp_rt;
				p_idrp_rt->route_out[i].p_back = p_idrp_rt;
				}
			else
				{
				/* non-singleton on list 
				 */

				p_idrp_rt_old->route_out[i].p_next->route_out[i].p_back = p_idrp_rt;
				p_idrp_rt_old->route_out[i].p_back->route_out[i].p_next = p_idrp_rt;
				p_idrp_rt->route_out[i].p_next = p_idrp_rt_old->route_out[i].p_next;
				p_idrp_rt->route_out[i].p_back = p_idrp_rt_old->route_out[i].p_back;
				}
			p_idrp_rt->route_out[i].route_id = p_idrp_rt_old->route_out[i].route_id;
			p_idrp_rt_old->route_out[i].p_back = p_idrp_rt_old->route_out[i].p_next = (idrpRoute *) NULL;
			p_idrp_rt_old->route_out[i].route_id = 0;
			}
		} PEER_CNT_LOOP_END;
}

boolean
idrp_any_rt_anns(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
int i;

	if (p_idrp_rt == NULL)
		{
		return(FALSE);
		}

	PEER_CNT_LOOP(i) 
		{
		if (p_idrp_rt->route_out[i].p_next != (idrpRoute *)NULL)
			{
			return(TRUE);
			}
		} PEER_CNT_LOOP_END;
	return(FALSE);
}			 		

void
idrp_copy_routelist(p_att,res)
idrp_attribute_record	*p_att; /* attribute structure */
parse_results		*res;	/* parse results */
{
int cnt;		/* cnt of routes */ 
int i;			/* loop counter */
idrpRoute	*p_head = 0;	/* temporary pointesr to head */
idrpRoute	*p_tail = 0;	/* temporary pointer to tail */
idrpRoute	*p_rt,*p_idrp_rt;	/* route pointers */

	/* this routine:
	 * loops by NLRI type to copy routes in main attribute
	 * 	other attributes in the parse pdu  
	 *
	 * 	1.1) allocates an idrpRoute  
	 *	1.2) bcopy current idrpRoute
	 *	1.3) sets new 
	 *		a) attribute pointer 
	 *		b) route_id from this route		
	 *	1.4) links the route structure to
	 *		route_id_list pointed to by
	 *	 	p_att->p_parse_re
	 * 
	 *     2) increments the reference count
	 * 	  by number of nlri
	 *
	 *     3) if new attribute (ref_cnt = nlri_cnt)
	 *	   links the attribute to the attribute list
	 */

	NLRI_FAMILY_LOOP(i)
		{
		p_tail = (idrpRoute *) 0;
		for (p_rt = res->p_att->p_parse_re->p_route[i];p_rt;p_rt = p_rt->p_next_nlri)
			{
			p_idrp_rt = (idrpRoute *) idrp_local_mem_fit(sizeof(idrpRoute));
			bcopy(p_rt,p_idrp_rt,sizeof(idrpRoute));

			/* change the attribute specific things */

			p_idrp_rt->p_attr = p_att;
			p_idrp_rt->pref_rcvd = p_att->p_parse_re->loc_pref;
			p_idrp_rt->route_id_in = p_att->p_parse_re->route_id;
			p_idrp_rt->p_next_nlri = (idrpRoute *) 0;

			/* link to this route_id list
			 */

			if (p_tail)
				{
				/* something on list */
				p_tail->p_next_nlri = p_idrp_rt; 
				p_tail = p_idrp_rt;
				}
			else
				{
				/* 1st time on list for this nlri  */
				p_tail = p_idrp_rt;
				p_head = p_idrp_rt; 
				}
			}

		/* copy in the head and  tail into the route_id structure */
	
		p_att->p_parse_re->p_route[i] = p_head;
		p_att->p_parse_re->p_route_tail[i] = p_tail; 
		p_att->ref_cnt += res->nlri_cnt;
		
		} NLRI_FAMILY_LOOP_END
}	 



/* insert rdi in to global RDI path
 * use: p_rdi = idrp_insert_global_rdi(&p_rdi);
 * return values: pointer to rdi 
 *		  or 
 *		  null if error
 * 	           	
 * action: takes pp_rdi and inserts into the global RDI structure 
 * 	   if exists frees the structure. 
 * 	   if not, sets structure in the RDI list 
 *	   [until we go to all local all RDIs are associated with local_peer memory]
 *
 * future: this routine will be replaced quickly by
 * 	   an insertion into a radix trie - see John Scudder 
 */
 

rdi *
idrp_insert_global_rdi(pp_rdi)
rdi	**pp_rdi;
{
rdi	*p_rdi_look;
rdi	*p_rdi,*p_rdi_last;
	
	p_rdi_look = *pp_rdi;
	/* routine to insert into Global memory
	 * can we get redix trie here first
	 * 
	 *  - rdi structure needs to have the
	 *  AF_ISO set 
	 */

	/* linear search until we get patrica trie
	 */

	if (rdi_list == (rdi *) NULL)
		{
		rdi_list = p_rdi_look;
		p_rdi_look->order = IDRP_GLOBAL_RDI_ZERO_VALUE;	
		return(p_rdi_look);
		}	

	IDRP_RDI_LIST(p_rdi)
		{
		switch(compare_iso_addr(&p_rdi->rdi,&p_rdi_look->rdi)) 	
			{
			case ISO_ADDR_EQUAL:
				IDRP_MEM_FIT_FREE(p_rdi_look);
				p_rdi->refcnt++;
				return(p_rdi);
				break;
	
			case ISO_ADDR_LESS_THAN:
				break;

			case ISO_ADDR_GREATER_THAN:
				/* ordering:  prev(less than new)
				 *	      new 
				 *            current
				 * 	      next
				 * 
				 */


				if (p_rdi == rdi_list)
					{
					/* new
					 * start of list
					 * next
					 */

					p_rdi->p_prev = p_rdi_look;		
					p_rdi_look->p_next = p_rdi;
					rdi_list = p_rdi_look;	
					p_rdi_look->refcnt = 1;
					if (p_rdi->order > 0)
						{ 
						p_rdi_look->order = p_rdi->order-1;
						}
					else
						{
						idrp_global_rdi_order();
						}		
				 
					}
				else
					{
					/*
					 * prev
					 * new
					 * current
					 * next 
					 */

					p_rdi->p_prev->p_next = p_rdi_look;
					p_rdi_look->p_next = p_rdi;
					p_rdi_look->p_prev = p_rdi->p_prev;
					p_rdi->p_prev = p_rdi_look;
					p_rdi_look->refcnt = 1;
					if (p_rdi->order == (p_rdi_look->p_prev->order + 1))
						{
						p_rdi_look->order = 0;
						}
					else
						{
						p_rdi_look->order = p_rdi_look->p_prev->order + 1;
						}
					}
				return(p_rdi_look);
				break;
			}
		p_rdi_last = p_rdi;

		}IDRP_RDI_LIST_END;		

	/* no match - set at the end of the list
	 */

	p_rdi_last->p_next = p_rdi_look;
	p_rdi_look->p_prev = p_rdi_last;
	p_rdi_look->refcnt = 1;
	p_rdi_look->order = p_rdi_look->p_prev->order + IDRP_GLOBAL_RDI_SPACING;
        return (p_rdi_look);
	
}

/* 
 * idrp_free_global_rdi(pp_rdi)
 *              action:  decrements the count of the RDI_T referred to by pp_rdi;
 *                      if zero, it frees this RDI from the global structure.  Also,
 *                      frees *pp_rdi.
 *              side-effects:   no others
 *              return values:
 *                      TRUE if successful, FALSE otherwise
 */

void
idrp_free_global_rdi(p_rdi)
rdi *p_rdi;
{

	/* make sure that the refcnt never goes to zero
	 * before we free it
	 */

	assert (p_rdi->refcnt != 0);
	p_rdi->refcnt--;	

	/* this rdi - was freed from the rdi usage
	 * - but it is being used by someone else
	 */

	if (p_rdi->refcnt > 0)
		return;

	
	/* linear search rdi - replace with patricia trie
	 */

	if (p_rdi == rdi_list)
		{
		rdi_list = p_rdi->p_next;
		if (rdi_list)
			{
			rdi_list->p_prev = (rdi *) NULL;
			}
		}
	else 	
		{
		p_rdi->p_prev->p_next = p_rdi->p_next;
		if (p_rdi->p_next)
			{
			p_rdi->p_next->p_prev = p_rdi->p_prev;
			}
		} 
	IDRP_MEM_FIT_FREE(p_rdi);
	
}


/*  idrp_find_global_rdi
 * 
 *   	call: given rdi find the global rdi if it exists 
 * 	actions:
 * 		 1) do patrcia tree search on rdi
 *		    - temporarily do linear search
 *			lousy but it works for now
 * 
 *		 2) return pointer to rdi if it works
 */

rdi *
idrp_find_global_rdi(rdi_len,p_rdi_str)
int	rdi_len;
byte	*p_rdi_str;
{
rdi	*p_rdi;
struct	iso_net_addr	net;

	bcopy(p_rdi_str,&net.isoa_genaddr[0],rdi_len);
	net.isoa_len = rdi_len;
	net.isoa_family = AF_ISO; 

	/* linear search until we get patrica trie
	 */

	IDRP_RDI_LIST(p_rdi)
		{
		switch(compare_iso_addr(&p_rdi->rdi,&net)) 	
			{
			case ISO_ADDR_EQUAL:
				return(p_rdi);
				break;
			case ISO_ADDR_LESS_THAN:
				break;
			case ISO_ADDR_GREATER_THAN:

				return((rdi *) NULL);
				break;
			}
		} IDRP_RDI_LIST_END;

	return((rdi *) NULL);

}
  
void
idrp_global_rdi_order()
{
rdi	*p_rdi;
int	i =  IDRP_GLOBAL_RDI_ZERO_VALUE;


	for (p_rdi = rdi_list; p_rdi; p_rdi = p_rdi->p_next)
		{
		p_rdi->order = i;
		i = i + IDRP_GLOBAL_RDI_SPACING;
		}
}

 
/*  
 * 
 * call: idrp_create_rdpath_list(flag_t seg_type, rdi *p_rdi);
 *
 * return: rdpath_list *
 * action: create rdpath_list with an rdpath structure and stuff in 1st rdi
 *         in rdi_array structure 
 */

rdpath_list *
idrp_create_rdpath_list(segtype, p_rdi)
flag_t	segtype;
rdi	*p_rdi;
{
rdpath *p_path;
rdpath_list	*p_pathl;

	p_path = (rdpath *) idrp_local_mem_fit(sizeof(rdpath));

	bzero(p_path,sizeof(rdpath));
	p_path->status = segtype;
	p_path->rdi_list.p_rdi_array[0] = p_rdi;
	p_path->p_last = &p_path->rdi_list;
	p_path->cnt = 1;

	p_pathl = (rdpath_list *) idrp_local_mem_fit(sizeof(rdpath_list));
	bzero(p_pathl,sizeof(rdpath_list));
	p_pathl->p_end = p_pathl->p_start = p_path;
	p_pathl->cnt = 1;

	/* - set the reference count to 1
	 */
 
	p_pathl->refcnt = 1;
	return (p_pathl);	
} 


/* add rdi to rdpath in segment type 
*/

boolean
idrp_add_to_rdpath(segtype,p_rdi,p_rdpath)
flag_t	segtype;
rdi	*p_rdi;
rdpath_list *p_rdpath;
{
rdi_array *p_rdi_new,*p_rdi_loop;
rdi_array *p_rdi_last = 0;
rdpath *p_pathf;
rdpath *p_path;
u_int	index;


	p_pathf = p_rdpath->p_end;
	p_path = p_rdpath->p_end;
	if (p_pathf->status != segtype) 
		{
		/* add in a new rdpath structure */
 
		p_pathf->p_next = p_path = (rdpath *) idrp_local_mem_fit(sizeof(rdpath));
		bzero(p_path,sizeof(rdpath));

		/* link into the rdpath structure 
		 *  set up prev,next and last
		 *  status, all else is zeroed
		 * - 
		 */

		p_path->status = segtype; 
		p_path->p_prev = p_pathf;
		p_path->p_last = &p_path->rdi_list;

		/* link on the end of the rdpath list
		 */
	
		p_rdpath->p_end = p_path;
		}
			 
	/* add an rdi to this path */

	index = p_path->cnt % IDRP_RD_PATH_ARRAY_SIZE;	
	if (index == 0 && p_path->cnt != 0) 
		{
		/* need another array structure */

		p_rdi_new  = (rdi_array *) idrp_local_mem_fit(sizeof(rdi_array));
		p_path->p_last->p_next = p_rdi_new;
		bzero(p_rdi_new,sizeof(rdi_array));
		p_rdi_new->p_prev = p_path->p_last;	
		p_path->p_last = p_rdi_new; 
		}

	/* now we have the rdi array - fill in the rdi 
	 */

	p_path->p_last->p_rdi_array[index] = p_rdi;
	p_path->cnt++;
	p_rdpath->cnt++;

	return(TRUE);
}



void
idrp_free_rdpath(p_rdpathl)
rdpath_list *p_rdpathl;
{
	if (p_rdpathl == (rdpath_list *) NULL)
	{	
	trace_tf(idrp_trace_options, TR_NORMAL,0, ("RDPATH pointer empty ")); 	
	return;
	}

	p_rdpathl->refcnt =  p_rdpathl->refcnt - 1;
	if (p_rdpathl->refcnt == 0)
		{		
		idrp_free_rdpath_hash_entry(p_rdpathl);
		idrp_free_rdpath_list(p_rdpathl);
		}
}

void
idrp_free_rdpath_list(p_rdpathl)
rdpath_list *p_rdpathl;
{	
rdpath	*p_rd;
rdpath	*p_rd_next;
rdi_array	*p_rdi_array;
rdi_array	*p_rdi_array_next;
int		cnt_rdi = 0;
int		cnt,i;

	

	IDRP_RDPATH_WALK_FREE(p_rd,p_rdpathl->p_start,p_rd_next)
		{
		/* walk down array list for each rdpath structure
		 * - to decrement count on global rdis
	 	*/

		cnt = p_rd->cnt;
		for (p_rdi_array = &p_rd->rdi_list; p_rdi_array; p_rdi_array = p_rdi_array_next)
			{
			p_rdi_array_next = p_rdi_array->p_next;
			cnt_rdi = IDRP_RD_PATH_ARRAY_SIZE;

			/* make sure that cnt is greater than zero if we are going to free things
			 */

			assert(cnt > 0);
			if (cnt < IDRP_RD_PATH_ARRAY_SIZE)
				{
				cnt_rdi = cnt;
				} 
			for (i = 0; i < cnt_rdi; i++)
				{
				/* free the count to the rdi
				 */
 	
				idrp_free_global_rdi(p_rdi_array->p_rdi_array[i]);
				}

			/* free the rdi_array entry
			   No, this is a pointer to static memory!!! You really, really
                           can't do this... CHL
			 */

			/* IDRP_MEM_FIT_FREE(p_rdi_array); */
			cnt -= cnt_rdi;
		      }

		/* free the rd path entry */
		IDRP_MEM_FIT_FREE(p_rd);

		} IDRP_RDPATH_WALK_FREE_END;
}

rdpath_list * 
idrp_insert_global_rdpaths(p_rd_pathl)
rdpath_list *p_rd_pathl;
{
rdpath_list *p_rdp_list,*p_rdp_list_start;
idrp_rdpath_hash *p_rdp_hash;
rdpath	    *p_path;
rdpath	    *p_new_path;
boolean	    rdi_match = FALSE; 
rdi	    *p_rdi;
int		i;
int		length_match;


	p_new_path = p_rd_pathl->p_start;
	/* look up RDI in the hash table
	 * (this can change to a Patricia trie )
	 * then find match on the RDI
	 */

	p_rdi = p_rd_pathl->p_start->rdi_list.p_rdi_array[0]; 
	p_rdp_hash = find_rdpath_hash_entry(p_rdi); 
	if (p_rdp_hash == (idrp_rdpath_hash *)  NULL)
		{
		/* empty hash entry  - add to hash table
		 *
		 */
		u_int index;

		index = IDRP_RDPATH_HASH((u_int) p_rdi); 

		p_rdp_hash = (idrp_rdpath_hash *) idrp_local_mem_fit(sizeof(idrp_rdpath_hash)); 					
		bzero((u_char *)p_rdp_hash,sizeof(idrp_rdpath_hash));
		p_rdp_hash->p_rdi = p_rdi;
		p_rdp_hash->p_pl_start = p_rdp_hash->p_pl_end = p_rd_pathl;
		p_rdp_hash->p_next = p_rdp_hash->p_prev = (idrp_rdpath_hash *) NULL;  
		
		if (rd_path_list_hash[index] == (idrp_rdpath_hash *) NULL)
			{
			rd_path_list_hash[index] = p_rdp_hash;			
			}
		else
			{
			idrp_rdpath_hash * p_rdp;	
			for (p_rdp = rd_path_list_hash[index]; p_rdp == (idrp_rdpath_hash *) NULL; p_rdp = p_rdp->p_next)
				{
				if (p_rdp->p_next == NULL)
					break;
				}
			p_rdp->p_next = p_rdp_hash;
			p_rdp_hash->p_prev = p_rdp;
			}			
			
		return(p_rd_pathl);
		}	

	p_rdp_list = find_rdpath_list(p_rdp_hash,p_rd_pathl);
	if (p_rdp_list != (rdpath_list *) NULL)
		{
		/* exact match - return the exact match 
		 * -free the rdpathlist			 
		 */
	
		p_rdp_list->refcnt++;	
		idrp_free_rdpath_list(p_rd_pathl);
		return(p_rdp_list);	
		}

	/* Not exact match - so add at the end of the
	 * - hash table
	 */

	p_rdp_hash->p_pl_end->p_next = p_rd_pathl;
	p_rd_pathl->p_prev = p_rdp_hash->p_pl_end;
	p_rdp_hash->p_pl_end = p_rd_pathl;	
		
	return(p_rd_pathl);

}

rdpath_list *
find_rdpath_list(p_hash,p_rd_pathl)
idrp_rdpath_hash *p_hash;
rdpath_list *p_rd_pathl;
{
rdpath_list *p_rdp_list;
rdpath	*p_path,*p_new_path;
rdi_array	*p_rdi_array,*p_rdi_new_array;	
boolean		rdi_match;
int		i,rdi_cnt;
u_int		index;
	p_new_path = p_rd_pathl->p_start;  	
	IDRP_RDPATH_LIST(p_rdp_list,p_hash->p_pl_start)
		{
		/* inside each ordered path list is a 
		 * cnt of the  
		 */

		rdi_match = FALSE;
		if (p_rdp_list->cnt == p_rd_pathl->cnt)
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0, (" path 1 cnt = %d, path2 cnt = %d",p_rdp_list->cnt,p_rd_pathl->cnt)); 	

			IDRP_RDPATH_RDI_LIST(p_path,p_rdp_list)
				{
				/* check the status and the count of RDIs
				 * - for this segment
				 */
	
				if (p_path->status != p_new_path->status)
					break;   
	
				if (p_path->cnt != p_new_path->cnt)
					break;

				/* status is the same
				 * - check the array of rdis
				 */
	
				p_rdi_array = &p_path->rdi_list;
				p_rdi_new_array = &p_new_path->rdi_list;
				rdi_match = TRUE;
	
				IDRP_RDI_ARRAY_WALK(p_path,i,rdi_cnt)
					{
					/* here compare the pointers to 	
					 * - the global rdis
					 * - if fail set flag for not equal
					 * - and set the count to rest or rdis 
					 * -- need to re-write code
					 */
	
					index = i%IDRP_RD_PATH_ARRAY_SIZE;	
					if (p_rdi_array->p_rdi_array[i] != p_rdi_new_array->p_rdi_array[i])
						{
						/* stop this loop */
						rdi_cnt = p_path->cnt+1;
						rdi_match = FALSE;	
						}
					else
						{
						rdi_cnt = 1;
						}

					if (((index+1)%IDRP_RD_PATH_ARRAY_SIZE) == 0)
						{
						p_rdi_array = p_rdi_array->p_next;
						p_rdi_new_array = p_rdi_new_array->p_next;
						}	
					
					} IDRP_RDI_ARRAY_WALK_END;
	
				/* if we have found a rdi that did not match -
				 * - stop this loop now
				 */
				if (!rdi_match)			
					break;
	
				p_new_path = p_new_path->p_next;
				} IDRP_RDPATH_RDI_LIST_END;	
	
			if (rdi_match)
				{
				/* full path match - return the pointer
				 */
	
				return(p_rdp_list);	
				}
			}
		else
			{
			trace_tf(idrp_trace_options, TR_NORMAL, 0, (" path 1 cnt = %d, path2 cnt = %d",p_rdp_list->cnt,p_rd_pathl->cnt)); 	
			}
	
		} IDRP_RDPATH_LIST_END;


	return((rdpath_list *) NULL);
}

idrp_rdpath_hash *
find_rdpath_hash_entry(p_rdi)
rdi *p_rdi;
{
idrp_rdpath_hash	*p_hash,*p_hash_start;
rdpath_list		*p_pathl;

	p_hash_start = rd_path_list_hash[(IDRP_RDPATH_HASH((u_int)p_rdi))];
	for (p_hash = p_hash_start; p_hash; p_hash = p_hash->p_next)
		{
		assert(p_hash)	
		if (p_hash->p_rdi == p_rdi)
			{
			return(p_hash);
			} 
		}
	return((idrp_rdpath_hash *) NULL);	
}


void
idrp_free_rdpath_hash_entry(p_rdpathl)
rdpath_list *p_rdpathl;
{
idrp_rdpath_hash *p_hash;
rdpath_list	*p_rdpath_le;

	
	p_hash = find_rdpath_hash_entry(p_rdpathl->p_start->rdi_list.p_rdi_array[0]);
	trace_tf(idrp_trace_options, TR_NORMAL,0,("freeing the rdpath entry: %x, from hash entry %x ",p_rdpathl,p_hash)); 

	assert(p_hash != (idrp_rdpath_hash *) NULL);

	IDRP_RDPATH_LIST(p_rdpath_le,p_hash->p_pl_start)	
		{
		/* walk through the rdpath list on this hash entry looking for
		 * - the point to this rdplath list
		 */

		if (p_rdpath_le == p_rdpathl)
			break;
		}IDRP_RDPATH_LIST_END;

	/* we should find this rdpath list
	 */

	assert (p_rdpath_le != (rdpath_list *) NULL);

	/* Found an rdpath_list
	 * - now try to remove this rdpath list
	 * - reset both the head and tail pointers
	 */

	if (p_rdpath_le != p_hash->p_pl_start && p_rdpath_le != p_hash->p_pl_end) 
		{
		/* link around the middle of the  list
		 * - of rdpath list with this rdi
		 */
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("idrp_free_rdpath_hash_entry - hash table mid terms p_hash =  %x, p_rdpath_le = %x, p_rdpath_le->p_prev = %x, p_next %x",
			p_hash,p_rdpath_le,p_rdpath_le->p_next,p_rdpath_le->p_prev));   

		p_rdpath_le->p_prev->p_next = p_rdpath_le->p_next;
		p_rdpath_le->p_next->p_prev = p_rdpath_le->p_prev; 
		return;
		}
	
	if (p_rdpath_le == p_hash->p_pl_start)
		{
		/* this is at the start of the list
		 */
	
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("idrp_free_rdpath_hash_entry - hash table = pl_pl_start p_hash =  %x, p_rdpath_le = %x, p_rdpath_le->p_next = %x, p_prev %x",
			p_hash,p_rdpath_le,p_rdpath_le->p_next,p_rdpath_le->p_prev));   
		p_hash->p_pl_start = p_rdpath_le->p_next;
		if (p_hash->p_pl_start != (rdpath_list *) NULL)
			{  
			p_rdpath_le->p_next->p_prev = (rdpath_list *) NULL;
			}
		}
 
	if (p_rdpath_le == p_hash->p_pl_end)
		{
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("idrp_free_rdpath_hash_entry - hash table = pl_pl_end p_hash =  %x, p_rdpath_le = %x, p_rdpath_le->p_next = %x, p_prev %x",
			p_hash,p_rdpath_le,p_rdpath_le->p_next,p_rdpath_le->p_prev));   

		p_hash->p_pl_end = p_rdpath_le->p_prev;
		if (p_hash->p_pl_end != (rdpath_list *) NULL)
			{  
			p_rdpath_le->p_prev->p_next = (rdpath_list *) NULL;
			}
		}

	if (p_hash->p_pl_start == (rdpath_list *) NULL)
		{
		/*  No more rdpath pathways for this rdi 
		 *  - see if there is any more 
		 */
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("idrp_free_rdpath_hash_entry - hash table start = null =  %x, p_hash->p_prev p_hash->p_next = %x, p_prev %x",
			p_hash,p_hash->p_next,p_hash->p_prev));   
   
		if (p_hash->p_prev == (idrp_rdpath_hash *) NULL)
			{
			/* the previous entry is null - so this is
			 * the first entry
			 */

			int index = IDRP_RDPATH_HASH((u_int)p_hash->p_rdi); 
			rd_path_list_hash[index] = p_hash->p_next;
			}	

		/* free this hash table entry */
 
		IDRP_MEM_FIT_FREE(p_hash);
		}
      
}


int
compare_rdi(p_rdi1,p_rdi2)
rdi     *p_rdi1;
rdi     *p_rdi2;
{

	/* here's where we need to change to order
	 * once the insert works
         * - see if we can turn this into a quick macro
         */
        
        /* code for compare with ordering
         * 
         */

        if (p_rdi1 == p_rdi2)
                return(PFX_ADDR_EQUAL);

        if (p_rdi2->order == p_rdi1->order)
                {
                /* this shouldn't happen 
                 */
                assert(FALSE);  
                }       

        if (p_rdi1->order < p_rdi2->order)
                return(PFX_ADDR_LESS_THAN);


        return(PFX_ADDR_GREATER_THAN);
        
        /* current code - with direct byte by byte compare
         *      return(compare_iso_addr(&p_rdi1->rdi,&p_rdi2->rdi));
         */     

}


