/* 
 * $Id: idrp_rt_ext_info.c,v 1.4 1995/10/08 17:05:05 skh Exp $
 * Merit IDRP release 1.1 (gated 3.5.4).  Copyright (c) 1994 by Merit Network, Inc. 
 */

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

/* idrp_rt_ext_info.c
 * - routines that handle  IDRP EXT_INFO processing
 * things includes:
 *  	1) IS-IS <-> IDRP
 *	2) static <-> IDRP
 *	3) interface <-> IDRP
 * 	4) BGP <-> IDRP
 * 
 * In the future, the BGP code will be pulled into
 * a separate file.
 *
 * %% finding the general route 
 * 
 *
 *  
 * %% ext_info routines for the attribute record (gated related)
 *
 * find_ext_info_attr_rec - find the external info attribute record to
 * 
 * idrp_aspath_to_canon_rdpath - as_path structure to RDI canonical pathway
 *  idrp_bgp_within_RD(p_idrp_rt) - is this bgp route within this RD
 */



/* %% external information attribute record */

idrp_attribute_record *
find_ext_info_attr_rec(p_rth,p_rt,p_opts,rib_id)
rt_head			*p_rth;
rt_entry		*p_rt;
idrpRoute_options	*p_opts;
int			rib_id;
{
idrp_attribute_record *p_att;

	/* Types of EXT_INFO attribute are in the attribute list:
 	* - 1) AS path of zero - therefore is linked 
	*   to a local RD EXT_INFO path
	* - 2) non-zero AS path
	*       one comes from BGP-3 or BGP-4 with
	* 	 full AS path or  EGP with single AS path
	*       OSPF or other might have some internal stuff
	*	 but it should be a full aspath
	* 
	* If you are case one, you can pick up the EXT_INFO
	* pathway already created for you in the local_peer_init code
	*   
	* walk the list looking for an EXT_INFO IDRP attribute 
	* with local attributes matching the protocol information 
	* came from
	*	
 	*  Three types of transfer are currently planned
	* 	No path information 	
	*		For ISO - just  IS-IS, static or ES-IS
	*		For IP - RIP, IS-IS which do not pass ASs
	*	
	*-- aspath length = 0, p_rt->rt_aspath = 0 
	*  find_local_att_rec()
	*       
	*
	*	IDRP_ASPATH_EXT_INFO 
 	*		For ISO - IS-IS with EXT_INFO		 
	*		for IP - EGP or IS-IS or OSPF with ASs
	* -- these should have an AS path of 1 or greater
        *  find_att_as_path() 
	*
	*	IDRP_ASPATH_BGP_XFR -
	*		We are transferring between like protocols.
	*		So transfer lots of information.
	*		BGP4 and IDRP speakers care inter-changable for IP.	
	*		(or at least that is the hope for now)
	*  -- these should have full bgp4 AS_SET and SEQUENCES
	*  -- bgp_path_to_att(p_rt,p_opts)  
 	* 
	*/

#ifdef	IDRP_ASPATH	
	if ((p_rt->rt_aspath != NULL)  && 
		(p_rt->rt_aspath->path_len != 0))
		{
		if (p_rt->rt_gwp->gw_proto == RTPROTO_BGP)
			{
			/* translate bgp-4 to idrp 
			 */

			p_att =  bgp_path_to_att(p_rt);
			}
		else
			{
			p_att = find_att_as_path(p_rt,p_opts);
			}
		}
#endif

	find_local_att_rec(p_rt->rt_routers[0],p_opts,rib_id);

}						

#ifdef IDRP_ASPATH

find_att_as_path(p_rt,p_opts)
rt_entry	 *p_rt	
idrpRoute_option *p_opts;	
int		
{
as_path		*p_as_path;
sockaddr_un	*p_next_hop;
idrp_attribute	*p_att;
int		rib_id = p_opts->rib_id;

	p_as_path = p_rt->rt_aspath;
	p_next_hop = p_rt->rt_routers[0];
 
	/* non- bgp attribute translation
	 */

	ATTR_LIST(p_att,rib_id)
		{
		/* look for an attribute with the 
		 * same attribute options  for
		 * a local route created from as path
		 */

		if ((p_att->p_opts->p_local_opts == p_opts) &&      
			(p_att->p_as_path == p_as_path) &&		
			(sockaddrcmp(p_rt->rt_routers[0],p_att->next_hop)))
			{
			return(p_att);
			}
		} ATTR_LIST_END;

	p_att = create_local_att_as(p_rt->rt_routers[0],p_opts,p_as_path);

}

