/* 
 * $Id: idrp_init_ribs.c,v 1.2 1995/10/31 17:13:30 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"


#ifdef	IDRP_QOS
 
/* idrp_init_ribs.c
 *  Susan Hares Merit/NSFNET. 
 *
 * idrp_qos_rib_reconfig() - copy set of qos_ribs, attr_list, oatt_list  to last_qos_ribs, 
 *			     last_attr_list, last_oatt_list
 *			(allows parsing to work the same and ordering to be built on rib_id)
 *     
 * idrp_qos_rib_cleanup()
 * 		all ribs not reloaded - causes a checkon the peer statement
 * 
 * 
 * idrp_find_rib(p_rib)
 * compare_ribs(p_rib1, p_rib2) 
 * idrp_init_ribs
 * idrp_init_qos_attr_list(rib_id)
 * idrp_init_qos_oatt_list(rib_id)
 * idrp_del_rib_drop_peers(rib_id) 	
	
 * 	
 */

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


void
idrp_qos_rib_reconfig()
{
idrpPeer	*peer;
int	rib_id;

/* copy the idrp_qos_ribs to the
 * - leave the default rib alone
 * backup pointers 
 */

	last_rib_cnt = rib_cnt;
	for (rib_id = 1; rib_id < last_rib_cnt; rib_id++ )
		{
		/* take a snap shot of the current arrays
		 * of qos_rib info (qos_rib)
		 *    attr_list (attr_list)
		 *   oatt_list (idrp_att_list)	
		 */

		last_qos_rib[rib_id] = qos_rib[rib_id];
		last_attr_list[rib_id] = idrp_attr_list[rib_id];	
		idrp_attr_list[rib_id] = (idrp_attribute_record *) NULL;
		if (idrp_oatt_list[rib_id])
			{
			last_oatt_list[rib_id] = idrp_oatt_list[rib_id];
			idrp_oatt_list[rib_id] = (idrp_att_list *) NULL;
			}
		} QOS_RIB_LIST_END

	IDRP_LIST(peer,idrp_peers)
		{
		/* copy over ids of ribs supported
		 * 
	 	 */	

		PEER_RIB_LIST(rib_id,peer->nrib_supp)
			{
			peer->last_rib_supp[rib_id] = peer->rib_supp[rib_id];
			} PEER_RIB_LIST_END
		peer->nrib_last_supp = peer->nrib_supp;
		} IDRP_LIST_END
}



void
idrp_qos_rib_cleanup()
{
int	rib_id;

/* All the parsing has been done
 *   
 *  for any rib_id that was on the old list and not on new
 *
 *    1) check peer to see if we need to take this
 * 		peer down due to rib in open that
 * 		we cannot support at this time	
 * 	   
 *    2) release last_attr_list
 *     	 - release associated routes
 * 	 - cleanup entry in last_attr_list array
 * 
 *    3) release last_oatt_list
 * 	  - release any structures
 * 	  - clean up entry in last_oatt_list array
 *
 *    4) log the changes	  	 	  
 */

    LAST_RIB_LIST(rib_id)
                {
		/* rib was shifted to the new
		 */
	
		if (last_qos_rib[rib_id] == (idrp_qos_rib *) NULL)
			continue;

		/* rib changed -> so drop any peers that
		 * are associated with this rib
		 */    

                 idrp_del_rib_drop_peers(rib_id);
	
		/* 2) free attribute list for this rib
		 * 
 		 *    3) release last_oatt_list
		 * 	  - release any structures
		 * 	  - clean up entry in last_oatt_list array
		 *
		 *    4) log the changes	  	 	  
		 */

		} LAST_RIB_LIST_END;	
		 
}



