/* 
 * $Id: idrp_attrib.c,v 1.14 1996/05/25 13:31:36 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_attrib.c
 *  
 *  attribute validation and handling routines
 *  (rd path handling and validation takes up lots of the module)
 *  there also seems to be a bunch of route-id chain handling here.
 *  perhaps there should be another chain for route-id handling?
 * 	valid_attr(peer,attr) - validate attribute record 
 * 	valid_dist_attr(peer,attr) - validate distinguished attributes in  2nd attribute record 
 *	valid_usr_attr(peer,attr) - user attributes
 *	add_local_rd_to_path(peer,attr_rec) - add our rd to the rd path
 *		also detects routing loops
 *	valid_seg_type(type,peer,flag) - checking the segement type of the rd path
 *	valid_rd_path(peer,p_att,p_rdpath,rdpath_length) - validate rd path, I guess
 *	link_rd_canon(p_rd_new,p_att) - link rds in canonical order on rdpath list
 *	link_rd_list(p_rd_new,p_list) - link rds in order in RD PATH attribute on rdpath list 
 * 	find_attr_rec - search for duplicate attribute record in attribute list  
 *     	compare_atts  - compare attributes
 *	idrp_dif_path - see if different path attribute- return
 *			mask of attributes that are different 
 *	ATTS_REC_ZERO(p_atts) - zero out path atts.  Maybe this should be a 
 *		macro and not a func.
 *	idrp_free_routeid_chain_id(p_att,id,peer) - free routeid by id, att and peer
 *	idrp_find_routeid_chain(p_rt,p_last_rte) - find routeid chain pointer for this idrpRoute
 *	idrp_free_nlri_att_rec(p_rt) - free nlri from chain
 *      idrp_free_attr_rec - free attribute record
 *	idrp_free_snpa_list - free snpa list in attribute record
 * 	link_att_list - link an attribute record to the list 
 * 	relink_free_att - relink attribute list to exclude attribute record and free
 * 	idrp_free_nlri_chain - free nlris off route_id chain off attribute list
 *	dist_list_check - check the distribution list for my rdi and rdc
 *	free_dist_list - free disttribution rdi list
 *	find_rdi_rdilist(p_rdi,p_list) - find rdi in rdilist 
 * 	idrp_wellknown_type(attr_type,peer,p_attrs) - check wellknown types
 * 	idrp_transitive_att - handle linking transitive attributes to a linked list off attribute record 
 * 	wellknown_attrib_twice - check for wellknown attribute type, and handle route separator
 * 	send_next_hop  - set up to  send next hop in pdu
 * 	fill_multi_exit - fill next hop for sending in pdu  
 *	idrp_parse_next_hop - parse next hop from pdu
 */ 
  
 

/*		
 * valid_attr - check for valid attributes 
 */
int 
valid_attr(peer,p_att)
idrpPeer *peer;
idrp_attribute_record *p_att;
{
int attr_type;
int attr_len;
u_int	longhold;
u_char *dataptr;
u_char *rdpath;
int	rd_loop_found = FALSE;

	/* now validate the attribute sections of UPDATE */

	/* (7.20.3 - d) check for well known attributes  */

	if ((p_att->idrp_mask & IDRP_ATTBT_MANDATORY) != IDRP_ATTBT_MANDATORY)
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
		peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MISS_WELKN_ATTR;
		if (p_att->idrp_mask & IDRP_ATTBT_ROUTE_SEPARATOR == 0 ) 
			{
			peer->last_error_pdu.data = (u_char *) &idrpAttrType[IDRP_ATTR_ROUTE_SEPARATOR]; 
			}
		else	{
			if (p_att->idrp_mask & IDRP_ATTBT_RD_PATH == 0)
				{
				peer->last_error_pdu.data = (u_char *) &idrpAttrType[IDRP_ATTR_RD_PATH];
				}
			else
				{
				peer->last_error_pdu.data = (u_char *) &idrpAttrType[IDRP_ATTR_HOP_COUNT];
				}
			}	
		peer->last_error_pdu.data_len = 1;
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;

		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP valid_attr error - update missing well known attributes.  Mandatory attribute mask = %x, provided attribute mask = %x",
			 IDRP_ATTBT_MANDATORY, p_att->idrp_mask));
		return(FALSE);
		}

	/* 7.20.3 i) check that DIST_LIST_INCL and DIST_LIST_EXCL  */
	/*           are not both active */

	if ((p_att->idrp_mask & IDRP_ATTBT_DIST_LIST_INCL) && 
	    (p_att->idrp_mask & IDRP_ATTBT_DIST_LIST_EXCL))
		{
		/* both IDRP DIST_LIST_INCL and IDRP_DIST_LIST_EXCL */
		/* exist - malformed attribute list error */
		
		peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
		peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MALF_ATTR;
		peer->last_error_pdu.data = NULL;
		peer->last_error_pdu.data_len = 0;
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;
		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP valid_attr error - both DIST_LIST_INCL and DIST_LIST_EXCL are present"));
		return (FALSE);
		} 


	/* allocate a options structure for attribute array 
	 */

	p_att->p_opts = (idrpRoute_options *) idrp_local_mem_fit (sizeof(idrpRoute_options));
	bzero(p_att->p_opts,sizeof(idrpRoute_options));

	/* all mandatory attribute there - check flag codes and each attribute */


	for (attr_type = 1; attr_type < IDRP_ATTR_MAX + 1; attr_type++) 
		{

		/* if  attribute not present skip all the tests */

		if (!p_att->attrib[attr_type].present)
			continue;

		dataptr = p_att->attrib[attr_type].data;
		attr_len = p_att->attrib[attr_type].length;

		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("  Attr %d, length %d, flags %x", attr_type,
			p_att->attrib[attr_type].length, p_att->attrib[attr_type].flags));

		/* validate attribute flags  (idrp 7.xx.x - x)  */
		if (p_att->attrib[attr_type].flags != idrpAttrFlags[attr_type])
			{
			peer->last_error_pdu.error_code=IDRP_ERROR_UPDATE_PDU;
			peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_ATTR_FLAGS_ERR;
			peer->last_error_pdu.data = p_att->attrib[attr_type].data;
			peer->last_error_pdu.data -= sizeof(struct _idrp_path_attr_hdr);  
			peer->last_error_pdu.data_len = p_att->attrib[attr_type].length + sizeof(struct _idrp_path_attr_hdr);
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0;
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP valid_attr error - problems with attribute type %d with flags 0x%x",
				attr_type, p_att->attrib[attr_type].flags));
			return(FALSE);
			}


		switch(attr_type) 
		{

		/* ROUTE_SEPARATOR */
		case IDRP_ATTR_ROUTE_SEPARATOR:
			if (attr_len != IDRP_ROUTE_SEPARATOR_LENGTH)
				{ 
				peer->last_error_pdu.error_code=IDRP_ERROR_UPDATE_PDU;
				peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_ATTR_LEN_ERR;
				peer->last_error_pdu.data = p_att->attrib[attr_type].data;
				peer->last_error_pdu.data -= sizeof(struct _idrp_path_attr_hdr);  
				peer->last_error_pdu.data_len = p_att->attrib[attr_type].length + sizeof(struct _idrp_path_attr_hdr);
				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0;
				trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP valid_attr error - route separator has invalid length.  Desired length = %d, provided length = %d", IDRP_ROUTE_SEPARATOR_LENGTH, attr_len));
				return(FALSE);
				}
			bcopy(dataptr, (caddr_t) &longhold,sizeof(route_id));
			longhold = htonl(longhold);
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("   ROUTE_ID 0x%x",longhold ));
			p_att->route_id_list->route_id=longhold;
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("   ROUTE_ID 0x%x",p_att->route_id_list->route_id));
			dataptr += sizeof(route_id);
			p_att->route_id_list->loc_pref = *dataptr;	
			break;			

		case IDRP_ATTR_EXT_INFO:
			if (attr_len != IDRP_EXT_INFO_LENGTH)
				{ 
				peer->last_error_pdu.error_code=IDRP_ERROR_UPDATE_PDU;
				peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_ATTR_LEN_ERR;
				peer->last_error_pdu.data = p_att->attrib[attr_type].data;
				peer->last_error_pdu.data -= sizeof(struct _idrp_path_attr_hdr);  
				peer->last_error_pdu.data_len = p_att->attrib[attr_type].length + sizeof(struct _idrp_path_attr_hdr);
				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0;
				trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP valid_attr error - ext info has invalid length.  Desired length = %d, provided length = %d", 
					IDRP_EXT_INFO_LENGTH, attr_len));
				return(FALSE);
				}
			break;

		/* RD Path.  Note its position so that we can copy it into our route. */

		case IDRP_ATTR_RD_PATH:
			{
			int ret_code;  

			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP valid_rd_path length(attr_len) = %d",attr_len ));

			ret_code = valid_rd_path(peer,p_att,dataptr,attr_len);

			switch (ret_code)
				{
				case IDRP_RDPATH_PARSE_FAILED:
				case IDRP_RDPATH_RDLOOP:	
					trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP valid_attr error - failed to validate RD path (valid_rd_path() failed)"));
					return (FALSE);
					break;
				
				case IDRP_RDPATH_LOCAL_LOOP:
					trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP valid_attr error - failed to add our RD to RD path (rdloop) failed)"));
					rd_loop_found = TRUE;
					break;
				
				case IDRP_RDPATH_VALID:
					break;
				}
			}					
			break;

		case IDRP_ATTR_HOP_COUNT:
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("   HOP_COUNT %d", *dataptr));
			p_att->p_opts->hopcount = *dataptr;
			break;

		case IDRP_ATTR_NEXT_HOP:
			if (!idrp_parse_next_hop(dataptr,attr_len,p_att))
				{
				return(FALSE);
				}	
			break;

		case IDRP_ATTR_DIST_LIST_INCL:
			{
			/* here we need to pull in the distribution list one nlri at at time.
			 * if the PDU is malformed within the distribution list
			 * I think we need to indicate a PDU error
			 * I know this is a little more problematic, but
			 * for a reference implementation, it is ok.  
			 */
			

			if (!dist_list_check(p_att,IDRP_ATTR_DIST_LIST_INCL,peer))
				{
				/* the distribution list either contains my
				 * rdi or rdc, or is malformed
				 */
				return (FALSE);
				}

			}
			break;
		
		case IDRP_ATTR_DIST_LIST_EXCL:
			{
			/* here we need to pull in the distribution list one nlri at at time.
			 * if the PDU is malformed within the distribution list
			 * I think we need to indicate a PDU error
			 * I know this is a little more problematic that
			 * ignorning malformities within an entity in production, but
			 * for a reference implementation, it is ok.  
			 */
			

			if (!dist_list_check(p_att,IDRP_ATTR_DIST_LIST_EXCL,peer))
				{
				/* the distribution list either contains my
				 * rdi or rdc, or is malformed
				 */
				return (FALSE);
				}
			}
			break;
				
			
		case IDRP_ATTR_MULTI_EXIT_DISC:
			p_att->p_opts->multi_exit_rcvd = *p_att->attrib[IDRP_ATTR_MULTI_EXIT_DISC].data; 
			break;

		case IDRP_ATTR_TRANSIT_DELAY:
		case IDRP_ATTR_RESIDUAL_ERROR:
		case IDRP_ATTR_EXPENSE:
		case IDRP_ATTR_SECURITY:
		case IDRP_ATTR_PRIORITY:
			{
			if (!idrp_qos_parse(p_att,dataptr,attr_type,peer))
				return(FALSE);
			}
			break;

		case IDRP_ATTR_CAPACITY:
			p_att->p_opts->capacity.value = *dataptr; 
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("Got the capacity value of 0x %x peer %s ",
				p_att->p_opts->capacity.value,peer->name));
			break;	

		  	 } /* end of switch statement */

		}	/* end of for statement checking all attributes */

	/* test for Loop in path found by adding this RDI 
	 */

	
	if (rd_loop_found)
		{
		return(FALSE);
		}
	return(TRUE);
}


int 
valid_dist_attr(peer,p_att)
idrpPeer *peer;
idrp_attribute_record *p_att;
{
	if ((p_att->idrp_mask & IDRP_ATTBT_DISTINGUISH) != 0)
		{ 
		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("internal idrp error processing DISTINGUISH ATTR from peer %s",peer->name));
		return (FALSE);
		}
	return(TRUE);
}

int 
valid_usr_attr(peer,p_att)
idrpPeer *peer;
idrp_attribute_record *p_att;
{
	if (p_att->idrp_mask & IDRP_ATTBT_USR == 0)
		{ 
		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("internal idrp error processing USR ATTR from peer %s",peer->name));
		}
	return(TRUE);
}


/* 
 *  add_local_rd_to_path(peer,attr_rec))
 * 
 * put the local RD in the rd_path by adding it into the 
 * locally added attributes portion of the attribute field
 * LOCAL_RD_PATH attribute of attributes 
 * 
 * link the attribute to the cannonical attribute list
 * if it fails to link, the we have a loop
 * 
 * return code = 0 failed
 * return code = 1 worked  
 */

