/* 
 * $Id: idrp_rt_phase_util.c,v 1.17 1995/11/06 16:30:18 skh Exp $ 
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

#include "include.h"
#include "iso.h"
#include "inet.h"
#include "idrp.h"

/* idrp_rt_phase_util.c
 * - utilities for idrp phases 
 * 
 * %% utility routines used by phase 1 and phase 3
 *
 * link_ann_list 	- link route to announce list
 * link_ann_list_mod 	- link route to announce list with outbound attribute in idrpRoute_out 
 * unlink_ann_list	- unlink route from announce list
 * create_ann_list_entry - create an announce list entry
 * find_ann_list_entry	- find an announce list entry
 * rem_ann_ann_list_entry  - remove entry and link around it
 * link_ann_list_ann_list  - link announce list to existing announce list 
 *				
 * free_ann_list - 	free an announce lsit
 *  
 * link_send_list	- link new send_list to old send list 
 * send_with_attr_replace - can't send nlri, and it replaces another route 
 * send_with_attr	- send withdrawn from announce list  
 * send_update_reset_send - send update pdu and reset send list
 * 
 * send_nlri_attr	- send nlri from from announce list 
 * create_send_list	- create a send list from task memory and
 *		          fill in with attributes
 * free_send_list	- free the send linst  
 *
 * flush_att_send_list  - flush the send list at end of
 *			  send list entry for an attribute record
 * idrp_send_list_room  - is there room in the UPDATE PDU for
 *			  this peer to include the additions
 *			  in the send list additions 
 *  
 * idrp_del_sent_routes  - delete routes after sending withdrawals
 * idrp_pref_compare(p_idrp_rt,p_best_ext) - preference comparison -
 *	returns TRUE if first argument is better than the second. 
 * %% parsing update help routines
 *   
 * idrp_add_route_locate
 *	- locate information about route addition  
 *
 * idrp_with_route_locate 
 *	- locate information about route withdrawl
 *  
 * idrp_rt_change(p_idrp_rt1,p_idrp_rt2)
 *	- check that these two idrpRoutes are different
 * 
 * free_rt_chain_walk - free idrp_rt_chain_walk structure
 *			create in idrp_add_route_locate or 
 *			idrp_with_route_locate
 *
 * find_next_best(p_idrp_rt) - find the next-best external route
 *	to the same NLRI as the external route p_idrp_rt
 *	points to.  Return a pointer to it.
 *
 * find_best_ext(p_idrp_rt) - find the best external route
 *      find the best external route, return pointer to it.
 * 
 *  
 * %%rdi utilies used by policy and the
 * 	parsing routines
 * rdi_in_dist_list(p_att,p_rdi)  
 * 
 * %% - dumping of tables for debugging routines
 * 
 * idrp_peer_tsi_dump  
 * dump_local_rib
 * find_nlri_ann_list - find nlri in the announce list
 * remove_ann_dup(p_with_list,p_ann_list) - remove withdraws duplicated
 *					    in announce list and flag
 * 					    as withdraw with explicit replace 
 */

		
/* %% routine for general sending
 * 
 * link_ann_list  - link route to ann list for type withdraw or announce  
 * unlink_ann_list - reverse the preceding
 * create_ann_list_entry - create an announce list entry
 * find_ann_list_entry - find an announce list entry
 * link_send_list - link route to send list 
 * send_with_attr - build send list for withdrawls 
 * send_nlri_attr - build send list for nlri 
 *  
 */


/*----------------------------------------------------------------------------
 * link_ann_list()
 *
 * The announce lists are now doubly linked instead of singly linked.  
 */
int
link_ann_list(p_idrp_rt,p_ann_list,type)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
int		type;
{
idrp_ann_list	*p_atl;
int		nlri_id;
int	found = 0;
int	i;


	nlri_id = family_to_nlri_id(p_idrp_rt->family);

	/* is the announce list empty
	 * if so skip search
	 */

	/* Our caller needs to create an ann_list before calling us.  We could change this routine
	 * to create the list if none exists,  and not require the caller to provide it.
	 */
	assert(p_ann_list);

	p_atl = find_ann_list_entry(p_idrp_rt->peer,p_idrp_rt->p_attr,p_ann_list);

	/* entry for attribute list has been found or created.
	 * link the new idrp_route to it with the P_ANN_NLRI
	 * link in the idrp route
	 */
 
	switch (type)
		{
		case NLRI_LINKED_P_NEXT:
			trace_tf (idrp_trace_options, TR_NORMAL,0,(" link_ann_list - only list through ann or withdraw route %s tried to link through p_next",iso_ptoa(&p_idrp_rt->nlri))); 
			return(FALSE);
			break;			

		case NLRI_LINKED_P_ANN:
			if (p_atl->ann_nlri[nlri_id].tail)
				{
				/* note that p_ann_nlri is the forward link on the announce chain */
				/* and p_ann_back is the back link on the announce chain */
				p_atl->ann_nlri[nlri_id].tail->p_ann_nlri = p_idrp_rt;
				p_idrp_rt->p_ann_back = p_atl->ann_nlri[nlri_id].tail;
				p_atl->ann_nlri[nlri_id].tail = p_idrp_rt;
				}
			else
				{
				p_atl->ann_nlri[nlri_id].head = p_atl->ann_nlri[nlri_id].tail = p_idrp_rt;
				p_idrp_rt->p_ann_back = NULL;
				}
			p_idrp_rt->p_ann_nlri = NULL;
			break;

		case NLRI_LINKED_P_WITH:
			if (p_atl->with_nlri[nlri_id].tail)
				{
				/* note that p_with is the forward link on the withdraw chain */
				/* and p_with_back is the backward link */
				p_atl->with_nlri[nlri_id].tail->p_with = p_idrp_rt;
				p_idrp_rt->p_with_back = p_atl->with_nlri[nlri_id].tail;
				p_atl->with_nlri[nlri_id].tail = p_idrp_rt;
				}
			else
				{
				p_atl->with_nlri[nlri_id].tail = p_atl->with_nlri[nlri_id].head = p_idrp_rt;
				p_idrp_rt->p_with_back = NULL;
				}
			p_idrp_rt->p_with  = NULL;
			break;
		}
	return(TRUE);
}
/*----------------------------------------------------------------------------
 * link_ann_list_mod()
 *
 * The announce lists are now doubly linked instead of singly linked.  
 */

int
link_ann_list_mod(p_idrp_rt,p_att,p_ann_list,type)
idrpRoute	*p_idrp_rt;
idrp_attribute_record  *p_att;	
idrp_ann_list	*p_ann_list;
int		type;
{
idrp_ann_list	*p_atl;
int		nlri_id;
int	found = 0;
int	i;


	nlri_id = family_to_nlri_id(p_idrp_rt->family);

	/* is the announce list empty
	 * if so skip search
	 */

	/* Our caller needs to create an ann_list before calling us.  We could change this routine
	 * to create the list if none exists,  and not require the caller to provide it.
	 */
	assert(p_ann_list);

	p_atl = find_ann_list_entry(p_att->peer_alloc,p_att,p_ann_list);

	/* entry for attribute list has been found or created.
	 * link the new idrp_route to it with the P_ANN_NLRI
	 * link in the idrp route
	 */
 
	switch (type)
		{
		case NLRI_LINKED_P_NEXT:
			trace_tf (idrp_trace_options, TR_NORMAL,0,(" link_ann_list - only list through ann or withdraw route %s tried to link through p_next",iso_ptoa(&p_idrp_rt->nlri))); 
			return(FALSE);
			break;			

		case NLRI_LINKED_P_ANN:
			if (p_atl->ann_nlri[nlri_id].tail)
				{
				/* note that p_ann_nlri is the forward link on the announce chain */
				/* and p_ann_back is the back link on the announce chain */
				p_atl->ann_nlri[nlri_id].tail->p_ann_nlri = p_idrp_rt;
				p_idrp_rt->p_ann_back = p_atl->ann_nlri[nlri_id].tail;
				p_atl->ann_nlri[nlri_id].tail = p_idrp_rt;
				}
			else
				{
				p_atl->ann_nlri[nlri_id].head = p_atl->ann_nlri[nlri_id].tail = p_idrp_rt;
				p_idrp_rt->p_ann_back = NULL;
				}
			p_idrp_rt->p_ann_nlri = NULL;
			break;

		case NLRI_LINKED_P_WITH:
			if (p_atl->with_nlri[nlri_id].tail)
				{
				/* note that p_with is the forward link on the withdraw chain */
				/* and p_with_back is the backward link */
				p_atl->with_nlri[nlri_id].tail->p_with = p_idrp_rt;
				p_idrp_rt->p_with_back = p_atl->with_nlri[nlri_id].tail;
				p_atl->with_nlri[nlri_id].tail = p_idrp_rt;
				}
			else
				{
				p_atl->with_nlri[nlri_id].tail = p_atl->with_nlri[nlri_id].head = p_idrp_rt;
				p_idrp_rt->p_with_back = NULL;
				}
			p_idrp_rt->p_with  = NULL;
			break;
		}
	return(TRUE);
}

/*----------------------------------------------------------------------------
 * unlink_ann_list()
 *
 * Take an idrpRoute off of an ann_list.  Useful for, e.g., min adv code when
 * it decides that we really don't want to advertise that damped route.
 *
 * Note that we do NOT want to delete the route, just take it off the announcement
 * list.
 *
 * Functionally, this is the inverse of link_ann_list().  
 *

/*----------------------------------------------------------------------------
 * unlink_ann_list()
 *
 * Take an idrpRoute off of an ann_list.  Useful for, e.g., min adv code when
 * it decides that we really don't want to advertise that damped route.
 *
 * Note that we do NOT want to delete the route, just take it off the announcement
 * list.
 *
 * Functionally, this is the inverse of link_ann_list().  
 *
 * We have added back links to the announce and withdraw chains to facilitate
 * the use of this routine.
 *
 * jgs -- One issue of concern here is how an empty announce/w-draw list is 
 * handled later.  We currently may empty a list w/o deleting it.  Perhaps we 
 * should get rid of the list too in that case.
 */
