/* 
 * $Id: idrp_rt_minadv.c,v 1.9 1995/10/31 17:13:44 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"

/* for timezone & timeval */
#include <sys/time.h>

/* idrp_rt_minadv.c
 *
 * Routines to support route advertisement frequency limitation 
 * per clause 7.17.3.1.  Interpretation of this clause has proven
 * to be a bit tricky; our reading is summarized as follows:
 *
 *  "My BIS shall not send updates representing routes learned from
 *  any BIS in an external RD more often than every 
 *  minRouteAdvertismentInterval time, excluding explicit withdrawals
 *  which shall be propagated right away.  My BIS shall not send 
 *  updates representing routes originating in my RD more often than
 *  every MinRDOriginationinterval time."
 *
 * We apply these rules to all updates, both for internal and inter-
 * domain IDRP.
 *
 * The basic algorithm we're implementing with these functions goes
 * like this (this is just an overview, it leaves out some details):
 *
 * - When a route is heard:  Check it to see if it's on a timer queue
 *   already.
 *   - If it is:  Mark it "dirty" and leave it on the queue.  Route 
 *     will be advertised when timer expires
 *   - If it's not:  Add it to a timer queue and mark the "last time
 *     I heard this" value as "now."  Propagate the route immediately.
 *
 * - When a timer expires:  Check timer queues which have expired.  For
 *   every route on a timer queue:
 *   - If the route has simply timed out, just leave it alone.  It
 *     has timed off the queue.
 *   - If the route is marked as having been changed, send an update
 *     for the route.
 *   - If the route is marked for deletion, this means that a withdraw
 *     was received for it.  Send the w/draw right away.
 *
 * Note that this algorithm has the effect of "gating" updates for
 * any given NLRI to occur at most once in (damping time).  There are
 * two issues to consider here:
 *
 * First, we run the risk of introducing synchronization into the 
 * global routing environment, especially if everyone runs with the
 * same damping times (i.e., if everyone just accepts the defaults).
 * See Sally Floyd and Van Jacobsen's paper in the '93 SIGCOMM
 * Proceedings for reasons this could be dangerous.  
 * 
 * Second, we might want to use a more sophisticated, alternative
 * approach, such as one that uses adaptive damping per peer, based
 * on how much "jabbering" that peer does.  
 *
 * However, this is how 10747 specs the damping function, so we 
 * implement it as spec'd... for now.
 *
 * We have augmented the rt_head structure (see rt_table.h) with a new
 * field, p_idrp_damping, which points to the idrpRoute being damped
 * if any, or NULL if no such route exists.  We put this into the 
 * rt_head structure since the damping is really per NLRI, not per
 * route as such (where "route" means "NLRI + attributes").  In the 
 * future, we'd kind of like to see a more general damping mechanism 
 * added to gated.  If this happens, many things may change.
 *
 * (jgs note:  if we get a flap cycle like this:  advertise/wdraw/
 * advertise/wdraw/... do we clamp the advertisements but propagate
 * the withdrawals?  If so, this is broken.  Look for this behavior.)
 *
 *
 * %% idrp_add_min_Route
 *
 * idrp_minadv_annlist	- add all of announce list nlri's to this 
 *			  announce list
 *
 * idrp_add_min_route_adv - add this route to minimum route
 *                          advertisement timer list
 * advmin_interval_calc   - for this run of routes, , cdvmin_interval_calc calculate
 *			    the interval after the next
 *			    min_route advertisement timer
 *			    goes off until the time for
 *			    this group of routes should go off.
 *			    for example if timer is 30 second
 *			    and 10 have gone by - 20 seconds
 *			    is the internval
 *			    Two types of timers handled - 
 *			    minimum route advertisement timer
 *			    (outside the domain)
 *			    and minimum route advertisment timer
 *			    (from within the domain)
 *                          to time after timer expires
 *                          or if no timer give full routines
 *
 * idrp_set_min_route_timer - set the minimum route timer to expire
 * idrp_process_minadv-   process list of routes after min route advertisement
 *                              timer expires
 * idrp_min_adv_rt              - process one route from without a domain
 * new_damping_queue		- set up a new damping queue for the specified peer
 * idrp_add_rt_min_advRD  - add this route to minimum adv
 *			    timer for this RD
 * idrp_set_min_advRD_time -  set the minimum advRD timer to expire 
 * idrp_process_minAdvRD - process routes damped by the min_advRd timer
 * idrp_min_advRd_rt		- process one route from within this RD 
 * idrp_process_with_minadv - process withdrawls in min_adv
 *     
 *   
 */

/*----------------------------------------------------------------------------
 * idrp_minadv_annlist()
 *
 * We walk through the NLRI portion of the announce list setting 
 * 1) the minimum advertisement timer for routes from other domains, and
 * 2) minadvRD for routes from this domain.
 *
 * We want to do the following things:
 * For announced routes:
 *
 * 1. If a route is already in damping, remove it from the announce list
 *    and mark it "dirty" so that it will be announced later, when the
 *    NLRI's timer fires.
 *
 *    This is achieved by marking the route with IDRP_STATUS_MIN_ADV_CHG.
 *
 * 2. If a route is not in damping, add it to the current timer list.
 *
 * For withdrawn routes:
 *
 * jgs -- The following cases (for w/drawn routes) had better be handled by the
 * "idrp_send_routes" (or whatever it's called) code:
 *
 * 1. If a route is not announced, remove it from the withdraw list and
 *    free the idrpRoute.
 *
 * 2. If a route is announced but is marked "REPLACE", mark it for 
 *    deletion & don't send a w/draw.
 *
 * 3. If a route is announced and is marked "WITH"(draw), mark it for
 *    deletion & do send a w/draw.
 */

idrp_ann_list *
idrp_minadv_annlist(p_ann,type)
idrp_ann_list	*p_ann;
int		type;
{

/* walk through an nlri portion of the announce list
 *	1) setting the minimum advertisement timer
 *	   for routes from other domains
 *	2) setting minadvRD for routes
 *		from this domain 
 * 		
 * 
 * flag on each route announced,
 * the appropriate flags in each route
 */

idrp_ann_list 	*p_atl;
idrpRoute	*p_irt;
idrpRoute	*p_irt_next;
int		i;

	ANN_LIST(p_atl,p_ann)
		{		
		/* walk the announce list for each NLRI Family */

		NLRI_FAMILY_LOOP(i)
			{
			ANN_ANN_LIST_DEL_WALK(p_irt,i,p_atl,p_irt_next)
				{
				/* walk the announce nlri portion
				 * set the add min route timer
				 * for routes from External peers
				 */

		
				trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, (" idrp_minadv_annlist nlri %s",iso_ptoa(&p_irt->nlri)));

				switch (p_irt->peer->type)
					{
					case IDRP_PEER_EXTERNAL:
						/* apply minRouteAdvertismentInterval damping to external routes
						 *  - if route already damped, take if off announce list
						 * 
						 * Route comes from external peer in phase 1 -
						 * 	 - ignore
						 * 	   we are not currently implementing phase1 damping
						 *
						 * external routes Phase 3 
						 * 	 need to be damped in phase1 or phase 3
						 *	 		 
						 */
						if (type == IDRP_MINADV_PHASE1)
							{
							trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_minadv_annlist(): phase 1 damping not done route %s from external peer", 
							iso_ptoa(&p_irt->nlri)));
							}
						else
							{
							idrp_add_min_route_adv(p_irt, p_ann);
							}
						break;
	
					case IDRP_PEER_INTERNAL:
						/*
						 * Minimum route Advertisement damping can 
						 * come in phase 1 or phase 3.
						 * (See Design Document for extended discussion )
						 *
						 *  
						 * Route comes from internal peer in phase 1 -
						 * 	 - ignore
						 * 	   we are not currently implementing phase1 damping
						 *
						 * Route comes from internal peer in phase 3
						 * 	-  find out if it is this RD or 
						 * 	   another RD, and then assign it to the
						 * 	   appropriate minimum route advertisement 
						 * 	   timer queue. 
						 * 
						 *  - if route already damped, take if off announce list
						 *	 
						 */

						if (type == IDRP_MINADV_PHASE1)
							{
							trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_minadv_annlist(): not damping route %s from internal peer", 
							iso_ptoa(&p_irt->nlri)));
							}
						else
							{
							/* phase 3
							 * add the internal route to the damping list
							 * for this RD
							 */

						
							if (idrp_my_rdi_rdpath(p_irt->p_attr))
								{		
								IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_LOCAL_RD);
								idrp_add_rt_min_advRD(p_irt, p_ann);
								}
							else
								{
								idrp_add_min_route_adv(p_irt, p_ann);
								}
							}
						break;											
						
	
					case IDRP_PEER_TEST:
						/* test peers should be vewy, vewy quiet */
						trace_log_tf (idrp_trace_options, 0, LOG_ERR,
							("idrp_minadv_annlist()(): Warning!  Received routes from test peer %s -- ignoring.",
							p_irt->peer->name));
						/* if we get routes from them, delete them -- they should not have arrived to begin with */
						/* jgs -- I think we want to be using idrp_free_nlri_att_rec() here but I'm not sure? */
						break;

					case IDRP_PEER_LOCAL:
						/* apply MinRDOriginationinterval damping to updates for our RD 
						 * 
						 * - All IGP routes will have local peer associated with them
						 *    since we originate the translation
						 *
						 * - All Locally configured routes  
						 *  
						 */
						trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("process min adv local peer nlri %s status %x",iso_ptoa(&p_irt->nlri),
									p_irt->status));

						idrp_add_rt_min_advRD(p_irt, p_ann);
						break;
					}
				} ANN_ANN_LIST_DEL_WALK_END;

			ANN_WITH_LIST_WALK(p_irt,i,p_atl)
				{
				switch(p_irt->peer->type)
					{
					case IDRP_PEER_LOCAL:
					case IDRP_PEER_EXTERNAL:
						trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("process min adv local peer with nlri %s status %x",iso_ptoa(&p_irt->nlri),
									p_irt->status));
						idrp_process_with_minadv(p_irt);
						break;

					case IDRP_PEER_INTERNAL:
						if (type == IDRP_MINADV_PHASE3)
							{
							/* for now all  tracing takes place from
							 * local peer configuration
							 *
							 * - this will change once gated changes again
							 */

							idrpPeer *p_this_node = &idrp_this_node;

							idrp_process_with_minadv(p_irt);
							trace_tf (idrp_trace_options, TR_MINADV,0, 
								  ("idrp_minadv_annlist()(): Received with routes from internal peer %s -- for nlri ",
								   p_irt->peer->name,iso_ptoa(&p_irt->nlri)));
						      }
						else
							{
							trace_log_tf (idrp_trace_options, 0, LOG_ERR,
								("idrp_minadv_annlist()(): Warning!  ph1: internal routes with processing peer %s -- ignoring.",
								p_irt->peer->name));
							}
						break;
									
		
					case IDRP_PEER_TEST:
						trace_log_tf (idrp_trace_options, 0, LOG_ERR,
							("idrp_minadv_annlist()(): Warning!  Received with routes from test peer %s -- ignoring.",
							p_irt->peer->name));
						break;
					}
				} ANN_WITH_LIST_WALK_END;

			} NLRI_FAMILY_LOOP_END;
		}ANN_LIST_END;

	p_ann = clean_ann_ann_list(p_ann);

	return(p_ann);
}

