ftp.nice.ch/pub/next/unix/network/system/gated.2.1pl2.NI.bs.tar.gz#/gated-2.1/src/bgp_rt.c

This is bgp_rt.c in view mode; [Download] [Up]

/*
 *  $Header: /disk/d/src/devel/gated/dist/src/RCS/bgp_rt.c,v 2.1 92/02/24 14:12:26 jch Exp $
 */

/*%Copyright%*/
/************************************************************************
*									*
*	GateD, Release 2						*
*									*
*	Copyright (c) 1990,1991,1992 by Cornell University		*
*	    All rights reserved.					*
*									*
*	THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY		*
*	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT		*
*	LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY		*
*	AND FITNESS FOR A PARTICULAR PURPOSE.				*
*									*
*	Royalty-free licenses to redistribute GateD Release		*
*	2 in whole or in part may be obtained by writing to:		*
*									*
*	    GateDaemon Project						*
*	    Information Technologies/Network Resources			*
*	    143 Caldwell Hall						*
*	    Cornell University						*
*	    Ithaca, NY 14853-2602					*
*									*
*	GateD is based on Kirton's EGP, UC Berkeley's routing		*
*	daemon	 (routed), and DCN's HELLO routing Protocol.		*
*	Development of Release 2 has been supported by the		*
*	National Science Foundation.					*
*									*
*	Please forward bug fixes, enhancements and questions to the	*
*	gated mailing list: gated-people@gated.cornell.edu.		*
*									*
*	Authors:							*
*									*
*		Jeffrey C Honig <jch@gated.cornell.edu>			*
*		Scott W Brim <swb@gated.cornell.edu>			*
*									*
*************************************************************************
*									*
*      Portions of this software may fall under the following		*
*      copyrights:							*
*									*
*	Copyright (c) 1988 Regents of the University of California.	*
*	All rights reserved.						*
*									*
*	Redistribution and use in source and binary forms are		*
*	permitted provided that the above copyright notice and		*
*	this paragraph are duplicated in all such forms and that	*
*	any documentation, advertising materials, and other		*
*	materials related to such distribution and use			*
*	acknowledge that the software was developed by the		*
*	University of California, Berkeley.  The name of the		*
*	University may not be used to endorse or promote		*
*	products derived from this software without specific		*
*	prior written permission.  THIS SOFTWARE IS PROVIDED		*
*	``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,	*
*	INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF	*
*	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.		*
*									*
************************************************************************/


#include "include.h"
#include "bgp.h"

#ifdef	PROTO_BGP

static rt_data as_base =
{&as_base, &as_base};

static bits asorigin_bits[] =
{
    {0, NULL},
    {ASO_IGP, "IGP"},
    {ASO_EGP, "EGP"},
    {ASO_INCOMPLETE, "Incomplete"},
    {0}
};

/*
 *	Dump the AS paths
 */
void
bgp_as_dump(fd)
FILE *fd;
{
    int i;
    rt_data *rtd;
    as_path *asp;

    /* Dump AS path information */
    (void) fprintf(fd,
		   "\n\nAS Paths:\n");

    RTDATA_LIST(rtd, &as_base) {
	asp = (as_path *) rtd->rtd_data;
	(void) fprintf(fd,
		       "\tRefcount %4d  Length %3d:",
		       rtd->rtd_refcount,
		       asp->as_count);
	for (i = 0; i < asp->as_count; i++) {
	    (void) fprintf(fd,
			   " %5u",
			   asp->as_number[i]);
	}
	(void) fprintf(fd,
		       " %5s\n",
		       trace_state(asorigin_bits, asp->as_origin));

    } RTDATA_LIST_END(rtd, &as_base);
    (void) fprintf(fd, "\n");

}


/*
 *	Dump an AS path
 */