idrp_qos_rib *	
idrp_find_rib(p_new_rib)
idrp_qos_rib	*p_new_rib;
{
idrp_qos_rib	*p_rib;
int		rib_id;

	
	LAST_RIB_LIST(rib_id)
		{
		/* here put the comparison of the rib */

		p_rib = last_qos_rib[rib_id];
		if (p_rib == NULL)
			continue;
	
		if (compare_ribs(p_rib,p_new_rib))
			{
			/* set the old rib in the new list
			 * - clear out the last rib list
			 */

			qos_rib[rib_cnt] = p_rib;
			qos_rib[rib_cnt]->status = IDRP_STATUS_RIB_RECONFIGURE;  
			last_qos_rib[rib_id] = NULL;
			idrp_attr_list[rib_cnt] = last_attr_list[rib_id];
			if (last_oatt_list[rib_id])
				{
				idrp_oatt_list[rib_id] = last_oatt_list[rib_id];
				last_oatt_list[rib_id] = NULL; 
				}
			else
				{
				idrp_oatt_list[rib_id] = (idrp_att_list *) idrp_local_mem_fit(sizeof(idrp_att_list));	
				}		

			last_attr_list[rib_id] = (idrp_attribute_record *) 0;
			return(p_rib);
			}
		} LAST_RIB_LIST_END;

	/* no match for peer */


	return((idrp_qos_rib *)NULL);
}

/* compare two ribs to see if the
 * rib values are the same
 */

int
compare_ribs(p_rib1,p_rib2)
idrp_qos_rib	*p_rib1;
idrp_qos_rib	*p_rib2;
{

	/* check for
	 * 1) number of attributes
	 * 2) idrp_mask for attributes
	 * 3) values for ribs - (later)    
	 */

	if (p_rib1->n_att != p_rib2->n_att)
		{
		return(FALSE);
		}
  
	if (p_rib1->att.idrp_mask != p_rib2->att.idrp_mask)
		{
		return(FALSE);
		}


	/* values for rib attributes */ 

	if (p_rib1->att.idrp_mask & IDRP_ATTBT_TRANSIT_DELAY)
		{
		/* test delay values */

		if (p_rib1->att.delay.value != p_rib2->att.delay.value)
			{
			return(FALSE);
			}
		}

	if (p_rib1->att.idrp_mask & IDRP_ATTBT_RESIDUAL_ERROR)
		{
		/* test error values */

		if (p_rib1->att.error.value != p_rib2->att.error.value)
			{
			return(FALSE);
			}

		}	

	if (p_rib1->att.idrp_mask & IDRP_ATTBT_EXPENSE)
		{
		/* test expense values */

		if (p_rib1->att.expense.value != p_rib2->att.expense.value)
			{
			return(FALSE);
			}

		}

	if (p_rib1->att.idrp_mask & IDRP_ATTBT_PRIORITY)
		{
		/* test priority values */

		if (p_rib1->att.priority.value != p_rib2->att.priority.value)
			{
			return(FALSE);
			}

		}

	if (p_rib1->att.idrp_mask & IDRP_ATTBT_SECURITY)
		{
		/* test security values */

		if ((p_rib1->att.sec_type.value != p_rib2->att.sec_type.value) && 
			(p_rib1->att.sec_value.value != p_rib2->att.sec_value.value))
			{
			return(FALSE);
			}
		}
	
	return(TRUE);
}



/* initialize ribs that valid and ready 
 *
 */   

void
idrp_init_ribs()
{
int		rib_id;		/* rib id */	
idrp_qos_rib	*p_rib;		/* pointer to rib */


	/* walk the rib list
	 *  1) initialize pointers for idrp_attr_list for rib
	 *    	NOTE: QOS and osilocal not allowed
	 *  2) intialize pointers for  idrp_oatt_list for rib     
	 *     	
	 */
 
	for (rib_id=1; rib_id < rib_cnt; rib_id++)
		{
		p_rib = qos_rib[rib_id];
		if (p_rib &&  p_rib->status == IDRP_STATUS_RIB_NEW)
			{
			/* just assign the attribute list space */
			idrp_attr_list[rib_id] = (idrp_attribute_record *)  NULL;
			idrp_oatt_list[rib_id] = (idrp_att_list *)idrp_local_mem_fit(sizeof(idrp_att_list));  
			}

		} QOS_RIB_LIST_END

}



void
idrp_del_rib_drop_peers(rib_id)
int	rib_id;
{
idrpPeer	*peer;

	IDRP_LIST(peer,idrp_peers)
		{
		if (peer->last_rib_supp[rib_id])
			{
			idrp_stop_event(peer);
			}
		} IDRP_LIST_END

}



