/* 
 * $Id: idrp_pdus.c,v 1.9 1996/05/25 13:31:38 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

#define DROP_PACKET (drop_packet())
/* #define DROP_PACKET (FALSE) */

#include "include.h"
#include "iso.h"
#include "idrp.h"
#include <stdio.h> 
#include "md4global.h"	/* RAREF globals and constants */
#include "md4.h"	/* md4-specific includes */

/* 
 * idrp_pdus.c 
 *
 * pdu sending 
 *	idrp_post(peer,buf) - post a message, and put on re-xmit if seq
 * 	idrp_post_freeze_seq_pdu(peer,buf) - freeze sequence
 *	post_enqueued_pdus(peer) - post as many enqueued PDUs as possible
 * 	idrp_send_pdu(peer,pdu,type,length,freeze_seq) - send pdu to peer 
 *	send_open(peer,freeze_seq) - send open BISPDU to peers
 *	send_update_pdu(peer,route,withdrawl,unreachable)
 *	idrp_send_error(peer,code)
 *      send_cease(peer)
 * 	send_keepalive(peer)
 *	send Refresh_pdu
 */ 
  
 
/*
 * Post a buffer to the socket for transmission.  Move it to the
 * retransmission queue if it is sequenced.
 *
 * The buffer must not be linked into any queue.
 */
static void 
idrp_post(peer, buf)
idrpPeer *peer;
idrpBuffer *buf;
{
	sockaddr_un *p_dst;
	u_int longhold;
	idrp_fixed_pdu_header *header;
	u_char	digest[IDRP_AUTH_CODE_SIZE];	

	header = &(buf->pdu->header);
	/* Fill in the flow control fields.  The sequence number was assigned
	 * earlier; all other fields come from the current state. */

	longhold = htonl(buf->seq);
	bcopy((caddr_t) &longhold, (caddr_t) header->seq, sizeof(header->seq));

	longhold = htonl(peer->rcv_seq_expected - 1);
	bcopy((caddr_t) &longhold, (caddr_t) header->ack, sizeof(header->ack));

	header->credit_offered = peer->rcv_credit;
	header->credit_available = peer->upper_window_edge - peer->send_next;

	bzero((caddr_t) header->validation, IDRP_AUTH_CODE_SIZE); /* zero validation now */
	switch  (peer->AuthType)
		{
		case IDRP_AUTH_CODE_TEST:
			bzero(digest, IDRP_AUTH_CODE_SIZE);
			break;
			
		case IDRP_AUTH_CODE_CLEAR:
			md4((u_char *)buf->pdu,buf->length,digest); 	 /* validation scheme */
			break;

		case IDRP_AUTH_CODE_CRYPT:
			/* with zeros in buffer should fail reception */ 
			bzero(digest, IDRP_AUTH_CODE_SIZE);
			trace_tf(idrp_trace_options, TR_NORMAL,0, ("No Authentication code crypt support; crypt is set on this peer %s",
				peer->name));
			break;   	
			
		case IDRP_AUTH_CODE_SIMPLE:
			bzero(digest, IDRP_AUTH_CODE_SIZE);
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("We don't support simple validation, but it is set for peer %s",
				peer->name));
			break;	
		}

	/* Copy the buffer to the send buffer and post it. */

	bcopy ((caddr_t) &digest,header->validation, 16);
	bcopy((caddr_t) buf->pdu, task_send_buffer, buf->proto_len);
	p_dst = idrp_send_dest_set(peer);

	/* test on the type of socket, and then call appropriate send
	 * routine
	 */

	switch (idrp_this_node.proto_sock)
		{
		case IDRP_PDU_PROTO_CLNP:
			idrp_send_clnp_pdu(&idrp_this_node,peer,task_send_buffer,buf->proto_len,p_dst); 
			break;

		case IDRP_PDU_PROTO_UDP:
			(void) task_send_packet(idrp_this_node.task, task_send_buffer, buf->proto_len,0, p_dst);
			break;

		case IDRP_PDU_PROTO_IP_RAW:
			if (task_send_packet(idrp_this_node.task,task_send_buffer,buf->proto_len,0,p_dst) < 0)
				{
				trace_log_tf (idrp_trace_options, 0, LOG_ERR,
					      ("error sending IDRP PID pdu peer %s",peer->name));
				}
			break;
		case IDRP_PDU_PROTO_IDRP:
			idrp_send_idrp_pdu(&idrp_this_node,peer,task_send_buffer,buf->proto_len,p_dst);
			break;
		}	

					
	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP (%s) XMIT %s (%s) seq %d ack %d offer %d avail %d len %d to %s \n", 
		trace_state(peer->state, idrpStates),
		idrpTypes[header->pdu_type], 
		buf->sequenced ? "seq" : "unseq",
		buf->seq,
		peer->rcv_seq_expected - 1,
		header->credit_offered,
		header->credit_available,
		buf->length,
		peer->name));

	/* dump the PDU (in hex) to the trace file */
	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("Sending:"));
	    {
	      int i;
	      char *cp = (char *) buf->pdu;
	      for (i=0; i<buf->length; i++) {
		tracef("%2.2x  ", *cp++);
		/* if ((i%35) == 0)
		  trace_tf(idrp_trace_options, TR_NORMAL, 0, NULL);*/
	      }
	      /* trace_tf(idrp_trace_options, TR_NORMAL, 0, NULL);*/
	    }
		
	/* Put the buffer on the retransmission queue if it was sequenced. */

	if (buf->sequenced) {
		*peer->rexmit_queue_tail = buf;
		peer->rexmit_queue_tail = &buf->next;
		start_rexmit_timer(peer);
		}
	else
		{
		free_buffer(buf);
		}
}

