/* 
 E $id: IDRP_parse_pdu.c,v 1.9 1995/08/09 10:07:47 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_parse_pdu.c
 *  
 * Parsing incoming pdus
 *  (process routines validate pdu, and call state machine
 *   with either pdu w/error or pdu w/out error
 *   parse routines - parse in order validate pdu  
 * 
 *	idrp_parse_open(peer,pdu,length) - parse open pdu and validate
 *	process_open(peer,pdu,length) - process open pdu    
 *	parse_update(peer,pdu,length,results,p_next_hop) - parse update and validate
 *	parse_update_cleanup(results) - cleanup from the parse update routine after failure
 *	process_update(peer,pdu,length,p_next_hop) - process update pdu
 *	process_keepalive(peer,pdu,length) - process keepalive pdu
 *	process_error(peer,pdu,length) - process error pdu
 *	process_cease(peer,pdu,length) - process cease pdu
 *	parse_rib_refresh(peer, pdu, length) - ???
 *	process_rib_refresh(peer,pdu,length) - process rib refresh pdu
 *	process_echo(peer,pdu,length) - process echo pdu
 *	process_echo_reply(peer,pdu,length) - process echo reply pdu 
 *	process_header_error(peer,pdu,length) - process header error   
 *	process_rcv_seq(peeri,pdu)
 *	in_sequence(peer,seq)
 * 	idrp_recv_pdu  - receive idrp pdu 
 *	idrp_recv - receive for master task for idrp protocol
 */ 

/*
 * parse_open(peer, pdu, length)
 */
int 
idrp_parse_open(peer, pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
u_short rcv_maxpdusize;
struct iso_net_addr	rdi_peer;
idrp_open_pdu	*p_pdu;
idrp_RIB_att	*p_open_rib_att;
idrp_confed_id	*p_open_confed_id;
idrp_authentication_code	*p_open_auth;
idrp_open_pdu_header *hdr;
u_int		count;
byte		*cp;

	/* calculate the idrp_fixed header portion based on
	 *  the pdu and the protocol header type
	 * - The header length in the task socket is a different
	 *   length depending on the protocol socket this IDRP
	 *   is running over. (UDP/IP/CLNP/IDRP socket
	 */

	hdr = (idrp_open_pdu_header *) &pdu->pdu.open; 
	bcopy((caddr_t) hdr->idrp_maxpdu_size, (caddr_t) &rcv_maxpdusize, sizeof(hdr->idrp_maxpdu_size));
	rcv_maxpdusize = ntohs(rcv_maxpdusize);

	/* a) check the header version */
	if (hdr->version != IDRP_VERSION_1)
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
		peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_UNSUP_VER;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data_len = 0; 
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0; 
		return(IDRP_ERROR_OPEN_PDU);
		}

	/* b) check the maximum PDU size */

	bcopy((caddr_t) hdr->idrp_maxpdu_size, (caddr_t) &rcv_maxpdusize, sizeof(hdr->idrp_maxpdu_size));
	rcv_maxpdusize = ntohs(rcv_maxpdusize);
	if (rcv_maxpdusize < MinPDULength) 
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
		peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_BAD_MAX_SIZE;
		peer->last_error_pdu.data = (u_char *) &hdr->idrp_maxpdu_size; 
		peer->last_error_pdu.data_len = sizeof(hdr->idrp_maxpdu_size);
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0; 
		return(IDRP_ERROR_OPEN_PDU);
		}

	p_pdu = (idrp_open_pdu *) hdr; 

	/* c) check the RDI */
	
	rdi_peer.isoa_len = p_pdu->source_rdi.source_RDI_length;
	bcopy ((caddr_t) &p_pdu->source_rdi.source_RDI,&rdi_peer.isoa_genaddr,rdi_peer.isoa_len);
	if (isoaddrcmp(&peer->rdi,&rdi_peer))
		{
		/* addresses are not equal so error */

		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP open error - RDI from peer wrong peer %s  rdi %A",
			peer->name, sockbuild_iso((byte *) rdi_peer.isoa_genaddr, rdi_peer.isoa_len)));
	
		peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
		peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_BAD_PEER_RD;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data_len = 0; 
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0; 
		return(IDRP_ERROR_OPEN_PDU);
		}	
		
	/* d) Rib_Att_Set	
	 *     set up the pointer so that variable field is indexed past 
	 */

	p_open_rib_att = (idrp_RIB_att *) &p_pdu->source_rdi.source_RDI + rdi_peer.isoa_len; 
	
	count = p_open_rib_att->RIB_att_count;		
	cp = parse_rib_att(p_open_rib_att,count,peer);
	if  (count != RIB_ATT_DEFAULT_ONLY_COUNT)
		{
		if (cp == (byte *) NULL)
			{		
			peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
			peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_BAD_RIB_ATT_SET;
			peer->last_error_pdu.data = (u_char *) 0;
			peer->last_error_pdu.data_len = 0; 
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0; 
			return(IDRP_ERROR_OPEN_PDU);
			}
		}
	
	/* e) RDC match check */

	p_open_confed_id = (idrp_confed_id *) cp; 

	if (((p_open_confed_id->confed_id_count == NO_RDC_SUPPORT_COUNT) && (peer->no_rdc != 0)) ||  
	    ((p_open_confed_id->confed_id_count != NO_RDC_SUPPORT_COUNT) && (peer->no_rdc == 0)))	
		{
		/* RDC configured for this peer - 
		 * - either no RDC and RDC configured
		 *   or RDC sent and no RDC configured
		 * ==  we have a mismatch
		 */
					
		peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
		peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_RDC_MISMATCH;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data_len = 0; 
		peer->last_error_pdu.data2 = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0; 
		return(IDRP_ERROR_OPEN_PDU);
		}

	if (peer->no_rdc)
		{
		/* we have both count and rdc configured,
		 * - is it the same rdc
		 */

		if (!idrp_confed_ok(peer,(u_char *)p_open_confed_id))
			{		
			peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
			peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_RDC_MISMATCH;
			peer->last_error_pdu.data = (u_char *) 0;
			peer->last_error_pdu.data_len = 0; 
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0; 
			return(IDRP_ERROR_OPEN_PDU);
			}
		}

		
	/* f) Authentication check */

	p_open_auth = (idrp_authentication_code *) p_open_confed_id + sizeof(idrp_confed_id); 

	switch (p_open_auth->authentication_code)
		{
		case IDRP_AUTH_CODE_TEST:
		case IDRP_AUTH_CODE_CLEAR:
			return(0);
			break;

		case IDRP_AUTH_CODE_CRYPT:
		case IDRP_AUTH_CODE_SIMPLE:
			p_open_auth++;
			if (valid_auth_data((u_char *) p_open_auth,peer))
				{
				peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
				peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_AUTH_FAILURE;
				peer->last_error_pdu.data = (u_char *) 0;
				peer->last_error_pdu.data_len = 0; 
				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0; 
				return(IDRP_ERROR_OPEN_PDU);
				}

		default:		
			peer->last_error_pdu.error_code = IDRP_ERROR_OPEN_PDU;
			peer->last_error_pdu.error_subcode = IDRP_OPEN_ERROR_UNSUP_AUTH_CODE;
			peer->last_error_pdu.data = (u_char *) 0;
			peer->last_error_pdu.data_len = 0; 
			peer->last_error_pdu.data2 = (u_char *) 0;
			peer->last_error_pdu.data2_len = 0; 
			return(IDRP_ERROR_OPEN_PDU);
			break;
			}
return (0);
}

int 
valid_auth_data(p_auth_data,peer)
u_char		*p_auth_data;
idrpPeer	*peer;
{
	return (TRUE);
}
	
/*
 * Open PDU
 */