/*----------------------------------------------------------------------------
/*  %% Minimum route advertisement timer related routines */

/*----------------------------------------------------------------------------
 * idrp_add_min_route_adv()
 *
 * Check to see if this route is currently held down (i.e., it's been advertised
 * recently).  If so, increment the time until it comes off of damping and take
 * it off of the announce list.  If it's not in damping, set its damping timer
 * and move on.
 */
void
idrp_add_min_route_adv(p_idrp_rt, p_ann)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann;
{
	idrpAdvRt		*p_advrt;	
	idrpPeer		*peer;
	struct	timezone 	tzp;
	struct	timeval		tp;	
	int			id;
	idrp_ann_list		*p_atl;


	/* look to see if the minimum route advertisement set-up for this phase processing is there yet */
	/* We set peer to idrp_this_node.  That is, we associate all timer queues with the local peer.
	 * this has the (desired) property of making damping take effect per NLRI rather than per peer.  The
	 * other option (and the way we used to implement this) is:
	 *	peer = p_idrp_rt->peer;
	 * this would associate a timer queue with each peer.  This would have the effect of damping
	 * one chattering peer while allowing other, well-behaved peers to advertise the same NLRI without
	 * being damped.  To do this right would require some other changes too (e.g., we wouldn't want
	 * to select routes from a chattering peer until they came off damping, IMO -- right now damping
	 * only damps propagation, not selection. */

	peer = &idrp_this_node;

	trace_tf(idrp_trace_options, TR_MINADV,0, ("idrp_add_rt_minadv nlri %s",iso_ptoa(&p_idrp_rt->nlri)));


	id = family_to_nlri_id(p_idrp_rt->family);

	/* first we want to see if this NLRI is being damped. */
	if (IDRP_DAMPED_ROUTE(p_idrp_rt) != NULL) /* this NLRI is already damped */
		{
		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp route %s is damped already",iso_ptoa(&p_idrp_rt->nlri)));
 
		/* Set the "changed" flag on the placeholder route.
		 */

		IDRP_STATUS_BIT_SET(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG);

		/* take this route off the announce list; it's being damped
		 */
		p_atl = find_ann_list_entry(p_idrp_rt->peer,p_idrp_rt->p_attr,p_ann);
		if (p_atl)
			{
			unlink_ann_list(p_idrp_rt, p_atl, NLRI_LINKED_P_ANN); 
			}
		else
			{
			trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("IDRP has tried to pull min adver route from ann list but can't find entry peer %s route %s",
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));								


			}
		}
	else /* this NLRI is not currently being damped */
		{
		/* we need to start damping this NLRI, so point the damping pointer at it 
		 * marking it as being damped 
		 */

		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, (" idrp_add_min_route - adding to queue %s",iso_ptoa(&p_idrp_rt->nlri)));

		IDRP_DAMPED_ROUTE(p_idrp_rt) = p_idrp_rt;

		/* make a damping queue if none is running yet
		 */

		if (!peer->start_min_adv)
			{
			new_damping_queue(peer);
			}

		/* add it to the damping queue
		 */

		p_advrt = peer->p_minadv_tail;
		if (p_advrt->routes[id].tail)
			{
			/* just insert our route at the tail of the timer queue
			 */

			p_advrt->routes[id].tail->p_min_adv = p_idrp_rt;
			p_idrp_rt->p_min_adv_back = p_advrt->routes[id].tail;
			p_advrt->routes[id].tail = p_idrp_rt;
			}
		else
			{
			/* empty (new) queue */
			p_advrt->routes[id].head = p_advrt->routes[id].tail = p_idrp_rt;
			p_idrp_rt->p_min_adv_back = (idrpRoute *) NULL;
			}
		p_idrp_rt->p_min_adv = (idrpRoute *) NULL;
		}

	return; 
}

/*----------------------------------------------------------------------------
 * new_damping_queue()
 *
 * We've decided that we don't have a damping queue for this interval yet, and
 * that we need one.  Set it up.
 */
static void new_damping_queue(peer)
idrpPeer	*peer;
{
	idrpAdvRt		*p_advrt;	
	struct	timezone 	tzp;
	struct	timeval		tp;	

	assert(!peer->start_min_adv);
	
	/* If start_min_adv is FALSE, there is no timer running yet for the present interval.  
	 * Set the flag and start a timer queue for this interval. 
	 */
	 
	/* set the "we've started a timer for this interval" flag
	 */

	peer->start_min_adv = TRUE;

	/* set up a queue for routes held down this interval, and stamp it with the length
	 * of the interval
	 */ 
	p_advrt = (idrpAdvRt *) idrp_local_mem_fit(sizeof(idrpAdvRt));	
	if (gettimeofday(&tp,&tzp))
		{
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("new_damping_queue(): gettimeofday() failed, results unpredictable."));
		return;
		}

	/* translate time of day here to seconds to calculate next interval 
	 * this gives us a delta, in seconds, from now until the end of the interval 
	 * (could be the full interval length if no timer was running already)
	 */

	p_advrt->interval = advmin_interval_calc(&tp,peer,IDRP_MIN_ADV_TIMER_TYPE);

	/* add this timer queue into the peer's queue of queues */
	if (peer->p_minadv_head)
		{
		/* insert this timer queue at the end of the peer's queue of timer queues 
		 * (since it's got one set up already) */
		peer->p_minadv_tail->p_next = p_advrt;
		peer->p_minadv_tail = p_advrt;
		}
	else
		{
		/* otherwise, start a queue of timer queues and make this the first member */
		peer->p_minadv_head = peer->p_minadv_tail = p_advrt;
		}
}
	
/*----------------------------------------------------------------------------
 * advmin_interval_calc()
 * 
 * If a timer is already running for this peer, return a delta between now and
 * when the timer will expire.  If no timer is running, return the full timer
 * value.
 */
int
advmin_interval_calc(p_tp,peer,type)
struct timeval	*p_tp;
idrpPeer	*peer;
int		type;
{
	if (type == IDRP_MIN_ADV_TIMER_TYPE)
		{		 
		if (peer->min_adv_started)
			{
			return(p_tp->tv_sec - peer->min_adv_started);
			}
		/* if the timer is not already running - return min adv time */
		if (peer->min_adv_time == 0)
			{
			return(1);
			}
		return(peer->min_adv_time);
		}
	if (type == IDRP_MIN_ADVRD_TIMER_TYPE)
		{
		if (peer->min_advRD_started)
			{
			return(p_tp->tv_sec - peer->min_advRD_started);
			}
		if (peer->min_advRD_time == 0)
			{
			return(IDRP_MIN_ADVRD);
			}
		/* if the timer is not already running - return min adv time */
		return(peer->min_advRD_time);
		}
		
	/* we should never reach this point, so complain and die */

	trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("advmin_interval_calc(): invalid type code %d", type));
	assert((type == IDRP_MIN_ADV_TIMER_TYPE) || (type == IDRP_MIN_ADVRD_TIMER_TYPE));
	return (0);
}

/*----------------------------------------------------------------------------
 * idrp_set_min_route_timer()
 * 
 * gets called from phase3_process()
 * XXX jgs -- we will also need to call from phase1, I think.
 *
 * Schedule a timer to fire when the requested interval expires.  When the timer
 * fires, idrp_process_minadv() will be called to process the timer queue.
 */
int
idrp_set_min_route_timer()
{
idrpPeer	*peer;
struct	timeval	tp;
struct timezone	tzp;
		
	/* what time is it? */
	if (gettimeofday(&tp,&tzp))
		{
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_set_min_route_timer(): gettimeofday() failed"));
		return (FALSE);
		}


	/* all minimum route timers belong to the local node
	 */
 
	peer = &idrp_this_node;
		{
		/* check to see if a min advertisement timer has been requested */
		if (peer->start_min_adv)
			{
			if (!peer->min_adv_started)
				{
				/* the timer has been requested, and isn't running yet
				 * 
 				 * stamp the "when I started this timer" value with the present time 
				 */

				bcopy(&tp.tv_sec,&peer->min_adv_started,sizeof(time_t));

				/* crank up a timer to fire when interval expires
				 */

				task_timer_set(peer->idrp_minadv_timer,peer->p_minadv_head->interval,(time_t) 0); /*@@@ minadvRD */

				/* zero out interval in preparation for doing this fun stuff again
				 */

				peer->p_minadv_head->interval = 0;	

				/* 
				 * trace the fact min adv was just started 
				 */

				trace_tf(idrp_trace_options, TR_MINADV,0, ("IDRP min_adv timer request, timer started")); 

				}
			else
				{
			
				/* 
				 * trace the fact min adv was already running 
				 */
				  trace_tf(idrp_trace_options, TR_MINADV,0, ("IDRP min_adv timer request, timer running")); 

				}
			peer->start_min_adv = FALSE;	
			}
		}


	return (TRUE);
}
		