int
add_local_rd_to_path(peer,p_att)
idrpPeer *peer;
idrp_attribute_record *p_att;
{
u_int16 hold;
idrp_canon_rdpath *p_rd;
flag_t segtype = IDRP_PATH_SEGMENT_SEQ;
 
	/* add in our RDI length + (1) RDI length byte + segement header length 
	 *  to the total RD path length 
	 */

	hold = sizeof(idrp_path_segment_header) + rt_rdi.isoa_len + 1;
	p_att->attrib[IDRP_ATTR_LOCAL_RD].length = hold;
 	hold += (u_int16) p_att->attrib[IDRP_ATTR_RD_PATH].length;
        bcopy((caddr_t) &hold,&p_att->attrib[IDRP_ATTR_RD_PATH].length, 2); 
        p_att->attrib[IDRP_ATTR_LOCAL_RD].present = TRUE;
	
	/* we link the data to the
	 * global structure that has our
	 * RD_PATH encoded
	 */
 
	p_att->attrib[IDRP_ATTR_LOCAL_RD].data = (u_char *) &local_rd_path;

	/* create canonical path attribute
	 *  and stuff the global rdi in the insertion sort for ordering 
	 */

	p_rd = (idrp_canon_rdpath *) idrp_local_mem_fit(sizeof(idrp_canon_rdpath));  
	p_rd->p_rdi = p_rt_rdi;
	p_rt_rdi->refcnt++;
	if (!link_rd_canon(p_rd,p_att)) 
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
		peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_RD_ROUTING_LOOP;
		peer->last_error_pdu.data = (u_char *) rt_rdi.isoa_genaddr; 
		peer->last_error_pdu.data_len = rt_rdi.isoa_len;      		
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;
		IDRP_MEM_FIT_FREE(p_rd);
		idrp_link_rdpath(segtype,p_rt_rdi,p_att);
		return(FALSE);
		}

	/* link in the local rdi to the order path
	 */
	 
	idrp_link_rdpath(segtype,p_rt_rdi,p_att);
	return(TRUE);
}
	
int 
valid_seg_type(type, p_flag)
int type;
flag_t	*p_flag;	
{
	switch (type)
		{
		case IDRP_PATH_SEGMENT_SET:
			*p_flag = IDRP_PATH_SEGMENT_SET;	
			return (TRUE);
			break;
		case IDRP_PATH_SEGMENT_SEQ:
			*p_flag = IDRP_PATH_SEGMENT_SEQ;
			return (TRUE);
			break;

		case IDRP_PATH_SEGMENT_ENTRY_SEQ:
			*p_flag = IDRP_PATH_SEGMENT_ENTRY_SEQ;
			return (TRUE);
			break;

		case IDRP_PATH_SEGMENT_ENTRY_SET:
			*p_flag = IDRP_PATH_SEGMENT_ENTRY_SET;
			return (TRUE);
			break;	
		}
	return (FALSE);
}


int 
valid_rd_path(peer, p_att, p_rdpath, rdpath_length)
idrpPeer *peer;
idrp_attribute_record *p_att;
u_char	*p_rdpath;
int	rdpath_length;
{
u_char	*cp;
int	pathlen;
u_short hold;
int	seglen;
int	seglen_local;
idrp_path_segment_header 	*seghead;
idrp_canon_rdpath	*p_rd;
rdi	*p_rdi;
flag_t	segtype;
 
	/* clear rd path list pointer, 
	 * set up path length for loop
	 * set-up pointer to pathway sequence
	 */ 

 
	p_att->rd_path = (idrp_canon_rdpath *) 0 ; 
	pathlen = rdpath_length;
	cp = (u_char *) p_rdpath;
	
	/* parse the rd path information */


	while (pathlen > 0)  
		{

		/* walk through segment type, length value */ 

		seghead = (idrp_path_segment_header *) cp;		
		cp += sizeof(idrp_path_segment_header);
		bcopy(&seghead->path_seg_len, (caddr_t) &hold, sizeof(seghead->path_seg_len));
		hold = ntohs(hold);
		seglen_local = seglen = (int) hold;

		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("   RD_PATH type %d, length %d",
			seghead->path_seg_type,seglen));

		/* check for valid segment type
		*/
		if (!valid_seg_type(seghead->path_seg_type, &segtype))
			{ 
			/* illegal type in the segement type */
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("RD PATH type %d illegal peer %s",
			seghead->path_seg_type,peer->name));
			peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
			peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_ILL_RD_PATH_SEG;
			peer->last_error_pdu.data = &seghead->path_seg_type;
			peer->last_error_pdu.data_len = sizeof(seghead->path_seg_type);	 
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0;
			return(IDRP_RDPATH_PARSE_FAILED);
			}

		
		/* valid segment type,  walk thorugh the RDI list */
 
		while (seglen_local > 0) 
			{
			/* build canonical rd path
			 * - rdi and rd path space needed
			 */

			p_rd = (idrp_canon_rdpath *) idrp_local_mem_fit(sizeof(idrp_canon_rdpath));  
			p_rdi = (rdi *) idrp_local_mem_fit(sizeof(rdi));

			/* copy in length byte
			 * - this will have to change if
		 	 * - changes off a byte 
			 */ 

			p_rdi->rdi.isoa_len = hold = *cp;		/* one byte length for RDI */
			cp++;

			/* copy in RDI */

			bcopy(cp,&p_rdi->rdi.isoa_genaddr,hold);
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("     %A", 
			      sockbuild_iso((byte *) &p_rdi->rdi.isoa_genaddr, p_rdi->rdi.isoa_len)));

			p_rd->p_rdi = idrp_insert_global_rdi(&p_rdi);
			if (!link_rd_canon(p_rd,p_att)) 
				{
				peer->last_error_pdu.error_code =IDRP_ERROR_UPDATE_PDU;
				peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_RD_ROUTING_LOOP;
				peer->last_error_pdu.data = (u_char *) &p_rdi->rdi.isoa_genaddr; 
				peer->last_error_pdu.data_len = p_rdi->rdi.isoa_len;      		
				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0;
				return(IDRP_RDPATH_RDLOOP);	
				}

			/* link to the ordered path */

			idrp_link_rdpath(segtype,p_rd->p_rdi,p_att);

			seglen_local = seglen_local - (hold +1);
			cp += hold;
			}

		/* set up for the next segment */

		pathlen -= seglen + sizeof(idrp_path_segment_header);

		} /* end of loop for the full rd_path */

	/* check to see if this global path has been seen,
	 * - if so replace it with the global path
	 * - and toss this rdpath structure 
	 */  

	/* add in the local rd path if peer
	 * received from is an external peer 
   	 */

	if (peer->type == IDRP_PEER_EXTERNAL)
		{
		if (!add_local_rd_to_path(peer,p_att))
			{
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP valid_attr error - received update from external peer, failed to successfully add our RD to the path"));
			p_att->p_rd_opath = idrp_insert_global_rdpaths(p_att->p_rd_opath);	
			return(IDRP_RDPATH_LOCAL_LOOP);	
			}
		}
	p_att->p_rd_opath = idrp_insert_global_rdpaths(p_att->p_rd_opath);	
	return(IDRP_RDPATH_VALID);
}

/* 
 * link_rd_canon(p_rd,att)
 *  - link RDIs in cannonical order 
 * 
 */
int 
link_rd_canon(p_rd_new,p_att)
idrp_canon_rdpath	*p_rd_new;
idrp_attribute_record	*p_att;
{
/* local variables */


	trace_tf(idrp_trace_options, 0, TR_NORMAL,("link_rd_canon"));

	/* list empty */

	if (p_att->rd_path == (idrp_canon_rdpath *)0 )
		{
		p_rd_new->p_next = (idrp_canon_rdpath *) 0;
		p_att->rd_path = p_rd_new;
		return(TRUE);
		}

	return(link_rd_list(p_rd_new,&(p_att->rd_path)));

}

boolean
idrp_link_rdpath(seg_type,p_rdi,p_att)
flag_t			seg_type;
rdi			*p_rdi;	
idrp_attribute_record	*p_att;
{

	if (p_att->p_rd_opath)
		{
		idrp_add_to_rdpath(seg_type,p_rdi,p_att->p_rd_opath);
		}
	else
		{
		p_att->p_rd_opath = idrp_create_rdpath_list(seg_type,p_rdi);
		}
}


/* link_rd_list:  Insertion sort the RDI given in p_rd_new into the list of RDIs
 * given by p_rd_list.  
 * 
 * The sense of the test in the for loop of the implementation that used to be
 * here was backwards ( == instead of != ).  I chose to rewrite it, however, to
 * tighten up the code and make it a little easier to grok.
 */

int 
link_rd_list(p_rd_new,p_rd_list)
idrp_canon_rdpath	*p_rd_new;
idrp_canon_rdpath	**p_rd_list;
{
idrp_canon_rdpath	*p_list;	/* pointer to rd path entry */
idrp_canon_rdpath	*p_cur;		/* pointer to rd path entry */
idrp_canon_rdpath	*p_prev;	/* pointer to rd path entry */

	p_list = *p_rd_list;	/* for convenience */

	trace_tf(idrp_trace_options, 0, TR_NORMAL, ("link_rd_new: new_rdi     %A", 
	      sockbuild_iso((byte *) &p_rd_new->p_rdi->rdi.isoa_genaddr, p_rd_new->p_rdi->rdi.isoa_len)));

	/* first see if we go at the head of the list 
	 * see if we can get the global ordering working  
	 */

	switch (compare_rdi(p_rd_new->p_rdi,p_list->p_rdi))
		{
		case ISO_ADDR_EQUAL:		/* loop in RD path */
			return(FALSE);

		case ISO_ADDR_LESS_THAN:	/* new RD is head */
			p_rd_new->p_next = p_list;
			*p_rd_list = p_rd_new;
			return(TRUE);

		default:			/* not head, just continue */
			break;
		}

	/* got here, so we have to look for where to insert */

	for (p_prev = p_list, p_cur = p_list->p_next; p_cur != NULL; p_prev = p_cur, p_cur = p_cur->p_next)
		{
		switch (compare_rdi(p_rd_new->p_rdi,p_cur->p_rdi))
			{
			case ISO_ADDR_EQUAL:		/* loop in RD path */
				return(FALSE);

			case ISO_ADDR_LESS_THAN:	/* new RD is before cur */
				p_rd_new->p_next = p_cur;
				p_prev->p_next = p_rd_new;
				p_rd_new->p_prev = p_prev;
				p_cur->p_prev = p_rd_new;
				return(TRUE); 

			default:			/* iterate */
				break;
			}
		}

	/* got here, so we fell out of the loop.  That means that we insert on the tail of the list, and
	 * p_prev will be pointing to the current tail.
	 */

	assert(p_prev->p_next == NULL);
	
	p_rd_new->p_next = NULL;
	p_rd_new->p_prev = p_prev;
	p_prev->p_next = p_rd_new; 
	return(TRUE);
}


/* unlink_rd_list:  Insertion sort the RDI given in p_rd_new into the list of RDIs
 * given by p_rd_list.  
 * 
 */

void
unlink_rd_list(p_rd_del,p_rd_list)
idrp_canon_rdpath	*p_rd_del;
idrp_canon_rdpath	**p_rd_list;
{
idrp_canon_rdpath	*p_list;	/* pointer to rd path entry */

	p_list = *p_rd_list;	/* for convenience */

	trace_tf(idrp_trace_options, 0, TR_NORMAL, ("unlink_rd_new: new_rdi     %A", 
	      sockbuild_iso((byte *) &p_rd_del->p_rdi->rdi.isoa_genaddr, p_rd_del->p_rdi->rdi.isoa_len)));

	/* first see if we go at the head of the list */

	if (p_rd_del == p_list)
		{
		*p_rd_list = p_list = p_rd_del->p_next;
		p_list->p_prev = (idrp_canon_rdpath *) NULL; 
		return;
		}

	/* not first of list
	 * - link around this one 
	 */

	p_rd_del->p_prev->p_next = p_rd_del->p_next;
	
	if (p_rd_del->p_next)
		{
		  p_rd_del->p_next->p_prev = p_rd_del->p_prev;
		}
	return;
}

/* find_attr_ann_list
 * - find the attribute in the announce list entry
 */

idrp_ann_list *
find_attr_ann_list(p_ann_list,p_att)
idrp_ann_list	*p_ann_list;
idrp_attribute_record 	*p_att;
{
idrp_ann_list	*p_atl;

	ANN_LIST(p_atl,p_ann_list)
		{
		if (p_atl->p_attr == p_att)
			return(p_atl);
		} ANN_LIST_END;

	return (0);

}		


/* find_attr_rec (idrpPeer *peer, idrp_attribute_record *attr)
 *  
 * look down link list of attributes for attribute that matches
 *	Match : 1) attribute mask 
 *		2) canonical represent of RDs
 *		3) full Attributes
 *
 */