int
unlink_ann_list(p_idrp_rt,p_atl,type)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_atl;
int		type;
{
int		nlri_id;
int		found = 0;
int		i;

	assert(p_idrp_rt && p_atl);
	nlri_id = family_to_nlri_id(p_idrp_rt->family);

	switch (type)
		{
		case NLRI_LINKED_P_ANN:
			if (p_atl->ann_nlri[nlri_id].head == p_atl->ann_nlri[nlri_id].tail) /* this is the only entry */
				{
				p_atl->ann_nlri[nlri_id].head = p_atl->ann_nlri[nlri_id].tail = NULL;	
				}
			else if (p_atl->ann_nlri[nlri_id].head == p_idrp_rt) /* this is the head of a non-singleton list */
				{
				p_atl->ann_nlri[nlri_id].head = p_idrp_rt->p_ann_nlri;
				p_idrp_rt->p_ann_nlri->p_ann_back = NULL;
				}
			else if (p_atl->ann_nlri[nlri_id].tail == p_idrp_rt) /* this is the tail of a non-singleton list */
				{
				p_atl->ann_nlri[nlri_id].tail = p_idrp_rt->p_ann_back;
				p_idrp_rt->p_ann_back->p_ann_nlri = NULL;
				}
			else /* this is not the head or the tail */
				{
				p_idrp_rt->p_ann_back->p_ann_nlri = p_idrp_rt->p_ann_nlri;
				p_idrp_rt->p_ann_nlri->p_ann_back = p_idrp_rt->p_ann_back;
				}
			p_idrp_rt->p_ann_nlri = p_idrp_rt->p_ann_back = NULL;
			break;

		case NLRI_LINKED_P_WITH:
			if (p_atl->with_nlri[nlri_id].head == p_atl->with_nlri[nlri_id].tail) /* this is the only entry */
				{
				p_atl->with_nlri[nlri_id].head = p_atl->with_nlri[nlri_id].tail = NULL;	
				}
			else if (p_atl->with_nlri[nlri_id].head == p_idrp_rt) /* this is head of a non-singleton list */
				{
				p_atl->with_nlri[nlri_id].head = p_idrp_rt->p_with;
				p_idrp_rt->p_with->p_with_back = NULL;
				}
			else if (p_atl->with_nlri[nlri_id].tail == p_idrp_rt) /* this is tail of a non-singleton list */
				{
				p_atl->with_nlri[nlri_id].tail = p_idrp_rt->p_with_back;
				p_idrp_rt->p_with_back->p_with = NULL;
				}
			else /* this is not the head or the tail */
				{
				p_idrp_rt->p_with_back->p_with = p_idrp_rt->p_with;
				p_idrp_rt->p_with->p_with_back = p_idrp_rt->p_with_back;
				}
			p_idrp_rt->p_with = p_idrp_rt->p_with_back = NULL;
			break;
			
		default:
			trace_tf(idrp_trace_options, TR_NORMAL, LOG_ERR, ("unlink_ann_list() -- Warning!  Unknown type code %d.", type));
			return(FALSE);
		}
	return(TRUE);
}

idrp_ann_list *
create_ann_list_entry(peer,p_att)
idrpPeer *peer;
idrp_attribute_record *p_att;
{
idrp_ann_list 	*p_anl;

	p_anl = (idrp_ann_list *) idrp_local_mem_fit (sizeof(idrp_ann_list));


	/* zeroing the structure will
	 * now allow default rib to be set and all pointers zeroed 
	 */

	bzero(p_anl,sizeof(idrp_ann_list));
	p_anl->peer = peer;
	p_anl->p_attr = p_att;
	p_anl->rib_id = p_att->rib_id;
	return(p_anl);
}

idrp_ann_list *
find_ann_list_entry(peer,p_att,p_ann_list)
idrpPeer	*peer;
idrp_attribute_record	*p_att;
idrp_ann_list		*p_ann_list;
{
idrp_ann_list	*p_atl;
idrp_ann_list	*p_last_atl;

	/* search for announce list entry  
	 * that matches
	 */

	if (!p_ann_list->p_attr)
		{
		/* list entry is there but empty - return this
		 * one after  initialization
		 */

		p_ann_list->peer = peer;
		p_ann_list->p_attr = p_att;
		if (p_att)
			{
			p_ann_list->rib_id = p_att->rib_id;
			}
		return (p_ann_list);
		}
		

	ANN_LIST(p_atl,p_ann_list) 
		{
		if (p_atl->p_attr == p_att)
			{
			/* attribute record match */
			return(p_atl);
			}
		p_last_atl = p_atl;
		} ANN_LIST_END 

	p_atl = create_ann_list_entry(peer,p_att);
	p_last_atl->p_next = p_atl;
	return(p_atl);
}

void
link_ann_list_ann_list(p_ann_list,peer,p_add_ann_list)
idrp_ann_list	*p_ann_list;
idrpPeer	*peer;
idrp_ann_list	*p_add_ann_list;
{
idrp_ann_list	*p_anl,*p_anl2;
int		i;


	p_anl = find_ann_list_entry(peer,p_add_ann_list->p_attr,p_ann_list);
	NLRI_FAMILY_LOOP(i)
		{
		if (p_anl->ann_nlri[i].head)
			{
			p_anl->ann_nlri[i].tail->p_ann_nlri = p_add_ann_list->ann_nlri[i].head;
			p_add_ann_list->ann_nlri[i].head->p_ann_back = p_anl->ann_nlri[i].tail; 
			p_anl->ann_nlri[i].tail = p_add_ann_list->ann_nlri[i].tail;
			}
		else
			{
			p_anl->ann_nlri[i].head = p_add_ann_list->ann_nlri[i].head;
			p_anl->ann_nlri[i].tail = p_add_ann_list->ann_nlri[i].tail;
			}
		if (p_anl->with_nlri[i].head)
			{
			p_anl->with_nlri[i].tail->p_with = p_add_ann_list->with_nlri[i].head;
			p_add_ann_list->with_nlri[i].head->p_with_back = p_anl->with_nlri[i].tail; 
			p_anl->with_nlri[i].tail = p_add_ann_list->with_nlri[i].tail;
			}
		else
			{
			p_anl->with_nlri[i].head = p_add_ann_list->with_nlri[i].head;
			p_anl->with_nlri[i].tail = p_add_ann_list->with_nlri[i].tail;
			}

	      } NLRI_FAMILY_LOOP_END;

}

void
rem_ann_ann_list_entry(p_atl,nlri_f,p_irt,p_irt_last)
idrp_ann_list	*p_atl;		/* announce list */
int		nlri_f;	/* nlri family */
idrpRoute	*p_irt;		/* route to remove */
idrpRoute	*p_irt_last;	/* last route accepted */
{
/* remove the idrp route from the announce list
 * p_irt_last - has last idrp route
 * 
 */

	if (p_irt == p_atl->ann_nlri[nlri_f].head)
		{
		p_atl->ann_nlri[nlri_f].head = p_irt->p_ann_nlri;
		}
	else
		{
		p_irt_last->p_ann_nlri = p_irt->p_ann_nlri;
		}


}


/* clean up the ann_ann list after a remove sequence
 * to make sure that any announce list
 * entries that are now empty are released
 * and linked around
 */

idrp_ann_list *
clean_ann_ann_list(p_ann_list)
idrp_ann_list	*p_ann_list;		/* announce list */
{

idrp_ann_list	*p_atl;		/* announce list variable */
idrp_ann_list	*p_atl_last = 0;	/* announce list pointers */
idrp_ann_list	*p_atl_first = 0;	/* announce list pointers */
idrp_ann_list	*p_free = 0;	/* announce list */
int		i;		/* loop counter */
int		empty = TRUE;	/* empty indicator for entry */

	ANN_LIST(p_atl,p_ann_list)
		{
		empty = TRUE;

		/* free a previous announce list entry that was empty */

		if (p_free)
			{
		 	IDRP_MEM_FIT_FREE(p_free);
			p_free = (idrp_ann_list *) NULL;
			}

		/* walk through the family loop setting
		 * the flag to non-empty if we find an entry
		 */

		NLRI_FAMILY_LOOP(i)
			{
			if ((p_atl->ann_nlri[i].head != (idrpRoute *) 0) 
				||  (p_atl->with_nlri[i].head != (idrpRoute *) 0))
				{
				empty = FALSE;
				break;
				}
				
			} NLRI_FAMILY_LOOP_END;


		if (empty) 
			{
			if (p_atl_last)
				{
				/* another entry exists on announce list
				 * set flag to free this entry
				 * and reset list to skip it
				 * leave last the same
				 */ 

				p_atl_last->p_next = p_atl->p_next;
				}
			p_free = p_atl;

			}
		else
			{
			/* if no re-adjust - reset the p_atl_latq
			 * if this is the first non-empty, set
			 * the initial pointer for the list
			 */
			if (!p_atl_last)
				{
				p_atl_first = p_atl;
				}	

			p_atl_last = p_atl;
			}
		}

	/* free up an empty list entry if it was the last one */
 
	if (p_free)
		{
		IDRP_MEM_FIT_FREE(p_free);
		}


	return (p_atl_first); 

}



void
free_ann_list(p_ann_list)
idrp_ann_list	*p_ann_list;
{
idrp_ann_list *p_atl,*p_atl2;

	if (p_atl == (idrp_ann_list *) NULL)
		{
		return;
		}
	p_atl = p_ann_list->p_next;
	while (p_atl)
		{
		p_atl2 = p_atl;
		p_atl = p_atl->p_next;
		IDRP_MEM_FIT_FREE(p_atl2);
		}
}

/* link_send_list - link a new send list on original
 * 
 */

int
link_send_list(p_send1,p_send_new)
idrp_send_list	*p_send1;
idrp_send_list	*p_send_new;
{
idrp_send_list	*p_send;
idrp_send_list	*p_send_last;

	p_send_last = p_send1;
	p_send = p_send1->p_next;

	/* loop to find the end of the send list */

	while(p_send)
		{
		p_send_last  = p_send;
		p_send = p_send->p_next;
		} 

	p_send_last->p_next = p_send_new;
	p_send_new->p_next = (idrp_send_list *) 0;
}


	
/* 
 * send_with_attr_replace 
 */

idrp_send_list *
send_with_replace(p_idrp_rt,p_att_dist,p_send_list,peer,flash)
idrpRoute		*p_idrp_rt;	/* idrpRoute with NLRI to be announced */ 
idrp_attribute_record	*p_att_dist;	/* attribute record distributed to this peer */
idrp_send_list		*p_send_list;	/* send list */
idrpPeer		*peer;		/* peer to send withdraw sequence to */
int			flash;
{
idrp_send_list	*p_tmp_send;
idrp_attribute_record	*p_att_rdist = 0;
idrpPeer	*p_this_peer;

	p_this_peer = &idrp_this_node;
/*  If the route was replace, but cannot be sent to the peer
 */


		
		trace_tf(idrp_trace_options, TR_PHASE3,0,("replace route %s peer %s not allowed replace route %s peer %s flash = %d",
			iso_ptoa(&p_idrp_rt->nlri),peer->name,iso_ptoa(&p_idrp_rt->p_replace->nlri),p_idrp_rt->p_replace->peer->name,flash));

		trace_tf(idrp_trace_options, TR_PHASE3,0,("replace route status p_idrp_rt( %x) p_replace(%x)",p_idrp_rt->status,p_idrp_rt->p_replace->status));
		if (p_send_list)
			{
			trace_tf(idrp_trace_options, TR_PHASE3,0,("withdraw count p_send_list %d )",p_send_list->withdraw->count));
			}



	if ((p_idrp_rt->peer->type == IDRP_PEER_INTERNAL) && (flash == IDRP_DIST_LOCAL_FLASH)) 
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0,("send_with_replace - internal distribution replace - ignore flash = %d",flash));
		return(p_send_list);
		}
	
	if ((p_idrp_rt->peer->type == IDRP_PEER_EXTERNAL) && (flash == IDRP_DIST_LOCAL_FLASH))
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0,("send_with_replace - internal distribute of best external override flash %d, nlri %s",
		flash,iso_ptoa(&p_idrp_rt->nlri)));
		return(p_send_list);
		}  
			/* here we've got to deal with the fact that
	 * - the route being replaced may have a different
	 *   attribute record
	 */

	p_att_rdist = DIST_ATTR(p_idrp_rt->p_replace->p_attr,peer);	
	if ((p_send_list != NULL) &&
		(p_send_list->pdu_bytes != 0) &&
		(p_send_list->p_attr != NULL))
		{
		/* send list - has been configured
		 * - does it have anything in it
		 */

		if (p_att_rdist != p_att_dist)
			{
			flush_att_send_list(peer,p_att_dist,p_send_list);
			}
		}

	p_tmp_send = send_with_attr(p_idrp_rt->p_replace,p_att_dist,p_send_list,peer,flash);

	if (p_tmp_send)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("withdraw count p_send_list %d )",p_tmp_send->withdraw->count));
		}
	else
		{
		trace_tf(idrp_trace_options, TR_NORMAL,LOG_ERR,("send_with_attr_replace call terminates with null send list"));
		}
	return(p_tmp_send);

}
	
