/* 
 * $Id: idrp_rt_peer.c,v 1.8 1996/05/25 13:31:42 skh Exp $ 
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

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

/* idrp_rt_peer - 
 * routing routines to handle peers up/down
 * and refresh request cycles.
 *
 * Also included in this is the Adjacency Rib
 * check routines
 * 
 *  Rib handling routine
 *
 * void consistency_check (rib_no) (John)
 *      int rib_no - Rib ID of attributes
 *      -1 - all ribs
 *      now only 0 - default
 *
 * void validate_AdjRib(rib_id,peer) (John)
 *	rib_id - validate for rib_id n 
 *      idrpPeer *peer  - peer structure to check
 *                        adjacency Rib in
 *      run checksum on data - to match specification
 * 	minimum 
 *  			
 * void validate_allRibs()
 *     validate all Ribstructures with a 
 *     idrp appropriate checksum
 * 
 *  %% Full routing blast routines  for IDRP
 *  
 * idrp_rt_send_init(peer) -
 *     send initial blast of routes to neighbor
 *       creates a send list for this peer by:
 *	 - get all active routes from gated
 *       - create send list
 *  	 hands send list to dist_policy
 *	 hands modified send list to send_updates
 *		which then sends pdus out one by one 
 *              via send_update_pdu in idrp.c
 * 
 * idrp_peer_down(peer) - delete all routes for Peer
 * 			 from gated, phase 1 processing  
 * 			 to replicate routes out
 *
 * idrp_rt_re-init -
 * 		re-init after policy change to
 *		drop and re-establish peers
 *  
 *  idrp_send_refresh_all_routes(peer) 
 *	simply calls the above routine  
 *
 * path_up - sets path up, starts timers,
 *	     calls idrp_rt_init
 * 
 * 
 * idrp_refresh_peer -
 *  - gets announce list and 
 *    sends peer refresh sequence
 * 
 *
*/ 

/* %% AdjRib checks */

/*
 * Check the route table for consistency
 */
static void 
consistency_check(rib_no)
int rib_no;
{
	/* idea behind the routine */
	/* if rib_no = -1 - check all IDRP routes */
	/* if rib_no = 0 - check active default   */

	/* do do consistency check */
 
	/* 1.) walk the gate table and check the links between */
	/*      nlri for one route_id */
	/* 2.) walk the outbound route_out id arrays at same time */

	/* define the consistency error to be one so that we */
	/* can see that it works */
 
	consistency_errors = 0;

	if (consistency_errors) {
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("errors found in rib att %s",rib_no));
		task_quit(0);	/* Take a dump */
	}
}

 /* 			
 * 2.) void validate_AdjRib(peer) (John)
 *      idrpPeer *peer  - peer structure to check
 *                        adjacency Rib in
 */
void 
validate_AdjRib(rib_id,peer)
int rib_id;
idrpPeer *peer;
{
	/* call the rt_locate with gateway to make sure */
	/* Adj_Rib in is check */
	/* save a checksum for this gateway in peer structure */
	/* in checksum in Peer structure */
	/* at first do a TCP checksum on all NLRI for now */
	/* other at John's decision */

	return;
}


/* 3.) void validate_allRibs()
 *     validate all Ribstructures with a 
 *     idrp appropriate checksum
 */
void
validate_allRibs(rib_no)
int	rib_no;
{
idrpPeer *peer;

	IDRP_LIST(peer, idrp_peers)
		{
		validate_AdjRib(rib_no, peer);
		}
	IDRP_LIST_END
	return;
}


/* %% Full routing dump routines
 * 
 *  idrp_rt_send_init -
 * 	(called by path_up)
 * 
 *  
 * idrp_send_refresh_all_routes -
 *    send unsolicity refresh request 
 *    this must be called by a kill -USR1 
 * 
 *  peer_down - pull all routes for peer 
 * idrp_rt_re_init - re-init after policy
 *		     change 
 *
 */ 

/* 
 *idrp_rt_send_init - 
 * Send the entire LocRib to a peer.
 * upon initial bring up of routes
 */

