/* 
 * $Id: idrp_rt_phase3.c,v 1.16 1995/11/06 16:30:15 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_phase3.c 
 *  - phase 3 routing table code  
 * 
 * Phase 3
 * 
 * idrp_flash(task,rtl) - receive all the active
 *		       routes from gated (LOC_RIB)
 *
 * phase_3(rtl) - phase 3 processing
 * idrp_phase3_dump(peer,rtl[]) - phase 3 processing initial peer up 
 *
 * phase3_status_change -- A route's status has changed -- do something
 *
 * ph3_status_cases 1-8 -- The noble eightfold ways in which a route's status
 *  may have changed.
 *
 * idrp_send_phase3_routes
 * 
 *  
 * 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
 * 
 * idrp_newpolicy -> phase3_status_change 
 *     with  special calls to phase3_newpolicy 
 * if any peers up, send out routes (idrp_send-phase3_route)
 * if all peer down, wait for phase3_dump to get it. 
 * 
 *
 */ 

	
/* 
 * 
 * Phase 3
 *
 *
 * idrp_flash(task,rtl) - receive all the active
 *		       routes from gated (LOC_RIB)
 *
 * phase_3(rtl) - phase 3 processing
 *  
 * phase3_int(peer,rtl); - phase three processing for internal
 *			   peer (only on initial turn up)
 * phase3_ext(peer,rtl); - phase three processing for external peers
 * 
 *  dist_policy(send_list,peer) - run distribution policy
 *                                on the send_list for this peer
 *	returns modified send list
 *
 *  DIST(idrpRoute,peer) - run policy on a NLRI and peer 
 */ 	


/* idrp_flash - route called by gated when new idrp routes */ 

void
idrp_flash(tp,rtl)
task *tp;
rt_list *rtl;
{
     /* we get a flash update from the gated process 
      * walk the gated change list to find the update
      */ 

     trace_tf(idrp_trace_options, TR_TASK ,0, ("idrp_do_flash: Doing flash update for IDRP"));

    /* do processing, but do not send routes out
     * unless a peer is established */

	phase3_process(rtl);
}

/*----------------------------------------------------------------------------
 * phase3_process()
 *
 * Take the route list gated flashes us with, and do the following things:
 * 1. Build an announce/withdraw list -- phase3_status_change()
 * 2. Figure out which routes are being damped, or should be damped -- idrp_minadv_annlist()
 *    When idrp_minadv_annlist() is done, the announce list should only contain routes
 *    which can really be advertised (i.e., aren't being damped).
 * 3. Actually send the routes.  This may do policy and other things before sending.
 *    The announce list should be looked at as CANDIDATES for announcement, not as 
 *    things which will definitely be announced.  -- idrp_send_phase3_routes()
 * 4. Clean withdrawn routes up.  -- idrp_del_sent_routes()
 */


void
phase3_process(rtl)
rt_list *rtl;
{
idrp_ann_list *p_def_ann;
int	       rib_id = 0;		

	/* create the send lists from the rt_list */

	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp flash processing called "));
	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp status change routine called")); 

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

	/* idrp new policy is not active 
	 */

	phase3_status_change(rtl,p_def_ann,FALSE);
	phase3_rib(p_def_ann,RIB_ID_DEFAULT);
	IDRP_MEM_FIT_FREE(p_def_ann);

#ifdef	IDRP_QOS
	idrp_qos_chg_rib();
#endif 
}

void
phase3_rib(p_idrp_ann,rib_id)
idrp_ann_list	*p_idrp_ann;
int		rib_id;
{	
	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("phase3_rib called p_ann = %x,  rib id = %d",p_idrp_ann,rib_id));
    	if (idrp_n_established)
		{
		if (idrp_ann_list_empty(p_idrp_ann))
			{
			trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp status change gave no announcements/withdrawals rib id %d",rib_id)); 
			return;
			}

		trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp status change routine finished with announcements/withdrawals")); 
	
		/* Now twiddle with the announce list.  We do a number of things here:
		 * 1. If a route should be damped rather than announced, take it off the announce list.
		 *    This deals with the announce list.
		 * 2. If an idrpRoute structure should be deleted, but a withdraw doesn't need to be
		 *    sent (if it's a replaced route, in other words)
		 *
		 *    The idrpRoute structure can be deleted only if it is not the damped rout.  
		 * 	
		 * We will need to keep the replaced routes around a little longer.
		 * The reason is this:  A new route might not be allowed to be propagated to a certain
		 * peer (policy, or dist list).  In that case, we need to send it an explicit w/drawal
		 * and for that we need the route ID, and for THAT we need the idrpRoute.
		 * 
		 * - This is implemented with the IDRP_STATUS_REPLACE flag,
		 *   and the p_replace variable. 
		 */

		p_idrp_ann = idrp_minadv_annlist(p_idrp_ann,IDRP_MINADV_PHASE3);	

		trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp sending routes to peers ")); 

		if (p_idrp_ann)
			{
			idrp_send_phase3_routes(p_idrp_ann);
			}

		trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp sent routes to peers ")); 

		/* schedule idrp_process_minadv() to run when the min route adver timer expires */

		idrp_set_minadv_timers();
		}	


	/* called even if no peers to deal with case where deleting local
	 * gated static routes. These routines should be looked at as part gated
	 * route table handling and part IDRP 
	 */

	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("idrp deleting routes requested DELETE after SENT"));

	if (p_idrp_ann)
		{
		idrp_del_sent_routes(p_idrp_ann);

		/* free the announce list space */

		free_ann_list(p_idrp_ann);	
		}
		
}
	      

int
idrp_phase3_dump(peer,actlist)
idrpPeer	*peer;
rt_list		*actlist[];
{
int		i;		/* loop counter NLRI_FAMILY_LOOP */
idrp_ann_list	ann_list;	/* announce list */
rt_entry	*p_rt;		/* gated route entry */	
idrpRoute	*p_irt;		/* idrpRoute pointer */	
idrp_attribute_record	*p_att;	/* idrpRoute attribute */ 
u_int		rtbit;		/* bit to set in route when announcing other
				 * protocols route 
				 */

rt_head		*rth;		/* header of destination in gated */
rt_list		*rtl;		/* active route list */ 
idrp_ann_list	*p_ann_list;	/* pointer for announce list for loop */
idrp_send_list	*p_send_list = 0;	/* send list */
idrp_ann_list	*p_atl;		/* pointer for announce list for loop */
int		status = 0;	/* status of route export */


	/* this sets up the default RIB in send and announce list by
	 * default -  In QOS we will need to change this to be specific.
	 *
	 */

	bzero(&ann_list,sizeof(idrp_ann_list));
	rt_open(idrp_this_node.task);
	rtbit = idrp_this_node.task->task_rtbit;
	NLRI_FAMILY_LOOP(i)
		{
		rtl = actlist[i];
		RT_LIST(rth,rtl,rt_head) {

			if (socktype(rth->rth_dest) != nlri_family[i].family)
				{
				/* not AF_ISO or AF_INET should be here */
				continue;
				}

			p_rt = rth->rth_active;
			if (!p_rt)
				{
				/* not active route for first dump ignore
				 */
				continue;
				}


			/* check for idrp route
			 * - all external routes
			 * will have been translated to
			 * idrp routes for sending out
			 * by the idrp_flash routines 
			 */

			p_irt = (idrpRoute *) p_rt->rt_idrp;
			if (!p_irt)
				{
				if (IS_AN_IDRP(p_rt))
					{
					trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("IDRP route without IDRP route pointer"));
					task_quit(0); 
					}
				else
					{
					continue;
					}

				}
			/* The PEER_UP_DUMPs checks both the
			 * attributes function and DIST function  
			 */
	
			if (DIST(p_irt,peer,IDRP_DIST_PEER_UP_DUMP))
				{
				status = idrp_route_export(p_irt,peer);
				if (status == IDRP_EXPORT_ERROR && (p_irt->peer == peer))
					{
					/* we have already sent the error pdu
					 * - now free the existing lists
					 * - and exit back up the tree 
					 */  
					free_send_list(p_send_list,peer);
					free_ann_list(p_ann_list);
					rt_close(idrp_this_node.task,&idrp_this_node.gw,0,NULL);
					trace_tf (idrp_trace_options, TR_NORMAL,0, ("phase 3 - first time dump for neighbo aborted due to RDC problem %s ",peer->name));
					return(FALSE);
					/* return from routine and exit this peer transmit */	
					}
				if (status == IDRP_EXPORT_VALID)
					{
					if (p_irt->route_out[peer->id].p_att != (idrp_attribute_record *) NULL)
						{
						link_ann_list_mod(p_irt,&ann_list,NLRI_LINKED_P_ANN);
						}
					else
						{	 
						link_ann_list(p_irt,&ann_list,NLRI_LINKED_P_ANN);
						}
					}
				}
			} RT_LIST_END(rth,rtl,rt_head);
		} NLRI_FAMILY_LOOP_END;

	p_ann_list = &ann_list;

	/* build the send list - by attribute,
	 * by family type (AF_ISO or AF_INET)
	 * and finally by NLRI
	 *
	 * - ANN_LIST - builds attribute
	 * - NLRI_FAMILY_LOOP builds  protocol type 
	 * - ANN_ANN_LIST builds the NLRI into pdus 
	 */

	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{
				/* link route to send list and 	
			 	* when I have enough for an UPDATE  BISPDU
			 	* send it
			 	*/

				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;

		/* before you go to the next attribute record - flush this one's nlri's out */ 
		flush_att_send_list(peer,p_atl->p_attr,p_send_list);
		} ANN_LIST_END;	

	/* release the single send list once
	 * the routes have been sent - this will change
	 * into a linked list of send lists farily
	 * easily.
	 */

	free_send_list(p_send_list,peer);
	free_ann_list(p_ann_list);
	rt_close(idrp_this_node.task,&idrp_this_node.gw,0,NULL);
	trace_tf (idrp_trace_options, TR_NORMAL,0, ("phase 3 - first time dump for neighbor %s ",peer->name));
	return(TRUE);	
}