idrp_attribute_record *
find_attr_rec(attr)
idrp_attribute_record *attr;
{
/* walk down chain of attributes */
/* linked in match of attribute mask */

int i;					/* list pointer */
idrp_attribute_record 	*p_attr;	/* attribute record */
int			rib_id = attr->rib_id;

	ATTR_LIST(p_attr,rib_id)
		{
		if (p_attr->idrp_mask != attr->idrp_mask)
			continue;
			
		/* compare the next_hop address,
		 * - the attribute records
		 * canonical RD path - testing is byte by byte
		 * skh --- (3/25/93) - we need to
		 * to add in the effieciency of
		 * a list of canonical rd_paths
		 *-- I am leaving this space
		 * efficiency out until
		 * the base protocol is working
		 */

		if ((attr->next_hop != (sockaddr_un *) NULL) &&
			(sockaddrcmp(p_attr->next_hop,attr->next_hop) == FALSE))
			continue;
	

		/* immediate next hop - fits
		 * - try the base comparision
		 * - if it fails go on
		 */
		if (compare_atts(p_attr,attr) == FALSE)
			continue;

		if ((attr->peer_alloc->type == IDRP_PEER_LOCAL) &&
			(p_attr->p_opts->p_local_opts == attr->p_opts->p_local_opts))
			{ 
			/* A more restrictive NEXT_HOP
			 * - test needs to be put in here
			 * - after 1.0 and we go for 1.1 with import 
			 * skh-- 4/9/94  
			 */
	
			return(p_attr);	
			}
		}
	return (0);
}

/*
 * compare_atts(p_att1,p_att2)
 *
 * 	- compare two attribute records
 *        attribute by attribute 
 * 	  returns 1 if match 
 *	  returns 0 if do not match 
 */ 
boolean
compare_atts(p_att1,p_att2)
idrp_attribute_record *p_att1;
idrp_attribute_record *p_att2;
{
u_int att_type;

	/* walk through all types */
		
	for (att_type = 1; att_type < IDRP_ATTR_MAX + 1; att_type++)
		{
		if (p_att1->attrib[att_type].present)
			{
			switch (att_type)
				{
				/* see that attribute data matches */
						
				case IDRP_ATTR_RD_PATH:
				case IDRP_ATTR_NEXT_HOP:
				case IDRP_ATTR_DIST_LIST_INCL:
				case IDRP_ATTR_DIST_LIST_EXCL:
				case IDRP_ATTR_MULTI_EXIT_DISC:
					{
					if (p_att1->attrib[att_type].length  !=
					    p_att2->attrib[att_type].length)
						{
						return (FALSE);
						}
					if (bcmp(p_att1->attrib[att_type].data, 
					     p_att2->attrib[att_type].data, 
					     p_att1->attrib[att_type].length))
						{
						return (FALSE);
						}
					}
					break;
			
				/* distinquishing attributes cases here */ 	
				}  /* end switch */
			} /* end if */
		}   /* end for loop */

	return (TRUE);
}

int 
idrp_dif_path(p_att1,p_att2)
idrp_attribute_record *p_att1;
idrp_attribute_record *p_att2;
{
int	mask = 0;		/* mask for return what doesn't match */ 
int	att_type;		/* attribute type for loop */
	
	for (att_type = 1; att_type < IDRP_ATTR_MAX + 1; att_type++) {
	    if (p_att1->attrib[att_type].present) {
		switch (att_type) {

		    /* see that attribute data matches */
                    case IDRP_ATTR_RD_PATH:
                    	if (p_att1->attrib[att_type].length != p_att2->attrib[att_type].length) { 
                            mask = mask | idrpAttrBits[att_type];   
                        } else { 
                            int len1 = p_att1->attrib[IDRP_ATTR_LOCAL_RD].length;
                            int len2 = p_att2->attrib[IDRP_ATTR_LOCAL_RD].length;
                                                
                            if (len1 == 0 && len2 == 0) {
                                /* no local RDs */
                                if (bcmp(p_att1->attrib[att_type].data, 
                                         p_att2->attrib[att_type].data, 
                                         p_att1->attrib[att_type].length)) {
                                    mask |= idrpAttrBits[att_type];
                                }
                            } else if (len1 == len2) {
                                /* Local RD in the 1st attribute && second  same
                                 * - so make sure bytes are compared right 
                                 */
                                                        
                                if (bcmp(p_att1->attrib[att_type].data, 
                                         p_att2->attrib[att_type].data, 
                                         (p_att1->attrib[att_type].length-len1))) {
                                    mask |= idrpAttrBits[att_type];
                                } else {
                                    if (bcmp(p_att1->attrib[IDRP_ATTR_LOCAL_RD].data,
                                             p_att2->attrib[IDRP_ATTR_LOCAL_RD].data,
					     len1)) {
                                        /* not equal - set mask */
                                        mask |= idrpAttrBits[att_type];
                                    }
				}
                            } else {
                                /* lengths equal, RD added, but not the same??
                                 * copy to temporary buffrer space
                                 * -- skh this is gross hack and 1.1 will remove it
                                 * - RD comparison will be by pointer in global rdi path
                                 */
                                int length = p_att1->attrib[att_type].length;                                           
                                char *p1 = (char *) idrp_local_mem_fit(length);
                                char *p2 = (char *) idrp_local_mem_fit(length);
                                char *p3,*p4; 

                                bzero(p1,length);
                                bzero(p2,length);
                                bcopy(p_att1->attrib[att_type].data,p1,(length - len1));
                                if (len1) {       
                                    p3 = p1 + (length - len1);
                                    bcopy(p_att1->attrib[IDRP_ATTR_LOCAL_RD].data,
					  p3,
					  len1);
                                }
                                bcopy(p_att2->attrib[att_type].data,
				      p2,
				      (length - len2));
                                if (len2) {
                                    p4 = p2 + (length - len2);
                                    bcopy(p_att2->attrib[IDRP_ATTR_LOCAL_RD].data,
					  p4,
					  len2);
                         	}
                                if (bcmp(p1,p2,length)) {
                                    /* not equal */ 
                                    mask |= idrpAttrBits[att_type];
                                }
                                idrp_mem_fit_free((void **) &p1, length);
                                idrp_mem_fit_free((void **) &p2, length);
                            }
			}
			break;                        
	

			case IDRP_ATTR_NEXT_HOP:
			case IDRP_ATTR_DIST_LIST_INCL:
			case IDRP_ATTR_DIST_LIST_EXCL:
			case IDRP_ATTR_MULTI_EXIT_DISC:
			    {
				if (p_att1->attrib[att_type].length  !=
				    p_att2->attrib[att_type].length) 
					{
					mask = mask | idrpAttrBits[att_type];	
					}
				else if (bcmp(p_att1->attrib[att_type].data, 
					     p_att2->attrib[att_type].data, 
					     p_att1->attrib[att_type].length)) 
					{
					mask = mask | idrpAttrBits[att_type];
					}
			    }
			    break;
			
				/* distinquishing attributes cases here */ 	
		}  /* end switch */
	    } /* end if */
	}   /* end for loop */

        return (mask);
}


/* zero attributes */
/* jgs -- why the use of all CAPS?  This conventionally indicates
 * jgs -- a macro rather than a func.
 * skh -- need to pull ATT_REC_ZERO into macro -(2/26/93)
 * skh -- caps indicate this should be done after debugging
 * skh -- or decide that this call is OK and
 * skh -- change to small letters 
  */
void 
ATTS_REC_ZERO(p_atts)
idrp_attribute_record *p_atts;
{
int i;

	for (i = 0; i < IDRP_ATTR_MAX; i++) 
		{ 
                p_atts->attrib[i].present = FALSE;
 		p_atts->attrib[i].flags = (u_char) 0; 
 		p_atts->attrib[i].length = 0; 
 		p_atts->attrib[i].data = (u_char *) 0;  
 		} 
        p_atts->idrp_mask = 0;
	return;
}


/* idrp_find_routeid_chain_id(p_att,id,peer)
 * - find routeid chain pointer based on id
 * - free entry along with nlris if
 * the memory is tasks
 * local routeid_chains should go through
 * the local memory freeing
 * - NOTE this route must only
 *   be called at parse_cleanup - no initial
 *   output lists
 *   OR after the output list have been relinked 
 * 
 */

void
idrp_free_routeid_chain_id(p_att,id,peer)
idrp_attribute_record *p_att;
int 	id;
idrpPeer	*peer;
{
idrpRoute_entry		*p_rte;
idrpRoute_entry		*p_rte_last = 0;

	p_rte = p_att->route_id_list;
	while (p_rte)
		{
		/* look for route_id match */

		if (id == p_rte->route_id && (peer == p_rte->peer))
			{
			/* got a match - free the associated nlris
		 	 * then relink route_id and free
			 */ 

			idrp_free_nlri_chain(p_rte);
			if (p_rte_last)
				{
				/* relink the route_id list */
				p_rte_last->p_next = p_rte->p_next;
				}
			else
				{
				p_att->route_id_list = p_rte->p_next;	
				}
			IDRP_MEM_FIT_FREE(p_rte);
			return;
			}
		p_rte_last = p_rte;
		p_rte = p_rte->p_next;
		}
	
	return;
}


int
idrp_free_routeid_entry(p_att, p_rte_ent)
idrp_attribute_record 	*p_att;
idrpRoute_entry		*p_rte_ent;
{
int			cnt = 0;
idrpRoute_entry		*p_rte = 0;
idrpRoute_entry		*p_rte_last = 0;
idrpPeer		*peer;

	peer = p_rte_ent->peer;

	for (p_rte = p_att->route_id_list; p_rte; p_rte = p_rte->p_next)
		{
		if (p_rte == p_rte_ent) 
			{
			/* got a match - free the associated nlris
		 	 * then relink route_id and free
			 */ 

			cnt =  idrp_free_nlri_chain(p_rte);
			if (p_rte_last)
				{
				/* relink the route_id list */
				p_rte_last->p_next = p_rte->p_next;
				}
			else
				{
				p_att->route_id_list = p_rte->p_next;
				}

			IDRP_MEM_FIT_FREE(p_rte);
			return(cnt);
			}
		p_rte_last = p_rte;
		}
	trace_tf (idrp_trace_options, 0, TR_NORMAL, (" peer %s couldn't find p_att %x memory p_rte to release",peer->name,p_att));

	return(cnt);

}


int
idrp_free_routeid_chain(p_att) 
idrp_attribute_record 	*p_att;
{
int			cnt = 0;
idrpRoute_entry		*p_rte = 0;
idrpRoute_entry		*p_rte_next = 0;

	for (p_rte = p_att->route_id_list; p_rte; p_rte = p_rte_next)
		{
		p_rte_next = p_rte->p_next;
		cnt =  idrp_free_nlri_chain(p_rte);
		IDRP_MEM_FIT_FREE(p_rte);
		}
	p_att->route_id_list = NULL;
	return(cnt);

}



/*  idrp_find_routeid_chain(p_rt,p_last_rte)
 *  - find routeid chain pointer for this idrpRoute
 *
 */
idrpRoute_entry *
idrp_find_routeid_chain(p_rt,p_last_rte)
idrpRoute *p_rt;	/* pointer to route */
idrpRoute_entry	**p_last_rte;
{
idrpRoute_entry		*p_rte;
idrpRoute_entry		*p_rte_last = 0;
idrpRoute_entry		*p_found = 0;
int			nlri_id;

	/* set the nlri id value from AF.ISO value */
	nlri_id = family_to_nlri_id(p_rt->family);

	/* walk down list of route_ids looking for match */
	p_rte = p_rt->p_attr->route_id_list; 
	while (p_rte)
		{
		/* look for route_id match
		 *
		 * note that this code is trivial for local routes and ext info routes -- in this case the peer
		 * will be the local peer, which should only ever have two route IDs ever, to wit 
		 * IDRP_LOCAL_ROUTE_ID and IDRP_EXT_INFO_ROUTE_ID. 
		 *
		 * It might be nice to move to something faster than a linear search to look up the route ID chain;
		 * for now we don't worry about it since we expect the chains to be short.
		 */

		if (p_rt->route_id_in == p_rte->route_id && 
			(p_rt->peer == p_rte->peer))
			{
			p_found = p_rte;
			*p_last_rte = p_rte_last;
			break;
			}
		else
			{
			if (p_rt->route_id_in == p_rte->route_id)
				{
				trace_tf (idrp_trace_options, 0, TR_NORMAL, ("idrp_find_routeid_chain p_rt (%x) ->peer: %s , p_rte(%x)->peer %s",
					p_rt,p_rt->peer->name,p_rte,p_rte->peer->name));
				}
			}

		p_rte_last = p_rte;
		p_rte = p_rte->p_next;
		}

return (p_found);
}

/* idrp_free_nlri_att_rec(p_rt)
 * - free nlri from chain
 *   (with indicates whether chain is linked through
 *	p_with or p_nlri_next
 */ 