static void 
process_open(peer, pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
u_short rcv_holdtime;
u_short rcv_maxpdusize;
idrp_open_pdu_header *hdr;


	/* set-up new hold time from pdu - whether we use it or not */

	hdr = &pdu->pdu.open.header;	
	bcopy((caddr_t) hdr->idrp_hold_time, (caddr_t) &rcv_holdtime, sizeof(hdr->idrp_hold_time));
	rcv_holdtime = ntohs(rcv_holdtime);
	bcopy((caddr_t) hdr->idrp_maxpdu_size, (caddr_t) &rcv_maxpdusize, sizeof(hdr->idrp_maxpdu_size));
	rcv_maxpdusize = ntohs(rcv_maxpdusize);
	peer->keepalive_time = rcv_holdtime / 3 ? rcv_holdtime / 3 : 1;
	peer->pdu_maxsendsize = rcv_maxpdusize;
	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Open hold time %d, max pdu size %d", rcv_holdtime, rcv_maxpdusize));

	/* simply call state machine for valid open */
	idrp_sm(peer,IDRP_OPEN);
}

int 
parse_update(peer, pdu, length, res, p_next_hop, p_dst_intf)
idrpPeer *peer;
idrpPdu *pdu;
int length;
parse_results *res;
sockaddr_un	*p_next_hop;
sockaddr_un	*p_dst_intf;
{
/* path attribute variables */

idrp_attribute_record *p_att = 0;
idrp_attribute_record *p_attr = 0;
idrp_path_attr *attrs;

short total_attr_len;
short attr_len;
u_int attr_type;
u_int usr_attr_index;
idrpRoute *p_idrp_rt;
 
/* nlri variables */ 

nlri_info *nlriptr;
nlri_proto_info *nlri_proto_ptr;
u_short nlrilen;
int nlri_cnt = 0;

/* general pointer for copies */ 

u_char *cp;
u_char *cp2;
int	len_left;
int	index;
int 	i;
int	family;
int 	nlri_id;
u_short	hold;

/* pdu headers work pointers */
idrp_update_pdu *update;
u_int id;


	/* - set update pointer to header plus protocol header
	 * to send PDU (CLNP header, UDP header, PID header,
	 * IDRP socket header)
	 */
 

	update = (idrp_update_pdu *) &pdu->pdu.update; 
	cp = (u_char *) update;
	
	/* Get Withdrawn route-id count and route_ids
	 *  
	 * route_ids are variable length
	 */

	len_left = length - sizeof(idrp_fixed_pdu_header);

	bcopy(update->unfeasible_route_count, (caddr_t) &hold,UNFEASIBLE_RT_CNT_LEN);
	hold = ntohs(hold);
	if (hold >= IDRP_WITHDRAW_MAX_IDs)
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0, ("Withdraw ids over maximum peer %s update pdu ",peer->name));
		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_len = 0;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;
		peer->last_error_pdu.data2 = (u_char *) 0;
		return (IDRP_ERROR_UPDATE_PDU);
		}


	cp +=  UNFEASIBLE_RT_CNT_LEN; 
	
	/* initialize results structure
	 * with zero, and init_route_id
	 * for tracking
	 * withdraw count to real count
 	 *
	 */	

	res->route_id_in =  IDRP_INIT_ROUTE_ID;
	res->withdraw->count = hold; 

 
	/* Parse the withdrawn route list. 
	 * putting withdraw routes into withdraw array 
	 */

	if (hold)
		{
		/* withdrawing route ids */

		for (i = 0; i < res->withdraw->count; i++)
			{
			bcopy((caddr_t) cp,&id,sizeof(route_id));
			id = ntohl(id);
			cp += sizeof(route_id);
			res->withdraw->withdraw[i] = id;
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("Withdraw route id %d peer %s",id,peer->name));
			}
		}

	/* copy in the length of the whole attribute 
 	 * - cp = total attribute length
	 *   Check it to make sure the length is sane with whole pdu length 
	 */
   
	/* save pointer to nlri information */

	bcopy((caddr_t) cp, (caddr_t) &total_attr_len, sizeof(update->tot_path_att_len));
	total_attr_len = ntohs(total_attr_len);
	cp += sizeof(update->tot_path_att_len); 
	index = sizeof(update->tot_path_att_len) + (sizeof (route_id) * res->withdraw->count) + total_attr_len + UNFEASIBLE_RT_CNT_LEN;
	len_left -= index;

	/* Section 7.20.3 a - Error Handling - length wrong
	 */


	if (len_left < 0)
		{
		/* length too short - pdu invalid
		 * 
		 */

		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_len = 0;
		peer->last_error_pdu.data = (u_char *) 0;
		peer->last_error_pdu.data2_len = 0;
		peer->last_error_pdu.data = (u_char *) 0;
		res->p_pdu = (u_char *) 0;
		parse_update_cleanup(res,peer);
		return(IDRP_UPDATE_PARSE_ERROR);
		}			

	/* Check for only withdraws - zero attribute length */
	
	if (total_attr_len == 0)
		{
		return (0);
		}
		
	
	/* path attributes and nrli ahead - start the parse
	 *
	 * start reading attributes one by one
	 */ 


	cp2 = (u_char *) cp + total_attr_len;
	nlri_proto_ptr = (nlri_proto_info *) cp2; 
	cp2 += sizeof(nlri_proto_info);
	nlriptr = (nlri_info *) cp2; 

	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP update from %s attr length %d", peer->name, total_attr_len));

	/* 1) allocate and fill a temporary attribute record  
	 * 2) allocate and fill memory to save attributes from UPDATE 
	 *    to associate with attribute record 
 	 */ 
 
	p_att = (idrp_attribute_record *) idrp_local_mem_fit(sizeof(idrp_attribute_record));
	p_att->peer_alloc = peer;
	p_att->p_attr_pdu = (u_char *) idrp_local_mem_fit(total_attr_len);
	bcopy(cp,p_att->p_attr_pdu,total_attr_len);
	p_att->attr_len = total_attr_len;


	/* set up the indexes to copy attributes into attribute record
	 */
 
	cp = p_att->p_attr_pdu; 
	res->p_att = p_att;
	usr_attr_index = 0;

	/* set-up results array in attribute record id 
	 * with the attributes
	 */


	while (total_attr_len > 0) 
		{
		/* set the attribute header to the next attribute
		 * - make sure the index into the bytes is correct
		 *   Been bit by the structure definitions too many
		 *   times. 
		 */
 
		attrs = (idrp_path_attr *) cp;
		attr_type = attrs->attr_hdr.attr_type;
		bcopy(attrs->attr_hdr.attr_length, (caddr_t) &attr_len, sizeof(attrs->attr_hdr.attr_length));
		attr_len = ntohs(attr_len);
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("  Attr %d, length %d, flags %x",
			 attrs->attr_hdr.attr_type,attr_len, attrs->attr_hdr.attr_flags));


		/* check for range within IDRP specification 
		 * of attributes
		 */ 


		if ((attrs->attr_hdr.attr_flags & IDRP_ATTR_FLAG_OPTIONAL) == 0)
                        {
			idrp_attribute_record *p_temp_att;
			/* check non-optional (or mandatory) attribute type range 
			 */

			if (!idrp_wellknown_type(attr_type,peer,attrs))	
				{
				res->p_pdu = p_att->p_attr_pdu;	
				trace_tf(idrp_trace_options, TR_NORMAL,0, ("IDRP %s got pdu wellknown attribute type %d outside range", peer->name, attr_type));
				parse_update_cleanup(res,peer);
				return(IDRP_UPDATE_PARSE_ERROR);
				}


			i = wellknown_attrib_twice(attr_type,attrs,res,peer,p_att,attr_len);
			switch (i)
				{

				/* well know attribute has been spotted
				 * twice within ROUTE_SEPARTOR sequence
				 */

				case ERROR_WELLKNOWN_ATTRIBUTE_TWICE:
					/* the wellknown_attribute_twice routine
					 * must set the appropriate pdu to save
					 *  
					 */

					parse_update_cleanup(res,peer);
					return(IDRP_UPDATE_PARSE_ERROR);
					break;


				/* use the original attribute 
				 * for all non-distinguishing attributes
				 */
	
				case WELLKNOWN_ATTRIBUTE_ONCE:

					p_temp_att = p_att;
					break; 

				/* Use the attribute for first route server
				 * unless there attribute for a   
				 * second (or beyond) Route separator
				 * attribute record 
				 */

				case WELLKNOWN_DIST_ATTRIBUTE_ONCE:
					p_temp_att = p_att;
					if (res->p_att_rs)
						{
						p_temp_att = res->p_att_rs;
						}	
				
					break;

				/* ROUTE_SEPARATOR has been 
				 * seen again, so switch to route attribute
				 *  for this processing
				 */
						
				case WELLKNOWN_ROUTE_SEPARATOR_TWICE:
			
					p_temp_att = res->p_att_rs;
					break;
				}

			/* for all attributes that are keepable -
			 * wellknown attribute ONCE
			 * wellknown distributed attribute once within RS
			 * ROUTE_SEPATOR twice - marking separation
			 * 
			 * - keep these attributes 
			 */  

	
			cp += sizeof(idrp_path_attr_hdr);
			p_temp_att->attrib[attr_type].present = TRUE;
			p_temp_att->attrib[attr_type].flags  = attrs->attr_hdr.attr_flags;
			p_temp_att->attrib[attr_type].length = attr_len;
			p_temp_att->attrib[attr_type].data = cp;
			p_temp_att->idrp_mask |= idrpAttrBits[attr_type];
			}
		else  
			{
			/*
			 *  optional attributes - Do we recognize them?
			 *   - 1) Is it a wellknown discretionary  attribute type or
			 *     2) is it an optional type we recognize 
			 *     3) is it a transitive we'll pass or ignore 
			 */


			/* wellknown discretionary attribute ? 
			 */

			if (idrp_wellknown_type(attr_type,peer,attrs))
				{
				i = wellknown_attrib_twice(attr_type,attrs,res,peer,p_att,attr_len);
				if (i == WELLKNOWN_ATTRIBUTE_ONCE)
					{
					cp += sizeof(idrp_path_attr_hdr);
					p_att->attrib[attr_type].present = TRUE;
					p_att->attrib[attr_type].flags  = attrs->attr_hdr.attr_flags;
					p_att->attrib[attr_type].length = attr_len;
					p_att->attrib[attr_type].data = cp;
					p_att->idrp_mask |= idrpAttrBits[attr_type];
					}
				else
					{
					/* wellknown optional attribute twice - 
					 * - return error 
					 */

					parse_update_cleanup(res,peer);
					}
				}
			else
				{
				if (attr_type >= IDRP_ATTR_USR_MIN && attr_type <= IDRP_ATTR_USR_MAX)
					{
					/* valid user type to process - it is in our range
					 * so leave bit setting and handling to the
					 * the valid_usr fields
					 * - process the rest in 
					 * these are items (7.11.1, b and d)
					 */

					 cp += sizeof(idrp_path_attr_hdr);
					 p_att->usr_attrib[usr_attr_index].present = TRUE;
					 p_att->usr_attrib[usr_attr_index].flags = attrs->attr_hdr.attr_flags;
					 p_att->usr_attrib[usr_attr_index].length = attr_len;
					 p_att->usr_attrib[usr_attr_index].data = cp;
					 usr_attr_index++;
					 p_att->idrp_mask = p_att->idrp_mask | idrpAttrBits[attr_type];
       		                         }
	                        else
					{
					/* unrecognized user attribute
                       		         * 7.11.1, a - process it if transitive 
                                	 *         c - ignore it if not transitive 
		 			 *		- by not saving it in attribute list
	                                 */

       					if ((attrs->attr_hdr.attr_flags & IDRP_ATTR_FLAG_TRANSITIVE) != 0 )
                                       	 	{
						cp += sizeof(idrp_path_attr_hdr);
						if (!idrp_transitive_att(attrs,cp,attr_len,attr_type,p_att))
							{
							/* invalid if same transitive attribute within attribute
							 * or route separator function 
							 * - error
							 */
							res->p_pdu = p_att->p_attr_pdu;
							parse_update_cleanup(res,peer);
							}
						}
					}
				   }
           		}		

		cp += attr_len;
		total_attr_len -= attr_len + sizeof(idrp_path_attr_hdr);
		}


	/* Set up route_id_list for first (and perhaps only!)
	 * attribute record
	 *
	 * validate the 1st attribute record with
	 * route separator
	 * and non-distinguishing attributes
	 */
 
	p_att->route_id_list = (idrpRoute_entry *)  idrp_local_mem_fit (sizeof(idrpRoute_entry)); 
	bzero(p_att->route_id_list, sizeof(idrpRoute_entry));
	p_att->route_id_list->peer = peer;

		
	if (!valid_attr(peer,p_att))
		{
		/* 
		 * Routing LOOP Detected 
		 *is not really an update error when we detect
		* a routing loop.  We don't want to close the connection due to this
		* problem. 
		*/

	         if((peer->last_error_pdu.error_code == IDRP_ERROR_UPDATE_PDU)
	      	      && (peer->last_error_pdu.error_subcode == IDRP_UPDATE_ERROR_RD_ROUTING_LOOP))
			{
			/* Phase 1 - 
			 *	   Handle the Withdraws
			 *
			 * Phase 2 - 
			 * 	    keep routes around to support SDRP
			 *          add the ROUTE_ID of the NLRI to the table
			 * 	    with the static
			 * 
			 * 
			 */  

			IDRP_STATUS_BIT_SET(res, IDRP_RID_ROUTE_LOOP);
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("RD_LOOP in UPDATE peer %s (status res: %x)" ,peer->name,res->status));
			}
		else 
			{
			/* trace error,  release pdu, and exit */

			res->p_pdu = p_att->p_attr_pdu;
			parse_update_cleanup_reuse(res,peer);
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Atttribute invalid. code = %x",p_att));
			return(IDRP_UPDATE_PARSE_ERROR);	
			}
		}

	/* put in the next hop of the attribute the
	 * next hop from the pdu remote peer
	 * in validating the pdus - it will check and
	 * put either the NEXT_HOP attribute
	 * pdu with the next hop  
	 */ 

	if (!p_att->next_hop)
		{
		/* if the next hop not filled in by
		 * NEXT_HOP Attribute in the pdu,
		 * fill it in now 
		 */
		
		int len_addr;		
		if (p_att->p_opts == NULL)
			{
			p_att->p_opts = (idrpRoute_options *) idrp_local_mem_fit (sizeof(idrpRoute_options)); 
			}

		if (p_next_hop->a.sa_family < AF_APPLETALK)
			{
		 	nlri_id =  family_nlri[p_next_hop->a.sa_family].nlri_id;
			}
		else
			{	
		 	nlri_id =  IDRP_NLRI_IP;
			}

		assert(p_att->p_opts->next_hop[nlri_id].cnt == 0);
		p_att->p_opts->next_hop[nlri_id].p_net = sockdup(p_next_hop);
		p_att->next_hop = p_att->p_opts->next_hop[nlri_id].p_net; 
		/* snap list is zero out in above bzero */
		}
 
	if (p_att->p_opts && p_att->p_opts->p_intf == NULL
		&& p_dst_intf != (sockaddr_un *) NULL)
		{
		p_att->p_opts->p_intf = sockdup(p_dst_intf);
		}

	/* If there was any User attributes, the index
	 * will be non-zero
	 */
 
	if (usr_attr_index) 
		{
		if (!valid_usr_attr(peer,p_att))
			{
			/* trace error, release attribute block and exit */
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Atttributes invalid (valid_usr_attr fails). code = %x",p_att));
			res->p_pdu = p_att->p_attr_pdu;	
			parse_update_cleanup_reuse(res,peer);
			return(IDRP_UPDATE_PARSE_ERROR);	
			}
		}



	/* now loop for all attribute records
	 * for all but the original  attribute record
	 *   route separators
	 * 
	 * 1) validate distinguishing attributes
	 * 2) copy of rest of attributes
	 * 3) see if you can find another attribute
	 * 4) if so, create another route_id and
	 *	get rid of the duplicate attribute 
	 */
	
	{
	idrp_attribute_record *p_att_last = 0;	
	u_int	mask;
	u_int   value = 0;

	IDRP_PARSE_ATT_LIST(p_att,res)
		{
		mask = p_att->idrp_mask & IDRP_ATTBT_DISTINGUISH; 	
		if (mask)
			{
#ifdef	IDRP_QOS
			if (mask & IDRP_ATTBT_SECURITY)
				{
				value = p_att->p_opts->p_qos->sec_type.value;
				}

			p_att->rib_id = rib_supp_mask(mask,value);
			if ((p_att->rib_id) && (peer->rib_supp[p_att->rib_id])) 
                        	{
	                        trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Non-Default Attributes supported for this now peer %s rib = (%d)",peer->name,p_att->rib_id));
				}
			else
#endif	/* IDRP_QOS */
				{
		
	                        /* non-matching Distinguishing attributes sent 
       		                  * that do not match the peer
       		                  */

               		         trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Non-Default Attributes no support for this now peer %s",peer->name));
				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_len = 0;
				res->p_pdu = peer->last_error_pdu.data = (u_char *) 0;

				peer->last_error_pdu.data2 = (u_char *) 0;
				peer->last_error_pdu.data2_len = 0;

				parse_update_cleanup_reuse(res,peer);
				return (IDRP_UPDATE_PARSE_ERROR);
                		}
			}	
		if (p_att != res->p_att)
			{
			if (!valid_dist_attr(peer,p_att))
				{
                        	/* non-matching Distinguishing attributes sent 
	                         * that do not match the peer
        	                 */
				trace_tf(idrp_trace_options, TR_NORMAL, 0, ("All Distinguishing  Attributes invalid for now: peer %s",peer->name));
	                        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_len = 0;
       		                res->p_pdu = peer->last_error_pdu.data = (u_char *) 0;
	                        peer->last_error_pdu.data2 = (u_char *) 0;
       		                peer->last_error_pdu.data2_len = 0;

                	        parse_update_cleanup_reuse(res,peer);
                       		return (IDRP_UPDATE_PARSE_ERROR);
				}
		
			/* copy in the non-distinguishing attributes
			 * pdu is already there for this attribute 
			 * can reference it 			
			 */
	
			idrp_copy_nondist_att(p_att,res);
			}


		p_attr = find_attr_rec(p_att);
 
		/* 
		 * check to see if this attribute is a duplicate 
		 */

		if (p_attr)
			{
			/* if old attribute matches attributes use it by:
			 * - resetting the parse link structure
			 *   to link to this attribute record 
			 * 
			 */

			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("parse_pdu: attribute found p_attr = %x p_att = %x ",p_attr,p_att));

			p_attr->p_parse_re = idrp_add_idrpRoute_entry(p_att->route_id_list->route_id,p_attr->route_id_list);
			p_attr->p_parse_re->route_id = p_att->route_id_list->route_id;
			p_attr->p_parse_re->loc_pref = p_att->route_id_list->loc_pref;
			p_attr->p_parse_re->peer = peer;
		
			/* link the new attribute into
			 * the attribute list chain for parsing
			 * 
			 */

			if (p_att_last)
				{
				p_att_last->p_parse_next = p_attr;
				}
			else
				{
				res->p_att = p_attr;
				}
	
			p_attr->p_parse_next = p_att->p_parse_next;	

			/* now get rid of the temporary attribute record */
 
			idrp_free_attr_rec(peer,p_att,IDRP_INIT_ROUTE_ID,FALSE);
			p_att = p_attr;

			}
		else
			{
			/* no duplicate attribute 
			 */
			p_att->p_parse_re = p_att->route_id_list;
			}

		/* update the last attribute in parse list */
 
		p_att_last = p_att;
	
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("RouteId %d pref %d peer %s",
		      p_att->route_id_list->route_id,
		      p_att->route_id_list->loc_pref,
		      peer->name));


		} IDRP_PARSE_ATT_LIST_END;

	/* re-order global rdi path
	 * - it's not sure that this is the
	 * right place, it may be too soon
         * to re-order global rdi
	 */
		 
	idrp_global_rdi_order();
	} /* end of defines for loop */

	/* rest the attribute pointer to the 1st (and perhaps only) attribute
	 * the chain of attributes
	 */ 
	
	p_att = res->p_att;		

	/* Parse the NLRI */
	/* watch our total count to make sure PDU is valid */
	/* NLRI  = prototype(octet), length(octet), value */
	/* repeat 1-5 for each protocol type */
	/* 1.) get protocol type */
	/* 2.) get length this nlri info */
	/* repeat 3-6 until NLRI done */
	/* 3.) get length of NLRI */
	/* 4.) get NLRI -  */
	/* 5.) create IDRPRoute block and rt_entry */
	/* 6.) do rt_lookup and give to add, change or delete list */

	while (len_left > 0)
		{
		/* copy protocol type, length and value  */ 
		nlrilen = (int) nlri_proto_ptr->proto_len;
 
		/* translate family to gated family */
		nlri_id = proto_to_nlri_id((char *) &nlri_proto_ptr->proto_type,nlrilen,(u_char *) &nlri_proto_ptr->proto_val);
		family = nlri_family[nlri_id].family;

		/* length left = length - length of protocol header 
		 *  protocol header = type, length + protocol field (variable ) 
		 *  nlrilen = protocol header length 
		 *  len_left = len_left - protocol_header length
		 */ 
		
		nlrilen += (sizeof(nlri_proto_ptr->proto_type) + sizeof(nlri_proto_ptr->proto_len)); 
		len_left -= nlrilen; 
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("NLRI (PROTO_ID %x, family %d length %d):", 
		      nlri_proto_ptr->proto_type,family, nlrilen));


		/* address length for nlri of this protocol type
		 * copy the length and check length left 
 		 */
  
		bcopy(nlriptr,&hold, sizeof(nlriptr->addr_len));
		hold = ntohs(hold);
		len_left -=(hold + sizeof(nlriptr->addr_len));

		if (len_left < 0)
			{
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("len of nrli exceeds pdu length len of nlri = %d",hold));
			peer->last_error_pdu.error_code = IDRP_ERROR_UPDATE_PDU;
			peer->last_error_pdu.error_subcode = IDRP_UPDATE_ERROR_ATTR_LEN_ERR;
                        res->p_pdu = peer->last_error_pdu.data = (u_char *) 0;
			peer->last_error_pdu.data_len = 0;
                        peer->last_error_pdu.data2 = (u_char *) 0;
                        peer->last_error_pdu.data2_len = 0;

			/* flag no route_id list for cleanup */

			parse_update_cleanup_reuse(res,peer);
			return (IDRP_UPDATE_PARSE_ERROR);
			}
	
		nlrilen = hold;
		
		/* loop till finish this protocol nlri group
		 * set-up the input pointer to start of NLRI 
	 	 *  after the protocol identifiers
		 * indexing over the total NLRI length for
		 * this address family 
		 * 
		 */

		cp = (u_char *) nlriptr; 
		cp += sizeof(nlriptr->addr_len);		

		while (nlrilen > 0) 
			{
			/* allocate an  idrpRoute buffer for
			 * each NLRI that is received.
			 * fill it up with basics as we process
			 */ 

			p_idrp_rt = idrp_alloc_idrpRoute(peer,p_att,family,p_att->p_parse_re->route_id);
			p_idrp_rt->status = IDRP_STATUS_ADJ_RIB;
			p_idrp_rt->pref_rcvd = p_att->p_parse_re->loc_pref;
 
			/* copy in the protocol id nlri into buffer 
			 * - watch here for zero length nlri
			 */

			bcopy(cp,&p_idrp_rt->nlri.pfx_len,sizeof(p_idrp_rt->nlri.pfx_len));
			cp += sizeof(p_idrp_rt->nlri.pfx_len);	
			bcopy(cp,&p_idrp_rt->nlri.pfx,PFX_BYTE_LENGTH(p_idrp_rt->nlri));
			cp += PFX_BYTE_LENGTH(p_idrp_rt->nlri);

			trace_tf(idrp_trace_options, TR_NORMAL, 0, (">nlri =   %s  nlrilen = %d",
				iso_ptoa(&p_idrp_rt->nlri), (PFX_BYTE_LENGTH(p_idrp_rt->nlri))));

			/* add it to the linked list of IDRP_routes
			 * use temporary space for header and tail
			 */
	
			if (p_att->p_parse_re->p_route[nlri_id])
				{
				p_att->p_parse_re->p_route_tail[nlri_id]->p_next_nlri = p_idrp_rt;
				p_att->p_parse_re->p_route_tail[nlri_id] = p_idrp_rt;
				}
			else
				{
				p_att->p_parse_re->p_route_tail[nlri_id] = p_att->p_parse_re->p_route[nlri_id] = p_idrp_rt;
				}				
			p_idrp_rt->p_next_nlri = (idrpRoute *) 0;
			nlri_cnt++;

			/* Do we have another prefix
		 	 *  - Is length still positive?
			 */		

			nlrilen -= (PFX_BYTE_LENGTH(p_idrp_rt->nlri) + sizeof(p_idrp_rt->nlri.pfx_len));
			}
		nlri_proto_ptr = (nlri_proto_info *) cp; 
		cp += (sizeof(nlri_proto_info));
		nlriptr = (nlri_info *) cp; 
		} /* end of this protocol id */

	/* valid update pdu
	 * - trace the fact we got a valid update pdu
	 * - up attribute reference count by number of nlri associated
	 *   with it from peer on this route_id
	 */
		
	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("valid pdu pdu from peer %s last nlri %s", peer->name,iso_ptoa(&p_idrp_rt->nlri)));

	/* here copy into the attribute record route_id_list
	 *  from temprorary space. 
	 */

	res->nlri_cnt = nlri_cnt;

	IDRP_PARSE_ATT_LIST(p_att,res) 
		{
		/* walk through the list of attributes
		 * copying the routes
		 * to the new attribute record 
		 */

		if (p_att != res->p_att)
			{
			/* copy nlris into the other attribute records */ 
 
			idrp_copy_routelist(p_att,res);
			}
		else
			{
			p_att->ref_cnt += nlri_cnt;
			}	

		if (p_att->p_parse_re == p_att->route_id_list)
			{
			/* the parse route_id is the
			 * first one, so link the attribute
			 * record as a new one
			 */

			link_att_list(p_att);
			}	
		} IDRP_PARSE_ATT_LIST_END; 
		
	/* increment the attribute record for the number of linked nlri on route_id
	 * Exciting note:  route_lists are separate for ip and osi
	 * - but reference count increment once for each NLRI - osi/ip
	 */

	IDRP_STATUS_BIT_TEST(res, IDRP_RID_ROUTE_LOOP)
		{
		return(IDRP_UPDATE_PARSE_RDLOOP_ERROR);	
		}
 
	return (IDRP_UPDATE_PARSE_OK);

}
	