static void
bgp_rt_dump(fd, rt)
FILE *fd;
rt_entry *rt;
{
    int i;
    as_path *asp;

    if (rt->rt_data && rt->rt_data->rtd_data) {
	asp = (as_path *) rt->rt_data->rtd_data;

	if (asp) {
	    (void) fprintf(fd, "\t\t\tPath:");
	    for (i = 0; i < asp->as_count; i++) {
		(void) fprintf(fd,
			       " %u",
			       asp->as_number[i]);
	    }
	    (void) fprintf(fd, " %s\n",
			   trace_state(asorigin_bits, asp->as_origin));
	}
    }
}


/*
 *	Allocate an AS entry given it's size
 */
rt_data *
bgp_as_alloc(pairs)
int pairs;
{
    rt_data *rtd;

    rtd = rtd_alloc(sizeof(as_path) + (sizeof(as_t) * (pairs - 1)));
    rtd->rtd_dump = bgp_rt_dump;

    return (rtd);
}


/*
 *	Build a one or two entry AS path, locate preexisting path and increment reference count
 */
as_path *
#ifdef	USE_PROTOTYPES
bgp_as_build(int count, int origin, as_t AS)
#else				/* USE_PROTOTYPES */
bgp_as_build(count, origin, AS)
int count;
int origin;
as_t AS;

#endif				/* USE_PROTOTYPES */
{
    static as_path asp;

    if (count) {
	asp.as_count = count;
	asp.as_number[0] = AS;
    }
    asp.as_origin = origin;

    return (&asp);
}