void
idrp_free_nlri_att_rec(p_rt,link)
idrpRoute	*p_rt;
int		link;	  /* how linked */ 
{
idrpRoute_entry	*p_rte,*p_last_rte;
idrp_rid_hash	*p_rid;
idrpRoute	*p_route;
idrpRoute	*p_last = (idrpRoute *) NULL;
idrp_attribute_record 	*p_att;

int	list_empty = TRUE;
int	i;
int	nlri_id;

	/* make sure we have been called with a supported link type -- this lets us eliminate
	 * a bunch of defensive error checking later in the code.
	 */
	assert((link == NLRI_LINKED_P_NEXT) || (link == NLRI_LINKED_P_WITH));

	/* found a route_id match, walk down nlri */
	/* list looking for the route pointer */

	nlri_id = family_to_nlri_id(p_rt->family);
	p_rte = idrp_find_routeid_chain(p_rt,&p_last_rte); 

	/* cannot find id */
	if (!p_rte)
		{
		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP (explicit replace) idrp_free_nlri_att_rec error -- can't find route ID chain for NLRI %s,peer %s ",
			iso_ptoa(&p_rt->nlri),p_rt->peer->name));
		return;
		}

	/* look for the nlri inside the route_id chain */

	trace_tf (idrp_trace_options, 0, TR_NORMAL, (" idrp_free_nlri_att_rec %x (nlri: %s) ",p_rte->p_route[nlri_id],iso_ptoa(&p_rte->p_route[nlri_id]->nlri)));

	for (p_route = p_rte->p_route[nlri_id]; ((p_route != (idrpRoute *) NULL) && (p_route != p_rt)) ; p_route = p_route->p_next_nlri) 
		{
		/* we'll need the back pointer to splice out the route */
		p_last = p_route;
		}

	/* At the end of the loop, p_route will always have a non-NULL value equal to p_rt if the route sought
	 * was found.  It will always be NULL if the route wasn't found.  If it wasn't, we have a
	 * problem.
	 */

	if (!p_route)
		{
		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("idrp_free_nlri_att_rec nlri:%s (%x) not found peer %s",iso_ptoa(&p_rt->nlri),p_rt,
				p_rt->peer->name));
		return;
		}	

	assert(p_rt == p_route);

	/* found - so let's see if we can relink the attribute
	 * record's route id list around it
	 */

	trace_tf(idrp_trace_options, 0, TR_NORMAL, (" idrp_free_nlri_att_rec nlri:%s p_att %x p_last %x",iso_ptoa(&p_rt->nlri),p_rt->p_attr,p_last));
	
	if (p_last)
		{
		trace_tf(idrp_trace_options, 0, TR_NORMAL, (" p_route_tail = %x",p_rte->p_route_tail[nlri_id]));
		if (p_rte->p_route_tail[nlri_id] == p_rt)
			{
			p_rte->p_route_tail[nlri_id] = p_last;
			trace_tf(idrp_trace_options, 0, TR_NORMAL, (" new p_route_tail = %x",p_rte->p_route_tail[nlri_id]));
			}

		/* this isn't the first thing on the chain */

		p_last->p_next_nlri = p_rt->p_next_nlri;
		p_rt->p_next_nlri = NULL;

		if (link == NLRI_LINKED_P_WITH)
			{
			trace_tf (idrp_trace_options, 0, TR_NORMAL, (" idrp_free_nlri_att_rec relink(p_last) p_with nlri:%s p_att %x p_last %x",
					iso_ptoa(&p_rt->nlri),p_rt->p_attr,p_last));
			if (p_rt->p_with_back)
				{
				p_rt->p_with_back->p_with = p_rt->p_with;
				}
			if (p_rt->p_with)
				{ 	
				p_rt->p_with->p_with_back = p_rt->p_with_back;
				}
			p_rt->p_with = p_rt->p_with_back = NULL;
			}
		}
	else	
		{
		/* first entry in the chain - readjust the head pointer 
		 * in the route_id_list and
		 * in the hash table to exclude this
		 * (note p_next_nlri could be NULL)
		 */

		p_rte->p_route[nlri_id] = p_rt->p_next_nlri;
		p_rid = idrp_rid_hash_get(p_rt->peer,p_rt->route_id_in);
		if (p_rid)
			{	
			p_rid->p_idrp_rt[nlri_id] = p_rt->p_next_nlri;  	
			}
		p_rt->p_next_nlri = NULL;

		/* If we have fallen off the end of the p_next_nlri chain, the tail had better
		 * be pointing to this entry.  If it's not, we have a big problem.
		 */
		if (p_rte->p_route[nlri_id] == NULL)
			{
			assert(p_rte->p_route_tail[nlri_id] == p_rt);
			
			/* not sure if we need to fix up the tail here or not.  If we're just
			 * going to get rid of this entry anyway we shouldn't care.  Do it for
			 * now for defensive coding...
			 */
			p_rte->p_route_tail[nlri_id] = NULL;
			}


		if (link == NLRI_LINKED_P_WITH)
			{
			trace_tf(idrp_trace_options, 0, TR_NORMAL, ("relink with withdraw p_rt->p_with = %x",p_rt->p_with));	
			if (p_rt->p_with_back)
				{
				p_rt->p_with_back->p_with = p_rt->p_with;
				}
			if (p_rt->p_with)
				{ 	
				p_rt->p_with->p_with_back = p_rt->p_with_back;
				}
			p_rt->p_with = p_rt->p_with_back = NULL;
			}
		}

	/* check for empty chains and remove route_id */

	trace_tf(idrp_trace_options, 0, TR_NORMAL,(" p_rte->p_route[%d] = %x p_route_tail[%d] = %x link = %d ",
		nlri_id,p_rte->p_route[nlri_id],
		nlri_id,p_rte->p_route_tail[nlri_id],
		link));

	NLRI_FAMILY_LOOP(i)
		{
		if (p_rte->p_route[i] != (idrpRoute *) NULL)
			{
			list_empty = FALSE;
			break;
			}
		}	

	if (list_empty)
		{

		trace_tf (idrp_trace_options, 0, TR_NORMAL,(" idrp_free_nlri_att_rec delete p_rte = %x, p_last_rte =  %x",p_rte,p_last_rte));

		/* relink the list */

		if (p_last_rte)
			{
			p_last_rte->p_next = p_rte->p_next;
			}
		else
			{
			/* this is the first entry on the list, so move the head pointer (route_id_list) forward
			 */

			p_rt->p_attr->route_id_list = p_rte->p_next;	
			}

		/* free the pointer in the hash table 
		 * - set the status of deleted all routes
		 */

			IDRP_STATUS_BIT_TEST(p_rt,IDRP_STATUS_RID_BAD)
				{
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("nlri %s (%x) peer %s has rid with bad status" ,
					iso_ptoa(&p_rt->nlri),p_rt->status,p_rt->peer->name));	
				assert(p_rt->route_id_in == 0);
				}
			else
				{		
				idrp_rid_hash *p_rid_1;
	
				p_rid_1 = idrp_rid_hash_get(p_rte->peer,p_rte->route_id);
				if (p_rid_1)
					{
					trace_tf(idrp_trace_options, 0, TR_NORMAL,("route_id %d setting entry to zero (IDRP_RID_ROUTE_GONE), got rid of all nlris",p_rte->route_id));
	
					/* zero all the idrpRoute pointers in the hash table entry 
					 */
						{
						int j = (sizeof(idrpRoute *)*IDRP_NLRI_FAMILIES_SUPPORTED);
						trace_tf(idrp_trace_options, 0, TR_NORMAL,(" p_rid_1= %x p_rid_1->route_id %d,(%x,%x,%x,%x) %x space freed",
							p_rid_1,p_rid_1->route_id,
							p_rid_1->p_idrp_rt[0], 
							p_rid_1->p_idrp_rt[1], 
							p_rid_1->p_idrp_rt[2], 
							p_rid_1->p_idrp_rt[3],j)); 
						} 
					bzero(&(p_rid_1->p_idrp_rt), (sizeof(idrpRoute *)*IDRP_NLRI_FAMILIES_SUPPORTED));
						{
						int j = (sizeof(idrpRoute *)*IDRP_NLRI_FAMILIES_SUPPORTED);
						trace_tf(idrp_trace_options, 0, TR_NORMAL,(" p_rid_1= %x p_rid_1->route_id %d,(%x,%x,%x,%x) %x space freed",
							p_rid_1,p_rid_1->route_id,
							p_rid_1->p_idrp_rt[0], 
							p_rid_1->p_idrp_rt[1], 
							p_rid_1->p_idrp_rt[2], 
							p_rid_1->p_idrp_rt[3],j)); 
						}
	
					/* Set the entry to note that the route has been deleted.  We will later (I think,
					 * I hope) be getting rid of the hash table entry altogether, but for now I guess
					 * we need to keep it around.
					 */
	
					IDRP_STATUS_BIT_SET(p_rid_1, IDRP_RID_ROUTE_GONE);
					}
				IDRP_MEM_FIT_FREE(p_rte);
				}
			}

	/* ref_cnt references the number of the routes attached to the attribute record
	 * ref_cnt should still only go to zero if there
	 * are no more route id chains on the block, so an assert() inside
	 * the tests for zero ref_cnt.  (Basically, if the ref_cnt reflects nlri and not
	 */

	/* decrement reference count on attribute block */ 
	p_rt->p_attr->ref_cnt--;

	trace_tf(idrp_trace_options, 0, TR_NORMAL,("decrement ref count attr %x (%d)  nlri %s",p_rt->p_attr,p_rt->p_attr->ref_cnt,iso_ptoa(&p_rt->nlri))); 


	if (p_rt->p_attr->ref_cnt == 0)
		{
		p_att = p_rt->p_attr;

		/* 5 step free
		 * 1) route_id_list - done
		 */
	
		assert(p_att->route_id_list == NULL);

		/* 2)  free options
		*/

		if (p_att->p_opts)
			{
			idrp_free_opts(p_att->p_opts);
			}

		/* 3) free the canonical rdpath 
		 */

		idrp_free_canon_rdpath(p_att->rd_path);

		/* 4) free the transitive attribute */

		if (p_att->p_transitive)
			{
			idrp_free_trans(p_att->p_transitive);
			}
		
		/* 5) free the data space
		 */
 
		if (BIT_TEST(p_att->local_mask, IDRP_LOCAL_NO_PDU))
			{
			free_local_att(p_att);
			}
		else
			{
			/* free the  attribute space from the pdu 
			 */

			idrp_mem_fit_free((void **) &p_att->p_attr_pdu,
					  p_att->attr_len);
			}


		/* finally let's free the attribute record
		 * - after we remember to relink around it
		 */	


		relink_free_att(p_att);
		}	
						
}
								 
/*
 * free_attr_attr_record
 *
 * 	free attribute record from this use 
 */  
void 
idrp_free_attr_rec(peer,p_att,id,relink)
idrpPeer *peer;
idrp_attribute_record *p_att;
int	id;
int	relink;
{

	/* this routine is only called with an unlinked attribute record
	 * with no nlris attached 
	 * - if you get here without - big problems
	 * refcnt should be zero 
	 */

	if (id != IDRP_INIT_ROUTE_ID)
		{
		if (p_att->ref_cnt)
			{
			trace_tf (idrp_trace_options, 0, TR_NORMAL,( "idrp_free_attr_rec(): Warning! %s has tried to free attribute record %s with route_id to init and ref_cnt = %d", 
			peer->name,p_att,p_att->ref_cnt));
			return;
			}
		IDRP_MEM_FIT_FREE(p_att->route_id_list);
		IDRP_MEM_FIT_FREE(p_att);
		return;
		}

 
	if (p_att->ref_cnt)
		{
		trace_tf(idrp_trace_options, 0, TR_NORMAL,("attribute ref cnt != 0 peer %s",peer->name));
		return;
		}

	/* free the options record */

	if (p_att->p_opts)
		{
		idrp_free_opts(p_att->p_opts);
		}
	
       /* free cannonical rd_path */

	if (p_att->rd_path)
		{
		idrp_free_canon_rdpath(p_att->rd_path);
		}

	/* free the transitive attribute */

	if (p_att->p_transitive)
		{
		idrp_free_trans(p_att->p_transitive);
		}
	
	/* free attributes from BISPDU */ 

	if (p_att->p_attr_pdu)
		{
		idrp_mem_fit_free((void **) &p_att->p_attr_pdu,p_att->attr_len);
		}

	if (relink)
		{
		relink_free_att(p_att);
		}
	else
		{	
		/* at last free the memory block */	
	
		IDRP_MEM_FIT_FREE(p_att);
		}
}


void
idrp_free_opts(p_opts)
idrpRoute_options	*p_opts;
{
idrp_next_hop		*p_hop;
idrp_next_hop		*p_hop_next;
int			i,j;

	assert(p_opts != NULL);
	

	/* next hop
	 */

	NLRI_FAMILY_LOOP(i)
		{
		for (p_hop = &p_opts->next_hop[i], j = p_opts->next_hop[i].cnt ; ((p_hop != NULL) || j > 0 ) ; p_hop = p_hop_next, j--)
			{
			p_hop_next = p_hop->p_next;

			/* free the sockaddr for net of the next_hop
			 */ 

			if (p_hop->p_net != (sockaddr_un *) NULL)
				{
				sockfree(p_hop->p_net);
				}

			if (p_hop->next_hop_snpas.cnt > 1)
				{
				idrp_free_snpa_list(&p_hop->next_hop_snpas);
				}
			if (i> 1)
				{
				IDRP_MEM_FIT_FREE(p_hop);
				}	
			}
		 } NLRI_FAMILY_LOOP_END;

	if (p_opts->p_intf != (sockaddr_un *) NULL )
		{
		sockfree(p_opts->p_intf);
		}
	if (p_opts->p_DIST_LIST_INCL)
		{
		free_dist_list(p_opts->p_DIST_LIST_INCL);
		}
	if (p_opts->p_DIST_LIST_EXCL)
		{
		free_dist_list(p_opts->p_DIST_LIST_EXCL);
		}

	/* free qos options - 
	 * -nothing for now
	 */

	if (p_opts->p_qos != NULL)
		{
		IDRP_MEM_FIT_FREE(p_opts->p_qos);
		}	

	IDRP_MEM_FIT_FREE(p_opts);
}