void
phase3_newpolicy(rth,p_ann_list)
rt_head		*rth;
idrp_ann_list	*p_ann_list;
{
rt_entry	*p_rt;

	/* test in phase3_status_change is 
	 *	if (newpolicy == TRUE && p_rt == (rt_entry *) 0 && p_old_rt == (rt_entry *) ) 	
	 */

	/* fixing so non-iso kernel can send
	 * and so the routes without any active
	 * list can come up from the
	 * gated into IDRP
	 */
  		
	p_rt = (rt_entry *)  rth->rt_forw;
	if (p_rt == (rt_entry *) NULL)
		return;
	
	/* test for null header here
	 *
	 */

	/* if (p_rt == &(rth)->rt_forw) BUG fix - chl ??? 2/16/94 */
	
	 if (p_rt == rth->rt_forw)
		return;

#ifdef	KERNEL_SUPPORTS_ISO

	/* not a null header - see if we
	 * can find an IDRP route as 
	 * the first hanging out
	 */

	if (IS_LOCAL_IDRP(p_rt)) 
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp_newpolicy no active route %s (%x) peer %s",
			iso_ptoa(&p_rt->rt_idrp->nlri), p_rt->rt_idrp->status,p_rt->rt_idrp->peer->name));

		IDRP_STATUS_BIT_TEST(p_rt->rt_idrp,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE)
			{
			IDRP_STATUS_BIT_TEST(p_rt->rt_idrp,IDRP_STATUS_LOCAL_DAMP_DEL)
				{
				trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp_newpolicy no active route local route damping %s (%x) peer %s",
					iso_ptoa(&p_rt->rt_idrp->nlri), p_rt->rt_idrp->status,p_rt->rt_idrp->peer->name));
				}
			else
	 			{
				/* here we have a local route that may not have 
				 * - have made it through the gated processing
				 *   in rt_flash
				 * - but we won't see again so, 
				 * let's handle it here
				 * 
				 */
				trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp_newpolicy no active route %s peer %s",iso_ptoa(&p_rt->rt_idrp->nlri),p_rt->rt_idrp->peer->name));
				ph3_status_case1(rth,p_ann_list,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
				}
			IDRP_STATUS_BIT_CLEAR(p_rt->rt_idrp,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
			}
		}
			
					
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_newpolicy called with no active route nlri %A ", rth->rth_dest));
		return;

#else /* KERNEL_SUPPORTS_ISO */

	if ((socktype(rth->rth_dest) == AF_ISO)) 
		{
		/* hi we have an route that can't 
		 * be called into the kernel, and 
		 * so will not be active, but is
		 * an iso route 
		 */

		if (IS_LOCAL_IDRP(p_rt)) 
			{
			IDRP_STATUS_BIT_TEST(p_rt->rt_idrp,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE)
				{
				/* here we have a local route that may not have 
				 * - have made it through the gated processing
				 *   in rt_flash
				 * - but we won't see again so, 
				 * let's handle it here
				 * 
				 */
				ph3_status_case1(rth,p_ann_list,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
				IDRP_STATUS_BIT_CLEAR(p_rt->rt_idrp,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
				}
			}
		trace_log_tf (idrp_trace_options, 0,LOG_ERR, ("testing: KERNEL_ISO_SUPPORT and osilocal route"));
		trace_log_tf (idrp_trace_options, 0,LOG_ERR, ("testing: route nlri %A ", rth->rth_dest));
		return;
		}
	else
		{
		/*  Bogus route    
		 */

		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_newpolicy called with no active route nlri %A ",
			rth->rth_dest));
		return;
		}					
#endif  /* KERNEL_SUPPORTS_ISO */	

}


idrp_ann_list *
ph3_newpolicy_deltas(p_ann_list)
idrp_ann_list	*p_ann_list;
{
idrp_ann_list	*p_atl;
int			i;
idrpRoute		*p_irt;
idrpRoute		*p_irt_next;

	ANN_LIST(p_atl,p_ann_list)
		{
		NLRI_FAMILY_LOOP(i)
			{
			/* walk the announce list pulling off
			 * the repeat announcements
			 */
			ANN_ANN_LIST_DEL_WALK(p_irt,i,p_atl,p_irt_next)
				{
				if (!(p_irt->status & IDRP_STATUS_RECONFIGURE)) 
					{
					unlink_ann_list(p_irt,p_atl,NLRI_LINKED_P_ANN);
					}
				IDRP_STATUS_BIT_CLEAR(p_irt,IDRP_STATUS_RECONFIGURE);

				} ANN_ANN_LIST_DEL_WALK_END; 
			} NLRI_FAMILY_LOOP_END;	
		} ANN_LIST_END;

	return (clean_ann_ann_list(p_ann_list));

}

/* %% - phase 3 status change routines */

void
phase3_status_change(rtl,p_ann_list,newpolicy)
rt_list *rtl;
idrp_ann_list	*p_ann_list;
int		newpolicy;
{
idrpPeer *peer;
u_int	rtbit;
rt_head *rth;
rt_entry *p_old_rt;
rt_entry *p_rt;
rt_changes	*rtc;
int	changes = 0;
int	not_changed;

	/* get an announce list allocated */

	rt_open(idrp_this_node.task);
	rtbit = idrp_this_node.task->task_rtbit;

	RT_LIST(rth,rtl,rt_head) {

		/* check for support for nlri passage for this
		 * family of routes
		 */
 
		if (!sock_supported(&idrp_this_node,rth->rth_dest))
			{
			continue;
			}


		/* route is ISO or INET */

		p_old_rt = rth->rth_last_active;
		p_rt = rth->rth_active; 

		if ((p_rt == (rt_entry *) 0 ) && (p_old_rt == (rt_entry *) 0))
			{
			if (newpolicy) 
				{
				phase3_newpolicy(rth,p_ann_list);
				}
			else
				{
				trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("phase3_status_change(): Warning!  Route with no active nor old"));
				}
			continue;
			}
			
#ifdef	IDRP_QOS
 		/* is this an IDRP QOS route from the local peer
		 * - well, then skip it - the 
		 * - qos processing will pick it up separately
		 *
		 */    
		if (p_rt && IS_AN_IDRP(p_rt) && (p_rt->rt_idrp->p_attr->rib_id != RIB_ID_DEFAULT))
			{
			/* Non-default rib id
			 * - skip it
			 */
			continue;	
			}
#endif	/* IDRP_QOS */

		if (p_old_rt == (rt_entry *) 0)
			{
			/* no old route */ 
			if (IS_AN_IDRP(p_rt))
				{
				/* new route protocol idrp */

				if (newpolicy)
					{
					IDRP_STATUS_BIT_CLEAR(p_rt->rt_idrp,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
					}
				ph3_status_case1(rth,p_ann_list,newpolicy);
				}
			else
				{
				/* new route protocol other than idrp */
				if ( ph3_status_case2(rth,p_ann_list,newpolicy))
					{
					changes++;
					}
				}
			}
		else
			{
			/* old route exists 
			 * - Is it simply a change to the same route?
			 * or is it a change of routes ?
			 */
			 	
			if (p_rt == p_old_rt)
				{
				/* change to existing same route */
				not_changed = 0;

				rtc = rth->rth_changes;
				if (rtc || newpolicy)
					{
					IS_IDRP(p_rt)
						{
						/* old route idrp, new route idrp */

						ph3_status_case3(rth,p_ann_list,newpolicy);
						}
					else
						{
						/* old route ext_info, new route ext_info */

						if (ph3_status_case4(rth,p_ann_list,newpolicy))
							{
							changes++;
							}
						}	 
					}

				}
			else
				{	
				/* routes are different and change is real */

				if ((p_rt != (rt_entry *) NULL) && ((p_rt->rt_state & RTS_DELETE) == 0))
					{
					/* replacement of rt_entry route structures */

					IS_IDRP(p_old_rt)	
						{  
						IS_IDRP(p_rt)
							{
							/* new route idrp and old route idrp */

							ph3_status_case3(rth,p_ann_list,newpolicy);
							}
						else
							{
							/* old route idrp, new route ext_info */

							if (ph3_status_case6(rth,p_ann_list,newpolicy))
								{
								changes++;
								}
		
							}
						}
					else
						{
						IS_IDRP(p_rt)
							{
							/* old route ext_info, new route idrp */
							ph3_status_case5(rth,p_ann_list,newpolicy);
							}
						else
							{
							/* old route ext_info and ext_info  */
							if (ph3_status_case4(rth,p_ann_list,newpolicy))
								{
								changes++;
								}
							}
						}
					}
				
				else
					{
					/* current route is being deleted */
					IS_IDRP(p_old_rt)
						{
						ph3_status_case7(rth,p_ann_list,newpolicy);
						}
					else
						{
						ph3_status_case8(rth,p_ann_list,newpolicy);
						}
					}

				}
			}

	} RT_LIST_END(rth,rtl,rt_head);
	rt_close(idrp_this_node.task,&idrp_this_node.gw,changes,NULL);
}