void
bgp_recv_Update(bnp, PDU, length)
bgpPeer *bnp;
bgpPdu *PDU;
int length;
{
    u_char *cp, *lp;
    int i, j;
    u_char asCount;
    u_char asDirection;
    as_t asNumber;
    u_short netCount;
    u_short netMetric;
    int error = 0;
    pref_t preference;
    struct sockaddr_in gateway;
    struct in_addr bgp_gateway;
    struct sockaddr_in netNumber;
    metric_t metric = 0;
    time_t rt_maxage = 0;		/* BGP routes don't time out */
    rt_entry *rt;
    rt_data *rtd = (rt_data *) 0;
    as_path *asp;

    sockclear_in(&gateway);
    sockclear_in(&netNumber);

    cp = (u_char *) PDU;
    lp = cp + length;
    cp += sizeof(pduHeader);

    rt_open(bnp->bgp_task);

    /* Parse the gateway */
    if ((cp + sizeof(bgp_gateway)) >= lp) {
	/* This should probably be an invalid length error */
	error = BGPUPDERR_GATEWAY;
	goto Error;
    }
    PickUp(cp, bgp_gateway);
    if (bnp->bgp_options & BGPO_GATEWAY) {
	gateway = bnp->bgp_gateway;	/* struct copy */
    } else {
	gateway.sin_addr = bgp_gateway;	/* struct copy */
    }

    /* Parse the AS path */
    PickUp(cp, asCount);
    if (asCount == 0) {
	error = BGPUPDERR_ASCOUNT;
	goto Error;
    }
    if ((cp + asCount * (sizeof(asDirection) + sizeof(asNumber))) >= lp) {
	error = BGPUPDERR_ASCOUNT;
	goto Error;
    }
    rtd = bgp_as_alloc(i = asCount);
    asp = (as_path *) rtd->rtd_data;
    asp->as_count = asCount;
    for (i = 0; i < asCount; i++) {
	/* Pickup direction and As Number */
	PickUp(cp, asDirection);
	PickUp(cp, asNumber);
	asNumber = ntohs(asNumber);

	/* Make sure direction is valid */
	if (!asDirection || (asDirection > asDirMax)) {
	    error = BGPUPDERR_DIRECTION;
	    goto Error;
	}
	/* Make sure EGP and Incompelete types only occur at the end of the list */
	if (((asDirection == asDirEgp) || (asDirection == asDirIncomplete)) && (i != asCount - 1)) {
	    error = BGPUPDERR_ORDER;
	    goto Error;
	}
	/* Check for loops */
	for (j = 0; j < i; j++) {
	    if ((asNumber == asp->as_number[j]) || (asNumber == bnp->bgp_asout)) {
		error = BGPUPDERR_LOOP;
		goto Error;
	    }
	}

	/* Looks OK, store this AS in the path structure */
	asp->as_number[i] = asNumber;
    }
    if (asDirection == asDirEgp) {
	asp->as_origin = ASO_EGP;
    } else if (asDirection == asDirIncomplete) {
	asp->as_origin = ASO_INCOMPLETE;
    } else {
	asp->as_origin = ASO_IGP;
    }

    /* Two phase rule violation check goes here */

    /* Lookup this path, if duplicate return pointer to old path */
    rtd = rtd_insert(rtd, &as_base);
    asp = (as_path *) rtd->rtd_data;

    /* Parse the networks */
    PickUp(cp, netCount);
    netCount = ntohs(netCount);
    if (netCount == 0) {
	error = BGPUPDERR_NETCOUNT;
	goto Error;
    }
    if ((cp + netCount * (sizeof(netMetric) + sizeof(netNumber.sin_addr))) != lp) {
	error = BGPUPDERR_NETCOUNT;
	goto Error;
    }
    for (i = 0; i < netCount; i++) {
	PickUp(cp, netNumber.sin_addr);
	PickUp(cp, netMetric);
	netMetric = ntohs(netMetric);
	metric = netMetric;

	/* Set default value of preference */
	preference = bnp->bgp_preference;

	/* Check if this network is valid from this peer */
	if (!is_valid_in((sockaddr_un *) & netNumber, bnp->bgp_accept, (adv_entry *) 0, (adv_entry *) 0, &preference)) {
	    IF_BGPUPD {
		trace(TR_BGP, 0, "bgp_recv_Update: net %-15A not valid from AS %5d",
		      &netNumber,
		      bnp->bgp_asin);
	    }
	    continue;
	}
	rt = rt_locate_gw(RTS_NETROUTE,
			  (sockaddr_un *) & netNumber,
			  bnp->bgp_task->task_rtproto,
			  &bnp->bgp_gw);
	if (!rt) {
	    /* New route */
	    if (netMetric == bgpMetricInfinity) {
		continue;
	    }
	    rt = rt_add((sockaddr_un *) & netNumber,
			(sockaddr_un *) 0,
			(sockaddr_un *) & gateway,
			&bnp->bgp_gw,
			metric,
			(flag_t) (RTS_NOAGE |
			       ((bnp->bgp_linktype == openLinkInternal) ?
				(RTS_INTERIOR | RTS_NOTINSTALL | RTS_NOADVISE) : (RTS_EXTERIOR))),
			bnp->bgp_task->task_rtproto,
			bnp->bgp_asin,
			rt_maxage,
			preference);
	    if (rt) {
		rt->rt_data = rtd;
		rtd->rtd_refcount++;
	    } else {
		error = BGPUPDERR_NETWORK;
		goto Error;
	    }
	} else {
	    /* Existing route */
	    if (netMetric == bgpMetricInfinity) {
		/* Route is to be deleted */
		(void) rt_delete(rt);
	    } else {
		/* Change metric/gateway */
		if (rt_change(rt,
			      (sockaddr_un *) & gateway,
			      metric,
			      rt_maxage,
			      preference)) {
		    rtd_unlink(rt->rt_data);
		    rt->rt_data = rtd;
		    rtd->rtd_refcount++;
		}
	    }
	}
    }

  Error:
    /* Send error and close connection if necessary */
    if (error) {
	bgp_send_NotifyUpdate(bnp, error, PDU, length);
    }
    /* Free the as_path pointer if necessary */
    if (rtd) {
	rtd_unlink(rtd);
    }
    i = netCount;
    if (!(bnp->bgp_options & BGPO_NOGENDEFAULT) && !(bnp->bgp_flags & BGPF_GENDEFAULT) && i) {
	if (rt_default_add()) {
	    bnp->bgp_flags |= BGPF_GENDEFAULT;
	}
    }
    if (rt_close(bnp->bgp_task, &bnp->bgp_gw, i)) {
	task_flash(bnp->bgp_task);
    }
    return;
}