/*
 * Post as many enqueued PDUs as possible.
 */
void 
post_enqueued_pdus(peer)
idrpPeer *peer;
{
	idrpBuffer *buf;

	/* Process the transmit queue. */

	while (peer->xmit_queue_head) {

		/* If we're flow blocked, give up. */

		if ((peer->state == IDRP_ESTABLISHED)
			&& (peer->upper_window_edge <= peer->send_next )) {
			trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP %s flow blocked", peer->name));
			break;
		}

		/* Not flow blocked.  Pop the top of the queue, assign it a sequence number, and post it. */

		buf = peer->xmit_queue_head;
		peer->xmit_queue_head = buf->next;
		if (buf->next == NULL)
			peer->xmit_queue_tail = &peer->xmit_queue_head;
		buf->next = NULL;
		buf->seq = peer->send_next++;

		idrp_post(peer, buf);
	}
}

/*
 * Post as many enqueued PDUs as possible.
 */
void
idrp_post_freeze_seq_pdu(peer,buf)
idrpPeer *peer;
idrpBuffer *buf;
{
        /* Process the transmit queue. */

        while (peer->xmit_queue_head) {

                /* If we're flow blocked, give up. */

                if ((peer->state == IDRP_ESTABLISHED)
                        && (peer->upper_window_edge <= peer->send_next )) 
			{
			trace_tf(idrp_trace_options, TR_NORMAL,0,("IDRP %s flow blocked", peer->name));
                        break;
                	}

                /* Not flow blocked.  Pop the top of the queue, it should be
                        the frozen open message
                 */

                if (buf != peer->xmit_queue_head)
                       {
                        /* we should only have the this message on the transmit queue */

			trace_tf(idrp_trace_options, TR_NORMAL,0,("this transmitt queue is having problems.peer = %s ",peer->name));
                        }
                else
                        {
                        peer->xmit_queue_head = buf->next;
                        if (buf->next == NULL)
                                peer->xmit_queue_tail = &peer->xmit_queue_head;
                        buf->next = NULL;

                        /* retransmit with last sequence number
                         * - post the buffer
                         * - assume we are going to get an open back with right ack
                         */

                        buf->seq = peer->send_next-1;
                        idrp_post(peer, buf);
                        }
        }
}



/*
 * Build and attempt to send a PDU.
 */

void 
idrp_send_pdu(peer,pdu,pdu_proto,type,length,proto_length,buf_len,freeze_seq)
idrpPeer *peer;
idrpPdu *pdu;
idrp_proto_header	*pdu_proto;
u_int type;
u_int length;			/* length of total buffer size */
u_int proto_length;		/* length of protocol header */
u_int buf_len;			/* length of protocol header */
u_int freeze_seq;               /* freeze the sequence number at current one */
{
	u_short hold;
	idrp_fixed_pdu_header *header;
	idrpBuffer *buf;

	header = &(pdu->header);
	/* Fill in most of the fixed header. */
	/* PID, length, type, source, destination */
	/* copy source and destination from the Peer structure */

	header->pid = IDRP_PID;
	hold = htons(length);
	bcopy((caddr_t) &hold, (caddr_t) header->pdu_length, sizeof(header->pdu_length));
	header->pdu_type = type;

	/* fill in the UDP header if proto_sock = UDP */
	if (peer->proto_sock == IDRP_PDU_PROTO_UDP)
		{
		/* use the initial rt_net 
		 * - further use of multiple NETs is left for ISO capable kernels
		 */

		bcopy((caddr_t) &peer->neighbor.isoa_genaddr, (caddr_t) &pdu_proto->hdr.udp.hdr.dest.isoa_genaddr,peer->neighbor.isoa_len);
		pdu_proto->hdr.udp.hdr.dest.isoa_len = peer->neighbor.isoa_len;
		bcopy((caddr_t) &rt_net[0].isoa_genaddr, (caddr_t) &pdu_proto->hdr.udp.hdr.src.isoa_genaddr,rt_net[0].isoa_len);
		pdu_proto->hdr.udp.hdr.src.isoa_len = rt_net[0].isoa_len;
		}

	/* Build the buffer structure. */

	buf = (idrpBuffer *) idrp_local_mem_fit(sizeof(idrpBuffer));
	buf->next = NULL;
	buf->pdu_proto = pdu_proto;
	buf->proto_len = length;
	buf->pdu = pdu;
	buf->length = length - proto_length;
	buf->buf_len = buf_len; 
	buf->sequenced = IS_SEQUENCED(type);

	if (freeze_seq)
		{
		*peer->xmit_queue_tail = buf;
		peer->xmit_queue_tail = &buf->next;
		idrp_post_freeze_seq_pdu(peer,buf);
		return;
		}

	/* If not sequenced, post the buffer immediately.  Otherwise, enqueue it. */

	if (buf->sequenced) {
		*peer->xmit_queue_tail = buf;
		peer->xmit_queue_tail = &buf->next;
		post_enqueued_pdus(peer);
	} else {
		buf->seq = peer->send_next;
		idrp_post(peer, buf);
	}
}

