/* 
 * $Id: idrp_rt_policy.c,v 1.14 1995/11/06 16:30:20 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_policy.c - 
 * 
 *  routines that interact with policy to  calculate idrp
 *  route preferences, Do Distribution (DIST), and
 *  aggregation of IDRP routes
 * 
 *  1) preference calculation -
 *     PREF(p_idrp_rt,p_idrp_pref,p_gated_pref)
 *	- do import policy on the idrp - not in service now
 *     idrp_proto_pref (proto
 *     idrp_ext_info_rt
 *     gated import function - ??
 * 
 *  2) Distribution policy for routes:
 * 	DIST(p_idrp_rt,peer)
 *
 *  3) DIST_LIST_INCL and DIST_LIST_EXCL
 *	for attribute records:
 * 
 *	DIST_ATTR(p_att,peer) 
 *
 *  4) export policy for gated
 *
 * %% distribution policy routines  	 
 * 
 *  DIST(idrpRoute,peer) - run policy on a NLRI and peer 
 *  DIST_ATTR(p_att,peer) - run policy on Attribute list
 * 
 * 7) Any outbound aggregretion policy
 *    hopefull none of this will be idrp specific
 *
 *  8)  preference match mediation -
 *	idrp_vs_proto_pref(proto,peer)
 * 	given protocols with route with same preference
 *	as idrp route, should protocols route take
 *	preference for peer
 *	- use preference table in policy structures
 *	eventually, now hard coded routine for all peers	 
 */

	 
/* preference on a route */

int
PREF(p_idrp_rt, p_idrp_pref, p_gated_pref)
idrpRoute	*p_idrp_rt;
int		*p_idrp_pref;
pref_t		*p_gated_pref;
{
pref_t		 preference;
idrpRoute_info	idrp_route_info;
adv_entry	*p_import;
if_addr		*p_ifa;
int		use;

	/* set up for import call
	 *
	 * set the import list and interface
	 * based on the protocol
	 */
 
	p_import = p_idrp_rt->peer->idrp_import;

	/* set up idrp Route options 
	 * - don't set route option
	 */ 
	
	idrp_route_info.idrp_pref = IDRP_INVALID_ROUTE_PREF; 
	idrp_route_info.p_idrp_rt = p_idrp_rt;
	idrp_route_info.route_options = 0; 

	/*
	 *   	import call has:
	 *		1) destination
	 *			 sockaddr_un from rt_table
	 *
	 *		2) * peer pointer to import list
	 * 		3) int_list - is the  idrp rdi path list policy  
	 * 
	 * 		4) gw_list - gateway list is zero
	 *
	 *
	 * 		5) preference value to be returned
	 * 		
	 *		6)  ifap - if_addr *
	 *
	 *		interface this will be going out??
	 *
	 *		7) ps_data - specific data for idrp
	 *
	 *			idrpRoute_options 
	 *	
	 *
	 *		call:
	 *	 	dst-> from idrp routes pointer to gated rt_entry
	 *		import list -> from peer
	 *		import_paths -> from peer
	 *		gw_list -> no gateway list
	 *		preference - we set this up
	 *		
	 *		ps_data  -  idrpRoute_info - options plus spot
	 *			    to return idrp preference
	 */

	if (p_idrp_rt->peer->type == IDRP_PEER_EXTERNAL) {
		preference = RTPREF_IDRP_EXT;
	} else {
		preference = RTPREF_IDRP_INT;
		}
	use = import(p_idrp_rt->p_rt->rt_dest,
			p_idrp_rt->p_rt->rt_dest_mask,
			p_import,
			idrp_import_list_any_rdi,
			idrp_import_rdpath_list,
			&preference,
			(if_addr *) NULL,
			(void_t) &idrp_route_info);

	if (!use) {
		   trace_tf (idrp_trace_options, TR_NORMAL,0,("PREF set noinstall %s (%d,%d)",iso_ptoa(&p_idrp_rt->nlri),preference,idrp_route_info.idrp_pref));	
		preference = IDRP_GATED_INVALID_ROUTE_PREF;
		BIT_SET(p_idrp_rt->p_rt->rt_state, RTS_NOTINSTALL);
		} 
	else
		{
		/* if this is a change from invalid to valid
		 * over a new policy hop
		 * - then change the default route's install bit
		 * qos - does not install anyway
		 */ 
		 
		 if ((p_idrp_rt->gated_pref == IDRP_GATED_INVALID_ROUTE_PREF) &&
			p_idrp_rt->p_attr->rib_id == 0)
			{
		   	trace_tf (idrp_trace_options, TR_NORMAL,0,("PREF reset noinstall %s (%d,%d)",iso_ptoa(&p_idrp_rt->nlri),preference,idrp_route_info.idrp_pref));	
			BIT_RESET(p_idrp_rt->p_rt->rt_state, RTS_NOTINSTALL);
			idrp_mod_rt_gated(p_idrp_rt,IDRP_NORUN_PREF);
			}	
			
		/* if IDRP route preference is invalid, but marked
		 * use - set to default preference for type
		 */

		 if (idrp_route_info.idrp_pref == IDRP_INVALID_ROUTE_PREF) 
			{
			idrp_route_info.idrp_pref = preference;
			}
				
		} 
	/* 
	 * even if import() returns FALSE, the set-up of 
	 *
	 *	idrp_route_info.idrp_pref = IDRP_INVALID_ROUTE_PREF; 
	 * 
	 *
	 * and return of
	 *
	 *	preference = IDRP_GATED_INVALID_ROUTE_PREF (255) 
	 *
	 * will make things work out fine.
	 */

	*p_idrp_pref  = (int) idrp_route_info.idrp_pref;
	*p_gated_pref =  preference; 
	return(*p_idrp_pref);
} 



int 
idrp_proto_pref(proto_rcv,peer)
u_int		proto_rcv;
idrpPeer	*peer;
{
	switch (proto_rcv)
		{
		case RTPROTO_IDRP:
		   trace_tf (idrp_trace_options, TR_NORMAL,0,("idrp_vs_proto_pref called with IDRP as protocol - peer %s", peer->name));	
		   return(FALSE);	

		case RTPROTO_BGP:
			return (TRUE);
			break;

		case RTPROTO_ISIS:
			default:
			return (FALSE);
			break;
		}
}


/* idrp_ext_info_pref (rt_entry *p_rt)
 * 		ext_info preference for idrp
 *
 */
 