/*----------------------------------------------------------------------------
 * The following eight functions do the appropriate thing for each of the eight
 * possible classes of status change gated might hand us.  Here is an enumeration of the 
 * noble eightfold cases (but see the design doc for exhaustive details):
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 1	<none>		IDRP		Advertise.
 * 2	<none>		non-idrp	Preference; Advertise if non-zero
 * 3	IDRP		IDRP		Advertise new.  This will implicitly w/draw old.
 * 					Keep old idrpRoute unless it was explicitly 
 * 					withdrawn.
 * 4	non-idrp	non-idrp	Preference; Advertise new if non-zero.  This will
 * 					implicitly w/draw old.  Trash old idrpRoute since
 * 					it's just added on for our convenience.
 * 					If preference is zero, we need to explicitly 
 * 					withdraw the route.
 * 5	non-idrp	IDRP		Advertise new.  This will implicitly w/draw old.  
 * 					Trash old idrpRoute since it's just added on for 
 * 					our convenience.
 * 6	IDRP		non-idrp	Preference; Advertise new if non-zero.  This will
 * 					implicitly w/draw old.  Keep old idrpRoute
 * 					unless it was explicitly withdrawn.
 * 					If preference is zero, we need to explicitly 
 * 					withdraw the route.
 * 7	IDRP		<none>		Delete idrpRoute.  Send explicit withdrawal.
 * 8	non-idrp	<none>		Delete idrpRoute.  Send explicit withdrawal.
 */

/*----------------------------------------------------------------------------
 * ph3_status_case1()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 1	<none>		IDRP		Advertise.
 */
void 
ph3_status_case1(rth,p_ann_list,newpolicy)
rt_head		*rth;
idrp_ann_list	*p_ann_list; 
int		newpolicy;
{
	rt_entry 	*p_rt;
	idrpRoute	*p_idrp_rt;

	/* in the  idrpRoute structure
	 * set LOC_RIB status
	 * link through announce list to annouce outward 
	 * add to min route advertisement timer list
	 */

	if (newpolicy == IDRP_STATUS_LOCAL_PH3_NEW_ROUTE)
		{
		p_idrp_rt = rth->rt_forw->rt_idrp;
		IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
		}
	else
		{	
		p_idrp_rt = (idrpRoute *) rth->rth_active->rt_idrp;
		}

	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("ph3_status_case1() called -- new IDRP-learned route"));
	trace_tf (idrp_trace_options, TR_NORMAL, 0, ("We learned it from peer %s.  Route is %s",p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));

	IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB); 

	/* link through p_next to announce list
	 * anything but test peers
	  */
	
	if (p_idrp_rt->peer->type == IDRP_PEER_TEST)
		{
		/* test peers should be vewy, vewy quiet */
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("ph3_status_case1(): Warning!  Test peer sent route.  Ignoring the route."));
		return;
		}
		
	/* The peer wasn't a test peer, so it must be (EXTERNAL or INTERNAL or LOCAL).  Go ahead and send the change. */

	/* We can probably just delete the test and call rtbit_set() directly in the future.  Per skh leave like
	 * this for now so's not to get bit by possible bug.
	 */
	if (!rtbit_isset(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit))  
		{
		rtbit_set(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit);
		}
		
	
	if (newpolicy)
		{
		/*  check to see if you have new policy, then set for reconfigure
		 */	
 
		int pref;
		pref_t gated_pref;

		IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
		if (p_idrp_rt->pref_cal != PREF(p_idrp_rt, &pref, &gated_pref) || idrp_dist_policy_change(p_idrp_rt))
			{
			/* preference has changed for this route, 
			 * so flag it to be sent in the reconfiguration deltas
			 */
			  if ((pref == IDRP_INVALID_ROUTE_PREF) &&
				 	(gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
					(p_idrp_rt->pref_cal != IDRP_INVALID_ROUTE_PREF))
				
				  {
				    /* we are going to have to withdraw this route
				     */
				    trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case4 (1)  rt=%s, idrp_pref %d, gated_pref %d",
						  iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
				  p_idrp_rt->pref_cal = pref;
				  p_idrp_rt->gated_pref = gated_pref;
				  link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
				  return;		  
				  }
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			p_idrp_rt->pref_cal = pref;
			p_idrp_rt->gated_pref = gated_pref;


			}
		if (idrp_new_route(p_idrp_rt))
			{ 
			/* this is a new route we've not seen, being added to the route list */
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			}
		}

	link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);	
	return;
}

/*----------------------------------------------------------------------------
 * ph3_status_case2()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 2	<none>		non-idrp	Preference; Advertise if non-zero
 */
int
ph3_status_case2(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int		newpolicy;
{
	rt_entry	*p_rt;
	idrpRoute	*p_idrp_rt;
	idrpPeer	*peer;
	u_int		rtbit;	
	int		route_id_in;
	int		pref;
	adv_results	adv_res;

	/* calculate preference to find out if
	 * this external route should be exported by IDRP 
	 */

	rtbit = idrp_this_node.task->task_rtbit;
	p_rt = rth->rth_active;
	
	/* calculate the preference -- 0 means "don't advertise via IDRP */
	pref = idrp_ext_info_pref(p_rt);
	if (pref == 0)
		{
		/* return no changes */
		return(FALSE);
		}

	/* check if route already has an IDRP route hanging off of it */
	p_idrp_rt = p_rt->rt_idrp;
	if (p_idrp_rt)
		/* Yes, the external route has already had an IDRP route created */
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0, ("ph3_status_case2() idrp route already %s",iso_ptoa(&p_idrp_rt->nlri)));
		if (!newpolicy)
			{
			/* if this IDRP route is already announced we shouldn't have heard it, eh? */
			assert(!rtbit_isset(p_rt,idrp_this_node.task->task_rtbit));
			/* otherwise go ahead and set the announce bit (and announce the route) */
			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
			return(TRUE);
			}
		else /* reconfiguration in progress; bets are off */
			{
			/* here we need to flag routes
			 * which will only be re-sent on deltas as RECONFIGURE 
			 * since we have no new policy for
			 * checking the preference difference
			 */


			int pref;
			pref_t gated_pref;

			IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			if (p_idrp_rt->pref_cal != PREF(p_idrp_rt, &pref, &gated_pref) || idrp_dist_policy_change(p_idrp_rt))
				{
				/* if the preference has changed, link the old route to the new policy list
				 */

				  if ((pref == IDRP_INVALID_ROUTE_PREF) &&
				 	(gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
					(p_idrp_rt->pref_cal != IDRP_INVALID_ROUTE_PREF))
				
				  {
				    /* we are going to have to withdraw this route
				     */
				    trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case(1)  rt=%s, idrp_pref %d, gated_pref %d",
					iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
				  p_idrp_rt->pref_cal = pref;
				  p_idrp_rt->gated_pref = gated_pref;
				  link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
				  return(FALSE);		  
				  }
				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				p_idrp_rt->pref_cal = pref;
				p_idrp_rt->gated_pref = gated_pref;
				}

			/* link all routes to the list so that 
			 * a full dump may be gotten for the Rib Refresh
			 */

			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);

			/* return false to indicate that we do not have a change
			 * to the gated table - we simply have
		 	 * a change to policy that requires us to send
			 * an announcement 
			 */
			
			return(FALSE);
			}
		}

	/* no IDRP route existed, so we need to create one */

	p_idrp_rt = ph3_create_ext_route(p_rt,rth,pref,&adv_res);

	trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case2() idrp route created %s",iso_ptoa(&p_idrp_rt->nlri)));


	if (!p_idrp_rt)
		{
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("ph3_status_case2(): Warning!  Failed to create IDRP route."));
		return(FALSE);	
		}

	if (newpolicy)
		{
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
		}
	link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("ph3_status_case2 called peer %s route %s ", p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));

