/* 
 * $Id: idrp_rt_phase1.c,v 1.13 1995/11/06 16:30:12 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 "idrp.h"

/* idrp_rt_phase1. c 
 *  Routing table code for IDRP - phase 1 routing routines 
 * 
 * 
 * IDRP decision routines  
 *
 * Phase 1 decision routes -
 * 
 *  idrp_pref(p_idrp_rt) - calculate preference
 *     for route based on the peer
 * 
 *  
 * %% Phase 1 processing
 * %% processing of update BISPDU routines
 * 
 * idrp_route_mod - add route to update parse change list
 * idrp_process_pdu_routes - process routes after parse
 * 
 *  
 * %% Phase one processing - per idrp spec
 * 
 * idrp_phase1 _process - process phase1 
 * phase1_internal 	- handling of internal routes in phase 1
 * phase1_external 	- handling of external routes in phase 1
 * ph1_with_int_route 	- phase1 withdraw internal route
 * ph1_with_rep_int_route - phase1 withdraw/replace internal route
 * ph1_add_int_route 	- phase 1 add internal route
 * ph1_with_ext_route 	- phase 1 withdrawl of external route
 * ph1_with_rep_ext_route - phase1 - withdraw with replace of external route
 * ph1_add_ext_route 	- phase1 add external route
 * send_best_ext 	- send best external routes
 * 
 * del_route_best_ext	- delete best external routes withdrawn
 * remove_ann_dup	- remove announce duplicates from announce list
 *  
 *    
 *
 * We have the following  entry points to the IDRP code:
 * idrp_process_update -> parse_update -> idrp_route_mod
 *			 and when done -> idrp_process_pdu_routes
 * 
 * idrp_process_pdu_routes -> calls phase 1 processing routines:
 * -> phase1_internal->ph1_int_add_route 
 *		     ->ph1_with_route
 *		     ->ph1_with_rep_route
 * -> phase1_external->ph1_add_ext_route
 *		     ->ph1_with_route
 *		     ->ph1_with_rep_route
 *	-> send_best_ext -> del_routes_best_ext
 *   
 * Before the routes are handed to gated, preferences for
 *  gated are set so that it can proform phase 2 processing:
 *    check for other route
 *    tie_break routine
 *    gated_pref routine 
 *   
 *  
 * idrp_flash - called to handle the list of new active routes
 * 		from the gated routing table.
 *              The phase 3 routes are only sent to external peers.
 * 
 * idrp_flash -> phase3_processing -> phase3_status_change
 * ->idrp_send_phase3_route -> idrp_del_ph3_routes
 * 
 * 
 *
 * Yet to work on for idrp - new policy and re-init routines
 * auxiliary policy and and terminate gateway routines :
 *
 * names will be something like:
 * idrp_rt_reinit
 * idrp_rt_terminate
 * idrp_newpolicy  
 * idrp_aux_policy(). 
 *
 */ 


 /*  %%  IDRP decision routines   */

 /*
 * Phase 1 decision routes -
 * 
 *  idrp_pref(p_idrp_rt) - calculate preference
 * 			   for idrp route 
 */

void 
idrp_pref(p_idrp_rt)
idrpRoute *p_idrp_rt;
{

	
	/* please note that IDRP's preference is
	 * high and gated preference is low
	 * - tie breaking code has to use high
	 * gated has to use low.
	 * inversion occurs in the gated prefence
	 * routine to configured maximum
	 */

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

	(void) PREF(p_idrp_rt,
		    &p_idrp_rt->pref_cal,
		    &p_idrp_rt->gated_pref);
	
	if (p_idrp_rt->p_rt != (rt_entry *) NULL) {
		trace_tf(idrp_trace_options, TR_NORMAL,0, 
			("idrp preference calculated %i rcvd %i, gated pref %i route %A/%A",
				p_idrp_rt->pref_cal,
				p_idrp_rt->pref_rcvd, 
				p_idrp_rt->gated_pref,
				p_idrp_rt->p_rt->rt_dest,
				p_idrp_rt->p_rt->rt_dest_mask));
	} else {
		trace_tf(idrp_trace_options, TR_NORMAL,0, 
			("idrp preference calculated %i rcvd %i, gated pref %i route %s",
				p_idrp_rt->pref_cal,
				p_idrp_rt->pref_rcvd, 
				p_idrp_rt->gated_pref,
				iso_ptoa(&p_idrp_rt->nlri)));
	}
} 

void 
idrp_def_pref(p_idrp_rt)
idrpRoute *p_idrp_rt;
{


      /* please note that IDRP's preference is
       * high and gated preference is low
       * - tie breaking code has to use high
       * gated has to use low.
       * inversion occurs in the gated prefence
       * routine to configured maximum
       *  IDRP_GATED_INIT_ROUTE_PREF = 0
       *  IDRP_GATED_INVALID_ROUTE_PREF = 255	
       */

      p_idrp_rt->pref_cal = p_idrp_rt->p_attr->p_opts->hopcount;
      p_idrp_rt->gated_pref = IDRP_GATED_INIT_ROUTE_PREF;
      trace_tf(idrp_trace_options, TR_NORMAL,0, 
	("idrp default preference calculated %i rcvd %i, gated pref %i route %s",
        p_idrp_rt->pref_cal,
	p_idrp_rt->pref_rcvd, 
	p_idrp_rt->gated_pref,
	iso_ptoa(&p_idrp_rt->nlri)));
} 


/*  %% phase 1 processing */
 


/*  %% phase 1 processing */
 

/*
 * idrp_process_pdu_routes(peer,res)
 * Process the routes received in a pdu.  
 */

idrp_ann_list *
idrp_process_pdu_routes(peer,res,type)
idrpPeer	*peer;
parse_results	*res;
int		type;
{
/* general pointers for processing */

idrp_rid_hash	*p_rid;			/* hash table entry */
idrpRoute	*p_rt;			/* pointer to idrp route structure */
idrp_ann_list	*p_ann_list = 0;		/* announce list structure */
idrp_ann_list	*p_with_list = 0;	/* routes withdrawn  by route ids */
idrp_attribute_record	*p_att;		/* idrp attribute record */

/* loop counters and flags */
int		i;

 
	/* process the routes from one pdu
	 * withdrawal routes 1st
 	 * with array initialized above
 	 * if routes were contained in the results
	 * add a hash table entry 
	 * 
	 * For multiple ROUTE_SEPARATORS, 
	 *  we may have multiple hash table entries 
	 */ 

 
       p_ann_list = idrp_parse_to_ann(res,peer);	


	/* handle withdrawn route_ids
	 */

	if (res->withdraw->count)
	   {
           for (i = 0; i < res->withdraw->count ;i++) 
		{
		/* hash the route id to get first route pointer 
		 * route - rt_entry pointer -> rt_data and idrp_Route info 
		 * idrp_Route_info -> other nlri associated with route_id 
		 */

		p_rid = idrp_rid_hash_get(peer,res->withdraw->withdraw[i]);
		if (p_rid != (idrp_rid_hash *) NULL) 
			{
			/* found hash entry 
			 * and there are announce routes
			 * link on withdrawal list 
			 *  are being announced by pdu
			 */ 

			p_with_list = link_rid_to_with_list(p_with_list,p_rid,peer);	
				
			} /* end of if (p_rid) */

		/* release the hash table entry now that the entry is done */

		idrp_free_hash_entry(peer,res->withdraw->withdraw[i]);	

		} /* end of for loop for each withdraw route_id */



	    /* check for an announce list
	     * if it is there - then remove
	     * any duplicates 
	     * - if not, just use withdraw list
	     *  - the test for the attribute is in case
	     *    the withdraw code fails to
	     * 	  find any of the list rids
	     */
 
	  
	    if (p_ann_list && (p_ann_list->p_attr != (idrp_attribute_record *) NULL))
		{
		IDRP_STATUS_BIT_TEST(res,IDRP_RID_ROUTE_LOOP)
			{
			/* we are handling a set of routes from
			 * a packet with an RD_LOOP in it or with our RD in it
		 	 */

			p_with_list = idrp_process_rdloop(p_with_list,p_ann_list,peer);
			if (p_ann_list)
				{
				free_ann_list(p_ann_list);
				IDRP_MEM_FIT_FREE(p_ann_list);
				}
			p_ann_list = p_with_list;
			}
		else
			{
			/* we are not handling a loop
			 * - change the duplicate announce/withdraws
			 *    into withdraw/replace 
			 *
			 * - clean up withdraw list after it has
			 *   been transferred onto the announce list 
			 */
 
			remove_ann_dup(p_with_list,p_ann_list);	
			if (p_with_list)
				{
				free_ann_list(p_with_list);
				IDRP_MEM_FIT_FREE(p_with_list);
				}
			}
		}
	    else
		{
		/* the withdrawl pointer won't be there unless
		 * the withdraw nlris are there
		 */

		p_ann_list = p_with_list;
		} 

	    } /* end of withdraw nlri processing */
	else
	    {
	    /* no withdraws - test to make sure the
	     * this is not the loop case
	     */
	
	    IDRP_STATUS_BIT_TEST(res,IDRP_RID_ROUTE_LOOP)
		{
		/* loop case - check to remove all 
		 *  nlri as implict replace
		 * --- loop turns these into withdraws
		 */

		if (p_ann_list)
			{
			p_with_list = idrp_process_rdloop(p_with_list,p_ann_list,peer);
			free_ann_list(p_ann_list);
			IDRP_MEM_FIT_FREE(p_ann_list);
			}
		p_ann_list = p_with_list;
		}
	    }

	/* set-up the hash table entry for the route_id and 
	 * route_id list 
	 */


	if (type == IDRP_RIB_REFRESH_TYPE)
		{
		return (p_ann_list);
		}
	else
		{
		if (p_ann_list == (idrp_ann_list *) NULL)
			{
			trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("pdu process with no routes"));
			return(p_ann_list);
			} 
		
		else
			{	
			idrp_phase1_process(p_ann_list);
#ifdef	IDRP_QOS
        		trace_tf(idrp_trace_options, TR_NORMAL,0,("got to the qos_n_ann_list check"));
			if (qos_in_ann_list(p_ann_list))
				{
				/* if qos in the pdus, set a timer to run the qos
				 */
				if (!qos_rib_changed)
					{
        				trace_tf (idrp_trace_options, TR_NORMAL,0,("got to the qos_rib_change flag phase1"));
					qos_rib_changed = TRUE;	
					task_timer_set(idrp_this_node.idrp_qos_timer,idrp_this_node.qos_poll_time,(time_t) 0);
					}
				else
					{
	        			trace_tf (idrp_trace_options, TR_NORMAL,0,("got to the qos_rib_change flag  changes and flag already set"));
					}
				}				 		
#endif	/* IDRP_QOS */
			}
		/* free the announce list
		 * utility frees all but first
		 */

		if (p_ann_list)
			{
			free_ann_list(p_ann_list);
			IDRP_MEM_FIT_FREE(p_ann_list);
			}
		return ((idrp_ann_list *)0 );
		}
}	