int
idrp_ext_info_pref(p_rt)
rt_entry	*p_rt;
{
proto_t	proto;
int	pref = 0;

	/* here's wehere we check to see if we can export this protocol 
	 * - We are currently exporting
	 *	Static - and associated the default attributes
	 *		for local node if the exterior flag is not set (IDRP_EXT_INFO_IGP)
	 *		for ext_info - if the exterior flag is set (IDRP_EXT_INFO 
	 * 
	 *	ISIS - and associating the exterior information	 
	 */ 

	switch (p_rt->rt_gwp->gw_proto)
		{
		case RTPROTO_STATIC:
			if (is_martian(p_rt->rt_dest,p_rt->rt_dest_mask))
				{
				/* not valid for this protocol) 
				 */
				return(pref);
				}
			if (p_rt->rt_state & RTS_EXTERIOR)
				{
				return (IDRP_EXT_INFO_EXT_INFO);
				}
			else
				{
				return(IDRP_EXT_INFO_IGP);
				}
			break;
			

		case RTPROTO_ISIS:
			/* IS-IS routes
			 * - need to test RTS_EXTERIOR
			 *   if set EXT_INFO route
			 *   if not set, IGP route
			 */
 
			if (p_rt->rt_state & RTS_EXTERIOR)
				{
				/* check to see that it is not
				 * my bis 
				 */
				if (!my_bis_peer(p_rt->rt_gwp))
					{
					/* not my peer - so announce the route */
					pref = IDRP_EXT_INFO_EXT_INFO;
					}
				}
			else
				{
				pref = IDRP_EXT_INFO_IGP;
				}				
			
			return(pref);
			break;

		case RTPROTO_IDRP:
			trace_tf (idrp_trace_options, TR_NORMAL,0,("Illegal protocol interactions - IDRP should not call ext_info pref"));
			return (pref);
			break;
		}
	return (pref);
}

int
DIST(p_idrp_rt,peer,type)
idrpRoute	*p_idrp_rt;
idrpPeer	*peer;
int		type;
{
  trace_tf(idrp_trace_options, TR_NORMAL,0,("DIST nlri %s (peer rt %s) peer %s type = %d",iso_ptoa(&p_idrp_rt->nlri),p_idrp_rt->peer->name ,peer->name,type)); 

	switch(type)
		{
		case IDRP_DIST_PEER_UP_DUMP:
		case IDRP_DIST_REFRESH:
			if (!DIST_ATTR(p_idrp_rt->p_attr,peer))
				{
				return(FALSE);
				}
		

		case IDRP_DIST_FLASH:
		case IDRP_DIST_NEWPOLICY_FLASH:
		case IDRP_DIST_PH1_BEST_EXT:
			/* here are simple distribution stuff
			 *
			 */

			if (peer->type == IDRP_PEER_INTERNAL)
				{
				/* don't send any internal routes
				 * to another peer
				 * - cut out redistribution here
				 * - until yes/no/maybe clause done
				 *
				 */

				if (p_idrp_rt->peer->type == IDRP_PEER_INTERNAL)
					{
					return(FALSE);
					}
				}

			if (peer == p_idrp_rt->peer)
				{
				/* Do not send routes back to peer we learned
				 * them from.  A bad idea, although BGP does it
					 */ 
				return (FALSE);
				}	
			break;

		case IDRP_DIST_LOCAL_FLASH:
			/* we want to distribute local routes to internal peers
			 * since we won't hear them in phase 1 
			 */
			if ( ! ((p_idrp_rt->peer->type == IDRP_PEER_LOCAL) && (peer->type == IDRP_PEER_INTERNAL)))
				{
				/* we only want to send to internal peers when we're
				 * using IDRP_DIST_LOCAL_FLASH
				 */
				return (FALSE);
				}
			break;
		}
	return(TRUE);
}

int
DIST_WITH(p_idrp_rt,peer,type)
idrpRoute	*p_idrp_rt;
idrpPeer	*peer;
int		type;
{
	switch(type)
		{
		case IDRP_DIST_LOCAL_FLASH:
			/* we only want to distribute w/draws of local routes to internal
			 * peers, since they will have heard all other w/draws in phase 1.
			 * Everyone else gets all w/draws.
			 */
			if ( ! ((p_idrp_rt->peer->type == IDRP_PEER_LOCAL) && (peer->type == IDRP_PEER_INTERNAL)))
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("DIST_WITH peer %s nlri %s and not allowing DIST_LOCAL_FLASH",iso_ptoa(&p_idrp_rt->nlri),peer->name)); 
			return(FALSE);
			}
			trace_tf(idrp_trace_options, TR_NORMAL,0,("DIST_WITH nlri %s peer %s and allowing DIST_LOCAL_FLASH",iso_ptoa(&p_idrp_rt->nlri),peer->name)); 
			break;

		default:
			trace_tf(idrp_trace_options, TR_NORMAL,0,("DIST_WITH peer not DIST_LOCAL_FLASH"));
			break;
		}

	return(TRUE);
}


idrp_attribute_record *
DIST_ATTR(p_att,peer)
idrp_attribute_record	*p_att;
idrpPeer		*peer;
{
idrp_attribute_record *p_valid_att = 0;

	/* attribute list will be modified for this peer
	 * check for the RDI in the DIST_LIST_INCL
	 * or DIST_LIST_EXCL parameter
	 */
	
	if (p_att->idrp_mask & IDRP_ATTBT_DIST_LIST_INCL)
		{

			
		trace_tf (idrp_trace_options, TR_POLICY,0,("DIST_ATTR called with DIST_LIST_INCL in pdu peer = %s\n",peer->name));
		if (rdi_in_dist_list(&p_att->attrib[IDRP_ATTR_DIST_LIST_INCL],&peer->rdi))
			{
			p_valid_att = p_att;
			}
		}
	else
		{
		if (p_att->idrp_mask & IDRP_ATTBT_DIST_LIST_EXCL)
			{
			trace_tf (idrp_trace_options, TR_POLICY,0,("DIST_ATTR called with DIST_list_EXCL in pdu, peer = %s\n",peer->name));
				
			if (!rdi_in_dist_list(&p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL],&peer->rdi))
				{
				p_valid_att = p_att;
				}
			}
		else
			{
			p_valid_att = p_att;
			}
		} 

			
	trace_tf (idrp_trace_options, TR_POLICY,0,("DIST_ATTR (%x)  returns with p_att = %x\n",p_att,p_valid_att));
			
	return (p_valid_att);
}

