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


/* 
 *  Initial Coding by Dave Katz, Merit/NSFNET
 *  Re-write by John Scudder, Susan Hares Merit/NSFNET. 
 *
 */

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

/* temporary inclusion for sending to TCP port */
#include "inet.h"

/* timer */
static task_timer *retransmission_timer;


/* The Idrp_Init Code saga begins here */

/* gated requires that the following sequence of initialization take place 
 * (If you want to play the idrp-gated blues, you  must play this intros 
 * This is the minimal overture to the task movement  
 *    * Initialization call sequence:
         * idrp_cleanup (only on reconfig)
         * idrp_var_init
	 * parse
	 * idrp_init
	 * idrp_reinit
	 * idrp_flash
	 * idrp_newpolicy

* bgp's intro does a symphony intro - it plays all the main themes of: 
* (gotta keep the beat with Mr Jones Q. BGP  or lose internet status)
	 *
         * bgp_cleanup(only on reconfig)
	 * bgp_var_init
         * parse
	 * bgp_init
	 * bgp_reinit - called after config file re-read and bgp_init done
	 * bgp_flash
	 * bgp_newpolicy
*/

/*
 *    idrp_cleanup before re-parse
 *    when called: before configuration file is to be re-read
 *    what done: loop through all import/export marking groups 
 *               to be deleted.  Since currently we only
 *               have global lists the pointers are set to zero.
 */

void
idrp_cleanup(tp)
task *tp; /*  NOTUSED but needed for GateD */
{
idrpPeer 	*peer;


	/* flag each peer as delete so initialization will clean up 
	 * each idrp_peer task will clean up structure
	 * idrp_cleanup - must loop through idrp peers and reset the globals
	 */


	idrp_reparsing = TRUE;

	/* clear the counters of established peers */

	idrp_n_disabled = 0;
	idrp_n_established = 0;


    /* set peers to delete */
 	 
    IDRP_LIST(peer, idrp_peers)
	{
	BIT_SET(peer->idrp_flags,IDRPF_DELETE);
	} IDRP_LIST_END	

    /* free local_route list flag on local chain
     * let new parsing fill in loca routes once again.
     */ 

	idrp_local_route_reset();

#ifdef IDRP_QOS
   /* reconfig the qos ribs 
    */
	idrp_qos_rib_reconfig();	 

#endif /* IDRP_QOS */

    /* BGP cleans up the import lists by groups */
    /* once we get to policy we will need to do */
    /* this too.  Right now the import and export */
    /* list is a simple global policy */ 
 

    adv_free_list(idrp_import_list);
    idrp_import_list = (adv_entry *) 0;
    adv_free_list(idrp_import_list_any_rdi);
    idrp_import_list_any_rdi = (adv_entry *) 0;
    adv_free_list(idrp_import_rdpath_list);
    idrp_import_rdpath_list = (adv_entry *) 0;
    adv_free_list(idrp_export_list);
    idrp_export_list = (adv_entry *) 0;
    adv_free_list(idrp_export_list_any_rdi); 
    idrp_export_list_any_rdi = (adv_entry *) 0;
 
    idrp_var_init();
}


void 
idrp_global_list_cleanup()
{

/* here's where we need to cleanup
 * - attribute list
 * - global rdi list
 * - global rd list 
 */ 

} 


/* 
 * idrp_var_init - initialize variables prior to gated
 *                 parsing the configuration file.
 * set up global idrp processing (doing_idrp or better
 * known as "doing the idrp blues"), trace flags
 * default metric, and default preference within gated 
*/

void 
idrp_var_init()
{
  	int i;

	doing_idrp = FALSE;
	idrp_default_metric = IDRP_METRIC_NONE;
	idrp_default_preference = RTPREF_IDRP_EXT;
	
	
	/* reset the local IDRP structures  prior to config run
	 * - reset count of local routes
	 * - pointer to local attribute record 
	 * - reset the local rd path length - since no one is using it 
 	 */

	idrp_local_route_cnt = 0;
	rt_net_cnt = 0;
	rt_ip_net_cnt = 0;
	bzero(&local_rd_path,sizeof(RD_path)); 
	
	/* set-up the Peer structure for this node */

	idrp_this_node.p_next = (idrpPeer *) 0;
	if (idrp_reparsing == FALSE)
		{
		/* set the RDI to zero if the 
		 * this is the initial call of idrp_var_inits 
		 */

		rt_rdi.isoa_len = 0;
		for (i = 0; i < IDRP_RDPATH_HASH_SIZE; i++)
			{
			rd_path_list_hash[0] = (idrp_rdpath_hash *) 0;
			}

		/* clear all the peer tracing */
		idrp_neighbors = 0;
		idrp_ext_neighbors = 0; 
		idrp_int_neighbors = 0; 
		idrp_n_disabled = 0; 
		idrp_n_established = 0; 
		idrp_n_ext_established = 0; 

		idrp_last_int_peer = (idrpPeer *) NULL;
		idrp_ext_peers = (idrpPeer *) NULL;
		idrp_peers = (idrpPeer *) NULL;
		idrp_peers_end = (idrpPeer *) NULL;

#ifdef	IDRP_QOS	
		/* clear the qos ribs
		 */
	
		rib_cnt = 0;
		last_rib_cnt = 0;
		for (i = 0; i <= IDRP_MAX_QOS_RIBS; i++)
			{
			last_qos_rib [i] = qos_rib[i] = (idrp_qos_rib *) 0;
			}

		 qos_rib[RIB_ID_DEFAULT] = (idrp_qos_rib *) idrp_local_mem_fit(sizeof(idrp_qos_rib));	
#endif
		}	
}