/*
 * Send an OPEN PDU to (peer).
 */

void 
send_open(peer,freeze_seq)
idrpPeer *peer;
u_int	freeze_seq;
{
	u_short hold;
	idrp_source_RDI *openrdi;
	idrp_RIB_att *openribatt;
	idrp_confed_id *openconfed;
	idrp_open_pdu_header *openhead;
	idrp_authentication_code *openauthcode;
	int len,header_len;
	u_char *cp;
	idrp_open_pdu	*open;
	u_char	*basecp;
	idrpPdu	*pdu;
	idrp_proto_header	*pdu_proto;
	u_int count;

	/* allocate the pdu */

	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(peer->pdu_maxsendsize);

	/* offset the pdu pointer for the protocol socket
	 * and record the extra header length 
	 */

	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock);
	open = (idrp_open_pdu *) &pdu->pdu.open;
	cp = (u_char *) open;
	basecp = (u_char *) open;

	/* Fixed header */


 	openhead = &(open->header);
	openhead->version = IDRP_VERSION_1;

	hold = htons(peer->hold_time);
	bcopy((caddr_t) &hold, (caddr_t) openhead->idrp_hold_time, sizeof(openhead->idrp_hold_time));

	hold = htons(peer->pdu_maxrcvsize);
	bcopy((caddr_t) &hold, (caddr_t) openhead->idrp_maxpdu_size, sizeof(openhead->idrp_maxpdu_size));

	cp += sizeof(idrp_open_pdu_header);

	/* Source RDI field */

	openrdi = (idrp_source_RDI *) cp;
	openrdi->source_RDI_length = rt_rdi.isoa_len;
	bcopy((caddr_t) rt_rdi.isoa_genaddr, (caddr_t) openrdi->source_RDI, openrdi->source_RDI_length);
	cp += openrdi->source_RDI_length + 1;

	/* RIB attributes */

#ifdef	IDRP_QOS
	openribatt = (idrp_RIB_att *) cp;
	cp = (byte *) send_rib_atts(openribatt,peer);

#else
	openribatt = (idrp_RIB_att *) cp;
	openribatt->RIB_att_count = 0;
	cp += sizeof(openribatt->RIB_att_count);

#endif	/* IDRP_QOS */

	/* Confederation IDs */
	/* %%eventually take from config */

	openconfed = (idrp_confed_id *) cp;
	openconfed->confed_id_count = 0;
	cp += sizeof(openconfed->confed_id_count);

	/* Authorization code */

	openauthcode = (idrp_authentication_code *) cp;
	openauthcode->authentication_code = IDRP_AUTH_CODE_CLEAR;
	cp += sizeof(openauthcode->authentication_code);

	/* Now send it out. */
	len = cp - basecp + sizeof(idrp_fixed_pdu_header) + header_len; 
	idrp_send_pdu(peer, pdu, pdu_proto, IDRP_OPEN_TYPE, len,header_len,peer->pdu_maxsendsize,freeze_seq);
}


/*
 * Send an update PDU
 */