/*----------------------------------------------------------------------------
 * idrp_process_minadv()
 *
 * This gets called when (and only when) the min route adver timer fires.  We go along
 * the queue of routes that are in damping and send updates for all those whose damping has 
 * expired, and free them from the timer queue.
 */
void
idrp_process_minadv(tip,interval)
task_timer	*tip;
time_t	interval;
{
idrpPeer	*peer;
idrpRoute	*p_irt;
idrpAdvRt	*p_adv;
idrpAdvRt	*p_adv_last = 0;
idrp_ann_list	*p_ann_list;
int		i;
int		changes = 0;
int		cnt = 0;

	/* what peer's timer task fired to invoke us?
	 */

	peer = (idrpPeer *) tip->task_timer_task->task_data;

	/* for now, the peer that called us should always be the local peer, else something's wrong 
	 */

	assert(peer == &idrp_this_node);

	/* set up the announce list -- we want to send any routes which have done their time 
	 */

	p_ann_list = (idrp_ann_list *)  idrp_local_mem_fit(sizeof(idrp_ann_list));
	rt_open(peer->task);
	p_adv = peer->p_minadv_head;
	while (p_adv)
		{
		/* loop along finding routes whose damping time has expired.  When we find such a route,
		 * release it from durance vile by putting it onto the announce list and then freeing it. */
		if (p_adv->interval) 
			{
			 /* If interval is non-zero, this route is still being held down.  Since the queue
			  * is temporally ordered, we can exit the loop, since all routes after this one
			  * will still be in damping. */
			break;
			}
		NLRI_FAMILY_LOOP(i)
			{
			idrpRoute *p_next_irt;	

			/* got here, so this timer queue's time is up.  set the routes up for sending. */
			for (p_irt = p_adv->routes[i].head; p_irt; p_irt = p_next_irt)
				{
				p_next_irt = p_irt->p_min_adv; 
				/* Two kinds of routes:  Those to delete (and not send updates, they were
				 * already sent as explicit withdrawals) and those to send updates for.
				 * Add this route to the ann_list if it's an update, else free it and
				 * bump changes' value.
				 */
				changes += idrp_min_adv_rt(p_irt,p_ann_list);
				cnt++;	/* not presently used but keep around in case we want to log # of routes operated on */
				p_irt->p_min_adv_back = p_irt->p_min_adv = (idrpRoute *) NULL;	
				}
			}
		/* free this timer queue entry:  the route has exited the queue and is winging its 
		 * way towards freedom in the north (er, we're going to send the update) */
		p_adv_last = p_adv;
		p_adv = p_adv->p_next;
		IDRP_MEM_FIT_FREE(p_adv_last);
		}	
	
	/* Tell gated that we've twiddled with the routing table.  All these changes should be route 
	 * deletions.
	 */

	rt_close(peer->task,&peer->gw,changes,NULL);
	
	/* must send the routes to the peers 
	 * jgs -- if it is true (as it seems to be) that we must do damping for phase1 as well,
	 * jgs -- this may have to change.  check the code for "send_phase3".
	 */ 

	if (p_ann_list->p_attr)
		{
		/* list is not empty
		 */

		idrp_send_phase3_routes(p_ann_list);
		}

#ifdef IDRP_QOS
	/* dump to the qos kernel 
	 * - dump to kerl to make sure
	 * - kernel stays in sync with idrp functions
	 */

	idrp_qos_kernel_dump();
#endif

	/* free the announce list 
 	 */

	free_ann_list(p_ann_list);	
	IDRP_MEM_FIT_FREE(p_ann_list);
	
	/* check to see if we need to restart timers */

	if (p_adv) 
		{
		struct	timeval	tp;
		struct timezone	tzp;
		
		assert(p_adv->interval);

		/* there are still routes in damping, so fix up the queue and schedule a timer
		 * to fire for next time 
		 */

		/* what time is it? */

		if (gettimeofday(&tp,&tzp))
			{
			trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_process_minadv(): gettimeofday() failed"));
			return;
			}

		bcopy(&tp.tv_sec,&peer->min_adv_started,sizeof(time_t));
		peer->p_minadv_head = p_adv;
		task_timer_set(peer->idrp_minadv_timer,p_adv->interval,(time_t) 0); /*@@ minadvRD ?? @@@*/
		p_adv->interval = 0;

		}
	else
		{
		/* the timer queue is empty; we have no routes in damping.  Fix up the queue...
		 */

		peer->p_minadv_head = peer->p_minadv_tail = (idrpAdvRt *) 0;

		/* and note that we don't need to run the timer
		 */
		peer->min_adv_started = (time_t) 0;
		peer->start_min_adv = FALSE;
		task_timer_reset(peer->idrp_minadv_timer);
		}


	  trace_tf(idrp_trace_options, TR_MINADV,0, ("end of idrp_process_minadv",0));


}	

/*----------------------------------------------------------------------------
 * idrp_min_adv_rt()
 *
 * Returns number of changes made to gated's table (these will be for w/draws
 * only).
 * 
 * We call this when a timer list's time has come.  Anything on this
 * list has by definition had its timer expire.
 *
 * We need to check two routes:  First, the route itself.  We always need
 * to check this one to see if it needs to be deleted.  Second, the current
 * active route.  This may be different from the route on the damping timer;
 * only one route per NLRI sits on the timer.
 *
 * For external routes:  Either delete the route (if IDRP_STATUS_DELETE is 
 * set) or add it to the announce list.
 *
 * For routes learned from internal peers, log an error:  Internal peer routes
 * should never be placed on damping.  Note that this is distinct from 
 * internal IDRP routes we have originated, e.g., routes leaked into IDRP from
 * IS-IS.  Such routes have their own damping timer we're obliged to enforce.
 *
 * The delete flag exists because we need to keep a record of a route's existence
 * around after it's been withdrawn, so that if it's re-advertised before the 
 * damping timer expires we will know about it.
 *
 * If the route isn't destined for the bit-bucket, we add it to the announce 
 * list.
 */
int
idrp_min_adv_rt(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
{
idrpPeer	*peer;

	peer = &idrp_this_node;	
	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_min_adv_rt processing route %s",iso_ptoa(&p_idrp_rt->nlri)));

	/* IDRP_EXT_INFO_ROUTE_ID is a magic cookie which translates as "I'm in the process of 
	 * importing this route into IDRP; it's coming in from some source other than IDRP or
	 * the IGP."  All external routes which reach this point should already have IDRP_STATUS_EXT_INFO
	 * set in their flags.
	 */
	assert(p_idrp_rt->route_id_in != IDRP_EXT_INFO_ROUTE_ID);

	/* Furthermore, we should never be called with an IIDRP-learned route.  Such routes should never
	 * be put onto timer queues to begin with.
	 */

	assert((p_idrp_rt->status & IDRP_STATUS_EXT_INFO) == 0);

	/* Anything internal to the RD had better go to idrp_min_advRD_rt
	 */

	assert((p_idrp_rt->status & IDRP_STATUS_RD_ROUTE) == 0);

	/* we used to keep a count variable to use for the return value.  However, since
	 * we're only operating on a single route at a time, we can just hard-code in 0 
	 * or 1 and save a few bytes and a few instructions. */
	 

	/* First check for the active route (if different from the damped route).  We need to do this
	 * first since the damped route might be going away. 
	 */

	if (p_idrp_rt->p_rt->rt_head == (rt_head *) NULL)
		{
		/* idrp route pointer has been
		 * detacted from the gated nlri head pointer
		 * - The  only reason is that the route has
		 *   been deleted, and we have not been successful
		 *   in stopping gated from delete our route
		 *   - LOG error this, and try to survive 
		 */
 
		 IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DELETE)
			{
			idrpRoute *p_irt;
			trace_log_tf(idrp_trace_options, 0, LOG_ERR,
				     ("(idrp_min_adv_rt nlri %s peer %s gated let go of delete idrp route to rt_entry link header pointer is null",
					iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));

			/* try to locate destination for nlri
			 *
			 */

			p_irt = find_active_idrp_route(p_idrp_rt);
			if (p_irt)
				{
				/* found the active route and it has a  idrp route
				 * associated with active route
				 *  - link it to the announce list
				 */

				IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_LOC_RIB);
				link_ann_list(p_irt,p_ann_list,NLRI_LINKED_P_ANN);
				}
						
			}
		}
	else if (IDRP_DAMPED_ROUTE(p_idrp_rt) == NULL) /* kludge kludge kludge -- delete this, oh please... */
		{
		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0,
			 ("idrp_min_adv_rt() error: route %s from peer %s has NULL damped route pointer!",
			iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));
		} /* end grody kludge */
	else
		{
#ifdef	IDRP_QOS

		if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
		    { 
		    qos_min_adv_rt(p_idrp_rt,p_ann_list);
		    }
		else
		   {
#endif /* IDRP_QOS */

 		   /* if there is an active route at all  */ 
  		   IS_IDRP_ACTIVE_ROUTE(p_idrp_rt)
			{
			if (ACTIVE_IDRP_ROUTE(p_idrp_rt))
				{
				IDRP_STATUS_BIT_TEST(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG)
					{
					IDRP_STATUS_BIT_TEST(ACTIVE_IDRP_ROUTE(p_idrp_rt),IDRP_STATUS_DELETE)
						{
						trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0,
							  ("idrp_min_adv_rt() error: route %s from peer %s damping timer expired: delete on active route",
							iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));
						}
					else
						{
						/* idrp active route - send it
						 */

						IDRP_STATUS_BIT_SET(ACTIVE_IDRP_ROUTE(p_idrp_rt),IDRP_STATUS_LOC_RIB);	
						link_ann_list(ACTIVE_IDRP_ROUTE(p_idrp_rt),p_ann_list,NLRI_LINKED_P_ANN);
						
						trace_tf (idrp_trace_options, TR_MINADV,0,
								  ("idrp_min_adv_rt(): route %s from peer %s damping timer expired; sending update", 
								iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->peer->name));
							
						}
					}
				}
			/* else nothing to advertise -- could happen if we've been holding on to an old w/drawn route, or if  
			 * the route hasn't changed.
			 */
			}
#ifdef	IDRP_QOS 
		    }	