void
idrp_free_snpa_list(p_snpa_list)
snpa_list *p_snpa_list;
{
snpa_entry	*p_snpa;
snpa_entry	*p_snpa2 = (snpa_entry *) NULL;
int		i;

	p_snpa = (snpa_entry *) &p_snpa_list->snpa1;
	for (i = 0; i < p_snpa_list->cnt; i++)
		{
		if (p_snpa->p_next)
			{
 			p_snpa2 = p_snpa->p_next;
			}
		IDRP_MEM_FIT_FREE(p_snpa);
		if (p_snpa2)
			{
			p_snpa = p_snpa2;
			}
		}
}
						

/* relink around this attribute and free it */ 

void
relink_free_att(p_att)
idrp_attribute_record 	*p_att;
{
idrp_attribute_record 	*p_attr;
int			rib_id = p_att->rib_id;

	if (ATTR_LIST_HEAD(rib_id) == p_att)
		{
		/* attribute at begining of list */	

		ATTR_LIST_HEAD(rib_id) = p_att->next;

		/* MITRE fix - many thanks to RLM */

		if (ATTR_LIST_HEAD(rib_id))
			{	
			ATTR_LIST_HEAD(rib_id)->prev = (idrp_attribute_record *) 0;
			}
		}
	else
		{
		/* attribute mid or end list */

		p_attr = p_att->prev;
		p_attr->next = p_att->next;
		if (p_att->next)
			{
			p_att->next->prev = p_attr;
			}
		}

	IDRP_MEM_FIT_FREE(p_att);

}

void
link_att_list(p_atts)
idrp_attribute_record	*p_atts;
{
idrp_attribute_record	*p_att, *p_last_att;
int			rib_id = p_atts->rib_id;

	ATTR_LIST_EMPTY(rib_id)
		{
		ATTR_LIST_HEAD(rib_id) = p_atts;
		ATTR_LIST_HEAD(rib_id)->prev = (idrp_attribute_record *) 0;
		} 
	else
		{
		/* walk through the attribute list look for the end */
		ATTR_LIST(p_att,rib_id)
			{
			p_last_att= p_att;
			} ATTR_LIST_END;
		
		p_last_att->next = p_atts;
		p_atts->prev = p_last_att;
		}
	p_atts->next = (idrp_attribute_record *) 0;
}	

 /* free the nlris on the found chain and relink around it */

int
idrp_free_nlri_chain(p_ire)
idrpRoute_entry	*p_ire;
{
idrpRoute	*p_irt, *p_irt_next;
int		i;
int		cnt = 0;


	NLRI_FAMILY_LOOP(i)
		{
		/* Is there any idrp routes */

		for (p_irt = p_ire->p_route[i]; p_irt; p_irt = p_irt_next)
			{
			p_irt_next = p_irt->p_next_nlri;

			/* free the idrp routes
			 * - this should be linked by p_next_nlri
			 * - all list pointers should have
			 * already been taken care of 
			 * - warning if you do not,
			 * - things will screw up big
			 * time.
			 * --skh(3/27/93) my thought is
			 * -- it is too pendatic to 
			 * --- check each time we use this
			 * -- to watch lists.  But I may be
			 * -- wrong. 
			 */
			IDRP_MEM_FIT_FREE(p_irt);
			cnt++;
			}
		} NLRI_FAMILY_LOOP_END;

	return(cnt);
}

int 
dist_list_check(p_att,type,peer)
idrp_attribute_record 	*p_att;
int			type;
idrpPeer		*peer;
{
idrp_canon_rdpath	*p_can;
int			len,i;
u_char			*cp;
byte			*p_rdi_str;
rdi			*p_rdi;

	/* allocate and build a canonical form
	 * as we walk through the internal structure
	 * This cannonical form routines
	 * will allow us to quickly check the
	 */
	trace_tf (idrp_trace_options, 0, TR_NORMAL,(" DIST_LIST type = %d, LIST includes: \n",type)); 

	if (!p_att->p_opts)
		{
		/* If the option structure  does not exists
		 */

		 p_att->p_opts = (struct _idrpRoute_options*) idrp_local_mem_fit(sizeof(idrpRoute_options));
		}

	cp = p_att->attrib[type].data;
	for (i = *cp++; i; i--) 
		{
		/* create a rd canon path structure to hold things 
		 *
		 */ 
	
		p_can = (idrp_canon_rdpath *) idrp_local_mem_fit(sizeof(idrp_canon_rdpath));
		bzero(p_can,sizeof(idrp_canon_rdpath));
		len = *cp++;
		p_rdi_str = (byte *) cp;	
		p_can->p_rdi = idrp_find_global_rdi(len,p_rdi_str);
		if (p_can->p_rdi == (rdi *)NULL)
			{
			p_rdi = (rdi *) idrp_local_mem_fit(sizeof(rdi));
			p_rdi->rdi.isoa_len = len;
			bcopy(cp,p_rdi->rdi.isoa_genaddr,p_rdi->rdi.isoa_len);
			p_can->p_rdi = idrp_insert_global_rdi(&p_rdi);
			}

		cp += len;

		trace_tf(idrp_trace_options, 0, TR_NORMAL, ("     %A", 
		      sockbuild_iso((byte *) p_can->p_rdi->rdi.isoa_genaddr, p_can->p_rdi->rdi.isoa_len)));

		switch (type)
			{
			case IDRP_ATTR_DIST_LIST_EXCL:
				if (p_att->p_opts->p_DIST_LIST_EXCL)
					{
					/* we have a list already started when we get to this rdi
					 * - so see if we have a loop in RDIs for DIST_LIST
					 */
 
					if (!link_rd_list(p_can,&p_att->p_opts->p_DIST_LIST_EXCL))
						{
						free_dist_list(p_att->p_opts->p_DIST_LIST_INCL);
						peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
						peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MALF_ATTR;
						peer->last_error_pdu.data = (u_char *) p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL].data;
						peer->last_error_pdu.data_len = p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL].length; 
						peer->last_error_pdu.data2 = (u_char *) 0;
						peer->last_error_pdu.data2_len = 0;

						/* print out the error message to the gated stuff
						 * cause we want to know what caused this
						 * update to be considered illegal
						 */
						trace_tf(idrp_trace_options, 0, TR_NORMAL,("Attribute DIST_LIST_INCL problem rdi %s",sockbuild_iso((byte *) p_can->p_rdi->rdi.isoa_genaddr,
							p_can->p_rdi->rdi.isoa_len)));
						}	
					}
				else
					{
					p_att->p_opts->p_DIST_LIST_EXCL = p_can;
					}
				break;	

			case IDRP_ATTR_DIST_LIST_INCL:
				if  (p_att->p_opts->p_DIST_LIST_INCL)
					{
					/* we have a list already started when we get to this rdi
					 * - so see if we have a loop in RDIs for DIST_LIST
					 */
					if (!link_rd_list(p_can,&p_att->p_opts->p_DIST_LIST_INCL))
						{
						free_dist_list(p_att->p_opts->p_DIST_LIST_INCL);
						peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
						peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MALF_ATTR;
						peer->last_error_pdu.data = (u_char *) p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL].data;
						peer->last_error_pdu.data -= sizeof(struct _idrp_path_attr_hdr);  
						peer->last_error_pdu.data_len = p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL].length + sizeof(struct _idrp_path_attr_hdr); 
						peer->last_error_pdu.data2 = (u_char *) 0;
						peer->last_error_pdu.data2_len = 0;

						/* print out the error message to the gated stuff
						 * cause we want to know what caused this
						 * update to be considered illegal
						 */
						trace_tf(idrp_trace_options, 0, TR_NORMAL,("Attribute DIST_LIST_INCL problem rdi %s",sockbuild_iso((byte *) p_can->p_rdi->rdi.isoa_genaddr,
							p_can->p_rdi->rdi.isoa_len)));
						}
					}
				else
					{
					p_att->p_opts->p_DIST_LIST_INCL = p_can;
					}
				break;
	
			}
		}

	/* now try to find this node's rdi and RDC */

	if (type == IDRP_ATTR_DIST_LIST_EXCL)
		{
		if (find_rdi_rdilist(&idrp_this_node.rdi,p_att->p_opts->p_DIST_LIST_INCL))
			{
			peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
			peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MALF_ATTR;
			peer->last_error_pdu.data = p_att->attrib[IDRP_ATTR_DIST_LIST_EXCL].data;
			peer->last_error_pdu.data -= sizeof(struct _idrp_path_attr_hdr);  
			peer->last_error_pdu.data_len = p_att->attrib[IDRP_ATTR_DIST_LIST_INCL].length + sizeof(struct _idrp_path_attr_hdr);
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0;
			trace_tf (idrp_trace_options, 0, TR_NORMAL,(" ATTRIBUTE DIST_LIST_EXCL problem my rdi is in exclude list\n"));
			return (FALSE);
			}
		
		/* check RDC here */
		}		
		
	return (TRUE); 
	 
}

void
free_dist_list(p_rdlist)
idrp_canon_rdpath	*p_rdlist;
{
idrp_canon_rdpath	*p_rd;
idrp_canon_rdpath	*p_rd2 = 0;


	for (p_rd = p_rdlist; p_rd; p_rd = p_rd->p_next)
		{
		if (p_rd2)
			{
			IDRP_MEM_FIT_FREE(p_rd2);
			p_rd2 = 0;
			}
		p_rd2 = p_rd;
		}
	if (p_rd2)
		{
		IDRP_MEM_FIT_FREE(p_rd2);
		}
}

int
find_rdi_rdilist(p_rdi,p_list)
struct	iso_net_addr	*p_rdi;
idrp_canon_rdpath	*p_list;
{
idrp_canon_rdpath	*p_rd;
int			res = 0;

	for (p_rd = p_list;p_rd;p_rd = p_rd->p_next)
		{
		res = compare_iso_addr(p_rdi,&p_rd->p_rdi->rdi);

		if (res == ISO_ADDR_EQUAL)
			{
			return(TRUE);
			}
		}
	return(FALSE);
}	
		

int
idrp_wellknown_type(attr_type,peer,p_attrs)
int		attr_type;
idrpPeer	*peer;
idrp_path_attr	*p_attrs;
{
 	/*
	 * Well known attributes
	 */

        if (attr_type < IDRP_ATTR_MIN && attr_type > IDRP_ATTR_MAX )
		{
                /* attribute value outside of range */

		if (idrpManatt[attr_type])
                	{
			/*
			 *  7.20.3 e) Unrecognized_Well-known_Attribute
			 * for Manditory attribute
			 */

			peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_UNREC_WELKN_ATTR;
			peer->last_error_pdu.data_len = sizeof (p_attrs->attr_hdr.attr_type);
			peer->last_error_pdu.data = (u_char *) &p_attrs->attr_hdr.attr_type;
			}
		else
			{
			/* 
			 * 7.20.3.d) Missing_Well-known_Attribute
			 * for discretionary attribute 
			 */
                 
                        peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_UNREC_WELKN_ATTR;
                        peer->last_error_pdu.data_len = sizeof (&p_attrs->attr_hdr);
                        peer->last_error_pdu.data = (u_char *) &p_attrs->attr_hdr;
			}
		peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;
		trace_tf(idrp_trace_options, 0, TR_NORMAL,("IDRP %s got pdu wellknown attribute type %d outside range", peer->name, attr_type));
		return(FALSE);
		}

	return(TRUE);
}


int
idrp_transitive_att(p_attrs,cp,attr_len,attr_type,p_att)
idrp_path_attr	*p_attrs;
u_char		*cp;
int		attr_len;
int		attr_type;
idrp_attribute_record 	*p_att;
{
idrpPeer		*peer; 
idrp_attribute_entry_list *p_tr_ent;


	peer = p_att->peer_alloc;

	/* transitive route - now follow	
	 * 7.11.1 - in processing of transitive partial routes 
	 * -item a) unrecongized optional transitive attribute
	 * 
	 * 1) copy in the transitive attribute to the link lists
	 * of transitive attributes
	 * 2) set partial bit 
	 */


	p_tr_ent = (idrp_attribute_entry_list *) idrp_local_mem_fit(sizeof(idrp_attribute_entry_list));
	bzero(p_tr_ent,sizeof(idrp_attribute_entry_list));

	p_tr_ent->flags = p_attrs->attr_hdr.attr_flags | IDRP_ATTR_FLAG_PARTIAL;
	p_tr_ent->length = attr_len;
	p_tr_ent->data = (u_char *) cp;
	p_tr_ent->type = attr_type; 

	p_tr_ent->p_next = (idrp_attribute_entry_list *) 0;

	/* is there already a transitive entry? */

	if (p_att->p_transitive)
		{
		/* already transitive attribute  link to end of list */

		idrp_attribute_entry_list *p_tran;
		idrp_attribute_entry_list *p_last_tran = 0;
		

		for (p_tran = p_att->p_transitive; p_tran; p_tran = p_tran->p_next)
			{
			if (attr_type == p_tran->type)
				{	
				/* duplicate transitive, optional attribute
				 * hit the error control breaks
				 * 70.20.p  -NonDistinguishing Attribute twice
				 * Duplicate attribute
				 */ 

				peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_DUP_ATTR;
				peer->last_error_pdu.data = (u_char *) &p_attrs->attr_hdr;
				peer->last_error_pdu.data_len = sizeof(p_attrs->attr_hdr.attr_type) + attr_len;
				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0;


				/* set pdu value in results so
				* that cleanup will not clear away pdu 
				*/

				trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP %s attribute %d present twice", peer->name, attr_type));
				return(FALSE);
				}

			p_last_tran = p_tran;
			}
		p_last_tran->p_next = p_tr_ent; 	
		}
	else
		{
		/*
		 * no transitive attributes, start  list
		 */
 
		p_att->p_transitive = p_tr_ent;
		}
	return(TRUE);
} 



