/* 
 * $Id: idrp_ribs.c,v 1.3 1995/11/06 16:30:10 skh Exp $
 * Merit IDRP release 3.0 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 *
/
/* idrp_ribs.c
*
* rib_supported(p_qos)
* ph3_create_qos_routes(p_rt,p_rth,pref,p_idrp_rt)
* qos_active_status(p_irt,rib_id)
* qos_chg_active(rib_id,newpolicy)
* ph3_create_qos_routes(p_rt,p_rth,pref,p_idrp_rt)
* qos_all_active(rib_id)
*/

#include "include.h"
#include "idrp.h"

#ifdef	IDRP_QOS

int
rib_supported(p_qos)
idrp_distinguish_att	*p_qos;
{
int		rib_id;
idrp_qos_rib	*p_rib;

	QOS_RIB_LIST(rib_id)
		{
		p_rib = qos_rib[rib_id];
		if (p_rib->att.idrp_mask != p_qos->idrp_mask)
			continue;
	 
                /* test delay values */

                if (p_rib->att.delay.value != p_qos->delay.value)
			continue;
		
                if (p_rib->att.error.value != p_qos->error.value)
			continue;
                
		if (p_rib->att.expense.value != p_qos->expense.value)
			continue;
		
		if (p_rib->att.priority.value != p_qos->priority.value)
			continue;

		if (p_rib->att.sec_type.value != p_qos->sec_type.value)
			continue;

		if (p_rib->att.sec_value.value != p_qos->sec_value.value)
			continue;

		return(rib_id);
		} QOS_RIB_LIST_END	

	return(FALSE);


}

int
rib_supp_mask(mask,value)
u_int	mask;
u_int   value;
{

int		rib_id = 0;
idrp_qos_rib	*p_rib;

	QOS_RIB_LIST(rib_id)
		{
		p_rib = qos_rib[rib_id];
		if (p_rib->att.idrp_mask != mask)
			continue;

		if (p_rib->att.idrp_mask & IDRP_ATTBT_SECURITY)
		  {
		   if (p_rib->att.sec_type.value == value)
		     return(rib_id);
		  }
		else
		  {
		   return(rib_id);
		  }
		} QOS_RIB_LIST_END	

	return (0);	
}


/* routine to create the qos routes from 
 * static routes
 * 
 * create a static qos route for each rib
 *  
 */


void
ph3_create_qos_routes(p_rt,p_rth,pref,p_idrp_rt)
rt_entry	*p_rt;
rt_head		*p_rth;
int		pref;
idrpRoute	*p_idrp_rt;
{
int	rib_id;
int	family;
idrpRoute	*p_irt;

	/* set up some global route variable information 
         */	

	family = socktype(p_rth->rth_dest);

	/* 
	 *  For each QOS 
         *  
	 *  1) Create IDRP QOS route based on static route
	 *  2) run preference (phase1 & phase 2) 
	 *  3) determine active status (qos_active_status)
	 *  4) do nothing further - (qos_change_list will do that) 
	 */


	QOS_RIB_ONLY_LIST(rib_id)
		{
		p_irt = ph3_create_qos_route(pref,p_idrp_rt,rib_id,family);
		qos_active_status(p_irt,rib_id);			
		} QOS_RIB_ONLY_LIST_END;

}