bgp_path_to_idrp(p_rt,p_opts)
rt_entry		*p_rt;
idrpRoute_options	*p_opts;
{
	idrp_aspath_bgp_xfr(p_can_head,p_att,peer,size,p_bgp));
}

idrp_attribute_record *
create_local_att_as(p_next_hop,p_opts,p_as_path)
sockaddr_un		*p_next_hop;
idrpRoute_options	*p_opts;
as_path			*p_as_path;
{
proto_t	idrp_local_proto;

	/* create the local attribute
	 */
	 
	create_local_att(p_next_hop,p_opt);
	idrp_local_proto = p_att->idrp_local_proto = p_rt->rt_gwp->gw_proto;
	p_att->as_path = p_rt->rt_aspath;
	p_att->p_opts->hopcount = p_rt->rt_aspath->path_len; 	

	/* 
	 * now redo the RDPATH created for the
	 * local peer 
	 */
		
	if (!idrp_aspath_to_canon_rdpath(p_rt->rt_aspath,p_att,&idrp_this_node,type,p_bgp))
		{
		/* the pathway contains an AS Loop - 
	 	 * IDRP checks to make sure it doesn't propogate
		 * anyone else's mistakes 
		 * or it's own confusion
		 * - So now we dump what we can to warn the user of AS path
		 * loopity - loop 
		 */

		trace (TR_IDRP,0,"idrp_aspath_to_canon_rdpath - find RDI loop pattern route %A",
			p_rth->rth_dest);
		trace (TR_IDRP,0,"dumping loop as RDI path for local node ");
		idrp_dump_rdi_path(p_att->rd_path,&idrp_this_node);	
		p_att = (idrp_attribute_record *) 0;
		return (p_att);
		}

	return(p_att);
}
 
/* create an idrp RDI canonical path from AS path
 * - if type to create is local EXT_INFO - just create
 *	attribute path  
 * - if type is BGP transfer -  
 * 
 */

int 
idrp_aspath_to_canon_rdpath(p_as,p_att,peer,type,p_bgp)
as_path			*p_as;
idrp_attribute_record	*p_att;
idrpPeer		*peer;
int			type;
u_char			*p_bgp;
{
idrp_canon_rdpath	*p_can;		/* canonical rd path */ 
idrp_canon_rdpath	*p_can_head = 0;	/* head of canon rdpath list */
idrp_canon_rdpath	*p_can_tail = 0;	/* tail of canon rdpath list */
register byte		*cp;		/* byte pointer for path attribute storage */ 
rdi			*p_rdi;		/* pointer to rdi */
int			size = 0;	/* size of rdi path in bytes */ 
int			hopcount = 0;	/* hopcount of rdi path */
int			i;		/* loop counter */
int			as;		/* integer value for as */  
struct	iso_net_addr	*p_rdi_base;	/* base value of as_rdi for translation */
		 
	/* pull as by AS off the as path and create RD path 
	 * in both the cannonical format and the normal format. 
	 * However, it concerns me there is no ability to pass on the 
 	 * origin of the path in IDRP.  Perhaps a IP specific user 
	 * related path
	 * - note by the time the code gets here
	 * - it has been check 
	 */
 

	cp = PATH_PTR(p_as);
        for (i = p_as->path_len/2; i > 0; i--)
		{
		/* grab memory for the cannonical rd path and clear structure */

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

		/* get as as integer so that you can call mapping routine for
	 	 * mapping to rdi prefix for as 
	 	 */
		
            	as = (int)(*cp++) << 8;
            	as += (int)(*cp++);
		p_rdi_base = idrp_as_rdi_map(as);

		/* RDI in hand (watch for this daring ISO bird)
		 * create the RDI from the AS in this pathway 
	  	 * - copy RDI and then AS bytes at the end 
	 	 */ 
		 
		bcopy(p_rdi_base->isoa_genaddr,p_rdi->rdi.isoa_genaddr,p_rdi->isoa_len);
            	bcopy(cp,&p_can->rdi.isoa_genaddr[(p_rdi->rdi.isoa_len)],IDRP_AS_SIZE);
		p_rdi->rdi.isoa_len = p_rdi_base->isoa_len + IDRP_AS_SIZE;
		p_can->p_rdi = idrp_insert_global_rdi(&p_rdi);
	
		/* keep running total on the size of the RDI path 
		 * that must go in the attribute pathway 
		 */
 
		size += p_can->rdi.isoa_len + IDRP_RDPATH_RDI_LENGTH_BYTE_LEN;   
		hopcount++;

		/* update my rd_path linked list 
		 * and fly every onward to the next as 
		 * as a bird into the sunset of ISO-IP wars 
		 * or the bird of paradise into trouble 
		 */
 
		if (!link_rd_canon(p_can,p_att)) 
			{
			/* found duplicate RDI using AS mapping *
			 * big time problem - return by leaving rd_path  
		 	 * and setting size to zero 
			 */

			return (FALSE);
			}
 
		}

	/* add the local as
	 * -- needs to be added
	 */	

	p_att->p_opts->hopcount = hopcount;

	fill_as_info(p_can,p_att,peer,size));
}