void
AGGR(peer,p_ann_list)
idrpPeer	*peer;
idrp_ann_list	*p_ann_list;
{
	trace_tf (idrp_trace_options, TR_NORMAL,0,("AGGR called to aggregated for peer %s ",peer->name));
}


/* check to see if this is a new route
 */

int
idrp_new_route(p_idrp_rt)
idrpRoute       *p_idrp_rt;
{
int cnt = 0;
idrpPeer	*peer;

	IDRP_LIST(peer, idrp_peers)                        
		{
		/* if any of the routes can be found outbound */ 
		if (peer->idrp_flags == IDRPF_CONNECT)
			{
			/* only process peers that our connected 
			 * -less errors more routes
			 */

			if (p_idrp_rt->route_out[peer->id].p_next)
				{
				/* found a route - it isn't a new route
				 */
				return(FALSE);
				}


			/* increment the count of the  active peers
			 * if we hit it, quit.  Dont keep looping.
			 */

			cnt++;
			if (cnt > idrp_n_established)
				{
				return(TRUE);
				}
			}
		} IDRP_LIST_END
	return(TRUE);
}




int
idrp_dist_policy_change(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
idrpRoute_options *p_opts = (idrpRoute_options *) NULL;
boolean		was_announced = FALSE;
idrpPeer	*peer;
int		res;
adv_results	result;	
 
	/* check the distribution policy change
	 * for the route
	 * in the gated source
	 * - This may be a withdrawl or anything else
	 *
	 */


	IDRP_LIST(peer,idrp_peers)
		{	
		if (p_idrp_rt->route_out[peer->id].route_id < 0)
			{
			was_announced = TRUE;
			p_opts = p_idrp_rt->p_export_opts;
			}

		res =  export(p_idrp_rt->p_rt,
			(proto_t) 0,
			 peer->idrp_export,
			(adv_entry *) 0,
			(adv_entry *) 0,
			&result);


		/* if the policy from a peer changes from 
		   SJR this line and a closing of the comment
		   were missing!
		 */

		if (res == FALSE && was_announced == TRUE)
			return(TRUE);

		if (res == TRUE &&  was_announced == FALSE)
			return(TRUE); 

		/* simplistic comparison for now
		 */

		if (p_opts != p_idrp_rt->p_export_opts)
			return(TRUE);
		}

	return(FALSE);
}




