/* 
 * $Id: idrp_rt_gated.c,v 1.12 1995/10/31 17:13:40 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

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

/*  idrp_rt_gated.c
 *    
 * %% gated interaction routines
 * 
 * - mediate_pref_match - modify the gated preference match as phase2 desires
 * - idrp_add_rt_to_gated - add a route to gated tables
 * - idrp_repl_rt_gated - replace an idrpRoute structure in gated table
 *                      - call the rt_change routine to handle changes if needed
 * - idrp_mod_rt_gated - modify gated routing table
 * - find_idrp_gated_rt - find from an external peer the idrp
 * 			  route 
*/ 

/* %% gated related routines  */


/* mediate_pref_match - modify gated preference so that  
 * 		    gated can pick a route within idrp routes
 *		    and external route.  idrp routes 
 *		    have constant + idrp_pref *2.  add or
 *		    zero route to select a single idrp route 
 */ 


int
mediate_pref_match(p_pref,p_irt)
idrp_rt_chain_walk	*p_pref;
idrpRoute		*p_irt;
{
idrp_rt_chain_walk	*p_ch;
idrpRoute		*p_best_rt;
idrpRoute		*p_irt_new;
int			idrp_best_rt = 0;
int			multi_idrp_best_rt = 0;	
u_int			proto;
u_int			tie_result;		
idrpPeer		*p_trace_peer;

	p_trace_peer = &idrp_this_node;
	/* mediate gated preference matches
	 * the idrp route past matches the list of gated routes
	 * - Dispatch on protocol 
	 *   if IDRP vs IDRP route -
	 *		use method from standard
	 *		of comparing routes
	 * 
	 *  return: TRUE if idrp route is best
	 *          FALSE if idrp route is not the best 
         * 
	 *  if IDRP vs another protocol -
	 *  
	 * 
	 *   prefer an interior protocol over BGP or IDRP
	 *
	 */

	p_best_rt = p_irt; 
		
	IDRP_WALK_RT_CHAIN(p_pref,p_ch)
		{ 
		if (p_ch->p_rt->rt_gwp->gw_proto == RTPROTO_IDRP)
			{
			/* count how many idrp routes preference match */
			
			multi_idrp_best_rt++;

			/* idrp tie breaking - 
			 *  use idrp tie breaking methods specified in
			 *  ISO or idrp for IP specification 
			 */
		
			trace_tf(idrp_trace_options, TR_NORMAL,0, ("IDRP tie breaking multiple preference matches route from peer %s route %s",
				p_irt->peer->name,iso_ptoa(&p_irt->nlri)));
 
			p_irt_new = (idrpRoute *) p_ch->p_rt->rt_idrp; 
			switch	(tie_break(p_best_rt,p_irt_new)) 
				{
				case PFX_ADDR_EQUAL:
					/* equal routes warn and use 2nd route idrp route */
			
					trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("2 routes, different peers same NET?? - oh no! peer %s, peer %s, route %s",
						p_best_rt->peer->name,p_irt_new->peer->name, iso_ptoa(&p_irt->nlri)));
					p_best_rt = p_irt_new;
					break;

				case PFX_ADDR_LESS_THAN:
					/* 1st route better than 2nd */
					break;
			
				case PFX_ADDR_GREATER_THAN:
					/* 1st prefix  greatern than 2nd
					 * therefore  best route is
					 * new idrp route 
					 */
					p_best_rt = p_irt_new; 
					break; 
				}
			}
		if (p_ch->p_rt->rt_gwp->gw_proto != RTPROTO_IDRP)
			{
			if (!idrp_proto_pref(p_ch->p_rt->rt_gwp->gw_proto,p_irt->peer))
				{
				/* other protocol prefered to idrp - return false */
	
				return (FALSE);
				}
			}
	
		} IDRP_WALK_RT_CHAIN_END


		trace_tf (idrp_trace_options, TR_NORMAL,0, (" peer %s route %s with %d idrp preference matches best pref from peer = %s", 
			p_irt->peer->name,iso_ptoa(&p_irt->nlri),multi_idrp_best_rt,p_best_rt->peer->name));


	if (p_best_rt == p_irt)
		{
		return (TRUE);
		}	
	return (FALSE);
}