void 
parse_update_cleanup(p_res,peer)
parse_results	*p_res;
idrpPeer	*peer;
{
/* routine to delete the partial results of the attribute
 * record when aborted mid-stream in parse_update 
 * no idrp_attribute records have been reused
 * nor any idrp routes associated with 
 * the parsing results records
 *
 */

parse_update_cleanup_free(p_res,peer,TRUE);

}

void 
parse_update_cleanup_reuse(p_res,peer)
parse_results	*p_res;
idrpPeer	*peer;
{
/* routine to delete the partial results of the attribute
 * record when aborted mid-stream in parse_update 
 *  idrp_routes may have been associated
 *  with existing attribute records
 */

parse_update_cleanup_free(p_res,peer,FALSE);

} 


int
parse_update_cleanup_free(p_res,peer,free)
parse_results	*p_res;
idrpPeer	*peer;
int		free;
{
idrp_attribute_record 	*p_att;
idrp_attribute_record 	*free_last_att = NULL ;

	/* check for attribute record - if none,
	 * only withdraws, so no pdu saved
	 */

	if (!p_res->p_att)
		{
		if (!free)
			{	
			trace_tf (idrp_trace_options, TR_NORMAL,0, ("IDRP parse_cleanup has no attribute record peer %s",peer->name));
			}	
		return;
		}

	/* free the attribute always if the free flag is
	 * set.
	 * Otherwise, some of the attributes are linked,
	 * check the full link sequence 
	 */


	IDRP_PARSE_ATT_LIST(p_att,p_res)
		{
		/* attribute record exists - try to find this route_id */

		if (free_last_att)
			{
			IDRP_MEM_FIT_FREE(free_last_att);
			}

		if (p_att->p_parse_re)
			{
			idrp_free_routeid_entry(p_att,p_att->p_parse_re);
			}


		/* if this attribute has a reference count, 
		 *  it comes from another use of the attribute
		 *  record, so leave it alone
		 */

		
		if (p_att->ref_cnt)
			{
			free_last_att = (idrp_attribute_record *) NULL;
			continue;
			}


		/* free the pdu associated with this 
		 * if the pdu is not the one we need to
		 * save to send error message out
		 * in process_update
		 */

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

		/* free the next hop */

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

		/* free rd path structure */

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

		if (p_att->p_transitive)
			{
			idrp_free_trans(p_att->p_transitive);
			}

		free_last_att = p_att;

		} IDRP_PARSE_ATT_LIST_END;

	if (free_last_att)
		{
		IDRP_MEM_FIT_FREE(free_last_att);
		}
}