extern void
send_update_pdu(peer,p_send_list,unreachable)
idrpPeer *peer;
idrp_send_list *p_send_list;
u_int unreachable;
{
/* send list provides the following
 * - pointer to attribute record 
 * - pointer head of idrpRoute list for nlri
 *   nlri is linked idrpRoute->route_out[peer->id] structure  
 * - pointer to tail of idrpRoute list for nlri 
 * - Witdrawl structure with IDs 

/* idrpRoute pointers */
idrpRoute	*p_idrp_rt;		/* generic route pointer */
idrpRoute	*p_irt_tail;		/* list of idrpRoute structures - tail */
idrp_attribute_record	*p_att;		/* attribute record */ 

/* pdu pointers */

idrp_proto_header	*pdu_proto;
idrpPdu *pdu;
idrp_update_pdu *hdr;
nlri_info *nlriptr;
u_char	*nlristart;

/* temporary idrp attribute space
 */ 
idrp_attribute_entry	xmit_attr;

/* generic character pointers */

int i;
int cnt;
int len;
int seglen;	
int tot_len,header_len;
u_char *segout;
u_char *cpout;
u_char *p_out;
u_char *cpbase;
u_short hold;
u_int longhold;


	/* set up pointers for the task */
	/* pdu and maximum pdu size */

	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(peer->pdu_maxsendsize);
	bzero(pdu_proto,peer->pdu_maxsendsize);

	/* set-up the pdu pointer to point to idrp fixed header */

	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock); 

	/* fill in the unfeasible route info. 
	 * make sure routes won't overload the UPDATE 
	 * maximum size 
	 * return with complaint
	 */

	tot_len = p_send_list->withdraw->count * sizeof (route_id);
	tot_len += MinPDULength + IDRP_NO_NLRI_UPDATE; 

	if (tot_len > peer->pdu_maxsendsize)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("bad withdraw pdu size at send_update_pdu peer %s cnt %s",
				peer->name,p_send_list->withdraw->count));
		trace_tf (idrp_trace_options, TR_NORMAL,0,("p_send_list pointer = %x",p_send_list));
		return;
		}

	/* drop the count of the withdraws into the pdu */

	hold = cnt = p_send_list->withdraw->count;
	hold = htons(hold);

	cpout = (u_char *) &pdu->pdu.update;

	bcopy(&hold,&pdu->pdu.update.unfeasible_route_count,UNFEASIBLE_RT_CNT_LEN);

	/* set the pointer to the first byte beyond the unfeasible route count */

	cpout += UNFEASIBLE_RT_CNT_LEN;

	if (cnt)
		{
		/* unfeasible route ids - so stuff in update pdu */ 
		for (i = 0;  i < cnt; i++)
			{
			/* 4 bytes of route_id out */

			longhold = htonl(p_send_list->withdraw->withdraw[i]);
			bcopy(&longhold, cpout, sizeof(route_id));
			cpout += sizeof(route_id);
			}
		}

	/* is it just unreachables - */

	if (unreachable)
		{ 
		/* - Just unreachables so put 
		   - put out fixed header for path attribute length */

		longhold = 0;
		bcopy((caddr_t) &longhold,cpout,sizeof(hdr->tot_path_att_len));
		cpout += sizeof(hdr->tot_path_att_len);
	
		/* trace this update and send with just unreachables */
	
		trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP XMIT Update to %s", peer->name));
		len = cpout - (u_char *) pdu + header_len;
		idrp_send_pdu(peer, pdu, pdu_proto, IDRP_UPDATE_TYPE, len, header_len,peer->pdu_maxsendsize,FALSE);
		return;
		}

	/* set up the attribute record */

	p_att = (idrp_attribute_record *) p_send_list->p_attr; 

	/* set the cpbase (output counter) to the first byte of the path attributes 
	 */
		
	cpout += sizeof(hdr->tot_path_att_len);
	cpbase = cpout;
	
	/* Now add the route ID. */

	*cpout++ = IDRP_ATTRFLG_ROUTE_SEPARATOR;  /* Well-known, mandatory */
	*cpout++ = IDRP_ATTR_ROUTE_SEPARATOR;
	*cpout++ = 0;
	*cpout++ = IDRP_ROUTE_SEPARATOR_LENGTH;

	/* route id out depends on the neighbor */ 

	longhold = htonl(peer->route_id_out);
	bcopy((caddr_t) &longhold, cpout, IDRP_ROUTE_ID_LENGTH);
	cpout += IDRP_ROUTE_ID_LENGTH;
	bcopy((caddr_t) &p_send_list->loc_pref,cpout,IDRP_ROUTE_PREF_LENGTH);
	cpout += IDRP_ROUTE_PREF_LENGTH;

	trace_tf (idrp_trace_options, TR_NORMAL,0,("peer %s got UPDATE with attribute mask %x",peer->name,p_att->idrp_mask)); 

	/* more attributes to go in the pdu
	 * - fill all in after the ROUTE_SEPARATOR
	 * - 1st EXT_INFO (optional) 
	 * - 2nd RDPATH (mandatory) 
	 * - got filled in above due to always changing
	 */
		
	if (p_att->attrib[IDRP_ATTR_EXT_INFO].present)
		{
		/* present and non-mandatory - stuff in the update pdu 
		 * - EXT_INFO only has zero length  
		 */
		*cpout++ = p_att->attrib[IDRP_ATTR_EXT_INFO].flags;
		*cpout++ = (u_char) IDRP_ATTR_EXT_INFO;
		*cpout++ = 0;
		*cpout++ = 0;
		}
		
	/* path attributes new routes - so fill in attributes 
	 * - from the attribute record 
	 * Mandatory RD_PATH
	 *
	 */  

	if (p_att->attrib[IDRP_ATTR_RD_PATH].present == 0)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("update has no rd_path peer = %A", 
			sockbuild_iso((byte *) peer->neighbor.isoa_genaddr, peer->neighbor.isoa_len)));
			return;
		}
	
	/* First encode the path information. 
	 * In, array but at first lets hard code it
	 */ 
	
	*cpout++ = IDRP_ATTRFLG_RD_PATH; /* Well-known, mandatory */
	*cpout++ = IDRP_ATTR_RD_PATH;
	
	/* our RD is in local part of array
	 * if neighbor is external or local Route 
	 * this part of array is already set-up 
	 * in the attribute - attr_rec->attrib[IDRP_LOCAL_RD_PATH];
	 */
	
	len = hold = p_att->attrib[IDRP_ATTR_RD_PATH].length; 
	hold = htons(hold);
	bcopy((caddr_t) &hold, cpout, 2);	/* Attribute length */
	cpout += 2;

	/* check to make sure length is not zero */

	if (!p_att->attrib[IDRP_ATTR_RD_PATH].length) {
		trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("null path in send_update_pdu, peer %s",peer->name));
		task_quit(0);
		}

	/* if a local RD path has been added to this RD path */
	/* then add the local in from the local path */ 

	if (p_att->attrib[IDRP_ATTR_LOCAL_RD].present)
		{
		/* take off the length of the local RD */
		seglen = p_att->attrib[IDRP_ATTR_LOCAL_RD].length;	
		len -= seglen; 
		segout = cpout + len;	
		bcopy ((caddr_t) p_att->attrib[IDRP_ATTR_LOCAL_RD].data,(caddr_t) segout,seglen);	
		}
	
        /* copy part that came from another router
         * for a local router this may be zero
         */

        if (len)
                {
                bcopy ((caddr_t) p_att->attrib[IDRP_ATTR_RD_PATH].data,cpout,len);
                }

        /* reset the length for the path attribute length to be after path attribute */

        cpout += p_att->attrib[IDRP_ATTR_RD_PATH].length;


	/* if we need to send the next hop */	 

	if (send_next_hop(p_att,peer,&xmit_attr))
		{ 
		*cpout++ = p_att->attrib[IDRP_ATTR_NEXT_HOP].flags;
		*cpout++ = (u_char) IDRP_ATTR_NEXT_HOP;
		hold = htons(p_att->attrib[IDRP_ATTR_NEXT_HOP].length);
		bcopy((caddr_t) &hold,cpout,2);  
		cpout +=2;
   		bcopy((caddr_t) p_att->attrib[IDRP_ATTR_NEXT_HOP].data,cpout,p_att->attrib[IDRP_ATTR_NEXT_HOP].length);
		cpout +=p_att->attrib[IDRP_ATTR_NEXT_HOP].length;
		}


	/* here we see if we need the  attributes
	 * (DIST_LIST_INCL (5)
	 * (DIST_LIST_EXCL (6)
	 * (MULTI_EXIT_DISC(7)
	 * (TRANSIT_DELAY  (8)
	 * (RESIDUAL_ERROR (9)
	 * (EXPENSE        (10)
	 * (LOCALLY_DEF    (11)
	 * (HIERARCH_REC   (12)
	 */ 

	for (i = IDRP_ATTR_DIST_LIST_INCL ; i <IDRP_ATTR_HOP_COUNT; i++)
		{
		if (p_att->attrib[i].present)
			{
			/* present and non-mandatory - stuff in the update pdu
			 */

			*cpout++ = p_att->attrib[i].flags;
			*cpout++ = (u_char) i;
			hold = htons(p_att->attrib[i].length);
			bcopy((caddr_t) &hold,cpout,2);  
			cpout +=2;
   			bcopy((caddr_t) p_att->attrib[i].data,cpout,p_att->attrib[i].length);
			cpout +=p_att->attrib[i].length;
			}
		}


	 /* (HOP_COUNT (mandatory)   (13)
	  * (Security 		     (14)
	  * CAPACITY (mandatory      (15)	 	 
	  * PRIORITY		     (16)	
	  */	
	 
	for (i = IDRP_ATTR_HOP_COUNT; i< IDRP_ATTR_MAX; i++)
		{
		if (p_att->attrib[i].present)
			{
			/* present - mandatory or not - stuff in update pdu
			 */

			*cpout++ = p_att->attrib[i].flags;
			*cpout++ = (u_char) i;
			hold = htons(p_att->attrib[i].length);
			bcopy((caddr_t) &hold,cpout,2);  
			cpout +=2;
			if (i == IDRP_ATTR_HOP_COUNT)
				{
				if (peer->type == IDRP_PEER_EXTERNAL)
					{
					*cpout++ = p_att->p_opts->hopcount + 1;
					}
				else
					{
					*cpout++ = p_att->p_opts->hopcount;
					}
				}
			else if (i == IDRP_ATTR_CAPACITY)
				{
				if ((p_att->peer_alloc->type ==  IDRP_PEER_EXTERNAL) &&
				(p_att->p_opts != NULL) && (p_att->p_opts->capacity.value > idrp_this_node.Capacity))
					{
					*cpout++ = p_att->p_opts->capacity.value;
					}
				else
					{
					/* local, internal, and test peers get the
					 * same capacity value as this node
					 * - also if the receiving route has a capacity value
		 			 * - also if the receiving route has a capacity value
					 *   less than this nodes
					 */
 
				 	*cpout++ = idrp_this_node.Capacity;	
					}
				}
			else   
				{
				bcopy((caddr_t) p_att->attrib[i].data,cpout,p_att->attrib[i].length);
				cpout +=p_att->attrib[i].length;
				}
			}
		else
			{
			if (idrpManatt[i] == IDRP_ATTR_FLAG_MANDATORY)
				{ 
				trace_log_tf(idrp_trace_options, 0, LOG_ERR, ("missing mandatory attribute(%d) in send_update_pdu, peer %s",i,peer->name));
				}
			
			}
		}

	if (p_att->idrp_mask & IDRP_ATTBT_USR)
		{
		for (i = 0; i < IDRP_ATTR_USR_NO;i++) 
			{
			if (p_att->usr_attrib[i].present)
				{		
				/* present and non-mandatory - stuff in the update pdu */

				if (idrpManatt[IDRP_ATTR_USR_MIN+i])
					{
					*cpout++ = p_att->usr_attrib[i].flags;
					*cpout++ = (u_char) i;
					hold = htons(p_att->usr_attrib[i].length);
					bcopy((caddr_t) &hold,cpout,2);  
					cpout +=2;
   					bcopy((caddr_t) p_att->usr_attrib[i].data,cpout,p_att->usr_attrib[i].length);
					cpout +=p_att->usr_attrib[i].length;
					}
				}
			}
		}

	if (p_att->p_transitive)
		{
		/* put the transitive attributes in the pdu */
		
		idrp_attribute_entry_list *p_tatt = p_att->p_transitive;
			
		while (p_att)
			{
			*cpout++ = p_tatt->flags;
			*cpout++ = p_tatt->type;
			hold = htons(p_tatt->length);
			bcopy((caddr_t) &hold,cpout,ATTRIBUTE_LENGTH_SIZE);  
			cpout +=ATTRIBUTE_LENGTH_SIZE;
   			bcopy((caddr_t) p_att->usr_attrib[i].data,cpout,p_att->usr_attrib[i].length);
			cpout +=p_att->usr_attrib[i].length;
			p_tatt = p_tatt->p_next;
			}
		}

	 
		
	/* That was fun.  Now go back and update the total attribute length. */

	hold = htons(cpout - cpbase);
	p_out = cpbase - sizeof(hdr->tot_path_att_len);
	bcopy((caddr_t) &hold, p_out,sizeof(hdr->tot_path_att_len));

	/* Lastly, add the NLRI */
	/* walk list of idrp Routes putting out routes */

	/* send list will provide linked list per ISO families */
	NLRI_FAMILY_LOOP(i)
		{
		if (p_send_list->ann_nlri[i].head == (idrpRoute *) NULL)
			{
			continue;
			}

		/* copy in protocol id, length and value */
	
		*cpout = idrp_nlri_proto_id[i].proto_type;
		cpout++;
		*cpout = hold = idrp_nlri_proto_id[i].proto_len;
		cpout ++;
		bcopy(idrp_nlri_proto_id[i].proto_val,cpout,hold);
		cpout +=hold;
	 	
		/* skip over total type length */	

		cpout += 2; 
		nlristart = cpout;	

		/* set up linked list for array headers */
	
		p_idrp_rt  = p_send_list->ann_nlri[i].head;
		p_irt_tail = p_send_list->ann_nlri[i].tail;

		trace_tf (idrp_trace_options, TR_NORMAL,0,("sending pdu - head = %x, tail %x peer %s id %d ",
				p_send_list->ann_nlri[i].head,p_send_list->ann_nlri[i].tail,peer->name,peer->id)); 

		/* start copying in the nlris */

		while (p_idrp_rt != (idrpRoute *) NULL )  
			{		
			/* prefix length */

			hold = 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),hold));
			*cpout = p_idrp_rt->nlri.pfx_len;
			cpout++;
		
			/* prefix */

			bcopy(&p_idrp_rt->nlri.pfx,cpout,hold);
			cpout += hold;

			/* set up route_out array and get next nlri if
			 * it is there
			 */

			if (p_idrp_rt == p_irt_tail)
				{
				/* last one - so close the outbound list for
				 * this peer and zero p_idrp_rt to stop loop
				 */
				p_idrp_rt->route_out[peer->id].p_next = p_send_list->ann_nlri[i].head;
				p_send_list->ann_nlri[i].head->route_out[peer->id].p_back = p_idrp_rt;
				p_idrp_rt->route_out[peer->id].route_id = peer->route_id_out;

				trace_tf (idrp_trace_options, TR_NORMAL,0,("sent pdu peer %s id %d route_ids (%d,%d,%d,%d,%d,%d) ",
				peer->name,peer->id,
				p_idrp_rt->route_out[0].route_id,
				p_idrp_rt->route_out[1].route_id,
				p_idrp_rt->route_out[2].route_id,
				p_idrp_rt->route_out[3].route_id,
				p_idrp_rt->route_out[4].route_id,
				p_idrp_rt->route_out[5].route_id));

				p_idrp_rt = (idrpRoute *) 0;
				}
			else
				{
				/* still more on the loop - so link route_out list
				 * to next peer
				 * get next route
				 */
 
				p_idrp_rt->route_out[peer->id].route_id = peer->route_id_out;
				trace_tf (idrp_trace_options, TR_NORMAL,0,("sent pdu peer %s id %d route_ids (%d,%d,%d,%d,%d,%d) ",
				peer->name,peer->id,
				p_idrp_rt->route_out[0].route_id,
				p_idrp_rt->route_out[1].route_id,
				p_idrp_rt->route_out[2].route_id,
				p_idrp_rt->route_out[3].route_id,
				p_idrp_rt->route_out[4].route_id,
				p_idrp_rt->route_out[5].route_id));
				p_idrp_rt = p_idrp_rt->route_out[peer->id].p_next;
				}
			}  /* done with nlri per family */

 
		/* copy in length */

		hold = htons(cpout - nlristart);
		p_out = nlristart - sizeof(nlriptr->addr_len); 
		bcopy(&hold,p_out,sizeof(nlriptr->addr_len));
 
		} /* looping for packing nlri for all families */ 

	/* Finally, send it out. */

	tot_len = cpout - (u_char *)pdu + header_len;
	if (tot_len > peer->pdu_maxsendsize)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("IDRP XMIT peer %s update has too big pdu %d max size %d", peer->name,tot_len,peer->pdu_maxsendsize));
		return;
		}

	/* well - it's going out as best as we can tell update route_id
	 * to be ready for the next UPDATE BISPDU
	 */
 
	peer->route_id_out++;
	trace_tf(idrp_trace_options, TR_NORMAL, 0, ("IDRP XMIT Update to %s", peer->name));
	idrp_send_pdu(peer, pdu, pdu_proto, IDRP_UPDATE_TYPE, tot_len,header_len,peer->pdu_maxsendsize,FALSE);
}