int
idrp_check_qos_vals(p_rib)
idrp_qos_rib	*p_rib;
{
u_int	mask = p_rib->att.idrp_mask;
u_int	tmp_mask;

	/* check bit mask for valid combination
	 * (section 7.11.2 in IDRP specification)   
	 *
	 * 1) empty set is valid - (but it will not land here)
	 * 2) security path plus only one of 
	 * 	RESIDUAL ERROR, TRANSIT_DELAY, EXPENSE, LOCALLY_DEFINED_QOS
	 * 
	 * 3) plus priority    
 	 */
	
	tmp_mask = mask & (IDRP_ATTBT_TRANSIT_DELAY | IDRP_ATTBT_RESIDUAL_ERROR | IDRP_ATTBT_EXPENSE | IDRP_ATTBT_LOCALLY_DEF_QOS);

	if (tmp_mask)
		{
		/* we have one of the bits - make sure it is only one
	 	 *  
		 */

		if ((tmp_mask == IDRP_ATTBT_TRANSIT_DELAY ) || (tmp_mask == IDRP_ATTBT_RESIDUAL_ERROR)
		  	|| (tmp_mask = IDRP_ATTBT_EXPENSE) ||  (tmp_mask == IDRP_ATTBT_LOCALLY_DEF_QOS))
			{
			p_rib->n_att = 1;  	

			}
		else
			{
			/* more than one 
			 * - return error and dump this QOS rib
			 */
			return (FALSE);
			}	
			
		}
	else
		{
		/* set to zero for the delay, error, expense, local_def_qos 
		 *  
		 */

		p_rib->n_att = 0;
		}

	if (mask & IDRP_ATTBT_PRIORITY)
		{
		p_rib->n_att++;
		}
	if (mask & IDRP_ATTBT_SECURITY)
		{
		p_rib->n_att++;
		}

	/* gated parser only allows one security and
	 * one priority to be input at this time
	 * so we don't have to check for equivalent distinguishing attributes
	 */   
	

	/* check ranges 
	 * 
	 */ 

	if ((p_rib->att.idrp_mask & IDRP_ATTBT_TRANSIT_DELAY != 0 ) && (p_rib->att.delay.value > IDRP_DELAY_VALUE_MAX))
		{
		return(FALSE);
		} 

	if ((p_rib->att.idrp_mask & IDRP_ATTBT_RESIDUAL_ERROR ) && (p_rib->att.error.value > IDRP_ERROR_VALUE_MAX))
		{
		return(FALSE);
		} 
       
	if ((p_rib->att.idrp_mask & IDRP_ATTBT_EXPENSE ) && (p_rib->att.expense.value > IDRP_EXPENSE_VALUE_MAX))
		{
		return(FALSE);
		} 
	
	if ((p_rib->att.idrp_mask & IDRP_ATTBT_PRIORITY ) && (p_rib->att.delay.value > IDRP_PRIORITY_VALUE_MAX))
		{
		return(FALSE);
		}
	
	if ((p_rib->att.idrp_mask & IDRP_ATTBT_SECURITY) && ( p_rib->att.sec_value.value > FAA_SECURITY_VALUE_MAX))
		{
		return(FALSE);
		}

	return(TRUE);
	
}