sockaddr_un *
idrp_find_interface(type)
int	type;
{
sockaddr_un	*localaddr;
idrpPeer	*peer;
if_addr		*ifap;

	/* walk down the list fo IDRP peers and try to find a configured interface */

	IDRP_LIST(peer, idrp_peers) {

		/* IP interface search  */ 
		if (type == AF_INET)
			{
		    	if (peer->ip_intf_addr) 
				{
				localaddr = peer->ip_intf_addr;
				break;
		    		}
			}

		/* ISO interface search */

		if (type == AF_ISO)
			{
			if (peer->iso_intf_addr.isoa_len)
				{
				localaddr = (sockaddr_un *) &peer->iso_intf_addr;
				break;
				}
			}
	} IDRP_LIST_END
	
	/* find an active interface address to send this pdu to the neighbor */
	
	if (!localaddr) {
	
	    IF_ADDR(ifap) { 
	        if (!BIT_TEST(ifap->ifa_state, IFS_LOOPBACK) &&
			(socktype(ifap->ifa_addr) == type))
			{
			localaddr = ifap->ifa_addr;
			break;
			}
    	      } IF_ADDR_END(ifap); 
	}

return (localaddr);
}



/* terminate the main reception task 
 *
 */


void
idrp_terminate(tp)
task *tp; /* NOTUSED but necessary for GateD */
{
idrpPeer	*peer;

/* terminate the master task */
/* since we can't receive -terminate all the other tasks */ 

	IDRP_LIST(peer, idrp_peers) {
		if (peer->idrp_flags == IDRPF_CONNECT)
			{
			idrp_peer_terminate(peer->task);
			}
		/* delete the idrp master task *
		 * skh -- need to remove the locally configured routes
		 *   from the route table 
		 */

		idrp_local_route_clean();
		task_delete(idrp_this_node.task);	
		idrp_this_node.task = (task *) 0;
 
	} IDRP_LIST_END;
}
 
 
/*
 *      shut down a peer 
 * 	delete the task, and delete
 *	the idrp structure
 */
void
idrp_peer_terminate(tp)
task *tp;
{
	idrpPeer *peer;

	peer = (idrpPeer *) tp->task_data;
	if ((peer->idrp_flags & IDRPF_CONNECT) != 0 )
		{
		idrp_stop_event(peer);
		task_timer_delete(peer->idrp_closewait_timer);
		task_timer_delete(peer->idrp_hold_timer);
		}
	idrp_min_adv_cleanup(peer);
	task_delete(tp);
    	idrp_delete(peer);
}


 
/*
 *      Cleanup for a peer
 */
void
idrp_peer_cleanup(tp)
task *tp;
{
	/* make sure delete flags are set-up for peer structure */
 
	idrpPeer *peer;

	peer = (idrpPeer *) tp->task_data;
	peer->idrp_flags |= IDRPF_DELETE;
 
}
 
 
/*
 *      Reinit a peer
 */