int
idrp_add_rt_to_gated(p_idrp_rt,p_dest)
idrpRoute *p_idrp_rt;
sockaddr_un	*p_dest;		    /* destination address for gated Route search */
{
rt_parms	rt_parameters;			/* rt parms for gated add */
sockaddr_un	*p_gw;				/* pointer to modified gateway for route */
						/* takes off NSEL off router address */

if_addr		*p_subnet_test = (if_addr *) NULL;			/* subnet test returns */

	/* brand new  route 
	 * add example for gated
	 */ 

	bzero ((caddr_t) &rt_parameters,sizeof(rt_parameters));  /* zero params */

	/* set preference in new route to IDRP generic preference
	 * + 2 times local preference + no tie break (0)  
	 * - tie break allows idrp to mediate ties 
	 *   per idrp specification 
	 */ 

/* here we need to fix things to include - rt_data as the beginning of idrpRoute 
 * we need to add in a dump routine and a free routine if possible
 */ 
 
 	rt_parameters.rtp_rtd = (rt_entry *) p_idrp_rt; 
	rt_parameters.rtp_rtd = (void_t) &p_idrp_rt->p_rt;
	rt_parameters.rtp_preference = idrp_to_gated_pref(p_idrp_rt);
	rt_parameters.rtp_dest = p_dest;
	rt_parameters.rtp_idrp = (idrpRoute *) p_idrp_rt;
	
	rt_parameters.rtp_n_gw = 1;

/* set the bit for the idrp route added to gated */

	switch (p_idrp_rt->family)
		{
		case AF_ISO:
	 	  rt_parameters.rtp_dest_mask = iso_mask_prefix(p_idrp_rt->nlri.pfx_len);
		  if (p_idrp_rt->p_loc_rt_peer != NULL)
		    {
		      rt_parameters.rtp_routers[0] = sockdup(p_idrp_rt->p_loc_rt_peer);
		    }	
		  else
		    {
			/* if the next hop is in the packet
			 * and it is if iso
			 * and if we do not expand the next hop - 
			 * - check to see that it is on my subnet
			 */

			if (p_idrp_rt->p_attr->next_hop)
				{
				p_subnet_test = if_iso_withsubnet((sockaddr_un *) (p_idrp_rt->p_attr->next_hop));

				trace_tf (idrp_trace_options,TR_NORMAL,0,
					("nexthop checks p_attr = %x, next_hop = %x, p_subnet = %x",
                                        p_idrp_rt->p_attr, p_idrp_rt->p_attr->next_hop,p_subnet_test));
				}
			else
				{
				p_subnet_test = NULL;
				} 
					    
			if ((p_idrp_rt->p_attr->next_hop != (sockaddr_un *) 0) && 
				(socktype(p_idrp_rt->p_attr->next_hop) == AF_ISO) &&
				((IDRP_LOCAL_CNF_TEST(IDRP_CNF_OPT_EXPAND_NEXT_HOP) == (u_int) 0) 
				||(p_subnet_test != (if_addr *) NULL))) 
				{
				rt_parameters.rtp_routers[0] = sockdup(p_idrp_rt->p_attr->next_hop);
				}
			else
		     		{ 
				/* sockaddr_un for remote peer must
			         * terminate the NSEL - a debate on what an
			         * NET really is.  
			         * Re-examine after EMAIL to cisco, IBM,
			         * proteon and 3COM 
			         * copy in the full NET for now
			         */

				struct	iso_net_addr	gw;

			      	gw.isoa_len =  p_idrp_rt->peer->neighbor.isoa_len - 1;
			      
				bcopy(&p_idrp_rt->peer->neighbor.isoa_genaddr,&gw.isoa_genaddr,gw.isoa_len);
				gw.isoa_family = AF_ISO; 

				/* add two for the iso structure for sockaddr_un 
				 *
				 */ 

				gw.isoa_len +=2;
				rt_parameters.rtp_routers[0] = sockdup((sockaddr_un *) &gw);

			        }
		    }
		
		if (p_idrp_rt->p_loc_intf == NULL)
			{		
		  	rt_parameters.rtp_gwp = &p_idrp_rt->peer->iso_gw;
			}
		else
			{
		  	rt_parameters.rtp_gwp = p_idrp_rt->p_loc_intf;
			}
		  break;	


		case AF_INET:
		
		  rt_parameters.rtp_dest_mask = inet_mask_prefix(p_idrp_rt->nlri.pfx_len);
		  if (p_idrp_rt->p_loc_rt_peer)
		    	{
			/* local peer - so use address within system */

			rt_parameters.rtp_routers[0] = sockdup(p_idrp_rt->p_loc_rt_peer);
		    	}
		  else
		    	{
		
			if (p_idrp_rt->p_attr->next_hop)
				{
				p_subnet_test = if_withsubnet((sockaddr_un *) (p_idrp_rt->p_attr->next_hop));
                                trace_tf(idrp_trace_options, TR_NORMAL,0,(" nexthop checks p_attr = %x,
					 next_hop = %x, p_subnet = %x", p_idrp_rt->p_attr,
					 p_idrp_rt->p_attr->next_hop,p_subnet_test));
                                }
			else
				{
				p_subnet_test = NULL;
				}

			if ((p_idrp_rt->p_attr->next_hop != (sockaddr_un *) 0) && 
				(socktype(p_idrp_rt->p_attr->next_hop) == AF_INET) &&
				((IDRP_LOCAL_CNF_TEST(IDRP_CNF_OPT_EXPAND_NEXT_HOP) == (u_int) NULL) || 
				(p_subnet_test != (if_addr *)NULL)))	
				{
				rt_parameters.rtp_routers[0] = sockdup((sockaddr_un *) p_idrp_rt->p_attr->next_hop);
				}
			else
				{
				/* if not local address - try the IP neighbor address 
				 */

			        rt_parameters.rtp_routers[0] = sockdup((sockaddr_un *) p_idrp_rt->peer->ip_neighbor);
				}
		    	}
		  rt_parameters.rtp_gwp = &p_idrp_rt->peer->gw;
		  break;

		default:
			trace_log_tf(idrp_trace_options, 0, LOG_WARNING, 
				("idrp_add_rt_to_gated(): WARNING - pdu with unknown address family %d", 
				p_idrp_rt->family));
			break;

		}
	trace_tf(idrp_trace_options, TR_NORMAL,0,(" sockdup nexthop checks p_attr = %x, next_hop = %x, p_subnet_test = %x",p_idrp_rt->p_attr,p_idrp_rt->p_attr->next_hop,p_subnet_test));
 
        /* set protocol metric to calculated metric for idrp
	 * local routes
	 */

	rt_parameters.rtp_metric = p_idrp_rt->pref_cal;


	/* set the no-age flag so that the local
	 * routes do not time out.
	 */

	rt_parameters.rtp_state = RTS_EXTERIOR; 

#ifdef	IDRP_QOS

	/* if the route is an internally generated QOS route
	 * or a route that is QOS and from another IDRP route
	 * - don't let it be installed	
	 */

	if (p_idrp_rt->p_qos_def || (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT))
		{
	 	rt_parameters.rtp_state |= (RTS_NOTINSTALL);
		if (p_idrp_rt->peer->type == IDRP_PEER_EXTERNAL || p_idrp_rt->peer->type == IDRP_PEER_INTERNAL)
			{
			p_idrp_rt->qos_status |= IDRP_STATUS_QOS_NEW;
			} 
		}


#endif /* IDRP_QOS */

#ifndef KERNEL_SUPPORTS_ISO
       if (p_idrp_rt->family == AF_ISO) {
	 trace_log_tf(idrp_trace_options, 0, LOG_WARNING, ("WARNING - attempt to add iso route -- not supported!"));
	 rt_parameters.rtp_state |= (RTS_NOADVISE | RTS_NOTINSTALL);
       }
#endif /* KERNEL_SUPPORTS_ISO */

	p_idrp_rt->p_rt = rt_add(&rt_parameters);

	if (p_idrp_rt->p_rt)
		{
		/* set the bit to indicate we are announcing this 
		 * - we will set this bit and see what
	 	 *   happens - at least we won't get the
		 *   null delete any more.
		 * - but then again, we may break the gated
		 *   state machine.   Sigh... life is
		 *   so much fun when the beat of gated
		 *   is a fleeting whisper in the wind, wafting
		 *   past our ears. 
		 *  
		 */

		idrp_pref(p_idrp_rt);	
		return(TRUE);
		}
	else
		{
		return(FALSE);
		}
}