void
idrp_fill_qospdu(p_rib)
idrp_qos_rib	*p_rib;
{
u_int	mask = p_rib->att.idrp_mask;
u_int	update_len;
u_int	open_len = 1;
u_int	open_atts = 0;
idrp_RIB_att	*p_rib_atts;
u_char		*p_open;
u_char		*p_update;
u_long		long_hold;
u_int16		hold;

	/* allocate a fix maximum space for the QOS
	 */
 	
	p_update = (byte *) p_rib->p_update_pdu = (byte *) idrp_local_mem_fit((IDRP_UPDATE_QOS_LENGTH));
	p_open = (byte *) p_rib->p_open_pdu = (byte *) idrp_local_mem_fit(IDRP_OPEN_QOS_MAX_LENGTH);

	/* open byte sequence has the following format for each rib 
	 * that gets copied into the RIB-AttsSet portion of the
	 * the OPEN pdu 
	 * 
	 * Number of Distinguishing Atts in N Rib-att 
	 * First attribute, Rib-att N 
	 * 2nd attribute , Rib-Att N 
	 * 3rd attribute , Rib-Att N 
	 * ... 
	 * Last attribute , Rib-Att N 
	 */

	*p_open = p_rib->n_att; 
	p_open++;

	/* set-up the attribute length by the size of
	 * flags, type and length byte in the pdu
	 * 
	 * then.. per attribute - add in the actual length 
	 */
 
	update_len = sizeof(idrp_path_attr_hdr)* p_rib->n_att; 	

	/* for each qos specified
	 * 1) up the length of the update and open pdu to include the QOS 
	 * 2) copy in the bytes into update and open pdu   
	 * 3) set-up the pointers in the rib into the byte sequence for the pdu 
	 * -- note that only one of  transit_delay, expense, residual_error can be specified
	 *    therefore: the if-elseif-elseif clause
	 */
 

	if (mask & IDRP_ATTBT_TRANSIT_DELAY)
		{
		/* Delay  - 2 bytes in update */

		/* copy over the attribute type to
		 * open pdu
		 */
		*p_open = IDRP_ATTR_TRANSIT_DELAY;
		p_open++;
		open_len++;


		/* copy over the UPDATE
		 */

		p_rib->p_delay = p_update;  
		hold = htons(p_rib->att.delay.value);
		bcopy(&hold,p_update,IDRP_TRANSIT_DELAY_LENGTH);
		p_update += IDRP_TRANSIT_DELAY_LENGTH;
		update_len +=  IDRP_TRANSIT_DELAY_LENGTH;
		}
	else if (mask & IDRP_ATTBT_RESIDUAL_ERROR)
		{
		/* ERROR - 4 bytes in update */

		/* copy over the attribute type to
		 * open pdu
		 */
		*p_open = IDRP_ATTR_RESIDUAL_ERROR;
		p_open++;
		open_len++;

		/* copy over the UPDATE bytes
		 */
		p_rib->p_error = p_update;  
		long_hold = htonl(p_rib->att.delay.value);
		bcopy(&long_hold,p_update,IDRP_RESIDUAL_ERROR_LENGTH);
		p_update += IDRP_RESIDUAL_ERROR_LENGTH;
		update_len +=  IDRP_RESIDUAL_ERROR_LENGTH;
		}
	else if (mask & IDRP_ATTBT_EXPENSE)
		{
		/* EXPENSE - 2 bytes in update */

		/* copy over the attribute type to
		 * open pdu
		 */
		*p_open = IDRP_ATTR_EXPENSE;
		p_open++;
		open_len++;

		/* copy over the UPDATE bytes
		 */
		p_rib->p_expense = p_update;  
		hold = htons(p_rib->att.delay.value);
		bcopy(&hold,p_update,IDRP_EXPENSE_LENGTH);
		p_update += IDRP_EXPENSE_LENGTH;
		update_len +=  IDRP_EXPENSE_LENGTH;
		}

	if (mask & IDRP_ATTBT_PRIORITY)
		{
		/* PRIORITY - 2 bytes in update */

		/* copy over the attribute type to
		 * open pdu
		 */
		*p_open = IDRP_ATTR_PRIORITY;
		p_open++;
		open_len++;

		/* copy over the UPDATE bytes
		 */
		p_rib->p_priority = p_update;  
		hold = htons(p_rib->att.delay.value);
		bcopy(&hold,p_update,IDRP_PRIORITY_LENGTH);
		p_update += IDRP_PRIORITY_LENGTH;
		update_len +=  IDRP_PRIORITY_LENGTH;
		} 

	if (mask & IDRP_ATTBT_SECURITY)
		{
		/* only a fixed length FAA security
		 * field is supported for now
		 */
 
		*p_open = IDRP_ATTR_SECURITY;
		p_open++;
		*p_open = FAA_SEC_ID_LENGTH;
		p_open++;
		*p_open = p_rib->att.sec_type.value;
		open_len += FAA_OPEN_LENGTH;   	

		/* now set-up the UPDATE pdus for FAA
		 */ 
		p_rib->p_security = p_update;
		*p_update = FAA_SEC_ID_LENGTH;
		p_update++;
		*p_update = p_rib->att.sec_type.value;
		p_update++;
		*p_update = FAA_INFOLENGTH; 
		*p_update++;
		long_hold = htonl(p_rib->att.sec_value.value); 
		bcopy(&long_hold,p_update,FAA_INFOLENGTH);	
		update_len +=  FAA_SECURITY_LENGTH ;
		} 

	p_rib->update_pdu_len = update_len;
	p_rib->open_pdu_len = open_len;
		
}	