#endif	/* IDRP_QOS */
		IDRP_STATUS_BIT_CLEAR(IDRP_DAMPED_ROUTE(p_idrp_rt),(IDRP_STATUS_MIN_ADV_CHG | IDRP_STATUS_MIN_ADV_CHG_RDS));
		IDRP_DAMPED_ROUTE(p_idrp_rt) = NULL;
		}
	
	/* we're done with the damping pointer and need to clear it */
	
	/* now check the damped route
	 */
	IDRP_STATUS_BIT_TEST(p_idrp_rt, IDRP_STATUS_DELETE)
		{
		/* route has been marked for deletion, make it GONE */			 
		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DEL_SEND)
			{
			  trace_tf (idrp_trace_options, TR_MINADV,0,("idrp_min_adv_rt(): route %s status %x from peer %s deletion timer expired; deleting route", 
								     iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 
				
			idrp_free_outlist(p_idrp_rt);
			idrp_free_nlri_att_rec(p_idrp_rt,NLRI_LINKED_P_NEXT);
			idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);	
			idrp_free_idrpRoute(p_idrp_rt);
			}
		else 
			{
			if ((p_idrp_rt->status & IDRP_STATUS_INT_MASK) == IDRP_STATUS_ADJ_RIB)
				{
					{
					trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0,("idrp_min_adv_rt route %s (%x) peer %s has only adjRib and delete, deleteing" ,
						iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 
					idrp_free_outlist(p_idrp_rt);
					idrp_free_nlri_att_rec(p_idrp_rt,NLRI_LINKED_P_NEXT);
					idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);	
					idrp_free_idrpRoute(p_idrp_rt);
					}
				
				}	
			else
				{
				  trace_tf (idrp_trace_options, TR_MINADV,0, ("idrp_min_adv_rt(): route %s status %x from peer %s not deleteing DEL_SEND not set ", 
									      iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 
					
				}
			}
		return(1); 
		}

	return(0);
}	

/*----------------------------------------------------------------------------
 * idrp_add_rt_min_advRD()
 *
 * Check to see if this route is currently held down (i.e., it's been advertised
 * recently).  If so, increment the time until it comes off of damping and take
 * it off of the announce list.  If it's not in damping, set its damping timer
 * and move on.
 * 
 * Note that this is pretty much the same as idrp_add_min_route_adv() only it's for
 * routes originating within our RD (i.e., statically configured routes or IGP
 * routes.  We pay attention to a different damping value than idrp_add_min_route_adv()
 * cares about.  (This timer is typically much longer.)
 */
void
idrp_add_rt_min_advRD(p_idrp_rt, p_ann) 
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann;
{
idrp_ann_list	*p_atl;
idrpAdvRt	*p_advrt;	
idrpPeer	*peer;
struct	timezone tzp;
struct	timeval	tp;	
int	id;


	/* look to see if the minimum route advertisement set-up for this phase processing is there yet */
	/* The other option for setting peer is:
	 *	peer = p_idrp_rt->peer;
	 * See more extensive discussion under idrp_add_min_route_adv().
	 */

	peer = &idrp_this_node;
	
	id = family_to_nlri_id(p_idrp_rt->family);

	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_add_rt_min_advRD route %s",iso_ptoa(&p_idrp_rt->nlri))); 

	/* first we want to see if this NLRI is being damped.
	 */

	if (IDRP_DAMPED_ROUTE(p_idrp_rt) != NULL) /* this NLRI is already damped */
		{
		trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0,(" idrp_add_rt_min_advRD is damped"));
		/* Set the "changed" flag on the placeholder route.
		 */

		IDRP_STATUS_BIT_SET(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG);

		/* take this route off the announce list; it's being damped
		 * 
		 */

		p_atl = find_ann_list_entry(p_idrp_rt->peer,p_idrp_rt->p_attr,p_ann);
		if (p_atl)
			{
			unlink_ann_list(p_idrp_rt, p_atl, NLRI_LINKED_P_ANN); 
			}
		else
			{
			trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("IDRP has tried to pull min adver route from ann list but can't find entry peer %s route %s",
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));								

			}

		}
	else /* this NLRI is not currently being damped */
		{
		/* we need to start damping this NLRI, so point the damping pointer at it 
		 */

		trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0,(" idrp_add_rt_min_advRD is not damped, adding to queue"));
		IDRP_DAMPED_ROUTE(p_idrp_rt) = p_idrp_rt;


		/* make a damping queue if none is running yet 
		 */

		if (!peer->start_min_advRD)
			{
			new_RD_damping_queue(peer);
			}

		/* add it to the damping queue 
		 * make sure queue came back ok
		*/


		assert((peer->p_minadvRD_head != (idrpAdvRt *) 0));
		
		p_advrt = peer->p_minadvRD_tail;	
		if (p_advrt->routes[id].tail)
			{
			/* just insert our route at the tail of the timer queue */

			p_advrt->routes[id].tail->p_min_advRD = p_idrp_rt;
			p_idrp_rt->p_min_advRD_back = p_advrt->routes[id].tail;
			p_advrt->routes[id].tail = p_idrp_rt;
			}
		else
			{
			/* empty (new) queue for this id */
			p_advrt->routes[id].head = p_advrt->routes[id].tail = p_idrp_rt;
			p_idrp_rt->p_min_advRD_back = (idrpRoute *) NULL;
			}

		/* clear the flag for the next route
		 * minimum route advertisement flag
		 */
 
		p_idrp_rt->p_min_advRD = (idrpRoute *)NULL;
		}
	

	return;
}

/*----------------------------------------------------------------------------
 * new_RD_damping_queue()
 *
 * We've decided that we don't have an RD damping queue for this interval yet, and
 * that we need one.  Set it up.
 */
static void new_RD_damping_queue(peer)
idrpPeer	*peer;
{
	idrpAdvRt		*p_advrt;	
	struct	timezone 	tzp;
	struct	timeval		tp;	

	assert(!peer->start_min_advRD);
	
	/* If start_min_adv is FALSE, there is no timer running yet for the present interval.  
	 * Set the flag and start a timer queue for this interval. 
	 */
	 
	/* set the "we've started a timer for this interval" flag
	 */
 
	peer->start_min_advRD = TRUE;

	/* set up a queue for routes held down this interval, and stamp it with the length
	 * of the interval
	 */ 

	p_advrt = (idrpAdvRt *) idrp_local_mem_fit(sizeof(idrpAdvRt));	
	if (gettimeofday(&tp,&tzp))
		{
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("new_RD_damping_queue(): gettimeofday() failed, results unpredictable."));
		return;
		}

	/* translate time of day here to seconds to calculate next interval 
	 * this gives us a delta, in seconds, from now until the end of the interval 
	 * (could be the full interval length if no timer was running already)
	 */

	p_advrt->interval = advmin_interval_calc(&tp,peer,IDRP_MIN_ADVRD_TIMER_TYPE);

	/* add this timer queue into the peer's queue of queues 
	 */

	if (peer->p_minadvRD_head)
		{
		/* insert this timer queue at the end of the peer's queue of timer queues 
		 * (since it's got one set up already)
		 */

		peer->p_minadvRD_tail->p_next = p_advrt;
		peer->p_minadvRD_tail = p_advrt;
		}
	else
		{
		/* otherwise, start a queue of timer queues and make this the first member
		 */
		peer->p_minadvRD_head = peer->p_minadvRD_tail = p_advrt;
		}
}

/*----------------------------------------------------------------------------
 * idrp_set_min_advRD_timer()
 *
 * Schedule the RD timer (which is the timer controlling routes originated 
 * within our RD, that is, static and IGP routes).
 *
 * For now, this timer is only on the local node.  The code could be changed,
 * but this is probably the Right Way (as opposed to the way it's done in
 * idrp_set_min_route_timer(), which is arguable).
 */