int
wellknown_attrib_twice(attr_type,p_attrs,p_res, peer,p_att,attr_len) 
int			attr_type;
idrp_path_attr		*p_attrs;
parse_results		*p_res;
idrpPeer		*peer;	
idrp_attribute_record *p_att;
int			attr_len;
{  
	if (!p_att->attrib[attr_type].present)
		{
		if (DISTINGUISHED_ATT(attr_type)) 
			{
			return(WELLKNOWN_DIST_ATTRIBUTE_ONCE);
			}
		else
			{
			return(WELLKNOWN_ATTRIBUTE_ONCE);
			}
		}


	/* The attribute has been seent twice,
	 * so find out if it is a ROUTE_SEPARATOR 
	 * -- which is legal or
	 * an illegal repitition
	 */

	if (attr_type == IDRP_ATTR_ROUTE_SEPARATOR)
		{
		/* process the dual route separator for the
		 * packing of two Route Separators within
		 * one 1 BISPDU 
		 * - here we need to create an additional
		 * - attribute record for each ROUTE_SEPARATOR 
		 */


		/* trace_tf that we are switching attribute records */

		trace_tf (idrp_trace_options, 0, TR_NORMAL,("Dual ROUTE separator in PDU from peer %s",peer->name)); 
		
		p_att = (idrp_attribute_record *) idrp_local_mem_fit(sizeof(idrp_attribute_record));
		bzero (p_att,sizeof(idrp_attribute_record));
	
		/* copy this new attribute record into 
		 * the route server portion of the results
		 * and link the attribute on the parse chain for
		 * this pdu  
		 */

		if (p_res->p_att_rs)
			{
			/* 2 route or more 
			 * route  separators already
			 * exist
			 */ 

			p_res->p_att_rs->p_parse_next = p_att;
			}

		p_res->p_att_rs->p_parse_next = p_att; 
		p_att->peer_alloc = peer;
		p_att->route_id_list = (idrpRoute_entry * ) idrp_local_mem_fit(sizeof(idrpRoute_entry));
		bzero (p_att,sizeof(idrpRoute_entry));
		p_att->route_id_list->peer = peer;

		return (WELLKNOWN_ROUTE_SEPARATOR_TWICE);
		}


	/* type appeared twice in PDU  - error */

	peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
	if (DISTINGUISHED_ATT(attr_type)) 
		{
		/*  70.20.3 (0) -  Distinguishing Attribute twice
		 *  List is malformed
		 */
		
		peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_MALF_ATTR;
		peer->last_error_pdu.data_len = 0;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;

		/* no data delete pdu prior to error processing
		*/

		p_res->p_pdu = (u_char *) 0; 
		}
	else
		{
		/* 70.20.p  -NonDistinguishing Attribute twice
		* Duplicate attribute
		*/ 

		peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_DUP_ATTR;
		peer->last_error_pdu.data_len = (sizeof(struct _idrp_path_attr_hdr) + attr_len);
		peer->last_error_pdu.data = (u_char *) &p_attrs->attr_hdr;
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;

		/* data do not delete pdu prior to error processing
		*/

		p_res->p_pdu = p_att->p_attr_pdu; 
		}

	trace_tf(idrp_trace_options, 0, TR_NORMAL, ("IDRP %s attribute %d present twice", peer->name, attr_type));

	return(ERROR_WELLKNOWN_ATTRIBUTE_TWICE);

}
	

void
idrp_copy_nondist_att(p_att,p_res)
idrp_attribute_record	*p_att;
parse_results		*p_res;
{
int	i;
int	cnt = 0;
int	len = 0; 
u_char	*cp;
u_int16  hold;
idrp_attribute_entry_list	*p_trans = 0;
idrp_attribute_entry_list	*p_trans_last = 0;
idrp_attribute_entry_list	*p_trans_new = 0;


	/* copy in the attributes from the original record
	 * by:
	 * 1) calculating the space you need for the attributes
	 *	information
	 * 
	 * 	1.1) add up lengths of all non_distinguishing attributes
	 *		(wellknown, user, and transitive)	
	 * 	1.2) allocate space for these attributse
	 *	1.3) copy the attributes into new attribute
	 * 		record by the types. 
	 */


	IDRP_ATTR_NUM_LOOP(i)
		{
		if ((p_res->p_att->attrib[i].present) && 
			(DISTINGUISHED_ATT(i) == 0) &&
			(i != IDRP_ATTR_ROUTE_SEPARATOR))

			{
			hold = ntohs(p_res->p_att->attrib[i].length);
			len += hold;
			cnt++;
			}
		} IDRP_ATTR_NUM_LOOP_END;

	if ((p_res->p_att->idrp_mask & IDRP_ATTBT_USR) != 0 )
		{
		IDRP_USR_ATTR_NUM_LOOP(i)
			{
			if (p_res->p_att->usr_attrib[i].present)
				{
				hold = ntohs(p_res->p_att->usr_attrib[i].length);
				len += hold;
				cnt++;
				}
			} IDRP_USR_ATTR_NUM_LOOP_END;
		}

	IDRP_TRANS_LIST(p_trans,p_att->p_transitive)
		{
		hold = ntohs(p_trans->length);
		len += hold;
		cnt++;
		} IDRP_TRANS_LIST_END;	

		

	trace_tf (idrp_trace_options, 0, TR_NORMAL,(" copying %n non-distinguishing attributes to new attribute, peer %s  route_id = %d ",
			cnt,p_att->peer_alloc->name,p_att->route_id_list->route_id));

	
	/* add in 10 byte fudge factor */

	len += 10;
	p_att->attr_len = len;
 	p_att->p_attr_pdu = (u_char *) idrp_local_mem_fit(len);
	cp = (u_char *) p_att->p_attr_pdu;

	IDRP_ATTR_NUM_LOOP(i)
		{
		if ((p_res->p_att->attrib[i].present) && 
			(DISTINGUISHED_ATT(i) == 0) &&
			(i != IDRP_ATTR_ROUTE_SEPARATOR))
			{ 
			bcopy(&p_res->p_att->attrib[i],&p_att->attrib[i],sizeof(idrp_attribute_entry));
			hold = ntohs(p_res->p_att->attrib[i].length);
			bcopy(p_res->p_att->attrib[i].data,cp,hold);
			p_att->attrib[i].data = cp;
			cp +=hold;
			}
		} IDRP_ATTR_NUM_LOOP_END;

	if ((p_res->p_att->idrp_mask & IDRP_ATTBT_USR) != 0 )
		{
		IDRP_USR_ATTR_NUM_LOOP(i)
			{
			if (p_res->p_att->usr_attrib[i].present)
				{
				bcopy(&p_res->p_att->attrib[i],&p_att->attrib[i],sizeof(idrp_attribute_entry));
				hold = ntohs(p_res->p_att->attrib[i].length);
				bcopy(p_res->p_att->attrib[i].data,cp,hold);
				p_att->usr_attrib[i].data = cp;
				cp +=hold;
				}
			} IDRP_USR_ATTR_NUM_LOOP_END;
		}

	IDRP_TRANS_LIST(p_trans,p_res->p_att->p_transitive)
		{
		p_trans_new = (idrp_attribute_entry_list *) idrp_local_mem_fit(sizeof(idrp_attribute_entry_list));
		bcopy(p_trans,p_trans_new,sizeof(idrp_attribute_entry_list));
		p_trans_new->p_next = (idrp_attribute_entry_list *) 0;
		hold = ntohs(p_trans->length);
		bcopy(p_trans->data,cp,hold);
		p_trans->data = cp;
		cp +=hold;

		if (p_trans_last)
			{
			/* more than one transitive attribute */
			p_trans_last->p_next = p_trans_new;
			}
		else
			{
			/* first transitive attribute in pdu */
		
			p_att->p_transitive = p_trans_new;
			}

		p_trans_new->p_next = (idrp_attribute_entry_list *) 0;
		p_trans_last = p_trans_new;
		
		} IDRP_TRANS_LIST_END;	

}		
		

int 
send_next_hop(p_atts,peer,p_oatt_nexthop)
idrp_attribute_record	*p_atts;
idrpPeer		*peer;
idrp_attribute_entry	*p_oatt_nexthop;
{
	/* you don't have to turn it off
	 * cause it never was on
	 */


	if (BIT_TEST(p_atts->local_mask, IDRP_LOCAL_DONT_SEND_NEXT_HOP) 
		|| !BIT_TEST(p_atts->local_mask, IDRP_LOCAL_ROUTE_SERVER_FLAG))
		{
		/* case 1:  next hop is only set if 
		 * 1) local configuration sets it 
		 * 2) route server received and route server flag
		 *    set    
		 *  
		 * - next hop may be there, but p_att->next_hop won't be set
		 * 
		 * case 2: 
		 * 1) local route  has don't send next hop set
		 */

		/* set the mask to zero to indicate nothing to send via xmit attributes
		 */

		if (p_atts->attrib[IDRP_ATTR_NEXT_HOP].present)
			{
			p_oatt_nexthop->present = FALSE;
			return (FALSE);
			}
		return (FALSE);
		}

	if (p_atts->peer_alloc->type == IDRP_PEER_LOCAL)
		{
		/* send anything from the local peer
		 * - if on, copy the next_hop to the xmit data entry 
		 */
		if (p_atts->attrib[IDRP_ATTR_NEXT_HOP].present)
			{
			bcopy((caddr_t *)&p_atts->attrib[IDRP_ATTR_NEXT_HOP],(caddr_t *)p_oatt_nexthop,sizeof(idrp_attr_data_entry));
			return(TRUE);	
			}  	
		return(FALSE);
		}

	/* now this node has route server capable route
	 * - it is either internal or external 
	 * that has been received or configured
	 */ 

	 /* Does this node support route server? 
	 *  - which really means  3rd party routes
	 * 
	 * to pass a  route server route 
	 * 5 conditions must be matched
	 * 7.12.4 p41 IS version of IDRP 
	 * 
	 * i)   IDRP_SERVER allowed flag on in pdu (test 1 in above sequence)
	 * ii)  All BIS on same subnet  - (use with_subnet style calls to gated (test 3)) 
	 *  
	 * iii) Managed object is true (test 2 in test below)
	 * iv)  1st and subsequent receipients are in different
	 *      routing domains (test 3 in if ) 
	 *      - translate this node and next node in different
	 *        routing domains - if external peer OK
	 * v) DIST_LISTS have been obeyed
	 *    - we don't get here without that clause being fufilled
	 *     
	 */

	if ((peer->type == IDRP_PEER_EXTERNAL) && (idrp_this_node.route_server)
		&& idrp_same_subnet(p_atts,peer)) 
		{
		/* you are going to send this next hop and
		 * you didn't turn off the route server flag
		 * - check to see that the next hop was not
		 *   this node
		 */
		if (p_atts->attrib[IDRP_ATTR_NEXT_HOP].present)
			{
			bcopy((caddr_t *) &p_atts->attrib[IDRP_ATTR_NEXT_HOP],(caddr_t *)p_oatt_nexthop,sizeof(idrp_attr_data_entry));
			return(TRUE);
			}  	
		return(FALSE);
		}

 	/*  - one of the  5 conditions has failed 
	 * and this is not a local route
	 * 
	 */

	if (p_atts->attrib[IDRP_ATTR_NEXT_HOP].present)
		{
		p_oatt_nexthop->present = FALSE;
		}	
	return(FALSE);
}


int 
fill_multi_exit(p_atts,peer,p_xmit)
idrp_attribute_record	*p_atts;
idrpPeer		*peer;
idrp_attr_data_entry	*p_xmit;
{
idrpRoute_options	*p_opts;

	p_opts = p_atts->p_opts;
	p_xmit->present = FALSE;

	if (peer->type == IDRP_PEER_EXTERNAL)
		{
		if (BIT_TEST(p_opts->status, IDRP_OPTS_MULTI_EXIT))
			{
			/* multiple exit set by policy
			 * - either local results
			 *   or by policy on outbound  
			 */

			p_xmit->present = TRUE;
			p_xmit->flags = IDRP_ATTRFLG_MULTI_EXIT_DISC;
			p_xmit->length = IDRP_MULTI_EXIT_DISC_LENGTH;
			p_xmit->attr = IDRP_ATTR_MULTI_EXIT_DISC;
			p_xmit->data = (u_char *) &p_xmit->data_in[0];
			p_xmit->data_in[0] = p_opts->multi_exit[peer->id]; 
			trace_tf(idrp_trace_options, 0, TR_NORMAL,("multi_exit to be sent to peer %s value %d",peer->name,
				p_opts->multi_exit[peer->id]));
			}
		else
			{
			trace_tf(idrp_trace_options, 0, TR_NORMAL,("multi_exit not to be sent to peer %s (rt from peer  %s) opts status %x value %d",
				peer->name, p_atts->peer_alloc->name,p_opts->status,
				p_opts->multi_exit[peer->id]));
			}	
		}
	else if (peer->type == IDRP_PEER_INTERNAL) 
		{
		/*  does route come from an external peer?
		 */
 
		if (p_atts->peer_alloc->type == IDRP_PEER_EXTERNAL) 
			{ 
			IDRP_STATUS_BIT_TEST(p_opts,IDRP_OPTS_MULTI_EXIT)
				{
				bcopy(&p_atts->attrib[IDRP_ATTR_MULTI_EXIT_DISC],p_xmit,sizeof(idrp_attribute_entry));
				p_xmit->attr = IDRP_ATTR_MULTI_EXIT_DISC;
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("multi_exit to be sent to peer %s value %x",peer->name,p_xmit->data));
				}
			else
				{
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("multi_exit not to be sent to peer %s (rt from peer  %s) opts status %x value %d",
					peer->name, p_atts->peer_alloc->name,p_opts->status,
					p_opts->multi_exit[peer->id]));
				}	
			}
		}

	return(p_xmit->present);
		
				
}