static void 
process_update(peer, pdu, length,p_next_hop,p_dst_intf)
idrpPeer *peer;
idrpPdu *pdu;
int length;
sockaddr_un	*p_next_hop;
sockaddr_un	*p_dst_intf;
{
parse_results *results;
int return_code = 0;

	results = (parse_results *) idrp_local_mem_fit (sizeof(parse_results));
	bzero(results,sizeof(parse_results));
	results->withdraw = (Withdrawl *) idrp_local_mem_fit (sizeof(Withdrawl));
	bzero(results->withdraw,sizeof(Withdrawl));

	/* parse the pdu */

	return_code = parse_update(peer,pdu,length,results,p_next_hop,p_dst_intf);

	switch (return_code)
		{
		case IDRP_UPDATE_PARSE_ERROR:
			/* free the results and the withdraw array space
			 */

			idrp_sm(peer,IDRP_UPDATE_ERROR);
			IDRP_MEM_FIT_FREE(results->withdraw);
			IDRP_MEM_FIT_FREE(results);
			break;

		case IDRP_UPDATE_PARSE_RDLOOP_ERROR:
			/* say we got a valid UPDATE, 
			 * for now toss the data.
			 * in the future MAYBE clause
			 * insert the data with a HIDDEN
			 * no-install feature in gated
			 */

			trace_tf(idrp_trace_options,TR_NORMAL, 0, ("maybe clause executed so no routes added from UPDATE peer %s, routes invalidated ",peer->name));

		case IDRP_UPDATE_PARSE_OK:
			/* valid pdu so let's send it through the state machine
			 */

			if (idrp_sm(peer,IDRP_UPDATE))
				{
				/* state machine says process update pdu */
	
				if (peer->refresh.seq_start)
					{
					/* mid rib_refresh, hook the rib refresh pdus
					 * together for processing 
					 */
	
					refresh_pdu_link(peer,results,pdu);
					return;
  					}
				else
					{
					/* away for processing of pdus into phase1 processing
					 *
					 */ 
	
					idrp_process_pdu_routes(peer,results,IDRP_UPDATE_TYPE);
	
					/* clean up the results array */
	
					IDRP_MEM_FIT_FREE(results->withdraw);
					IDRP_MEM_FIT_FREE(results);
					}
				} /* if state machine returns process pdu */
			else
				{
				/* state machine error - don't process pdus and release it 
				 * - free the pdu
				 * - results the results arrays
				 */ 
		
				idrp_free_valid_results(peer,results);
				IDRP_MEM_FIT_FREE(results->withdraw);
				IDRP_MEM_FIT_FREE(results);
				}
			break;
	
		} 	/* end of switch */
}

