diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 1996-09-05 14:31:56 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 1996-09-05 14:31:56 +0000 |
commit | 9d64f3837f5ab4ff9b72434834f689bcba72df25 (patch) | |
tree | 5c1834cd30bb963ec1f1e8bbc100c0d34de6c8a1 /sbin/routed/input.c | |
parent | 35859215e82e540ee7ac7506a2fc76110ab82eb6 (diff) |
fix import.
mention that routed is from vjs@sgi.com
Diffstat (limited to 'sbin/routed/input.c')
-rw-r--r-- | sbin/routed/input.c | 918 |
1 files changed, 625 insertions, 293 deletions
diff --git a/sbin/routed/input.c b/sbin/routed/input.c index 941725028d0..c17e4fd2a36 100644 --- a/sbin/routed/input.c +++ b/sbin/routed/input.c @@ -1,5 +1,4 @@ -/* $OpenBSD: input.c,v 1.3 1996/06/23 14:32:27 deraadt Exp $ */ -/* $NetBSD: input.c,v 1.16 1995/07/13 23:20:10 christos Exp $ */ +/* $OpenBSD: input.c,v 1.4 1996/09/05 14:31:27 mickey Exp $ */ /* * Copyright (c) 1983, 1988, 1993 @@ -34,365 +33,698 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 +#if !defined(lint) static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; #else -static char rcsid[] = "$OpenBSD: input.c,v 1.3 1996/06/23 14:32:27 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: input.c,v 1.4 1996/09/05 14:31:27 mickey Exp $"; #endif -#endif /* not lint */ -/* - * Routing Table Management Daemon - */ #include "defs.h" -#include <syslog.h> +static void input(struct sockaddr_in *, struct interface*, struct rip *, int); +static void input_route(struct interface *, naddr, + naddr, naddr, naddr, struct netinfo *); -/* - * "Authenticate" router from which message originated. - * We accept routing packets from routers directly connected - * via broadcast or point-to-point networks, - * and from those listed in /etc/gateways. + +/* process RIP input */ -static struct interface * -rip_verify(from) - struct sockaddr *from; +void +read_rip(int sock, + struct interface *ifp) { - struct interface *ifp; - char buf[256]; - - if ((ifp = if_iflookup(from)) == 0) { - syslog(LOG_ERR, "trace command from unknown router, %s", - (*afswitch[from->sa_family].af_format)(from, buf, - sizeof(buf))); - return NULL; - } + struct sockaddr_in from; + int fromlen, cc; + union pkt_buf inbuf; - if ((ifp->int_flags & - (IFF_BROADCAST|IFF_POINTOPOINT|IFF_REMOTE)) == 0) { - syslog(LOG_ERR, - "trace command from router %s, with bad flags %x", - (*afswitch[from->sa_family].af_format)(from, buf, - sizeof(buf)), - ifp->int_flags); - return NULL; - } - if ((ifp->int_flags & IFF_PASSIVE) != 0) { - syslog(LOG_ERR, - "trace command from %s on an active interface", - (*afswitch[from->sa_family].af_format)(from, buf, - sizeof(buf))); - return NULL; - } + for (;;) { + fromlen = sizeof(from); + cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0, + (struct sockaddr*)&from, &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("recvfrom(rip)"); + break; + } + if (fromlen != sizeof(struct sockaddr_in)) + logbad(1,"impossible recvfrom(rip) fromlen=%d", + fromlen); - return ifp; + input(&from, + (ifp != 0) ? ifp : iflookup(from.sin_addr.s_addr), + &inbuf.rip, cc); + } } -/* - * Process a newly received packet. +/* Process a RIP packet */ -void -rip_input(from, rip, size) - struct sockaddr *from; - register struct rip *rip; - int size; +static void +input(struct sockaddr_in *from, /* received from this IP address */ + struct interface *ifp, + struct rip *rip, + int size) { - register struct rt_entry *rt; - register struct netinfo *n; - register struct interface *ifp; - struct sockaddr dst, gateway, netmask; - int count, changes = 0; - register struct afswitch *afp; - static struct sockaddr badfrom; - char buf1[256], buf2[256]; - - ifp = 0; - TRACE_INPUT(ifp, from, (char *)rip, size); - if (from->sa_family >= af_max || - (afp = &afswitch[from->sa_family])->af_hash == NULL) { - syslog(LOG_INFO, - "\"from\" address in unsupported address family (%d), cmd %d\n", - from->sa_family, rip->rip_cmd); +# define FROM_NADDR from->sin_addr.s_addr + static naddr use_auth, bad_len, bad_mask; + static naddr unk_router, bad_router, bad_nhop; + + struct rt_entry *rt; + struct netinfo *n, *lim; + struct interface *ifp1; + naddr gate, mask, v1_mask, dst, ddst_h; + int i; + + + if (ifp != 0) + ifp->int_state |= IS_ACTIVE; + + trace_rip("Recv", "from", from, ifp, rip, size); + + if (rip->rip_vers == 0) { + if (from->sin_addr.s_addr != bad_router) + msglog("RIP version 0, cmd %d, packet received" + " from %s", + rip->rip_cmd, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; return; } - if (rip->rip_vers == 0) { - syslog(LOG_ERR, - "RIP version 0 packet received from %s! (cmd %d)", - (*afswitch[from->sa_family].af_format)(from, buf1, - sizeof(buf1)), - rip->rip_cmd); + if (size > MAXPACKETSIZE) { + if (from->sin_addr.s_addr != bad_router) + msglog("packet at least %d bytes too long received" + " from %s", + size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; return; } - switch (rip->rip_cmd) { + n = rip->rip_nets; + lim = (struct netinfo *)((char*)rip + size); + /* Notice authentication. + * As required by section 4.2 in RFC 1723, discard authenticated + * RIPv2 messages, but only if configured for that silliness. + * + * RIPv2 authentication is lame, since snooping on the wire makes + * its simple passwords evident. Also, why authenticate queries? + * Why should a RIPv2 implementation with authentication disabled + * not be able to listen to RIPv2 packets with authenication, while + * RIPv1 systems will listen? Crazy! + */ + if (!auth_ok + && rip->rip_vers >= RIPv2 + && n < lim && n->n_family == RIP_AF_AUTH) { + if (from->sin_addr.s_addr != use_auth) + msglog("RIPv2 message with authentication" + " from %s discarded", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + trace_pkt("discard authenticated RIPv2 message\n"); + return; + } + + switch (rip->rip_cmd) { case RIPCMD_REQUEST: - n = rip->rip_nets; - count = size - ((char *)n - (char *)rip); - if (count < sizeof (struct netinfo)) - return; - for (; count > 0; n++) { - if (count < sizeof (struct netinfo)) - break; - count -= sizeof (struct netinfo); - - n->rip_metric = ntohl(n->rip_metric); - n->rip_family = ntohs(n->rip_family); - /* - * A single entry with sa_family == AF_UNSPEC and - * metric ``infinity'' means ``all routes''. + /* did the request come from a router? + */ + if (from->sin_port == htons(RIP_PORT)) { + /* yes, ignore it if RIP is off so that it does not + * depend on us. + */ + if (rip_sock < 0) { + trace_pkt("ignore request while RIP off\n"); + return; + } + + /* Ignore the request if we talking to ourself + * (and not a remote gateway). + */ + if (ifwithaddr(FROM_NADDR, 0, 0) != 0) { + trace_pkt("discard our own RIP request\n"); + return; + } + } + + /* According to RFC 1723, we should ignore unathenticated + * queries. That is too silly to bother with. Sheesh! + * Are forwarding tables supposed to be secret? When + * a bad guy can infer them with test traffic? + * Maybe on firewalls you'd care, but not enough to + * give up the diagnostic facilities of remote probing. + */ + + if (n >= lim + || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("request of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + for (; n < lim; n++) { + n->n_metric = ntohl(n->n_metric); + + /* A single entry with family RIP_AF_UNSPEC and + * metric HOPCNT_INFINITY means "all routes". * We respond to routers only if we are acting * as a supplier, or to anyone other than a router - * (eg, query). + * (i.e. a query). + * + * Answer a query from a stray program with all + * we know. Filter the answer to a query from a + * router in the about same way broadcasts are + * filtered. + * + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us an a router. */ - if (n->rip_family == AF_UNSPEC && - n->rip_metric == HOPCNT_INFINITY && count == 0) { - if (supplier || (*afp->af_portmatch)(from) == 0) - supply(from, 0, 0, 0); + if (n->n_family == RIP_AF_UNSPEC + && n->n_metric == HOPCNT_INFINITY + && n == rip->rip_nets + && n+1 == lim) { + if (from->sin_port != htons(RIP_PORT)) { + /* query from `rtquery` or similar + */ + supply(from, ifp, + OUT_QUERY, 0, rip->rip_vers); + } else if (supplier) { + /* a router trying to prime its + * tables. + */ + supply(from, ifp, + OUT_UNICAST, 0, rip->rip_vers); + } + return; + } + + if (n->n_family != RIP_AF_INET) { + if (from->sin_addr.s_addr != bad_router) + msglog("request from %s" + " for unsupported (af %d) %s", + naddr_ntoa(FROM_NADDR), + ntohs(n->n_family), + naddr_ntoa(n->n_dst)); + bad_router = from->sin_addr.s_addr; return; } - if (n->rip_family < af_max && - afswitch[n->rip_family].af_hash) { - if (!(*afswitch[n->rip_family].af_get)( - DESTINATION, n, &dst)) - return; - rt = rtlookup(&dst); + + dst = n->n_dst; + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad queried destination" + " %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask)) + || 0 != (ntohl(dst) & ~mask)) + mask = ripv1_mask_host(dst,ifp); + + rt = rtget(dst, mask); + if (!rt && dst != RIP_DEFAULT) + rt = rtfind(n->n_dst); + + n->n_tag = 0; + n->n_nhop = 0; + if (rip->rip_vers == RIPv1) { + n->n_mask = 0; + } else { + n->n_mask = mask; + } + if (rt == 0) { + n->n_metric = HOPCNT_INFINITY; + } else { + n->n_metric = rt->rt_metric+1; + n->n_metric += (ifp!=0) ? ifp->int_metric : 1; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + if (rip->rip_vers != RIPv1) { + n->n_tag = rt->rt_tag; + if (ifp != 0 + && on_net(rt->rt_gate, + ifp->int_net, + ifp->int_mask) + && rt->rt_gate != ifp->int_addr) + n->n_nhop = rt->rt_gate; + } } - else - rt = 0; -#define min(a, b) (a < b ? a : b) - n->rip_metric = rt == 0 ? HOPCNT_INFINITY : - min(rt->rt_metric + 1, HOPCNT_INFINITY); - n->rip_metric = htonl(n->rip_metric); + HTONL(n->n_metric); } + /* Answer about specific routes. + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us an a router. + */ rip->rip_cmd = RIPCMD_RESPONSE; - memcpy(packet, rip, size); - (*afp->af_output)(s, 0, from, size); + rip->rip_res1 = 0; + if (rip->rip_vers != RIPv1) + rip->rip_vers = RIPv2; + if (from->sin_port != htons(RIP_PORT)) { + /* query */ + (void)output(OUT_QUERY, from, ifp, rip, size); + } else if (supplier) { + (void)output(OUT_UNICAST, from, ifp, rip, size); + } return; case RIPCMD_TRACEON: case RIPCMD_TRACEOFF: /* verify message came from a privileged port */ -#ifdef TRACING - if ((*afp->af_portcheck)(from) == 0) + if (ntohs(from->sin_port) > IPPORT_RESERVED) { + msglog("trace command from untrusted port on %s", + naddr_ntoa(FROM_NADDR)); return; - - if ((ifp = rip_verify(from)) == NULL) + } + if (ifp == 0) { + msglog("trace command from unknown router %s", + naddr_ntoa(FROM_NADDR)); return; - - ((char *)rip)[size] = '\0'; - if (rip->rip_cmd == RIPCMD_TRACEON) - traceon(rip->rip_tracefile); - else - traceoff(); -#endif + } + if (rip->rip_cmd == RIPCMD_TRACEON) { + rip->rip_tracefile[size-4] = '\0'; + trace_on((char*)rip->rip_tracefile, 0); + } else { + trace_off("tracing turned off by %s\n", + naddr_ntoa(FROM_NADDR)); + } return; case RIPCMD_RESPONSE: + if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("response of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + /* verify message came from a router */ - if ((*afp->af_portmatch)(from) == 0) + if (from->sin_port != ntohs(RIP_PORT)) { + trace_pkt("discard RIP response from unknown port\n"); return; - (*afp->af_canon)(from); - /* are we talking to ourselves? */ - ifp = if_ifwithaddr(from); - if (ifp) { - if (ifp->int_flags & IFF_PASSIVE) { - syslog(LOG_ERR, - "bogus input (from passive interface, %s)", - (*afswitch[from->sa_family].af_format)(from, - buf1, sizeof(buf1))); - return; + } + + if (rip_sock < 0) { + trace_pkt("discard response while RIP off\n"); + return; + } + + /* Are we talking to ourself or a remote gateway? + */ + ifp1 = ifwithaddr(FROM_NADDR, 0, 1); + if (ifp1) { + if (ifp1->int_state & IS_REMOTE) { + if (ifp1->int_state & IS_PASSIVE) { + msglog("bogus input from %s on" + " supposedly passive %s", + naddr_ntoa(FROM_NADDR), + ifp1->int_name); + } else { + ifp1->int_act_time = now.tv_sec; + if (if_ok(ifp1, "remote ")) + addrouteforif(ifp1); + } + } else { + trace_pkt("discard our own RIP response\n"); } - rt = rtfind(from); - if (rt == 0 || (((rt->rt_state & RTS_INTERFACE) == 0) && - rt->rt_metric >= ifp->int_metric)) - addrouteforif(ifp); - else - rt->rt_timer = 0; return; } - /* - * Update timer for interface on which the packet arrived. - * If from other end of a point-to-point link that isn't - * in the routing tables, (re-)add the route. + + /* Check the router from which message originated. We accept + * routing packets from routers directly connected via + * broadcast or point-to-point networks, and from + * those listed in /etc/gateways. + */ + if (!ifp) { + if (from->sin_addr.s_addr != unk_router) + msglog("discard packet from unknown router %s" + " or via unidentified interface", + naddr_ntoa(FROM_NADDR)); + unk_router = from->sin_addr.s_addr; + return; + } + if (ifp->int_state & IS_PASSIVE) { + trace_act("discard packet from %s" + " via passive interface %s\n", + naddr_ntoa(FROM_NADDR), + ifp->int_name); + return; + } + + /* Check required version + */ + if (((ifp->int_state & IS_NO_RIPV1_IN) + && rip->rip_vers == RIPv1) + || ((ifp->int_state & IS_NO_RIPV2_IN) + && rip->rip_vers != RIPv1)) { + trace_pkt("discard RIPv%d response\n", + rip->rip_vers); + return; + } + + /* Ignore routes via dead interface. */ - if ((rt = rtfind(from)) && - (rt->rt_state & (RTS_INTERFACE | RTS_REMOTE))) - rt->rt_timer = 0; - else if ((ifp = if_ifwithdstaddr(from)) && - (rt == 0 || rt->rt_metric >= ifp->int_metric)) - addrouteforif(ifp); - - if ((ifp = rip_verify(from)) == NULL) + if (ifp->int_state & IS_BROKE) { + trace_pkt("discard response via broken interface %s\n", + ifp->int_name); return; + } + + /* Authenticate the packet if we have a secret. + */ + if (ifp->int_passwd[0] != '\0') { + if (n >= lim + || n->n_family != RIP_AF_AUTH + || ((struct netauth*)n)->a_type != RIP_AUTH_PW) { + if (from->sin_addr.s_addr != use_auth) + msglog("missing password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + + } else if (0 != bcmp(((struct netauth*)n)->au.au_pw, + ifp->int_passwd, + sizeof(ifp->int_passwd))) { + if (from->sin_addr.s_addr != use_auth) + msglog("bad password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + } + } - size -= 4 * sizeof (char); - n = rip->rip_nets; - for (; size > 0; size -= sizeof (struct netinfo), n++) { - if (size < sizeof (struct netinfo)) - break; - n->rip_metric = ntohl(n->rip_metric); - n->rip_family = ntohs(n->rip_family); - if (!(*afswitch[n->rip_family].af_get)(DESTINATION, n, - &dst)) + for (; n < lim; n++) { + if (n->n_family == RIP_AF_AUTH) continue; - if (!(*afswitch[n->rip_family].af_get)(NETMASK, - n, &netmask)) - memset(&netmask, 0, sizeof(netmask)); - if (!(*afswitch[n->rip_family].af_get)(GATEWAY, - n, &gateway)) - memcpy(&gateway, from, sizeof(gateway)); - if (dst.sa_family >= af_max || - (afp = &afswitch[dst.sa_family])->af_hash == NULL) { - syslog(LOG_INFO, - "route in unsupported address family (%d), from %s (af %d)\n", - dst.sa_family, - (*afswitch[from->sa_family].af_format)(from, - buf1, sizeof(buf1)), - from->sa_family); + + NTOHL(n->n_metric); + dst = n->n_dst; + if (n->n_family != RIP_AF_INET + && (n->n_family != RIP_AF_UNSPEC + || dst != RIP_DEFAULT)) { + if (from->sin_addr.s_addr != bad_router) + msglog("route from %s to unsupported" + " address family %d," + " destination %s", + naddr_ntoa(FROM_NADDR), + n->n_family, + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; continue; } - if (((*afp->af_checkhost)(&dst)) == 0) { - syslog(LOG_DEBUG, - "bad host %s in route from %s (af %d)\n", - (*afswitch[dst.sa_family].af_format)( - &dst, buf1, sizeof(buf1)), - (*afswitch[from->sa_family].af_format)(from, - buf2, sizeof(buf2)), - from->sa_family); - continue; + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad destination %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; } - if (n->rip_metric == 0 || - (unsigned) n->rip_metric > HOPCNT_INFINITY) { - if (memcmp(from, &badfrom, - sizeof(badfrom)) != 0) { - syslog(LOG_ERR, - "bad metric (%d) from %s\n", - n->rip_metric, - (*afswitch[from->sa_family].af_format)(from, - buf1, sizeof(buf1))); - badfrom = *from; - } - continue; + if (n->n_metric == 0 + || n->n_metric > HOPCNT_INFINITY) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad metric %d from %s" + " for destination %s", + n->n_metric, + naddr_ntoa(FROM_NADDR), + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; + return; } - /* - * Adjust metric according to incoming interface. + + /* Notice the next-hop. */ - if ((unsigned) n->rip_metric < HOPCNT_INFINITY) - n->rip_metric += ifp->int_metric; - if ((unsigned) n->rip_metric > HOPCNT_INFINITY) - n->rip_metric = HOPCNT_INFINITY; - rt = rtlookup(&dst); - if (rt == 0 || - (rt->rt_state & (RTS_INTERNAL|RTS_INTERFACE)) == - (RTS_INTERNAL|RTS_INTERFACE)) { - /* - * If we're hearing a logical network route - * back from a peer to which we sent it, - * ignore it. - */ - if (rt && rt->rt_state & RTS_SUBNET && - (*afp->af_sendroute)(rt, from)) - continue; - if ((unsigned)n->rip_metric < HOPCNT_INFINITY) { - /* - * Look for an equivalent route that - * includes this one before adding - * this route. - */ - rt = rtfind(&dst); - if (rt && equal(&gateway, &rt->rt_router)) - continue; - rtadd(&dst, &gateway, &netmask, - n->rip_metric, 0); - changes++; + gate = from->sin_addr.s_addr; + if (n->n_nhop != 0) { + if (rip->rip_vers == RIPv2) { + n->n_nhop = 0; + } else { + /* Use it only if it is valid. */ + if (on_net(n->n_nhop, + ifp->int_net, ifp->int_mask) + && check_dst(n->n_nhop)) { + gate = n->n_nhop; + } else { + if (bad_nhop != from->sin_addr.s_addr) + msglog("router %s to %s has" + " bad next hop %s", + naddr_ntoa(FROM_NADDR), + naddr_ntoa(dst), + naddr_ntoa(n->n_nhop)); + bad_nhop = from->sin_addr.s_addr; + n->n_nhop = 0; + } + } + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask))) { + mask = ripv1_mask_host(dst,ifp); + } else if ((ntohl(dst) & ~mask) != 0) { + if (bad_mask != from->sin_addr.s_addr) { + msglog("router %s sent bad netmask" + " %#x with %s", + naddr_ntoa(FROM_NADDR), + mask, + naddr_ntoa(dst)); + bad_mask = from->sin_addr.s_addr; } continue; } + if (rip->rip_vers == RIPv1) + n->n_tag = 0; + + /* Adjust metric according to incoming interface.. + */ + n->n_metric += ifp->int_metric; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + + /* Recognize and ignore a default route we faked + * which is being sent back to us by a machine with + * broken split-horizon. + * Be a little more paranoid than that, and reject + * default routes with the same metric we advertised. + */ + if (ifp->int_d_metric != 0 + && dst == RIP_DEFAULT + && n->n_metric >= ifp->int_d_metric) + continue; - /* - * Update if from gateway and different, - * shorter, or equivalent but old route - * is getting stale. + /* We can receive aggregated RIPv2 routes that must + * be broken down before they are transmitted by + * RIPv1 via an interface on a subnet. + * We might also receive the same routes aggregated + * via other RIPv2 interfaces. + * This could cause duplicate routes to be sent on + * the RIPv1 interfaces. "Longest matching variable + * length netmasks" lets RIPv2 listeners understand, + * but breaking down the aggregated routes for RIPv1 + * listeners can produce duplicate routes. + * + * Breaking down aggregated routes here bloats + * the daemon table, but does not hurt the kernel + * table, since routes are always aggregated for + * the kernel. + * + * Notice that this does not break down network + * routes corresponding to subnets. This is part + * of the defense against RS_NET_SYN. */ - if (equal(&gateway, &rt->rt_router)) { - if (n->rip_metric != rt->rt_metric) { - rtchange(rt, &gateway, - &netmask, n->rip_metric); - changes++; - rt->rt_timer = 0; - if (rt->rt_metric >= HOPCNT_INFINITY) - rt->rt_timer = - GARBAGE_TIME - EXPIRE_TIME; - } else if (rt->rt_metric < HOPCNT_INFINITY) - rt->rt_timer = 0; - } else if ((unsigned) n->rip_metric < rt->rt_metric || - (rt->rt_metric == n->rip_metric && - rt->rt_timer > (EXPIRE_TIME/2) && - (unsigned) n->rip_metric < HOPCNT_INFINITY)) { - rtchange(rt, &gateway, &netmask, n->rip_metric); - changes++; - rt->rt_timer = 0; + if (have_ripv1_out + && (v1_mask = ripv1_mask_net(dst,0)) > mask + && (((rt = rtget(dst,mask)) == 0 + || !(rt->rt_state & RS_NET_SYN)))) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + if (i >= 511) { + /* Punt if we would have to generate + * an unreasonable number of routes. + */ +#ifdef DEBUG + msglog("accept %s from %s as 1" + " instead of %d routes", + addrname(dst,mask,0), + naddr_ntoa(FROM_NADDR), + i+1); +#endif + i = 0; + } else { + mask = v1_mask; + } + } else { + i = 0; + } + + for (;;) { + input_route(ifp, FROM_NADDR, + dst, mask, gate, n); + if (i-- == 0) + break; + dst = htonl(ntohl(dst) + ddst_h); } } break; } +} + - /* - * If changes have occurred, and if we have not sent a broadcast - * recently, send a dynamic update. This update is sent only - * on interfaces other than the one on which we received notice - * of the change. If we are within MIN_WAITTIME of a full update, - * don't bother sending; if we just sent a dynamic update - * and set a timer (nextbcast), delay until that time. - * If we just sent a full update, delay the dynamic update. - * Set a timer for a randomized value to suppress additional - * dynamic updates until it expires; if we delayed sending - * the current changes, set needupdate. +/* Process a single input route. + */ +static void +input_route(struct interface *ifp, + naddr from, + naddr dst, + naddr mask, + naddr gate, + struct netinfo *n) +{ + int i; + struct rt_entry *rt; + struct rt_spare *rts, *rts0; + struct interface *ifp1; + time_t new_time; + + + /* See if the other guy is telling us to send our packets to him. + * Sometimes network routes arrive over a point-to-point link for + * the network containing the address(es) of the link. + * + * If our interface is broken, switch to using the other guy. */ - if (changes && supplier && - now.tv_sec - lastfullupdate.tv_sec < SUPPLY_INTERVAL-MAX_WAITTIME) { - u_long delay; - - if (now.tv_sec - lastbcast.tv_sec >= MIN_WAITTIME && - timercmp(&nextbcast, &now, <)) { - if (traceactions) - fprintf(ftrace, "send dynamic update\n"); - toall(supply, RTS_CHANGED, ifp); - lastbcast = now; - needupdate = 0; - nextbcast.tv_sec = 0; - } else { - needupdate++; - if (traceactions) - fprintf(ftrace, "delay dynamic update\n"); - } -#define RANDOMDELAY() (MIN_WAITTIME * 1000000 + \ - (u_long)random() % ((MAX_WAITTIME - MIN_WAITTIME) * 1000000)) - - if (nextbcast.tv_sec == 0) { - delay = RANDOMDELAY(); - if (traceactions) - fprintf(ftrace, - "inhibit dynamic update for %d usec\n", - delay); - nextbcast.tv_sec = delay / 1000000; - nextbcast.tv_usec = delay % 1000000; - timeradd(&nextbcast, &now, &nextbcast); - /* - * If the next possibly dynamic update - * is within MIN_WAITTIME of the next full update, - * force the delay past the full update, - * or we might send a dynamic update just before - * the full update. + ifp1 = ifwithaddr(dst, 1, 1); + if (ifp1 != 0 + && !(ifp1->int_state & IS_BROKE)) + return; + + /* Look for the route in our table. + */ + rt = rtget(dst, mask); + + /* Consider adding the route if we do not already have it. + */ + if (rt == 0) { + /* Ignore unknown routes being poisoned. + */ + if (n->n_metric == HOPCNT_INFINITY) + return; + + /* Ignore the route if it points to us */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + /* If something has not gone crazy and tried to fill + * our memory, accept the new route. + */ + if (total_routes < MAX_ROUTES) + rtadd(dst, mask, gate, from, n->n_metric, + n->n_tag, 0, ifp); + return; + } + + /* We already know about the route. Consider this update. + * + * If (rt->rt_state & RS_NET_SYN), then this route + * is the same as a network route we have inferred + * for subnets we know, in order to tell RIPv1 routers + * about the subnets. + * + * It is impossible to tell if the route is coming + * from a distant RIPv2 router with the standard + * netmask because that router knows about the entire + * network, or if it is a round-about echo of a + * synthetic, RIPv1 network route of our own. + * The worst is that both kinds of routes might be + * received, and the bad one might have the smaller + * metric. Partly solve this problem by faking the + * RIPv1 route with a metric that reflects the most + * distant part of the subnet. Also never + * aggregate into such a route. Also keep it + * around as long as the interface exists. + */ + + rts0 = rt->rt_spares; + for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) { + if (rts->rts_router == from) + break; + /* Note the worst slot to reuse, + * other than the current slot. + */ + if (rts0 == rt->rt_spares + || BETTER_LINK(rt, rts0, rts)) + rts0 = rts; + } + if (i != 0) { + /* Found the router + */ + int old_metric = rts->rts_metric; + + /* Keep poisoned routes around only long enough to pass + * the poison on. Get a new timestamp for good routes. + */ + new_time =((old_metric == HOPCNT_INFINITY) + ? rts->rts_time + : now.tv_sec); + + /* If this is an update for the router we currently prefer, + * then note it. + */ + if (i == NUM_SPARES) { + rtchange(rt,rt->rt_state, gate,rt->rt_router, + n->n_metric, n->n_tag, ifp, new_time, 0); + /* If the route got worse, check for something better. */ - if (nextbcast.tv_sec > lastfullupdate.tv_sec + - SUPPLY_INTERVAL - MIN_WAITTIME) - nextbcast.tv_sec = lastfullupdate.tv_sec + - SUPPLY_INTERVAL + 1; + if (n->n_metric > old_metric) + rtswitch(rt, 0); + return; + } + + /* This is an update for a spare route. + * Finished if the route is unchanged. + */ + if (rts->rts_gate == gate + && old_metric == n->n_metric + && rts->rts_tag == n->n_tag) { + rts->rts_time = new_time; + return; } + + } else { + /* The update is for a route we know about, + * but not from a familiar router. + * + * Ignore the route if it points to us. + */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + rts = rts0; + + /* Save the route as a spare only if it has + * a better metric than our worst spare. + * This also ignores poisoned routes (those + * received with metric HOPCNT_INFINITY). + */ + if (n->n_metric >= rts->rts_metric) + return; + + new_time = now.tv_sec; } + + trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time); + + rts->rts_gate = gate; + rts->rts_router = from; + rts->rts_metric = n->n_metric; + rts->rts_tag = n->n_tag; + rts->rts_time = new_time; + rts->rts_ifp = ifp; + + /* try to switch to a better route */ + rtswitch(rt, rts); } |