void
idrp_peer_reinit(tp)
task *tp;
{
    idrpPeer *peer;
 
    peer = (idrpPeer *) tp->task_data;
    if (!doing_idrp) 
	{
	/* idrp turned off - so delete any peer reaching
	 * this re-init routine 
	 */

	peer->idrp_flags |= IDRPF_DELETE;
       	idrp_peer_terminate(tp);
	}
    else
	{
	/* idrp functioning 
	 * handle changes in peers due to re-config
	 * peer delete - means was in config/ now not in config
	 * peer UNCONFIGURED - means error in parsing routines
	 * peer TRY_CONNECT - normal state for peer
	 *		  should be new peer/peer with no connection 
	 *
	 * peer CONNECT - 
	 * 	problem with parsing - no configuration
	 *	delete peer
	 * 
	 * peer CONNECT & TRY_CONNECT  - if we are reparsing idrp
	 *		  then leave this peer alone.
	 *		  (skh- hopefully this is correct gated behavior)
	 * 
	 * peer CONNECT & WRITEFAILED 
	 *		- path_down on peer 
	 *		- reinit the peer
	 *
	 * peer CONNECT & IDLED 
	 *		- path down on peer
	 *		- leave the task and timers on the peer
	 *		skh--- if space becomes a problem (3/7/93)
	 *		skh--- we can delete this structure 
 	 * 
	 * peer WRITE_FAILED - socket problems 
	 *			try after gated re-init
	 * 
	 * peer IDLED - no connect configured peer off.
	 * 		leave timers intact and task intact
	 *		skh--- if space becomes a problem (3/7/93)
	 *		skh--- we can delete this structure 
	 */



	if (peer->idrp_flags & IDRPF_DELETE)
		{
		trace_log_tf (idrp_trace_options,0, LOG_ERR, ("peer %s deleted from configuration file status (peer = %x (%x)",peer->name,peer,peer->idrp_flags));
		}

	switch (peer->idrp_flags) {
		case IDRPF_UNCONFIGURED:
			/* send error message */
			trace_tf(idrp_trace_options,0, LOG_ERR,  ("idrp_peer_reinit: re-init unconfigured neighbor %s", peer->name));
			break;

		case IDRPF_DELETE:
		case (IDRPF_DELETE | IDRPF_TRY_CONNECT):
		case (IDRPF_DELETE | IDRPF_CONNECT):
		case (IDRPF_DELETE | IDRPF_WRITEFAILED):
		case (IDRPF_DELETE | IDRPF_IDLED):

			/* send error message */
			trace_tf(idrp_trace_options,0, LOG_ERR,  ("idrp_peer_reinit: deleted neighbor %s", peer->name));
			idrp_peer_terminate(tp);
			break;

		case IDRPF_CONNECT:
			/* send error messages */

			trace_tf(idrp_trace_options,0, LOG_ERR,  ("idrp_peer_reinit:connected neighbor %s unconfigured so deleted",peer->name)); 
			idrp_peer_terminate(tp);
			break;

		case IDRPF_TRY_CONNECT:
		case (IDRPF_CONNECT | IDRPF_WRITEFAILED):

			/* initialize unconnected */

			trace_tf(idrp_trace_options,0,LOG_ERR,("idrp_peer_reinit: peer %s reinit last flags %x",peer->name,peer->idrp_flags)); 
			peer->idrp_flags = IDRPF_TRY_CONNECT;
			if (!peer->start_timer_running)
				{
				idrp_release_hash_tbl(peer);
				idrp_peer_ete_reinit(peer);
				idrp_sm(peer,IDRP_START_EVENT);
				}
			break;
 

		case (IDRPF_CONNECT | IDRPF_TRY_CONNECT):

			if (!idrp_rdi_change)
				{   
				peer->idrp_flags = IDRPF_CONNECT;
				idrp_reinit_ete_timers(peer);	
				trace_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_peer_reinit: connected peer %s left connected",peer->name));
				}
			else
				{
				idrp_sm(peer,IDRP_STOP_EVENT);
				trace_tf(idrp_trace_options, 0, LOG_ERR, ("idrp_peer_reinit: connected peer %s disconnected due to rdi change",peer->name));
				}
			break;

		case (IDRPF_CONNECT | IDRPF_IDLED):

			/* clear down this peer
			 * - leave task structure intact
			 */
			begin_close(peer);
			idrp_sm(peer,IDRP_STOP_EVENT);
			peer->idrp_flags = IDRPF_IDLED;	
			idrp_n_disabled++;
			trace_tf (idrp_trace_options,0,LOG_ERR,("idrp re-configuration idles peer %s",peer->name));
			break;
 
	
		case IDRPF_WRITEFAILED:
			/* not real sure about this - write failed --skh 3/7/93
			 */
  
			trace_tf (idrp_trace_options,0,LOG_ERR,("idrp reinit on write failed port peer %s",peer->name));
			idrp_release_hash_tbl(peer);
			idrp_peer_ete_reinit(peer);
			idrp_sm(peer,IDRP_START_EVENT);
			break;

		case IDRPF_IDLED:	
			/* send message */
	   	 	trace_tf(idrp_trace_options, 0,LOG_ERR, ("idrp_peer_reinit: config permanently idled neighbor %s", peer->name));
			idrp_n_disabled++;
			break;
		} /* end of switch */	
        } /* end of else */
}

/* idrp initialization task */ 
 
void
idrp_init()
{
idrpPeer	*peer;

    if (!doing_idrp)
	{ 
        /* IDRP is not running, but it may have been 
	 *  cleanup all ribs 
	 *  Delete all peers and associated tasks
 	 */ 

#ifdef	IDRP_QOS
	idrp_init_local_ribs();
#endif /* IDRP_QOS */
 
        IDRP_LIST(peer, idrp_peers) {
		if (!peer->task)
			{
			task_delete(peer->task);
			peer->task=NULL;
			}
			/* here free up the adv_entry for the peer 
			 *  when we go to peer by peer policy 
			 */

        	idrp_delete(peer);
        } IDRP_LIST_END

      /* idrp is gone - clean up the ribs
       * and delete all the remaining
       */	 		  	

      idrp_route_cleanup();
      idrp_cleanup((task *) NULL);
      return;
      } 

      	/* gated is "doing_idrp"  so start the task blues  
	 * here's the bgp receipe for initialization task 
	 *      - 1.) get rid of groups 
	 *        2.) free any policy  
	 *        3.) init the mib      
         *        4.) start up ability to receive on sockets 
	 * We are leaving groups, some policy, and the
	 * mib until phase2.  
	 *    4.) start up receive on sockets - 
	 * 	  This is done by opening up UDP/CLNP/IP/IDRP
	 *	  socket.  Or any combination of these. 
 	 * 
	 * skh -- 3/7/93 - still need to hack in CLNP/IP/IDRP sockets
	 */


	/* initialize things that could have changed on the
	 * local peer during a policy re-configuration
	 * - this may change during a re-init as well
	 */

	idrp_local_peer_init();


	/* initialize the local node peer  and the master task
	 * - the master task structure is the focus for all
	 *   received BISPDUs from any socket
	 * - the local peer structure holds the pointer to
	 *   the master task structures,
	 *   the minimum route advertisement for this RD timer
 	 * - is the peer structure for all local idrp routes
	 *
	 * - this initialization is done each time
	 *   the peer is activated.  Where it the above
	 *   re-configuration of local pathways may
	 *   only be done after a re-configuration bid.
	 *   I still need to find out how you disable
	 *   a peer without -usr2 to gated 
	 */

	idrp_init_this_node();

	/* initialize the task structure for the
	 * tasks
	 */

	idrp_init_peer_tasks();

	 /* - call idrp_init_local_routes
	  * - this can be called during the re-initialization
	  *   after re-configuration or new policy 
	  */

#ifdef IDRP_QOS
	idrp_init_ribs();
#endif
	idrp_init_local_rt();

}