/*
 * KEEPALIVE PDU
 */
static void 
process_keepalive(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
	idrp_sm(peer,IDRP_KEEPALIVE);
}


static void 
process_error(peer, pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
int err_code = 0;
int subcode = 0;
int error = 0; 

	/* validate ERROR pdu */

	err_code = pdu->pdu.error.header.error_code;
	subcode = pdu->pdu.error.header.error_subcode;
	
	switch (err_code)
		{
		case IDRP_ERROR_OPEN_PDU:
			if ((subcode < IDRP_OPEN_ERROR_MIN_SUBCODE)
			 && (subcode > IDRP_OPEN_ERROR_MAX_SUBCODE))
				{	 
				error=TRUE;
				}
			break;

		case IDRP_ERROR_UPDATE_PDU:
			if ((subcode < IDRP_UPDATE_ERROR_MIN_SUBCODE)
			 && (subcode > IDRP_UPDATE_ERROR_MAX_SUBCODE))
				{	 
				error = TRUE; 
				}
			break;

		case IDRP_ERROR_HOLDTIME_EXPIRED:
			if (subcode != IDRP_HOLDTIME_EXPIRED_ERROR_SUBCODE)
				{
				error = TRUE;  
				}
			break;

		case IDRP_ERROR_FSM:
			if ((subcode < IDRP_FSM_ERROR_MIN_SUBCODE)
			 && (subcode > IDRP_FSM_ERROR_MAX_SUBCODE))
				{	 
				error = TRUE; 
				}
			break;

		case IDRP_ERROR_RIB_REFRESH:
			if ((subcode < IDRP_RIB_REFRESH_ERROR_MIN_SUBCODE)
			 && (subcode > IDRP_RIB_REFRESH_ERROR_MAX_SUBCODE))
				{
				error = TRUE;
				}
			break;

		default:
			error = TRUE;
			break;
		}

	if (error)
		{
		idrp_sm(peer,IDRP_ERROR_ERROR);
		log_nm_event(peer,IDRP_ERROR_PDU_ERROR,err_code);
		}
	else
		{
		idrp_sm(peer,IDRP_ERROR);
		}
}