void 
idrp_phase1_process(ann_list)
idrp_ann_list 	*ann_list;
{
int	changes = 0;
/* Phase 1 logic - 
 * splits on internal or external
 */
	/* multiple attribute in list, but all from same peer */

	rt_open(ann_list->peer->task);
	switch (ann_list->peer->type)
		{	
		case IDRP_PEER_INTERNAL:
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("peer %s phase1 internal pdu processing ",ann_list->peer->name));

			changes = phase1_internal(ann_list);
			break;
	
		case IDRP_PEER_EXTERNAL:
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("peer %s phase1 external pdu processing",ann_list->peer->name));
			changes = phase1_external(ann_list);
			break;
		}
	/* close gated route table */

	rt_close(ann_list->peer->task,&ann_list->peer->gw,changes,NULL);
}

int
phase1_internal(ann_list)
idrp_ann_list	*ann_list;
{
idrp_ann_list	*p_atl;
idrpPeer 	*peer;
idrpRoute	*p_rt,*p_rt_next;
int		i;
int		changes = 0;
	
	/* process nlri families separately */

	ANN_LIST(p_atl,ann_list)
		{
		NLRI_FAMILY_LOOP(i) 
			{
			/* process withdrawals */

			ANN_WITH_LIST_DEL_WALK(p_rt,i,p_atl,p_rt_next)
				{
				/* walk through withdrawal list */
				ph1_with_int_route(p_rt);
				changes++;
				} ANN_WITH_LIST_DEL_WALK_END;
		
			/* process announces */
			ANN_ANN_NEXT_NLRI(p_rt,i,p_atl)
				{
				IDRP_STATUS_BIT_TEST(p_rt,IDRP_STATUS_REPLACE)
					{
					if (p_rt->p_with == (idrpRoute *) NULL)
						{
						idrpRoute *p_crash;
						trace_tf(idrp_trace_options, TR_NORMAL,0,("ph1_internal error in the REPLACE flag on nlri %s",iso_ptoa(&p_rt->nlri))); 
 						p_crash = p_rt->p_with;
						p_crash->p_rt = 0;
						}	
						
					/* handle replaces
				 	 * - get rid of replace route
					 * - reset gated route to
					 *   new values from peer
					 * and then get rid of REPLACE bit
					 */

					ph1_with_rep_int_route(p_rt);

					IDRP_STATUS_BIT_CLEAR(p_rt,IDRP_STATUS_REPLACE);
					}
				else
					{
					/* handle additions */
					ph1_add_int_route(p_rt);
					}
				changes++;
				} ANN_ANN_NEXT_NLRI_END;
			}  NLRI_FAMILY_LOOP_END
		} ANN_LIST_END;
	
	return(changes);
}


int
phase1_external(ann_list)
idrp_ann_list	*ann_list;
{
idrp_ann_list	*p_atl;
idrpRoute	*p_rt,*p_rt_next;
idrp_ann_list	out_ann_list;
int		i;
int		changes = 0;	
	/* process nlri families separately */

	bzero(&out_ann_list,sizeof(idrp_ann_list));

	/* set peer to the first peer in the
	 *  idrp list which internal peer
	 *  if there are internal peers
	 *  or first external peer
	 */
 
	out_ann_list.peer = idrp_peers; 

	ANN_LIST(p_atl,ann_list)
		{	
		NLRI_FAMILY_LOOP(i)
			{
			/* process withdrawls */
			ANN_WITH_LIST_DEL_WALK(p_rt,i,p_atl,p_rt_next)
				{
				ph1_with_ext_route(p_rt,&out_ann_list);
				changes++;
				} ANN_WITH_LIST_DEL_WALK_END;
		
			/* process announces */
			ANN_ANN_NEXT_NLRI(p_rt,i,p_atl)
				{
				if (p_rt->status & IDRP_STATUS_REPLACE)
					{
					/* handle replaces */
					ph1_with_rep_ext_route(p_rt,&out_ann_list);
					IDRP_STATUS_BIT_CLEAR(p_rt,IDRP_STATUS_REPLACE);
					}
				else
					{
					/* handle additions */
					ph1_add_ext_route(p_rt,&out_ann_list);
					}
				changes++;
				} ANN_ANN_NEXT_NLRI_END;
			} NLRI_FAMILY_LOOP_END 
		} ANN_LIST_END;

		/* jgs -- added 'cause we need holddown on phase 1 too :-(
		 * Logic description for future:
		 * idrp_minadv_annlist(&out_ann_list)
		 * - called to pull out any routes for phase 1 announcement
		 *   to internal peers that are being damped either phase1/3
		 * 
	 	 * Need three min-adv queues:
		 *    1) Local -> external peers
		 *		spec requires a  different timer
		 * 
		 *    2) external -> internal peers
		 *         IDRP spec says - 1 timer, but may want different 
		 *         need linked approach since allows for
	 	 *	   quick determination of who to send it two
		 *
		 * 	     
		 *   3) internal/external -> external peers
		 *	   phase3 only
		 *
		 *  
		 * NLRI still flagged damping, but
		 * with the three queue approach internal
		 * peer damping can be quickly check versus
		 * external peer damping due to queue
		 * 
		 * If a best external because BEST_EXT & LOC_RIB
		 * then the node is pulled off the internal damping
		 * queue and stuffed on external queue,
		 * a flas are set to indicates internal transmission  
		 *  
		 * internal announcement expires
		 *  - send to internal peers best external  
		 *
		 * when external peer, expires
		 *  - send  to internal peers the
		 *	internal nlris
		 * - send to external peers 
		 *      NLRIs
		 * provides first warning to internal peers upon damping 
		 *
		 * simple call to accomplish this here is: 
		 * 
		 * idrp_minadv_annlist(&out_ann_list,INTERNAL)
		 * 
		 * the minadv_annlist  and rest need 
		 * to be changed for real 
		 * 
		 * AFTER Release 1 changes
		 * 
		 * 
		 */

		
		/* now send the best external routes to our internal peers */
		send_best_ext(&out_ann_list);
	
		/* delete the best_external routes which are no 
		 * longer needed after being withdrawn during the 
		 * sending of the new best_ext routes 
		 */
 
		del_routes_best_ext(&out_ann_list);
	
	return(changes);
}