/* 
 * send_with_attr - 
 */

idrp_send_list *
send_with_attr(p_idrp_rt,p_att_dist,p_send_list,peer,dist)
idrpRoute		*p_idrp_rt;	/* idrpRoute with NLRI to be announced */ 
idrp_attribute_record	*p_att_dist;	/* attribute record distributed to this peer */

idrp_send_list		*p_send_list;	/* send list */
idrpPeer		*peer;		/* peer to send withdraw sequence to */
int                    dist;                /* flash distribution flag */
{
int	route_id;
idrp_send_list	tmp_send;
Withdrawl	withdraw;
idrpPeer	*p_trace_peer;
idrpRoute	*p_irt;
idrpRoute	*p_irt_first;
idrpRoute	*p_irt_next;
int		pdu_bytes;
int		nlri_cnt = 0;
int		nlri_ann = 0;
int		nlri_early = 0;
int		nlri_repl = 0;
int		i;
int		nlri_id;
boolean		new_send_list = FALSE;

	p_trace_peer = &idrp_this_node;
	bzero(&tmp_send,sizeof(idrp_send_list));
	bzero(&withdraw,sizeof(Withdrawl));	
	tmp_send.withdraw = &withdraw;

	/* withdraw processing  belonged to A
	 * an outbound route_id that
	 * has already been processed
	 * - so skip it this time around
	 */
 

	IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_WITH_EARLY_PROC)	
		{	
		trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing of nlri %s",iso_ptoa(&p_idrp_rt->nlri)));
		trace_tf(idrp_trace_options, TR_NORMAL,0,("send_with_attr (%d,%d,%d,%d,%d,%d)",
			p_idrp_rt->route_out[0].route_id,
			p_idrp_rt->route_out[1].route_id,
			p_idrp_rt->route_out[2].route_id,
			p_idrp_rt->route_out[3].route_id,
			p_idrp_rt->route_out[4].route_id,
			p_idrp_rt->route_out[5].route_id));
		return(p_send_list);
		}

	/* idrp replace processing has already processed
	 * this route, so
	 *  - skip the processing
	 */

	IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_REPLACE)
		{
		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_REPLACE_PROC)
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing of replace nlri %s",iso_ptoa(&p_idrp_rt->nlri)));
			trace_tf(idrp_trace_options, TR_NORMAL,0,("send_with_attr no route route_out[0]= %d, route_out[1]= %d,route_out[2]=%d, route_out[3]=%d, route_out[4]=%d, route_out[5]=%d",
				p_idrp_rt->route_out[0].route_id,
				p_idrp_rt->route_out[1].route_id,
				p_idrp_rt->route_out[2].route_id,
				p_idrp_rt->route_out[3].route_id,
				p_idrp_rt->route_out[4].route_id,
				p_idrp_rt->route_out[5].route_id);
			return(p_send_list));
			}
		}
	

	if (p_idrp_rt->route_out[peer->id].p_next == (idrpRoute*) NULL)
		{
		/* no route announced out to this peer */
	

			
			trace_tf(idrp_trace_options, TR_PHASE3,0,("send_with_attr no route announced to peer %s(%d) nlri %s route_id %d ",
				peer->name,peer->id,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->route_out[peer->id].route_id));
			trace_tf(idrp_trace_options, TR_PHASE3,0,("send_with_attr no route route_out[0]= %d, route_out[1]= %d,route_out[2]=%d, route_out[3]=%d, route_out[4]=%d, route_out[5]=%d",
				p_idrp_rt->route_out[0].route_id,
				p_idrp_rt->route_out[1].route_id,
				p_idrp_rt->route_out[2].route_id,
				p_idrp_rt->route_out[3].route_id,
				p_idrp_rt->route_out[4].route_id,
				p_idrp_rt->route_out[5].route_id));
			
		if (p_idrp_rt->p_damped_clear != (idrpRoute *) NULL)
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0,(" send_with_attr damped route needs clearing on delete %s for peer %s (%d) id %d [peer %s sending]",
				iso_ptoa(&p_idrp_rt->nlri),
				p_idrp_rt->p_damped_clear->peer->name,
				p_idrp_rt->p_damped_clear->peer->id,
				p_idrp_rt->p_damped_clear->route_out[peer->id].route_id,
				p_idrp_rt->peer->name));
	 
			p_idrp_rt = p_idrp_rt->p_damped_clear;
			if (p_idrp_rt->route_out[peer->id].p_next == (idrpRoute*) NULL)
				{
				trace_tf(idrp_trace_options, TR_NORMAL,0,("nothing on the send list for damped route for peer %s (%d)",
					peer->name,peer->id));
				return(p_send_list);
				}
			}
		else
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("nlri %s p_damped_clear not set  peer %s (%d)",
				iso_ptoa(&p_idrp_rt->nlri),
				peer->name,peer->id)); 
			return(p_send_list);
			}
		}

	{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("p_idrp_rt = %x nlri = %s",p_idrp_rt,iso_ptoa(&p_idrp_rt->nlri)));
	trace_tf (idrp_trace_options, TR_NORMAL,0,("route_out[0].id=%d  route_out[1].id = %d route_out[2].id = %d, route_out[3].id = %d, route_out[4].id = %d, route_out[5].id = %d",
			p_idrp_rt->route_out[0].route_id, 
			p_idrp_rt->route_out[1].route_id, 
			p_idrp_rt->route_out[2].route_id, 
			p_idrp_rt->route_out[3].route_id, 
			p_idrp_rt->route_out[4].route_id, 
			p_idrp_rt->route_out[5].route_id));
	}

	/* this route was announced out so add the withdraw to
	 * to our outbound list
	 *
	 * build temporary send list for this node
	 * see if this withdraw list can be added to the 
	 * send list withdraw sequence 
 	 *   
	 *  
	 * 1) save this route_id in  
	 *
	 * 2) walk the outbound list chain until we reach this
	 *    this address again,
	 * 	2.1)  build send list of those NLRI which will still need to be added   
	 * 	2.2) after list is built, check list against the withdraw processing 
	 *	2.3) flag any routes found for this route_id with early processing
	 *	 	so they will be ignored in the loop
	 *		Inefficent, but stable. 
	 *
	 *     2.4) Force out the withdraw pdus with one sequence initially
	 *
	 * later - calculate pdus and see if can stuff max in 
	 * 
	 * (later) 
	 * 3) calculate the number of bytes that will need
	 *    to be used to add the nlri sequence to 
	 *
	 * 4) see if we can stuff this addition to the
	 *	the UPDATE pdu
 	 *
	 * 5) if can stuff in UPDATE PDU, then add withdraw ID
	 *	and nlri to send list and exit   
	 * 
	 * 6) if we cannot stuff this withdraw sequence
	 * 	then give this pdu off to the send_update_pdu
	 *	routine to send the pdu
	 */ 

        route_id = p_idrp_rt->route_out[peer->id].route_id;

	tmp_send.pdu_bytes = PFX_BYTE_LENGTH(p_idrp_rt->nlri);
	tmp_send.p_attr = p_idrp_rt->p_attr;  
	if (peer->type == IDRP_PEER_INTERNAL)
		{
		tmp_send.loc_pref = p_idrp_rt->pref_cal;
		}

	p_irt_next = p_idrp_rt->route_out[peer->id].p_next;

	/* bug hunting -- this specific case hosed the code at least once.  The assert down below
	 * (which looks for wdraw/replace counters to have been bumped) catches this too, but this
	 * pinpoints the cause more accurately.
	 *
	 * This assert can probably be nuked when this case has been fixed.
	 */

	assert(p_irt_next->route_out[peer->id].p_next != NULL);

	
	for (p_irt = p_idrp_rt; p_irt_next->route_out[peer->id].p_next != NULL; p_irt = p_irt_next)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("send_with_attr loop p_irt = %s id = %d",iso_ptoa(&p_irt->nlri),p_irt->route_out[peer->id].route_id));  
		nlri_cnt++;

		/* 1) set this idrpRoute to the last idrproute
	 	 *   pointer so can check for circular list
		 * 2) set to next link in chain
		 * 3) make sure route_id is equal to current
		 * 4) add pdu bytes to end
		 */ 
	 
		p_irt_next = p_irt->route_out[peer->id].p_next;
		p_irt->route_out[peer->id].p_next = (idrpRoute *) 0;  
		p_irt->route_out[peer->id].p_back = (idrpRoute *) 0;

		/* bug hunting -- looking for malformed route_out chains.
		 *
		 * we always want to start out with a fully linked circular route_out chain, so 
		 * p_irt_next should never go to NULL (though next->next may; indeed, this
		 * is the termination condition).
		 */
		assert(p_irt_next != NULL);

		/* go through the list until you get to
	 	* the original node 
	 	* - only link out nlris without withdraw set  
		*/

		if ((p_irt->status & IDRP_STATUS_WITH) == 0)  
			{
			nlri_id = family_to_nlri_id(p_irt->family);

			/* make sure attribute record is filled in
			 * - with attribute pointer and 
			 *   preference if local peer
			 */

			IDRP_STATUS_BIT_TEST(p_irt,IDRP_STATUS_REPLACE)
				{
				IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_REPLACE_PROC);
				nlri_repl++;
				p_irt->route_out[peer->id].route_id = 0;	
				}
			else	
			        {
				  /* if this is to be distributed unmodified - you can process it
				   * here - otherwise the announce list needs to handle it
				   */

				if (dist_unmod(p_irt,peer,dist))
				    {
				    nlri_ann++;
			       

				    if (tmp_send.ann_nlri[nlri_id].head == (idrpRoute *) NULL)
					{
					/* set-up the linked list head */
	
					tmp_send.ann_nlri[nlri_id].head = p_irt;
					tmp_send.ann_nlri[nlri_id].tail = p_irt;
					}
				    else
					{
					/* list already set-up - so just add to tail of list */
	
					tmp_send.ann_nlri[nlri_id].tail->route_out[peer->id].p_next = p_irt;
					p_irt->route_out[peer->id].p_back = tmp_send.ann_nlri[nlri_id].tail;
					tmp_send.ann_nlri[nlri_id].tail = p_irt;
					}
				   tmp_send.pdu_bytes = tmp_send.pdu_bytes + PFX_BYTE_LENGTH(p_irt->nlri); 
				   p_irt->route_out[peer->id].route_id = peer->route_id_out;
                                   IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_WITH_EARLY_PROC);
                                  trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing set for nlri %s",iso_ptoa(&p_irt->nlri)));
				
				}
			    }
			}
		else
			{
			if (route_id ==  p_irt->route_out[peer->id].route_id)
				{
				/* early processing of another route on the chain */
				
				IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_WITH_EARLY_PROC);
				trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing set for nlri %s",iso_ptoa(&p_idrp_rt->nlri)));
				nlri_early++;
				}
			p_irt->route_out[peer->id].route_id = 0;	
			}
		
		/* check to make sure route_id is equal or links screwed up
		 */
	
		if ((route_id != p_irt_next->route_out[peer->id].route_id) &&
			(p_irt_next->route_out[peer->id].p_next != NULL))
			{
			trace_tf(idrp_trace_options, TR_NORMAL,LOG_ERR,("outbound route linked list screwed up peer %s route_id %d new id %d", 
				peer->name,
				route_id,   /* !!! */
				p_irt->route_out[peer->id].route_id));
			return(p_send_list);
			}
		}

	trace_tf (idrp_trace_options, TR_NORMAL,0,
		("%d nlri processed for withdraw route_id %d peer %s.  ONLY %d (repl %d)  re-ann early proc %d routes", 
		nlri_cnt,
		route_id,   /* !!! */
		peer->name, nlri_ann , nlri_repl,nlri_early));

	{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("route_out[0].id=%d  route_out[1].id = %d route_out[2].id = %d, route_out[3].id = %d, route_out[4].id = %d, route_out[5].id = %d",
			p_idrp_rt->route_out[0].route_id, 
			p_idrp_rt->route_out[1].route_id, 
			p_idrp_rt->route_out[2].route_id, 
			p_idrp_rt->route_out[3].route_id, 
			p_idrp_rt->route_out[4].route_id, 
			p_idrp_rt->route_out[5].route_id));
	} 
 	
	/* add in bytes for withdraw ID */

	tmp_send.pdu_bytes += sizeof(route_id);
   
	/* finished the output loop 
	 * 1.) Does send list have pdu_bytes_attr set?
	 * 	-no - set attribute record stuff in send list
	 *  
	 * 2.) do we have enough room in the pdu
	 *	- if send list is new, increment send list
	 *	- for attribute byte list 
	 * 
	 * 3.) if no more room (idrp_send_list_room_pdu)
	 *		(idrp_send_list_room- governs how much
	 *		 gets stuffed in an IDRP BISPDU UPDATE)
	 * 
	 *		a.) send pdu with current list
	 *		b.) put temporary send list into running send list
	 * 
	 * 4.) if enough room (idrp_send_list_room)
	 *		a.) add temporary send list to permanent send list
	 *
	 */

	if (!p_send_list) {
		/* no send list exists yet - so set it up
		 * with attributes record, rib id, attribute length
		 */

		if (p_att_dist == (idrp_attribute_record *) 0)
			{
			p_send_list = create_send_list(tmp_send.p_attr,peer);
			}
		else
			{
			p_send_list = create_send_list(p_att_dist,peer);
			}
		new_send_list = TRUE;
		}	
	
	/* We had better be withdrawing at least one thing!
	 */
	assert(nlri_repl | nlri_early);

	/* We use a subscript of 1 since we'll only have one w/draw at a time on tmp list
	 */
	tmp_send.withdraw->withdraw[1] = route_id;

	if (idrp_send_list_room(p_send_list,&tmp_send,new_send_list))
		{
		/* enough room in pdu - so stuff in the withdraw id
		 * and nlri routes
		 */

		p_send_list->withdraw->withdraw[p_send_list->withdraw->count++] = route_id;
		p_send_list->pdu_bytes = p_send_list->pdu_bytes + tmp_send.pdu_bytes;
		p_send_list->loc_pref = tmp_send.loc_pref;
		if (p_send_list->p_attr == (idrp_attribute_record *) NULL)
			{
			p_send_list->p_attr = tmp_send.p_attr;
			}

		if (nlri_ann)
			{
			/* loop adding all NLRI per family */

			NLRI_FAMILY_LOOP(i)
				{
				/* add the nlris to the linked list
				 * 
				 */
	
				if (p_send_list->ann_nlri[i].head)
					{
					/* link list for routes set-up 
					 * so just add to the tail 
					 */

					p_send_list->ann_nlri[i].tail->route_out[peer->id].p_next = tmp_send.ann_nlri[i].head;
					tmp_send.ann_nlri[i].head->route_out[peer->id].p_back = p_send_list->ann_nlri[i].tail;
					}
				else
					{
					p_send_list->ann_nlri[i].head = tmp_send.ann_nlri[i].head;
					}	
				p_send_list->ann_nlri[i].tail = tmp_send.ann_nlri[i].tail;
				}
			}
		}
	else
		{
		/* send out the pdu */
		trace_tf (idrp_trace_options, TR_NORMAL,0,("send_with_attr(): sending out 2nd withdraw in pdu send_with_nlri peer %s ",peer->name)); 

		send_update_reset_send_list(peer,p_send_list,&tmp_send);
		}
	return(p_send_list);
}