#ifdef IDRP_QOS
	ph3_create_qos_routes(p_rt,rth,pref,p_idrp_rt);
#endif /* IDRP_QOS */

	return(TRUE);
}

/*----------------------------------------------------------------------------
 * ph3_status_case3()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 3	IDRP		IDRP		Advertise new.  This will implicitly w/draw old.
 * 					Keep old idrpRoute unless it was explicitly 
 * 					withdrawn.
 */

void
ph3_status_case3(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int		newpolicy;
{
	rt_entry	*p_rt,*p_rt_old;
	idrpRoute	*p_idrp_rt,*p_idrp_rt_old;

	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case3() called -- replace one IDRP route with another"));

	p_rt = rth->rth_active;
	p_rt_old = rth->rth_last_active;
	p_idrp_rt = (idrpRoute *) p_rt->rt_idrp;
	p_idrp_rt_old = (idrpRoute *) p_rt_old->rt_idrp;
	IDRP_STATUS_BIT_CLEAR(p_idrp_rt_old,IDRP_STATUS_LOC_RIB); 
	IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_LOCAL_PH3_NEW_ROUTE);
	IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
	trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case3() - old: %s, status %x",iso_ptoa(&p_idrp_rt_old->nlri),p_idrp_rt_old->status));
	trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case3() - new: %s, status %x",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));
	

	if (!rtbit_isset(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit))  
		{
		rtbit_set(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit);
		}


	/* Sub case 1 - 
	 *  Route is being damped
	 * 
	 * 	1.1)  old is being withdrawn - link to withdrawl list,
	 *		set the DELETE flag
	 *
	 *		Minimum advertisement routes will handle finding
	 *		1) set STATUS_MIN_ADV_CHG flag on the DAMPED route
	 *		2) coping with the route if it is the damped route	
	 * 
	 *	1.2) old is simply being replaced - link announce list
	 * 		1) link to minadv list, and let minimum route
	 *		   advertisement handle route
	 *		
	 *  Why the withdrawl to the peer if route is damped?
	 *  bad news always travels fast
	 *
	 * if this route was not sent to a peer, 
	 * the send_with_nlri will handle not sending the route 
	 * 
	 */

	if (IDRP_DAMPED_ROUTE(p_idrp_rt))
		{
		/* if the route change was originally withdrawn, link to
		 * withdraw route
		 */

		IDRP_STATUS_BIT_TEST(p_idrp_rt_old,IDRP_STATUS_WITH)
			{
			/* set the replace flag on the old route to
			 * indicate that a phase3 replacement has been found
			 *
			 * minimum route advertisement processing
			 */
			trace_tf(idrp_trace_options, TR_NORMAL,0,("case 3 damped route, with set, link old route to withdraw list, set min_adv_chg"));

			IDRP_STATUS_BIT_SET(p_idrp_rt_old, (IDRP_STATUS_REPLACE | IDRP_STATUS_DELETE)); 
			IDRP_STATUS_BIT_SET(IDRP_DAMPED_ROUTE(p_idrp_rt), IDRP_STATUS_MIN_ADV_CHG);	
			IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_REPLACE);
			link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);
			}
		else
			{
			/* not withdraw, so let regular minimum route
			 * advertisement testing work
			 */
			
			trace_tf(idrp_trace_options, TR_NORMAL,0,(" case3 damped route, with not set, link route to annlist "));

		
			/* for new policy set reconfigure on any truly changed
			 * route
			 */

			if (newpolicy)
				{
				/* check to see if we have the same routes reconfigured 
				 */

				if (p_idrp_rt == p_idrp_rt_old)
					{
					int pref;
					pref_t gated_pref;

					/* check to see if this is needed 
					 */
 
					if ((p_idrp_rt->pref_cal != PREF(p_idrp_rt, &pref, &gated_pref)) || idrp_dist_policy_change(p_idrp_rt))
						{
						  if ((pref == IDRP_INVALID_ROUTE_PREF) &&
						      (gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
						      (p_idrp_rt->pref_cal != IDRP_INVALID_ROUTE_PREF))
				
						    {
						      /* we are going to have to withdraw this route
						            */
						      trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case3(2)  rt=%s, idrp_pref %d, gated_pref %d",
							     iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
						      p_idrp_rt->pref_cal = pref;
						      p_idrp_rt->gated_pref = gated_pref;
						      link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
						      return;		  
						    }
						IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
						p_idrp_rt->pref_cal = pref;
						p_idrp_rt->gated_pref = gated_pref;
						}
					else
						{
						IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
						}
					}
				else
					{
					IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
					}
				}
			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
			}
		return;
		}

	/* Sub case 2/3 
	 * Route is not being damped and old route, and has delete
	 * - need to delete route after it has been replaced
	 *   in distribution  
	 * - the min_adv processing sets the DEL_SEND flag 
	 *  
	 * SubCase 3  - simple replace one idrp
	 *  	        route for another of the same preference
	 *
	 * replaced save so that if route cannot be distributed
	 *   - it will be replaced 
	 */

	if (p_idrp_rt_old != p_idrp_rt)
		{	
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_REPLACE);
		IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_REPLACE);
		p_idrp_rt->p_replace = p_idrp_rt_old;
		trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case3 -  announce list linked with current route and old as replace route")); 
		if (newpolicy)
			{
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			}
		else
			{
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			}
			

		}
	else
		{
		if (newpolicy)
			{
			int pref;
			pref_t gated_pref;

			/* check to see if this is needed 
			 */
 
			if ((p_idrp_rt->pref_cal != PREF(p_idrp_rt, &pref, &gated_pref)) || idrp_dist_policy_change(p_idrp_rt))
				{
				 if ((pref == IDRP_INVALID_ROUTE_PREF) &&
				     (gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
				      (p_idrp_rt->pref_cal != IDRP_INVALID_ROUTE_PREF))
				
				        {
				        /* we are going to have to withdraw this route
					 */
				        trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case3(2)  rt=%s, idrp_pref %d, gated_pref %d",
					    iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
					p_idrp_rt->pref_cal = pref;
					p_idrp_rt->gated_pref = gated_pref;
					link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
					return;		  
					}
				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				p_idrp_rt->pref_cal = pref;
				p_idrp_rt->gated_pref = gated_pref;
				}
			else	
				{
				IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				}		
			}
		}	

	link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
	return;
}

/*----------------------------------------------------------------------------
 * ph3_status_case4()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 4	non-idrp	non-idrp	Preference; Advertise new if non-zero.  This will
 * 					implicitly w/draw old.
 *					case 4.0 - same gated route, same idrp route
 *						   check for preference changes and reconfiguration
 * 
 *					case 4.1-4.5 - different non-idrp routes
 *
 * 					case 4.1 - old non-idrp route does not have idrp route attached
 *					           new non-idrp route does not have idrp route attached
 *
 *					case 4.2 - old non-idrp route does not have idrp route attached
 *						   new non-idrp route can have idrp route attached
 *						 - create and link to announce list
 * 
 *					case 4.3 - old non-idrp route does have idrp route attached
 *						   new non-idrp route cannot have idrp route attached
 *						   but we are in damped state.
 *						 - link to withdraw list, and set IDRP status delete gated delete is on
 *			
 * 					case 4.4 - old non-idrp route does have idrp route attached
 *						   new non-idrp route cannot have idrp route attached
 *						   no damping on route
 *						- link old route to withdraw list, and set
 *						  delete on idrp route if gated delete is on.
 *
 * 					case 4.5 - old non-idrp route does idrp route attached
 *						   new non-idrp routes does want idrp route attached
 *						   create idrp route, link to announce list
 *						      
 *   If preference is zero imples no new non-idrp route.
 */