/*
 *  idrp_repl_rt_gated:
 *  
 * Replace the idrp route in the gated structure
 * destination has been replaced. 
 * 
 *
 */
idrpRoute *
idrp_repl_rt_gated(p_idrp_rt,p_rt)
idrpRoute	*p_idrp_rt;
rt_entry	*p_rt;
{
idrpRoute	*p_idrp_rt_old;
int		rib_id = 0;

	/* The idrp route structure is pointed to by rt_idrp & the rt_data in the
	 * gated rt_entry
	 * We need to pull out the rt_data links  from the old idrp route
	 * and put in the new idrp route.  I think it is
	 * as simple as that.
	 *
	 * The steps in the process are as follows:
	 * 1.) grab a copy of the pointer to idrpRoute structure from the route
	 * 2.) copy over the header from the old idrp route
	 * 3.) set the new idrpRoute structure at rt_entry->rt_data 
 	 * 4.) return the copy of the old idrp structure  
	 */


	/* grab pointer */

	p_idrp_rt_old = (idrpRoute *) p_rt->rt_idrp; 
	idrp_del_best_ext(p_idrp_rt_old);


	/* relink the outbound route lists to replace this one
	 * may need to put this routine in-line
	 */

	idrp_relink_repl_outlist(p_idrp_rt,p_idrp_rt_old);
	
	/* copy the old rt_entry pointer */
 
	p_idrp_rt->p_rt = p_rt;

	/* set this data pointer to point to idrp route
	 * with gated route
	 * and reset the idrp route pointer
         */

	p_rt->rt_data = (void_t) p_idrp_rt; 
	p_rt->rt_idrp = p_idrp_rt;

	/* copy over old policy so we know if this peer needs
	 * to be reset for the no announce to announce shift
	 */

	p_idrp_rt->pref_cal = p_idrp_rt_old->pref_cal;
	p_idrp_rt->gated_pref = p_idrp_rt_old->gated_pref;  

	/* if this route is being used as a damping placeholder, we also have
	 * to re-link the min_adv chain(s)
	 */

	if (IS_IDRP_DAMPED_ROUTE(p_idrp_rt_old))
		{
		/* replace the old route with the new one in the damping 
 		 * chain(s)
		 */

		trace_tf(idrp_trace_options, TR_NORMAL,0, ("replacing damped route old nlri %s status %x ",
			iso_ptoa(&p_idrp_rt_old->nlri),p_idrp_rt_old->status));
		idrp_repl_minadv_chains(p_idrp_rt,p_idrp_rt_old);

		/* point the damping pointer to the new route */

		IDRP_DAMPED_ROUTE(p_idrp_rt) = p_idrp_rt;

		/* copy over relevant status flags from old route 
		 * skh - watch for more bits needed
		 */
		
		IDRP_STATUS_BIT_SET(p_idrp_rt,(p_idrp_rt_old->status & (IDRP_STATUS_MIN_ADV_CHG | IDRP_STATUS_INT_MASK)));
		}
	else
		{
		IDRP_STATUS_BIT_SET(p_idrp_rt,(p_idrp_rt_old->status & (IDRP_STATUS_INT_MASK)));
		}

#ifdef	IDRP_QOS

	if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
		{
		p_idrp_rt->qos_status =p_idrp_rt_old->qos_status;
		IDRP_QOS_STATUS_BIT_TEST(p_idrp_rt,(IDRP_STATUS_QOS_ACTIVE|IDRP_STATUS_QOS_LAST_ACTIVE))
			{
			idrp_qos_status_repl(p_idrp_rt,p_idrp_rt_old);
			}
		}	
	
#endif  /* IDRP_QOS */		 

	return (p_idrp_rt_old);
}