static void 
process_cease(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
	idrp_sm(peer,IDRP_CEASE);	
}

u_int 
parse_rib_refresh(peer,pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
	/* range check for the Rib Refresh types */

	if (pdu->pdu.refresh.hdr.opcode < IDRP_RIB_REFRESH_MIN_CODE  ||
	    pdu->pdu.refresh.hdr.opcode > IDRP_RIB_REFRESH_MAX_CODE)
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_RIB_REFRESH;
		peer->last_error_pdu.error_subcode = IDRP_RIB_REFRESH_ERROR_INV_OPCODE;
		peer->last_error_pdu.data_len = 0;
		peer->last_error_pdu.data2_len = 0;
	 	return (IDRP_ERROR_RIB_REFRESH);
		}

	/* check the attribute types */
		
	/* d) Rib_Att_Set - Default only	 */
	
	if  (pdu->pdu.refresh.rib_att[0].RIB_att_count != RIB_ATT_DEFAULT_ONLY_COUNT)
		{
		peer->last_error_pdu.error_code = IDRP_ERROR_RIB_REFRESH;
		peer->last_error_pdu.error_subcode = IDRP_RIB_REFRESH_ERROR_UNSUP_ATTRS;
		peer->last_error_pdu.data_len = 0;
		peer->last_error_pdu.data2_len = 0;
	 	return (IDRP_ERROR_RIB_REFRESH);
		}
	return(0);
}


static void 
process_rib_refresh(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
int seq_no = 0;
	
	if (parse_rib_refresh(peer,pdu))
		{
		/* error in pdu */

		idrp_sm(peer,IDRP_REFRESH_ERROR);
		}
	else
		{
		/* valid Rib Refresh */
		/* check against state machine */

		if(idrp_sm(peer,IDRP_REFRESH))
			{
	    		switch (pdu->pdu.refresh.hdr.opcode)
				{
				case IDRP_RIB_REFRESH_REQ:
					idrp_send_refresh_all_routes(peer);
					break;	

				case IDRP_RIB_REFRESH_START:
					if (peer->refresh.seq_start !=0)
						{
						rib_refresh_pdu_release(peer);
						}	
					bcopy(&pdu->header.seq,&seq_no,sizeof(pdu->header.seq));

					trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP Refresh with start Seq no: = %x peer %s",
					ntohl(seq_no),peer->name));
					peer->refresh.seq_start=seq_no;
					peer->refresh.pdu_list_start = (idrpRefresh * ) 0;
					peer->refresh.pdu_list_end = (idrpRefresh * ) 0;
					break;

				case IDRP_RIB_REFRESH_END:	
					bcopy(&pdu->header.seq,&seq_no,sizeof(pdu->header.seq));
					trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP Refresh with End Seq no = %x peer %s",
					seq_no,peer->name));
					peer->refresh.seq_stop=seq_no;
					rib_refresh_update_process(peer);
					peer->refresh.seq_start = 0;
					peer->refresh.seq_stop = 0;
					break;
				} 
			} /* if for state machine */
		} /* if parse valid */
}


#ifdef ECHOKLUDGE
static void 
process_echo(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
	switch (peer->state) {

	case IDRP_ESTABLISHED:
		send_echo_reply(peer);
		break;

	default:
		break;
	}
}


static void 
process_echo_reply(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
	switch (peer->state) {

	default:
		break;
	}
}
#endif


static void 
process_header_error(peer, pdu, length)
idrpPeer *peer;
idrpPdu *pdu;
int length;
{
	log_nm_event(peer, IDRP_PDU_HEADER_ERROR, IDRP_PDU_HEADER_ERR_BAD_TYPE);
	idrp_sm(peer, IDRP_HEADER_ERROR);
}

/*
 * Bump the expected receive sequence number if the packet is sequenced.
 * Send a keepalive as acknowledgement if either the send queue is empty,
 * or the connection is flow blocked, so long as we're in ESTABLISHED state.
 *
 * If the received frame is sequenced, cancel any pending keepalive
 * retransmit (due to the peer being flow blocked).  If the received frame
 * has credit_available nonzero (the peer is not flow blocked), cancel any
 * pending keepalive retransmit.
 */
static void 
process_rcv_seq(peer, pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
	if (IS_SEQUENCED(pdu->header.pdu_type)) {
		peer->rcv_seq_expected++;
		if (peer->cred_avail_zero_seq_last) {
			peer->cred_avail_zero_seq_last = 0;
			kill_rexmit_timer(peer);
		}
	}

	if (pdu->header.credit_available) {
		if (peer->cred_avail_zero_seq_last) {
			peer->cred_avail_zero_seq_last = 0;
			kill_rexmit_timer(peer);
		}
	}

	/*
 	 * Track the received credit available as part of a mechanism to ensure that
	 * our peer doesn't get flow blocked for a long time because our ack got lost.
	 */

	peer->rcved_credit_avail = pdu->header.credit_available;
	if (peer->rcved_credit_avail == 0) {
		peer->cred_avail_zero_seq = peer->rcv_seq_expected - 1;
	}

	if (IS_SEQUENCED(pdu->header.pdu_type)) {
		if (peer->state == IDRP_ESTABLISHED &&
		    (peer->xmit_queue_head == NULL || (peer->upper_window_edge <= peer->send_next && !acked_something))) {
			send_keepalive(peer);
		}
	}
}


/*
 * Used only in the open_rcv case -
 * do everything except bump the sequence number
 * and we got an open - which is sequenced
 *
 */