void
send_update_reset_send_list(peer,p_send_list,p_tmp_send)
idrpPeer        *peer;
idrp_send_list  *p_send_list;
idrp_send_list  *p_tmp_send;
{
int     i;
int     only_unreachables;
idrpRoute	*p_irt;

	/* not enough room in this pdu - so send the
	* UPDATE pdu and go on
	* Note that every time the upper routines
	* change  attribute records , the
	* rest of the send list will be forced out
	*/


	only_unreachables = TRUE;
	NLRI_FAMILY_LOOP(i)
		{
		if (p_send_list->ann_nlri[i].head != (idrpRoute *) 0)
			{
			/* empty list */
			only_unreachables = FALSE;
			break;
			}
		} NLRI_FAMILY_LOOP_END

	
	send_update_pdu(peer,p_send_list,only_unreachables);

	/* clear out linked list of send lists */

	if (!p_tmp_send)
		{
		bzero(p_send_list,sizeof(idrp_send_list));
		bzero(p_send_list->withdraw,sizeof(Withdrawl));
		return;
		}

	p_send_list->p_next = (idrp_send_list *) 0;
	p_send_list->p_attr = p_tmp_send->p_attr;
	p_send_list->rib_id = p_tmp_send->rib_id;
	p_send_list->pdu_bytes = p_tmp_send->pdu_bytes;
	p_send_list->loc_pref = p_tmp_send->loc_pref;
	NLRI_FAMILY_LOOP(i)
		{
		/* 1) rest the head and tail of the announce nlri attributes
		 * 2) set all these temporary routes  to the next route_out id 
		 *    for this peer
		 * 
		 */  
		p_send_list->ann_nlri[i].head = p_tmp_send->ann_nlri[i].head;
		p_send_list->ann_nlri[i].tail = p_tmp_send->ann_nlri[i].tail;

		} NLRI_FAMILY_LOOP_END

	p_send_list->withdraw->count = p_tmp_send->withdraw->count;
	for (i = 0; i < p_send_list->withdraw->count; i++)
		{
		p_send_list->withdraw->withdraw[i] = p_tmp_send->withdraw->withdraw[i];
		}		 	
}




idrp_send_list *
send_nlri_attr (p_idrp_rt,p_att_dist,p_send_list,peer)
idrpRoute		*p_idrp_rt;	/* idrpRoute with NLRI to be announced */ 
idrp_attribute_record 	*p_att_dist;	/* attribute record to be distributed */ 
idrp_send_list		*p_send_list;	/* send list */
idrpPeer		*peer;		/* peer to send UPDATE to */
{
int	i;
int	id = 0;
idrp_send_list		tmp_send;	/* send list */
Withdrawl		withdraw;	/* withdraw array */
boolean			new_send_list = FALSE; /* new send list */


	/* nlri processing  belonged to A
	 * an outbound route_id that
	 * has already been processed
	 * - so skip it this time around
	 */

	IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_WITH_EARLY_PROC)	
		{	
		trace_tf(idrp_trace_options, TR_NORMAL,0,(" (send_nlri_attr)early processing of nlri %s",iso_ptoa(&p_idrp_rt->nlri)));
		trace_tf(idrp_trace_options, TR_NORMAL,0,("send_nlri_attr (%d,%d,%d,%d,%d,%d)",
			p_idrp_rt->route_out[0].route_id,
			p_idrp_rt->route_out[1].route_id,
			p_idrp_rt->route_out[2].route_id,
			p_idrp_rt->route_out[3].route_id,
			p_idrp_rt->route_out[4].route_id,
			p_idrp_rt->route_out[5].route_id));
		return(p_send_list);
		}


	/* The send_nlri_attr and send_with_attr routines are
 	 *   threaded to a single attribute
	 *   during  upgrade of code to IS ballot this send
	 *   routines will be changed to allow multiple attribute  
	 */ 


	id = family_to_nlri_id(p_idrp_rt->family);

	/* reset the current route_id out for this
	 * list to link around this nlri
	 *
	 * jgs -- why do we only do this if next is not NULL?  Next should NEVER be null, no?
	 * jgs -- perhaps we should assert that 
	 * jgs --      (p_idrp_rt->route_out[peer->id].p_next != (idrpRoute *) NULL)
	 */
	
	if (p_idrp_rt->route_out[peer->id].p_next != (idrpRoute *) NULL)
		{
		idrp_rem_relink_outlist(p_idrp_rt,peer->id);		
		}
	
	/* set-up temporary send list  */

	bzero(&tmp_send,sizeof(idrp_send_list));
	tmp_send.p_attr = p_idrp_rt->p_attr;
	tmp_send.rib_id = p_idrp_rt->p_attr->rib_id;
	tmp_send.pdu_bytes_attr = p_idrp_rt->p_attr->attr_len;	
	tmp_send.pdu_bytes = PFX_BYTE_LENGTH(p_idrp_rt->nlri);
	tmp_send.ann_nlri[id].head = tmp_send.ann_nlri[id].tail = p_idrp_rt;
	tmp_send.withdraw = &withdraw;
	tmp_send.withdraw->count = 0;	
	tmp_send.loc_pref = 0;
	if (peer->type  == IDRP_PEER_INTERNAL)
		{
		if (p_idrp_rt->pref_cal > 255)
			{
			tmp_send.loc_pref = (byte) 0xff;
			}
		else
			{ 
			tmp_send.loc_pref = (byte) p_idrp_rt->pref_cal;
			}
		}
	/* if no send list - allocate it */

	if (!p_send_list)
		{
		/* no existing send list allocate  a send list structure 
		 * - and fill it with  
		 */

		if (p_att_dist == (idrp_attribute_record *) NULL)
			{
			/* if the distribution attribute is
			 * zero because we are processing
			 * attributes - substitute
			 * the attribute here
			 */

			p_send_list = create_send_list(tmp_send.p_attr,peer);
			}
		else
			{
			/* the distribution attribute is set
			 * - so we do have an active
			 *   re-setting of the attribute record
			 */

			/***/ p_send_list = create_send_list(p_att_dist,peer);
			}
		new_send_list = TRUE;
		}

	/* see if this nlri overflows the pdu - if so send pdu */

	if (idrp_send_list_room(p_send_list,&tmp_send,new_send_list))
		{
		/* room to stuff in this nlri
		 * - so add it to announce tail 
		 * (here's where the code needs to
		 *  change for multiple attributes per
		 *  the send list.  The code here
		 *  needs to search for right attribute in pdu 
		 *  idrp_send_list will be multiple records 
		 *  with the combination being sent out)
	 	 * 
		 */
		p_send_list->pdu_bytes = p_send_list->pdu_bytes + tmp_send.pdu_bytes;
		p_send_list->p_attr = tmp_send.p_attr;
		p_send_list->rib_id = tmp_send.rib_id; 
		p_send_list->loc_pref = tmp_send.loc_pref;

		if (p_send_list->ann_nlri[id].head)
			{
			p_send_list->ann_nlri[id].tail->route_out[peer->id].p_next = p_idrp_rt;
			p_idrp_rt->route_out[peer->id].p_back = p_send_list->ann_nlri[id].tail;
			p_send_list->ann_nlri[id].tail = p_idrp_rt;
			}
		else	
			{
			p_send_list->ann_nlri[id].head = p_idrp_rt;
			p_send_list->ann_nlri[id].tail = p_idrp_rt;
			p_idrp_rt->route_out[peer->id].p_back = (idrpRoute *) 0;
			}
		p_idrp_rt->route_out[peer->id].route_id = peer->route_id_out;
		p_idrp_rt->route_out[peer->id].p_next = (idrpRoute *) 0;
		}
	else
		{
		
		/* send update and reset send list */
		send_update_reset_send_list(peer,p_send_list,&tmp_send);
		}

	return (p_send_list);
}