int
idrp_route_export(p_idrp_rt,peer)
idrpRoute *p_idrp_rt;
idrpPeer  *peer;	
{
idrpRoute		*p_irt;
idrp_attribute_record	*p_att;
adv_results		result;	
boolean			new_idrp_attribute = FALSE;
idrp_attr_data_entry	*p_xmit_attr[IDRP_XMIT_ATTR_NO];
u_int			omask = 0;
u_char			*p_data,*p_data_start;
int			type = 0;
int			status;
int			i;


	p_att = p_idrp_rt->p_attr;
	bzero(&result,sizeof(adv_results));

	/* clean-up on the previous export information for this peer 
	 */

	status = export(p_idrp_rt->p_rt,
		(proto_t) 0,
		 peer->idrp_export,
		(adv_entry *) 0,
		(adv_entry *) 0,
		&result);

	if (!status)
		{
		/* idrp protocol says send everything to
		 * internal peers.   This code will
		 * need to be smarter yet if we combine
		 * IDRP routes into a domain with aggregation
		 *  or other policy considerations.
		 */ 

		if (peer->type == IDRP_PEER_INTERNAL)
			return(IDRP_EXPORT_VALID);	
		
		return(IDRP_EXPORT_INVALID);
		}
			


	/* 1) allocate new pointers for the 
	 * the changable attributes
	 * 
	 * 2) allocate data space for the changed attributes
	 */     
	 
	for (i = 0; i < IDRP_XMIT_ATTR_MAX ; i++)
		{
		p_xmit_attr[i] = (idrp_attr_data_entry *) idrp_local_mem_fit(sizeof(idrp_attr_data_entry));
		}

	
	p_data_start = p_data = idrp_local_mem_fit(IDRP_OATT_PDU_SPACE);	

	/* Do we need to create a new idrp_attribute
	 */
	
	if (p_idrp_rt->gated_pref != result.res_metric)
		{
		 trace_tf (idrp_trace_options, TR_NORMAL,0,("metric from IDRP export != %s (i:%d,e:%d)", \
			 peer->name,p_idrp_rt->gated_pref,result.res_metric));	
		}	

	if (p_idrp_rt->p_export_opts == (idrpRoute_options *) NULL)
		{
		for (i = 0; i < IDRP_XMIT_ATTR_MAX ; i++)
			{
			IDRP_MEM_FIT_FREE(p_xmit_attr[i]);
			}
		return (IDRP_EXPORT_VALID);
		}


	/* check the qos values */	
	if (p_idrp_rt->p_export_opts->p_qos != (idrp_distinguish_att *) NULL )
		{
		/* QOS has changed -- this is illegal in IDRP
		 */
		  trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP export policy tries to change qos  peer %s", peer->name));	

		}
		

	/* New idrp_attribute required for export of route
	 * - check to see what is changed
	 * 1) if the next_hop changes - 
	 * 	cannot change next_hop - location
	 *
	 * 2) status 
	 * 	2.1) MULTI_EXIT - on, values set 
	 * 	 skh - this can be linked unto the current route and accessed
	 * 	 skh    via the send_multi_exit routines,
	 * 	 skh --> no further processing, pick up in send_multi_exit
	 *	
	 * 	2.2) ROUTE_SERVER  - values handled by send_next_hop 
	 *	  -- send_next_hop -> attend to peer 	
	 *
	 *	2.2) EXT_INFO - shouldn't be set here 
	 *   	2.3) HEIR_REC - not supported yet (export/import) 
	 *	   -- here we need check  
	 * 3) values	
	 *	Capacity - if changes -> see new attribute 	
	 * 	QOS - should stay the same
	 * 
	 */


	if (p_idrp_rt->p_export_opts->status != p_idrp_rt->p_attr->p_opts->status)
		{
		/* change in status - see what changed
		 */
		  trace_tf (idrp_trace_options, TR_NORMAL,0,("IDRP export policy status different peer %s (old,new)(%x,%x)", 
				peer->name,p_idrp_rt->p_export_opts->status,p_idrp_rt->p_attr->p_opts->status));	
		}		

	if ((p_idrp_rt->p_export_opts->status & IDRP_OPTS_MULTI_EXIT) ||
	    (p_idrp_rt->p_attr->p_opts->status & IDRP_OPTS_MULTI_EXIT))
		{
		/* multi_exit disc - needs a new attribute record 
		 * 
		 */
		if (fill_multi_exit(p_idrp_rt->p_attr,peer,p_xmit_attr[IDRP_XMIT_MULTI_EXIT_DISC]))
			{
			omask |= IDRP_OATT_MULTI_EXIT_DISC; 
			}
			
		}

	omask |= idrp_confed_change(p_idrp_rt,peer,p_xmit_attr[IDRP_XMIT_HIER],p_xmit_attr[IDRP_XMIT_RDPATH],&p_data,&status);
	
	if (status ==IDRP_UPDATE_ERROR_MISCON_RDCS)
		{
		/* we have an error that must be passed up the
		 * return code for this
		 * - peer, send an ERROR message and then
		 *   cut the connection - abort any further link list on this
		 *   peer 
		 */
	
		idrpPeer	*route_peer = p_idrp_rt->peer;  

		route_peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR;
                route_peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MISCON_RDCS;
                route_peer->last_error_pdu.data = (u_char *) 0;
                route_peer->last_error_pdu.data_len = 0;
                route_peer->last_error_pdu.data2 = (u_char *) 0;
                route_peer->last_error_pdu.data2_len = 0;
		idrp_sm(route_peer,IDRP_UPDATE_ERROR);	

		for (i = 0; i < IDRP_XMIT_ATTR_MAX ; i++)
			{
			IDRP_MEM_FIT_FREE(p_xmit_attr[i]);
			}
		return(IDRP_EXPORT_ERROR);
		}
		
	/* check to see if the distribution list 
	 * needs to be changed
	 */
	omask |= export_dist_list(p_idrp_rt,peer,&type);

	if (omask == 0)
		{
		assert(p_idrp_rt->route_out[peer->id].p_att);
		for (i = 0; i < IDRP_XMIT_ATTR_MAX ; i++)
			{
			IDRP_MEM_FIT_FREE(p_xmit_attr[i]);
			}
		return(IDRP_EXPORT_VALID);
		}

	/* create the attribute record
	 *  then check copy over the multi_exit 
	 */ 

	p_att = create_idrp_out_att(p_idrp_rt,peer);
	p_att->local_mask = omask; 


	/* Now test modify the outbound attribute
	  * to link to the used outbound attribute records  
	  */
	  
	if (p_att->local_mask & IDRP_OATT_MULTI_EXIT_DISC)
		{
		/* link in the multi_exit attribute  */
		bcopy((caddr_t *) &p_xmit_attr[IDRP_XMIT_MULTI_EXIT_DISC],(caddr_t *)&p_att->attrib[IDRP_ATTR_MULTI_EXIT_DISC],sizeof(idrp_attribute_entry));
		p_att->idrp_mask |= IDRP_ATTBT_MULTI_EXIT_DISC;	
		}
	else
		{
		IDRP_MEM_FIT_FREE(p_xmit_attr[IDRP_XMIT_MULTI_EXIT_DISC]);
		}

	if (omask & (IDRP_OATT_RDPATH_CONFED | IDRP_OATT_RDPATH_CONFED_POL)) 
		{
		bcopy((caddr_t *)&p_xmit_attr[IDRP_XMIT_HIER],(caddr_t *) &p_att->attrib[IDRP_ATTR_HIERARCH_REC],sizeof(idrp_attribute_entry));
		bcopy((caddr_t *)&p_xmit_attr[IDRP_XMIT_RDPATH],(caddr_t *) &p_att->attrib[IDRP_ATTR_RD_PATH],sizeof(idrp_attribute_entry));
		if (p_xmit_attr[IDRP_XMIT_HIER]->present)
			{
			p_att->idrp_mask |= IDRP_ATTBT_HIERARCH_REC;	
			}
		}
	else
		{
		IDRP_MEM_FIT_FREE(p_xmit_attr[IDRP_XMIT_HIER]);
		IDRP_MEM_FIT_FREE(p_xmit_attr[IDRP_XMIT_RDPATH]);
		}

			
	if (omask & (IDRP_OATT_DIST_LIST_NEW || IDRP_OATT_DIST_LIST_EXIST))
		{	
		/* need to test the type of distribution list
		 */

		fill_DIST_LIST(p_att,p_att->p_opts->p_DIST_LIST[peer->id],type);
		}

	send_next_hop(p_idrp_rt->p_attr,peer,&p_att->attrib[IDRP_ATTR_NEXT_HOP]);	

	/* set the attribute in the idrpRoute
	 */
 
	p_idrp_rt->route_out[peer->id].p_att = p_att;
	link_out_att(p_att);
	return(IDRP_EXPORT_VALID);	

}