int
ph3_status_case4(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int	newpolicy;
{
	rt_entry	*p_rt,*p_rt_old;
	idrpRoute	*p_idrp_rt = 0;
	idrpRoute	*p_idrp_rt_old = 0;
	u_int		rtbit;
	int		pref = 0;
	adv_results	adv_res;

	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case4() called -- replace non-IDRP route with another non-IDRP route" ));

	/* set up the old route and the new route */
	p_rt = rth->rth_active;
	p_rt_old = rth->rth_last_active;

	/* see if routes have IDRP structures */
 
	p_idrp_rt = p_rt->rt_idrp;
	p_idrp_rt_old = p_rt_old->rt_idrp;
	rtbit = idrp_this_node.task->task_rtbit;

	/* Do pref on new route */

	pref = idrp_ext_info_pref(p_rt);

	/* case 4.0 - same gated and idrp route with
         * reconfigure 
	 */
	
	if ((p_rt == p_rt_old) && (p_idrp_rt == p_idrp_rt_old) && (p_idrp_rt != (idrpRoute *) NULL))  
		{
		/* we've got the same route, same idrp route
		 * 
		 */
		trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4() new and old route identical")); 
	
		/* check to see if the preference actually changed
		 * for the idrp route
                 * -1 = IDRP_INVALID_ROUTE_PREF
		 * 255 = IDRP_GATED_INVALID_ROUTE_PREF
		 */

		if (newpolicy)
			{
			int pref;
			pref_t gated_pref;

			if ((p_idrp_rt->pref_cal != PREF(p_idrp_rt, &pref, &gated_pref)) || idrp_dist_policy_change(p_idrp_rt))
				{
				/* flag it for the new policy and link to list
				 */
				  trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case4 (1.1)  rt=%s, idrp_pref %d(%d), gated_pref %d(%d)",
				     iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,pref,p_idrp_rt->gated_pref,gated_pref));
 
				if ((pref == IDRP_INVALID_ROUTE_PREF) &&
				 	(gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
					(p_idrp_rt->pref_cal != IDRP_INVALID_ROUTE_PREF))
				
				  {
				    /* we are going to have to withdraw this route
				     */
				    trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case4 (1)  rt=%s, idrp_pref %d, gated_pref %d",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
				  p_idrp_rt->pref_cal = pref;
				  p_idrp_rt->gated_pref = gated_pref;
				  IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_WITH);
				  link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
				  return(FALSE);		  
				  }
				 else
				  {
				  IDRP_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_RECONFIGURE|IDRP_STATUS_LOC_RIB));
				  p_idrp_rt->pref_cal = pref;
				  p_idrp_rt->gated_pref = gated_pref;
    				  link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
				  return(FALSE);
				  }
				}
			else
				{
				if (idrp_new_route(p_idrp_rt))
					{  
					IDRP_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_RECONFIGURE|IDRP_STATUS_LOC_RIB));
					}
				else
					{
					IDRP_STATUS_BIT_CLEAR(p_idrp_rt,(IDRP_STATUS_RECONFIGURE));
					}
				}
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_case4 (2)=%s, idrp_pref %d, gated_pref %d",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,p_idrp_rt->gated_pref)); 
			}
		else
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4(3) same gated and idrp route without newpolicy set nlri",iso_ptoa(&p_idrp_rt->nlri))); 
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
			IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
			} 
		link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
		return(FALSE);
		}
	else
		{
		/* routes are not the same clear the LOC_RIB flag
		 * in the old route
		 */

		if (p_idrp_rt_old)
			{
			/* make sure the old route (if there was one) is sane.  croak if not.
			 * this routine is a wrapper around an assert
		 	 */
			verify_extinfo_idrp_rt(p_idrp_rt_old,p_rt_old);
			IDRP_STATUS_BIT_CLEAR(p_idrp_rt_old,(IDRP_STATUS_LOC_RIB));
			}
		}

	/* do we want to announce the new route?
	 * - positive preferences allows
	 * this to occur
	 */

	if (pref)
		{
		/* make sure the new route's idrpRoute (if there was one) is sane. 
		 *  croak if not.
		 */
		p_idrp_rt = p_rt->rt_idrp;
		verify_extinfo_idrp_rt(p_idrp_rt,p_rt); 

		if (p_idrp_rt == (idrpRoute *) NULL)
			{			 
			/* ok, go ahead and set up the idrpRoute if needed */

			p_idrp_rt = ph3_create_ext_route(p_rt,rth,pref,&adv_res);


			/* create the QOS routes that go with this route */

			#ifdef IDRP_QOS
			ph3_create_qos_routes(p_rt,rth,pref,p_idrp_rt);
			#endif /* IDRP_QOS */

			}
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
		}


	if (p_idrp_rt_old == NULL)
		{
		/* no old idrp route */

		if (!pref)
			{
			/* subcase 1 - old ext_info did not have idrp route
			 *	       new_ext_info does not have idrp route
			 *
			 * return with false to indicate no changes 
			 */
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4.1 - no old_ext_info, no new ext_info"));
			return(FALSE);
			}
		else
			{
	
			/* subcase 2 - old ext_info did not have idrp route
			 *	     - new ext_info does have idrp route
			 * 	
			 * link route to announce list 
			 * 	and let the minimum route advertisement
			 *	timers be check for announcement
			 */
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4.2 - no old_ext_info, new ext_info nlri %s ",iso_ptoa(&p_idrp_rt->nlri)));
		
			if (newpolicy)
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				}	
			else
				{
				IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				}
			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
			return(TRUE);
			}
		}
	else
		
		{
		if (!pref)
			{

			 /* subcase 3 - old ext_info has route
			  * 	       new_ext_info has not route
			  *	       ROUTE IS DAMPED, set withdrawal
			  *		flag to force withdrawal
			  *		so that "bad news travels fast" 
			  *
			  * subcase 4 - old_ext_info has IDRP route
			  * 	       new_ext_info has no idrp route
			  *  
			  * if old non-idrp route is being deleted, set IDRP DELET flag 
			  * for debugging aid:
			  *  watch for DAMPED and set MIN_ADV_CHG flag in route linked to
			  *  withdraw list 
	 		  *
			  * link to withdraw list
			  */

			IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_WITH);

			if (p_idrp_rt_old->p_rt->rt_state & RTS_DELETE)
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_DELETE);
				}
	
			 if (IDRP_DAMPED_ROUTE(p_idrp_rt))
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_MIN_ADV_CHG);
				}

			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4.4/4  old_ext_info, no new ext_info nlri %s(%x) ",
				iso_ptoa(&p_idrp_rt_old->nlri),p_idrp_rt->status));

			link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);
	
			}
		else
			{
			/* subcase 5 - old_ext_info has IDRP route
			 * 		new external info has IDRP route
			 *
			 * - link to announce list and let minimum route
			 *   advertisement processing of announce list handle
			 * 
			 */
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case4.5  old_ext_info, new ext_info nlri %s ",iso_ptoa(&p_idrp_rt->nlri)));

			if (newpolicy)
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				}	
			else
				{
				IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
				}

			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_REPLACE);
			IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_REPLACE);
			p_idrp_rt->p_replace = p_idrp_rt_old;
			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
			return(TRUE);
			}
		}


	return(FALSE);
}

/*----------------------------------------------------------------------------
 * ph3_status_case5()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 5	non-idrp	IDRP		Advertise new.  link to list, let
 * 					min adver test pick up the damping
 * 				        for the announcement
 * 
 *					if old non-idrp route is being deleted
 *					get rid of idrp route, and clear our
 * 					announce bits.
 *  
 *
 */
void
ph3_status_case5(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int	newpolicy;
{
	rt_entry	*p_rt,*p_rt_old;
	idrpRoute	*p_idrp_rt = 0;
	idrpRoute	*p_idrp_rt_old = 0;
	u_int		rtbit;


	/* set-up the old route and the new route */

	p_rt = rth->rth_active;
	p_rt_old = rth->rth_last_active;
	rtbit = idrp_this_node.task->task_rtbit;
	p_idrp_rt = (idrpRoute *) p_rt->rt_idrp;
	p_idrp_rt_old = p_rt_old->rt_idrp;

	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case5() called -- replace non-IDRP route with IDRP-learned route nlri %s",
			iso_ptoa(&p_idrp_rt->nlri)));


	if (newpolicy)
		{
		IDRP_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_LOC_RIB| IDRP_STATUS_RECONFIGURE));
		}
	else
		{
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
		IDRP_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_RECONFIGURE);
		}

	if (!rtbit_isset(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit))  
		{
		rtbit_set(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit);
		}
	
	/* queue the new route for announcement */

	link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);

	if (rtbit_isset(p_rt_old,rtbit))
		{
		/* the old route must have been announced by IDRP
		 * do sanity checks on route
		 */
 
		p_idrp_rt_old = p_rt_old->rt_idrp;
		assert(p_idrp_rt_old && p_idrp_rt_old != p_idrp_rt);
		
        	IDRP_STATUS_BIT_CLEAR(p_idrp_rt_old,IDRP_STATUS_LOC_RIB);

		/* Checks succeeded.  Reckon we need to free the idrpRoute portion of the old route.
		 * Is the non-idrp route being deleted and we have an idrp route?
		 *  then we need to fneed to withdraw the idrp route from the  
		 * 
		 * if the route is not being deleted, we have preference replace. 
		 */

		if ((p_rt_old->rt_state & RTS_DELETE))
			{
			IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_DELETE);
			}

		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_REPLACE);
		IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_REPLACE);
		p_idrp_rt->p_replace = p_idrp_rt_old;
	
		}	
	return;
}