void ph1_with_int_route(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
idrpPeer	*peer;
idrpPeer	*p_trace_peer;
rt_entry	*p_rt;
int		status_illegal;

	p_trace_peer = &idrp_this_node;

	/* peer and rt_entry for gated table */

	peer = p_idrp_rt->peer;
	p_rt = p_idrp_rt->p_rt;

#ifdef IDRP_QOS
	trace_tf (idrp_trace_options, TR_PHASE1 ,0, ("idrp ph1_with_int_route %s status %x (%x)rib = %d peer %s route_id %d", 
	iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status, p_idrp_rt->qos_status,p_idrp_rt->p_attr->rib_id,peer->name,p_idrp_rt->route_id_in)); 
#else
	trace_tf (idrp_trace_options, TR_PHASE1 ,0, ("idrp ph1_with_int_route %s status %x rib = %d peer %s route_id %d", 
		iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status, p_idrp_rt->p_attr->rib_id,peer->name,p_idrp_rt->route_id_in)); 
#endif
		

	
	assert(p_rt != NULL);

	status_illegal = IDRP_STATUS_EXT_INFO | IDRP_STATUS_REPLACE;
	if  (p_idrp_rt->status & status_illegal)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp ph1_with_int_route - illegal status %x peer %s route_id %d", 
			 p_idrp_rt->status,peer->name,p_idrp_rt->route_id_in)); 
		return;
		}
	
	IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_WITH);
	
	if (!(p_idrp_rt->status & IDRP_STATUS_LOC_RIB)) 
		{
		/* 
		 * Not local rib - so free this idrp route now
		 * It will go away later
		 * clear bits in gated protocol 
		 * free idrpRoute structure
		 */
	
		if (!IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
			{
			idrp_free_outlist(p_idrp_rt);
			idrp_free_nlri_att_rec(p_idrp_rt,NLRI_LINKED_P_WITH); /* free the nlri from the route via withdrawl */
			idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);	
			idrp_free_idrpRoute(p_idrp_rt);
			}
		else /* This is a damped route, we can't get rid of it yet, so just mark it for deletion 
		      * and it will be deleted when the appropriate minadv timer fires.  We still want
		      * to run the delete code so that gated will pick a new feasible route. */
			{
			IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);
			idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);	
			}
		}
	else /* route is in local RIB */
		{
		if (p_rt->rt_state == RTS_DELETE)
			{
			/* put warning flags in all over */
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_int_route) route from peer %s has route id %d with already delete flag for nlri %s", 
				peer->name,p_idrp_rt->route_id_in,iso_ptoa(&p_idrp_rt->nlri)));
			}
 		if (!(p_rt->rt_state & RTS_EXTERIOR)) 
			{
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_int_route) peer %s route_id %d does not have exterior flag for nlri %s", 
				peer->name,p_idrp_rt->route_id_in,iso_ptoa(&p_idrp_rt->nlri)));
			}

		/* delete this route - it will be flashed if 
		 * the status is local rib 
		 * and from there we must watch for deletion
		 */

		idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);
		IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);

		/* for now all tracking flags live in the local_node 
		 * peer structure
		 * in the future, the tracing will be by peer
		 */


		peer = &idrp_this_node;		

		trace_tf (idrp_trace_options, TR_PHASE1, 0, 
			("IDRP (ph1_with_int_route) LOC_RIB route from peer %s has route id %d with delete nlri %s", 
			p_idrp_rt->peer->name,p_idrp_rt->route_id_in,iso_ptoa(&p_idrp_rt->nlri)));
			
		} 
	return;
}

/*
 * ph1_with_rep_int_route -- Withdrawal with replace of internal route.
 * 
 * Called from: phase1_internal().
 * 
 */

void
ph1_with_rep_int_route(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
idrpPeer	*peer;
idrp_rt_chain_walk	*p_gate;
idrp_rt_chain_walk	*p_pref;
struct	iso_net_addr	dest;
sockaddr_un		*p_dest;
idrpRoute		*p_best_ext;
int			pref;

	
	/* phase1 - receives a route with REPLACE status flag set  and
	 *	    p_with with the withdrawn route location
	 *	1) allocate memory for gated and preference chains
	 *	2) try to find the route in the gated tables
	 *	    - it should be there
	 *	    - quit the task if it is not
	 *	3) mediate preference
	 *	4) call idrp_rt_repl_route - replace the
	 *	    route in the gated table for this peer
	 */

	peer = p_idrp_rt->peer;
	p_gate = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	p_pref = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	p_dest = (sockaddr_un * )&dest; 

	if (!idrp_add_route_locate(p_dest,p_idrp_rt,p_gate,p_pref,&p_best_ext))
		{
		/* quit the task - you should find this route in the gated tables */

		trace_log_tf (idrp_trace_options, 0, LOG_WARNING, ("IDRP:ph1_with_rep_int_route: peer %s route %s no gated rt", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		assert(FALSE);
		}

	if (p_pref->p_rt)
		{
		if (mediate_pref_match(p_pref,p_idrp_rt))
			{
			/* if it is the p_idrp_rt = best preference - set to minus 1
			 * otherwise -  set value to pref plus 1
			 */
			p_idrp_rt->gated_pref -= 1;
			}
		else
			{
			p_idrp_rt->gated_pref += 1;
			}
			
		free_rt_chain_walk(p_pref->p_next,peer);
		IDRP_MEM_FIT_FREE(p_pref);
		}

	if (p_gate->p_rt == (rt_entry *) NULL )
		{
		trace_log_tf (idrp_trace_options, 0,LOG_WARNING, ("IDRP:ph1_with_rep_int_route: peer %s route %s status %x no gated rt delete but no damping for this peer", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));
		assert(FALSE);
		}
	else if ((p_gate->p_rt != p_idrp_rt->p_with->p_rt) &&
			 (p_gate->p_rt->rt_idrp != (idrpRoute *) NULL))
		{
		trace_log_tf (idrp_trace_options, 0, LOG_WARNING, ("IDRP:ph1_with_rep_int_route: peer %s route %s gated rt doesn't match withdrawal", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		assert(FALSE);
		}



	/* get rid of intermediate structures */

	free_rt_chain_walk(p_gate->p_next,peer);
	IDRP_MEM_FIT_FREE(p_gate);


	/* replace in the gated route the old idrp route with new idrp route 
	 * takes care of copying over damping stuff if needed
	 * - free old idrp route from the attribute record
	 * - free the memory space 
	 */

	idrp_repl_rt_gated(p_idrp_rt,p_idrp_rt->p_with->p_rt);
	idrp_mod_rt_gated(p_idrp_rt,IDRP_RUN_PREF);

	/* get rid of the old route
	 * - we need to do idrpRoute delete here
	 * Note: we do not relink output list because that has been done
	 *       in idrp_repl_rt_gated routine for replace route 
	 * because gated route has been kept 
	 */

#ifdef IDRP_QOS
	trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP:ph1_with_rep_int_route: peer %s route %s (status %x(%x>) p_with gated replaced",peer->name, 
			iso_ptoa(&p_idrp_rt->p_with->nlri),p_idrp_rt->p_with->status,p_idrp_rt->p_with->qos_status)); 
#else
	trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP:ph1_with_rep_int_route: peer %s route %s (status %x) p_with gated replaced",peer->name, 
			iso_ptoa(&p_idrp_rt->p_with->nlri),p_idrp_rt->p_with->status)); 
#endif	/* IDRP_QOS */
 
	idrp_free_nlri_att_rec(p_idrp_rt->p_with,NLRI_LINKED_P_NEXT);
	idrp_free_idrpRoute(p_idrp_rt->p_with);	

	IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_REPLACE);

#ifdef	IDRP_QOS	
	trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP:ph1_with_rep_int_route: peer %s route %s (status %x(%x>) p_idrp_rt gated replaced",peer->name, 
			iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt->qos_status)); 
#else
	trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP:ph1_with_rep_int_route: peer %s route %s (status %x) p_idrp_rt gated replaced",peer->name, 
			iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status)); 
#endif
}