int
export_dist_list(p_idrp_rt,peer,type)
idrpRoute	*p_idrp_rt;
idrpPeer	*peer;
int		*type;
{
idrp_canon_rdpath	*p_incl; 
idrp_canon_rdpath	*p_excl; 
idrp_canon_rdpath	*p_can;
idrp_canon_rdpath	*p_can_find;
idrp_canon_rdpath	*p_rdi_list;
idrp_canon_rdpath	*p_rdi_last;
idrp_canon_rdpath	*p_can_next;
idrpRoute_options	*p_export_opt;
idrpRoute_options	*p_opt;

	p_opt = p_idrp_rt->p_attr->p_opts;
	/* check the distribution lists
	 */

	p_export_opt = p_idrp_rt->p_export_opts; 
	p_incl = p_export_opt->p_DIST_LIST_INCL;
	p_excl = p_export_opt->p_DIST_LIST_EXCL;

	/* distribution lists have changed
	 * walk through and create a new distribution list
	 * based on the intersection of the two lists
	 * - put the list for now back in the orginal idrpRoute_options
	 * - if two policies - exclude takes precedence over include
	 *  
	 */
				

	if ((p_excl == (idrp_canon_rdpath *) NULL  ||
	    p_incl == (idrp_canon_rdpath *) NULL))
		{
		return(IDRP_OATT_DIST_LIST_NOCHG);
		}

	/* the distribution lists are originally null
	 */
 

	if ((p_opt->p_DIST_LIST_INCL == NULL) ||
	    (p_opt->p_DIST_LIST_EXCL == NULL))
		{
		/* simply copy the 
		 * rdi list to the peer location
		 * - if there are two use the include list
		 */

		p_rdi_list  = copy_canon_rdi_list(p_excl);
		p_rdi_list  = copy_canon_rdi_list(p_incl);
		}

	/* case 1)  DIST_LIST_INCL & p_incl - make sure 
	 *  -> include list has intersection of both
	 *  -> rdi has to be in both lists
	 *
	 * case 2) DIST_LIST_INCL & p_excl 
	 * 	    - take out of the include list the excluded RDIs
	 * 	
	 * case 3) DIST_LIST_EXCL & p_incl - ignore policy
	 * 	  flag ignoring policy
	 * 
	 * case 4) DIST_LIST_EXCL & p_excl 
	 * 	 add excludes to the routes 
	 */	

	if (p_opt->p_DIST_LIST_INCL != (idrp_canon_rdpath *) NULL)
		{
		*type = IDRP_ATTR_DIST_LIST_INCL;
		/* copy the rdi_list from the idrp_attribute 
		*/

		p_rdi_list  = copy_canon_rdi_list(p_opt->p_DIST_LIST_INCL);

		/* walk the list of routes,
		 * pull out any on the excluded list
		 * pull out any not found on the inclusion list 
		 */
	
		if (p_excl != (idrp_canon_rdpath *) NULL)
			{
			RDPATH_LIST(p_excl,p_can)
				{
				p_can_find = find_global_rdi_rdilist(p_can->p_rdi,p_rdi_list);
				exclude_global_rdilist(p_can_find,&p_rdi_list);
				} RDPATH_LIST_END;
			}
		
		if (p_incl != (idrp_canon_rdpath *) NULL)
			{
			/* look for each rdi in the include list
			 */
	
			for(p_can = p_rdi_list;p_can; p_can = p_can_next)
				{
				p_can_find = find_global_rdi_rdilist(p_can->p_rdi,p_incl);
				if (p_can_find == (idrp_canon_rdpath *) NULL)
					{
					/* didn't find RDI in the include list
					 * drop it out of the rdi list
					 */
					p_can_next = p_can->p_next;
					exclude_global_rdilist(p_can,&p_rdi_list);
					}
				else
					{
					p_can_next = p_can->p_next;
					}
				}
			}

		}
						 
	if (p_opt->p_DIST_LIST_EXCL != (idrp_canon_rdpath *) NULL)
		{
		*type = IDRP_ATTR_DIST_LIST_EXCL;
		p_rdi_list  = copy_canon_rdi_list(p_opt->p_DIST_LIST_EXCL);
		if (p_incl)
			{	
			trace_tf (idrp_trace_options, TR_NORMAL,0,("include policy and DIST_LIST_EXCL in attribute, ignore - peer %s", peer->name));	
			}
		if (p_excl)
			{
			/* get end of rdi_list
			 */
			RDPATH_LIST(p_rdi_list,p_can)
				{
				p_rdi_last = p_can;
				} RDPATH_LIST_END; 		
	
			RDPATH_LIST(p_excl,p_can)
				{
				/* find the excluded RDI in the list
				 */
				p_can_find = find_global_rdi_rdilist(p_can->p_rdi,p_rdi_list);

				/* if not there, add it 
				 */

				if (p_can_find == (idrp_canon_rdpath *) NULL)
					{
					p_rdi_last = idrp_add_end_rdilist(p_can->p_rdi,p_rdi_last);
					}
				} RDPATH_LIST_END;
			}	
		/* copy the new rdi list to the idrp route options 
		 * for that peer, attribute record 
		 */ 

		}

	if (p_opt->p_DIST_LIST[peer->id])
		{
		/* something was there last time
		 */

		if (same_dist_list(p_opt->p_DIST_LIST[peer->id],p_rdi_list))
			{
			/* same list - free the list 
			 */	
		
			free_dist_list(p_rdi_list);
			return(IDRP_OATT_DIST_LIST_EXIST);
			}
		else
			{
			free_dist_list(p_opt->p_DIST_LIST[peer->id]);
			p_opt->p_DIST_LIST[peer->id] = p_rdi_list;
			}	
		}
	else
		{			
		p_opt->p_DIST_LIST[peer->id] = p_rdi_list;
		}
	return(IDRP_OATT_DIST_LIST_NEW);	


}


int
idrp_confed_change(p_idrp_rt,peer,p_hier,p_rdpath,p_data,p_status)
idrpRoute		*p_idrp_rt;
idrpPeer		*peer;
idrp_attr_data_entry	*p_hier;
idrp_attr_data_entry	*p_rdpath;
u_char			**p_data;
int			*p_status;	
{
int	status;
int	mask = 0;
u_char	*p_dat = *p_data;
rdpath_list	*p_rdc_path = 0;

	/* Process confederation change only if
	 * the peer is external (7.12.3.3) 
	 * with different rdcs (7.12.3.3 a)
	 */  	

	if (peer->type != IDRP_PEER_EXTERNAL || 
		(peer->no_rdc_diff == 0))
		{	
		return(0);
		}

	status = confed_chk(p_idrp_rt,peer,&p_rdc_path,p_dat);
	if (status == IDRP_UPDATE_ERROR_MISCON_RDCS)
		{
		/* return: status of error on this peer
		 *  up the chain 
		 */
		*p_status  = status; 
		return(0);
		}

	fill_rdc_path(p_idrp_rt,peer,p_rdc_path,p_hier,p_rdpath,p_dat);
	return(IDRP_OATT_RDPATH_CONFED);	
	
}