void
qos_active_status(p_irt,rib_id)
idrpRoute	*p_irt;
int		rib_id;
{
rt_head	*p_rt_head = p_irt->p_rt->rt_head;

idrpRoute	*p_active_irt = p_rt_head->p_qos_active[rib_id];  
idrpRoute	*p_last_active = p_rt_head->p_qos_lastactive[rib_id];  
int		res;

	if (p_irt == NULL)
		return; 

	if (p_active_irt == p_irt)
		{
		/* active route is this route
		 * - check to make sure that replace has occurred
		 *  - putting last active and current active in the
		 * same slot
		 */
		assert(p_irt->qos_status & IDRP_STATUS_QOS_ACTIVE);
		IDRP_QOS_STATUS_BIT_TEST(p_irt,IDRP_STATUS_QOS_LAST_ACTIVE)
			{
			if (p_last_active == p_irt)
				{
				p_rt_head->p_qos_lastactive[rib_id] = NULL;
				IDRP_QOS_STATUS_BIT_CLEAR(p_irt,IDRP_STATUS_QOS_LAST_ACTIVE);
				}
			}
		return;
		}

	if (p_active_irt)
		{
		/* we have an active route for this route
		 * -- is this route better?
		 * 1) gated pref of new < gated pref of active 
	 	 *  &&  idrp pref of new > idrp pref of active 
		 */

		if (((p_irt->gated_pref < p_active_irt->gated_pref)  &&
		    (p_irt->pref_cal > p_active_irt->pref_cal)) ||
		    ((p_irt->gated_pref ==  p_active_irt->gated_pref)
		     && (p_irt->pref_cal > p_active_irt->gated_pref)))  
			{
			/* the new route is better - so use it
			 */

			if (p_last_active)
				{
				IDRP_QOS_STATUS_BIT_CLEAR(p_last_active,IDRP_STATUS_QOS_LAST_ACTIVE);
				}

			IDRP_QOS_STATUS_BIT_CLEAR(p_active_irt,IDRP_STATUS_QOS_ACTIVE);
			IDRP_QOS_STATUS_BIT_SET(p_active_irt,IDRP_STATUS_QOS_LAST_ACTIVE);
			IDRP_QOS_STATUS_BIT_SET(p_irt,(IDRP_STATUS_QOS_ACTIVE | IDRP_STATUS_QOS_CHG));   
			p_rt_head->p_qos_active[rib_id] = p_irt;
			p_rt_head->p_qos_lastactive[rib_id] = p_active_irt;
			trace_tf (idrp_trace_options, TR_NORMAL,0,("qos_active_status qos route %s (%x,%x) new active ", 
				iso_ptoa(&p_irt->nlri),p_irt->status,p_irt->qos_status)); 
			trace_tf (idrp_trace_options, TR_NORMAL,0,("qos_active_status qos route %s (%x,%x) old active ", 
				iso_ptoa(&p_active_irt->nlri),p_active_irt->status,p_active_irt->qos_status)); 
			return;
			}

		/* preferences are tied and use tie break of router
		 */

		if ((p_irt->gated_pref == p_active_irt->gated_pref) &&
		    (p_irt->pref_cal == p_active_irt->pref_cal))
			{
			/*  1) tie break router
			 *  2) figure out how to set active flag  
			 */

			res = tie_break(p_irt,p_active_irt);
			if (res == PFX_ADDR_LESS_THAN)
				{
				/* new route is better so use it
				 */
			
				if (p_last_active)
					{
					IDRP_QOS_STATUS_BIT_CLEAR(p_last_active,IDRP_STATUS_QOS_LAST_ACTIVE);
					}

				IDRP_QOS_STATUS_BIT_CLEAR(p_active_irt,IDRP_STATUS_QOS_ACTIVE);
				IDRP_QOS_STATUS_BIT_SET(p_active_irt,IDRP_STATUS_QOS_LAST_ACTIVE);
				IDRP_QOS_STATUS_BIT_SET(p_irt,(IDRP_STATUS_QOS_ACTIVE | IDRP_STATUS_QOS_CHG));   
				p_rt_head->p_qos_active[rib_id] = p_irt;
				p_rt_head->p_qos_lastactive[rib_id] = p_active_irt;
				trace_tf (idrp_trace_options, TR_NORMAL,0,("qos_active_status qos route %s (%x,%x) new active ", 
					iso_ptoa(&p_irt->nlri),p_irt->status,p_irt->qos_status)); 
				trace_tf (idrp_trace_options, TR_NORMAL,0,("qos_active_status qos route %s (%x,%x) old active ", 
					iso_ptoa(&p_active_irt->nlri),p_active_irt->status,p_active_irt->qos_status)); 
				return;
					
				}
			}

		}
	else
		{
		/* no active rib - start one now
		 */
		IDRP_QOS_STATUS_BIT_SET(p_irt,(IDRP_STATUS_QOS_ACTIVE | IDRP_STATUS_QOS_NEW));
		p_rt_head->p_qos_active[rib_id] = p_irt;
		p_rt_head->p_qos_lastactive[rib_id] = NULL;
		return;
		}			

}