void 
ph1_add_int_route(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
idrp_rt_chain_walk	gateway;		/* chain for route locate search for gateway */ 
idrp_rt_chain_walk	preference;		/* chain for route locate serach for perference */
sockaddr_un	*p_dest;			/* destination address for gated Route search */
sockaddr_un	*p_mask;			/* mask for gated table Route search */
rt_entry	*p_rt;				/* gated route entry */
idrpRoute	*p_idrp_rt_old;			/* pointer to old route */
int		pref = 0;			/* preference for gated */
idrpPeer	*peer;				/* peer for route */
idrpRoute	*p_best_rt;			/* pointer to best route for location for idrp */

	
	peer = p_idrp_rt->peer;
	p_dest = (sockaddr_un *) idrp_local_mem_fit(sizeof(struct iso_net_addr));
	idrp_add_route_locate(p_dest,p_idrp_rt,&gateway,&preference,&p_best_rt);
	if (preference.p_rt)
		{
		if (mediate_pref_match(&preference,p_idrp_rt))
			{
			/* if it is the best preference - set to minus one */
			/* otherwise -  set value to pref plus 1 */
			p_idrp_rt->gated_pref -= 1;
			}
		else
			{
			p_idrp_rt->gated_pref += 1;
			}
			
		free_rt_chain_walk(preference.p_next,peer);
		}

	if (gateway.p_rt)
		{
		/* check that it really is a change */

		p_rt = gateway.p_rt;
		if (idrp_rt_change(p_rt,p_idrp_rt))
			{
			/* unlink old idrp route and  link in new route 
			 */
			
			p_idrp_rt_old = idrp_repl_rt_gated(p_idrp_rt, p_rt);
			idrp_mod_rt_gated(p_idrp_rt,IDRP_RUN_PREF); 

			/* we need to do the idrp_route free
			 * - because idrp_repl and mod
			 * - we do not free the output list because the
			 *  the idrp_repl_rt_gated has already relinked the
			 *  the new route into the outbound list  
			 *   
			 * captured the gated route
			 */

			idrp_free_nlri_att_rec(p_idrp_rt_old,NLRI_LINKED_P_NEXT); /*free attribute linked through p_nlri */ 
			idrp_free_idrpRoute(p_idrp_rt_old);
			}
		
		else
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("peer %s route %s said changed but didn't really", 
				peer->name,iso_ptoa(&p_idrp_rt->nlri)));
			}
		free_rt_chain_walk(gateway.p_next,peer);
		} /* end of if p_gate.p_rt - found a gateway match */
	else
		{
		idrp_add_rt_to_gated(p_idrp_rt,p_dest);
#ifdef	IDRP_QOS
		if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
			{
			qos_active_status(p_idrp_rt,p_idrp_rt->p_attr->rib_id);
			}
#endif	/* IDRP_QOS */
		}

	IDRP_MEM_FIT_FREE(p_dest);

}

/* ph1_with_ext_route
 * Withdraw an external route 
 *
 * (Merit Design Doc Section 4.3.3.2)
 */
void
ph1_with_ext_route(p_idrp_rt,p_ext_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ext_ann_list;
{
idrpPeer	*peer;
idrpPeer	*p_trace_peer;
rt_entry	*p_rt;
int		status_illegal;
idrp_rt_chain_walk	*p_gate;	/* return chain for matched gateways */
idrp_rt_chain_walk	*p_pref;	/* return chain for matched idrp preferences */ 
sockaddr_un		*p_dest;	/* destination */
idrpRoute		*p_best_ext;	/* best external route  */
u_int			status;

	p_trace_peer = &idrp_this_node;
	/* peer and rt_entry for gated table */

	peer = p_idrp_rt->peer;
	p_rt = p_idrp_rt->p_rt;
	p_dest = (sockaddr_un *) NULL;
	p_pref = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	p_gate = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	bzero(p_pref,sizeof(idrp_rt_chain_walk));
	bzero(p_gate,sizeof(idrp_rt_chain_walk));


	/* Is this route in AdjRib, Best External and Local Rib? */

	status = p_idrp_rt->status & IDRP_STATUS_INT_MASK; 

	if (status == (IDRP_STATUS_ADJ_RIB | IDRP_STATUS_BEST_EXT | IDRP_STATUS_LOC_RIB))
		{
		/* AdjRib, BestExternal and Local Rib */
 
		if (!idrp_with_route_locate(p_dest, p_idrp_rt, p_gate, p_pref, &p_best_ext))
			{
			/* no route found for this nlri - logic error in code */
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_ext_route) no route to NLRI %s from peer %s", 
				iso_ptoa(&p_idrp_rt->nlri), peer->name));
			task_quit(0);
			}
		
		if (p_gate->p_rt != p_idrp_rt->p_rt || p_gate->p_next->p_rt)  
			{
			/* 2nd route this destination and this gateway -- it's illegal */
			trace_tf (idrp_trace_options, 0,  LOG_WARNING, 
				("IDRP (ph1_with_ext_route) duplicate route to NLRI %s from peer %s", 
				iso_ptoa(&p_idrp_rt->nlri), peer->name));
			task_quit(0);
			}

		/* pull this route out of the BER chain -- idrp_del_best_ext returns a pointer to the 
		 * new best if found, else NULL.
		 */

		p_best_ext = idrp_del_best_ext(p_idrp_rt);

		/* set the withdraw flag in this route */
		
		IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_WITH);

		if (p_best_ext)
			{
			/* We have a new best external route 
			 *   1) link new route to phase1 announce lsit
			 *   2) delete this route - (idrp flash will do final delete )
			 * - watch that this route gets idrp route cleared  
			 */

			trace_tf(idrp_trace_options, TR_NORMAL,0,(" 1) set best ext on %s (peer %s) p_idrp_rt ph1_with_ext",iso_ptoa(&p_best_ext->nlri),p_best_ext->peer->name)); 
			trace_tf(idrp_trace_options, TR_NORMAL,0, (" 1) clear best ext on %s (peer %s) p_idrp_rt ph1_with_ext",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name)); 

			link_ann_list(p_best_ext, p_ext_ann_list, NLRI_LINKED_P_ANN);

			IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);
			idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);
			}
		else
			{
			  if (p_idrp_rt->p_p_best_ext)
			    {
			      trace_tf(idrp_trace_options, TR_PHASE3,0, (" refcount for deleting no other extern route -- NLRI %s, refcount %d",
									  iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->p_p_best_ext->refcount));
			    }
			  else
			    {
			      trace_tf(idrp_trace_options, TR_PHASE3,0, (" no best external structure, deleteing no other extern route -- NLRI is  %s",
									 iso_ptoa(&p_idrp_rt->nlri)));
			    }
				

	
			/* no replacement for this route, it was the only one
			 * However, it is a local rib, so we will get an 
			 * announcement in phase3 of a replacement
			 * - Don't clear the IDRP announce bit if this
			 *   is indeed a damped route
			 * - that assures that the minimum route
			 *   advertisement code will see the
			 *   route to do clearnup 
			 */

			link_ann_list(p_idrp_rt, p_ext_ann_list, NLRI_LINKED_P_WITH);

			IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);
			idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);
			}
		} /* if in Adj_RIB and Best_Ext and Loc_RIB */
	
	else if (status == (IDRP_STATUS_ADJ_RIB | IDRP_STATUS_BEST_EXT))
		{
		/* locate any associated routes to the same destination */

		if (!idrp_with_route_locate (p_dest,p_idrp_rt, p_gate, p_pref, &p_best_ext))
			{
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_ext_route) no gated rt NLRI %s from peer %s", 
				iso_ptoa(&p_idrp_rt->nlri), peer->name));
			task_quit(0);
			}
			
		if ((p_gate->p_rt != p_idrp_rt->p_rt) && !p_idrp_rt->p_min_adv)
			{
			/* 2nd route this destination and this gateway -- it's illegal unless	*/
			/* min_route_advertisement is set on the route				*/
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_ext_route) duplicate/no min adv timer route to NLRI %s from peer %s", 
				iso_ptoa(&p_idrp_rt->nlri), peer->name));
			task_quit(0);
			}

		if (p_best_ext != p_idrp_rt)
			{
			trace_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_ext_route) not best external but flag set NLRI %s from peer %s", 
				iso_ptoa(&p_idrp_rt->nlri), peer->name));
			}

		/* find the new best external since this route is deleted -- idrp_del_best_ext returns pointer
		 * to the new best, if any */
 
		p_best_ext = idrp_del_best_ext(p_idrp_rt);

		if (p_best_ext)
			{
			/* We have a new best external route available
			 * 1) link to announce list
			 * 2) delete the gated route
			 * 3) free the idrp route structures 
			 */

			trace_tf(idrp_trace_options, TR_NORMAL,0,(" 2) set best ext on %s p_idrp_rt ph1_with_ext",iso_ptoa(&p_idrp_rt->nlri)); 
			link_ann_list(p_best_ext, p_ext_ann_list, NLRI_LINKED_P_ANN));

			if (!IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
				{
				idrp_free_outlist(p_idrp_rt);	
				trace_tf (idrp_trace_options, TR_NORMAL,0, ("ph1_with_ext_route 2aa) deleteing route nlri"));
				idrp_free_nlri_att_rec(p_idrp_rt, NLRI_LINKED_P_WITH);
				idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);	
				idrp_free_idrpRoute(p_idrp_rt);
				}
			else 
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);
				idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);	
				}
			}
		else
			{
			/* no replacement for this route, it was the only one 
			 */

			  if (p_idrp_rt->p_p_best_ext)
			    {
			      trace_tf(idrp_trace_options, TR_PHASE3,0,(" deleting withdrawn route -- no replacement. NLRI is %s, refcnt %d",
									iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->p_p_best_ext->refcount));
			    }
			  else
			    {
			      trace_tf(idrp_trace_options, TR_PHASE3,0,(" deleting withdrawn route -- no replacement, no p_p_best_ext. NLRI is %s",
									iso_ptoa(&p_idrp_rt->nlri)));
			    }
				

			IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_WITH);
			IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DEL_SEND);
			link_ann_list(p_idrp_rt, p_ext_ann_list, NLRI_LINKED_P_WITH);
                        }
		} /* if in Adj_RIB and Best_Ext */

	else if (status == IDRP_STATUS_ADJ_RIB)
		{
		
			/* only in AdjRib so just delete the route right now
			 * relink the best routes chain around this route
			 * 
			 */

			p_best_ext = idrp_del_best_ext(p_idrp_rt);

			/* idrp_del_best_ext returns NULL if the best route didn't
			 * change, so check for NULL return value -- error if not
			 */

			if (p_best_ext != NULL)
				{
				trace_tf(idrp_trace_options, TR_NORMAL,0,(" Error -- idrp_del_best_ext returned non-null pointer 0x%x, STATUS_BER not set", 
					p_best_ext));
				}

			if (!IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
				{
				idrp_free_outlist(p_idrp_rt);
				idrp_free_nlri_att_rec(p_idrp_rt, NLRI_LINKED_P_WITH);
				idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);
				idrp_free_idrpRoute(p_idrp_rt);

				}
			else
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt, IDRP_STATUS_DELETE);
				idrp_del_rt_gated(p_idrp_rt,IDRP_NO_RESET_RT_BIT);
				}
		} /* if in Adj_RIB */

	else /* not in Adj_RIB, must be an error */
		{
			trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
				("IDRP (ph1_with_ext_route) withdrawn route to NLRI %s status: %x from peer %s is not in Adj_RIB",
					 iso_ptoa(&p_idrp_rt->nlri),status, peer->name));
		}

	free_rt_chain_walk(p_pref->p_next,peer);
	free_rt_chain_walk(p_gate->p_next,peer);
	IDRP_MEM_FIT_FREE(p_pref);
	IDRP_MEM_FIT_FREE(p_gate);
}