int
idrp_set_min_advRD_timer()
{
idrpPeer	*peer;
struct	timeval	tp;
struct timezone	tzp;
		
	/* what time is it? */
	if (gettimeofday(&tp,&tzp))
		{
		trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_set_min_advRD_timer(): gettimeofday() failed"));
		return (FALSE);
		}
	
	/* Use local peer instead of keeping a per-peer timer.  See more detailed comments under
	 * idrp_set_min_route_timer(). This is probably the right thing in this case. */
	peer = &idrp_this_node; 
	
	/* check to see if a min advertisement timer has been requested */
	if (peer->start_min_advRD)
		{
		if (!peer->min_advRD_started)
			{
			/* the timer has been requested, and isn't running yet
			 * 
 			 * stamp the "when I started this timer" value with the present time 
			 */

			bcopy(&tp.tv_sec,&peer->min_advRD_started,sizeof(time_t));

			/* crank up a timer to fire when interval expires */

			task_timer_set(peer->idrp_minadvRD_timer,peer->p_minadvRD_head->interval,(time_t) 0);

			/* zero out interval in preparation for doing this fun stuff again */

			peer->p_minadvRD_head->interval = 0;	

			/* 
			 * trace the fact min adv was just started 
			 */

			  trace_tf(idrp_trace_options, TR_MINADV,0,("IDRP min_advRD timer request, timer started")); 
				
			}
		else
			{
		
			/* 
			 * trace the fact min advRD really set
			 */
			  trace_tf(idrp_trace_options, TR_MINADV,0, ("IDRP set the min_advRd timer request, timer running")); 
				

			}
		peer->start_min_advRD = FALSE;
		}

	return (TRUE);
}
		
/*----------------------------------------------------------------------------
 * idrp_process_minadvRD()
 *
 * This gets called when (and only when) the min RD adver timer fires.
 */
void
idrp_process_minadvRD(tip,interval)
task_timer	*tip;
time_t	interval;
{
idrpPeer	*peer;
idrpRoute	*p_irt;	
idrpAdvRt	*p_adv;
idrpAdvRt	*p_adv_last;
idrp_ann_list	*p_ann_list;
int		i;
int		changes = 0;
int		cnt = 0;

	/* what peer's timer task fired to invoke us?  This should probably always be the local peer. */
	peer = (idrpPeer *) tip->task_timer_task->task_data;
	/* For now this should always be the local peer.  However, note that this might not hold true for a multi-policy route server. */
	assert(peer == &idrp_this_node);


	/* set up the announce list -- we want to send any routes which have done their time 
	*/

	p_ann_list =  (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));
	rt_open(peer->task);
	p_adv = peer->p_minadvRD_head;
	while (p_adv)
		{
		/* loop along finding routes whose damping time has expired.  When we find such a route,
		 * release it from durance vile by putting it onto the announce list and then freeing it. */
		if (p_adv->interval)
			{
			/* If interval is non-zero, this route is still being held down.  Since the queue
			 * is temporally ordered, we can exit the loop, since all routes after this one
			 * will still be in damping.
			 */
			break;
			}
		NLRI_FAMILY_LOOP(i)
			{
			idrpRoute *p_next_irt;

			/* got here, so this timer queue's time is up.  set the routes up for sending.
			 */

			for (p_irt = p_adv->routes[i].head; p_irt; p_irt = p_next_irt)
				{
				p_next_irt = p_irt->p_min_advRD;
				/* Two kinds of routes:  Those to delete (and not send updates, they were
				 * already sent as explicit withdrawals) and those to send updates for.
				 * Add this route to the ann_list if it's an update, else free it and
				 * bump changes' value. 
				 *
				 */
				changes += idrp_min_advRD_rt(p_irt,p_ann_list);
				cnt++;
				p_irt->p_min_advRD = p_irt->p_min_advRD_back = (idrpRoute *)NULL;
				}
			}
		/* free this timer queue entry:  the route has exited the queue and is winging its 
		 * way towards freedom in the north (er, we're going to send the update)
		 */
		p_adv_last = p_adv;
		p_adv = p_adv->p_next;
		IDRP_MEM_FIT_FREE(p_adv_last);
		}	

	/* Tell gated that we've twiddled with the routing table.  All these changes should be route 
	 * deletions.
	 */

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

	/* send routes to peers 
	 * if there is something on the announce list
	 * and then free the announce list 
	 */

	if (p_ann_list->p_attr)
		{
		idrp_send_phase3_routes(p_ann_list);
		}

	/* free the announce list */

	free_ann_list(p_ann_list);
	IDRP_MEM_FIT_FREE(p_ann_list);

	/* check to see if we need to restart timers */
	if (p_adv) 
		{
		struct	timeval	tp;
		struct timezone	tzp;
		
		assert(p_adv->interval);

		/* there are still routes in damping, so fix up the queue and schedule a timer
		 * to fire for next time 
		 */

		/* what time is it? */

		if (gettimeofday(&tp,&tzp))
			{
			trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("idrp_process_minadvRD(): gettimeofday() failed"));
			return;
			}

		bcopy(&tp.tv_sec,&peer->min_advRD_started,sizeof(time_t));

		/* there are still routes in damping, so fix up the queue and schedule a timer
		 * to fire for next time
		 */

		peer->p_minadvRD_head = p_adv;
		task_timer_set(peer->idrp_minadvRD_timer,p_adv->interval,(time_t) 0); /*@@@ */
		p_adv->interval = 0;
		}
	else
		{
		/* the timer queue is empty; we have no routes in damping.  Fix up the queue... 
		 */

		peer->p_minadvRD_head = peer->p_minadvRD_tail = (idrpAdvRt *) 0;
		task_timer_reset(peer->idrp_minadvRD_timer);

		/* and note that we don't need to run the timer
		 */
		peer->start_min_advRD = FALSE;
		peer->min_advRD_started = (time_t) 0;
	      }

              trace_tf(idrp_trace_options, TR_NORMAL,0, ("end of idrp_process_minadvRD",0));
	
             return;
}

/*----------------------------------------------------------------------------
 * idrp_min_advRD_rt
 *
 * Returns number of changes made to gated's table (these will be for w/draws
 * only).
 * 
 * We call this when a timer list's time has come.  Anything on this
 * list has by definition had its timer expire.
 *
 * For internal routes:  Either delete the route (if IDRP_STATUS_DELETE is 
 * set) or add it to the announce list.  Note that "internal" routes means 
 * IGP or static routes, NOT IIDRP routes.  IIDRP routes should never be held
 * down at all.
 *
 * For routes learned from external peers, log an error:  External peer routes
 * should never be placed on this damping timer, they have their own timer.
 *
 * The delete flag exists because we need to keep a record of a route's existence
 * around after it's been withdrawn, so that if it's re-advertised before the 
 * damping timer expires we will know about it.
 *
 * If the route isn't destined for the bit-bucket, we add it to the announce 
 * list.
 */