/*
 * Send an error PDU
*/
void
idrp_send_error(peer, code)
idrpPeer	*peer;
u_int		code;
{
u_int header_len;
u_int packetlen;
idrp_proto_header	*pdu_proto;
idrpPdu *pdu;
u_char *p_errpdu;

	if (code != peer->last_error_pdu.error_code)
		{
		trace_tf (idrp_trace_options, TR_NORMAL,0, ("error with send_error routine peer %s last_error_pdu code = %c call = %c", 
		peer->name,code,peer->last_error_pdu.error_code));
		}


	/* set the 2nd datalen value to zero if the first
	 * value is zero
	 */


	if (!peer->last_error_pdu.data_len) 
		{
		peer->last_error_pdu.data2_len = 0;
		}

	/* determine how much memory we need for packet
	 *
	 */

	header_len = MinPDULength; 
	packetlen = header_len +  peer->last_error_pdu.data_len + 
			peer->last_error_pdu.data2_len + sizeof(idrp_error_header);

	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(packetlen); 

	/* set up pointer to error pdu
	 */
	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock);	
	p_errpdu = (u_char *) &pdu->pdu.error;

	/* put out the error code, then the subcode 
	 */

	*p_errpdu = peer->last_error_pdu.error_code;
	p_errpdu++;	
	*p_errpdu = peer->last_error_pdu.error_subcode;
	p_errpdu++;	

	/* at last the data
	 */


	if (peer->last_error_pdu.data_len)
		{
		bcopy(peer->last_error_pdu.data, p_errpdu,peer->last_error_pdu.data_len);
		p_errpdu += peer->last_error_pdu.data_len;

		if (peer->last_error_pdu.data2_len)
			{
			bcopy(peer->last_error_pdu.data2,p_errpdu,peer->last_error_pdu.data2_len);
			}

		}


	idrp_send_pdu(peer, pdu,pdu_proto, IDRP_ERROR_TYPE, packetlen,header_len,packetlen,FALSE);
}