void
idrp_init_this_node()
{
task		*p_master_task;
sockaddr_un	*localaddr = NULL;


	/* initialize this node's peer structure and
	 * - check to make sure master task
	 *   has not been configured before
	 * 
	 * the master task associated with it
 	 * - all receive packets come to this
	 *   set of structures
	 * - all local routes associated with
	 *   idrp configuration or EXT_INFO
	 *   routes are associated with this 
	 *   set of structures
 	 * - minimum advertisement of routes
	 *   within RD timer associated with
	 *   this structure 
	 */


 
	/* Allocate the send/receive task. */

	if (idrp_reparsing)
		{
		/* check to see if the proto_sock has changed
		 * - re-init the task socket and
		 * close old socket
		 * - empty until I attack two protocol sockets  
		 */
		return;
		}
 
        p_master_task = task_alloc("IDRP",TASKPRI_EXTPROTO, idrp_trace_options);
	localaddr = idrp_find_interface(AF_INET);

	idrp_this_node.ip_neighbor = localaddr;
	idrp_this_node.ip_intf_addr = localaddr;

	/* set-up the master task with routines for: 
	 * idrp_recv - receive data from socket 
	 * idrp_terminate - terminating all idrp peer sessions 
         *   and clean-out tasks 
	 * idrp_cleanup - cleanup all peer sessions deleted, reset globals
 	 */

        p_master_task->task_rtproto = RTPROTO_IDRP;
        p_master_task->task_data = (void_t) &idrp_this_node;
	p_master_task->task_rtbit = rtbit_alloc(idrp_master_task,FALSE, IDRP_TSI_SIZE,(caddr_t) &idrp_this_node,idrp_peer_rtbit_dump);

	/* set the master task 
	 * routines
	 */		
 
	task_set_recv(p_master_task,idrp_recv);	
	task_set_terminate(p_master_task,idrp_terminate);	
	task_set_cleanup(p_master_task,idrp_cleanup);	
	task_set_reinit(p_master_task,idrp_reinit);
	task_set_dump(p_master_task,idrp_master_dump);
	task_set_flash(p_master_task,idrp_flash);
	task_set_newpolicy(p_master_task,idrp_newpolicy);


	/* following things are not in master task vocabulary 
	 *  not applicable because  IP and CLNP protocol
	 * task_accept - process accepts 
	 * task_connect - process connect complete
	 * task_except - process exceptional 
	 */

	/* Find an interface to use.
	 * initialize the sockets we need
	 * right now, we need to initialize
	 * one socket.  This will change prior
	 * to delivery 1
	 */

	idrp_init_local_proto_sock(p_master_task);

	/* allocate receive and transmit buffers */
 
	task_alloc_recv(p_master_task, IDRP_PKTSIZE);
	task_alloc_send(p_master_task, IDRP_PKTSIZE);

	/* set the task bits */


	/* put the local routes as part of the master task 
	 * it's since there are no timers to release these routes
  	 */  

	idrp_this_node.task = p_master_task;
	idrp_this_node.gw.gw_task = p_master_task;
	idrp_this_node.iso_gw.gw_task =p_master_task;


	idrp_this_node.gw.gw_rtq.rtq_forw = idrp_this_node.gw.gw_rtq.rtq_back
	  = &(idrp_this_node.gw.gw_rtq);

	idrp_this_node.iso_gw.gw_rtq.rtq_forw = idrp_this_node.iso_gw.gw_rtq.rtq_back
	  = &(idrp_this_node.iso_gw.gw_rtq);

	/* set-up the timer for the minimum Route Advertisement timers
	 * for remote routing domains and  
	 * for this routing domain 
	 * - Even though this could be global in the current concept
	 *   of routers, it is likely under the QOS or  Internet  Route
	 *   Server model this timer will need to be set as the
	 *   master task per group of idrp speakers . 
	 *    
	 */  

	idrp_this_node.idrp_minadvRD_timer = task_timer_create(p_master_task,
			    "Minimum Route Adver this RD",
			    0,
			    (time_t) 0,
			    (time_t) 0,
			    idrp_process_minadvRD,
			    (caddr_t) 0);

	idrp_this_node.idrp_minadv_timer = task_timer_create(p_master_task,
			    "Minimum Route Adver ",
			    0,
			    (time_t) 0,
			    (time_t) 0,
			    idrp_process_minadv,
			    (caddr_t) 0);

#ifdef IDRP_QOS
	idrp_this_node.idrp_qos_timer = task_timer_create(p_master_task,
			    "QOS change timer ",
			    0,
			    (time_t) 0,
			    (time_t) 0,
			    idrp_process_qos,
			    (caddr_t) 0);

#endif /* IDRP_QOS */
}