/*----------------------------------------------------------------------------
 * ph3_status_case6()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 6	IDRP		non-idrp	subcase 1 - IDRP route DELETED but DAMPED
 *						    No new route
 * 
 *					  - link to withdraw list with MIN_ADV_CHG flag
 *					  - min route processing will check
 *						DELETE should be set in idrp route
 *						set IDRP_STATUS_DEL_SEND flag 
 *						if route can be deleted. 
 * 
 * 					subcase 2 - IDRP route DELETED, but not damped  
 *						    no new route,
 *					    - link to withdrawl list and
 *					    - SET IDRP_DEL_SEND flag
 *  
 *
 * 					subcase 3 - IDRP route not deleted, but
 *						    superceded by new route not advertised
 *						    DAMPING function has nothing to
 * 						    do with this one
 * 
 *					- link idrp route to withdrawl list
 * 			 		- make sure deleted flag is cleared
 *  
 *						
 *					subcase 4 - new route, idrpROUTE deleted
 *						    DAMPING on 
 *					    - link to withdraw list with 
 *					    MIN_ADV_CHG flag set 						    
 *  
 * 
 *					subcase 5 - new route, idrp_route DELETED,
 *						    no damping
 * 
 *					    - link to announce list 
 * 
 *					subcase 6 - new route, idrpRoute not deleted
 *						    just over run by new route  
 * 						- link to announce list
 *
 * 						  
 * 		 
 * 
 *			 
 */

int
ph3_status_case6(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int	newpolicy;
{
	rt_entry	*p_rt,*p_rt_old;
	idrpRoute	*p_idrp_rt = 0;
	idrpRoute	*p_idrp_rt_old = 0;
	u_int		rtbit;
	int		pref;
	adv_results	adv_res;

	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case6() called -- replacing IDRP-learned route with non-IDRP route"));

	p_rt = rth->rth_active;
	p_rt_old = rth->rth_last_active;
	rtbit = idrp_this_node.task->task_rtbit;
	p_idrp_rt_old = (idrpRoute*)p_rt_old->rt_idrp;

        IDRP_STATUS_BIT_CLEAR(p_idrp_rt_old,IDRP_STATUS_LOC_RIB);

	/* Do pref on new route */

	pref = idrp_ext_info_pref(p_rt);


	if (pref == 0)
		{
		/* new non-idrp route does not want an idrp route
		 */
 
		 /* idrp	subcase 1 - IDRP route DELETED but DAMPED
		  *		            new non-idrp route does not
		  *			    want idrp route
 		  *
 		  * 		- link IDRP route to withdraw list with MIN_ADV_CHG flag
 		  *		- min route processing of withdraw list will check 
		  *
		  * 		subcase 2 - IDRP route DELETED but not damped
		  *		            new non-idrp route does not
		  *			    want idrp route
 		  *		- link IDRP route to withdrawl list,
		  *
		  */

		IDRP_STATUS_BIT_TEST(p_idrp_rt_old,IDRP_STATUS_DELETED)
			{
			if (IS_IDRP_DAMPED_ROUTE(p_idrp_rt_old))
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_MIN_ADV_CHG);
				}
			link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);
			return(FALSE);
			}
		else
			{			 
			/*
			 * subcase 3 - IDRP route not deleted, but
			 *  superceded by new route not advertised
			 * 
 			 * - link idrp route to withdrawl list
			 * 
			 *  If we don't link we will get a black hole
			 *  when the kernel routes one way, and idrp
			 *  routes another 
 			 */

				
			link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);
			return(FALSE);
			}
			
		}
	else
		{
		p_idrp_rt = p_rt->rt_idrp;
		if (p_idrp_rt)
			{	
			verify_extinfo_idrp_rt(p_idrp_rt,p_rt);
			}
		else
			{
			p_idrp_rt = ph3_create_ext_route(p_rt,rth,pref,&adv_res);
#ifdef IDRP_QOS
			ph3_create_qos_routes(p_rt,rth,pref,p_idrp_rt);
#endif /* IDRP_QOS */
			}

		/* new route idrp route created for non-idrp route
		 *
		 */
 
		IDRP_STATUS_BIT_TEST(p_idrp_rt_old,IDRP_STATUS_DELETED)
			{
			if (IS_IDRP_DAMPED_ROUTE(p_idrp_rt_old))
				{
				/* 
 				 * subcase 4 - new route, idrpROUTE deleted
 				 *   DAMPING on 
				 *      - link to withdraw list with 
				 *	   MIN_ADV_CHG flag set 						    
				 */

				IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_MIN_ADV_CHG);
				link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);
				}
 
			else
				{
				/*  
				 * subcase 5 - new route, idrp_route DELETED,
				 *             and no damping
				 * 
				 * 1) delete IDRP route 
				 * 2) link new route to announce list
				 *    - link to announce list 
				 */

				if (newpolicy)
					{
					IDRP_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_REPLACE | IDRP_STATUS_RECONFIGURE));
					}
				else
					{
					IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_REPLACE);
					}	
				IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_REPLACE);
				p_idrp_rt->p_replace = p_idrp_rt_old;
				link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
				}
			}
		else
			{
			/* 
 			 * subcase 6 - new route, idrpRoute not deleted
 			 * just over preferenced by new route  
 			 * - link to announce list
 			 *
			 */

			if (newpolicy)
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_REPLACE | IDRP_STATUS_RECONFIGURE));
				}
			else
				{
				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_REPLACE);
				}
			IDRP_STATUS_BIT_SET(p_idrp_rt_old,IDRP_STATUS_REPLACE);	
			p_idrp_rt->p_replace = p_idrp_rt_old;

			link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
			}
		}
	
	return(TRUE);	
		
						 
}

/*----------------------------------------------------------------------------
 * ph3_status_case7()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 7	IDRP		<none>		Delete idrpRoute.  Send explicit withdrawal.
 */
void
ph3_status_case7(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int		newpolicy;
{
	rt_entry	*p_rt_old;
	idrpRoute	*p_idrp_rt_old = NULL;
	u_int		rtbit;


	p_rt_old = rth->rth_last_active;
	p_idrp_rt_old =(idrpRoute *)  p_rt_old->rt_idrp;

/* New code: */
	assert(p_idrp_rt_old != (idrpRoute *) NULL);

	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case7() called -- deleting IDRP-learned route nlri %s",iso_ptoa(&p_idrp_rt_old->nlri)));


/* Old code;
	if (p_idrp_rt_old == (idrpRoute *) NULL)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case7() called -- deleting IDRP-learned route nlri %x",rth->rth_dest));

		return;
		}
*/
 
	/* tag it for explicit withdrawal */

	IDRP_STATUS_BIT_CLEAR(p_idrp_rt_old,IDRP_STATUS_LOC_RIB);
	IDRP_STATUS_BIT_SET(p_idrp_rt_old, (IDRP_STATUS_DEL_SEND|IDRP_STATUS_WITH));

	/* if damped route
	 * 1) check for damped route being this route - if so skip setting
	 *    of flag
	 *
	 * 2) check for no routes being announced out for
	 *    this route out  - if routes out, skip setting of flag 
	 *  
	 * 3) set p_damped_clear - to the damped route so that
	 *    the send_with will handle it
	 *  
	 */

	{
	idrpRoute *p_idrp_damped_rt = IDRP_DAMPED_ROUTE(p_idrp_rt_old);

	if ((p_idrp_damped_rt != (idrpRoute *) NULL) &&
		(p_idrp_damped_rt != p_idrp_rt_old) &&
		(idrp_any_rt_anns(p_idrp_rt_old) == FALSE) &&
		(idrp_any_rt_anns(p_idrp_damped_rt)))   
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case7(): Setting p_damped_clear on %x", p_idrp_rt_old));
		p_idrp_rt_old->p_damped_clear = p_idrp_damped_rt; 
		IDRP_STATUS_BIT_SET(p_idrp_damped_rt, IDRP_STATUS_WITH);
		}
	else
		{
		if (p_idrp_damped_rt == (idrpRoute *) NULL)
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case7(): p_idrp_damped_rt == NULL"));
		if (p_idrp_damped_rt == p_idrp_rt_old)
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case7(): p_idrp_damped_rt == p_idrp_rt_old"));
		if (idrp_any_rt_anns(p_idrp_rt_old) != FALSE)
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case7(): idrp_any_rt_anns(p_idrp_rt_old) did not return FALSE"));
		if (idrp_any_rt_anns(p_idrp_damped_rt) == FALSE)
			trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case7(): idrp_any_rt_anns(p_idrp_damped_rt) did return FALSE"));
		}
	}
	
	 
	/* and link it to the withdraw list */

	link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);

		
	return; 	
}