int
idrp_min_advRD_rt(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
{
idrpPeer	*p_trace_peer;

	p_trace_peer = &idrp_this_node;
        trace_tf(idrp_trace_options, TR_NORMAL,0, ("idrp_min_advRD(1)  nlri %s (%x) [%x] peer: %s",
		iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt,p_idrp_rt->peer->name));
		

	/* IDRP_EXT_INFO_ROUTE_ID is a magic cookie which translates as "I'm in the process of 
	 * importing this route into IDRP; it's coming in from some source other than IDRP or
	 * the IGP."  All that stuff should be caught by idrp_min_adv_rt().
	 * 
	 */

	if (p_idrp_rt->peer->type == IDRP_PEER_LOCAL)
		{ 	
		assert(p_idrp_rt->route_id_in != IDRP_EXT_INFO_ROUTE_ID);
		
	 	/* jgs -- we may be vulnerable to funny interactions when we somehow
		 * jgs -- lose an RD route and have it replaced by an external
		 * jgs -- route
		 */

		assert(p_idrp_rt->status & IDRP_STATUS_RD_ROUTE);

		/* should this be an AND here or the combination of all three
		 */


		if ((p_idrp_rt->status & (IDRP_STATUS_LOCAL_DAMP_DEL|IDRP_STATUS_MIN_ADV_CHG|IDRP_STATUS_LOCAL_PH3_NEW_ROUTE)) == 
							(IDRP_STATUS_LOCAL_DAMP_DEL|IDRP_STATUS_MIN_ADV_CHG|IDRP_STATUS_LOCAL_PH3_NEW_ROUTE))
			{
			/* OK -we have a special case 
			 * osilocal route, deleted and returned
			 * to functionality within the min adv timeout
			 */

                	trace_tf(idrp_trace_options, TR_NORMAL,0, ("idrp_min_advRD(2) nlri %s (%x) peer: %s",
			iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt->peer->name));
		
			return(idrp_minadvRD_local_init(p_idrp_rt,p_ann_list));
			}
		}


	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("(idrp_min_advRD_rt (3) nlri %s peer %s ",
				iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));

	assert(p_idrp_rt->p_rt != NULL);	

	/* First check for the active route (if different from the damped route).  We need to do this
	 * first since the damped route might be going away. 
	 */

	if (p_idrp_rt->p_rt->rt_head == (rt_head *) NULL)
		{
		/* idrp route pointer has been
		 * detacted from the gated nlri head pointer
		 * - The  only reason is that the route has
		 *   been deleted, and we have not been successful
		 *   in stopping gated from delete our route
		 *   - LOG error this, and try to survive 
		 */
 
		 IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DELETE)
			{
			idrpRoute *p_irt;
			trace_log_tf(idrp_trace_options, TR_NORMAL, LOG_ERR,
				     ("(idrp_min_advRD_rt(4) nlri %s peer %s gated let go of delete idrp route to rt_entry link header pointer is null",
					iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));

			/* try to locate destination for nlri
			 *
			 */

			p_irt = find_active_idrp_route(p_idrp_rt);
			if (p_irt)
				{
				/* found the active route and it has a  idrp route
				 * associated with active route
				 *  - link it to the announce list
				 */

				IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_LOC_RIB);
				link_ann_list(p_irt,p_ann_list,NLRI_LINKED_P_ANN);
				}
						
			}
		}
	else
		{
		/* p_idrp_rt->p_rt->p_head is there
		 * - now try the qos  
		 * 
		 */
 
#ifdef	IDRP_QOS

		if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
		    { 
		    qos_min_advRD_rt(p_idrp_rt,p_ann_list);
		    }
		else
		   {
#endif /* IDRP_QOS */
		    IS_IDRP_ACTIVE_ROUTE(p_idrp_rt) /* if there is an active route at all */
			{
			/* if we have an active route - 
			 * - do we have a damped IDRP route?
			 */	
			if (IDRP_DAMPED_ROUTE(p_idrp_rt) != NULL)
			  {
			   /* we have an active route and a damped idrp route,
			    * - do we have a IDRP route attached to the  
			    *   active route? 
			    */
	 
			   if (ACTIVE_IDRP_ROUTE(p_idrp_rt) != NULL)
				{
				/* Got an Active, IDRP route
				 * Does the damped idrp route - have IDRP_STATUS_MIN_ADV_CHG attached to it 
				 */ 
				IDRP_STATUS_BIT_TEST(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG)
					{
					/* Got an Active IDRP route
					 *  - got a Damped route with IDRP_STATUS_MIN_ADV_CHG
					 * :::=  this means that there were changes during the
					 * transition time
					 */  
		
					idrpRoute *p_irt = ACTIVE_IDRP_ROUTE(p_idrp_rt);	

					IDRP_STATUS_BIT_TEST(p_irt,IDRP_STATUS_DELETE)
						{
						/* got an Active IDRP Route, Got a Damped route with 
						 * IDRP_STATUS_MIN_ADV_CHG -> changes during transition
						 *  Did we also have a deleted route as the active route?
						 * --- if we do BIG problame-o, yeper  
						 */
	
						trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_min_advRD_rt(5): route %s from peer %s damping timer expired; not sending update delete flag on active route", 
							iso_ptoa(&p_irt->nlri), p_irt->peer->name));
						}
					else
						{
						/* Got an Active IDRP Route, Got a Damped route with 
						 * IDRP_STATUS_MIN_ADV_CHG -> changes during transition
						 * -- No Delete on route --> so announce the active route
						 * yes.. yes.. yess!!!
						 */ 

						IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_LOC_RIB);
						link_ann_list(p_irt,p_ann_list,NLRI_LINKED_P_ANN);
						trace_tf (idrp_trace_options,TR_NORMAL,0, ("idrp_min_advRD_rt(6): route %s from peer %s damping timer expired; sending update", 
											    iso_ptoa(&p_irt->nlri), p_irt->peer->name));
							
						}
					 }
				trace_tf (idrp_trace_options,TR_NORMAL,0, ("idrp_min_advRD_rt(7): route %s (%x) from peer %s ", 
					iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt,p_idrp_rt->peer->name));
				}

		  	  /* else nothing to advertise -- could happen if we've been holding on to an old w/drawn route, or if  
			   * THE ROUTE hasn't changed.
			   */

			  IDRP_STATUS_BIT_CLEAR(IDRP_DAMPED_ROUTE(p_idrp_rt),(IDRP_STATUS_MIN_ADV_CHG |IDRP_STATUS_MIN_ADV_CHG_RDS));
			  IDRP_DAMPED_ROUTE(p_idrp_rt) = NULL;
			  trace_tf (idrp_trace_options,TR_NORMAL,0, ("idrp_min_advRD_rt(8): route %s (%x) from peer %s ", 
				iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt,p_idrp_rt->peer->name));
			  }
		  else
			  {
			   /* there is no damped route
			    * -  We must be holding here for minadvtimeout 
			    */	
			   trace_tf (idrp_trace_options,TR_NORMAL,0, ("idrp_min_advRD_rt(9): route %s (%x) from peer %s - no damped route", 
				iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt,p_idrp_rt->peer->name));
			  }

			 
			}
		else
			{
			if (IDRP_DAMPED_ROUTE(p_idrp_rt) != NULL)
				{
			   	IDRP_STATUS_BIT_CLEAR(IDRP_DAMPED_ROUTE(p_idrp_rt),(IDRP_STATUS_MIN_ADV_CHG |IDRP_STATUS_MIN_ADV_CHG_RDS));
			        IDRP_DAMPED_ROUTE(p_idrp_rt) = NULL;
				}
			   trace_tf (idrp_trace_options,TR_NORMAL,0, ("idrp_min_advRD_rt(10): route %s (%x) from peer %s ", 
				iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status,p_idrp_rt->peer->name));
			}
			
#ifdef	IDRP_QOS
		   }
#endif  /* IDRP_QOS */

		}
	
	/* we're done with the damping pointer and need to clear it */


	IDRP_STATUS_BIT_TEST(p_idrp_rt, IDRP_STATUS_DELETE)
		{
		/* route has been marked for deletion, make it GONE */			 

		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DEL_SEND)
			{
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp_min_advRD_rt(12): route %s status %x from peer %s deletion timer expired; deleting route", 
				      iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 
				
			idrp_free_outlist(p_idrp_rt);
			idrp_free_nlri_att_rec(p_idrp_rt,NLRI_LINKED_P_NEXT);

			IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_LOCAL_DAMP_DEL)
				{
				/* osilocal route - Oh how I can't wait to
				 * get rid of this hack 
				 * 
				 */
				
				assert(p_idrp_rt->p_rt != NULL);
		
				p_idrp_rt->p_rt->rt_data = (void_t) NULL;
				idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);
				IDRP_MEM_FIT_FREE(p_idrp_rt);
				}
			else	
				{
			
				/* gated does not free idrp route so free it
				 */
 

				idrp_del_rt_gated(p_idrp_rt,IDRP_RESET_RT_BIT);	
				idrp_free_idrpRoute(p_idrp_rt);
				}
				
			} /* end of test for DEL_SEND that says we've sent withdraw 
			   * now delete the route
			   */

		else
			{
			/* No DEL_SEND - we've not gotten through
			 * phase 3 testing of LOC_RIB rib route
			 */

			trace_tf (idrp_trace_options, TR_MINADV,0, ("idrp_min_advRD_rt(13): route %s status %x from peer %s not deleteing DEL_SEND not set ", 
				iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 
				
			}
		/* return indication that we've modified the
		 * route - that is deleted it. 
		 */
		return(1); 
		}
	trace_tf (idrp_trace_options, TR_MINADV,0, ("idrp_min_advRD_rt(14): route %s status %x from peer %s  ", 
			iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status, p_idrp_rt->peer->name)); 

	/* well the route made it through our run 
	 * without delete
	 * return that information
	 */

	return(0);
}

void
idrp_set_minadv_timers()
{
idrpPeer	*peer;

	peer = &idrp_this_node;
	if (!idrp_set_min_route_timer())
		{
			trace_tf (idrp_trace_options, TR_MINADV,0, ("error while setting idrp set min_route advertisement timer"));
			
		}

	if (!idrp_set_min_advRD_timer())
		{
			trace_tf (idrp_trace_options, TR_MINADV,0,("error while setting idrp set min_route advertisement timer for this RD"));
		}

}


void
idrp_process_with_minadv(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
	/* logic:
	 *  if damped route:
	 *	1) set idrp status flag to indicate changes will min adver timer
	 *	   running (IDRP_STATUS_MIN_ADV_CHG)
	 *
	 *	2) test to see if damped route is this one
	 *		if not, set flag DEL_SEND 
	 *		if it is, just leave delete flag
	 *		and nlris intact.
	 *
	 *  if not damped:
	 *	1) put IDRP_STATUS_DELETE to DEL_SEND
	 *	2) if it doesn't have an IDRP_STATUS_DELETE,
	 *	   the route will remian, just out of permission for now. 
 	 *
	 */

	/* is this a damped routed?
	 */

	if (IDRP_DAMPED_ROUTE(p_idrp_rt))
		{
		/* is it damped and deleted
		 * - meaning we got a delete, withdraw
		 *   or phase3 replace for a nlri
		 *   that was being damped
		 */

		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DELETE)
			{	
			IDRP_STATUS_BIT_SET(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG);
			if (!IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
				{
				/* is the nlri the DAMPED route?
				 * - if not delete the nlri after sending
				 *   the withdraw
				 */

				IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_DEL_SEND);
				}
				
			}
		}
	else
		{
						
		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DELETE)
			{
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_DEL_SEND);
			}
		}
}


void
idrp_repl_minadv_chains(p_idrp_rt,p_idrp_rt_old)
idrpRoute	*p_idrp_rt;
idrpRoute	*p_idrp_rt_old;
{
int type;

	/* set to see that status changes 
	 */

	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_minadv_chains: new nlri: %s (%x) old nlri %s (%x)",
		iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,iso_ptoa(&p_idrp_rt_old->nlri),p_idrp_rt_old->status)); 

	IDRP_STATUS_BIT_TEST(p_idrp_rt_old,IDRP_STATUS_MIN_ADV_CHG_RDS)
		{
		/* here we have changed from local RD to remote RD to local RD
		 * or from remote to local RD to remove
	         * -during a minimum route advertisement timer			
		 */

		trace_log_tf(idrp_trace_options, 0, LOG_ERR, (" idrp_repl_minadv_chain High flux nlri %s",iso_ptoa(&p_idrp_rt->nlri)));
		}	 

	type = find_repl_minadv(p_idrp_rt,p_idrp_rt_old);	
	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_minadv_chains: %d chain exists for nlri: %s",type,iso_ptoa(&p_idrp_rt->nlri)));

	if (p_idrp_rt->peer->type == IDRP_PEER_INTERNAL &&   (idrp_my_rdi_rdpath(p_idrp_rt->p_attr)))
		{	
		IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_RD_ROUTE);
		}


	IDRP_STATUS_BIT_TEST(p_idrp_rt_old,IDRP_STATUS_RD_ROUTE)
		{
		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_minadv_chains nlri %s local RD reseting",iso_ptoa(&p_idrp_rt_old->nlri)));

		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_RD_ROUTE)
			{	
			/* must be minimum route advertisement for this
			 * RD
			 */

			trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_min_chains local RD-> local RD nlri %s",iso_ptoa(&p_idrp_rt->nlri))); 
			}
		else
			{
			trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_min_chains local RD-> remote RD nlri %s",iso_ptoa(&p_idrp_rt->nlri))); 
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_MIN_ADV_CHG_RDS);	
			}
 
		}
	else
		{	
		IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_RD_ROUTE)
			{
			/* must be minimum route advertisement for this
			 * RD
			 */

			trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_min_chains remote RD-> RD nlri %s",iso_ptoa(&p_idrp_rt->nlri))); 
			IDRP_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_MIN_ADV_CHG_RDS);	
			}
 
		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("idrp_repl_minadv_chains p_idrp_rt->p_minadv = %x minadv_back = %x",
			p_idrp_rt->p_min_adv,p_idrp_rt->p_min_adv_back));
		}

}