#ifdef	IDRP_QOS
void
idrp_init_local_ribs()
{
	
/* here's where we cleanup the local ribs
 */

	idrp_qos_rib_cleanup();
	idrp_init_ribs();
}
#endif

void
idrp_init_local_rt()
{
int i;
idrp_attribute_record *p_att;
idrpRoute	*p_next_irt;
idrpRoute	*p_irt;
idrpRoute_entry	*p_ire;
int		changes = 0 ;
int		rib_id = 0;


	/* first clean up any local routes left over
	 * delete any routes that 
	 * from the parsing routines -
	 */

	rt_open(idrp_this_node.task);
	if (IDRP_LOCAL_CNF_TEST(IDRP_CNF_OPT_NET_AUTO_CONFIG))
		{
		changes = changes + idrp_net_cnf_prefix();
		}

	changes += idrp_local_route_clean();  
	 
 
	/* By now, the gated routing structure should be able
	 * handle route additions
	 * walk the attribute list looking for new local routes until you get
	 * to the end of the local attributes 
	 * only default routes are osilocal compatible
 	 */

	rib_id = 0;
	ATTR_LIST(p_att,rib_id)
		{
		if (p_att->p_rd_opath == (rdpath_list *) NULL)
			{
			p_att->p_rd_opath = p_my_rdpath;
			p_my_rdpath->refcnt++;
			}	
		for (p_ire = p_att->route_id_list; p_ire; p_ire = p_ire->p_next)
		    {
		     NLRI_FAMILY_LOOP(i) 
			{
			for (p_irt = p_ire->p_route[i]; p_irt != (idrpRoute *) 0; p_irt = p_next_irt)
				{
				p_next_irt = p_irt->p_next_nlri; 
				if (p_irt->status & IDRP_STATUS_LOCAL_NEW_ROUTE)
					{
					if (!idrp_add_local_rt_gated(p_irt))
						{
					 	/* route could not be added to gated 
					 	 * flag it and  pull it out of the list 
						 */
			
						trace_tf (idrp_trace_options, TR_NORMAL,0,("local route %s could not be added to gated table, deleted peer %s ",
						iso_ptoa(&p_irt->nlri),p_irt->peer->name));
				

						/* free this attribute list 
						 * -free will relink around the attribute list
						 */

						idrp_free_outlist(p_irt);
						idrp_free_nlri_att_rec(p_irt,NLRI_LINKED_P_NEXT);
						IDRP_MEM_FIT_FREE(p_irt);
	 
						/* free the idrp_route here that is able to be
						 * added to gated, if fixed
						 * - then the next re-parse will find it again.  
						 */

						}
					else
						{
						/* good route added to gated table */
	
						changes++; 
						IDRP_STATUS_BIT_TEST(p_irt,IDRP_STATUS_BEST_EXT)
							{
							p_irt->status = (IDRP_STATUS_LOCAL_RD | IDRP_STATUS_LOCAL_PH3_NEW_ROUTE | IDRP_STATUS_BEST_EXT);
							}
						else
							{
							p_irt->status = (IDRP_STATUS_LOCAL_RD | IDRP_STATUS_LOCAL_PH3_NEW_ROUTE );
							}
						}	
					} /* end if new local route */ 
				} /* end of for loop */
			    } NLRI_FAMILY_LOOP_END;
			} /* end of loop for route_id */
		} ATTR_LIST_END;

	/* close the task and gateway for IP, but ISO will have
	 * happened as well.  Since one open aught to be OK,
	 * we will let this kludge continue
	 * skh--- /3/17/93  
	 */
	rt_close(idrp_this_node.task,&idrp_this_node.gw,changes,NULL);
}

void
idrp_peer_ete_init(peer)
idrpPeer *peer;
{
	/* set-up all the ete initialization for a peer
	 * - set close state
	 * - reset all timers to global values
	 * - clear bad AdjRib flags
	 * - reset xmit and rexmit queues
	 * - init the hold table
	 * - init the route_out table
	 */
 
	peer->send_next = 1;
	peer->send_unack = 0;
	idrp_peer_ete_reinit(peer);

}

void
idrp_peer_ete_reinit(peer)
idrpPeer	*peer;
{
	/* jgs -- hack hack hack -- I think this timer reinit should go in the state machine, when we send
	 * jgs -- the start event, but there is some disagreement...
	 */
	 if (peer->task)
        	{
        	task_timer_reset(peer->idrp_closestay_timer);
        	 }
	/* jgs -- end hack */

        peer->state = IDRP_CLOSED;
	peer->rcv_credit = IDRP_MAX_CREDIT;
	peer->checksum = 0;
	peer->AdjRibBad = 0;

        /* check the queues to make sure that if anything is on it -
         * - release and then reset the pointers
         */
  
        flush_rexmit_queue(peer);
        flush_xmit_queue(peer);

	peer->xmit_queue_head = NULL;
	peer->xmit_queue_tail = &peer->xmit_queue_head;
	peer->rexmit_queue_head = NULL;
	peer->rexmit_queue_tail = &peer->rexmit_queue_head;

	/* initialize the hash table again */
	idrp_init_hash_tbl(peer);
}
 