/*----------------------------------------------------------------------------
 * ph3_status_case8()
 *
 * case	old route	new route	action
 * ----	---------	---------	-------------------------------------------
 * 8	non-idrp	<none>		Delete idrpRoute.  Send explicit withdrawal.
 */
void
ph3_status_case8(rth,p_ann_list,newpolicy)
rt_head *rth;
idrp_ann_list	*p_ann_list; 
int		newpolicy;
{
rt_entry	*p_rt_old;
idrpRoute	*p_idrp_rt_old = 0;
u_int		rtbit;


	trace_tf (idrp_trace_options, TR_NORMAL,0,("ph3_status_case8() called -- deleting non-IDRP route"));

	/* set up the old route; there is no new route
	 */
	p_rt_old = rth->rth_last_active;
	rtbit = idrp_this_node.task->task_rtbit;

	/* Does this ext_info route have idrp route bit set? - if yes, continue.
	 * If no, return -- we've never advertised it, so we don't need to withdraw it.
	 */

	if (!rtbit_isset(p_rt_old,rtbit))  
		{
		return;
		}

	p_idrp_rt_old = p_rt_old->rt_idrp;


	trace_tf(idrp_trace_options, TR_NORMAL,0,(" ph3_status_case8() nlri %s",iso_ptoa(&p_idrp_rt_old->nlri)));

	/* sanity-check the idrpRoute 
	 */
	verify_extinfo_idrp_rt(p_idrp_rt_old,p_rt_old); /* wrapper around an assert() */

	/* tag it for deletion after sending */

	IDRP_STATUS_BIT_SET(p_idrp_rt_old, (IDRP_STATUS_DEL_SEND|IDRP_STATUS_WITH));

	/* if damped route
	 * 1) check for damped route being this route - if so skip setting
	 *    of flag
	 *
	 * 2) check for no routes being announced out for
	 *    this route out  - if routes out, skip setting of flag 
	 *  
	 * 3) set p_damped_clear - to the damped route so that
	 *    the send_with will handle it
	 *  
	 */

	if (p_idrp_rt_old->p_rt)
		{
		idrpRoute *p_idrp_damped_rt = IDRP_DAMPED_ROUTE(p_idrp_rt_old);

		if ((p_idrp_damped_rt != (idrpRoute *) NULL) &&
			(p_idrp_damped_rt != p_idrp_rt_old) &&
			(idrp_any_rt_anns(p_idrp_rt_old) == FALSE) &&
			(idrp_any_rt_anns(p_idrp_damped_rt)))   
			{
			p_idrp_rt_old->p_damped_clear = p_idrp_damped_rt; 
			}
	
		}
	
	 
	/* and link it to the withdraw list */

	link_ann_list(p_idrp_rt_old,p_ann_list,NLRI_LINKED_P_WITH);

#ifdef	IDRP_QOS
	idrp_qos_del_static(p_idrp_rt_old,p_ann_list);

#endif	/* IDRP_QOS */
	return;
}

/*----------------------------------------------------------------------------
 */
void 
idrp_send_phase3_routes(p_ann_list)
idrp_ann_list *p_ann_list;
{
idrpPeer		*peer;		/* peer structure pointers */


	/* trace the fact that we're trying to send phase 3 routes */


	trace_tf( idrp_trace_options, TR_NORMAL,0,("idrp_send_phase3_routes called"));

	if (p_ann_list->p_attr == (idrp_attribute_record *) 0)
		{
		trace_tf( idrp_trace_options, TR_NORMAL,0,("idrp_send_phase3_routes called but no attribute records"));
		return;
		} 

	/* first send local (static) routes only to internal peers --
	 * we need to do this since we won't have heard them in phase 1
	 */

	if (idrp_peers)
		{
		IDRP_INT_PEER(peer)
		    {
		    if (PEER_CONNECTED(peer))
			{
			/* if peer connected - send the list of route */
			idrp_send_ann_routes(peer,p_ann_list,IDRP_DIST_LOCAL_FLASH);
			}
		    } IDRP_INT_PEER_END;
		}
	
	/* then send all routes to external peers */
	IDRP_EXT_PEER(peer)
		{
		if (PEER_CONNECTED(peer))
			{
			/* if peer connected - send the routes */
			idrp_send_ann_routes(peer,p_ann_list,IDRP_DIST_FLASH);
			}
		} IDRP_EXT_PEER_END;

	/* clean the replaced and damped routes
	 */

	idrp_clean_replace_routes(p_ann_list);	


}	