/* qos status walk for the 
 * the changed 
 */

idrp_ann_list * 
qos_chg_active(rib_id,newpolicy)
{
idrp_attribute_record *p_att;
idrpRoute_entry	*p_rte;
idrp_ann_list	*p_ann;
int		i;
idrpRoute	*p_irt,*p_irt_old;
idrpRoute        *p_next_nlri;
int		changes = 0;

	rt_open(idrp_this_node.task);
	p_ann = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));   

	ATTR_LIST(p_att,rib_id)
		{
		/* get all the active routes in the attribute list
		 */

		  
		for (p_rte = p_att->route_id_list; p_rte; p_rte = p_rte->p_next)
			{
			/* look for a route with an active route list 
			 */

			NLRI_FAMILY_LOOP(i)
				{
				for (p_irt = p_rte->p_route[i]; p_irt; p_irt = p_next_nlri)
					{
					p_next_nlri = p_irt->p_next_nlri;
					/* status of IDRP or static route that is new and
					 * - the active route, link it 
					 */ 

					IDRP_QOS_STATUS_BIT_TEST(p_irt,(IDRP_STATUS_QOS_ACTIVE))
						{
						IDRP_QOS_STATUS_BIT_TEST(p_irt,(IDRP_STATUS_QOS_NEW))
							{
							/* new route - all routes are IDRP routes
							 */

							ph3_qos_case1(p_irt,p_ann,newpolicy,rib_id);
							changes++;
							}
						else IDRP_QOS_STATUS_BIT_TEST(p_irt,(IDRP_STATUS_QOS_CHG))
							{
							/* active route, and changed route
							 * - find last active route
							 * IS_IDRP_HEAD(p_irt)
							 */
							  if (((p_irt)->p_rt != NULL) && (p_irt)->p_rt->rt_head != NULL)	
								{
								/* idrp route can point to head */

								p_irt_old = IDRP_QOS_LAST_ACTIVE(p_irt,rib_id);
								if (p_irt_old)
									{	
									ph3_qos_case3(p_irt,p_irt_old,p_ann,newpolicy,rib_id);	
									changes++;
									}
								else
									{
									/* here we had a route
									 * - replaced by same route
									 */

									ph3_qos_case1(p_irt,p_ann,newpolicy,rib_id);
									}
								}
							else
								{
								/* halt for now
								 */

								task_quit(32);
								}
							}
						else if (newpolicy)
							{
							/* here is the first time policy hook
							 */
							ph3_qos_case1(p_irt,p_ann,newpolicy,rib_id);

							}	
						}

					else IDRP_QOS_STATUS_BIT_TEST(p_irt,(IDRP_STATUS_QOS_LAST_ACTIVE)) 
						{
						/* IS_IDRP_HEAD(p_irt) */
						if ((p_irt->p_rt) && (p_irt->p_rt->rt_head))
							{
							IS_IDRP_QOS_ACTIVE(p_irt,rib_id)
								{
								/* Is there a qos active flag
								 * - check to make sure that the LAST_ACTIVE down
								 * not have the delete flag set 
								 */	
	
								 IDRP_QOS_STATUS_BIT_TEST(p_irt,(IDRP_STATUS_QOS_DEL))
								   {
								   
								     if (!IS_IDRP_DAMPED_ROUTE(p_irt))
							
								       {
									 /* not damped
									  * delete the route 
									  * 
									  */
									 idrpRoute	*p_rt_qos = p_irt->p_qos_def;	
									 int		rib_id = p_irt->p_attr->rib_id;
		
									 if (p_rt_qos)
									   {
									   p_rt_qos->p_rt_qos[rib_id] = NULL;	
									   }
									 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);
								       }
								   }
								 trace_tf (idrp_trace_options, TR_NORMAL,0,("qos_chg_active:qos route %s (%x,%x) last active & active present ", 
								iso_ptoa(&p_irt->nlri),p_irt->status,p_irt->qos_status)); 
								}
							else
								{
								ph3_qos_case7(p_irt,p_ann,rib_id,newpolicy);
								changes++;
								}
							}
						else
							{
							/* trace the delete state if there
							 * - is no longer any link to the idrp route
							 */
							trace_tf (idrp_trace_options, TR_NORMAL,0,(" qos_chg_active:qos route (last_active) %s (%x,%x) no head there ", 
								iso_ptoa(&p_irt->nlri),p_irt->status,p_irt->qos_status)); 

							}
						}
					} /* end of route loop */
				} NLRI_FAMILY_LOOP_END

			} /* end of route_list loop */
		} ATTR_LIST_END 				

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