int repl_min_adv_head(p_idrp_rt_old,p_idrp_rt,type)
idrpRoute	*p_idrp_rt_old;
idrpRoute	*p_idrp_rt;
int		type;
{
idrpAdvRt	*p_adv;
idrpAdvRt	*p_adv_list_head;
idrpAdvRt	*p_adv_list_tail;
int		i;
int		found = FALSE;

	i = family_to_nlri_id(p_idrp_rt->family);
	if (type == IDRP_MIN_ADV_TYPE)
		{
		p_adv_list_head = idrp_this_node.p_minadv_head;
		p_adv_list_tail = idrp_this_node.p_minadv_tail;
		}
	else
		{
		p_adv_list_head = idrp_this_node.p_minadvRD_head;
		p_adv_list_tail = idrp_this_node.p_minadvRD_tail;
		}		

	IDRP_ADV_LIST_WALK(p_adv,p_adv_list_head)
		{
		if (p_idrp_rt_old == p_adv->routes[i].head)
			{
			trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("replacing p_adv %x routes[%i] head %x with %x,",p_adv,i,p_adv->routes[i].head,p_idrp_rt));
			p_adv->routes[i].head = p_idrp_rt;
			found = TRUE;	
			}
		if (p_idrp_rt_old == p_adv->routes[i].tail)
			{
			trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0,("replacing p_adv %x routes[%i] tail %x with %x,",p_adv,i,p_adv->routes[i].tail,p_idrp_rt));
			p_adv->routes[i].tail = p_idrp_rt;
			found = TRUE;
			}
		if (found)
			{
			return(TRUE);
			}
		} IDRP_ADV_LIST_WALK_END;

	return(FALSE);
} 
				

int 
idrp_minadvRD_local_init(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
{
struct	iso_net_addr	dest;	
idrp_rt_chain_walk      gate;        /* return chain for matched gateways */
idrp_rt_chain_walk      pref;        /* return chain for routes with same IDRP pref */ 
idrpRoute		*p_best_ext;	/*  best external */
rt_entry *p_rt;

       trace_tf(idrp_trace_options, TR_NORMAL,0,(" ***halt*** local route idrp_m
inadvRD_local_init routine route %s",iso_ptoa(&p_idrp_rt->nlri)));

        return(FALSE);

	p_rt = p_idrp_rt->p_rt;
	bzero(&dest,sizeof(dest));

	trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0,("local route idrp_minadvRD_local_init routine route %s",iso_ptoa(&p_idrp_rt->nlri)));
	trace_tf(idrp_trace_options, TR_NORMAL,0,(" ***halt*** local route idrp_minadvRD_local_init routine route %s",iso_ptoa(&p_idrp_rt->nlri)));

	return(FALSE);

	idrp_add_route_locate((sockaddr_un *)&dest,p_idrp_rt,&gate,&pref,&p_best_ext);

	if (p_rt != (rt_entry *) NULL && p_rt->rt_head != (rt_head *) NULL) 
		{
		trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0,("local route idrp_minadvRD_local_init: local route still has p_rt pointer %x",p_rt));

		/* skh - The removal of the damped route from the
		 * skh   the timer queue occurs at the normal min_advRD_rt call  this
		 * skh  - routine that calls idrp_minadvRD_rt
		 *      - nulled here instead of the in the idrp_minadvRD_rt processing
		 *      - which just nulls and then returns to the process_minadvRD_rt
		 * skh
		 * skh/jgs  -- of COURSE I could be hosed as well
		 */ 

		IDRP_DAMPED_ROUTE(p_idrp_rt) = (idrpRoute *) 0;
		p_idrp_rt->status = IDRP_STATUS_LOCAL_ROUTE | IDRP_STATUS_LOCAL_RD;

		if (p_rt->rt_idrp != p_idrp_rt)
			{
			idrpRoute *p_irt_old = p_rt->rt_idrp;	

			trace_log_tf (idrp_trace_options, 0, LOG_ERR, ("local route idrp_minadvRD_local_init: p_rt but p_rt(%x)->rt_idrp (%x) != p_idrp_rt(%x) ",
				p_rt,p_rt->rt_idrp,p_idrp_rt));
			idrp_repl_rt_gated(p_idrp_rt,p_rt);
			IDRP_MEM_FIT_FREE(p_irt_old);
			}

		if (p_idrp_rt->p_p_best_ext == (idrp_best_ext *) NULL )
			{
			if (insert_in_pref_order(p_idrp_rt, p_best_ext))
				{
				link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
				}
			}	
	
		idrp_mod_rt_gated(p_idrp_rt,p_idrp_rt->gated_pref);
		}
	else if (p_rt == (rt_entry *) NULL && p_rt->rt_head == (rt_head *) NULL)
		{
		idrp_add_rt_to_gated(p_idrp_rt,(sockaddr_un *) &dest);
		if (p_idrp_rt->p_p_best_ext == (struct  _idrp_best_ext  *) NULL )
			{
			if (insert_in_pref_order(p_idrp_rt, p_best_ext))
				{
				link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_ANN);
				}
			}
		else
			{
			trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("local route idrp_mindavRD_local_init nlri %s has p_p_best_ext %x",
				iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->p_p_best_ext));
			}	
		}
	else 
		{
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("local route idrp_mindavRD_local_init routine route doesn't have both p_rt %x and rt_head %x nlri %s",
				p_rt,p_rt->rt_head,iso_ptoa(&p_idrp_rt->nlri)));
		}
	return(1); 
}


int
find_repl_minadv(p_idrp_rt,p_idrp_rt_old)
idrpRoute	*p_idrp_rt;
idrpRoute	*p_idrp_rt_old;
{
int type;

	/* find the non-zero pointer */

	if (p_idrp_rt_old->p_min_adv != NULL || p_idrp_rt_old->p_min_adv_back != NULL)
		{
		type = IDRP_MIN_ADV_TYPE;
		if (p_idrp_rt_old->p_min_adv != NULL)
			{
			p_idrp_rt->p_min_adv = p_idrp_rt_old->p_min_adv;
			p_idrp_rt->p_min_adv->p_min_adv_back = p_idrp_rt;
       		        }

	 	if (p_idrp_rt_old->p_min_adv_back != NULL)
       			{
			p_idrp_rt->p_min_adv_back = p_idrp_rt_old->p_min_adv_back;
			p_idrp_rt->p_min_adv_back->p_min_adv = p_idrp_rt;
			}
	
		if (p_idrp_rt_old->p_min_advRD != (idrpRoute *) NULL)
			{
			trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("nlri %s has two min_adv chains clogged up in find_repl_minadv",iso_ptoa(&p_idrp_rt->nlri)));
			} 

		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("find_repl_chains p_idrp_rt->p_minadv = %x minadv_back = %x",
			p_idrp_rt->p_min_adv,p_idrp_rt->p_min_adv_back));

		repl_min_adv_head(p_idrp_rt_old,p_idrp_rt,type);
		}
	else
		{		
		type = IDRP_MIN_ADVRD_TYPE;
		if (p_idrp_rt_old->p_min_advRD != NULL)
			{
			p_idrp_rt->p_min_advRD = p_idrp_rt_old->p_min_advRD;
			p_idrp_rt->p_min_advRD->p_min_advRD_back = p_idrp_rt;
       		        }

	 	if (p_idrp_rt_old->p_min_advRD_back != NULL)
       			{
			p_idrp_rt->p_min_advRD_back = p_idrp_rt_old->p_min_advRD_back;
			p_idrp_rt->p_min_advRD_back->p_min_advRD = p_idrp_rt;
			}
		if (p_idrp_rt_old->p_min_adv != (idrpRoute *) NULL)
			{
			trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("nlri %s has two min_adv chains clogged up in find_repl_minadv",iso_ptoa(&p_idrp_rt->nlri)));
			} 


		trace_tf(idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("find_repl_minadv_chains p_idrp_rt->p_minadvRD = %x minadvRD_back = %x",
			p_idrp_rt->p_min_advRD,p_idrp_rt->p_min_advRD_back));

		repl_min_adv_head(p_idrp_rt_old,p_idrp_rt,type);
		}
	return(type);				
}		

#ifdef	IDRP_QOS

void
qos_min_adv_rt(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;	
{

   /* if there is an active route at all  */ 

    IS_IDRP_QOS_ACTIVE_ROUTE(p_idrp_rt)
	{
	if (ACTIVE_QOS_IDRP_ROUTE(p_idrp_rt))
		{
		IDRP_STATUS_BIT_TEST(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG)
			{
			IDRP_STATUS_BIT_TEST(ACTIVE_IDRP_ROUTE(p_idrp_rt),IDRP_STATUS_DELETE)
				{
				trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0,
					  ("qos_min_adv_rt() error: route %s from peer %s damping timer expired: delete on active route",
							iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name));
				}	
			else
				{
				/* idrp active route - send it
				 */

				IDRP_STATUS_BIT_SET(ACTIVE_QOS_IDRP_ROUTE(p_idrp_rt),IDRP_STATUS_LOC_RIB);	
				link_ann_list(ACTIVE_QOS_IDRP_ROUTE(p_idrp_rt),p_ann_list,NLRI_LINKED_P_ANN);
						
				trace_tf (idrp_trace_options, TR_MINADV,0,
					  ("qos_min_adv_rt(): route %s from peer %s damping timer expired; sending update", 
							iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->peer->name));
						
				}
			}
		}
	}
}
	