/*
 * Send a cease PDU
 */

void 
send_cease(peer)
idrpPeer *peer;
{
int	header_len,len,buf_len;
idrpPdu	*pdu;
idrp_proto_header	*pdu_proto;

	/* get enough for IDRP_CEASE plus the udp header */
 
	buf_len = IDRP_CEASE_SIZE + sizeof(idrp_fixed_clnp_header);	
	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(buf_len);
	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock);
	len = IDRP_CEASE_SIZE + header_len;
	idrp_send_pdu(peer, pdu,pdu_proto,IDRP_CEASE_TYPE,len ,header_len,buf_len,FALSE);
}


/*
 * Send a keepalive PDU.  Ensure that this keepalive will be retransmitted soon if
 * the peer thinks he is flow blocked.
 */

void 
send_keepalive(peer)
idrpPeer *peer;
{
idrp_proto_header	*pdu_proto;
idrpPdu	*pdu;
int	header_len,buf_len;
int	len;

	buf_len = IDRP_KEEPALIVE_SIZE+ sizeof(idrp_fixed_udp_header);
	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(buf_len);
	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock);
	len = IDRP_KEEPALIVE_SIZE + header_len;
	idrp_send_pdu(peer, pdu,pdu_proto,IDRP_KEEPALIVE_TYPE,len ,header_len,buf_len,FALSE);

	if (peer->cred_avail_zero_seq_last == 0) {	/* If we aren't currently retransmitting keepalives */
		if (peer->rcved_credit_avail == 0) {	/* If the other guy is flow blocked */
			peer->cred_avail_zero_seq_last = peer->cred_avail_zero_seq;
			start_rexmit_timer(peer);
		}
	}
}