int 
idrp_mod_rt_gated(p_idrp_rt,pref)
idrpRoute *p_idrp_rt;
int		pref;
{
metric_t	metric;
metric_t	tag = 0;
sockaddr_un	*gw;
pref_t gated_pref;
int      idrp_pref;

	if (pref)
		{
		/* run the preference metric if the flag is on
		 */
       
		metric = (metric_t) PREF(p_idrp_rt,&idrp_pref,&gated_pref);
		}

       if (p_idrp_rt->pref_cal != idrp_pref || p_idrp_rt->gated_pref != gated_pref)
	 {
      	  trace_tf (idrp_trace_options, TR_NORMAL, 0, (" idrp_mod_gated peer %s route %s change gated table pref(%d,%d) gated_pref (%d,%d", 
		p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->pref_cal,idrp_pref, p_idrp_rt->gated_pref,gated_pref)); 
         p_idrp_rt->pref_cal = idrp_pref;
         p_idrp_rt->gated_pref = gated_pref;
	 }

	/* tag set to zero until we figure out from dennis what it is */

	if ((p_idrp_rt->status & (IDRP_STATUS_DEL_SEND | IDRP_STATUS_DELETE)) == IDRP_STATUS_DELETE)
		{
		/* only delete set and not DEL_SEND - the
		 * try to kludge the change
		 */
		
	       if (p_idrp_rt->p_attr->next_hop)
		{
		gw = p_idrp_rt->p_attr->next_hop;
		}

	       if (p_idrp_rt->p_attr->next_hop == NULL || 
		(socktype(gw) !=  p_idrp_rt->family)) 	
		   {
		   switch(p_idrp_rt->family)
			{
			case AF_ISO:
				gw = p_idrp_rt->peer->iso_gw.gw_addr;
				break;

			case AF_INET:
				gw = p_idrp_rt->peer->gw.gw_addr;
				break;
					
			}
		   }
		
		metric = 255;
		if (!rt_change(p_idrp_rt->p_rt,metric,0,tag,pref,0, 1,&gw))
			{
			trace_tf (idrp_trace_options, TR_NORMAL, 0, (" peer %s route %s tried to delete-change (255) gated table but could  not", 
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri))); 
			return(FALSE);
			}
		
		return(TRUE);
		}
	
	if (p_idrp_rt->p_attr->next_hop)
		{
		gw = p_idrp_rt->p_attr->next_hop;
		}

	if (p_idrp_rt->p_attr->next_hop == NULL || 
		(socktype(gw) !=  p_idrp_rt->family)) 	
		{
		switch(p_idrp_rt->family)
			{
			case AF_ISO:
				gw = p_idrp_rt->peer->iso_gw.gw_addr;
				break;

			case AF_INET:
				gw = p_idrp_rt->peer->gw.gw_addr;
				break;
			}
		}

	if (!rt_change(p_idrp_rt->p_rt,metric,0, tag,p_idrp_rt->gated_pref,0, 1,&gw))
		{
		trace_tf (idrp_trace_options, TR_NORMAL, 0, (" peer %s route %s tried to change gated table but could  not", 
			p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri))); 
		return (FALSE);
		}