struct update_packet {
    struct update_packet *up_next;	/* Pointer to next packet on chain */
    struct in_addr up_gateway;		/* Gateway */
    as_path *up_asp;			/* Pointer to AS path */
    u_char *up_ncp;			/* Pointer to netCount field */
    u_char *up_np;			/* Network/Metric fill field */
    u_short up_net_count;		/* Number of networks in this update */
};

void
bgp_send_update_packet(bnp, packet)
bgpPeer *bnp;
struct update_packet *packet;
{
    int length;
    u_short uns_short;
    bgpPdu *PDU;
    u_char *cp;

    cp = (u_char *) packet + sizeof(struct update_packet);	/* Point to PDU */
    PDU = (bgpPdu *) cp;
    length = packet->up_np - cp - sizeof(pduHeader);	/* Calculate length */

    /* Set packet type */
    PDU->header.type = bgpPduUpdate;

    /* Add Network Count field to packet */
    uns_short = htons(packet->up_net_count);
    packet->up_net_count = 0;
    packet->up_np = packet->up_ncp;
    PutDown(packet->up_np, uns_short);

    bgp_send(bnp, PDU, length);

}


void
bgp_send_update(bnp, flash_flag)
bgpPeer *bnp;
int flash_flag;				/* Flash update */
{
    int i;
    u_short uns_short;
    u_short net_Metric;
    metric_t metric;
    u_char *cp;
    u_char uns_char;
    as_path *asp;
    struct update_packet *up, *upn;
    struct update_packet *up_base = NULL;
    struct sockaddr_in *gateway;
    rt_entry *rt;

    trace(TR_TASK, 0, "bgp_send_update: sending updates to %s",
	  bnp->bgp_name);

    RT_TABLE(rt) {
	metric = bgp_default_metric;
	if (rt->rt_state & (RTS_HOSTROUTE | RTS_SUBNET | RTS_NOADVISE)) {
	    /* Subnets and hostroutes not allowed - also catch routes not to be announced */
	    continue;
	}
	if (flash_flag && (bnp->bgp_task->task_rtrevision >= rt->rt_revision)) {
	    /* Processing a flash update and this route has not been modified */
	    continue;
	}
	if (!propagate(rt,
		       (proto_t) 0,
		       bnp->bgp_propagate,
		       (adv_entry *) 0,
		       (adv_entry *) 0,
		       &metric)) {
	    continue;
	}
	/* Get pointer to an asp path for this route */
	switch (rt->rt_proto) {
	    case RTPROTO_BGP:
		asp = (as_path *) rt->rt_data->rtd_data;
		if (!asp->as_count && (bnp->bgp_linktype == openLinkInternal)) {
		    /* Don't propogate internal routes over Internal links */
		    continue;
		}
		break;

	    case RTPROTO_EGP:
		asp = bgp_as_build(1, ASO_EGP, rt->rt_as);
		break;
		
	    default:
		if (bnp->bgp_linktype == openLinkInternal) {
		  /* Do not propagate interior routes over Internal links */
		  continue;
		}
		
		asp = bgp_as_build(0, ASO_IGP, 0);
		break;
	}

	/* Check for split horizon */
	for (i = 0; i < asp->as_count; i++) {
	    if (asp->as_number[i] == bnp->bgp_asin) {
		/* Don't send route back to this peer, it would cause a loop */
		break;
	    }
	}
	if (i != asp->as_count) {
	    continue;
	}
	/* Determine gateway for this path */
	if (gd_inet_wholenetof(rt->rt_router.in.sin_addr) !=gd_inet_wholenetof(bnp->bgp_interface->int_addr.in.sin_addr)) {
	    /* Gateway is my address */
	    gateway = &bnp->bgp_interface->int_addr.in;
	} else {
	    /* Gateway is next hop */
	    gateway = &rt->rt_router.in;
	}

	/* Locate or allocate a packet for this path */
	for (up = up_base; up; up = up->up_next) {
	    if ((asp == up->up_asp) && equal_in(gateway->sin_addr, up->up_gateway)) {
		/* Send packet if already full */
		if ((up->up_np - (u_char *) up - sizeof(struct update_packet) + 6 /* XXX */ ) > BGPMAXPACKETSIZE) {
		    bgp_send_update_packet(bnp, up);
		}
		break;
	    }
	}
	if (!up) {
	    up = (struct update_packet *) calloc(1, BGPMAXPACKETSIZE + sizeof(struct update_packet));
	    if (!up) {
		trace(TR_ALL, LOG_ERR, "bgp_send_update: calloc %m");
		quit(errno);
	    }
	    /* Add to chain */
	    up->up_next = up_base;
	    up_base = up;
	    /* Save gateway and as_path pointers */
	    up->up_gateway = gateway->sin_addr;
	    up->up_asp = asp;
	    /* Point to packet */
	    cp = (u_char *) up + sizeof(struct update_packet) + sizeof(pduHeader);
	    /* Add gateway */
	    PutDown(cp, gateway->sin_addr);
	    /* AS path */
	    if (bnp->bgp_linktype == openLinkInternal) {
		/* Don't prepend my AS and link type if Internal */
		uns_char = asp->as_count;
		PutDown(cp, uns_char);	/* Save AS count */
	    } else {
		/* Prepend my AS and link type if not Internal */
		uns_char = asp->as_count + 1;
		PutDown(cp, uns_char);	/* Save AS count */
		uns_char = bnp->bgp_linktype;
		PutDown(cp, uns_char);	/* Put down direction */
		uns_short = htons(bnp->bgp_asout);
		PutDown(cp, uns_short);	/* Put down AS number */
	    }
	    /* Add AS path */
	    for (i = 0; i < asp->as_count; i++) {
		uns_char = asDirHorizontal;
		PutDown(cp, uns_char);
		uns_short = htons(asp->as_number[i]);
		PutDown(cp, uns_short);
	    }

	    up->up_ncp = cp;		/* Save pointer to network count */
	    up->up_np = cp + sizeof(u_short);	/* Save pointer to where to save network */

	    /* XXX - The following is for compatibility with BGP */
	    /* version 1 and will hopefully be removed soon */
	    cp -= sizeof(as_t) + sizeof(uns_char);
	    if (asp->as_origin == ASO_IGP) {
		uns_char = asDirHorizontal;
	    } else if (asp->as_origin == ASO_EGP) {
		uns_char = asDirEgp;
	    } else {
		uns_char = asDirIncomplete;
	    }
	    PutDown(cp, uns_char);
	    /* XXX - end of compatibility*/

	}
	up->up_net_count++;
	/* Add networks and metrics */

	/* Store the network */
	PutDown(up->up_np, rt->rt_dest.in.sin_addr);

	/* Metric is already set.  Check here for metricout or unreachable */
	if (!(rt->rt_ifp->int_state & IFS_UP) || (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE))) {
	    metric = bgpMetricInfinity;
	} else if (bnp->bgp_options & BGPO_METRICOUT) {
	    metric = bnp->bgp_metricout;
	}
	net_Metric = metric;
	PutDown(up->up_np, net_Metric);

    } RT_TABLEEND;

    /* Send and free any packets */
    for (up = up_base; up; up = upn) {
	upn = up->up_next;
	bgp_send_update_packet(bnp, up);
	(void) free((caddr_t) up);
    }

    bnp->bgp_task->task_rtrevision = rt_revision;
}

#endif				/* PROTO_BGP */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.