/* send a Rib Refresh PDU */
void
send_Refresh_pdu(peer,type)
idrpPeer	*peer;
int		type;
{
idrp_proto_header	*pdu_proto;
idrpPdu	*pdu;
int	len,header_len,buf_len;
idrp_rib_refresh_pdu	*p_rrib;

	switch (type)
		{
		case IDRP_RIB_REFRESH_REQ:
		case IDRP_RIB_REFRESH_START:
		case IDRP_RIB_REFRESH_END:
			break;

		default:
			trace_tf(idrp_trace_options, TR_NORMAL,0, (" Logic error, IDRP tried to send Rib Refresh with bad type = %d peer %s",  
			type,peer->name)); 
			return;
		}
				
	buf_len = IDRP_RIB_REFRESH_SIZE+sizeof(idrp_fixed_udp_header);
	pdu_proto = (idrp_proto_header *) idrp_local_mem_fit(buf_len);

	/* change pdu to jump over different header lengths points */


	header_len = proto_offset(&pdu,pdu_proto,peer->proto_sock);
	len = MinPDULength + header_len + IDRP_RIB_REFRESH_ADD_SIZE;

	/* set up for rib refresh pdu */

	p_rrib = (idrp_rib_refresh_pdu *) &pdu->pdu.refresh; 

	/* copy in 1 byte type */

	p_rrib->hdr.opcode = (u_char) type;
	p_rrib->rib_att[0].RIB_att_count =  RIB_ATT_DEFAULT_ONLY_COUNT;
	idrp_send_pdu(peer, pdu, pdu_proto,IDRP_RIB_REFRESH_TYPE, len, header_len,buf_len,FALSE);
}