int
fill_as_info(p_can_list,p_att,peer,size)
idrp_canon_rdpath	*p_can_list;	/* rd path list */
idrp_attribute_record	*p_att;		/* attribute record */
idrpPeer		*peer;		/* idrp peer ext_info associate with */
int			size;		/* size of RDI structure */
{
idrp_canon_rdpath	*p_rdi;
u_char			*cp;
u_char			*cp_base; 
	
	/* now it's time to create the fake pdu
 	 * - get the size of the attribute rdpath to be created 
 	 * - put in route_id of -1 - so it is local
	 * - put in RD_PATH copied from the cannonical  
	 * - put in hopcount
	 * handle the Route_id - must be changed later to sequence 
         */		 


	p_att->attr_len  = size + IDRP_RD_HOP_COUNT_LENGTH;	
	p_att->p_attr_pdu = (u_char *) idrp_local_mem_fit(len); 

	/* copy in the RD_HOPCOUNT next */
	
	p_att->attrib[IDRP_ATTR_HOP_COUNT].present = TRUE;
	p_att->attrib[IDRP_ATTR_HOP_COUNT].flags = IDRP_ATTRFLG_HOP_COUNT;
	p_att->attrib[IDRP_ATTR_HOP_COUNT].data = cp; 
	p_att->attrib[IDRP_ATTR_HOP_COUNT].length = IDRP_RD_HOP_COUNT_LENGTH;
	*cp = p_att->hopcount;
	cp++;
		
	/* now for the RDI pathway */

	cp_base = cp;
	p_att->attrib[IDRP_ATTR_RDPATH].present = TRUE;
	p_att->attrib[IDRP_ATTR_RDPATH].flags = IDRP_ATTRFLG_RDPATH; 
	p_att->attrib[IDRP_ATTR_HOP_COUNT].data = cp_base; 


	/* here we need 
	 * - sequence of RDIs = RDI path 
	 * seq, # rdi 
	 */

	*cp = IDRP_SEQ;
  	
	for (p_rdi = p_can_list; p_rdi; p_rdi->p_next)
		{
		*cp = p_rdi->rdi.isoa_len;
		cp++;
		bcopy(&p_rdi->rdi.isoa_genaddr,cp,p_rdi->rdi.isoa_len);
		cp = cp + p_rdi->rdi.isoa_len;
		}		
	p_att->attrib[IDRP_ATTR_HOP_COUNT].length = cp - cpbase;

	return (TRUE);
}

int
idrp_bgp_within_RD(p_idrp_rt)
idrpRoute	*p_idrp_rt;
{
	/* if AS path exists  and the hop count
	 * greater than one - we have more than
	 * this RD as part of our path 
	 */
	if ((p_idrp_rt->p_attr->as_path != (as_path *) NULL) &&
		(p_idrp_rt->p_attr->hopcount > 1))
		{
		return (FALSE);
		}
	return (TRUE);
}	

idrp_aspath_bgp_xfr(p_can_list,p_att,peer,size,p_bgp)
idrp_canon_rdpath	*p_can_list;
idrp_attribute_record	*p_att;
idrpPeer		*peer;
int			size;
u_char			*p_bgp;
{
idrp_canon_rdpath	*p_rdi;
u_char			*cp;

	/* no code yet */

	trace_tf (idrp_trace_options, TR_NORMAL,0, ("idrp_aspath_bgp_xfr called and no support yet -  Oh no momma bunny"));
	return (FALSE);   

}	

/* AS to RDI mapping
 * 	configure table needed.  Right now we code in 
 * 	definition in the idrp for ip specification
 *
 */

struct iso_net_addr *idrp_as_rdi_map(as)
int	as;
{
	/* right now just use the global RDI defined in IDRP for IP */
	/* in the future configure a table */
	
	return (&rt_as_rdi);
}	

find_local_att_as(p_next_hop,p_opts,p_as_path)
sockaddr_un		*p_next_hop;
idrpRoute_options	*p_opts;
as_path			*p_as_path;
{
}
	
#endif 