boolean
send_multi_exit(p_att,peer,p_xmit)
idrp_attribute_record   *p_att;
idrpPeer                *peer;
idrp_attr_data_entry	*p_xmit;
{

        if (p_att->attrib[IDRP_ATTR_MULTI_EXIT_DISC].present == FALSE)
                {
                return(FALSE);
                }

        /* if route was generated locally, it
         * passed to internal peers and external peers
         */

        if (p_att->peer_alloc->type == IDRP_PEER_LOCAL)
                {
                return (FALSE);
                }

        if (p_att->peer_alloc->type == IDRP_PEER_INTERNAL &&
                peer->type == IDRP_PEER_INTERNAL)
                {
                /* should not be res-sending internal routes
                 */

                task_quit(0);
                }

        if (p_att->peer_alloc->type == IDRP_PEER_INTERNAL &&
                (peer->type == IDRP_PEER_EXTERNAL) &&
                (idrp_my_rdi_rdpath(p_att) ))
                {
	 	/* It's an internal peer,
                 * it's my RDI so it is a local route
                 * - And we are sending it to an external peer
                 * - so depending on the peer value for Multi-exit discriminator
                 * - re-write the MULTI_EXIT value
                 *
                 * Here's what I'm going to do for Bob Moose
                 * -- if he configures MULTI_EXIT on local
                 * -- it will automatically come up with
                 * -- appropriate values send at other BIS
                 *    that speak to other external nodes
                 *    if he configures multi-exit on those nodes
                 *
                 *
                 */

                if (p_att->p_opts->multi_exit[peer->id] >= 0 && p_att->p_opts->multi_exit[peer->id] <=  IDRP_MAX_MULTI_EXIT_VALUE )
                        {
			if (BIT_TEST(p_att->p_opts->status, IDRP_OPTS_MULTI_EXIT))
				{
				/* multiple exit set by policy
				 * - either local results
				 *   or by policy on outbound  
				 */

				p_xmit->present = TRUE;
				p_xmit->flags = IDRP_ATTRFLG_MULTI_EXIT_DISC;
				p_xmit->length = IDRP_MULTI_EXIT_DISC_LENGTH;
				p_xmit->attr = IDRP_ATTR_MULTI_EXIT_DISC;
				p_xmit->data = (u_char *) &p_xmit->data_in[0];
				p_xmit->data_in[0] = p_att->p_opts->multi_exit[peer->id]; 
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("multi_exit to be sent to peer %s value %d",peer->name,
				peer->name, p_att->peer_alloc->name,p_att->p_opts->status,
				p_att->p_opts->multi_exit[peer->id]));
				}
		
			}
		else
			{
                	if (peer->multi_exit >= 0 && peer->multi_exit <=  IDRP_MAX_MULTI_EXIT_VALUE )
				{
				bcopy(&p_xmit->data_in[0],&peer->multi_exit,4);
				p_xmit->present = TRUE;
				p_xmit->flags = IDRP_ATTRFLG_MULTI_EXIT_DISC;
				p_xmit->length = IDRP_MULTI_EXIT_DISC_LENGTH;
				p_xmit->attr = IDRP_ATTR_MULTI_EXIT_DISC;
				p_xmit->data = (u_char *) &p_xmit->data_in[0];
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("MED from p_att out of range, sent peer MED to peer %s (rt from peer  %s) opts status %x value %d",
				peer->name, p_att->peer_alloc->name,p_att->p_opts->status,peer->multi_exit));
				}
			else
				{
	                        p_xmit->present = FALSE;
				trace_tf(idrp_trace_options, 0, TR_NORMAL,("MED from p_att out of range, no MED to peer %s (rt from peer  %s) opts status %x value %d",
				peer->name, p_att->peer_alloc->name,p_att->p_opts->status,peer->multi_exit));
				}
                        }
                return(TRUE);
                }

        /* if the route was generated externally
         * it cannot have the multi-exit-disc passed to
         * external peers
         */

        if (p_att->peer_alloc->type == IDRP_PEER_EXTERNAL &&
                peer->type == IDRP_PEER_EXTERNAL)
                {
                p_att->attrib[IDRP_ATTR_MULTI_EXIT_DISC].present = FALSE;

               return (TRUE);
                }

        /* external routes can send to internal routes
         * local routes can send to external peers
         * - test routes should get this far
         */

        return(FALSE);
}


idrp_parse_next_hop(dataptr,attr_len,p_att)
u_char	*dataptr;
int	attr_len;
idrp_attribute_record *p_att;
{

/* fill in the next hop information */
int	nsnpa = 0;
int	len = 0;
int	nlri_len = 0;
int 	i;
int	family;
int	nlri_id;
int	next_hop_len; 

idrp_next_hop_attribute *p_att_hop;
idrp_next_hop_info *p_att_hop_info;
u_char *p,*snpa_start, *p_next_hop_start;
snpa_entry 	*p_snpa;
snpa_entry	*p_snpa_old = 0;
nlri_proto_info	*p_proto; 
sockaddr_un *p_ip,*p_iso;
idrp_next_hop *p_next_hop = NULL;
struct	iso_net_addr	gw;
sockaddr_un 	*p_gw = (sockaddr_un *) &gw;
idrp_next_hop *p_last_next_hop[IDRP_NLRI_FAMILIES_SUPPORTED];

	NLRI_FAMILY_LOOP(i)
		{
		p_last_next_hop[i] = (idrp_next_hop *) NULL;
		}	

	next_hop_len = attr_len - 1; 
	p_att_hop = (idrp_next_hop_attribute *) dataptr;
	if (p_att->p_opts == NULL)
		{
		p_att->p_opts = (idrpRoute_options *) idrp_local_mem_fit(sizeof(idrpRoute_options));
		}

	if (p_att_hop->idrp_server_allowed ==  IDRP_SERVER_ALLOWED)
		{
		/* build those next hop and snpa structures for the task 
		 * next_hop - in sockaddr_un style
		 * snpa_list - in the list style
		 */

		p_att->local_mask |= IDRP_LOCAL_ROUTE_SERVER_FLAG;
		p_att->p_opts->status |= IDRP_OPTS_ROUTE_SERVER; 
		}


	p_att_hop_info = (idrp_next_hop_info * ) &p_att_hop->next_hops[0]; 
	NLRI_FAMILY_LOOP(i)
		{
		if (p_att->p_opts->next_hop[i].cnt > 0)
			{
			for (p_next_hop = &p_att->p_opts->next_hop[nlri_id];p_next_hop; p_next_hop = p_next_hop->p_next)
				{
				p_last_next_hop[i] = p_next_hop;
				}
			}
		else
			{
			p_last_next_hop[i] = NULL;
			}		
		} NLRI_FAMILY_LOOP_END;

	
	while (next_hop_len > 0)
		{
		/* copy in the protocol information
		 */

		p_proto = (nlri_proto_info *) &p_att_hop_info->next_hop_proto;	
		p_next_hop_start = (u_char *) p_proto; 				
		family = (int) proto_to_socktype((char *) &p_proto->proto_type,p_proto->proto_len,&p_proto->proto_val[0]);
		nlri_id = family_nlri[family].nlri_id;

		/* set up the next hop area to be copied into */
 
		if (p_last_next_hop[nlri_id])
			{
			p_next_hop = p_last_next_hop[nlri_id] = (idrp_next_hop *) idrp_local_mem_fit(sizeof(idrp_next_hop));
			}
		else
			{			 
			p_next_hop = &p_att->p_opts->next_hop[nlri_id];
			}

		p_last_next_hop[nlri_id] = p_next_hop;

		/* copy over the gateway information
		 * into a gw structure to
		 * to sockdup
		 */
 
		len = p_att_hop_info->net_len; 
		p_gw->a.sa_family = family; 
		switch (family)
			{
			case AF_ISO:
				bcopy(&p_att_hop_info->net,&gw.isoa_genaddr,len); 	
				p_gw->a.sa_len =  len + 2;
				break;
	
			case AF_INET:
				/* copy in the format for
				 * ip addresses
				 */
	
				p_gw->in.sin_len = sizeof(p_ip->in); 
				p_gw->in.sin_port = 0;
				bcopy(&p_att_hop_info->net,&p_gw->in.sin_addr,len); 	
				break;

			}
		p_next_hop->p_net = sockdup(p_gw); 

		/* index over the NET and try for SNPAs */

		if (p_att->next_hop == (sockaddr_un *) NULL)
			{
			p_att->next_hop =  p_next_hop->p_net;
			}

		trace_tf(idrp_trace_options, 0, TR_NORMAL,(" next_hop = %A",sockbuild_iso((byte *) p_next_hop->p_net->a.sa_data,
				p_next_hop->p_net->a.sa_len)));

		/* set-up copy to copy in SNPAs
		 */

		p = (u_char *) &p_att_hop_info->net;	 	
		p += len;
	
		
		/* set-up start count */

		nsnpa = *p;
		if (nsnpa) 
			{
			snpa_start = p++;

			/* fill in the snpa list in the attribute record */
			
			p_next_hop->next_hop_snpas.cnt = nsnpa;

			/* copy in the snpa one at a time into the snpa list */

			p_snpa = &p_next_hop->next_hop_snpas.snpa1;

			for (i = 0; i < nsnpa; i++,p_snpa_old = p_snpa) 
				{
				/* the first snpa is contained in the snpa_list structure
				 * later snpas require task memory , and must be linked on list
				 */

				if (p_snpa_old)
					{
					/* all others need to be gotten via memory allocation
					 * link via p_next 
					 */ 
					p_snpa = (snpa_entry *) idrp_local_mem_fit(sizeof(snpa_entry));
					p_snpa_old->p_next = p_snpa;
					}

				/* set-up the snpa_entry structure
				 */

				p_snpa->p_next = (snpa_entry *) 0;
				p_snpa->len = *p++;

				/* if zero, length - flag it for tracing */

				if (p_snpa->len == 0) 
					{
					trace_tf(idrp_trace_options, 0, TR_NORMAL, ("snpa of length zero - don't save peer %s \n",p_att->peer_alloc->name));
					bzero (p_snpa->snpa,SNPA_SIZE);
					} 
				else
					{
					/* copy over the bytes */

					bcopy(p,p_snpa->snpa,p_snpa->len);
					}
				p +=p_snpa->len;
				}
			p_next_hop->next_hop_snpas.pdu_len = p - snpa_start;	
			}
		else
			{
			/* no snpas - so set the pointer in attribute record to zero */
			p++;
			p_next_hop->next_hop_snpas.cnt  = 0;
			}

		nlri_len = p - p_next_hop_start; 

		trace_tf(idrp_trace_options, 0, TR_NORMAL,("parse_next_hop, nlri_id %d family %x attr length %d  nlri len %d snpa_cnt %x snpas_length %d remaining_len %d",
				nlri_id,family,attr_len,nlri_len,
				p_next_hop->next_hop_snpas.cnt,
				p_next_hop->next_hop_snpas.pdu_len,
				(next_hop_len - nlri_len)));

		next_hop_len -= nlri_len; 
		p_att_hop_info = (idrp_next_hop_info *)  p;
	 	}
	return(TRUE);
}