idrp_send_list *
create_send_list(p_att_dist,peer)
idrp_attribute_record 	*p_att_dist;
idrpPeer		*peer;
{
idrp_send_list *p_send_list;
int		i;

	p_send_list = (idrp_send_list *) idrp_local_mem_fit(sizeof(idrp_send_list));
	bzero(p_send_list, sizeof(idrp_send_list));
	p_send_list->p_attr = p_att_dist;
	p_send_list->pdu_bytes_attr = p_att_dist->attr_len;
	p_send_list->rib_id = p_att_dist->rib_id;
	p_send_list->withdraw = (Withdrawl *) idrp_local_mem_fit(sizeof(Withdrawl));
	bzero(p_send_list->withdraw, sizeof(Withdrawl));
	NLRI_FAMILY_LOOP(i)
		{
		p_send_list->ann_nlri[i].head = p_send_list->ann_nlri[i].tail = (idrpRoute *) 0;
		}
	return(p_send_list);
}

void
free_send_list(p_send_list,peer)
idrp_send_list	*p_send_list;
idrpPeer	*peer;
{

	/* this is a simple task mem free
	 *  - if send list becomes a linked list
	 *  - then this call must be changed to
	 *  - free the linked list
	 *  -- skh 3/10/93
	 */

	if (p_send_list)
	{
		if (p_send_list->withdraw)
		{
		IDRP_MEM_FIT_FREE(p_send_list->withdraw);
		}
		IDRP_MEM_FIT_FREE(p_send_list);
	}
}
	

void
flush_att_send_list(peer,p_att_dist,p_send_list)
idrpPeer		*peer;
idrp_attribute_record	*p_att_dist;
idrp_send_list		*p_send_list;
{
u_int	only_unreachables = TRUE;
int	i;


	/* if no send list - don't flush it or it will flush you */

	if (!p_send_list)
		{
		return;
		}

	/* check for only unreachables */

	NLRI_FAMILY_LOOP(i)
		{
		if (p_send_list->ann_nlri[i].head != (idrpRoute *) 0 )
			{
			only_unreachables  = FALSE;
			break;
			}
		}

	/* check for only unreachables and no withdraws */

	if (only_unreachables && (p_send_list->withdraw->count == 0))
		{
		/* flag an unreachable without a withdraw count
		 */
		idrpPeer *p_this_node = &idrp_this_node;	

			
			trace_tf (idrp_trace_options, TR_PHASE3,0,("only unreachables and count zero"));
			
		return;
		} 

	if (!only_unreachables)
		{

		/* here I am looking for a broken send list
		 * - no attribute record
		 */

		if (p_send_list->p_attr == (idrp_attribute_record *) NULL)
			{
			trace_tf(idrp_trace_options, TR_NORMAL,LOG_ERR,("no attribute record listed flush_send_list"));
			task_quit(0);
			}
		}
	
		

	trace_tf (idrp_trace_options, TR_NORMAL,0,("flush attribute send list routine entered "));
	send_update_pdu(peer,p_send_list,only_unreachables);

	/* finished update, clear the send list */

	p_send_list->p_next = (idrp_send_list *) 0;
	p_send_list->p_attr = (idrp_attribute_record *) 0;
	p_send_list->pdu_bytes = 0;
	p_send_list->pdu_bytes_attr = 0;
	NLRI_FAMILY_LOOP(i)
		{
		p_send_list->ann_nlri[i].head = p_send_list->ann_nlri[i].tail = (idrpRoute *) 0;
		}	

	p_send_list->withdraw->count = 0;

}


boolean
idrp_send_list_room(p_list,p_new,new_send_list)
idrp_send_list	*p_list;
idrp_send_list	*p_new;
u_int		new_send_list;
{
	/* check for first time use of send list
	 */

	if (new_send_list != 0)
		{
		/* first time use return - just fine
		*/

		return (TRUE);
		} 	
	
	/* if the new routes do not have the
	 * same preference - cut out this
	 * packet and send the next one.
	 */	


 	if (p_list->loc_pref != p_new->loc_pref)
		{
		return(FALSE);
		}	

	/* this is where the logic lies to pack in
	 *
	 * - in the future it will try to package as
	 * - many sequences as possible
	 */

	if (p_new->withdraw->count)
		{
		if (p_list->pdu_bytes + p_new->pdu_bytes + (p_new->withdraw->count*4) < (IDRP_PKTSIZE - sizeof(idrp_fixed_pdu_header)))
			{
			return(TRUE);
			}
		}
	
	if (p_list->pdu_bytes + p_new->pdu_bytes < (IDRP_PKTSIZE - sizeof(idrp_fixed_pdu_header)))
		{
		return (TRUE);
		}
			
	
	return (FALSE);
} 


void
idrp_del_sent_routes(p_ann_list)
idrp_ann_list *p_ann_list;
{
int		changes = 0;	/* changes to route table */
int			i;
idrp_ann_list		*p_atl;
idrpRoute		*p_irt;

	/* walk through the announce list building 
	 * the send list   
	 */

	rt_open(idrp_this_node.task);
	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{

			/* the location of the walk routine has to be 
			 * be saved in case the p_irt->p_with is cleared in the
			 * route
			 */

			idrpRoute *p_irt_next;
			ANN_WITH_LIST_DEL_WALK(p_irt,i,p_atl,p_irt_next)
				{
				/* delete the routes with IDRP_STATUS_DEL_SEND 
				 *
				 */

				if (p_irt->status & IDRP_STATUS_DEL_SEND)
					{
					if (!IS_IDRP_DAMPED_ROUTE(p_irt))
						{
						idrp_free_outlist(p_irt);
						idrp_free_nlri_att_rec(p_irt,NLRI_LINKED_P_WITH);
						idrp_del_rt_gated(p_irt,IDRP_RESET_RT_BIT);
						idrp_free_idrpRoute(p_irt);
						}
					else
						{
						IDRP_STATUS_BIT_SET(p_irt, IDRP_STATUS_DELETE);
						idrp_del_rt_gated(p_irt,IDRP_NO_RESET_RT_BIT);
						}
					changes++;
					}
				} ANN_WITH_LIST_DEL_WALK_END;
			} NLRI_FAMILY_LOOP_END;
		} ANN_LIST_END;

	rt_close(idrp_this_node.task,&idrp_this_node.gw,changes,NULL);

	/* trace that we've deleted withdrawn routes */
	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp_del_sent_routes(): %d withdrawn routes idrp routes deleted ",changes));
}




/* idrp_add_locate -
 * 	calculate idrp preference
 *	calculate gated preferences
 *
 * 	walk nlri list to 	
 *	Set the gated preference and add to tables
 * 		- as change or add 
 *	so idrp decision process 2 needs to be run  	
 */

