/* 
 * $Id: idrp_rib_refresh.c,v 1.8 1995/10/31 17:13:38 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"
#include <stdio.h>


/* idrp_rib_refresh.c
 *
 * rib refresh routines:
 *	rib_refresh_pdu_release(peer) - release all the Rib Refresh updates
 *	refresh_pdu_link(peer,results,pdu)
 *	rib_refresh_update_process(peer) - parse all the updates by walking 
 *		the Rib Refresh chain
 */ 
 

void rib_refresh_pdu_release(peer) 
idrpPeer *peer;
{
/* release all the Rib Refresh updates */

idrpRefresh	*p_ref;
idrpRefresh	*p_ref2;
	/* walk the chain and release the refresh structures */

	p_ref = peer->refresh.pdu_list_start;
	while (p_ref) 
		{ 
		refresh_free_pdu_results(p_ref,peer);
		IDRP_MEM_FIT_FREE(p_ref->results->withdraw);
		IDRP_MEM_FIT_FREE(p_ref->results);
		p_ref2 = p_ref;
		p_ref= p_ref->p_next;
		IDRP_MEM_FIT_FREE(p_ref2);
		}
}

void refresh_pdu_link(peer,results,pdu)
idrpPeer *peer;
parse_results *results;
idrpPdu *pdu;
{
idrpRefresh		*p_refresh;	
refresh_pdu_parse	*p_ref_pdu;
refresh_pdu_parse	*p_ref_pdu_head;
refresh_pdu_parse	*p_ref_pdu_tail;
idrp_attribute_record	*p_att;

	p_ref_pdu_tail = p_ref_pdu_head = (refresh_pdu_parse *) NULL;

	IDRP_PARSE_ATT_LIST(p_att,results)	
		{

		/* walk through the multiple attribute list
		 * for the ROUTE SEPARATORS
		 * and reset all the counters to zero
		 * set unprocess pdus in refresh save block 
		 */

		/* set-up a refresh_pdu parse structure per pdu
	 	 *
		 */

		p_ref_pdu = (refresh_pdu_parse *) idrp_local_mem_fit(sizeof(refresh_pdu_parse));
		bzero(p_ref_pdu, sizeof(refresh_pdu_parse));
		p_ref_pdu->p_att = p_att;
		p_ref_pdu->peer = peer;
		p_ref_pdu->p_parse_re = p_att->p_parse_re; 
		p_ref_pdu->p_parse_next = p_att->p_parse_next;

		/* now that we saved all parsing information
		 * from attribute
		 * - we shall clear so another can come in 
		 */
 
		p_att->p_parse_re = (idrpRoute_entry *) NULL;
		p_att->p_parse_next = (idrp_attribute_record *) 0;


		/*  for multiple ROUTE_SEPARATPRS we will
		 * need multiple entries
		 * in this refresh list
		 */

		if (p_ref_pdu_head != (refresh_pdu_parse *) NULL)
			{
			p_ref_pdu_tail->p_next = (refresh_pdu_parse *) p_ref_pdu;
			}
		else
			{
			p_ref_pdu_head = p_ref_pdu_tail = p_ref_pdu;
			} 
		} IDRP_PARSE_ATT_LIST_END;

	results->p_refresh = p_ref_pdu_head;

	p_refresh = (idrpRefresh *) idrp_local_mem_fit(sizeof(idrpRefresh));
	bzero(p_refresh,sizeof(idrpRefresh));
	p_refresh->results = results;

	/* if this is not the first pdu on the list */

	if (peer->refresh.pdu_list_start)
		{
		peer->refresh.pdu_list_end->p_next = p_refresh;
		peer->refresh.pdu_list_end = p_refresh;
		peer->refresh.cnt++;
		} 
	else
		{
		peer->refresh.pdu_list_end = peer->refresh.pdu_list_start = p_refresh;
		peer->refresh.cnt = 1;
		}
}


	
void 
rib_refresh_update_process(peer)
idrpPeer *peer;
{
idrpRefresh *p_ref,*p_ref2; 
idrpRoute   *p_rt,*p_idrp_rt;
register rtq_entry *rtq;
register rt_entry *rt;
refresh_pdu_parse	*p_parse_ref;
idrp_ann_list	*p_w_list;
idrp_ann_list *p_ann_list = NULL;
idrp_ann_list *p_atl = NULL;
int		i;
int		changes = 0;	

/* parse all the updates by walking the Rib Refresh */
/* chain */

	if (peer->refresh.pdu_list_start == (idrpRefresh *) 0) 
		{
		/* nothing in the link list - just return */
		trace_tf(idrp_trace_options, TR_NORMAL,0,("Rib Refresh stop with no update pdus for peer %s  ",
			peer->name));
		return;
		}

	/* go through full list and update the gated routing table 
	 *  - first release all routes associated with this
	 *  - by pulling a list of routes that need to be
	 *    deleted
	 */

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

	bzero(p_w_list,sizeof(idrp_ann_list));	

	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) {
	    p_idrp_rt = (idrpRoute *) rt->rt_idrp;	
	    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_w_list,NLRI_LINKED_P_WITH);
	 	}
	    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) {
	    p_idrp_rt = (idrpRoute *) rt->rt_idrp;	
	    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_w_list,NLRI_LINKED_P_WITH);
	 	}
	    changes++;
	  } RTQ_LIST_END(&peer->iso_gw.gw_rtq, rt);

	trace_tf(peer->trace_options, TR_NORMAL,0, ("%d delete of routes for peer %s rib refresh delete of routes",changes,peer->name));


	for (p_ref = peer->refresh.pdu_list_start; peer->refresh.cnt; peer->refresh.cnt-- )
		{
		IDRP_PARSE_REFRESH_PDU(p_parse_ref,p_ref->results)
			{
			p_parse_ref->p_att->p_parse_next = p_parse_ref->p_parse_next;
			p_parse_ref->p_att->p_parse_re = p_parse_ref->p_parse_re;
			} IDRP_PARSE_REFRESH_PDU_END;

		if (p_ann_list == (idrp_ann_list *) NULL)
			{
			p_atl = p_ann_list = idrp_process_pdu_routes(peer,p_ref->results,IDRP_RIB_REFRESH_TYPE);
			}
		else
			{
			p_atl->p_next = idrp_process_pdu_routes(peer,p_ref->results,IDRP_RIB_REFRESH_TYPE);
			if (p_atl->p_next)
				{	
				p_atl = p_atl->p_next;
				}
			}
		/* add current announce list to the old announce and withdrawl list
		 *
		 */

		/* clean up the results array */

		IDRP_MEM_FIT_FREE(p_ref->results->withdraw);
		IDRP_MEM_FIT_FREE(p_ref->results);
		p_ref2 = p_ref;
		p_ref = p_ref->p_next;
		IDRP_MEM_FIT_FREE(p_ref2);

		} /* loop for the rib refreshes */

	remove_ann_dup(p_w_list,p_ann_list);	
	free_ann_list(p_w_list);

	if (peer->type == IDRP_PEER_EXTERNAL && p_ann_list != (idrp_ann_list *) NULL)
		{
		changes = phase1_external(p_ann_list);
		}
	else
		{
		changes = phase1_internal(p_ann_list);	
		}


	if (p_ann_list)
		{ 
		free_ann_list(p_ann_list);
		}		

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("rib refresh for peer %s had %d changes to route table",peer->name,changes));

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