/*
 * Pretty-print an ISO prefix.
 * %% Needs to be able to handle non-semi-octet-aligned prefixes!
 */

#define ISO_PTOA_LENGTH 61
#define hexchar(x) ((x)>9?(x)-10+'A':(x)+'0')

char *
iso_ptoa(prefix)
struct iso_prefix *prefix;
{
static char result[ISO_PTOA_LENGTH];
int resultix = 0;
int addrix;

	for (addrix = 0; addrix < PFX_BYTE_LENGTH(*prefix); addrix++) {
		if (addrix & 1) {
			if (resultix > ISO_PTOA_LENGTH - 4)
				break;
			result[resultix++] = '.';
		}
		if (resultix > ISO_PTOA_LENGTH - 3)
			break;
		result[resultix++] = hexchar((prefix->pfx[addrix] & 0xf0) >> 4);
		if (addrix == prefix->pfx_len/8 && ((prefix->pfx_len & 7) <= 4))
			break;
		result[resultix++] = hexchar(prefix->pfx[addrix] & 0x0f);
	}
	result[resultix++] = '\0';

	if (resultix == 1)
	  return ("Default");
    
	return(result);
}
 
#define hexchar(x) ((x)>9?(x)-10+'A':(x)+'0')

/*
 * isoaddrcmp() = 0, if addr1 == addr2
 *		  1, otherwise
 */
int 
isoaddrcmp(addr1, addr2)
struct iso_net_addr *addr1, *addr2;
{
	if (addr1->isoa_len != addr2->isoa_len)
		return(1);

	return(bcmp((caddr_t) addr1->isoa_genaddr, (caddr_t) addr2->isoa_genaddr, addr1->isoa_len));
}

int
isoaddr_netiso_cmp(addr1, addr2)
struct iso_addr *addr1;
struct iso_net_addr *addr2;
{
	if (addr1->isoa_len != addr2->isoa_len)
		return(1);

	return(bcmp((caddr_t) addr1->isoa_genaddr, (caddr_t) addr2->isoa_genaddr, addr1->isoa_len));
}

int
compare_iso_addr(addr1,addr2)
struct iso_net_addr *addr1, *addr2;
{
int len,i;

	/* min(addr1.isoa_len,addr2.isoa_len) */

	len = (addr1->isoa_len > addr2->isoa_len) ? addr2->isoa_len : addr1->isoa_len;

	for (i = 0; i < len; i++)
		{
		if (addr1->isoa_genaddr[i] != addr2->isoa_genaddr[i])
			{
			if ((u_int) addr1->isoa_genaddr[i] < (u_int) addr2->isoa_genaddr[i])
				{	
				return (ISO_ADDR_LESS_THAN);
				}
			else
				{
				return (ISO_ADDR_GREATER_THAN);
				}
			}
		}

	if (addr1->isoa_len == addr2->isoa_len)
		{
		return (ISO_ADDR_EQUAL);
		}
	if (addr1->isoa_len < addr2->isoa_len)
		{
		return (ISO_ADDR_LESS_THAN);
		}

	/* addr1 longer than address 2  - so greater than */

	return (ISO_ADDR_GREATER_THAN);
}


isopfxcompare(pfx1, pfx2)
struct iso_prefix *pfx1, *pfx2;
{
	int i;
	for (i = 0; i < MIN(PFX_BYTE_LENGTH(*pfx1), PFX_BYTE_LENGTH(*pfx2)); i++) {
		if (pfx1->pfx[i] < pfx2->pfx[i])
			return(-1);
		if (pfx1->pfx[i] > pfx2->pfx[i])
			return(1);
	}
	if (pfx1->pfx_len == pfx2->pfx_len)
		return(0);

	if (pfx1->pfx_len < pfx2->pfx_len)
		return(-1);
	else
		return(1);
}


/*
 * idrp_reinit - this is called after the configuration file has been
 * reread, and after idrp_init() has been called to start the master
 * task has been set-up to deal with the reception of  BISPDU packets
 *  from gated.
 * 
 * skh---- I cannot find anything specific to do for the master
 * skh--- task in task reinit. I'm sure I'll find something later.
 * skh 3/7/93 - once receive tasks for multiple sockets (ip /clnp) 
 * 
 * Peer changes are handled by the idrp_peer_reinit routine.
 * 
*/

/* note that for now - we have no concept of the grouping of 
 * idrp peers.  We will need to add this to keep up with BGP Q JONES 
 * the pride of the internet family 
 */
 
void
idrp_reinit(tp)
task *tp; /*  NOTUSED but needed for GateD */
{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp_reinit called, but null processing ")); 
	return;
} 