int
idrp_add_route_locate(p_dest,p_idrp_rt,p_gate,p_pref,p_best_ext)
sockaddr_un		*p_dest;	/* destination structure */
idrpRoute		*p_idrp_rt;	/* route */
idrp_rt_chain_walk	*p_gate;	/* return chain for matched gateways */
idrp_rt_chain_walk	*p_pref;	/* return chain for routes with same IDRP pref */ 
idrpRoute		**p_best_ext;	/* best external route  */
{
rt_entry	*p_rt,*p_rt_first;	/* rt_entry for gated route table entry */
idrpRoute	*p_irt;		/* idrpRoute */ 
idrpPeer	*peer;		/* peer idrpRoute received from */
int		type;		/* type of peer route came from */
				/* internal, external, local (this node) */
				/* %%%% future - test to mirror bgp */
gw_entry	*rt_gwp;	/* gateway for gated */
sockaddr_un	*p_mask;	/* mask for gated route hacking  */
int		dest_passed = 0;	/* flag for destination passed */
				
		 
			
	
	/* Note you MUST USE THE GATED MASK - Why?
	 * - the radix compare does a pointer compare
	 * - not a bit by bit compare 
	 * It's really not a bad way to do it. But I'm just frosted from
	 * having to learn it during the debugging of the code.
	 * I guess it is too much to expect the 'old boys
	 * to tell me about it.  It must be some test
	 * of sorts to enter the club to have to figure it
	 * out from the code. 
	 * 
	 * dest_passed - sockaddr for destination
	 *		 must be passed into this route
	 * 
	 * 		Why? - we still have
	 * 		osilocal routes
	 *	        whose memory comes out of task allocation core
	 * 		
	 *
	 *   
	 */   

        trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));

	if (p_dest != (sockaddr_un *) NULL)
		{
		dest_passed = TRUE;
		} 

	/* logic of adding route is:
	 *  1.) calculate IDRP preference and gated preference for new route
	 *  2.) determine type of peer route came from: 
	 *      - external or internal, local (this node), test
	 *  3.) try to get any match on the nlri and protocol
	 *	via rt_locate call to gated 
	 *  4.) if no for idrp, set flags
	 *	  no gateway match
	 *	  no preference match
	 *	  no old best_ext (nulled out if the p_ext chain pointer is null )
	 *
	 *  5.) if return, walk the rest of the chain
	 *	to find:
	 *	 if gateway match
	 *	 if preference match, link matched preference
	 *	   on an idrpRoute list - using preference as route_id
	 *	 if new route is external, 	
	 *		link any external route to idrpRoute_list
	 *		(nulled out if the p_ext chain pointer is null 
  	 * 
 	 * chains to the caller:
	 */ 

	/* initialize the idrp chain walking structures */

	bzero(p_gate,sizeof(idrp_rt_chain_walk));
	bzero(p_pref,sizeof(idrp_rt_chain_walk));
	*p_best_ext = (idrpRoute *) 0;	/* no best external best external route  */

	/* calculate IDRP default preference for route */
	/* and put it in pref_cal part of idrpRoute structure */

	idrp_def_pref(p_idrp_rt);
        trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));

	/* calculate gated preference for route */
	/* this is a phase 2 calculation, but not action */

	p_idrp_rt->gated_pref = idrp_to_gated_pref(p_idrp_rt);
	peer = p_idrp_rt->peer;
		
	/* set-up masks to either first locate, then add the route
	 * - recalculate the byte length to force check on copy
	 */ 

	isopfx_sock_mask_pair(p_idrp_rt,&p_dest,&p_mask);
		
	/* here add in the look-up in the rt_table to find match for gateway */
	/* to find exact match and best match */

	p_rt = rt_locate(RTS_NETROUTE,p_dest,p_mask,RTPROTO_BIT(RTPROTO_IDRP));

	/* if nothing was return - no nlri with IDRP protocol */

	if (!dest_passed)
		{
		IDRP_MEM_FIT_FREE(p_dest);
		}
	
	if (!p_rt)
		{
		/* set rt_entry pointers to zero for gate, preference 
		* and the external if it is called
		*/ 

		p_gate->p_rt = (rt_entry *) 0;
		p_pref->p_rt = (rt_entry *) 0;
		return(FALSE);
		}

	/* walk the rest of nlri structure 
	 * looking for gateway match, best_ext 
	 * and preference matches 
	 * -- We will know we've hit the end of the
	 * -- IDRP route structure when we use the
	 * -- same test as the test from
	 *--- gated
	 * * Scan all routes for this destination 
	 * #define RT_ALLRT(rt, rth)       { for (rt = (rth)->rt_forw; rt != (rt_entry *) &(rth)->rt_forw; rt = rt->rt_forw)
	 * #define RT_ALLRT_END(rt, rth)   if (rt == (rt_entry *) &(rth)->rt_forw) rt = (rt_entry *) 0; }
	 *
	 */

	while (p_rt != (rt_entry *) &p_rt->rt_head->rt_forw)
		{
		if (IS_AN_IDRP(p_rt) || IS_LOCAL_IDRP(p_rt))	
			{
			if (p_rt->rt_idrp) 
			    {
			    /* idrp route */

			    if ((!(BIT_TEST(p_rt->rt_state, (RTS_DELETE)))) &&
				 (p_rt->rt_idrp->p_attr->rib_id == p_idrp_rt->p_attr->rib_id))
				{
				/* only want to pay attention to route if it hasn't been marked as deleted 
				 * and it is of the same rib
				*/

				if (p_rt->rt_gwp->gw_addr == peer->gw.gw_addr || p_rt->rt_gwp->gw_addr == peer->iso_gw.gw_addr)
					{
					/* matching gateway, because pointers match save on list */
	
					p_gate->p_rt = p_rt;
					p_gate->p_next = (idrp_rt_chain_walk *) idrp_local_mem_fit (sizeof(idrp_rt_chain_walk));
					p_gate->p_next->p_next = 0;
					p_gate->p_next->p_rt = 0;
					p_gate = p_gate->p_next; 
					}
				else
					{
					/* if this route is not from the same gateway, check preference and */
					/* external route */
	
					/* compare pref on existing route to pref on our new route
				 	 * use idrp route pointer (rt_idrp) 
					 */
					p_irt = (idrpRoute *) p_rt->rt_idrp; 
					
					if (p_irt->pref_cal == p_idrp_rt->pref_cal)
						{
						/* equal gated preferences - link to list of equiv pref rts */

						p_pref->p_rt = p_rt;
						p_pref->p_next = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
						p_pref->p_next->p_next = 0;
						p_pref->p_next->p_rt = 0;
						p_pref = p_pref->p_next;
						}
	
					/* We add external routes to BER chain; we also add local routes to the chain to
					 * prevent external routes from replacing local routes (unless they actually should).
					 */
					if ((p_irt->peer->type == IDRP_PEER_EXTERNAL) || (p_irt->peer->type == IDRP_PEER_LOCAL))
						{	
						/*  find best external - not should come back null 
						 *
						 */
						if ((p_irt->status & IDRP_STATUS_RDLOOP) == 0)
							{
							if (p_irt->p_p_best_ext != NULL)
								{
								trace_tf (idrp_trace_options, TR_NORMAL,0,(" add_route_locate: find best_ext %s ",iso_ptoa(&p_idrp_rt->nlri))); 
								*p_best_ext = IDRP_BER(p_irt);
								assert((*p_best_ext)->p_p_best_ext != NULL);
								}
							else
								{
								trace_tf (idrp_trace_options, TR_NORMAL,0,(" add_route_locate: p_irt->p_p_best_ext == null %s(%x)(p_irt = %x)",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt,p_irt)); 

								}	
							}
						} /* end test for external  */
					} /* end test for matching gateway */ 
				    } /* end test for not deleted */
				} /* end of test for idrp route existing */

			else
				{
				/* if we have an idrp route without a 
				 * an idrp route Route - it better be
				 * scheduled for release by gated and just
				 * not have the timer expired.
			 	 * test and flag any thing that does not match that
				 *
				 */


				if ((p_rt->rt_state & RTS_RELEASE) == 0 )
					{ 
					/* NOT the release 
					 */
			
					trace_tf (idrp_trace_options, TR_NORMAL,LOG_ERR,("nlri: %s has p_rt->rt_idrp = 0 and not in release",
						iso_ptoa(&p_idrp_rt->nlri)));
					}
				} /* end of the logic to handle p_rt->rt_idrp = 0 */
	
					 
			} /* end bit test for the idrp route - */

		p_rt = p_rt->rt_forw;
		} /* loop walking down the rest of the rout entries */ 

        trace_tf (idrp_trace_options, 0,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));

	return(TRUE);
}


/* idrp_with_route_locate-
 * 	calculate idrp preference
 *	calculate gated preferences
 *
 * 	walk nlri list using p_with parameter 
 *	Set the gated preference and add to tables
 * 		- as change or add 
 *	so idrp decision process 2 needs to be run  	
 */

int
idrp_with_route_locate(p_dest,p_idrp_rt,p_gate,p_pref,p_best_ext)
sockaddr_un		*p_dest;	/* destination structure */
idrpRoute		*p_idrp_rt;	/* route */
idrp_rt_chain_walk	*p_gate;	/* return chain for matched gateways */
idrp_rt_chain_walk	*p_pref;	/* return chain for matched idrp preferences */ 
idrpRoute		**p_best_ext;	/* best external route  */
{
int	found;
idrpPeer	*peer;

	peer = &idrp_this_node;
	found =  idrp_add_route_locate(p_dest,p_idrp_rt,p_gate,p_pref,p_best_ext);
	if (!found)
		{
		trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp_with_route_locate:  No IDRP routes for NLRI %A", p_dest));
		return(FALSE);
		}
	
	/* This is the real code to find the best external route.  Dead easy.
 	 *   just grab the best external from the list 
   	 */


		
		trace_tf (idrp_trace_options, TR_IDRP_DEBUG,0,(" with_route_locate: find best_ext %s ",iso_ptoa(&p_idrp_rt->nlri))); 
		

	return (TRUE);	/* ---skh return value - true if route found */
}


int 
idrp_rt_change(p_rt,p_idrp_rt)
rt_entry *p_rt;
idrpRoute *p_idrp_rt;
{
idrpRoute *p_idrp_rt2;
int	change = FALSE;

	/* copy over changes to the rt_entry data structure */

	p_idrp_rt2 = (idrpRoute *)p_rt->rt_idrp;

	/* walk down and find out what is different between the two routes */

	if (p_idrp_rt2 == (idrpRoute *) NULL)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,LOG_ERR,("match for idrp_rt_change peer %s to null route - ERROR!!",
			p_idrp_rt->peer->name));
		return(change);
		}

	if (p_idrp_rt2->peer != p_idrp_rt->peer)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,LOG_ERR,("match for idrp_rt_change peer %s to peer %s - ERROR!!",
			p_idrp_rt->peer->name,p_idrp_rt2->peer->name));
		return(change);
		}

	/* check on attribute record change */

	if (p_idrp_rt2->p_attr != p_idrp_rt->p_attr)
		{
		change = TRUE;	
		}

	if (p_idrp_rt2->route_id_in != p_idrp_rt->route_id_in ||
		p_idrp_rt2->route_id_in == IDRP_LOCAL_ROUTE_ID)
		{
		change = TRUE;
		}
	return (change);

}

/* free_rt_chain_walk
 *
 */

void
free_rt_chain_walk(p_ch,peer)
idrp_rt_chain_walk	*p_ch;
idrpPeer		*peer;
{
idrp_rt_chain_walk	*p_free;
idrp_rt_chain_walk	*p_next;


	for (p_next = p_ch, p_free = p_ch; p_next; p_free = p_next)
		{
		p_next = p_free->p_next;
		IDRP_MEM_FIT_FREE(p_free);
		}
return;
}

/*
 * find_next_best(p_idrp_rt) - find the next-best external route
 *	to the same NLRI as the external route p_idrp_rt
 *	points to.  Return a pointer to it.
 */
idrpRoute *
find_next_best(p_idrp_rt)
idrpRoute *p_idrp_rt;
{
	if (!(p_idrp_rt->status & IDRP_STATUS_BEST_EXT))
		{
		trace_tf (idrp_trace_options, TR_NORMAL, LOG_WARNING, ("IDRP (find_next_best) route is not best external"));
		return NULL;
		}
		
	return p_idrp_rt->p_next_best;
}
			
/*
 * find_best_ext(p_rt) - if the NLRI of p_rt has a best external IDRP route,
 * find it and return a pointer to it.  Otherwise, return NULL.
 *
 * This code is largely scavenged from add_route_locate.  If (when) we go
 * to keeping the p_best_ext in the rt_head, we will be able to turn this
 * into a simple pointer dereference instead of a loop.
 *
 * If we don't go to keeping p_best_ext in rt_head, we still need to go over
 * this code, which is currently a horrible hack.
 */
idrpRoute *
find_best_ext(p_rt1,p_idrp_rt)
rt_entry *p_rt1;
idrpRoute	*p_idrp_rt;
{
rt_entry	*p_rt = p_rt1->rt_head->rt_forw;	/* I hope this sets us to the first route in the NLRI chain, yuk */

	/* we'll use p_rt to traverse the list of routes on this NLRI chain until we find
	 * one with a BER or we reach the end 
	 */

	while (p_rt != (rt_entry *) &p_rt->rt_head->rt_forw)
		{
		if ((p_rt != p_rt1) && (IS_AN_IDRP(p_rt) || IS_LOCAL_IDRP(p_rt)) && 
			(p_rt->rt_idrp->p_attr->rib_id == p_idrp_rt->p_attr->rib_id) && 
 			(p_rt->rt_idrp->p_p_best_ext != NULL))
			{
			    /* idrp route with best external set */

			    if (!(BIT_TEST(p_rt->rt_state, RTS_DELETE)))
				{
				/* only want to pay attention to route if it hasn't been marked as deleted */

					if ((p_rt->rt_idrp->peer->type == IDRP_PEER_EXTERNAL) || (p_rt->rt_idrp->peer->type == IDRP_PEER_LOCAL))
						{	
						return(IDRP_BER(p_rt->rt_idrp));
						} /* end test for external  */
			    	} /* end test for not deleted */
			 } /* end test for idrp route */
		p_rt = p_rt->rt_forw;
		} /* loop walking down the rest of the rout entries */ 

	/* no BER was found */
	return(NULL);
}

/*
 * idrp_pref_compare(idrpRoute *p_idrp_rt, idrpRoute *p_best_ext)
 *
 * Description:  Return TRUE if p_idrp_rt is the best external route
 *  and FALSE otherwise.  Do this by comparing to p_best_ext,
 *  the current best external (or NULL if there is no best
 *  external to this destination).
 *
 * Discussion:  The algorithm used (just compare to current BER) 
 *  assumes that the tie-break algorithm imposes a complete ordering
 *  on routes to the same destination.  I can't imagine how it could
 *  just be a partial ordering, so I think we're okay.
 * 
 *  We assume that whoever called idrp_pref_compare is planning to 
 *  actually set the best route pointer to the (new) best ext route,
 *  set the IDRP_STATUS_BEST_EXT bit in the status flag, and so on.
 */