int
idrp_send_ann_routes(peer,p_ann_list,flash)
idrpPeer	*peer;
idrp_ann_list	*p_ann_list;
int		flash;		/* type of flash 
				 * - newpolicy or regular flash
				 */ 
{
idrpRoute		*p_irt;		/* pointer to idrpRoute for loop processing */ 
idrpRoute		*p_irt_next;	/* pointer to next idrpRoute for Withdraw processing */
idrp_ann_list		*p_atl;		/* announce list */
idrp_ann_list		*p_out_atl;	/* outbound announce list */
idrp_attribute_record	*p_att_dist;	/* distribution attribute record pointer */
idrp_send_list		*p_send_list = 0;	
int			i;		/* NLRI loop counter */ 
int			status = 0 ;	/* status of the route */	

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

	/* walk through the announce list building
	 * send lists and sending them to the external peers 
	 */
 
	ANN_LIST(p_atl,p_ann_list)
		{
		p_att_dist = DIST_ATTR(p_atl->p_attr,peer);
		NLRI_FAMILY_LOOP(i)
			{
			ANN_WITH_LIST_DEL_WALK(p_irt,i,p_atl,p_irt_next)
				{
				if (DIST_WITH(p_irt, peer, flash))
					{
					p_send_list = send_with_attr(p_irt,p_att_dist,p_send_list,peer,flash);
					}
				} ANN_WITH_LIST_DEL_WALK_END;

			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{
				if (p_att_dist)
					{
					if (DIST(p_irt,peer,flash))
						{
						status = idrp_route_export(p_irt,peer);
						if (status == IDRP_EXPORT_ERROR && (p_irt->peer == peer))
							{
							/* we have already sent the error pdu
							 * - now free the existing lists
							 * - and exit back up the tree 
							 */  

							free_send_list(p_send_list,peer);
							idrp_free_outatt_list();
							idrp_clean_early_proc(p_ann_list);
							return;
							}
						if (status == IDRP_EXPORT_VALID)
							{
		                                        if (p_irt->route_out[peer->id].p_att != (idrp_attribute_record *) NULL)
               		                                 	{
								/* new attribute for outbound list - link
								 */ 
	                                                	link_ann_list_mod(p_irt,p_out_atl,NLRI_LINKED_P_ANN);
       		                                         	}
							else
								{
								/* send out the route */
								p_send_list = send_nlri_attr(p_irt,p_att_dist,p_send_list,peer);
								 }
							}
						}
					 else 
					   {
						IDRP_STATUS_BIT_TEST(p_irt,IDRP_STATUS_REPLACE)
							{
							p_send_list = send_with_replace(p_irt,p_att_dist,p_send_list,peer,flash);
							}
						}
					}
				} ANN_ANN_LIST_WALK_END;
			} NLRI_FAMILY_LOOP_END;
		flush_att_send_list(peer,p_att_dist,p_send_list);
		} ANN_LIST_END;

	/* send pdus for attributes
	 * which have modified attributes
	 */

	ANN_LIST(p_atl,p_out_atl)
		{
		ANN_ANN_LIST_WALK(p_irt,i,p_atl)
			{
			/* send out the routes which are on the output list
			 */

			p_send_list = send_nlri_attr(p_irt,p_atl->p_attr,p_send_list,peer);
			} ANN_ANN_LIST_WALK_END; 

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

	idrp_free_outatt_list();
	free_send_list(p_send_list,peer);
	idrp_clean_early_proc(p_ann_list);
}

/*----------------------------------------------------------------------------
 * verify_extinfo_idrp_rt()
 *
 * Make sure that this idrpRoute is sane -- if there's an idrpRoute, the 
 * idrpRoute rt_bit had better be set, and if there's not, the rt_bit had
 * better be clear.
 * 
 * This is just a wrapper around an assert(), which is a change.  Perhaps it
 * should be a macro (or we should just write out the assert).  However, any
 * reasonable compiler will inline it anyway.
 */

int
verify_extinfo_idrp_rt(p_idrp_rt,p_rt)
idrpRoute	*p_idrp_rt;
rt_entry	*p_rt;
{
	/* There was a bunch of code here to test for presence of IDRP rt bit + presence of IDRP route.
	 * Since one should always imply the other, I changed this to an assert().
	 */

	assert((rtbit_isset(p_rt,idrp_this_node.task->task_rtbit)) ? (p_idrp_rt != NULL) : (p_idrp_rt == NULL));
	
	/* didn't croak on the assert(), all is well. */
	return;
}

/* create an external route for phase 3 */


idrpRoute *
ph3_create_ext_route(p_rt,rth,pref,p_adv_res)
rt_entry	*p_rt;
rt_head		*rth;
int		pref;
adv_results	*p_adv_res;
{
idrpRoute_entry	*p_rte,*p_rte_last;
int		family;
idrp_attribute_record *p_att;
idrpRoute		*p_idrp_rt;
idrpPeer		*peer;
int			route_id_in;
idrpRoute		*p_best_ext = (idrpRoute *)NULL;

	trace_tf(idrp_trace_options, TR_NORMAL,0,
		("...called ph3_create_ext_route(p_rt=0x%lx (%A/%A), rt_head=0x%lx (%A/%A),\npref=%d, adv_results=0x%lx (met1=%d, met2=%d, ps_info=0x%lx, flag=0x%lx))", 
		p_rt, 
		p_rt->rt_head->rth_dest, 
		p_rt->rt_head->rth_dest_mask, 
		rth, 
		rth->rt_head->rth_dest, 
		rth->rt_head->rth_dest_mask, 
		pref, 
		p_adv_res, 
		p_adv_res->res_u1.resu_metric, 
		p_adv_res->res_u2.resu_metric, 
		p_adv_res->ps_info, 
		p_adv_res->res_flag)); 

	/* set up the family for this destination */

	family = socktype(rth->rth_dest);

	/* No idrp route exists for this route
	 * - so set-up an IDRP route for the
 	 *  this gated route.
	 * - check for route in the  
	 *  1.) set the IDRP protocol in the rt_entry
	 */

	rtbit_set(p_rt,idrp_this_node.task->task_rtbit);
 
	/* 1)  If pref is non-zero we will accept the
	 * route and there is no route already.
	 * create a route type based on the
	 * the value of the preference flag
	 * 
	 * 2) pref is  
	 * IDRP_EXT_INFO_IGP -
	 * create a non-idrp route with
	 * the local attribute record
	 * for non-exterior routes
	 *   
	 * 3) pref is
	 * IDRP_EXT_INFO_EXT_INFO 
	 * have an IDRP route with the EXT_INFO
	 * attribute. 
	 *
	 * 4) set the peer to the peer from this attribute record 
	 *	if non-bgp:
         *		this will be local_nodes's idrp peer
	 *      if BGP: may change may transfer the peer this
	 *		to an IDRP parallel peer structure	 
	 * 
	 *
	 *  4.) create an idrpRoute for this Route
	 *	 - allocate the idrp route and stuff in initial info
	 *	 - put nlri in route
	 *	 - attach this route to the attribute record 
	 *	   (will be local node except for bgp 
	 *
	 * 5) set the Route_id_in to be the external info route_id
	 *		information  is received one 
	 *
	 * 	we build the attribute record with the right peer
	 * 	this double check is useful 
	 *
	 *
	 */

	if (pref == IDRP_EXT_INFO_IGP)
		{
		/* gated route from this IGP (IS-IS or STATIC)
	 	 * for now all external routes are 
		 * set-up with no options on external
		 * attribute
		 * this will change in the
		 * future as we
		 * work on it per configuration
		 */ 

		p_att = find_local_att_rec(p_rt->rt_routers[0],idrp_this_node.p_local_info,RIB_ID_DEFAULT);
		p_att->p_opts->idrp_local_proto = p_rt->rt_gwp->gw_proto;
		p_idrp_rt = idrp_alloc_idrpRoute(&idrp_this_node,p_att,family,IDRP_LOCAL_ROUTE_ID);
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOCAL_RD);

		/* need to copy in the prefix prior to 
		 * setting up the best external for debugging
		 * info that we need for now
		 * 
		 */

		sockun_to_prefix(p_rt->rt_dest,p_rt->rt_dest_mask,&p_idrp_rt->nlri);	

		/* Hook route onto best ext route chain.
		 * This is a piece of ugly hackery to block the IIDRP announcement
		 * of a BER if we get an external to the same destination.
		 * Really, we should not be using statics, and we should not 
		 * be distributing the IGP routes into IIDRP.  This really, really
		 * should be fixed later.
		 *
		 * Deleting the route from the best ext chain will be handled by
		 * del_rt_gated, HOWEVER we need to think about what happens with
		 * BER stuff if this happens -- it seems likely that the BER will
		 * not be sent without further contortions.
		 *
		 * - this does not seem to handle what happens if
		 *   we add the local peer later I need to chat with 
		 *   John
		 */

		p_best_ext = find_best_ext(p_rt,p_idrp_rt);
		insert_in_pref_order(p_idrp_rt, p_best_ext);

		/* I pulled out the setting of the IDRP_STATUS_EXT_INFO
		 * here indicating that it is not external info
		 * -
		 * Our IGP sources are currently:
		 *  static routes:
		 *     a) definitely our only way to do IDRP configurations
		 *     b) we can fake IS-IS by using exterior on a static route
		 *	 
		 *  IS-IS routes
		 *    a) If is-is has external info that it will flag it in
		 *       the protocol which will get reflected in exterior
		 * 	 flag 
	 	 *
		 *    b) the regular isis will get flaged as interior 
		 * 
		 */
 
		}
	else
		{
		/* pref = IDRP_EXT_INFO_EXT_INFO */
	
		p_att = find_ext_info_attr_rec(rth,p_rt,idrp_this_node.p_ext_info,RIB_ID_DEFAULT);
		p_att->p_opts->idrp_local_proto = p_rt->rt_gwp->gw_proto;
		p_idrp_rt = idrp_alloc_idrpRoute(&idrp_this_node,p_att,family,IDRP_EXT_INFO_ROUTE_ID);
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_EXT_INFO);
		sockun_to_prefix(p_rt->rt_dest,p_rt->rt_dest_mask,&p_idrp_rt->nlri);	
		}


	

	/* here we need to fill in the idrp_rt allocated
	 * 6) fill in idrp route entry to link to gated (p_rt)
	 * 7) fill in rt_entry p_idrp_rt  to link idrp route  to idrp route
	 * 8) set IDRP route status to indicate it is already part of LOC_RIB 
	 *  
	 * 9) p_loc_rt_peer  - does it get filled in with the
	 *		      peer structure of the exterior router
	 *		      that sent the route or the next hop
	 *		      - next hop is set right now from 
	 *		      - the attribute record 
	 */


	p_idrp_rt->p_rt = p_rt;
	p_rt->rt_idrp = p_idrp_rt;
	p_idrp_rt->p_loc_rt_peer = p_rt->rt_routers[0];
	IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_LOC_RIB);
	

	/* 9) link the idrp route to the attribute record
	 *    with either EXT_INOF_ROUTE_ID or
	 *    LOCAL_NODE_ROUTE_ID
	 *
	 */ 

	p_rte = idrp_find_routeid_chain(p_idrp_rt,&p_rte_last);
		
	if (p_rte)
		{
		/* found it - add the new idrpRoute to
		 * idrpRoute_entry in chain with EXT_INFO
		 */

		if (!add_nlri_routeid_chain(p_rte,p_idrp_rt))
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0,("error in finding routeid chain route %s peer %s", 
				iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer)); 
			idrp_free_idrpRoute(p_idrp_rt);
			return(0);
			}
		}
	else
		{
		/* none found -  create one based on the route ID
	 	 * and add it to the route_id chain    
		 */

		int i;
		i = family_to_nlri_id(p_idrp_rt->family);

		p_rte = (idrpRoute_entry *) idrp_local_mem_fit(sizeof(idrpRoute_entry));
		bzero(p_rte,sizeof(idrpRoute_entry));
		p_rte->peer = peer;
		p_rte->route_id = p_idrp_rt->route_id_in;
		p_rte->status = IDRP_ATTR_ROUTE_ID_NORM;
		
		p_rte->p_route[i] = p_rte->p_route_tail[i] = p_idrp_rt;
		p_idrp_rt->p_next_nlri = (idrpRoute *) 0;
		}	

	/* 10) increment reference count on the attribute record
	 * 11) here we need to calculate the idrp_preference
	 */

	trace_tf(idrp_trace_options, TR_NORMAL,0,("ph3_status_case2() 2.2] p_rt->rt_routers[0] for %x [%x]",*p_rt->rt_routers[0],p_rt->rt_routers[0]));

	p_att->ref_cnt++;	
	idrp_pref(p_idrp_rt);
	return(p_idrp_rt);

}