void 
process_rcv_seq_open(peer, pdu)
idrpPeer *peer;
idrpPdu *pdu;
{
	if (peer->cred_avail_zero_seq_last) 
		{
		peer->cred_avail_zero_seq_last = 0;
		kill_rexmit_timer(peer);
		}

	if (pdu->header.credit_available) {
		if (peer->cred_avail_zero_seq_last) {
			peer->cred_avail_zero_seq_last = 0;
			kill_rexmit_timer(peer);
		}
	}

	/*
 	 * Track the received credit available as part of a mechanism to ensure that
	 * our peer doesn't get flow blocked for a long time because our ack got lost.
	 */

	peer->rcved_credit_avail = pdu->header.credit_available;
	if (peer->rcved_credit_avail == 0) {
		peer->cred_avail_zero_seq = peer->rcv_seq_expected - 1;
	}

}

static 
in_sequence(peer, seq)
idrpPeer *peer;
u_int seq;
{
	if (seq == peer->rcv_seq_expected) {
		trace_tf(idrp_trace_options,TR_NORMAL, 0, ("In sequence"));
		return(TRUE);
	} else {
		trace_tf(idrp_trace_options,TR_NORMAL, 0, ("Out of sequence, sequence number received was %d, expected was %d",
			seq, peer->rcv_seq_expected));
		return(FALSE);
	}
}

static 
in_seq_range(peer, seq)
idrpPeer *peer;
u_int seq;
{
	if (seq == peer->rcv_seq_expected-1 ) 
		{
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("In sequence in_seq_range"));

		return(TRUE);
		}
	 else  {
		trace_tf(idrp_trace_options, TR_NORMAL, 0, (" Out of sequence, sequence number received was %d, expected was %d",
			seq, peer->rcv_seq_expected));
		return(FALSE);
		}
		 
}



/*
 * A packet for a particular peer task has arrived.  Figure out what kind of packet it
 * is, and do something about it.
 */

static void 
idrp_recv_pdu(peer, pdu, length,p_next_hop,p_dst_intf)
idrpPeer *peer;
idrpPdu *pdu;
int length;
sockaddr_un	*p_next_hop;
sockaddr_un	*p_dst_intf;
{
u_char type;
u_int rcv_seq, rcv_ack;		/* Seq, Ack fields from received PDU */


	/* If we're closed, don't even worry about it. */
	if (peer->state == IDRP_CLOSED)
		return;


	/* Validate the checksum. */

	if (!checksum_ok(peer, pdu, length))
		{
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, 
			     ("IDRP PDU error - checksum failed on PDU from peer %s", peer->name));
		return;
		}

	/* Validate the header - min and maximum length 
	 * - please notice the header length is only the BISPDU header
	 *   length having been unwrapped from the protocol header
	 *
	 */ 
	
	if (length < MinPDULength)
		{ 
		trace_tf(idrp_trace_options, TR_NORMAL,0,("failed minimum size check"));
		idrp_sm(peer,IDRP_HEADER_ERROR);
		return;
		}
	if (!valid_header(peer,pdu,length))
		{
		trace_tf(idrp_trace_options, TR_NORMAL,0,("failed valid header check"));
		idrp_sm(peer,IDRP_HEADER_ERROR);	
		return;
		}
 
	/* Extract sequence information. */

	bcopy((caddr_t) pdu->header.seq, &rcv_seq, sizeof(pdu->header.seq));
	rcv_seq = ntohl(rcv_seq);
	bcopy((caddr_t) pdu->header.ack, &rcv_ack, sizeof(pdu->header.ack));
	rcv_ack = ntohl(rcv_ack);


	/* Update the hold timer */

	task_timer_set(peer->idrp_hold_timer, peer->hold_time, (time_t) 0);

	/* Process CEASE and ERROR PDUs before anything else. 
	 * CEASE and ERROR pdus need to be processed with ack  
	 * - processed first because bad news needs 
	 * to travel fast
	 */

	type = pdu->header.pdu_type;

	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP (%s) RCV %s seq %d ack %d offer %d avail %d len %d from %s", 
	      trace_state(peer->state, idrpStates),
	      (type > IDRP_PDUTYPE_MAX ? "Bogus" : idrpTypes[type]), 
	       rcv_seq, rcv_ack, pdu->header.credit_offered, pdu->header.credit_available,
		length, peer->name));

	switch (type) {

	case IDRP_OPEN_TYPE:
		if (idrp_parse_open(peer, pdu))  
			{
			/* bogus open - don't process acks */
			idrp_sm(peer,IDRP_OPEN_ERROR);
			pdu = NULL;
			}
		break;

	case IDRP_ERROR_TYPE:
		process_error(peer, pdu);
		pdu = NULL;
		break;

	case IDRP_CEASE_TYPE:
		process_cease(peer, pdu, length);
		pdu = NULL;
		break;
	}
	
	if (pdu == NULL)
		return;		/* Nothing else to do. */

	/* Process received acknowledgements. */
	/* the above code has streamed out    */
	/* - close state 		*/
	/* ERROR pdus (with or without errors) */  
	/* CEASE pdus (with or without errors) */
	/* OPEN pdus with errors */
	/* - ack_pdu handles ack by type and state */  

	acked_something = FALSE;
	switch (peer->state) {
	
	/* Open_Sent state: for OPEN PDUs only, process ack
	 */
	
	case IDRP_OPEN_SENT:
		if (type == IDRP_OPEN_TYPE)
			{ 	
			ack_pdu(peer, rcv_ack, pdu->header.credit_offered,type);
			}
		break;

	default:
		ack_pdu(peer,rcv_ack,pdu->header.credit_offered,type);
		break;
	}

	/* Now process the send sequence number. */

	switch (peer->state) {

	/*
	 * Open_sent state:  treat everything as in-sequence except an out-of-sequence OPEN.
	 * This allows us to bitch about FSM errors.
	 */
	case IDRP_OPEN_SENT:
		peer->rcv_seq_expected = rcv_seq;  /* Establish initial sequence number */
		if (type == IDRP_OPEN_TYPE) {
			if (in_sequence(peer, rcv_seq)) {
				process_rcv_seq(peer, pdu);
			} else {
				pdu = NULL;
			}
		}
		break;


        case IDRP_OPEN_RCVD:
                if (type == IDRP_OPEN_TYPE)
                        {
                        if (in_seq_range(peer,rcv_seq))
                                {
                                process_rcv_seq_open(peer,pdu);
                                }
                        else
                                {
                                pdu = NULL;
                                }

                        }
                else
                        {
                        /* process just like the rcv_seq  */
                        if (in_sequence(peer, rcv_seq))
                                {
                                process_rcv_seq(peer, pdu);
                                }
                         else
                                {
                                pdu = NULL;
                                }
                        }
                break;



	/*
	 * Everything else--received packets must be in-sequence or we drop them.
	 */
	default:
		if (in_sequence(peer, rcv_seq)) {
			process_rcv_seq(peer, pdu);
		} else {
			pdu = NULL;
		}
		break;
	}

	if (pdu == NULL)
		return;		/* Nothing else to do. */

	/*
	 * Now process the received PDUs.
	 */
	switch (type) {

	case IDRP_OPEN_TYPE:
		process_open(peer, pdu);
		break;

	case IDRP_UPDATE_TYPE:
		process_update(peer, pdu, length,p_next_hop,p_dst_intf);
		break;

	case IDRP_KEEPALIVE_TYPE:
		process_keepalive(peer, pdu, length);
		break;

	case IDRP_RIB_REFRESH_TYPE:
		process_rib_refresh(peer, pdu, length);
		break;

#ifdef ECHOKLUDGE
	case IDRP_ECHO_TYPE:
		process_echo(peer, pdu, length);
		break;

	case IDRP_ECHO_REPLY_TYPE:
		process_echo_reply(peer, pdu, length);
		break;
#endif

	default:
		process_header_error(peer, pdu, length);
		break;

	}
}


/*
 * A packet has arrived on the common socket.  Fetch it and demux.
 * - here we have to change radically the idrp code 
 * - currently it does only IP based CLNP -  
 * - note received pdu is returned by task???  
 */