int
idrp_pref_compare(p_idrp_rt, p_best_ext)
idrpRoute	*p_idrp_rt;	/* the route we're checking */
idrpRoute	*p_best_ext;	/* Current best ext route */
{
	/* 
	 * 7.17.1.b says to pick this route as a new BER if:
	 *  1. This route has the highest degree of pref,
	 *  2. This route is the only route to the destination, or
	 *  3. This route is selected as the result of a tie-break.
	 */

	trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare begin)"));

	/* Check clause 2  */

	if (p_best_ext == NULL)
		{
		/* only route is this route */
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 1) p_idrp_rt = %s, no p_best_ext",iso_ptoa(&p_idrp_rt->nlri)));
		return (TRUE);
		}

	trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 1a) p_idrp_rt = %s (%d)(peer: %s)",
		 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name));
	trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 1a-)  p_best_ext (%d) (peer:%s)", p_best_ext->pref_cal,p_best_ext->peer->name));

	/* clause 1 */

 	if  (p_idrp_rt->pref_cal <  p_best_ext->pref_cal)
		{ 
		/* higher preference calculated is worse 
		 * higher RD paths - need to fix idrp_pref
		 * idrp preference is suppose be higher better
		 * need to fix this
		 */
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 2) p_idrp_rt = %s (%d)(peer: %s \n p_best_ext (%d) (peer:%s) \n",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (TRUE);
		}

	if (p_idrp_rt->pref_cal > p_best_ext->pref_cal)
		{		
		/* higher preference calculated is worse 
		 * higher RD paths - need to fix idrp_pref
		 * idrp preference is suppose be higher better
		 * need to fix this
		 */
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 2a) p_idrp_rt = %s (%d)(peer: %s \n p_best_ext (%d) (peer:%s) \n",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (FALSE);
		}

	/* here is the equal idrp preferences - check on gated preferences
	 * to indicate tie breaking has already occurred
	 */

	if (p_idrp_rt->gated_pref < p_best_ext->gated_pref)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 3) p_idrp_rt = %s (%d)(peer: %s),  p_best_ext (%d) (peer:%s)",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (TRUE);
		}
	
	if (p_idrp_rt->gated_pref > p_best_ext->gated_pref)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 3a) p_idrp_rt = %s (%d)(peer: %s),  p_best_ext (%d) (peer:%s)",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (FALSE);
		}

	/* if the idrp peer structure has the same
	 * address we are dealing with a replace with 
	 */

	if (p_idrp_rt->peer == p_best_ext->peer)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 4) p_idrp_rt = %s (%d)(peer: %s),  p_best_ext (%d) (peer:%s)",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (TRUE);
		}
	
	if (tie_break(p_idrp_rt, p_best_ext) == ISO_ADDR_LESS_THAN)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 5) p_idrp_rt = %s (%d)(peer: %s),  p_best_ext (%d) (peer:%s)",
			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
		return (TRUE);
		}
	trace_tf(idrp_trace_options, TR_NORMAL,0,("(idrp_pref_compare 5) p_idrp_rt = %s (%d)(peer: %s),  p_best_ext (%d) (peer:%s)",
		 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->peer->name,p_best_ext->pref_cal,p_best_ext->peer->name));
	return (FALSE);
}

/*
 * insert_in_pref_order(idrpRoute *p_rt, idrpRoute *p_best)
 *
 * Insert p_rt into the sorted-by-preference list of routes to the same NLRI.
 *
 * This function has gotten a lot smarter -- it will now do the right thing
 * if given a null BER chain (it makes a new one and links it to the BER-Rib)
 * or if the given route is the new best (it changes the BER flag bits).
 *
 * Returns TRUE if the best ext route changed, or FALSE if the route was just
 * inserted.
 */
int
insert_in_pref_order(p_rt, p_best)
idrpRoute *p_rt;	/* route to be inserted */
idrpRoute *p_best;	/* best route in chain of idrpRoutes to NLRI */
{

	/* make sure we weren't handed a NULL p_best.  If we were, we 
	 * need to set up the BER structs here. 
	 */

	trace_tf (idrp_trace_options, TR_NORMAL,0,(" insert_in_pref_order %s p_p_best_ext = %x p_better %x, p_next_best %x p_best %x", 
	iso_ptoa(&p_rt->nlri), p_rt->p_p_best_ext,p_rt->p_better, p_rt->p_next_best,p_best)); 
        trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));

	if (p_best == (idrpRoute *) NULL)
		{
		/* this is the first external/local route to this NLRI, so make a new BER entry
		 * for it. 
		 */

		/* Create the idrp_best_ext struct and link the route into the chain */
		p_rt->p_p_best_ext = (idrp_best_ext *) idrp_local_mem_fit(sizeof(idrp_best_ext));
		IDRP_BER(p_rt) = p_rt;				/* this must be the best ext */
		p_rt->p_p_best_ext->refcount = 1;               /* only we refer to it so far */
		p_rt->p_better = p_rt->p_next_best = NULL;	/* no other routes on the chain */

		/* Now link into BER-Rib, unsorted */
		p_rt->p_p_best_ext->p_next = ber_rib_head;
		p_rt->p_p_best_ext->p_prev = NULL;
		if (ber_rib_head != NULL)
			{
			ber_rib_head->p_prev = p_rt->p_p_best_ext;
			}
		ber_rib_head = p_rt->p_p_best_ext;
	
		IDRP_STATUS_BIT_SET(p_rt,IDRP_STATUS_BEST_EXT);
        
		trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));
		trace_tf (idrp_trace_options, TR_NORMAL,0,(" insert_in_pref_order %s p_p_best_ext = %x p_better %x, p_next_best ", 
		iso_ptoa(&p_rt->nlri), p_rt->p_p_best_ext,p_rt->p_better, p_rt->p_next_best)); 
		
		return TRUE; /* best ext changed */
		}

	/* We already had a best ext chain set up, so check to see if p_rt is the new best 
	 * ext -- if so, insert at the head of the chain.
	 */

	if (idrp_pref_compare(p_rt, p_best))
		/* p_rt is best, insert at the head of the chain */
		{
		/* we always want p_best to be the real BER */
		assert((p_best->p_better == NULL) && (IDRP_BER(p_best) == p_best));

		p_rt->p_next_best = p_best;
		
		p_rt->p_better = NULL;
		p_best->p_better = p_rt;
		p_rt->p_p_best_ext = p_best->p_p_best_ext;
		IDRP_BER(p_rt) = p_rt;
                p_rt->p_p_best_ext->refcount++;

		IDRP_STATUS_BIT_CLEAR(p_best,IDRP_STATUS_BEST_EXT);
		IDRP_STATUS_BIT_SET(p_rt,IDRP_STATUS_BEST_EXT);
		
		trace_tf (idrp_trace_options, TR_NORMAL,0,(" insert_in_pref_order %s p_p_best_ext = %x p_better %x, p_next_best %x p_best %X ", 
		iso_ptoa(&p_rt->nlri), p_rt->p_p_best_ext,p_rt->p_better, p_rt->p_next_best,p_best)); 
		trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d",
			__FILE__, __LINE__));
		

		return TRUE; /* best ext changed */
		}
	
	/* else offered route isn't the best; find the right point to insert it --
	 * use p_best as a scratch pointer to walk down the best ext chain.
	 */

	while (p_best != NULL)
		{
		if (idrp_pref_compare(p_rt, p_best->p_next_best))
			/* insert between scratch and p_best->p_next_best */
			{
			p_rt->p_next_best = p_best->p_next_best;
			p_rt->p_better = p_best;
			p_best->p_next_best = p_rt;
			if (p_rt->p_next_best != NULL) /* NULL if we inserted at end of list */
				{
				p_rt->p_next_best->p_better = p_rt;
				}

			/* point p_p_best_ext correctly to BER struct */
			p_rt->p_p_best_ext = p_best->p_p_best_ext;

			/* and bump BER refcount */
			p_rt->p_p_best_ext->refcount++;

			trace_tf (idrp_trace_options, TR_NORMAL,0,(" insert_in_pref_order %s p_p_best_ext = %x p_better %x, p_next_best %x ", 
			iso_ptoa(&p_rt->nlri), p_rt->p_p_best_ext,p_rt->p_better, p_rt->p_next_best)); 
			return FALSE; /* best ext remained the same, we just inserted */
			}
		p_best = p_best->p_next_best;
		}

	trace_tf(idrp_trace_options, TR_NORMAL,0,("WARNING: insert_in_pref_order() failed to insert the given route!"));
	return FALSE;
}


/* 
 * idrp_del_best_ext -- delete the given idrpRoute from the best external routes chain.  Also delete
 * the chain struct itself if refcount goes to zero.  Return NULL if no new best or pointer to new
 * best if the best route changed.
 *
 * made idrp_del_best_ext slightly smarter -- now correctly sets BEST_EXT bit if the next best just
 * became the best, and returns pointer to new best.  Else (if the guy being deleted wasn't BEST_EXT, 
 * or there was no next best) returns NULL.
 */
idrpRoute *
idrp_del_best_ext(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
	idrpRoute *new_best = NULL;

	trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp_del_best_ext nlri: %s p_p_best_ext %x ",
			iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->p_p_best_ext));

	if (p_idrp_rt->p_p_best_ext != (idrp_best_ext *) NULL)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("refcount = %d",p_idrp_rt->p_p_best_ext->refcount));

		p_idrp_rt->p_p_best_ext->refcount--;

		if (p_idrp_rt->p_next_best)
			{	
			p_idrp_rt->p_next_best->p_better = p_idrp_rt->p_better;

			IDRP_STATUS_BIT_TEST(p_idrp_rt, IDRP_STATUS_BEST_EXT)
				{
				/* route being pulled from chain was the best ext route, and we have a new best ext */

				new_best = p_idrp_rt->p_next_best;
				IDRP_BER(p_idrp_rt) = new_best;

				IDRP_STATUS_BIT_CLEAR(p_idrp_rt, IDRP_STATUS_BEST_EXT);
				IDRP_STATUS_BIT_SET(new_best, IDRP_STATUS_BEST_EXT);

				assert((p_idrp_rt->p_better == NULL) && (p_idrp_rt->p_p_best_ext->refcount != 0));
				}
			else
				{
				/* debugging, can remove this assert later */
				assert(IDRP_BER(p_idrp_rt) != p_idrp_rt);
				}
			}

		if (p_idrp_rt->p_better)
			{
			p_idrp_rt->p_better->p_next_best = p_idrp_rt->p_next_best;
			}

		if (p_idrp_rt->p_p_best_ext->refcount == 0)
			{
			idrp_best_ext *p_ber = p_idrp_rt->p_p_best_ext;
			trace_tf (idrp_trace_options, TR_NORMAL,0,("best external structure goes to zero nlri %s deleting ",iso_ptoa(&p_idrp_rt->nlri)));

			/* if refcount went to zero, this should have been a singleton in the BER chain */
			assert((p_idrp_rt->p_better == NULL) && (p_idrp_rt->p_next_best == NULL));

			/* relink best external routes chain
			 */

			if (p_ber == ber_rib_head)
				{
				/* head of chain, reset the head
				 * of chain
				 */

				ber_rib_head = p_ber->p_next;
				p_ber->p_prev = (idrp_best_ext *) NULL;
				}
			else
				{
				/* not head of chain 
				 * - readjust
				 */

				p_ber->p_prev->p_next = p_ber->p_next;
				if (p_ber->p_next)
					{
					/* watch for tail
					 * where p_ber->p_next is zero
					 */

					p_ber->p_next->p_prev = p_ber->p_prev;
					}
				}

			IDRP_MEM_FIT_FREE(p_idrp_rt->p_p_best_ext);

			}
		p_idrp_rt->p_p_best_ext = (idrp_best_ext *) 0;
		p_idrp_rt->p_better = (idrpRoute *)  0;
		p_idrp_rt->p_next_best = (idrpRoute *) 0; 
		}
	return (new_best); /* NULL if best didn't change or no more externals, else pointer to the new best */
}