void
refresh_free_pdu_results(p_ref,peer)
idrpRefresh	*p_ref;
idrpPeer	*peer;
{
refresh_pdu_parse	*p_parse_ref;
refresh_pdu_parse	*p_parse_ref_last = NULL;

	/* here we've got to free the things saved for
	 * processing on the pdu
	 *
	 * Loop through all pdus that the 
  	 * refresh results points to doing the following:
	 * 
	 *  1) free the idrpRoute associated with
	 * 	an route_id 
	 * 
	 *  2)  free the route_id_list associated with
	 * 	a route id
	 * 
	 * 3) dereference the attribute, and
	 *    if it is gone - free it
	 *
	 */  

	IDRP_PARSE_REFRESH_PDU(p_parse_ref,p_ref->results)
		{
		if (p_parse_ref_last)
			{
			IDRP_MEM_FIT_FREE(p_parse_ref_last);
			}
		
		/* free the idrpRoute associated with this id */
 
		idrp_free_nlri_chain(p_parse_ref->p_parse_re);

		idrp_free_idrpRoute_entry(p_parse_ref->p_parse_re,p_parse_ref->p_att);

		if (p_parse_ref->p_att->route_id_list == (idrpRoute_entry *) NULL)
			relink_free_att(p_parse_ref->p_att);

		p_parse_ref_last = p_parse_ref;

		} IDRP_PARSE_REFRESH_PDU_END;

	if (p_parse_ref_last)
		{
		IDRP_MEM_FIT_FREE(p_parse_ref_last);
		}	
}