/* qos status walk for the 
 * the active status
 */

idrp_ann_list * 
qos_all_active(rib_id)
{
idrp_attribute_record *p_att;
idrpRoute_entry	*p_rte;
idrp_ann_list	*p_ann;
int		i;
idrpRoute	*p_irt;


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

	ATTR_LIST(p_att,rib_id)
		{
		/* get all the active routes in the attribute list
		 */

		  
		for (p_rte = p_att->route_id_list; p_rte; p_rte = p_rte->p_next)
			{
			/* look for a route with an active route list 
			 */

			NLRI_FAMILY_LOOP(i)
				{
				for (p_irt = p_rte->p_route[i]; p_irt; p_irt = p_irt->p_next_nlri)
					{
					IDRP_QOS_STATUS_BIT_TEST(p_irt,IDRP_STATUS_QOS_ACTIVE)
						{	
						link_ann_list(p_irt,p_ann,NLRI_LINKED_P_ANN);
						}
					}
				} NLRI_FAMILY_LOOP_END;

			}
		} ATTR_LIST_END; 				

	return(p_ann);		
}



void
qos_phase3_dump(peer)
idrpPeer *peer;
{
idrp_ann_list	*p_ann;
idrp_ann_list	*p_ann_out,*p_atl;
int	rib_id;
int	i;
idrpRoute	*p_irt;
idrp_send_list	*p_send_list = 0;

      QOS_RIB_ONLY_LIST(rib_id)
	{
	  /* if this peer does not support this rib
	   *   skip setting this up
	   */

	if (peer->rib_supp[rib_id] == 0)
	  continue;

	p_ann = qos_all_active(rib_id);

	/* determine what is the new announce list
	 * based on the policy
	 */

	p_ann_out = qos_export(p_ann,rib_id,peer,NLRI_LINKED_P_ANN); 
	free_ann_list(p_ann);	

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

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

                /* before you go to the next attribute record - flush this one's
		 * nlri's out
		 */

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


}


idrp_ann_list *
qos_export(p_ann_list,rib_id,peer,link)
idrp_ann_list 	*p_ann_list;
int		rib_id;
idrpPeer	*peer;
int		link;
{
idrp_ann_list	*p_ann,*p_atl;
idrpRoute	*p_irt,*p_irt_next;
int		i;
int		status = 0;

/* here we need to check whether the policy
 * allows these routes to be outputted to
 * this peer
 *  
 * 1) allot announce list
 * 2) fill from 1st announce list to either unmodified announce list (p_ann)
 *    (link_ann_list)	
 * 3) fill modified to announce list (link_ann_list_mod)
 * 
 */
	p_ann = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));  

	ANN_LIST(p_atl,p_ann_list)
		{
                NLRI_FAMILY_LOOP(i)
                        {
			/* this needs to be replaced by ANN_ANN_LINK_WALK macro (skh 9/25/95)
			 * 
			 */

			for (p_irt = p_atl->ann_nlri[i].head; p_irt; p_irt = p_irt_next)
                                {
                                switch(link) { 
                                       case NLRI_LINKED_P_NEXT: 
                                                 p_irt_next = p_irt->p_next_nlri; 
                                                 break; 
                                        case NLRI_LINKED_P_WITH: 
                                                 p_irt_next = p_irt->p_with; 
                                                  break;
                                        case NLRI_LINKED_P_ANN: 
                                                  p_irt_next = p_irt->p_ann_nlri; 
                                                  break; 
					case NLRI_LINKED_P_MINADV:
					case NLRI_LINKED_P_MIN_ADV_RD:
                                                  p_irt_next = p_irt->p_ann_nlri; 
                                                  break; 
					}

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

		 		           free_ann_list(p_ann_list);
					   free_ann_list(p_ann);		
		 			   return((idrp_ann_list *) 0);
					   }

	                 		if (status == IDRP_EXPORT_VALID)
       		          			{
			                        if (p_irt->route_out[peer->id].p_att != (idrp_attribute_record *) NULL)
               				           	{
               	                			 link_ann_list_mod(p_irt,p_ann,NLRI_LINKED_P_ANN);
			                                }
						else
							{
               		                 		link_ann_list(p_irt,p_ann,NLRI_LINKED_P_ANN);
							}
						}
					
                                	} /* end of if DIST */
				} 
                        } NLRI_FAMILY_LOOP_END;

                /* before you go to the next attribute record - flush this one's nlri's out */
                } ANN_LIST_END;

	return(p_ann);
}