void
qos_min_advRD_rt(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;	
{

	/* check for active route
	 */

	IS_IDRP_QOS_ACTIVE_ROUTE(p_idrp_rt) /* if there is an active route at all */
		{
		if (ACTIVE_QOS_IDRP_ROUTE(p_idrp_rt))
			{
			IDRP_STATUS_BIT_TEST(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG)
				{
				idrpRoute *p_irt = ACTIVE_QOS_IDRP_ROUTE(p_idrp_rt);	

				IDRP_STATUS_BIT_TEST(p_irt,IDRP_STATUS_DELETE)
					{
					trace_tf (idrp_trace_options, TR_NORMAL|TR_TIMER,0, ("qos_min_advRD_rt(): route %s (%x,%x)from peer %s damping timer expired; not sending update delete flag on active route", 
						iso_ptoa(&p_irt->nlri),p_irt->status,p_irt->qos_status,p_irt->peer->name));
					}
				else
					{
					IDRP_STATUS_BIT_SET(p_irt,IDRP_STATUS_LOC_RIB);
					link_ann_list(p_irt,p_ann_list,NLRI_LINKED_P_ANN);
					trace_tf (idrp_trace_options,TR_NORMAL,0, ("qos_min_advRD_rt(): route %s (%x, %x) from peer %s damping timer expired; sending update", 
							    iso_ptoa(&p_irt->nlri), p_irt->status, p_irt->qos_status,p_irt->peer->name));
					}
				 IDRP_STATUS_BIT_CLEAR(IDRP_DAMPED_ROUTE(p_idrp_rt),IDRP_STATUS_MIN_ADV_CHG);
				 }
			trace_tf (idrp_trace_options,TR_NORMAL,0, ("qos_min_advRD_rt(7): route %s (%x)(%x,%x) from peer %s ", 
					iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt,p_idrp_rt->status,p_idrp_rt->qos_status,p_idrp_rt->peer->name));
			}
		/* else nothing to advertise -- could happen if we've been holding on to an old w/drawn route, or if  
		 * THE ROUTE hasn't changed. */
		}
}
#endif	/* IDRP_QOS */


void
idrp_min_adv_cleanup(peer)
idrpPeer	*peer;
{
int changes = 0;
	/* clean up both within RD 
	 * and between RD minimum route advertisement 
 	 * list
	 */
	rt_open(idrp_this_node.task);
	changes +=min_adv_list_clean(peer,NLRI_LINKED_P_MINADV);
	changes +=min_adv_list_clean(peer,NLRI_LINKED_P_MIN_ADV_RD);
	rt_close(idrp_this_node.task,&idrp_this_node.gw,changes,NULL);
}	
 
int
min_adv_list_clean(peer,type)
idrpPeer *peer;
int type;
{ 
idrpAdvRt *p_adv,*p_adv_head;
idrpRoute *p_irt;
idrpRoute *p_irt_next;
int i;
int changes = 0;  
  if (type == NLRI_LINKED_P_MINADV)
    p_adv_head = idrp_this_node.p_minadv_head;
  else if (type == NLRI_LINKED_P_MIN_ADV_RD)
    p_adv_head = idrp_this_node.p_minadvRD_head;
  
  IDRP_ADV_LIST_WALK(p_adv,p_adv_head)
    {
    NLRI_FAMILY_LOOP(i)
      {
      for (p_irt = p_adv->routes[i].head; p_irt; p_irt = p_irt_next)
	{
	if (type == NLRI_LINKED_P_MINADV)
	  {
	   p_irt_next = p_irt->p_min_adv;
          }
        else
          {
          p_irt_next = p_irt->p_min_advRD;
          }

	/* see if this route belongs to a 
	 * route for a peer that is being delted
	 */	

	if (p_irt->peer == peer)
	  {
	  /* deleting route from peer going away:
	   *    1) relink p_min_adv
	   *   2) free the idrp route
	   */  
	  /* relink the list
	     */
	  if (type == NLRI_LINKED_P_MINADV)
	    {
	    relink_min_adv_list(p_irt,type,p_adv,i);
	    }
	  else
	    {
	    relink_min_advRD_list(p_irt,type,p_adv,i);
	    }
	  changes++;
	  idrp_free_outlist(p_irt);
	  idrp_free_nlri_att_rec(p_irt,NLRI_LINKED_P_NEXT);
	  idrp_del_rt_gated(p_irt,IDRP_RESET_RT_BIT);
	  idrp_free_idrpRoute(p_irt);
	  }
	}

      /* set flag true if the 
       * heads stil there
       */
 
      } NLRI_FAMILY_LOOP_END;
    }IDRP_ADV_LIST_WALK_END;

   /* get rid of any empty 
    * 
    */
  if (type == NLRI_LINKED_P_MINADV)
	min_adv_empty_cleanup();
  else if (type == NLRI_LINKED_P_MIN_ADV_RD)
	min_advRD_empty_cleanup();

    return(changes);	

}

/* delete empty segments from the min_adv list
 */
 

void  
min_adv_empty_cleanup()
{
idrpAdvRt	*p_adv;
idrpAdvRt	 *p_adv_next = NULL;
idrpAdvRt	 *p_adv_last = NULL;
int		full;
int		i;

  /* walk through the minimum advertisement
   * list looking for empties
   * - delete the empties
   */

  for (p_adv = idrp_this_node.p_minadv_head; p_adv; p_adv = p_adv_next)
	{
	p_adv_next = p_adv->p_next;
	full = FALSE;
	NLRI_FAMILY_LOOP(i)
	  {
	  if (p_adv->routes[i].head != NULL)
		{
		full = TRUE;
		}
	  } NLRI_FAMILY_LOOP_END;

	if (full)
	    {
	    p_adv_last = p_adv;
	    }
	else
	    {
            if (p_adv == idrp_this_node.p_minadv_head)
		{
		idrp_this_node.p_minadv_head = p_adv->p_next; 
		}
	    else
		{
		p_adv_last->p_next = p_adv->p_next;
		}
	    if (p_adv == idrp_this_node.p_minadv_tail)
		{
		idrp_this_node.p_minadv_tail = p_adv_last;
		}
	    }
	}	 
}

/* cleanup any empty p_adv entries in the
 *  Minimum route advertisement queue for 
 *  this node
 */ 

void  
min_advRD_empty_cleanup()
{
idrpAdvRt	*p_adv, *p_adv_next;
idrpAdvRt	*p_adv_last = NULL;
int		full;
int		i;

  /* walk through the minimum advertisement
   * list looking for empties
   * - delete the empties
   */

  for(p_adv = idrp_this_node.p_minadvRD_head; p_adv; p_adv = p_adv_next)
	{
	p_adv_next = p_adv->p_next;
	full = FALSE;
	NLRI_FAMILY_LOOP(i)
	  {
	  if (p_adv->routes[i].head != NULL)
		{
		full = TRUE;
		}
	  } NLRI_FAMILY_LOOP_END;

	if (full)
	    {
	    p_adv_last = p_adv;
	    }
	else
	    {
            if (p_adv == idrp_this_node.p_minadvRD_head)
		{
		idrp_this_node.p_minadvRD_head = p_adv->p_next; 
		}
	    else
		{
		p_adv_last->p_next = p_adv->p_next;
		}
	    if (p_adv == idrp_this_node.p_minadvRD_tail)
		{
		idrp_this_node.p_minadvRD_tail = p_adv_last;
		}
	    }
	}	 
}

void
relink_min_adv_list(p_irt,type,p_adv,nlri_type)
idrpRoute *p_irt;
int       type;
idrpAdvRt *p_adv;
int        nlri_type;
{

  /* does it equal the head of the list
   */

  if (p_irt == p_adv->routes[nlri_type].head)
    {
    p_adv->routes[nlri_type].head = p_irt->p_min_adv;
    }

  if (p_irt == p_adv->routes[nlri_type].tail)
    {
    p_adv->routes[nlri_type].tail = p_irt->p_min_adv_back;
    }

  /* relink around the idrpRoute
   */

  if (p_irt->p_min_adv_back)
    {
    p_irt->p_min_adv_back->p_min_adv = p_irt->p_min_adv;
    }
  if (p_irt->p_min_adv)
    {
    p_irt->p_min_adv->p_min_adv_back = p_irt->p_min_adv_back;
    }
}
 
void
relink_min_advRD_list(p_irt,type,p_adv,nlri_type)
idrpRoute *p_irt;
int       type;
idrpAdvRt *p_adv;
int        nlri_type;
{

  /* does it equal the head of the list
   */

  if (p_irt == p_adv->routes[nlri_type].head)
    {
    p_adv->routes[nlri_type].head = p_irt->p_min_advRD;
    }

  if (p_irt == p_adv->routes[nlri_type].tail)
    {
    p_adv->routes[nlri_type].tail = p_irt->p_min_advRD_back;
    }

  /* relink around the idrpRoute
   */

  if (p_irt->p_min_advRD_back)
    {
    p_irt->p_min_advRD_back->p_min_advRD = p_irt->p_min_advRD;
    }
  if (p_irt->p_min_advRD)
    {
    p_irt->p_min_advRD->p_min_advRD_back = p_irt->p_min_advRD_back;
    }
}
 