int 
idrp_ann_list_empty(p_ann_list)
idrp_ann_list 	*p_ann_list;
{
int	i;
/* check to see that the neither the ann_nlri or with_nrli
 * has anything on it - heads of lists should be non-zero
 * in first attribute
 */

	NLRI_FAMILY_LOOP(i)
		{
		if (p_ann_list->ann_nlri[i].head)
			{
			return(FALSE);
			}
		if (p_ann_list->with_nlri[i].head)
			{
			return(FALSE);
			}
		} NLRI_FAMILY_LOOP_END;
	return(TRUE);
} 

idrpRoute *
find_nlri_in_ann_nlri(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
{
idrp_ann_list	*p_atl;
idrpRoute	*p_irt;
int		i;

	i = family_to_nlri_id(p_idrp_rt->family); 
	ANN_LIST(p_atl,p_ann_list)
		{
		ANN_ANN_NEXT_NLRI(p_irt,i,p_atl)
			{
			/* right now this does not check
		 	 * to see if the attribute record
			 * it only checks the nlri
			 */
			if (!isopfxcompare(&p_irt->nlri,&p_idrp_rt->nlri) && 
				(p_irt->p_attr->rib_id == p_idrp_rt->p_attr->rib_id))
				{
				return(p_irt);
				}
			} ANN_ANN_NEXT_NLRI_END;
		} ANN_LIST_END;
	return((idrpRoute *)0);
}


int
rdi_in_dist_list(p_attrib,p_rdi)
idrp_attribute_entry	*p_attrib;
struct	iso_net_addr	*p_rdi;
{
struct iso_addr		*p_net;
u_char			*cp;
int			cnt,len;

	/* check to see if the attribute is present */

	if (!p_attrib->present)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0,("rdi_in_dist_list called with no attribute present \n "));
		return (FALSE);
		}

	/* get the count of the attribute */

	cp = p_attrib->data; 
	for (cnt = *cp++; cnt; cnt--) 
		{
		len = *cp++;
		if (len == p_rdi->isoa_len)
			{
			/* if the bytes are the same, return zero */

			if (!bcmp(cp,p_rdi->isoa_genaddr,len))
				{
				return(TRUE);
				}
			}
		cp += len;  
		}
	return (FALSE);
}

void
idrp_clear_reconfig(p_ann_list)
idrp_ann_list	*p_ann_list;
{
idrp_ann_list	*p_atl;
int		i;
idrpRoute	*p_irt;


	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)	
			{
			ANN_WITH_LIST_WALK(p_irt,i,p_atl)
				{
				IDRP_STATUS_BIT_CLEAR(p_irt,IDRP_STATUS_RECONFIGURE);
				} ANN_WITH_LIST_WALK_END;

			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{
				IDRP_STATUS_BIT_CLEAR(p_irt,IDRP_STATUS_RECONFIGURE);
				} ANN_ANN_LIST_WALK_END;

			} NLRI_FAMILY_LOOP_END;
		} ANN_LIST_END;
}


 

/* walk through the remove routes and the new routes,
 * comparing the nlris and weeding out 
 * all withdraws that are not permanent for 
 * final announce list to hand to phase1 processing
 */


void
remove_ann_dup(p_w_list,p_ann_list)
idrp_ann_list	*p_w_list;	/* withdrawls list */
idrp_ann_list	*p_ann_list;	/* rib refresh list */
{
idrp_ann_list	*p_atl;
idrpRoute	*p_irt;
idrpRoute	*p_irt_ann;
idrpRoute	*p_irt_next;	/* next withdraw on list */
int		i;


	/* walk the withdrawal list and try to weed out the
	 * announces
	 */

	ANN_LIST(p_atl,p_w_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			ANN_WITH_LIST_DEL_WALK(p_irt,i,p_atl,p_irt_next)
				{
				p_irt_ann = find_nlri_in_ann_nlri(p_irt,p_ann_list);
				if (p_irt_ann)
					{
					/* set phase1 withdraw Replace flag
					 * set the remove idrp_rt to withdraw
					 * clear the withdraw chain link in this 
					 * route 
					 */

					IDRP_STATUS_BIT_SET(p_irt_ann,IDRP_STATUS_REPLACE);

					/* close up the announce list */

					if (p_irt->p_with_back)
						{
						p_irt->p_with_back->p_with = p_irt->p_with;
						}
					if (p_irt->p_with)
						{
						p_irt->p_with->p_with_back = p_irt->p_with_back;
						}		 
					p_irt_ann->p_with = p_irt;
					p_irt->p_with = p_irt->p_with_back =  (idrpRoute *)NULL;
				  	}
				else 
					{ 
					/* if not found in announce nlri, linked to withdraw list
				 	 */
				    
					link_ann_list(p_irt,p_ann_list,NLRI_LINKED_P_WITH);
				  	}
				} ANN_WITH_LIST_DEL_WALK_END; 
			} NLRI_FAMILY_LOOP_END;
		} ANN_LIST_END;
	
}

void
idrp_clean_replace_routes(p_ann_list)
idrp_ann_list	*p_ann_list;
{
idrp_ann_list	*p_atl;
int		i;
int		changes = 0;
idrpRoute	*p_irt;
idrpRoute	*p_old_rt;
idrpPeer	*p_this_peer;

	p_this_peer = &idrp_this_node;
	if (p_ann_list->p_attr == NULL)
		return;	

	rt_open(idrp_this_node.task);
	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{
				IDRP_STATUS_BIT_TEST(p_irt, IDRP_STATUS_REPLACE)
					{
					p_old_rt = p_irt->p_replace;
					if (p_old_rt)
						{
						IDRP_STATUS_BIT_CLEAR(p_old_rt,(IDRP_STATUS_REPLACE | IDRP_STATUS_REPLACE_PROC));
						IDRP_STATUS_BIT_TEST(p_old_rt,IDRP_STATUS_DELETE)
							{
							idrp_free_outlist(p_old_rt);
							idrp_free_nlri_att_rec(p_old_rt,NLRI_LINKED_P_WITH);
							idrp_del_rt_gated(p_old_rt,IDRP_RESET_RT_BIT);	
						 	idrp_free_idrpRoute(p_old_rt);
							changes++;
							}
						p_irt->p_replace = (idrpRoute *) 0; 
						}
					else
						{

							
							trace_tf(idrp_trace_options, TR_PHASE3,0,("replace flag set route %s peer %s without p_replace route",
												  iso_ptoa(&p_irt->nlri),p_irt->peer->name));
							       
						      }
						IDRP_STATUS_BIT_CLEAR(p_irt,(IDRP_STATUS_REPLACE | IDRP_STATUS_REPLACE_PROC));	
					      }
				      } ANN_ANN_LIST_WALK_END;
			      } NLRI_FAMILY_LOOP_END;
		      }ANN_LIST_END;
		
		rt_close(idrp_this_node.task,&idrp_this_node.gw,changes,NULL);
}


void
idrp_clean_early_proc(p_ann_list)
idrp_ann_list	*p_ann_list;
{
  idrp_ann_list	*p_atl;
  int		i;
  int		changes = 0;
  idrpRoute	*p_irt;
  
  if (p_ann_list->p_attr == NULL)
    return;	
  
  trace_tf(idrp_trace_options, TR_NORMAL,0,("cleaning up early processing of routes"));
	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			ANN_WITH_LIST_WALK(p_irt,i,p_atl)
				{
				IDRP_STATUS_BIT_CLEAR(p_irt, (IDRP_STATUS_REPLACE_PROC | IDRP_STATUS_WITH_EARLY_PROC));
				 trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing cleared for nlri %s",iso_ptoa(&p_irt->nlri)));
				} ANN_WITH_LIST_WALK_END;
			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{
				IDRP_STATUS_BIT_CLEAR(p_irt, (IDRP_STATUS_REPLACE_PROC | IDRP_STATUS_WITH_EARLY_PROC));
				trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing cleared for nlri %s",iso_ptoa(&p_irt->nlri)));
				if (p_irt->p_replace)
					{
					IDRP_STATUS_BIT_CLEAR(p_irt->p_replace, (IDRP_STATUS_REPLACE_PROC | IDRP_STATUS_WITH_EARLY_PROC));
					trace_tf(idrp_trace_options, TR_NORMAL,0,("early processing cleared for nlri %s",iso_ptoa(&p_irt->p_replace->nlri)));
					}
				} ANN_ANN_LIST_WALK_END;	
			} NLRI_FAMILY_LOOP_END;
		}ANN_LIST_END;
}



/* find active route for the nlri
 * listed in the idrpRoute
 */


idrpRoute *
find_active_idrp_route(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
sockaddr_un	*p_dest;
sockaddr_un	*p_mask;
rt_entry	*p_rt;

	/* allocate the destination */

	p_dest = (sockaddr_un *) NULL;
	isopfx_sock_mask_pair(p_idrp_rt,&p_dest,&p_mask);
	p_rt = rt_locate(RTS_NETROUTE,p_dest,p_mask,RTPROTO_BIT_ANY);
	
	/* got a gated route
	 * check for active route, 
	 * and then idrp route associated with active route
	 */

	if (p_rt)
		{
		/* got route for nlri
		 */

		if (p_rt->rt_active)
			{
			/* got active route for nlri
			 * is an idrp route associated?
			 */

			return (p_rt->rt_active->rt_idrp);
			}
		}
	return ((idrpRoute *) NULL);



}	


void
isopfx_sock_mask_pair(p_idrp_rt,p_dest,p_mask)
idrpRoute	*p_idrp_rt;
sockaddr_un	**p_dest;
sockaddr_un	**p_mask;
{
int	pfx_len; 
int	length;
sockaddr_un	*p_dst;


	/* for osilocal we must grab the space out of
	 *    task allocated memory, so we can't
	 *    let the automatic task_mem_malloc work
	 *  
	 * We hope that the gated 3.5 or other parser
	 * changes will make this code obsolete as
	 * it is a work around / hack.
	 */

	p_dst = *p_dest;

	if (p_dst == (sockaddr_un *) NULL)
		{	
		p_dst = (sockaddr_un *) idrp_local_mem_fit (sizeof(struct iso_net_addr));
		}

	pfx_len = p_idrp_rt->nlri.pfx_len;		
	switch (p_idrp_rt->family)
		{
		case AF_ISO:
			length = PFX_BYTE_LENGTH(p_idrp_rt->nlri); 
			p_dst->iso.siso_len = length + 2;
			p_dst->iso.siso_family = p_idrp_rt->family;
			bcopy((caddr_t) &p_idrp_rt->nlri.pfx,(caddr_t) &p_dst->iso.siso_addr,length);
			*p_mask = iso_mask_prefix(pfx_len);
			break;

		case AF_INET:
			/* copy the prefix over as byte */
 
			bcopy((caddr_t) &p_idrp_rt->nlri.pfx,(caddr_t) &p_dst->in.sin_addr,sizeof(struct in_addr));
			p_dst->in.sin_family = AF_INET; 
			p_dst->in.sin_len = 8; 
			p_dst->in.sin_port = 0;
			*p_mask = inet_mask_prefix(pfx_len);	
			break;
	
		default:
			/* if not ISO or INET
			 * - flag error in processing
			 * HALT for now 
			 */
			assert(TRUE);
			break;	
		}

	/* set the pointer for destination sockaddr 
	 */

	*p_dest = p_dst;
		
}