idrpRoute *
ph3_create_qos_route(pref,p_irt,rib_id,family)
int		pref;
idrpRoute	*p_irt;
int		rib_id;
int		family;
{
idrpRoute_entry	*p_rte,*p_rte_last;
idrp_attribute_record *p_att;
idrpRoute		*p_idrp_rt;
idrpPeer		*peer;
int			route_id_in;
idrpRoute		*p_best_ext;



	/* QOS idrp route follows default route
 	 * (if default comes -> this comes
	 *   if default goes -> this goes
	 *   p_qos_def -> points to default 
	 *   originator of this route in static.
	 *
	 * p_qos[] -> points to the array of routes 
  	 *            created from this static route	 	
	 * 
	 * 1) pref is IDRP_EXT_INFO_IGP:
	 *     - build with p_localinfo route options + QOS in rib  
	 * 
	 * 2) pref IDRP_EXT_INFO_EXT_INFO  (p_ext_info + qos)
	 *
	 * 2) set the peer to the peer from this attribute record 
	 *	if non-bgp:
         *		this will be local_nodes's idrp peer
	 *      if BGP: may change may transfer the peer this
	 *		to an IDRP parallel peer structure	 
	 * 
	 *
	 *  4.) create an idrpRoute for this Route
	 *	 - allocate the idrp route and stuff in initial info
	 *	 - put nlri in route
	 *	 - attach this route to the attribute record 
	 *	   (will be local node except for bgp 
	 *
	 * 5) set the Route_id_in to be the external info route_id
	 *		information  is received one 
	 *
	 * 	we build the attribute record with the right peer
	 * 	this double check is useful 
	 *
	 *
	 */

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

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

		/* need to copy in the prefix prior to 
		 * setting up the best external for debugging
		 * info that we need for now
		 * 
		 */
	
		bcopy(&p_irt->nlri,&p_idrp_rt->nlri, sizeof(p_irt->nlri));	
		if (!idrp_add_rt_to_gated(p_idrp_rt,p_idrp_rt->p_qos_def->p_rt->rt_dest))
			{
			/* write in here some error message 
			 * and free up the route
			 */
			}	

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

		p_best_ext = find_best_ext(p_irt->p_rt,p_irt);
		insert_in_pref_order(p_idrp_rt, p_best_ext);

		/* I pulled out the setting of the IDRP_STATUS_EXT_INFO
		 * here indicating that it is not external info
		 * -
		 * Our IGP sources are currently:
		 *  static routes:
		 *     a) definitely our only way to do IDRP configurations
		 *     b) we can fake IS-IS by using exterior on a static route
		 *	 
		 *  IS-IS routes
		 *    a) If is-is has external info that it will flag it in
		 *       the protocol which will get reflected in exterior
		 * 	 flag 
	 	 *
		 *    b) the regular isis will get flaged as interior 
		 * 
		 */
 
		}
	else
		{
		/* pref = IDRP_EXT_INFO_EXT_INFO */

			
		p_att = find_ext_info_attr_rec(p_irt->p_rt->rt_head,p_irt->p_rt,idrp_this_node.p_ext_info,rib_id);
		p_att->p_opts->idrp_local_proto = p_irt->p_rt->rt_gwp->gw_proto;
		p_idrp_rt = idrp_alloc_idrpRoute(&idrp_this_node,p_att,family,IDRP_EXT_INFO_ROUTE_ID);
		p_idrp_rt->p_qos_def = p_irt;
		IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_EXT_INFO);
		bcopy(&p_irt->nlri,&p_idrp_rt->nlri, sizeof(p_irt->nlri));	
		if (!idrp_add_rt_to_gated(p_idrp_rt,p_idrp_rt->p_qos_def->p_rt->rt_dest))
			{
			/* write in here some error message 
			 * and free up the route
			 */
			}	
		}


	

	/* here we need to fill in the idrp_rt allocated
	 * 6) fill in idrp route entry to link to originating idrp route 
	 * 7) fill in qos_route 
	 * 8) set status  
	 */


	p_irt->p_rt_qos[rib_id] = p_idrp_rt; 
	p_idrp_rt->p_loc_rt_peer = p_irt->p_rt->rt_routers[0];
	IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_QOS_NEW);
	

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

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

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

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

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

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

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

}