void 
idrp_rt_send_init(peer)
idrpPeer *peer;
{
rt_list *rtl[IDRP_NLRI_FAMILIES_SUPPORTED];
rt_entry *route;
u_int proto;
int	i;
int	error;

	NLRI_FAMILY_LOOP(i)
		{
		if (!peer->nlri_supp[i])
			{
			rtl[i] = (rt_list *) 0;
			continue;
			}
	
		/* protocol supported - so try to get active list */
	
		rtl[i] = rthlist_active(peer->nlri_supp[i]);
		} NLRI_FAMILY_LOOP_END 
		  
	if (!idrp_phase3_dump(peer,rtl))
		{
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("error in sending phase 3 dump (usually rdcs) %s",peer->name));
		}
#ifdef	IDRP_QOS
	/* dump all the qos pdus */
	qos_phase3_dump(peer);
#endif	
		
	
}

void
idrp_send_refresh_all_routes(peer)
idrpPeer *peer;
{
	/* send all Loc_Rib to this peer null for now */ 
	/*  - send rib refresh start at beginning
	 * -  call idrp_rt_send_init
	 * - send rib refresh end 
	 */

	send_Refresh_pdu(peer,IDRP_RIB_REFRESH_START);
	idrp_rt_send_init(peer);
	send_Refresh_pdu(peer,IDRP_RIB_REFRESH_END);
}

/*
 * A peer has gone down.  Delete its routes.
 */

void 
idrp_peer_down(peer)
idrpPeer *peer;
{
/* gated list pointers for phase 1 handling */

int 	changes = 0;
idrpRoute	*p_idrp_rt;
idrp_ann_list	ann_list;
register rt_entry *rt;

	trace_tf(peer->trace_options, TR_NORMAL,0, (" idrp_peer_down routine called for peer %s",peer->name));

	bzero(&ann_list,sizeof(idrp_ann_list));
	ann_list.rib_id = RIB_ID_DEFAULT;
	ann_list.peer = peer;

	/* note the IP routes and the ISO routes
	 * are called explicitly here
	 * - if we go to other protocols,
	 *   we will need to change this
	 *  to an NLRI_FAMILY_LOOP
	 */


	rt_open(peer->task);

	/* get the IP routes for this peer */
	if (peer->gw.gw_n_routes > 0) 
	  RTQ_LIST(&peer->gw.gw_rtq, rt) {
	    idrp_peer_route_pull(rt,&ann_list);
	    changes++;
	  } RTQ_LIST_END(&peer->gw.gw_rtq, rt);
	
 
        /* get the ISO routes for this peer */

	if (peer->iso_gw.gw_n_routes > 0) 
	  RTQ_LIST(&peer->iso_gw.gw_rtq, rt) {
	    idrp_peer_route_pull(rt,&ann_list);
	    changes++;
	  } RTQ_LIST_END(&peer->iso_gw.gw_rtq, rt);


	/* now process the announce list in one fell swoop
	 * - if it is internal peer, just delete
	 * - if it is external peer, you may need
	 *   to send out best_external routes and
	 *   then complete delete of routes from peer 
	 */
 
	
	if (peer->type == IDRP_PEER_INTERNAL)
		{
		phase1_internal(&ann_list);
		}
	else
		{
		phase1_external(&ann_list);
		}

	/* Now clean out all of the route ID hash table
	 * entries for this peer.
	 */
	idrp_reinit_hash_tbl(peer);
	
	rt_close(peer->task,&peer->gw,changes, NULL);
}


idrp_peer_route_pull(rt,p_ann_list)
rt_entry 	*rt;
idrp_ann_list	*p_ann_list;
{
idrpRoute		*p_idrp_rt;
int			nlri_id;

	/* do my own delete so I can send phase1
	 * - withdrawls to internal neighbors
	 * - normally would use rt_gwunreach call
	 * - in gated
	 */


         p_idrp_rt = (idrpRoute *) rt->rt_idrp;
	 if (p_idrp_rt->p_rt != rt)
		{
	     	trace_tf(idrp_trace_options, TR_NORMAL,0, 
		      ("idrp route %s (status %x) peer %s being pulled, but not added to pull list p_idrp_rt(%x)->rt != rt (%x)",
		       iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt->peer->name,p_idrp_rt,rt));
		rt_delete(rt);
		}
			 			
         IDRP_STATUS_BIT_TEST(p_idrp_rt,IDRP_STATUS_DELETE)
	   {
	     trace_tf(idrp_trace_options, TR_NORMAL,0, 
		      ("idrp route %s (status %x) peer %s being pulled, but not added to pull list because delete flag set",
		       iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->status,p_idrp_rt->peer->name));
			
	   }
	 else
	   {
	     link_ann_list(p_idrp_rt,p_ann_list,NLRI_LINKED_P_WITH);
	   }
}