void
idrp_copy_opts(p_opts1,p_opts2,rib_id)
idrpRoute_options	*p_opts1;
idrpRoute_options	*p_opts2;
int			rib_id;
{
int i,j,k;
idrp_next_hop		*p_hop,*p_hop2;
idrp_canon_rdpath	*p_rd,*p_rd2,*p_rd_list;
snpa_entry		*p_snpa, *p_snpa2;
idrp_distinguish_att	*p_qos1, *p_qos2;

	/* if no options - no optional
	 * parameters to copy
	 */

	if (p_opts1 == (idrpRoute_options *) NULL)
		{
		return;
		}

	bzero(p_opts2,sizeof(idrpRoute_options));

	p_opts2->rib_id = p_opts1->rib_id;
	p_opts2->status = p_opts1->status;
	for (i = 0; i < p_opts1->multi_exit_peers; i++)
		{
		p_opts2->multi_exit[i] = p_opts1->multi_exit[i];
		}	 

	p_opts2->multi_exit_rcvd = p_opts1->multi_exit_rcvd;
	p_opts2->capacity.operator = p_opts1->capacity.operator;
	p_opts2->capacity.value = p_opts1->capacity.value;
	p_opts2->hopcnt_pref = p_opts1->hopcnt_pref;
	p_opts2->hopcount = p_opts1->hopcount;

	/* copy over next_hop information */

	NLRI_FAMILY_LOOP(i)
		{
		for (p_hop = &p_opts1->next_hop[i],p_hop2 = &p_opts2->next_hop[i], j = p_opts1->next_hop[i].cnt; j ; p_hop = p_hop->p_next,p_hop2 = p_hop2->p_next, j--)
			{
			/* duplicate the sockaddress 
			 */

			p_hop2->p_net = sockdup(p_hop->p_net);
			p_hop2->cnt = p_hop->cnt;

			/* duplicate the next_hop snpa addresses
			 * snpa - points to old snpa list 
			 * snpa2 - points to new snpa list 
			 * k is the count of the snpas
			 * 	
			 */
			if (p_hop->next_hop_snpas.cnt)
				{
				p_hop2->next_hop_snpas.cnt = p_hop->next_hop_snpas.cnt; 
				p_hop2->next_hop_snpas.pdu_len = p_hop->next_hop_snpas.pdu_len; 
				p_snpa = &p_hop->next_hop_snpas.snpa1;
				p_snpa2 = &p_hop2->next_hop_snpas.snpa1;
	
				for (k = p_hop->next_hop_snpas.cnt; k; k--, p_snpa = p_snpa->p_next, p_snpa2 = p_snpa2->p_next)
					{
					/* there are some snpas from the next hop
					 */
				
					p_snpa2->len = p_snpa->len;	
					bcopy(&p_snpa->snpa[0],&p_snpa2->snpa[0],p_snpa->len);			

					/* is there an extended list of snpas
					 */
		
					if (p_snpa->p_next)
						{
						p_snpa2->p_next = (snpa_entry *) idrp_local_mem_fit(sizeof(snpa_entry));
						}				
					}
				}	

			/* check to see if more than 1 next hop exists 
			 */

			if (p_hop->cnt > 1 && p_hop->p_next != NULL)
				{
				p_hop2->p_next = (idrp_next_hop *) idrp_local_mem_fit(sizeof(idrp_next_hop));
				}
			
			}
		} NLRI_FAMILY_LOOP_END;

	/* copy over DIST_LIST information
	 * - first the include list
	 * - then the exclude list 
	 * - should only have one
	 */

	if (p_opts1->p_DIST_LIST_INCL)
		{
		for (p_rd = p_opts1->p_DIST_LIST_INCL; p_rd; p_rd = p_rd->p_next)
			{
			p_rd2 = (idrp_canon_rdpath *) idrp_local_mem_fit(sizeof(idrp_canon_rdpath));
			bcopy(p_rd,p_rd2,sizeof(idrp_canon_rdpath));
			if (p_opts2->p_DIST_LIST_INCL == (idrp_canon_rdpath *) NULL)
				{
				p_rd_list = p_opts2->p_DIST_LIST_INCL = p_rd2;
				}
			else
				{
				p_rd_list->p_next = p_rd2;
				p_rd_list = p_rd2;
				}
				
			p_rd2->p_next = (idrp_canon_rdpath *) NULL;				
			}
		}

	 if (p_opts1->p_DIST_LIST_EXCL)
		{	
		for (p_rd = p_opts1->p_DIST_LIST_EXCL; p_rd; p_rd = p_rd->p_next)
			{
			p_rd2 = (idrp_canon_rdpath *) idrp_local_mem_fit(sizeof(idrp_canon_rdpath));
			bcopy(p_rd,p_rd2,sizeof(idrp_canon_rdpath));
			if (p_opts2->p_DIST_LIST_EXCL == (idrp_canon_rdpath *) NULL)
				{
				p_rd_list = p_opts2->p_DIST_LIST_INCL = p_rd2;
				}
			else
				{
				p_rd_list->p_next = p_rd2;
				p_rd_list = p_rd2;
				}
			p_rd2->p_next = (idrp_canon_rdpath *) NULL;
			}
		}

	/* save the local options 
	 */

	p_opts2->p_local_opts = p_opts1;

	/*  qos support 
	 */

	p_qos1 = p_opts1->p_qos;
	if (rib_id == RIB_ID_DEFAULT && p_qos1 != NULL)
		{ 
		p_qos2 = (idrp_distinguish_att *) idrp_local_mem_fit(sizeof(idrp_distinguish_att));
		p_qos2->rib_id	= p_qos1->rib_id;
		p_qos2->idrp_mask = p_qos1->idrp_mask;

		p_qos2->delay.value = p_qos1->delay.value;
		p_qos2->delay.operator = p_qos1->delay.operator;

		p_qos2->error.value = p_qos1->error.value;
		p_qos2->error.operator = p_qos1->error.operator;
		p_qos2->expense.value = p_qos1->expense.value;
		p_qos2->expense.operator = p_qos1->expense.operator;
		p_qos2->priority.value = p_qos1->priority.value;
		p_qos2->priority.operator = p_qos1->priority.operator;

		p_qos2->sec_type.value = p_qos1->sec_type.value;
		p_qos2->sec_type.operator = p_qos1->sec_type.operator;
		p_qos2->sec_value.value = p_qos1->sec_value.value;
		p_qos2->sec_value.operator = p_qos1->sec_value.operator;
		}
	else if (rib_id > 0)
		{
#ifdef	IDRP_QOS
		p_opts2->p_qos = p_qos2 = (idrp_distinguish_att *) idrp_local_mem_fit(sizeof(idrp_distinguish_att));
		bcopy(&qos_rib[rib_id]->att,p_qos2,sizeof(idrp_distinguish_att)); 
#else  /* IDRP_QOS */
		p_opts2->p_qos = (idrp_distinguish_att *)  NULL;
#endif /* IDRP_QOS */
		}

	/* gateways should be copied in elsewhere
	 * - p_iso
	 * - p_gw
	 * - p_intf
	 */

}

boolean
opts_compare(p_opts1,p_opts2)
idrpRoute_options	*p_opts1;
idrpRoute_options	*p_opts2;
{
int i;
idrp_canon_rdpath *p_rd;

	/* compare all options except for
	 *  next_hop - which also has SNPAs
	 *  for now
	 *  p_iso, p_gw, p_intf - are short cuts for the next hop
	 *
	 */

	/* 
	 *  parameters to compare 
	 *  1) status,  
	 */

	if (p_opts1 == (idrpRoute_options *) NULL ||
		p_opts2 == (idrpRoute_options *)NULL )
		{
		return(FALSE);
		}

	if ((p_opts2->rib_id != p_opts1->rib_id) ||
		(p_opts1->status !=p_opts2->status))
		{
		return(FALSE);
		} 

	/* compare input and output multi_exit_disc */ 

	if (p_opts2->multi_exit_rcvd != p_opts1->multi_exit_rcvd)
		{
		return(FALSE);
		}

	if (p_opts2->multi_exit_peers != p_opts1->multi_exit_peers)
		{
		return(FALSE);
		}
 
	for (i = 0; i < p_opts1->multi_exit_peers; i++)
		{
		if (p_opts2->multi_exit[i] != p_opts1->multi_exit[i])
			{
			return(FALSE);
			}
		}

	/* check capacity, hopcnt
	 */
	   
	if ((p_opts2->capacity.operator != p_opts1->capacity.operator) || 
		( p_opts2->capacity.value != p_opts1->capacity.value)
		|| (p_opts2->hopcnt_pref != p_opts1->hopcnt_pref))
		{
		return(FALSE);
		}


	/* check to see that DIST_LIST_INCL 
	 * - are the same
	 */

	if ((p_opts1->p_DIST_LIST_INCL == NULL)  &&
		(p_opts2->p_DIST_LIST_INCL != NULL))
		{
		return(FALSE);
		}

	/* both DIST_LIST_INCL are the same -
	 * - compare the LISTS - if you can add an RDI
	 *	 to the DIST_LIST we have different lists
	 *
	 */ 

	for (p_rd = p_opts1->p_DIST_LIST_INCL; p_rd; p_rd = p_rd->p_next)
		{
		if (link_rd_list(p_rd,&p_opts2->p_DIST_LIST_INCL) != FALSE)
			{
			unlink_rd_list(p_rd,&p_opts2->p_DIST_LIST_INCL);
			return(FALSE);
			}	
					
		}
	
	if ((p_opts1->p_DIST_LIST_EXCL == NULL) &&
		(p_opts2->p_DIST_LIST_EXCL))
		{
		return(FALSE);
		}

	for (p_rd = p_opts1->p_DIST_LIST_EXCL; p_rd; p_rd = p_rd->p_next)
		{
		if (link_rd_list(p_rd,&p_opts2->p_DIST_LIST_EXCL) != FALSE)
			{
			unlink_rd_list(p_rd,&p_opts2->p_DIST_LIST_EXCL);
			return(FALSE);
			}	
					
		}
	
	return(TRUE);
}

int
idrp_opts_to_mask(p_opts,p_next_hop,rib_id)
idrpRoute_options       *p_opts;
sockaddr_un             *p_next_hop;
int			rib_id;
{
int idrp_mask = IDRP_ATTBT_MANDATORY;


        if (p_next_hop != (sockaddr_un *) 0)
                {
                idrp_mask |= IDRP_ATTBT_NEXT_HOP;
                }

        if (p_opts == (idrpRoute_options *) 0)
                {
                return(idrp_mask);
                }

        if (p_opts->multi_exit)
                {
                idrp_mask |= IDRP_ATTBT_MULTI_EXIT_DISC;
                }

	IDRP_STATUS_BIT_TEST(p_opts,IDRP_OPTS_EXT_INFO)	
                {
                idrp_mask |= IDRP_ATTBT_EXT_INFO;
                }

        if (p_opts->p_DIST_LIST_INCL)
                {
                idrp_mask |= IDRP_ATTBT_DIST_LIST_INCL;
                }
        else
                {
                if (p_opts->p_DIST_LIST_EXCL)
                        {
                        idrp_mask |= IDRP_ATTBT_DIST_LIST_EXCL;
                        }
                }

        /* QOS attributes later */

#ifdef	IDRP_QOS
	if (rib_id)
		{
		idrp_mask |= qos_rib[rib_id]->att.idrp_mask; 
		}				

#endif /* IDRP_QOS */	
	
        return(idrp_mask);
}


/* Is this confederation right for this
 * this peer?
 */
	 

int
idrp_confed_ok(peer,p_open_confed_id)
idrpPeer	*peer;
u_char		*p_open_confed_id;
{ 

	/* see if this confederation sequence matches
	 * what is configured in the peer connection
	 * 
	 */


	return(TRUE);
}

int		
idrp_qos_parse(p_att,dataptr,attr_type,peer)	
idrp_attribute_record *p_att;
byte		*dataptr;
int		attr_type;
idrpPeer	*peer;	
{
u_char	*cp = dataptr;
u_short	svalue;
u_long  lvalue;
u_int	value;	
u_int	id_len;
	
idrp_distinguish_att *p_qos;

	if (p_att->p_opts->p_qos == NULL)
		p_qos = p_att->p_opts->p_qos = (idrp_distinguish_att *) idrp_local_mem_fit(sizeof(idrp_distinguish_att));

	switch(attr_type)
		{
		case IDRP_ATTR_TRANSIT_DELAY:
			bcopy(cp,&svalue,IDRP_TRANSIT_DELAY_LENGTH);
			value = p_qos->delay.value = ntohs(svalue);
			break;
	
		case IDRP_ATTR_RESIDUAL_ERROR:
			bcopy(cp,&lvalue,IDRP_RESIDUAL_ERROR_LENGTH);
			value = p_qos->error.value = ntohl(lvalue);
			break;
			
		case IDRP_ATTR_EXPENSE:
			bcopy(cp,&svalue,IDRP_EXPENSE_LENGTH);
			value = p_qos->expense.value = ntohs(svalue);
			break;

		case IDRP_ATTR_PRIORITY:	
			value = p_qos->priority.value = *cp;
			break;

		case IDRP_ATTR_SECURITY:
			{
			id_len = *cp;
			cp++;

			/* this is very FAA specific - but
			 * - we don't want to support QOS SECURITY
			 * - outside of know bounds
			 */
	
			if (id_len == FAA_SEC_ID_LENGTH)
				{
				/*
				  this code is really FAA specific
				 * - if someone else uses Security
				 * - we simply copy in the
				 * - user and the rest
				 */ 
					
				p_qos->sec_type.value = *cp;
				*cp++;
				id_len = *cp;
				cp++;
				if (id_len != FAA_INFOLENGTH)
					{
					p_qos->sec_value.value = 0;
					return(FALSE);	
					}	
				lvalue = 0;
				bcopy(cp,&lvalue,id_len);
				p_qos->sec_value.value = lvalue;								
				trace_tf(idrp_trace_options, 0, TR_NORMAL, ("QOS FAA Security id = %x value of %x peer %s ",
					idrpAttrTypeStrings[attr_type],p_qos->sec_type,p_qos->sec_value.value ,peer->name));
				return(TRUE);
				}
			else
				{
				return(FALSE);
				}	
			}
			break;
		}
	trace_tf(idrp_trace_options, 0, TR_NORMAL, ("QOS ATTR %s value of  %x peer %s ",
		idrpAttrTypeStrings[attr_type],value,peer->name));

	return(TRUE);
}