void
idrp_newpolicy(tp, rtl)
task *tp; /*  NOTUSED but needed for GateD */
rt_list	*rtl;
{
idrp_ann_list	*p_ann_list,*p_ann_delta;
idrpPeer	*peer;
int		newpolicy = TRUE;
int		rib_id;
#ifdef IDRP_ANSI_FIX
idrp_ann_list	*p_ann[IDRP_MAX_RIBS+1];
idrp_ann_list	*p_ann_qos_delta[IDRP_MAX_RIBS+1];
#else
idrp_ann_list	*p_ann[IDRP_MAX_RIBS+1] = {0,0,0,0,0,0,0,0,0,0};
idrp_ann_list	*p_ann_qos_delta[IDRP_MAX_RIBS+1] = {0,0,0,0,0,0,0,0};
#endif

	p_ann_list = (idrp_ann_list *) idrp_local_mem_fit(sizeof(idrp_ann_list));
	p_ann_list->peer = &idrp_this_node;
	

	/* New policy gets all the current routes after 
	 * refresh.  So, these routes should
	 * recreated out of kernel or local
	 * routes.  Run policy on them
	 * to see if we should send them  
	 */

	phase3_status_change(rtl,p_ann_list,newpolicy);

#ifdef	IDRP_QOS

	/* Get the announce list for all the QOS ribs
	 */
  
	QOS_RIB_ONLY_LIST(rib_id)
		{
		p_ann[rib_id] = qos_chg_active(rib_id,TRUE);
		} QOS_RIB_ONLY_LIST_END;

	{
	/* temporary dump to file of routes
	 * that will go to the kernel with the change
	 */
	FILE *fd = NULL;    /* qos attribute dump file */
	FILE *fdk = NULL;   /*kernel dump file */

	idrp_qos_attr_dump(fd,fdk);
	idrp_local_rib_dump_only(fd);
	} 

#endif /* IDRP_QOS */

/* no peers established so skip
 * the attempts to send
 */

	if (!idrp_n_established)
		{
		/*  Cleanup routes sent
		 * clear reconfigure flags in local routes
		 * - cleanup replace routes
		 * free routes awaiting the send
		 * free announce list
		 * - not setting min advertisement since no sending
		 */


		idrp_reconfig_rib(p_ann_list,RIB_ID_DEFAULT);

#ifdef	IDRP_QOS
		QOS_RIB_ONLY_LIST(rib_id)
			{
			idrp_reconfig_rib(p_ann_list,rib_id);
			} QOS_RIB_ONLY_LIST_END;
#endif
		return;
		}
	

	 
	IDRP_LIST(peer, idrp_peers)
		{
		if (peer->idrp_flags == IDRPF_CONNECT)
			{
			if (peer->refresh_flags == SEND_RIB_REFRESH_ON_HUP)
				{
#ifdef	IDRP_QOS
				idrp_refresh_peer(p_ann_list,peer,&p_ann,TRUE);
#else
					
				idrp_refresh_peer(p_ann_list,peer,&p_ann,FALSE);
#endif
				}			
			}
		} IDRP_LIST_END;

	/* scan the announce list for the idrp structures with
	 * the IDRP_STATUS_LOCAL_NEW_ROUTE (osilocal hack for now) 
	 * the IDRP_STATUS_RECONFIGURE (local iso and ip routes
	 * 
	 */

	p_ann_delta = ph3_newpolicy_deltas(p_ann_list);
	idrp_newpolicy_deltas(p_ann_delta,RIB_ID_DEFAULT);
 
#ifdef	IDRP_QOS
	QOS_RIB_ONLY_LIST(rib_id)
		{
		p_ann_qos_delta[rib_id] = ph3_newpolicy_deltas(p_ann[rib_id]);	
		idrp_newpolicy_deltas(p_ann_qos_delta[rib_id],rib_id);
		} QOS_RIB_ONLY_LIST_END;

#endif  /* IDRP_QOS */			

	idrp_set_minadv_timers();

}

void
idrp_newpolicy_deltas(p_ann_delta,rib_id)
idrp_ann_list	*p_ann_delta;
int		rib_id;
{
idrpPeer	*peer;

   /* do not send the rib if the
    * the peer does not support this rib   
    * 	
    */	  		


     if (p_ann_delta)
	{
	p_ann_delta = idrp_minadv_annlist(p_ann_delta,IDRP_MINADV_PHASE3);	
	
          /* if any delta routes still need to be sent
	      * - start sending them	
	      */
 		
	     if (p_ann_delta)
		{
		IDRP_LIST(peer, idrp_peers)
			{
			if (peer->idrp_flags == IDRPF_CONNECT)
				{
#ifdef	IDRP_QOS
				/* if qos enabled - test to see if send_update on hup and this rib supported
				 */

				if ((peer->refresh_flags == SEND_UPDATE_ON_HUP) &&
				    ((rib_id == 0) || ((rib_id > 0) && (peer->rib_supp[rib_id] != 0))))
#else

				if (peer->refresh_flags == SEND_UPDATE_ON_HUP)
#endif
					{
					idrp_send_ann_routes(peer,p_ann_delta,IDRP_DIST_NEWPOLICY_FLASH);
					}
				else
					{
					trace_tf(idrp_trace_options, TR_NORMAL,0,("idrp_newpolicy(): Not sending update to peer %s because SEND_UPDATE_ON_HUP isn't set",
						peer->name));
					}
				}
			} IDRP_LIST_END;

		  idrp_clean_replace_routes(p_ann_delta);
		  idrp_del_sent_routes(p_ann_delta);
		  free_ann_list(p_ann_delta);
		  }
	}

	return;
} 


/* processing if no peers are up for a rib
 */ 