/*
 * ph1_with_rep_ext_route -- withdraw with replace of external route.
 *
 * Called from: phase1_external().
 *
 * (Merit IDRP Design Doc 4.3.2.2)
 */

void 
ph1_with_rep_ext_route(p_idrp_rt,p_ext_ann_list)
idrpRoute 	*p_idrp_rt;
idrp_ann_list	*p_ext_ann_list;
{
idrpPeer	*peer;
rt_entry	*p_rt;
int		status_illegal;
idrp_rt_chain_walk	*p_gate;	/* return chain for matched gateways */
idrp_rt_chain_walk	*p_pref;	/* return chain for matched idrp preferences */ 
sockaddr_un		*p_dest;
idrpRoute		*p_best_ext;	/* best external route  */
int			pref;


	peer = p_idrp_rt->peer;
	p_rt = p_idrp_rt->p_rt;
	p_dest = (sockaddr_un *) NULL;	

	/* initialization of routine lists */
	p_gate = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	p_pref = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));


	if (!idrp_add_route_locate(p_dest,p_idrp_rt,p_gate,p_pref,&p_best_ext))
		{
		/* quit the task - you should find this route in the gated tables */

		trace_log_tf (idrp_trace_options, 0, LOG_WARNING, ("IDRP:ph1_with_rep_ext_route: peer %s route %s no gated rt", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		task_quit(0);
		}

	/* The gated route found for this node should be
	 * the idrp gated route, this existing route
	 */


        /* This is only good without multiple loc-rib. */
	if (p_gate->p_rt && (p_gate->p_rt != p_idrp_rt->p_with->p_rt))
		{	
		trace_log_tf (idrp_trace_options,0, LOG_WARNING,("IDRP:ph1_with_rep_int_route: peer %s route %s no gated rt for this peer", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		trace_log_tf (idrp_trace_options, 0,LOG_WARNING,("IDRP:ph1_with_rep_ext_route: p_rt %x (%x) ",p_gate->p_rt,p_idrp_rt->p_with->p_rt)); 
		task_quit(0);
		}

	if (p_pref->p_rt)
		{
		if (mediate_pref_match(p_pref,p_idrp_rt))
			{
			/* preference value is the best preference
			 * 
			 * otherwise -  set value to pref plus 1 
			 */
			p_idrp_rt->gated_pref -= 1;
			}
		else
			{
			p_idrp_rt->gated_pref += 1;
			}
		}

	if (p_gate->p_rt != p_idrp_rt->p_with->p_rt)
		{	
		trace_log_tf (idrp_trace_options, 0, LOG_WARNING,("IDRP:ph1_with_rep_ext_route: peer %s route %s gated rt doesnt match withdrawn route", 
			peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		task_quit(0);
		}

	free_rt_chain_walk(p_pref->p_next,peer);	
	free_rt_chain_walk(p_gate->p_next,peer);	
	IDRP_MEM_FIT_FREE(p_pref);
	IDRP_MEM_FIT_FREE(p_gate);
	idrp_replace_ext(p_idrp_rt,p_idrp_rt->p_with->p_rt,p_ext_ann_list,p_best_ext,p_idrp_rt->gated_pref);
}


/*
 * ph1_add_ext_route -- phase1 add external route
 *
 * Called from: phase1_external()
 */

void 
ph1_add_ext_route(p_idrp_rt,p_ext_ann_list)
idrpRoute 	*p_idrp_rt;
idrp_ann_list	*p_ext_ann_list;
{
idrp_rt_chain_walk	gateway;		/* chain for route locate search for gateway */ 
idrp_rt_chain_walk	preference;		/* chain for route locate search for preference */
sockaddr_un 	*p_dest;			/* destination address for gated Route search */
rt_entry	*p_rt;				/* gated route entry */
idrpRoute	*p_idrp_rt_old;			/* pointer to old route */
int		pref;				/* preference for gated */
idrpRoute	*p_best_rt;			/* pointer to best route for location for idrp */
idrpPeer	*peer;

	
	peer = p_idrp_rt->peer;
	trace_tf (idrp_trace_options, TR_NORMAL,0,("Called ph1_add_ext_route peer %s route_id %d", 
			peer->name,p_idrp_rt->route_id_in));

	/* set AdjRib flag at beginning */

	p_dest = (sockaddr_un *) idrp_local_mem_fit(sizeof(struct iso_net_addr));
	trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d", 
		__FILE__, __LINE__));
	idrp_add_route_locate((sockaddr_un *) p_dest,p_idrp_rt,&gateway,&preference,&p_best_rt);

	/* If existing routes with the same preference were found, then do a tie-break. */


	trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d", 
		__FILE__, __LINE__));
	if (preference.p_rt)
		{
		if (mediate_pref_match(&preference,p_idrp_rt))
			{
			/* if it is the best preference - set to minus one 
			 * otherwise -  set value to pref plus 1 
			 */
			p_idrp_rt->gated_pref -= 1;
			}
		else
			{
			p_idrp_rt->gated_pref += 1;
			}
			
		free_rt_chain_walk(preference.p_next,peer);
		}


	/* If the "new" route is a replacement,
	 *  that is from a gateway  that has already given us this rt
	 */
	trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d", 
		__FILE__, __LINE__));

	if (gateway.p_rt)
		{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d", 
		__FILE__, __LINE__));
		/* Implicit Replace 
		 * - Check to see if we really need to
		 *   change this route
		 * 	(idrp_rt_change - checks the idrp_attributes
		 * 	to see if this is really a change)
		 */

		p_rt = gateway.p_rt;
		if (idrp_rt_change(p_rt,p_idrp_rt))
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp implicit replace peer %s newroute %s (%x), same peer %s route %s",
					peer->name, iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt, iso_ptoa(&p_rt->rt_idrp->nlri),
					iso_ptoa(&p_rt->rt_idrp->nlri))); 

			trace_tf(idrp_trace_options,TR_NORMAL,0,("implict 2) p_best_rt = %x (%x)",p_best_rt,p_idrp_rt));	
			idrp_replace_ext(p_idrp_rt,p_rt,p_ext_ann_list,p_best_rt,p_idrp_rt->gated_pref);
			}
		else
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("IDRP peer %s route %s said changed but didn't really", 
				peer->name,iso_ptoa(&p_idrp_rt->nlri)));
			}


		free_rt_chain_walk(gateway.p_next,peer);
		} /* end of if p_gate.p_rt - found a gateway match */
	else
		{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d p_best = %x", 
		__FILE__, __LINE__,p_best_rt));
		/* True add of external route to gated  */ 
		 
		/*  Do other external routes exist? -- if so, sanity-check
		 */

		if (p_best_rt == (idrpRoute *) 0 )	
			{
			/* no best external should mean no external route */
			
			if (gateway.p_next)
				{
				trace_tf(idrp_trace_options, TR_NORMAL,0, ("Best external destination %s peer %s broken no route added ",
					iso_ptoa(&p_idrp_rt->nlri),peer->name));

				IDRP_MEM_FIT_FREE(p_dest);
				return;
				}
			}
			
		/* insert_in_pref_order() has gotten smarter and does :
		 * - if no BER exists, create structs, make p_idrp_rt the best & return TRUE
		 * - if BER exists, see if p_idrp_rt should become best, if so twiddle bits & return TRUE
		 * - if BER exists and p_idrp_rt is not best, just insert and return FALSE
		 */
	
		trace_tf (idrp_trace_options, TR_NORMAL,0,("Got here:  %s, line %d p_best = %x", 
		__FILE__, __LINE__,p_best_rt));

		if (insert_in_pref_order(p_idrp_rt, p_best_rt))
			{
			/* if TRUE, p_idrp_rt is the new best so it needs to be advertised */
			link_ann_list(p_idrp_rt,p_ext_ann_list,NLRI_LINKED_P_ANN);
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("route %s status %x ",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));
			if (p_best_rt)
				{
				trace_tf (idrp_trace_options, TR_NORMAL,0,("route %s status %x ",iso_ptoa(&p_best_rt->nlri),p_best_rt->status));
				}
			}
			
		idrp_add_rt_to_gated(p_idrp_rt,(sockaddr_un *) p_dest);