void
idrp_qos_del_static(p_idrp_rt,p_ann_list)
idrpRoute	*p_idrp_rt;
idrp_ann_list	*p_ann_list;
{
int	rib_id;

	QOS_RIB_ONLY_LIST(rib_id)
		{
		/* QOS RIB
		 * If there is a idrp route that is  
		 * static, delete associated QOS routes 
		 */
		if (p_idrp_rt->p_rt_qos[rib_id])
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0, ("idrp_qos_del_static  route for route %s (%x, %x) peer %s ",
			iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status,p_idrp_rt->qos_status,p_idrp_rt->peer->name));
			if (qos_active_del(p_idrp_rt->p_rt_qos[rib_id]))
				{
				trace_tf(idrp_trace_options, TR_NORMAL,0, ("idrp_qos_del_static deleted route above "));
				}
			else
				{	
				trace_tf(idrp_trace_options, TR_NORMAL,0, ("idrp_qos_del_static  route for route %s (%x, %x) peer %s ",
				iso_ptoa(&p_idrp_rt->nlri), p_idrp_rt->status,p_idrp_rt->qos_status,p_idrp_rt->peer->name));
				}
			}
	
		} QOS_RIB_ONLY_LIST_END;
 

}

/* delete all the active routes associated with this original
 * idrp route created from a local static route
 */


int
qos_active_del(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
int	active;
int	rib_id = p_idrp_rt->p_attr->rib_id;

	/* don't process any null route */ 

	if (p_idrp_rt == NULL)
		return (FALSE);

	/* 
	 * 1) if the QOS route is active
         * 	1.1) move the route to last active
	 *	1.2) set the status flags
         *      	 STATUS_QOS_LASTACTIVE & STATUS_QOS_CHG
         * 	1.3) set the delete flag in the QOS flags (STATUS_QOS_DELETE)
	 *
	 * 
	 * 2) if the QOS route is the last active route
	 * 
	 *   2.1) if there is an active route,
	 *      a) if this route is damped route ->
	 *        set STATUS_QOS_DELETE 
	 * 
	 *      b) if not, delete this route and
	 *         clear out the last_active in the header  
	 * 
	 *  2.2) No active route -
	 *
	 * 	1)  is this route damped? - if so
	 *     	     set delete on the IDRP route and the STATUS_QOS_DELETE
	 * 	2) if this route is not damped, delete this route
	 * 
	 * 3) if the QOS route is non-active,
         * 	2.1)if the route is the damped route
         *       - flag the delete in the route and the (STATUS_QOS_DELETE)
	 *
         * 	2.2) if the route is not the damped route,
       	 *       - delete the route from the gated tables
	 * 
	 */

	IDRP_QOS_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_QOS_ACTIVE)
		{
		idrpRoute  *p_irt_act = NULL;	
		/* active route - 
		 *  move the route to last active
		 */

		p_idrp_rt->p_rt->rt_head->p_qos_lastactive[rib_id] = p_idrp_rt;
		p_irt_act = p_idrp_rt->p_rt->rt_head->p_qos_active[rib_id] = find_next_qos_active(p_idrp_rt);	
		if (p_irt_act)
		  {
		  IDRP_QOS_STATUS_BIT_SET(p_irt_act,IDRP_STATUS_QOS_ACTIVE);
		  }
		IDRP_QOS_STATUS_BIT_CLEAR(p_idrp_rt,IDRP_STATUS_QOS_ACTIVE);

		if (IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
		    {
		    IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_QOS_LAST_ACTIVE|IDRP_STATUS_QOS_CHG| IDRP_STATUS_QOS_DEL));
		    }	
		else
		    {
		      IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,(IDRP_STATUS_QOS_LAST_ACTIVE| IDRP_STATUS_QOS_DEL));
 	       	    }
		return(FALSE);
		}
	else IDRP_QOS_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_QOS_LAST_ACTIVE)
		{
		/* last active route 
		 *
		 * - if this is the damped route set status delete
		 */

		if (IS_IDRP_DAMPED_ROUTE(p_idrp_rt))
			{
			/* just set delete on the route
			 */

			IDRP_QOS_STATUS_BIT_SET(p_idrp_rt,IDRP_STATUS_QOS_DEL);
			}
		else
			{
			/* not damped
			 * - 1) delete the route 
			 * 
			 */
			 idrpRoute	*p_rt_qos = p_idrp_rt->p_qos_def;	
			 int		rib_id = p_idrp_rt->p_attr->rib_id;
		
		         p_rt_qos->p_rt_qos[rib_id] = NULL;	
                         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);
			return(TRUE);
 			}
		}
	else if (p_idrp_rt->qos_status == IDRP_STATUS_QOS_NOTACTIVE)
		{
		/* not active 
		 * - 1) delete the route 
		 * 
		 */
		 idrpRoute	*p_rt_qos = p_idrp_rt->p_qos_def;	
		 int		rib_id = p_idrp_rt->p_attr->rib_id;
		
	        p_rt_qos->p_rt_qos[rib_id] = NULL;	
       		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);
		return(TRUE);
		}

	return(FALSE);	

} 