void 
idrp_recv(tp)
task *tp;
{
	idrpPeer *peer;
	idrpPdu *pdu;
	idrp_proto_header *pdu_proto;
	size_t length;
	int len;
	sockaddr_un *p_next_hop;
	sockaddr_un  dst;		
	sockaddr_un *p_dst_intf = &dst; 

	pdu_proto = (idrp_proto_header *) task_recv_buffer;
	if (task_receive_packet(tp, &length)) {
		return;
	}

	switch (idrp_this_node.proto_sock)
		{
		case IDRP_PDU_PROTO_IP_RAW:
			{
			peer = idrp_ip_raw_sock_recv(pdu_proto);
			if (!peer)
				{
				/* trace this and return */
				return;
				}
			pdu = (idrpPdu *) &pdu_proto->hdr.ip.pdu;

			/* horrible hack to get around the problems
			 * with the pdus not being the
			 * right thing.  I'll fix it ASAP
			 * skh -- 
			  * dst.a.sa_family = AF_INET;
			  * dst.a.sa_len = sizeof(sockaddr_un);  
			  * bcopy(&p_ip->hdr.ip.ip_dst,&dst.in.sin_addr,sizeof(struct in_addr));
			 */
	
			p_dst_intf = (sockaddr_un *) NULL;
			len = length - IDRP_IP_HEADER_LEN;
			p_next_hop = (sockaddr_un *) peer->gw.gw_addr;
			}
			break;

		case IDRP_PDU_PROTO_UDP:
			peer = idrp_udp_sock_recv(pdu_proto);
			if (!peer)
				{
				/* trace this as below and return */
				return;
				}
			p_next_hop = task_recv_srcaddr;  
			pdu = (idrpPdu *) &pdu_proto->hdr.udp.pdu;
			len = length - IDRP_UDP_HEADER_LEN;
			p_next_hop = (sockaddr_un *) peer->gw.gw_addr;
			break;

		case IDRP_PDU_PROTO_CLNP:
			peer = idrp_clnp_sock_recv(pdu_proto);
			if (!peer)
				{
				/* trace this and exit */

				return;
				}
			pdu = (idrpPdu *) &pdu_proto->hdr.clnp.pdu;
			len = length - (peer->neighbor.isoa_len + rt_net[0].isoa_len);
			p_next_hop = (sockaddr_un *) peer->iso_gw.gw_addr;
			p_dst_intf = (sockaddr_un *) NULL;
			break;

		case IDRP_PDU_PROTO_IDRP:
			{
			peer = idrp_idrp_sock_recv(pdu_proto,
				task_recv_srcaddr, 
				task_recv_dstaddr);

			/* fix this to put in next hop */

			if (!peer)
				{
				/* just exit - bogus packet */
				return;
				}

			/* 
			 * hack to set to null till I fix it.
			  p_next_hop = (sockaddr_un *) &p_idrp_hdr->src;
			 */
			p_dst_intf = (sockaddr_un *) NULL ;	
			len = length;
			pdu = (idrpPdu *) pdu_proto;
			}
			break;
		}


	/* Ensure that it's an IDRP packet. */

	if (pdu->header.pid != IDRP_PID) {
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("PDU type 0x%x not IDRP, from ", peer->name));
		return;
	}

	/* Deliver the packet to the appropriate task. */
	
	idrp_recv_pdu(peer, pdu, len,p_next_hop,p_dst_intf);
}

sockaddr_un *
idrp_sockdup __PF2( src, sockaddr_un *,
		    dst, sockaddr_un *)
{
    register size_t len = src->a.sa_len;
    register byte *sp = (byte *) src;
    register byte *dp;

   /* like sockup, except we take out of idrp memory */

   dst = (sockaddr_un *) idrp_local_mem_fit(len); 

    dp = (byte *) dst;
    while (len--) {
	*dp++ = *sp++;
    }

    return dst;
}


void 
idrp_free_valid_results(peer,p_res)
idrpPeer		*peer;
parse_results		*p_res;
{
idrp_attribute_record	*p_att;
int cnt = 0;
idrpRoute_entry		*p_rte;
idrpRoute_entry		*p_next_rte;

	if (p_res->p_att == (idrp_attribute_record *) NULL)
		{
		/* only withdraws so leave the rest
		 * be freed in process_update
		 */
 
		return;
		}

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("pre-call idrp_free_valid_results p_att = %x",p_res->p_att));

	IDRP_PARSE_ATT_LIST(p_att,p_res)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("(cnt %x) idrp_free_valid_results p_att = %x",cnt,p_att));

		/* throw away any route from the 
		 * parse_re route_id
		 */
	
		IDRP_PARSE_RTE_DEL_WALK(p_rte,p_att,p_next_rte)
			{	
			cnt += idrp_free_routeid_entry(p_att,p_rte);
			} IDRP_PARSE_RTE_DEL_WALK_END;

		p_att->p_parse_re = (idrpRoute_entry *) NULL;	

		trace_tf (idrp_trace_options, TR_NORMAL,0, ("(cnt %x) idrp_free_valid_results p_att = %x",cnt,p_att));

		p_att->ref_cnt = p_att->ref_cnt - cnt;
		if (p_att->ref_cnt ) 
			{
			assert(p_att->route_id_list != (idrpRoute_entry *) NULL);
			trace_tf(idrp_trace_options, TR_NORMAL,0, ("not all route deleted from attribute record %x %s",p_att,peer->name));
			}
		else
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0, (" all routes deleted from attribute record %x %s",p_att,peer->name));


			if (p_att->local_mask == IDRP_LOCAL_NO_PDU)
				{	
				trace_log_tf(idrp_trace_options, 0, LOG_ERR,   
					     ("problem with deleting the throw away valid update pdus peer %s",peer->name));
				task_quit(0);
				}
			else
				{
				idrp_free_attr_rec(peer,p_att,IDRP_INIT_ROUTE_ID,TRUE);
				}
			}
		}	
}	
			

byte *
parse_rib_att(p_open_rib_att,rib_count,peer)
idrp_RIB_att	*p_open_rib_att;
u_int		rib_count;
idrpPeer	*peer;
{
int	i,j,rib_id;
u_int	att_cnt;
u_int   len;
byte	*cp = (byte *)p_open_rib_att;
byte	*cp_null = 0; 
u_int	flag;
u_int   tmp_flag = 0;
u_int	tmp_mask = 0;
int     value;

	cp++;

#ifdef	IDRP_QOS

	/* increate the pointer */

	/* create idrp_mask out of the open
	 * RibAtts
	 */

	for (i = 0; i < rib_count; i++)
		{
		/* each rib has m number of  distinguishing attributes
		 * 1) grab the count of the attributes
		 * 2) grab each attribute and build a temporary mask
		 */

		att_cnt = *cp;
		cp++;
		for (j = 0; j < att_cnt; j++)
			{
			/* get attributes for this rib into temporary mask
			 */

			flag = *cp;
			if (flag < 0 && flag > IDRP_ATTR_MAX)
				return(cp_null);
			if (!idrpAttrDIS[flag]) 
				return(cp_null); 

			tmp_flag = idrpAttrBits[flag];
			tmp_mask |= tmp_flag;
			cp++;
			if (tmp_flag == IDRP_ATTBT_SECURITY)
			  {
			  len = *cp;
			  cp++;
			  if (len !=  FAA_SEC_ID_LENGTH)
			    return(cp_null);
			  value = *cp;
			  cp++;
			  }
			if (tmp_flag == IDRP_ATTBT_LOCALLY_DEF_QOS)
			  {
			    /* No support for Locally_defined qos */

			  return(cp_null);
			  }
		           
			}
		/* translate mask to qos ribs supported
		 */

		rib_id = rib_supp_mask(tmp_mask,value); 

		/* if the rib_id returned is zero, then this rib is not supported
		 * if the peer does not support the rib 
		 */
 
		if (rib_id <= 0 && rib_id > rib_count )
			return(cp_null);

		if (peer->rib_supp[rib_id] == 0)
			return(cp_null);


		tmp_mask = 0;
		}

	return(cp);

#else	/* IDRP_QOS */

	if (rib_count)
		return(cp_null);

	/* if count is zero return the value
	 * of the pointer increamented
	 */
	
	return(cp);

#endif	/* IDRP_QOS */ 
}