#ifdef	IDRP_QOS
		if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
			{
			qos_active_status(p_idrp_rt,p_idrp_rt->p_attr->rib_id);
			}
#endif	/* IDRP_QOS */
		}

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

	return;

}


/* idrp_replace external routes 
 * - replace routes for implict or explicit
 *   replace of external routes
 *    
 * (Merit IDRP Design Doc 4.3.3.2)
 */

void 
idrp_replace_ext(p_idrp_rt,p_rt,p_ext_ann_list,p_best_rt,pref)
idrpRoute	*p_idrp_rt;	/* idrpRoute replacing old route */
rt_entry	*p_rt;		/* gated rt_entry for old idrpRoute being replaced */
idrp_ann_list	*p_ext_ann_list;  /* idrp ann list for ph 1 ext routes to int peers */
idrpRoute	*p_best_rt;	/* current bext ext route */
int		pref;		/* mediated preference */
{
idrpRoute	*p_old_idrp_rt;	/* old idrp route being replaced */
idrpRoute	*p_best;		/* scratch to hold new best route */
int		status;

	p_old_idrp_rt = (idrpRoute*) p_rt->rt_idrp;
	status = p_old_idrp_rt->status & IDRP_STATUS_INT_MASK;
	trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp_replace_ext peer %s nlri %s new pref %d status %d", 
			p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri),pref,status));


	/* handle the route based on the old route status */
  
	switch (status)
		{
		case (IDRP_STATUS_ADJ_RIB):
			{
			if (p_best_rt)
				{
				/* there is another best route
				 *
				 * insert_in_pref_order() handles divers cases
				 */

				if (insert_in_pref_order(p_idrp_rt, p_best_rt))
					{
					/* if TRUE, p_idrp_rt is the new best */
					trace_tf (idrp_trace_options, TR_NORMAL,0,(" 5) route %s status %x",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));

					link_ann_list(p_idrp_rt, p_ext_ann_list, NLRI_LINKED_P_ANN);
					}
				}				
			else
				{
				/* No best route - old idrp_rt was external 
				 * And we didn't have the BEST_EXT set??
				 * - trace this error
				 */

	
				trace_log_tf (idrp_trace_options, 0, LOG_WARNING, 
					("IDRP idrp_replace_ext peer %s has route id %d with status AdjRib %x for nlri %s", 
					p_idrp_rt->peer->name,p_idrp_rt->route_id_in,status,iso_ptoa(&p_idrp_rt->nlri)));

				}
	
			break;
			}

		case (IDRP_STATUS_ADJ_RIB | IDRP_STATUS_BEST_EXT | IDRP_STATUS_LOC_RIB):
		case (IDRP_STATUS_ADJ_RIB | IDRP_STATUS_BEST_EXT):
			{
			/* now that insert_in_pref_order and idrp_del_best_ext are smarter
			 * about maintaining BER flags, this code can be simpler.  This version
			 * collapses the two former cases (new route is still best or new 
			 * route isn't still best so there's a third route which is best)
			 * into one.  Differences are handled inside insert and del.
			 */

			/* sanity checking -- is this really the best external route?
			 */
			assert(IDRP_BER(p_old_idrp_rt) == p_old_idrp_rt);

			/* both insert_in_pref_order and idrp_del_best_ext return non-NULL if the
			 * best ext route changes.  We don't even have to check this in this 
			 * case, though:   of them must ALWAYS return non-NULL --
			 * either the replacement route is now best, or some other route is!
			 *
			 * A future optimization would use something like 
			 * "idrp_swap_best_est(p_idrp_rt, p_idrp_rt_old)" to save a few grubby
			 * instructions which are wasted by using the general routines.
			 */

			insert_in_pref_order(p_idrp_rt, p_old_idrp_rt);
			idrp_del_best_ext(p_old_idrp_rt);

			/* if replacement is still best ext, we need to copy LOC_RIB flag 
			 */
			IDRP_STATUS_BIT_TEST(p_idrp_rt, IDRP_STATUS_BEST_EXT)
				{ /* replacement is still best */
				IDRP_STATUS_BIT_TEST(p_old_idrp_rt,IDRP_STATUS_LOC_RIB)
					{
					/* copy over the status bit set
					 * - if preference changes then the phase3 logic
					 * - will change that
					 */
					IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
					}				
				trace_tf (idrp_trace_options, TR_NORMAL,0,(" 6) route %s status %x",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));
				}
			else /* some other route must be best now */
				{
				trace_tf (idrp_trace_options, TR_NORMAL,0, (" ) route %s status %x",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));

				/* sanity checking -- carried forward from old form of this function -- delete later
				 */

				assert(IDRP_BER(p_idrp_rt) != NULL);
 
				}

			/* now advertise whichever route became best ext
			 */
			
			link_ann_list(IDRP_BER(p_idrp_rt), p_ext_ann_list, NLRI_LINKED_P_ANN);

			break;
			}
		}
	
	/* replace the idrp route in gated route
	 * 
	 * this takes care of moving over minadv pointers and stuff if need be 
	 * - also the outbound lists are moved to the new route
	 */
	idrp_repl_rt_gated(p_idrp_rt, p_rt);

	/* change gated route so we flash if gated route changes */
	idrp_mod_rt_gated(p_idrp_rt,IDRP_RUN_PREF);	

	/* free up the old idrp route, but only if it's not being used still
	 * by minimim route advertisement code
	 */

	if (!IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
		{
		/* don't free the outlist because
		 * the idrp_repl_rt_gated routine
	         * has already gotten transfered the outlist
		 * to the new route 
		 */
 
		idrp_free_nlri_att_rec(p_old_idrp_rt,NLRI_LINKED_P_NEXT);
		idrp_free_idrpRoute(p_old_idrp_rt);
		}
	else
		{
		/* this should be redundant */
		IDRP_STATUS_BIT_SET(p_old_idrp_rt,IDRP_STATUS_DELETE);
		}
}


/*
 * send_best_ext(idrp_ann_list *p_ext_ann_list)
 *
 * Send best external routes to internal IDRP neighbors.
 *
 * (Merit IDRP design document 4.3.4.1, documented as "idrp_send_phase1_ann")
 */
