This is egp_rt.c in view mode; [Download] [Up]
/*
* $Header: /disk/d/src/devel/gated/dist/src/RCS/egp_rt.c,v 2.1 92/02/24 14:12:32 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. *
* *
************************************************************************/
/*
* rt_egp.c
*
* EGP route update and processing and preparation functions.
*
* Functions: egp_rt_send, egp_rt_recv
*/
#include "include.h"
#include "egp.h"
#ifdef PROTO_EGP
/*
* egp_rt_send() prepares the network part of the EGP Network Reachability
* update message with respect to the shared network of the EGP peer.
* This only includes the networks in the interior routing table (direct
* networks, and remote networks of non-routing gateways of this autonomous
* system) other than the net shared by the EGP peer. If the user has
* specified that only certain networks are allowed to be advised all others
* are excluded from outgoing NR update messages.
* If the interior routing table includes other interior gateways on the
* network shared with the EGP peer (i.e. indirect neighbors), they are
* included in updates as the appropriate first hop to their attached
* networks.
* This function checks the status of routes and if down sets the
* distance as unreachable.
*
* Returns the length of the EGP NR packet in octets or ERROR if an error
* has occurred.
*/
int
egp_rt_send(nrpkt, ngp)
struct egpnr *nrpkt; /* start of NR message */
struct egpngh *ngp; /* Pointer to entry in neighbor table */
{
rt_entry *rt;
int n_bytes;
struct in_addr current_gw;
register u_char *nrp; /* next octet of NR message */
u_char *n_distance = NULL, *distance = NULL, *n_nets = NULL;
metric_t this_metric = 0;
u_long current_net;
struct sockaddr_in addr;
struct net_order { /* temporary linked list for ordering nets */
struct net_order *next; /* Next entry */
struct in_addr net; /* Network */
struct in_addr gateway; /* Gateway */
u_char exterior; /* True if this is an exterior gateway */
u_char distance; /* Distance to advertize */
} *start_net, *free_net, *last_net;
register struct net_order *net_pt, *this_net; /* current search point */
rt_entry *pollnet_rt;
if (!(pollnet_rt = rt_locate(RTS_INTERIOR, (sockaddr_un *) & ngp->ng_paddr, RTPROTO_DIRECT))) {
trace(TR_INT, LOG_ERR, "egp_rt_send: no route to polled net, %A wanted",
&ngp->ng_paddr);
return (ERROR);
}
sockclear_in(&addr);
/*
* Reorder the interior routes as required for the NR message with respect to
* the given shared net. Uses a temporary linked list terminated by NULL
* pointer. The first element of the list is a dummy so insertions can be done
* before the first true entry. The route status is checked and if down the
* distance is set as unreachable. The required order groups nets by gateway
* and in order of increasing metric. This gateway is listed first (with all
* nets not reached by gateways on the shared net) and then neighbor gateways
* on the shared net, in any order. As there are few nets to be reported by a
* stub gateway, each route is copied from the interior routing table and
* inserted in the temporary reordered list using a linear search.
*
* Use the total number of networks to be sure we allocate a large enough
* buffer.
*/
/* XXX - need an indication of whether this is an interior or exterior route, list the interior ones first and set */
/* XXX - the number of interior and exterior gateways correctly. */
start_net = (struct net_order *) malloc((unsigned) (rt_net_routes + 1) * sizeof (struct net_order));
if (start_net == NULL) {
trace(TR_ALL, LOG_ERR, "egp_rt_send: malloc: %m");
return (ERROR);
}
last_net = start_net + rt_net_routes + 1;
start_net->next = NULL;
/*
* ensures first gateway listed is self
*/
start_net->gateway = pollnet_rt->rt_router.in.sin_addr; /* struct copy */
start_net->exterior = 0;
free_net = start_net + 1; /* first element dummy to ease insertion code */
/*
* check all interior routes of route table
*/
RT_TABLE(rt) {
if (rt->rt_state & (RTS_HOSTROUTE | RTS_SUBNET | RTS_NOADVISE)) {
/* Don't send subnets or networks not to be announces */
continue;
}
IF_EGPUPD tracef("EGP UPDATE\tnet %-15A AS %5d - ",
&rt->rt_dest,
rt->rt_as);
/*
* Don't allow the DEFAULT net through, unless we are allowed to
* send DEFAULT.
*/
if ((rt->rt_dest.in.sin_addr.s_addr == DEFAULTNET) &&
!(ngp->ng_options & NGO_DEFAULTOUT)) {
IF_EGPUPD trace(TR_EGP | TR_NOSTAMP, 0, "not propogating default.");
continue;
}
/*
* ignore nets that are not Class A, B or C
*/
current_net = rt->rt_dest.in.sin_addr.s_addr;
if (gd_inet_class((u_char *) & current_net) == 0) {
IF_EGPUPD trace(TR_EGP | TR_NOSTAMP, 0, "not Class A, B or C");
trace(0, LOG_ERR, "egp_rt_send: net not class A, B or C: %A",
&rt->rt_dest);
continue;
}
this_metric = egp_default_metric;
if (!propagate(rt,
(proto_t) 0,
ngp->ng_propagate,
(adv_entry *) 0,
(adv_entry *) 0,
&this_metric)) {
IF_EGPUPD trace(TR_EGP | TR_NOSTAMP, 0, "restrictions prohibit announcement");
continue;
}
/* Check for metric of infinity and metricout */
if ((!rt->rt_ifp->int_state & IFS_UP) || (rt->rt_state & (RTS_HOLDDOWN | RTS_DELETE))) {
this_metric = EGP_INFINITY;
} else if (ngp->ng_options & NGO_METRICOUT) {
this_metric = ngp->ng_metricout;
}
/*
* committed to advertising net
*/
if ((this_net = free_net++) >= last_net) {
trace(TR_ALL, LOG_ERR, "egp_rt_send: rt_net_routes too low, overflowed allocated network list");
return (ERROR);
}
this_net->net = rt->rt_dest.in.sin_addr; /* struct copy */
this_net->distance = this_metric;
this_net->exterior = 0;
/*
* assign gw on shared net
*/
if (gd_inet_wholenetof(rt->rt_router.in.sin_addr) !=gd_inet_wholenetof(ngp->ng_paddr.sin_addr)) {
this_net->gateway = pollnet_rt->rt_router.in.sin_addr;
} else {
/* gw is neighbor */
this_net->gateway = rt->rt_router.in.sin_addr;
if (rt->rt_as && (rt->rt_as != my_system)) {
this_net->exterior++;
}
}
/*
* If this is the DEFAULT net, set the specified metric, we are
* the gateway.
*/
if (this_net->net.s_addr == DEFAULTNET) {
this_net->gateway = pollnet_rt->rt_router.in.sin_addr;
this_net->distance = ngp->ng_defaultmetric;
IF_EGPUPD tracef(" DEFAULT - ");
}
IF_EGPUPD tracef("metric %3d ", this_net->distance);
/*
* insert net in ordered list
*/
for (net_pt = start_net; net_pt->next; net_pt = net_pt->next) {
if (this_net->exterior) {
if (!net_pt->next->exterior) {
continue;
}
} else {
if (net_pt->next->exterior) {
break;
}
}
if (equal_in(this_net->gateway, net_pt->next->gateway)) {
if (this_net->distance <= net_pt->next->distance)
break;
} else {
if (equal_in(this_net->gateway, net_pt->gateway)) {
break;
}
}
} /* for (all nets to be announced) */
/*
* insert this net after search net
*/
this_net->next = net_pt->next;
net_pt->next = this_net;
addr.sin_addr = this_net->gateway; /* sstruct copy */
IF_EGPUPD trace(TR_EGP | TR_NOSTAMP, 0, "added to update distance %3d gateway %A%s",
this_net->distance,
&addr,
this_net->exterior ? " exterior" : "");
} RT_TABLEEND;
IF_EGPUPD trace(TR_EGP, 0, NULL);
/*
* copy nets into NR message
*/
nrpkt->en_igw = 0; /* init # interior gateways */
nrpkt->en_egw = 0; /* init # exterior gateways */
nrp = (u_char *) (nrpkt + 1); /* start nets part NR msg */
current_gw.s_addr = 0; /* ensure first gateway addr copied */
for (net_pt = start_net->next; net_pt != NULL; net_pt = net_pt->next) {
if (!equal_in(net_pt->gateway, current_gw)) {
/* new gateway */
current_gw = net_pt->gateway;
current_net = current_gw.s_addr;
n_bytes = 4 - gd_inet_class((u_char *) & current_net);
memcpy((char *) nrp, (char *) ¤t_net + 4 - n_bytes, n_bytes);
nrp += n_bytes;
if (net_pt->exterior) {
nrpkt->en_egw++;
} else {
nrpkt->en_igw++;
}
n_distance = nrp++;
*n_distance = 1;
distance = nrp++;
*distance = net_pt->distance;
n_nets = nrp++;
*n_nets = 1;
} else if ((net_pt->distance != *distance) || (*n_nets == 255)) {
/* New distance or this distance if ull */
(*n_distance)++;
distance = nrp++;
*distance = net_pt->distance;
n_nets = nrp++;
*n_nets = 1;
} else {
(*n_nets)++;
}
current_net = net_pt->net.s_addr;
n_bytes = gd_inet_class((u_char *) & current_net);
memcpy((char *) nrp, (char *) ¤t_net, n_bytes);
nrp += n_bytes;
} /* end for each net */
free((char *) start_net);
return (nrp - (u_char *) nrpkt); /* length of NR message */
}
/*
* egp_rt_recv() updates the exterior routing tables on receipt of an NR
* update message from an EGP neighbor. It first checks for valid NR counts
* before updating the routing tables.
*
* EGP Updates are used to update the exterior routing table if one of the
* following is satisfied:
* - No routing table entry exists for the destination network and the
* metric indicates the route is reachable (< 255).
* - The advised gateway is the same as the current route.
* - The advised distance metric is less than the current metric.
* - The current route is older (plus a margin) than the maximum poll
* interval for all acquired EGP neighbors. That is, the route was
* omitted from the last Update.
*
* Returns 1 if there is an error in NR message data, 0 otherwise.
*/
int
egp_rt_recv(ngp, pkt, egplen)
struct egpngh *ngp; /* pointer to neighbor state table */
struct egppkt *pkt;
int egplen; /* length EGP NR packet */
{
register u_char *nrb;
struct egpnr *nrp = (struct egpnr *) pkt;
struct sockaddr_in destination, gateway;
u_char gw[4]; /* gateway internet address */
int gw_class, net_class, ng, nd, nn, n_gw, n_dist = 0, n_net = 0, checkingNR = TRUE,
NR_nets = 0;
pref_t preference;
u_int state;
metric_t distance;
rt_entry *rt;
as_t pkt_system;
task *tp = ngp->ng_task;
sockclear_in(&destination);
sockclear_in(&gateway);
/*
* check class of shared net
*/
*(u_long *) gw = nrp->en_net.s_addr;/* set net part of gateways */
if ((gw_class = gd_inet_class((u_char *) & gw[0])) == 0) {
return (EBADDATA); /* NR message error */
}
pkt_system = htons(pkt->egp_system);
n_gw = nrp->en_igw + nrp->en_egw;
/*
* First check NR message for valid counts, then repeat and update routing
* tables
*/
repeat:
if (!checkingNR) {
rt_open(tp);
}
nrb = (u_char *) nrp + sizeof(struct egpnr); /* start first gw */
/* XXX - Need to keep track of interior vs exterior gateways and install routes with interior vs exterior flag set */
/* XXX - so we don't compare the metric of routes that are not really from the same AS */
for (ng = 0; ng < n_gw; ng++) { /* all gateways */
switch (gw_class) { /* fill gateway local address */
case CLAA:
gw[1] = *nrb++;
case CLAB:
gw[2] = *nrb++;
case CLAC:
gw[3] = *nrb++;
}
gateway.sin_addr.s_addr = (ngp->ng_options & NGO_GATEWAY) ? ngp->ng_gateway.sin_addr.s_addr : *(u_long *) gw;
n_dist = *nrb++;
for (nd = 0; nd < n_dist; nd++) { /* all distances this gateway */
distance = (u_short) (*nrb++);
n_net = *nrb++;
if (!checkingNR) {
NR_nets += n_net;
}
for (nn = 0; nn < n_net; nn++) { /* all nets this distance */
preference = ngp->ng_preference;
if ((net_class = gd_inet_class(nrb)) == 0) {
net_class = 3;
}
destination.sin_addr.s_addr = 0; /* zero unused bytes*/
memcpy((char *) &destination.sin_addr.s_addr, (char *) nrb, net_class);
nrb += net_class;
if (!gd_inet_class((u_char *) & destination.sin_addr.s_addr)) {
if (checkingNR) {
trace(TR_EXT, LOG_ERR, "egp_rt_recv: net %A not class A, B or C from %s via %A",
&destination,
ngp->ng_name,
&gateway);
}
#ifdef notdef
return (EBADDATA); /* Ignore complete NR packet */
#else /* notdef */
continue; /* Ignore only this route */
#endif /* notdef */
}
if ((destination.sin_addr.s_addr == DEFAULTNET) && !(ngp->ng_options & NGO_DEFAULTIN)) {
if (checkingNR) {
trace(TR_EXT, LOG_WARNING, "egp_rt_recv: ignoring net %A from %s via %A",
&destination,
ngp->ng_name,
&gateway);
}
continue;
}
if (checkingNR) { /* first check counts only */
if (nrb > (u_char *) nrp + egplen + 1)
return (EBADDATA); /* erroneous counts in NR */
} else { /* update routing table */
if (equal_in(gateway.sin_addr, ngp->ng_interface->int_addr.in.sin_addr)) {
continue;
}
/* Check of this network is valid from this AS */
if (!is_valid_in((sockaddr_un *) & destination,
ngp->ng_accept,
(adv_entry *) 0,
(adv_entry *) 0,
&preference)) {
if (trace_flags & TR_EGP) {
trace(TR_EGP, 0, "egp_rt_recv: net %-15A not valid from AS %5d",
&destination,
pkt_system);
}
continue;
}
/*
* check for an existing route
*/
state = 0;
rt = rt_locate_gw(RTS_EXTERIOR, (sockaddr_un *) & destination, RTPROTO_EGP, &ngp->ng_gw);
if (!rt) { /* new route */
if (distance >= EGP_INFINITY) {
continue;
}
if (rt = rt_add((sockaddr_un *) & destination,
(sockaddr_un *) 0,
(sockaddr_un *) & gateway,
&ngp->ng_gw,
distance,
RTS_EXTERIOR | state,
RTPROTO_EGP,
ngp->ng_asin,
ngp->ng_rtage,
preference)) {
}
} else { /* existing route */
if (equal(&rt->rt_router, &gateway) && (rt->rt_as == ngp->ng_asin)) { /* same gw */
if (distance < EGP_INFINITY) {
if (distance < rt->rt_metric) {
if (rt_change(rt,
(sockaddr_un *) & gateway,
distance,
ngp->ng_rtage,
preference)) {
}
}
rt_refresh(rt);
} else {
(void) rt_delete(rt);
}
} else { /* different gateway */
if (rt->rt_as == ngp->ng_asin) {
/* Same Autonotmous system */
if (distance < rt->rt_metric) {
if (rt_change(rt,
(sockaddr_un *) & gateway,
distance,
ngp->ng_rtage,
preference)) {
}
}
} else if ((distance < rt->rt_metric) ||
((distance >= rt->rt_metric) && (rt->rt_timer > (ngp->ng_T2 * (EGP_N_POLLAGE - 1))) &&
!(rt->rt_state & RTS_CHANGED | RTS_REFRESH))) {
/* Override route if metric is better, or this one has almost expired (and has not been refreshed) */
if (rt_change(rt,
(sockaddr_un *) & gateway,
distance,
ngp->ng_rtage,
preference)) {
}
}
}
} /* end else existing route */
} /* end else update routing table */
} /* end for all nets */
} /* end for all distances */
} /* end for all gateways */
if (checkingNR) {
if (nrb > (u_char *) nrp + egplen) {
return (EBADDATA); /* erroneous counts */
} else {
checkingNR = FALSE;
}
goto repeat;
}
/*
* Generate default if not prohibited and the NR packet
* contains more than one route
*/
if (!(ngp->ng_options & NGO_NOGENDEFAULT) && !(ngp->ng_flags & NGF_GENDEFAULT) && NR_nets) {
if (rt_default_add()) {
ngp->ng_flags |= NGF_GENDEFAULT;
}
}
if (rt_close(tp, &ngp->ng_gw, NR_nets)) {
task_flash(tp);
}
return (NOERROR);
}
#endif /* PROTO_EGP */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.