int
confed_chk(p_idrp_rt,peer,p_rdc_opath,p_data)
idrpRoute		*p_idrp_rt;
idrpPeer		*peer;
rdpath_list		**p_rdc_opath;
u_char			*p_data;
{
int cnt = 0 ;
int status = 0;	
idrp_attribute_record 	*p_att;
rdpath_list		*p_rd_opath;
rdpath_list		*p_rdc_path;
rdpath			*p_rdp;
rdi_array		*p_array;
rdc_check		rdc_diff[IDRP_MAX_RDCS];	
rdc_check		rdc_not_diff[IDRP_MAX_RDCS];	
rdc_path_chk		path[IDRP_MAX_PATH_SEG];

/* variables for the path parsing
 */

int			no_rdc_not_diff;
int			no_rdc_diff;
int			irdc = 0;
int			irdi = 0;
int			pseg_cnt = 0;
int			global_rdi_cnt = 0;
int			pathseg_rdc_cnt = 0;
int			array_cnt = 0;
int			array_rdicnt = 0;
int			rdc_status = 0;
int			rdc_local_status = 0;

	p_att = p_idrp_rt->p_attr;
	p_rd_opath = p_att->p_rd_opath;
	p_rdc_path = *p_rdc_opath;

	no_rdc_not_diff = peer->no_rdc - peer->no_rdc_diff;
	no_rdc_diff = peer->no_rdc_diff;

	/* confederations are different
         *	
	 * NESTing order of RDC
	 * outer most RDC --> inner most nesting RDC
	 * within same level - numerical ordering  
	 *
	 * 
         * processing algorithm (7.12.3.3 b)
	 *
	 * 1) forward processing check for RDC existing in the
	 *    path sequence:
	 * 
	 *   build an array of the location of different
	 * 	RDCs (rdc_proc)
	 *	1) path segment pointer
	 *	2) array pointer
	 *	3) array location within array pointer (cnt)
	 * 	4) count within full path
	 * 	5) global pointer for rdi
	 *
	 */

	for (irdc = 0; irdc < peer->no_rdc_diff; irdc++)
		{
		rdc_diff[irdc].p_rdi = peer->rdc_diff[irdc].p_rdi;
		rdc_diff[irdc].nest_level = peer->rdc_diff[irdc].nest_level;     
		rdc_diff[irdc].status = RDC_CHK_STATUS_NOTFOUND;
		}

	for (irdc = 0; irdc < peer->no_rdc; irdc++) 	
		{
		rdc_not_diff[irdc].p_rdi = peer->rdc_not_diff[irdc].p_rdi;
		rdc_not_diff[irdc].nest_level = peer->rdc_not_diff[irdc].nest_level;     
		rdc_not_diff[irdc].status = RDC_CHK_STATUS_NOTFOUND;
		}

	/* set the sequence count of path segments to zero 
	 * set the global rdi count to zero
	 */

	pseg_cnt = 0;
	global_rdi_cnt = 0;

	IDRP_RDPATH_LIST(p_rdp,p_rd_opath->p_start)
		{
		status = (p_rdp->status & IDRP_RDPATH_TYPE);
		p_array = &p_rdp->rdi_list;
		array_cnt = 0;
		path[pseg_cnt].p_path = p_rdp;
		pathseg_rdc_cnt = 0;
		for (array_rdicnt = 0;  array_rdicnt < p_rdp->cnt; array_rdicnt++)
			{
			global_rdi_cnt++;
			irdi = array_rdicnt%IDRP_RD_PATH_ARRAY_SIZE;

			if (irdi == 0 & array_rdicnt != 0)
				{
				array_cnt++ ;
				p_array = p_array->p_next;
				assert(p_array != NULL);
				}

			/* walk through the rdis in this path element
			 * - looking to see if there are any rdcs
			 * - in the rdi array - either in the differences rdcs
			 */

			rdc_status = 0;	
			for (irdc = 0; irdc < no_rdc_diff; irdc ++)
				{
				if (p_array->p_rdi_array[irdi] == rdc_diff[irdc].p_rdi)
					{
					if (rdc_diff[irdc].status)
						{
						/* error in RDC path - return with
						 * - misconfigured RDCs
						 */

						return(IDRP_UPDATE_ERROR_MISCON_RDCS);
						}
					rdc_status = rdc_diff[irdc].status = RDC_CHK_STATUS_FOUND;
					rdc_diff[irdc].array_cnt = array_cnt;
					rdc_diff[irdc].aseq_cnt = irdi; 	
					rdc_diff[irdc].pseq_cnt = pseg_cnt;
					rdc_diff[irdc].rdi_cnt = global_rdi_cnt;	
					path[pseg_cnt].rdi_index[pathseg_rdc_cnt] = irdc;
					pathseg_rdc_cnt++;
					break;
					}
				} 
			
			/* loop through RDCs that are the same between this node and
			 * the remote node
			 */
	
			if (!rdc_status)
				{		
				for (irdc = 0; irdc < no_rdc_not_diff; irdc++)
					{
					if (p_array->p_rdi_array[irdi] == rdc_not_diff[irdc].p_rdi)
						{
						if (rdc_not_diff[irdc].status != RDC_CHK_STATUS_NOTFOUND)
							{
							/* error in RDC path - return with
							 * - misconfigured RDCs
							 */
	
							return(IDRP_UPDATE_ERROR_MISCON_RDCS);
							}
						rdc_local_status = rdc_not_diff[irdc].status = RDC_CHK_STATUS_FOUND;
						rdc_not_diff[irdc].array_cnt = array_cnt;
						rdc_not_diff[irdc].aseq_cnt = irdi; 	
						rdc_not_diff[irdc].pseq_cnt = pseg_cnt;
						rdc_not_diff[irdc].rdi_cnt = global_rdi_cnt;	
						break;
						}
					}
				} 
			
			
	
			/* process the list based on the type of entry
			 * 
			 */

  	
			switch (status)
				{	
				case IDRP_RDPATH_RDC_ENTRY_SEQ:
					/* now we have an RDI marked in RDC that has entry seq
					 */
					if (rdc_status)
						{
						rdc_diff[irdc].status |= RDC_CHK_STATUS_RDC_ENTRY_SEQ; 
						}
					break;
					
				case IDRP_RDPATH_RDC_ENTRY_SET:
					/* now we have an RDI marked in RDC that has entry set
					 */
					if (rdc_status)
						{
						rdc_diff[irdc].status |= RDC_CHK_STATUS_RDC_ENTRY_SET; 
						}
					break;
					

				case IDRP_RDPATH_SET:
				case IDRP_RDPATH_SEQ: 
				default:
					break;	
				}
			
			}
		path[pseg_cnt].cnt = pathseg_rdc_cnt;
		pseg_cnt++;  
		} IDRP_RDPATH_LIST_END;


	/* check the RDCs to see (7.12.3.3 b) paragraph I and II 
	 *     
	 * 1) if all the exited  RDCs are in the rdpath ENTRY_SET/ENTRY_SEQ segments 
	 * 2) nesting order    
	 */	

	for (irdc = 0; irdc < no_rdc_diff; irdc++)
		{
		if ((rdc_diff[irdc].status & RDC_CHK_STATUS_FOUND) == 0)
			{
			return(IDRP_UPDATE_ERROR_MISCON_RDCS);
			}
	
 		if (rdc_diff[irdc].status & (RDC_CHK_STATUS_RDC_ENTRY_SET | RDC_CHK_STATUS_RDC_ENTRY_SEQ) == 0)
			{
			return(IDRP_UPDATE_ERROR_MISCON_RDCS);
			}
			
		/* if more than 1 RDC exiting
		 * type of path sequence is SEQUENCE
		 * same path sequence between two rdcs
		 * and RDC B is nested within RDC A 
		 * - check that the order within path is RDC A precedes RDC B
		 */	

		if ((irdc >= 1) && (rdc_diff[irdc].status & RDC_CHK_STATUS_RDC_ENTRY_SEQ != 0)
			&& (rdc_diff[irdc].pseq_cnt == rdc_diff[irdc-1].pseq_cnt))
			{
			/* we have more than one RDC in the ENTRY SEQUENCE
			 * RDC B = nest level 2 
			 * RDC A = nest level 1 
			 * order within the pathway needs to be 
			 * RDC A  
			 */

			if (rdc_diff[irdc].nest_level != rdc_diff[irdc-1].nest_level)
				{
				if (rdc_diff[irdc].rdi_cnt <= rdc_diff[irdc-1].rdi_cnt)
					{
					/* if not in the right order - error
					 */
 
					return(IDRP_UPDATE_ERROR_MISCON_RDCS);
					}
				}
			}
		}
		

	/* now walk through the path creating a new path given
	 * the following things:
	 * 
	 * 1) rdc_chk array for those things we are exiting
	 * 2) original pathway count
	 *
	 * create an alternate path for the rdc and insert
	 * in the global rd_path sequences	  	 
	 */
   
	p_rdc_path = create_rdc_rdpath(no_rdc_diff,rdc_diff,pseg_cnt,path,p_rd_opath,p_data,peer);	   	  
	*p_rdc_opath = idrp_insert_global_rdpaths(p_rdc_path);	

}