#ifdef	IDRP_QOS

byte *
send_rib_atts(openribatt,peer)
idrp_RIB_att	*openribatt;
idrpPeer	*peer;	
{
int	rib_id;	
byte	*cp;
byte	*cp_begin = (byte *) openribatt;
u_int	count;	
 
	cp = cp_begin;

	/* peer supports just the default rib
	 * 
	 */

	if (peer->nrib_supp <= 1)
		{
		openribatt->RIB_att_count = 0;
		cp++;
		return(cp);
		}

	openribatt->RIB_att_count = peer->nrib_supp-1; 
	cp++;	 

	/* walk the peer list
	 * inserting the distinguishing attributes from
	 * the qos_rib structure for
	 * each peer supported by the peer 	  
	 */

	QOS_RIB_ONLY_LIST(rib_id)
		{
		/* if the peer supports this rib, copy
		 * in from the qos_rib the open pdu parts 
		 */  
		
		if (peer->rib_supp[rib_id])
			{
			/* copy over the open bytes from the qos rib
			 */

			bcopy(qos_rib[rib_id]->p_open_pdu,cp,qos_rib[rib_id]->open_pdu_len); 
			cp += qos_rib[rib_id]->open_pdu_len;  
			}
		} QOS_RIB_ONLY_LIST_END;	

	/*
	 * Count and updated pointer returned 
	 *   
	 */

	return(cp);
}

#endif /* IDRP_QOS */


	
	
#if 0 /* under construction */
/*
 * Trace a PDU
 */
idrp_trace_pdu(idrpPdu *pdu)
{
      char *i = pdu;
      int  j = 0;
      int  type = 0;
      int  length = 0;
      iso_net_addr theAddr;

      trace(TR_NORMAL, LOG_ERR, "IDRP PDU:");
      
      /* now log the fixed header */
      trace(TR_IDRP, LOG_ERR, " Identifier: %d", *i++);
      trace(TR_IDRP, LOG_ERR, " Length    : %d", (short)*i++);
      trace(TR_IDRP, LOG_ERR, " Type      : %s", idrpTypes[type = *i++]);
      trace(TR_IDRP, LOG_ERR, " Sequence  : %d", (int)*i++);
      trace(TR_IDRP, LOG_ERR, " Ack       : %d", (int)*i++);
      trace(TR_IDRP, LOG_ERR, " Cred Offer: %d", *i++);
      trace(TR_IDRP, LOG_ERR, " Cred Avail: %d", *i++);
      trace(TR_IDRP, LOG_ERR, " Validation: 0x%x%x%x%x", (int)*i++,(int)*i++,(
int)*i++,(int)*i++);

      /* now log the rest */
      switch(type) 
              {
              case IDRP_OPEN_TYPE:
                      {
                      trace(TR_IDRP, LOG_ERR, " Version   : %d", *i++);
                      trace(TR_IDRP, LOG_ERR, " Holdtime  : %d", (short)*i++);
                      trace(TR_IDRP, LOG_ERR, " MaxPDUSize: %d", (short)*i++);

                      theAddr.isoa_len = *i++;
                      theAddr.isoa_family = *i++;
                      bcopy(i, theAddr.isoa_genaddr, theAddr.isoa_len);
                      i += theAddr.isoa_len;

                      trace(TR_IDRP, LOG_ERR, " Source RDI: %A", (sockaddr_un)
theAddr);
                      UNDER CONSTRUCTION -- jgs

                      
      trace(
#endif /* under construction */