idrpRoute *
find_next_qos_active(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
rt_entry	*p_rt = NULL;
rt_head		*p_rth = p_idrp_rt->p_rt->rt_head;
idrpRoute	*p_irt = NULL;
idrpRoute	*p_irt_act = NULL;

	/* find the next active route
	 * for this QOS route
	 * 1) must be an idrp route (static routes get IDRP routes generated for them)
	 * 2) if it has the lowest idrp preference && the highest - sink it on
	 * 
	 */

	 
	RT_ALLRT(p_rt,p_rth)
                {
                IS_IDRP(p_rt)
                        {
			/* we should have an IDRP route associated with a
			 * gated route that
			 * is active
			 * - not being deleted
			 *  
			 */
			p_irt = p_rt->rt_idrp;
			if (p_irt && (p_irt != p_idrp_rt) && (p_irt->p_attr->rib_id == p_idrp_rt->p_attr->rib_id) &&  
				  (!(IDRP_STATUS_BIT_ON(p_irt,IDRP_STATUS_DELETE))) && 
				(!IDRP_QOS_STATUS_BIT_ON(p_irt,IDRP_STATUS_QOS_DEL)))
				{
				if (p_irt_act == NULL)
					{
					/* no active route - so use this one
					 */	
					p_irt_act = p_irt;		
					}
				 else 
					{
					if (p_irt_act->pref_cal > p_irt->pref_cal)
						{
						/* replace the active route 
						 */

						p_irt_act = p_irt;
						}
 					else if (p_irt_act->pref_cal == p_irt->pref_cal)
						{
						if (p_irt_act->gated_pref < p_irt->gated_pref)
							{
							p_irt_act = p_irt;
							}
						}
					}
				}   
				 
			}

		}  RT_ALLRT_END(p_rt, p_rth) ; 	

	 if (p_irt_act)
	   {
	   trace_tf (idrp_trace_options, TR_NORMAL,0,("find_next_qos_active route %s (%x,%x) new active ", 
				iso_ptoa(&p_irt_act->nlri),p_irt->status,p_irt->qos_status));
	   }
	 else
	   {
	   trace_tf (idrp_trace_options, TR_NORMAL,0,("find_next_qos - no route new active ")); 
	   }
	return(p_irt_act);
}	 	

#endif /* IDRP_QOS */