rdpath_list *
create_rdc_rdpath(no_dif_rdc,rdc_dif_proc,path_segment_cnt,path,p_rd_opath,p_data,peer)
int		no_dif_rdc;
rdc_check	rdc_dif_proc[];
int		path_segment_cnt;	
rdc_path_chk	path[];
rdpath_list	*p_rd_opath;
u_char		*p_data;
idrpPeer	*peer;
{
int		i,j;
int		type;  
rdpath_list	*p_rdc_path = 0;


	/*  
	 *  
	 */

  	for (i = 0;  i <  path_segment_cnt; i++)
		{
		type = path[i].p_path->status;	
		if (path[i].cnt == 0)
			{
			/* no rdcs in this segment, just copy
			 * the rdis into a new path segment
			 */   
	
			p_rdc_path =  copy_rdpath_seg(peer,path[i].p_path,&p_rdc_path,type);
			}
		else
			{

			if (path[i].cnt == path[i].p_path->cnt) 
				{
				/* check to see if this ENTRY_SEQ/SET has only the
				 * rdcs exited
				 *
				 * if so, simply create a PATH SEQ/ SET    
				 */

				if (path[i].p_path->status == IDRP_RDPATH_RDC_ENTRY_SEQ)
					{
					type = IDRP_RDPATH_SEQ;
					}
				if (path[i].p_path->status == IDRP_RDPATH_RDC_ENTRY_SET)
					{
					type = IDRP_RDPATH_SET;
					}
				p_rdc_path = copy_rdpath_seg(peer,path[i].p_path,&p_rdc_path,type);
				}
			else	
				{
				/* if only some of RDCs in this  rdpath segment
				 * have been exited, then follow the IDRP specification
				 * section p 39 (i - iv) 
				 */
		
				convert_rdcpath_seg(peer,path[i],&p_rdc_path);						
				}
			}
		}			  
}

rdpath_list *
copy_rdpath_seg(peer,p_path,p_rdc_path,type)
idrpPeer	*peer;
rdpath		*p_path;
rdpath_list	**p_rdc_path;
int		type;
{
rdpath_list	*p_rdc;
rdpath		*p_seg;  
rdi_array	*p_array,*p_array_old;
int		i,j;
rdi		*p_rdi;	

	p_rdc = *p_rdc_path;	
/* 
 */


	if (p_rdc == (rdpath_list *) NULL)
		{
		/* no existing rdpath - create a new rdpath list
		 * and point the segment pointer to it 
		 */
	
		p_rdc = idrp_create_rdpath_list(type,p_path->rdi_list.p_rdi_array[0]);
		p_seg = p_rdc->p_end;
		}
	else
		{
		/* 
		 * path exists, create a segment memory chunk and set the
		 * segment pointer to it 
		 */
	
		p_seg = (rdpath *) idrp_local_mem_fit(sizeof(rdpath));
		p_seg->p_last = &p_seg->rdi_list;
		p_seg->p_prev = p_rdc->p_end;
		p_rdc->p_end->p_next = p_seg;
		p_rdc->p_end = p_seg;
		} 	

	/* segment pointer
	 */

	p_seg->status = type;
	p_array = p_seg->p_last; 
	p_array_old = &p_path->rdi_list;
	for (i = 1; i < p_path->cnt; i++)
		{
		j = i%IDRP_RD_PATH_ARRAY_SIZE;
		if (j == 0 && i != 0)
			{
			/* index has wrapped via one array	
			 */

			p_array->p_next = (rdi_array *) idrp_local_mem_fit(sizeof(rdi_array));
			p_array->p_next->p_prev = p_array;
			p_array = p_rdc->p_end->p_last = p_array->p_next; 
			p_array_old = p_array_old->p_next;
  			}	

		/* copy the rdi into the new path sequence
		 * - increment count on the rdi 
		 */

		p_rdi = p_array->p_rdi_array[j] = p_array_old->p_rdi_array[j];
		p_seg->cnt++;
		}
	return(p_rdc);
}	