void 
send_best_ext(p_ext_ann_list)
idrp_ann_list	*p_ext_ann_list;
{
idrpPeer	*peer;
idrp_ann_list	*p_atl;
idrpRoute	*p_wdraw;
idrpRoute	*p_irt;
idrpRoute	*p_irt_next;
int		family;
idrp_send_list	*p_send_list = NULL;

	/* send announce list of best_ext routes to each idrp internal peer */
	
	trace_tf (idrp_trace_options, TR_NORMAL,0, ("send_best_ext called "));

	/* walk through internal peers sending the
	 * information
	 * -
	 * If no internal peers, than just skip
	 */

	if ((idrp_neighbors - idrp_ext_neighbors) == 0)
		return;

	IDRP_INT_PEER(peer)
		{
		/* if this peer is not connected
		 * - skip sending the
		 */

		if (!(PEER_CONNECTED(peer))) 
			continue;

		ANN_LIST(p_atl,p_ext_ann_list)
			{
			NLRI_FAMILY_LOOP(family)
				{
				ANN_WITH_LIST_DEL_WALK(p_wdraw,family,p_atl,p_irt_next)
					{
						/* for each withdraw NLRI/idrpRoute structure, 
						 * process withdraw into send list for a single
						 * update bispdu.  If update bispdu is full, it
						 * will be set to neighbor (send_with_attr).
						 */
						 p_send_list = send_with_attr(p_wdraw, p_atl->p_attr, p_send_list,peer,IDRP_DIST_PH1_BEST_EXT);
					} ANN_WITH_LIST_DEL_WALK_END;

				ANN_ANN_LIST_WALK(p_irt,family,p_atl)
					{
					/* process each nlri/idrproute structure on announce
					 * list and add to a send list for a single update
					 * bispdu.  If the update bispdu is full, it will
					 * be sent to neighbor (send_nlri_attr).
					 */
					 p_send_list = send_nlri_attr(p_irt,p_atl->p_attr,p_send_list,peer);
					} ANN_ANN_LIST_WALK_END; 

				} NLRI_FAMILY_LOOP_END; 
			/* Flush the send list before starting on a new attribute record.
			 * Any outstanding announcements will be stuffed into a PDU and sent.
			 */

			flush_att_send_list(peer,p_atl->p_attr, p_send_list);

			} ANN_LIST_END; 

		/* clean up early announcements for best external
		 * routes
		 */
  
		idrp_clean_early_proc(p_ext_ann_list);
		} IDRP_INT_PEER_END;
}
	 

/*
 * del_routes_best_ext(idrp_ann_list *p_ext_ann_list)
 *
 * Delete defunct best external routes.  We only need to do this for BERs that
 * were not in the LOC_Rib when they were withdrawn, so gated will not flash
 * us to inform us that they were deleted.
 *
 * (Merit IDRP design document 4.3.4)
 */
void 
del_routes_best_ext(p_ext_ann_list)
idrp_ann_list	*p_ext_ann_list;
{
idrp_ann_list	*p_atl;
idrpRoute	*p_wdraw,*p_rt_next;
int		family;

	/* delete routes on best external routes to each idrp internal peer */
	
	trace_tf (idrp_trace_options, TR_NORMAL,0, ("del_routes_best_ext called "));

	/* "Walk withdraw route NLRI list doing... */

	ANN_LIST(p_atl,p_ext_ann_list)
		{
		NLRI_FAMILY_LOOP(family)
			{
			ANN_WITH_LIST_DEL_WALK(p_wdraw,family,p_atl,p_rt_next)
				{
					/* Coding construct for ANN_WITH_LIST_DEL WALK
					 * should be changed - skh 12/13/93 
					 */

					p_rt_next = p_wdraw->p_with;

					if (p_wdraw->status & IDRP_STATUS_DEL_SEND)
						{
						/* skh/jgs -- Verify that we keep route around
						 *         -- awaiting delete with delete flag
						 *	   --only if on damping queue. 
						 */
						if (!IS_IDRP_DAMPED_ROUTE(p_wdraw))
							{
							idrp_free_outlist(p_wdraw);
							idrp_free_nlri_att_rec(p_wdraw, NLRI_LINKED_P_WITH);
							idrp_del_rt_gated(p_wdraw,IDRP_RESET_RT_BIT);
						 	idrp_free_idrpRoute(p_wdraw);
							}
						else
							{
							IDRP_STATUS_BIT_SET(p_wdraw, IDRP_STATUS_DELETE);
							idrp_del_rt_gated(p_wdraw,IDRP_NO_RESET_RT_BIT);
							}
						}

				} ANN_WITH_LIST_DEL_WALK_END ;
			} NLRI_FAMILY_LOOP_END;
		} ANN_LIST_END; 
}




idrp_ann_list *
idrp_parse_to_ann(res,peer)
parse_results	*res;
idrpPeer	*peer;
{
idrp_ann_list *p_ann_list = 0;
idrp_ann_list *p_atl;	
idrp_attribute_record	*p_att;
idrp_attribute_record	*p_att_last = 0;
idrp_rid_hash	*p_rid = 0;
int 	i;


	if  (!res->p_att)	
		{
		/* no attribute in list - so return null idrp_ann_list  
		 * 
		 */
		return((idrp_ann_list *) 0);
		}

	
	IDRP_PARSE_ATT_LIST(p_att,res)
		{
		/* zero last attribute records p_parse_next
		 * entry
		 */

		if (p_att_last)
			{
			p_att_last->p_parse_next = (idrp_attribute_record *) 0;
			}

		/* with multiple route separators
		 * we will need to create an announce list
		 * with multiple attribute records
		 */

		 	
		p_atl = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));
		bzero(p_atl,sizeof(idrp_ann_list));
		p_atl->peer = peer;
		p_atl->p_attr = res->p_att;
	

		/* for now set the rib_id to default 
		 */

		if (p_atl->p_attr)
			{
			/* set the rib id from the attribute
			 * or leave at default for withdraws
			 */
 
			p_atl->rib_id = res->p_att->rib_id; 
			}



		/* normal announcement
		 */

		NLRI_FAMILY_LOOP(i)	
			{
			/* set up head and tail of ann_nlri list
			 * linked by p_next_nlri
			 * 
			 */

			p_atl->ann_nlri[i].head = p_att->p_parse_re->p_route[i];
			p_atl->ann_nlri[i].tail = p_att->p_parse_re->p_route_tail[i];
			
			} NLRI_FAMILY_LOOP_END;

		p_rid = idrp_add_hash_entry(peer,p_att->p_parse_re->route_id,p_att->p_parse_re->p_route);

		IDRP_STATUS_BIT_TEST(res,IDRP_RID_ROUTE_LOOP)
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0, (" RDLOOP in rid %d",p_rid->route_id));
			IDRP_STATUS_BIT_SET(p_rid,IDRP_RID_ROUTE_LOOP);
			}
		

		p_att->p_parse_re = (idrpRoute_entry *) 0;

		/* set-up the announce list
		 *
		 */

		if (p_ann_list)
			{
			p_ann_list->p_next = p_atl;
			p_ann_list = p_atl;
			}
		else
			{
			p_ann_list = p_atl;
			}
		p_att_last = p_att;

		} IDRP_PARSE_ATT_LIST_END;

	if (p_att_last)
		{
		p_att_last->p_parse_next = (idrp_attribute_record *) 0;
		}

	return (p_ann_list);
}