void
idrp_reconfig_rib(p_ann_list,rib_id)
idrp_ann_list *p_ann_list;
int		rib_id;
{

	/* clear out the reconfig flag
	 */

	idrp_local_clear_reconfig(p_ann_list);
	if (p_ann_list->p_attr)
		{
		idrp_clean_replace_routes(p_ann_list);
		idrp_del_sent_routes(p_ann_list);
		}
	free_ann_list(p_ann_list);

}


void
idrp_reinit_ete_timers(peer)
idrpPeer	*peer;
{

	/* reset the tranport layer timers - to make sure the 	
	 * timers are reset - 
	 * I am not sure this is the right thing. (skh, 11/15/93) 
	 * If continue to have problems, we need to simply
	 * delete this.
	 */ 
 
	task_timer_reset(peer->idrp_keepalive_timer);
	task_timer_reset(peer->idrp_hold_timer);
	task_timer_reset(peer->idrp_closestay_timer);
	task_timer_set(peer->idrp_hold_timer, peer->hold_time, (time_t) 0);
	start_keepalive_timer(peer);
	kill_rexmit_timer(peer);
	
}

void
idrp_init_peer_tasks()
{
task 		*p_task;
idrpPeer	*peer;

	/* Now create the tasks for each peer.
	 * - task have timers for peer connections
	 *   include mininum route Advertisement timer
	 *   for that peer
	 * - RD minimum route advertisement timer is
	 *   on an RD basis.
	 */  

        IDRP_LIST(peer, idrp_peers) {

	    /* if task does not already exists */

            if (!peer->task) {
			trace_log_tf (idrp_trace_options,0, LOG_ERR, ("peer %s does not have a task peer = %x (%x)",peer->name,peer,peer->idrp_flags));

		/* initialize the configuration portion of
		 * the peer's structure
		 * - the can be configured after re-parse
		 */ 

		idrp_peer_config_init(peer);	


		/* initialize the ETE structure
		 * - this structure can be re-init
		 *   when you enable peer
		 */

		idrp_peer_ete_init(peer);	

		/* skh --- 3/7/93 do idrp task initialization here 
		 * the task is open for ip_intf
		 * if the proto_sock = clnp, this will have to change 
		 */ 

		p_task = peer->task = task_alloc("IDRP", TASKPRI_EXTPROTO, idrp_trace_options);
		p_task->task_addr = sockdup(peer->ip_intf_addr);
                p_task->task_data = (void_t) peer;
		sock2port(p_task->task_addr) = idrp_port;
                p_task->task_rtproto = RTPROTO_IDRP;
		peer->iso_gw.gw_task = p_task;
		peer->gw.gw_task = p_task;

		/* peer task routines  
		 * - all reception and minadv timers
		 *  are on the main routine.
		 */
		
		task_set_cleanup(p_task, idrp_peer_cleanup);
		task_set_dump(p_task,idrp_peer_dump);
		task_set_terminate(p_task,idrp_peer_terminate);
		task_set_reinit(p_task,idrp_peer_reinit);

		BIT_SET(p_task->task_flags,TASKF_LOWPRIO);

                if (!task_create(p_task)) {
                    task_quit(EINVAL);
                }


		/* timers */
		peer->idrp_keepalive_timer = task_timer_create(p_task,
				    "Keepalive",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_event_keepalivetimer,
				    (caddr_t) 0);
		peer->idrp_closewait_timer = task_timer_create(p_task,
				    "CloseWait",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_event_closetimer,
				    (caddr_t) 0);
		peer->idrp_hold_timer = task_timer_create(p_task,
				    "Holdtime",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_event_holdtimetimer,
				    (caddr_t) 0);
		peer->idrp_retransmit_timer = task_timer_create(p_task,
				    "Retransmit",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_event_rexmittimer,
				    (caddr_t) 0);

		if (peer->idrp_flags == IDRPF_IDLED)
			{
			/* do not start start timer  */
			peer->start_timer_running = FALSE;
			}
		else
			{
			/* start the start connection timer */

			peer->start_timer_running = TRUE;

			peer->idrp_start_timer = task_timer_create(p_task,
				    "Start",
				    0,
				    (time_t) IDRP_START_TIME,
				    (time_t) IDRP_START_TIME,
				    idrp_event_starttimer,
				    (caddr_t) 0);

			}
		peer->idrp_opensent_timer = task_timer_create(p_task,
				    "Open Sent",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_event_opensenttimer,
				    (caddr_t) 0);
		(void) task_timer_create(p_task,
				    "Mininum Route Advertisement ",
				    0,
				    (time_t) 0,
				    (time_t) 0,
				    idrp_process_minadv,
				    (caddr_t) 0);
		peer->idrp_closestay_timer = task_timer_create(p_task,
				    "Close State",
				    0,
				    (time_t) IDRP_START_TIME,
				    (time_t) IDRP_START_TIME,
				    idrp_event_closestaytimer,
				    (caddr_t) 0);


		}
	else
		{
		trace_log_tf (idrp_trace_options,0, LOG_ERR, ("peer %s does have a task peer = %x (%x)",peer->name,peer,peer->idrp_flags));
		
		/* task already exists for this peer 
		 * - we are in the midst of reparsing
		 * here's where the proto socket has
		 * to change if peer->old_proto_sock
		 * is different than peer->proto_sock 
		 * initialized one should be zero or IP 
		 */

		idrp_peer_config_reinit(peer);	

		}
        } IDRP_LIST_END;

}