void
convert_rdcpath_seg(peer,path,p_rdc_path)
idrpPeer	*peer;
rdc_path_chk	path;
rdpath_list	**p_rdc_path;
{
rdpath		temp_path;
rdpath		*p_temp_path;
rdpath		*p_path;
int		new_type = 0;
int		old_type = 0;
int		i;
int		last_rdi_index, rdi_index;	

	p_temp_path = &temp_path;
	p_path = path.p_path;	
	/* this should be an ordered list 
	 * of rdi's array addresses
	 */

	if (p_path->status == IDRP_RDPATH_RDC_ENTRY_SEQ)
		{
		new_type = IDRP_RDPATH_SEQ;
		} 
	if (p_path->status == IDRP_RDPATH_RDC_ENTRY_SET)
		{
		new_type = IDRP_RDPATH_SEQ;
		} 

	last_rdi_index = 0;
	for (i = 0  ; i < path.cnt; i++)
		{
		/* copy the pathway count up to the first rdi
		 * that an RDC, then copy 
		 * -- review this to see that it works
		 *      
		 */

		rdi_index = path.rdi_index[i]-1;
		copy_part_rdpath_seg(peer,path.p_path,p_rdc_path,old_type,last_rdi_index,rdi_index); 
		copy_part_rdpath_seg(peer,path.p_path,p_rdc_path,new_type,path.rdi_index[i],path.rdi_index[i]); 
		last_rdi_index = path.rdi_index[i]+1;
		}

}

void
copy_part_rdpath_seg(peer,p_path,p_rdc_path,type,start_rdi,end_rdi)
idrpPeer	*peer;
rdpath		*p_path;
rdpath_list	**p_rdc_path;
int		type;
int		start_rdi;
int		end_rdi;
{
rdpath_list	*p_rdc;
rdpath		*p_seg;
int		i,j,i1;
rdi_array	*p_array,*p_array_old;
rdi		*p_rdi;
	/* 
	 */

	p_rdc = *p_rdc_path;	

	if (p_rdc == (rdpath_list *) NULL)
		{
		p_rdc = idrp_create_rdpath_list(type,p_path->rdi_list.p_rdi_array[start_rdi]);
		p_seg = p_rdc->p_end;
		}
	else
		{
		/* 
		 * path exists, create a segment memory chunk and set the
		 * segment pointer to it 
		 */
	
		p_seg = (rdpath *) idrp_local_mem_fit(sizeof(rdpath));
		p_seg->p_last = &p_seg->rdi_list;
		p_seg->p_prev = p_rdc->p_end;
		p_rdc->p_end->p_next = p_seg;
		p_rdc->p_end = p_seg;
		} 	

	/* segment pointer
	 */

	p_array = p_seg->p_last; 
	p_array_old = &p_path->rdi_list;
	for (i = 0, i1 = start_rdi+1; i < end_rdi - start_rdi;i1++, i++)
		{
		j = i%IDRP_RD_PATH_ARRAY_SIZE;
		if (j == 0 && i != 0)
			{
			/* index has wrapped via one array	
			 */

			p_array->p_next = (rdi_array * ) idrp_local_mem_fit(sizeof(rdi_array));
			p_array->p_next->p_prev = p_array;
			p_array = p_rdc->p_end->p_last = p_array->p_next; 
			p_array_old = p_array_old->p_next;
  			}	

		/* copy the rdi into the new path sequence
		 * - increment count on the rdi 
		 */

		p_rdi = p_array->p_rdi_array[j] = p_array_old->p_rdi_array[i1];
		}
}	


void
fill_rdc_path(p_idrp_rt,peer,p_rdc_path,p_hier,p_rdpath,p_data)
idrpRoute	*p_idrp_rt;
idrpPeer	*peer;
rdpath_list	*p_rdc_path;
idrp_attr_data_entry	*p_hier;
idrp_attr_data_entry	*p_rdpath;
u_char			*p_data;
{
rdi_array	*p_array;
rdi		*p_rdi;
rdpath	*p_rdp;
u_char	*p_cout;
u_char	*p_len = 0;
u_int	hold;
u_int	total_length;
u_int	len_segment;
int	i,j;

	p_cout = p_data;
	/*  take the rdc path and put it in the
	 *  the rdpath
	 */

	p_hier->present = TRUE;
	p_hier->flags = IDRP_ATTRFLG_HIERARCH_REC;   	
	p_hier->length = IDRP_HIERARCHICAL_RECORDING_LENGTH;
	p_hier->data = &p_hier->data_in[0];
	*p_hier->data = 1;
	
	p_rdpath->present = TRUE;
	p_rdpath->flags = IDRP_ATTRFLG_RD_PATH;   	
 	p_rdpath->data = p_data;

	total_length = 0;	
		
	IDRP_RDPATH_RDI_LIST(p_rdp,p_rdc_path)
		{
		/* Here's the data 
		 */

		*p_cout = p_rdp->status;
		p_len = p_data;
		p_cout += 3;

		/* set up the legnth,
		 * rdi count and
		 * this rdi array pointer
		 */
	
		len_segment = 2;
		p_array = &p_rdp->rdi_list;
		IDRP_RDI_ARRAY_WALK(p_rdp,i,1)
			{
			j = i%IDRP_RD_PATH_ARRAY_SIZE;
			if (i != 0 && j == 0)
				{
				p_array = p_array->p_next;
				assert(p_array != (rdi_array *) NULL);
				}

			/* set the length to network byte order
			 */

			p_rdi = p_array->p_rdi_array[i]; 
			hold = p_rdi->rdi.isoa_len;
                        bcopy((caddr_t) &hold,p_cout,2);
			p_cout +=2; 

			/* copy out the rdi 
			 * adjust the length of the segment, and output pointers
			*/

			bcopy(&p_rdi->rdi.isoa_genaddr,p_cout,p_rdi->rdi.isoa_len);
			p_cout += p_rdi->rdi.isoa_len;
			len_segment = len_segment + 2 + p_rdi->rdi.isoa_len; 
			} IDRP_RDI_ARRAY_WALK_END;

	        hold = htons(len_segment);
		bcopy((caddr_t) &hold,p_cout,2);
	
		total_length += len_segment;
		} IDRP_RDPATH_RDI_LIST_END; 	

	p_rdpath->length = total_length;
 				
}




dist_unmod(p_irt,peer,flash)
idrpRoute  *p_irt;
idrpPeer    *peer;
int              flash;
{
int status = 0;

  /* check to see if this route can be send to this peer
   */

         if  (DIST(p_irt,peer,flash)== FALSE) 
	      return(FALSE);

       status = idrp_route_export(p_irt,peer);

       if (status == IDRP_EXPORT_ERROR)
             return(FALSE);

       if ((status == IDRP_EXPORT_VALID)  && (p_irt->route_out[peer->id].p_att != (idrp_attribute_record *) NULL))
	  return(FALSE);

     return(TRUE);
}