idrp_ann_list *
link_rid_to_with_list(p_with_list,p_rid,peer)
idrp_ann_list	*p_with_list;
idrp_rid_hash	*p_rid;
idrpPeer	*peer;
{
idrp_ann_list	*p_wtl;		/* withdraw list temporary */
idrp_ann_list	*p_atl;		/* with list walk temporary */ 
idrp_ann_list	*p_atl_last;	/* last entry on withdraw list of attributes */ 
idrpRoute	*p_idrp_rt;
idrpRoute	*p_idrp_rt_last;
idrpRoute	*p_next_nlri;
int		i;
int		nlri_cnt = 0;

	p_wtl = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));
	bzero(p_wtl,sizeof(idrp_ann_list));

	IDRP_STATUS_BIT_TEST(p_rid, IDRP_RID_ROUTE_GONE)
		{
		trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("No routes still exist for withdraw id = %d peer %s ",p_rid->route_id,peer->name));
		IDRP_MEM_FIT_FREE(p_wtl);
		return((idrp_ann_list *) NULL);
		}

	NLRI_FAMILY_LOOP(i)
		{
		p_wtl->with_nlri[i].head = p_rid->p_idrp_rt[i];

		/* loop pulling these routes off
		 * the p_next_nlri loop, unto the p_with link
		 * leave the p_next_nlri intact to
		 * allow for final removal of route
		 */

		if (p_rid->p_idrp_rt[i])
			{
			p_wtl->p_attr = p_rid->p_idrp_rt[i]->p_attr;
			p_wtl->rib_id = p_rid->p_idrp_rt[i]->p_attr->rib_id;
			p_idrp_rt_last = NULL;
			for (p_idrp_rt = p_wtl->with_nlri[i].head; p_idrp_rt; p_idrp_rt = p_idrp_rt->p_with)
				{
				p_idrp_rt->p_with_back = p_idrp_rt_last;
				p_idrp_rt->p_with = p_idrp_rt->p_next_nlri;
				nlri_cnt++;
				p_idrp_rt_last = p_idrp_rt;
				}
			p_wtl->with_nlri[i].tail = p_idrp_rt_last;	
			}
		else
			{
			p_wtl->with_nlri[i].tail = p_rid->p_idrp_rt[i];
			}
	      }
	p_wtl->peer = peer;

	if (nlri_cnt == 0)
		{
		/* no routes in the rid
		 *
		 */
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("No routes in withdraw id = %d peer %s ",p_rid->route_id,peer->name));
		IDRP_MEM_FIT_FREE(p_wtl);
		return((idrp_ann_list *) NULL);
		}

	/* has the withdraw list been started yet?
	 * - no (zero) then return just this
	 * idrp_announce list for the
	 * withdraw list
	 */
 

	if (!p_with_list)
		{
		return (p_wtl);
		}

	/* other announce list for this sequence of withdraw ids
	 */

 	
	ANN_LIST(p_atl,p_with_list)
		{
		if (p_atl->p_attr == p_wtl->p_attr)
			{
			/* found an existing attribute record
			 * in the announce list that matches
			 * - so process it quickly
			 */

			NLRI_FAMILY_LOOP(i)
				{
				/* if it has a tail set - then there are other routes
				 * - so link it to the end
			 	 */
				if (p_atl->with_nlri[i].tail)
					{
					if (p_wtl->with_nlri[i].head)
						{
						p_atl->with_nlri[i].tail->p_with = p_wtl->with_nlri[i].head;
						p_wtl->with_nlri[i].head->p_with_back = p_atl->with_nlri[i].tail;
						p_atl->with_nlri[i].tail = p_wtl->with_nlri[i].tail;
						}
					}
				else
					{
					/* no tail - so this attribute, this nlri family
					 * not seen, but the attribute record is the same
					 */

					p_atl->with_nlri[i].head = p_wtl->with_nlri[i].head;
					p_atl->with_nlri[i].tail = p_wtl->with_nlri[i].tail;
					}	
				}
			IDRP_MEM_FIT_FREE(p_wtl);
			return(p_with_list);
			}
		p_atl_last = p_atl;
		}


	/* this attribute record not found in the withdraw list
	 * - so add a new announce list entry on for
	 * this record 
	 */

	p_atl_last->p_next = p_wtl;

	return(p_with_list);
}	

idrp_ann_list *
idrp_process_rdloop(p_with_list,p_ann_list,peer)
idrp_ann_list *p_with_list;	
idrp_ann_list *p_ann_list;	
idrpPeer	*peer;
{
idrpRoute	*p_irt;
idrpRoute	*p_irt_w;
idrpRoute	*p_irt_next = 0;
idrp_ann_list *p_atl;	
idrp_ann_list *p_atl_w;	
int 	i;
int	found;
idrp_rt_chain_walk      *p_gate;
idrp_rt_chain_walk      *p_pref;
sockaddr_un             *p_dest = (sockaddr_un *) NULL;
idrpRoute               *p_best_rt;
int                     pref;


	trace_tf(idrp_trace_options, TR_NORMAL,0,("rdloop processing peer %s",peer->name));
	p_dest = (sockaddr_un *) idrp_local_mem_fit(sizeof(struct iso_net_addr));
	p_gate = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));
	p_pref = (idrp_rt_chain_walk *) idrp_local_mem_fit(sizeof(idrp_rt_chain_walk));

	/* pull out withdraws
	 */

	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			ANN_ANN_NEXT_NLRI_DEL(p_irt,i,p_atl,p_irt_next)
				{
				/*
				 * 1) try to find route in the tables
				 *    - all preparation necessary for addition any way
				 * 
				 * 2) check to see if the nlri is
				 *    already listed in the withdraw
				 *    - then just leave alone
				 *
				 *
				 * 3) if announcement not already in withdraws -
				 *     - look for current nlri in pdus
				 *     And withdraw
				 *   
				 */

				IDRP_STATUS_BIT_SET(p_irt,(IDRP_STATUS_RDLOOP | IDRP_STATUS_ADJ_RIB));

				bzero(p_dest,sizeof(struct iso_net_addr));				
				bzero(p_gate,sizeof(idrp_rt_chain_walk));				
				bzero(p_dest,sizeof(idrp_rt_chain_walk));				



				/*  1) Withdraw-Replace - will simply
				 *     function as withdraw with a subsequent announcement
				 *     at metric infinity, noinstall, no advise
				 *
				 *  2) Withdraws function as just withdraws
				 *  3) implicit withdraws -
				 * 	 function as withdraw and 
				 * 	 noadvise, no install addition 
				 * 
				 *  4) Additions function as
				 *     additions with high noadvise,noinstall flags 
				 */

				found = FALSE;

				/* only check if we have a withdraw list
				 */
 
				if ((p_with_list != (idrp_ann_list *) NULL) &&
					(p_with_list->p_attr != (idrp_attribute_record *) NULL))
					{
					trace_tf(idrp_trace_options, TR_NORMAL,0,("rdloop processing withdraw lists exist and looping for nlri %s (peer %s)",
						iso_ptoa(&p_irt->nlri),peer->name));
					 
					found = find_nlri_in_with(p_irt,p_with_list,i);	
					}
				 
				if (!found)
					{
					/* Not in withdraw list so -
					 * - this is not case of Withdraw/replace
					 * - check for implicit withdraw 
					 */
   
					idrp_add_route_locate(p_dest,p_irt,p_gate,p_pref,&p_best_rt);

					if ((p_gate->p_rt != (rt_entry *) NULL) && 
						(p_gate->p_rt->rt_idrp != (idrpRoute *) NULL))
						{
						/* -- skh here I need to re-examine
						 * -- skh after break
						 * -- skh to make sure we are detaching things
						 * -- as they are suppose to be detached 
						 */
						if (p_with_list == (idrp_ann_list *) NULL)
							{
							p_with_list = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list *));
							bzero(p_with_list,sizeof(idrp_ann_list));
							}
			
						trace_tf(idrp_trace_options, TR_NORMAL,0,(" nlri %s linked to withdraw list for RDLOOP ann",
								iso_ptoa(&p_gate->p_rt->rt_idrp->nlri)));
 
						link_ann_list(p_gate->p_rt->rt_idrp,p_with_list,NLRI_LINKED_P_WITH);
						}
					}		

				/* free the route from the attribute record 
				 */

				idrp_free_nlri_att_rec(p_irt,NLRI_LINKED_P_NEXT);
				idrp_free_idrpRoute(p_irt);

				/* In the future, we will
				 *  allow the routes to be added to gated,
				 * for now, we release the route once we've check for 
				 *  
				 * things to add for keeping looped routes:
				 * 
				 * idrp_add_rt_to_gated:
			 	 *  if (IDRP_STUAT_RDLOOP)
				 *  set the gated noinstall, noadvise flag
				 * the idrp_pref - must set 255 for the RD_LOOP route
				 *   with no additional policy
				 */
					
				} ANN_ANN_NEXT_NLRI_DEL_END;
			} NLRI_FAMILY_LOOP_END;
		} ANN_LIST_END;	

	IDRP_MEM_FIT_FREE(p_dest);
	IDRP_MEM_FIT_FREE(p_gate);
	IDRP_MEM_FIT_FREE(p_pref);
	return(p_with_list);

}

 
int
find_nlri_in_with(p_idrp_rt,p_with_list,family)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_with_list;
int		family;
{
idrp_ann_list	*p_atl;
idrpRoute	*p_irt;

	ANN_LIST(p_atl,p_with_list)
		{
		ANN_WITH_LIST_WALK(p_irt,family,p_atl)
			{
			if ((!isopfxcompare(&p_irt->nlri,&p_idrp_rt->nlri)))
				return(TRUE);
			
			} ANN_WITH_LIST_WALK_END;
		} ANN_LIST_END;
return(FALSE);
}