/* walk the QOS supported list trying to find
 * the parse id 
 */   

int
find_rib_parseid(parseid)
int	parseid;
{
int	rib_id;
idrp_qos_rib	*p_rib;


	QOS_RIB_LIST(rib_id)
		{
		p_rib = qos_rib[rib_id];
		if (p_rib->parse_id == parseid)
			{
			return(rib_id);
			}
		} QOS_RIB_LIST_END

	/* zero is the non-rib id since it
	 * is the default rib id
	 */

	return(0);
	
}	

void
idrp_supp_all_ribs(peer)
idrpPeer	*peer;
{
int		rib_id;

	peer->nrib_supp = rib_cnt;
	QOS_RIB_LIST(rib_id)	
		{
		peer->rib_supp[rib_id] = qos_rib[rib_id]->parse_id;
		} QOS_RIB_LIST_END;
}	


void
idrp_rib_cleanup()
{
int		rib_id;
idrp_qos_rib	*p_rib;

/* idrp protocol is gone away
 * peers are gone away
 *  
 */

	QOS_RIB_LIST(rib_id)
		{
		p_rib = qos_rib[rib_id];
		if (p_rib == NULL)
			continue;
			
		idrp_free_rib(p_rib);	
		idrp_free_attr_list(idrp_attr_list[rib_id]);
		idrp_free_oatt_list(idrp_oatt_list[rib_id]);

		} QOS_RIB_LIST_END;
	


}

void
idrp_init_def_rib()   	
{
u_int att_mask;

	/* set up the default qos_rib to
	 * 1) have default id of zero
	 * 2) parse_id of zero
	 * 3) capacity value of the local node
	 */
		
	qos_rib[RIB_ID_DEFAULT]->rib_id = RIB_ID_DEFAULT;
	qos_rib[RIB_ID_DEFAULT]->parse_id = 0;
	if (idrp_this_node.Capacity)
		{ 	
		att_mask |= IDRP_ATTBT_CAPACITY;
		}

	qos_rib[RIB_ID_DEFAULT]->att.idrp_mask = att_mask;
	qos_rib[RIB_ID_DEFAULT]->status = IDRP_STATUS_RIB_NEW;

	if (idrp_oatt_list[0] == NULL)
		{
		idrp_oatt_list[0] = (idrp_att_list *)idrp_local_mem_fit(sizeof(idrp_att_list));
		}

}


int
compare_peer_ribs(peer1,peer2)
idrpPeer	*peer1;
idrpPeer	*peer2;
{
int i;

	/* if the number of ribs support is not equal
	 * quit
	 */

	if (peer1->nrib_supp != peer2->nrib_supp)
			return(FALSE);

	for (i = 0; i < peer1->nrib_supp; i++)
		{
		if (peer1->rib_supp[i] != peer2->rib_supp[i])
			return(FALSE);
		}		

	return(TRUE);

}
void
idrp_free_rib(p_rib)
idrp_qos_rib    *p_rib;
{
int     rib_id;

         /* information
         *
         * 1) kernel string
         * 2) update pdu string
         * 3) open pdu
         */

        idrp_mem_fit_free((void **)&p_rib->p_qos_str,p_rib->kern_len);
        idrp_mem_fit_free((void **)&p_rib->p_update_pdu,IDRP_UPDATE_QOS_LENGTH);
        idrp_mem_fit_free((void **)&p_rib->p_open_pdu,IDRP_OPEN_QOS_MAX_LENGTH);

}


#endif /* IDRP_QOS */