#ifdef	IDRP_QOS	

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

	return (TRUE);
}

/*
 * idrp_del_rt_gated(idrpRoute *p_idrp_rt,reset bit)
 *
 * Delete the gated route (rt_entry) associated with the idrpRoute pointed to by
 * p_idrp_rt.
 */
idrp_del_rt_gated(p_idrp_rt,reset_rt_bit)
idrpRoute *p_idrp_rt;
int	  reset_rt_bit;	/* TRUE if the rt_bit should be reset, else FALSE 
			 * We want this FALSE if we want to keep the route around 'cuz
			 * it's holding a minadv timer */
{
rt_entry	*p_rt;

	p_rt = p_idrp_rt->p_rt;

	/* once we get here - we best not be announcing the route
	 * so clear the gated bit
	 */ 

	if (!p_rt)
		{
		trace_log_tf(idrp_trace_options, 0, LOG_WARNING, (" Withdraw of idrp route without gated rt peer %s route %s", 
		p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		return;
		} 	

	if (p_rt->rt_head == (rt_head *) NULL)
		{
		trace_log_tf(idrp_trace_options, 0, LOG_WARNING, (" Withdraw of idrp route without gated rt->head peer %s route %s", 
		p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));
		return;
		}	

#ifdef	IDRP_QOS

	/* check to see if the route is a QOS 
	 * -> then perform a gated-ism  
	 * 1) reset the qos active 
	 * 2) set a new active  
	 * 3)
	 */ 

	if (p_idrp_rt->p_attr->rib_id > RIB_ID_DEFAULT)
		{
		/* Rib ID points to QOS*/

		idrp_qos_gated_del(p_idrp_rt,p_rt,reset_rt_bit);		
		return;
		}
	
#endif /* IDRP_QOS */	


	/* deleting route - get rid of best external
	 * -  with or with RESET_RT_BIT , the structures should  disappear
	 * - if called already, it 
	 * finds a null p_p_best_ext in idrpRoute and will ignore
	 */ 

	idrp_del_best_ext(p_idrp_rt);


	/* if reset_rt_bit is FALSE, we want to save this route for later */

	if (reset_rt_bit == IDRP_RESET_RT_BIT)
		{

		/* we can get rid of this route
		 * I'm not sure about gated
		 * getting rid of the IDRP route
		 * - so I've nulled out my pointers
		 * and then I hand gated the 
		 * rt_entry
		 */

	       	/* case where gated has already deleted static route, and IDRP just needs to
		 * release it's bits. The gated reset code will remove the gated route
		 * IS_IDRP - idrp route received on the wire
		 * IS_LOCAL_IDRP - route gotten from other protocol 
	         */

		if (IS_AN_IDRP(p_rt) || IS_LOCAL_IDRP(p_rt))
			{
			p_rt->rt_data = (void_t)  0;
			p_rt->rt_idrp = (idrpRoute *) 0;
		  	if (rtbit_isset(p_rt,idrp_this_node.task->task_rtbit))  
		               	{
				trace_tf(idrp_trace_options, TR_NORMAL, 0,(" rtbit set idrp route peer %s route %s status %x rts_state %x", 
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_rt->rt_state));
				if ((p_rt->rt_state & RTS_RELEASE) == 0)
					{
					/* if we touch it when it is release -
					 * everything blows up
					 * - Delete is just not good enough
					 */

					rtbit_reset(p_rt,idrp_this_node.task->task_rtbit);
					}
				}

			if (((p_rt->rt_state & RTS_DELETE) != 0) || p_rt->rt_state == 0 ) 
				{
				trace_tf(idrp_trace_options, TR_NORMAL, 0,(" delete set at drp_rt_del_gated  idrp route peer %s route %s status %x <%x>", 
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_rt->rt_state));
				
				return;
				}
			else
				{
				trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("idrp_rt_gated reset_rt_bit true for %s <%x><%x>",
	 			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_rt->rt_state));
				rt_delete(p_rt); 
				}

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

		}
	else
		{
		/* hack here - delete so we can change
		 * the status of the active route
		 * but  don't let gated get
		 * to my idrp structures,
		 * I need them for the min advertisement code
		 */

		if (IS_AN_IDRP(p_rt) || IS_LOCAL_IDRP(p_rt))
			{
			if ((p_rt->rt_state & RTS_DELETE) == 0)
				{
				rt_delete(p_rt);
				}
			}
		else
			{
			trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("SUE's hack point route %s <%x><%x>", iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_rt->rt_state));
			}
	
		}
}

#ifdef	IDRP_QOS

void
idrp_qos_gated_del(p_idrp_rt,p_rt,reset_rt_bit)
idrpRoute	*p_idrp_rt;
rt_entry	*p_rt; 
int	  reset_rt_bit;	/* TRUE if the rt_bit should be reset, else FALSE 
			 * We want this FALSE if we want to keep the route around 'cuz
			 * it's holding a minadv timer */
{
int	rib_id = p_idrp_rt->p_attr->rib_id;
	/* 
	 * QOS specific handling
	 * 1) change the active to inactive
         */
	 
	trace_tf(idrp_trace_options, TR_NORMAL, 0,(" idrp_qos_gated_del peer=%s route %s status %x (%x) <%x>", 
	     p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt->qos_status,p_rt->rt_state));

        IDRP_QOS_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_QOS_ACTIVE)
                {
                /* active route -
                 *  move the route to last active
                 */

		idrpRoute *p_irt;

                p_idrp_rt->p_rt->rt_head->p_qos_lastactive[rib_id] = p_idrp_rt;
                p_irt = p_idrp_rt->p_rt->rt_head->p_qos_active[rib_id] = find_next_qos_active(p_idrp_rt);

		if (p_irt)
		    {
		    IDRP_QOS_STATUS_BIT_SET(p_irt,IDRP_STATUS_QOS_ACTIVE);
		    IDRP_QOS_STATUS_BIT_CLEAR(p_irt,IDRP_STATUS_QOS_LAST_ACTIVE);
		    }
                IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_QOS_LAST_ACTIVE|IDRP_STATUS_QOS_CHG | IDRP_STATUS_QOS_DEL));
                IDRP_QOS_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_QOS_ACTIVE);
                }
	 else
                {
	   IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_QOS_DEL);
	        }
    

	/* deleting qos route - get rid of best external
	 * -  with or with RESET_RT_BIT , the structures should  disappear
	 * - if called already, it 
	 * finds a null p_p_best_ext in idrpRoute and will ignore
	 */ 

	idrp_del_best_ext(p_idrp_rt);


	/* if reset_rt_bit is FALSE, we want to save this route for later */

	if (reset_rt_bit == IDRP_RESET_RT_BIT)
		{

		/* we can get rid of this route
		 * I'm not sure about gated
		 * getting rid of the IDRP route
		 * - so I've nulled out my pointers
		 * and then I hand gated the 
		 * rt_entry
		 */

	       	/* case where gated has already deleted static route, and IDRP just needs to
		 * release it's bits. The gated reset code will remove the gated route
	         */

		IS_IDRP(p_rt)
			{
			p_rt->rt_data = (void_t)  0;

			p_rt->rt_idrp = (idrpRoute *) 0;
		  	if (rtbit_isset(p_rt,idrp_this_node.task->task_rtbit))  
		               	{
				if ((p_rt->rt_state & RTS_RELEASE) == 0)
					{
					/* if we touch it when it is release -
					 * everything blows up
					 * - Delete is just not good enough
					 */

					rtbit_reset(p_rt,idrp_this_node.task->task_rtbit);
					}
				}

			if (((p_rt->rt_state & RTS_DELETE) != 0) || p_rt->rt_state == 0 ) 
				{
				trace_tf(idrp_trace_options, TR_NORMAL, 0,(" delete set at drp_rt_del_gated  idrp route peer %s route %s", 
				p_idrp_rt->peer->name,iso_ptoa(&p_idrp_rt->nlri)));
				
				return;

				}	
			else
				{
				trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("idrp_rt_gated reset_rt_bit true for %s <%x>",
	 			 iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status));
				rt_delete(p_rt); 
				}

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

		}
	else
		{
		/* hack here - delete so we can change
		 * the status of the active route
		 * but  don't let gated get
		 * to my idrp structures,
		 * I need them for the min advertisement code
		 */
		trace_tf(idrp_trace_options, TR_NORMAL,0, ("QOS route %s delete rtbit not set", iso_ptoa(&p_idrp_rt->nlri)));
	
		return;
	  	if (rtbit_isset(p_idrp_rt->p_rt,idrp_this_node.task->task_rtbit))  
	               	{
			rtbit_set(p_rt,idrp_this_node.task->task_rtbit);
			}
		IS_IDRP(p_rt)
			{
			if ((p_rt->rt_state & RTS_DELETE) == 0)
				{
/*				rt_delete(p_rt); */
				}
			}
		else
			{
			trace_log_tf(idrp_trace_options, 0,LOG_ERR, ("qos SUE's hack point route %s", iso_ptoa(&p_idrp_rt->nlri)));
			}
	
		}

}	

#endif /* IDRP_QOS */