/* idrp_rt_reinit - redo all policy after a reinit.  The things which can
 *     change which effect us are the ?? - for now just return
 */

void
idrp_rt_reinit(peer)
idrpPeer *peer;
{
}
/*
 * Bring up the path to our neighbor
 */
void 
path_up(peer)
idrpPeer *peer;
{
	/* increment the number of idrp peers */

	idrp_n_established++;
	peer->idrp_flags = IDRPF_CONNECT;	
	trace_log_tf(idrp_trace_options, 0, LOG_ERR,  ("IDRP path to %s is up", peer->name));
	
	send_keepalive(peer);
	start_keepalive_timer(peer);
#ifdef ECHOKLUDGE
	start_echo_timer(peer);
#endif
	idrp_rt_send_init(peer);
}


void
idrp_refresh_peer(p_ann_list,peer,p_ann_rib,qos)
idrp_ann_list	*p_ann_list;
idrpPeer	*peer;
idrp_ann_list	*p_ann_rib[];
int		qos;
{
idrp_ann_list	*p_atl;
int		i,rib_id;
idrpRoute	*p_irt;
idrp_send_list	*p_send_list = 0;
idrp_ann_list	*p_ann[IDRP_MAX_RIBS+1];


	send_Refresh_pdu(peer,IDRP_RIB_REFRESH_START);

	
#ifdef	IDRP_QOS
	p_ann[0] = p_ann_list;
	
	QOS_RIB_ONLY_LIST(rib_id)
		{
		if (peer->rib_supp[rib_id])
			{
			p_ann[rib_id] = p_ann_rib[rib_id];
			}
		else
			{
			p_ann[rib_id] = (idrp_ann_list *) 0;
			} 
		} QOS_RIB_ONLY_LIST_END;	

	QOS_RIB_LIST(rib_id)
	  {
	  p_ann_list = p_ann[rib_id];  	
#endif IDRP_QOS	

	  /* build and send the updates for the Rib Refresh peer
	   * one thing that can be done in the future is
	   * to hand the update code a linked send list
	   * - right now we just do one at a time
	   * and loop here
	   */ 



	   ANN_LIST(p_atl,p_ann_list)
		{
		/* walk NLRI list for this attribute */	
		NLRI_FAMILY_LOOP(i)
			{
			/* walk the announce tree for nlri this attribute */
			ANN_ANN_LIST_WALK(p_irt,i,p_atl)
				{	
				/* link attribute to send list if OK for peer */
			
				if (DIST(p_irt,peer,IDRP_DIST_REFRESH))
					{	
					p_send_list = send_nlri_attr(p_irt,p_atl->p_attr,p_send_list,peer);
					}
				} ANN_ANN_LIST_WALK_END;
			} NLRI_FAMILY_LOOP_END;
		flush_att_send_list(peer,p_atl->p_attr,p_send_list);
		} ANN_LIST_END;		

#ifdef 	IDRP_QOS
	} QOS_RIB_LIST_END;

#endif	/* IDRP_QOS */		
	free_send_list(p_send_list,peer);
	send_Refresh_pdu(peer,IDRP_RIB_REFRESH_END);

	/* queue a keepalive */
	start_keepalive_timer(peer);
			
}		

void
idrp_req_refresh(peer)
idrpPeer	*peer;
{

	/* requesting Rib Refresh from peer from gated NM action
	 * or from internal determination that Rib is not good
	 */

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("request rib refresh from peer %s",peer->name));
	send_Refresh_pdu(peer,IDRP_RIB_REFRESH_REQ);
}
