diff options
author | markm <markm@FreeBSD.org> | 1999-05-02 13:14:16 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 1999-05-02 13:14:16 +0000 |
commit | c74fc4e83b599157005f6adfcfebde46851bd0d4 (patch) | |
tree | e8f3c95ddb4dcfbbc332816196635e042e72873b /sbin/routed/table.c | |
parent | a60c9cfa3a8359247f7ca33b3ebc8574c5c7194a (diff) | |
download | FreeBSD-src-c74fc4e83b599157005f6adfcfebde46851bd0d4.zip FreeBSD-src-c74fc4e83b599157005f6adfcfebde46851bd0d4.tar.gz |
Merge-o-matic and add a bunch of $Id's
Diffstat (limited to 'sbin/routed/table.c')
-rw-r--r-- | sbin/routed/table.c | 641 |
1 files changed, 389 insertions, 252 deletions
diff --git a/sbin/routed/table.c b/sbin/routed/table.c index 1d95b12..fa01db3 100644 --- a/sbin/routed/table.c +++ b/sbin/routed/table.c @@ -11,7 +11,7 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: + * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors @@ -29,19 +29,28 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; -#endif -static const char rcsid[] = - "$Id$"; -#endif /* not lint */ - #include "defs.h" +#if !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] __attribute__((unused)) = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +__RCSID("$NetBSD$"); +#endif +#ident "$Revision: 2.17 $" + static struct rt_spare *rts_better(struct rt_entry *); +static struct rt_spare rts_empty = {0,0,0,HOPCNT_INFINITY,0,0,0}; +static void set_need_flash(void); +#ifdef _HAVE_SIN_LEN +static void masktrim(struct sockaddr_in *ap); +#else +static void masktrim(struct sockaddr_in_new *ap); +#endif + struct radix_node_head *rhead; /* root of the radix tree */ @@ -117,6 +126,10 @@ ag_out(struct ag_info *ag, naddr bit; + /* Forget it if this route should not be output for split-horizon. */ + if (ag->ag_state & AGS_SPLIT_HZ) + return; + /* If we output both the even and odd twins, then the immediate parent, * if it is present, is redundant, unless the parent manages to * aggregate into something coarser. @@ -142,12 +155,14 @@ ag_out(struct ag_info *ag, */ if (ag->ag_state & AGS_REDUN0) { if (ag->ag_state & AGS_REDUN1) - return; + return; /* quit if fully redundant */ + /* make it finer if it is half-redundant */ bit = (-ag->ag_mask) >> 1; ag->ag_dst_h |= bit; ag->ag_mask |= bit; } else if (ag->ag_state & AGS_REDUN1) { + /* make it finer if it is half-redundant */ bit = (-ag->ag_mask) >> 1; ag->ag_mask |= bit; } @@ -234,6 +249,8 @@ ag_flush(naddr lim_dst_h, /* flush routes to here */ /* If the coarse route has a good enough * metric, it suppresses the target. + * If the suppressed target was redundant, + * then mark the suppressor redundant. */ if (ag_cors->ag_pref <= ag->ag_pref) { if (ag_cors->ag_seqno > ag->ag_seqno) @@ -331,6 +348,9 @@ ag_check(naddr dst, || (ag_cors->ag_state & AGS_CORS_GATE))) { if (ag_cors->ag_seqno > ag->ag_seqno) ag_cors->ag_seqno = ag->ag_seqno; + /* If the suppressed target was redundant, + * then mark the suppressor redundant. + */ if (AG_IS_REDUN(ag->ag_state) && ag_cors->ag_mask==ag->ag_mask<<1) { if (ag_cors->ag_dst_h == dst) @@ -373,11 +393,11 @@ ag_check(naddr dst, * Routes are encountered in lexical order, so a * route is never promoted until the parent route is * already present. So we know that the new route is - * a promoted pair and the route already in the slot - * is the explicit route. + * a promoted (or aggregated) pair and the route + * already in the slot is the explicit route. * * Prefer the best route if their metrics differ, - * or the promoted one if not, following a sort + * or the aggregated one if not, following a sort * of longest-match rule. */ if (pref <= ag->ag_pref) { @@ -397,9 +417,13 @@ ag_check(naddr dst, if (ag->ag_seqno > seqno) ag->ag_seqno = seqno; - /* some bits are set if they are set on either route */ - ag->ag_state |= (state & (AGS_PROMOTE_EITHER - | AGS_REDUN0 | AGS_REDUN1)); + /* Some bits are set if they are set on either route, + * except when the route is for an interface. + */ + if (!(ag->ag_state & AGS_IF)) + ag->ag_state |= (state & (AGS_AGGREGATE_EITHER + | AGS_REDUN0 + | AGS_REDUN1)); return; } @@ -407,12 +431,12 @@ ag_check(naddr dst, * be suppressed, it may be possible to combine them or * worthwhile to promote one. * - * Note that any route that can be promoted is always + * Any route that can be promoted is always * marked to be eligible to be suppressed. */ - if (!((state & AGS_PROMOTE) + if (!((state & AGS_AGGREGATE) && (ag->ag_state & AGS_SUPPRESS)) - && !((ag->ag_state & AGS_PROMOTE) + && !((ag->ag_state & AGS_AGGREGATE) && (state & AGS_SUPPRESS))) break; @@ -424,13 +448,13 @@ ag_check(naddr dst, || AG_IS_REDUN(state) || (ag->ag_gate == gate && ag->ag_pref == pref - && (state & ag->ag_state & AGS_PROMOTE) != 0)) { + && (state & ag->ag_state & AGS_AGGREGATE) != 0)) { /* We have both the even and odd pairs. * Since the routes are encountered in order, * the route in the slot must be the even twin. * - * Combine and promote the pair of routes. + * Combine and promote (aggregate) the pair of routes. */ if (seqno > ag->ag_seqno) seqno = ag->ag_seqno; @@ -440,7 +464,7 @@ ag_check(naddr dst, state |= AGS_REDUN0; else state &= ~AGS_REDUN0; - state |= (ag->ag_state & AGS_PROMOTE_EITHER); + state |= (ag->ag_state & AGS_AGGREGATE_EITHER); if (ag->ag_tag != tag) tag = 0; if (ag->ag_nhop != nhop) @@ -452,7 +476,7 @@ ag_check(naddr dst, ag_del(ag); } else if (ag->ag_pref >= pref - && (ag->ag_state & AGS_PROMOTE)) { + && (ag->ag_state & AGS_AGGREGATE)) { /* If we cannot combine the pair, maybe the route * with the worse metric can be promoted. * @@ -473,11 +497,17 @@ ag_check(naddr dst, ag->ag_tag = tag; tag = x; + /* The promoted route is even-redundant only if the + * even twin was fully redundant. It is not + * odd-redundant because the odd-twin will still be + * in the table. + */ x = ag->ag_state; + if (!AG_IS_REDUN(x)) + x &= ~AGS_REDUN0; + x &= ~AGS_REDUN1; ag->ag_state = state; state = x; - if (!AG_IS_REDUN(state)) - state &= ~AGS_REDUN0; x = ag->ag_metric; ag->ag_metric = metric; @@ -487,24 +517,30 @@ ag_check(naddr dst, ag->ag_pref = pref; pref = x; + /* take the newest sequence number */ if (seqno >= ag->ag_seqno) seqno = ag->ag_seqno; else ag->ag_seqno = seqno; } else { - if (!(state & AGS_PROMOTE)) + if (!(state & AGS_AGGREGATE)) break; /* cannot promote either twin */ - /* promote the new, odd twin by shaving its + /* Promote the new, odd twin by shaving its * mask and address. + * The promoted route is odd-redundant only if the + * odd twin was fully redundant. It is not + * even-redundant because the even twin is still in + * the table. */ + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN1; + state &= ~AGS_REDUN0; if (seqno > ag->ag_seqno) seqno = ag->ag_seqno; else ag->ag_seqno = seqno; - if (!AG_IS_REDUN(state)) - state &= ~AGS_REDUN1; } mask <<= 1; @@ -581,10 +617,10 @@ ag_check(naddr dst, } -static char * +static const char * rtm_type_name(u_char type) { - static char *rtm_types[] = { + static const char *rtm_types[] = { "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", @@ -614,8 +650,8 @@ rtm_type_name(u_char type) /* Trim a mask in a sockaddr - * Produce the index of the first zero byte. - * i.e. Produce a index of 4 for an mask of 0. (default route) + * Produce a length of 0 for an address of 0. + * Otherwise produce the index of the first zero byte. */ void #ifdef _HAVE_SIN_LEN @@ -624,13 +660,15 @@ masktrim(struct sockaddr_in *ap) masktrim(struct sockaddr_in_new *ap) #endif { - register char *cp; + char *cp; - ap->sin_port = 0xffff; /* buffer zone for default route */ + if (ap->sin_addr.s_addr == 0) { + ap->sin_len = 0; + return; + } cp = (char *)(&ap->sin_addr.s_addr+1); while (*--cp == 0) continue; - /*ap->sin_port = 0x0;*/ /* may not be needed (who cares?)*/ ap->sin_len = cp - (char*)ap + 1; } @@ -656,16 +694,18 @@ rtioctl(int action, /* RTM_DELETE, etc */ #endif } w; long cc; +# define PAT " %-10s %s metric=%d flags=%#x" +# define ARGS rtm_type_name(action), rtname(dst,mask,gate), metric, flags again: - bzero(&w, sizeof(w)); + memset(&w, 0, sizeof(w)); w.w_rtm.rtm_msglen = sizeof(w); w.w_rtm.rtm_version = RTM_VERSION; w.w_rtm.rtm_type = action; w.w_rtm.rtm_flags = flags; w.w_rtm.rtm_seq = ++rt_sock_seqno; w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; - if (metric != 0) { + if (metric != 0 || action == RTM_CHANGE) { w.w_rtm.rtm_rmx.rmx_hopcount = metric; w.w_rtm.rtm_inits |= RTV_HOPCOUNT; } @@ -685,41 +725,36 @@ again: w.w_mask.sin_addr.s_addr = htonl(mask); #ifdef _HAVE_SA_LEN masktrim(&w.w_mask); + if (w.w_mask.sin_len == 0) + w.w_mask.sin_len = sizeof(long); w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len); #endif } - if (TRACEKERNEL) - trace_kernel("write kernel %s %s->%s metric=%d flags=%#x\n", - rtm_type_name(action), - addrname(dst, mask, 0), naddr_ntoa(gate), - metric, flags); - #ifndef NO_INSTALL cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); - if (cc == w.w_rtm.rtm_msglen) - return; if (cc < 0) { if (errno == ESRCH && (action == RTM_CHANGE || action == RTM_DELETE)) { - trace_act("route to %s disappeared before %s", - addrname(dst, mask, 0), - rtm_type_name(action)); + trace_act("route disappeared before" PAT, ARGS); if (action == RTM_CHANGE) { action = RTM_ADD; goto again; } return; } - msglog("write(rt_sock) %s %s --> %s: %s", - rtm_type_name(action), - addrname(dst, mask, 0), naddr_ntoa(gate), - strerror(errno)); - } else { - msglog("write(rt_sock) wrote %d instead of %d", - cc, w.w_rtm.rtm_msglen); + msglog("write(rt_sock)" PAT ": %s", ARGS, strerror(errno)); + return; + } else if (cc != w.w_rtm.rtm_msglen) { + msglog("write(rt_sock) wrote %ld instead of %d for" PAT, + cc, w.w_rtm.rtm_msglen, ARGS); + return; } #endif + if (TRACEKERNEL) + trace_misc("write kernel" PAT, ARGS); +#undef PAT +#undef ARGS } @@ -733,14 +768,15 @@ static struct khash { short k_metric; u_short k_state; #define KS_NEW 0x001 -#define KS_DELETE 0x002 +#define KS_DELETE 0x002 /* need to delete the route */ #define KS_ADD 0x004 /* add to the kernel */ #define KS_CHANGE 0x008 /* tell kernel to change the route */ #define KS_DEL_ADD 0x010 /* delete & add to change the kernel */ #define KS_STATIC 0x020 /* Static flag in kernel */ #define KS_GATEWAY 0x040 /* G flag in kernel */ #define KS_DYNAMIC 0x080 /* result of redirect */ -#define KS_DELETED 0x100 /* already deleted */ +#define KS_DELETED 0x100 /* already deleted from kernel */ +#define KS_CHECK 0x200 time_t k_keep; #define K_KEEP_LIM 30 time_t k_redirect_time; /* when redirected route 1st seen */ @@ -773,7 +809,7 @@ kern_add(naddr dst, naddr mask) k = (struct khash *)rtmalloc(sizeof(*k), "kern_add"); - bzero(k, sizeof(*k)); + memset(k, 0, sizeof(*k)); k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; @@ -792,26 +828,58 @@ kern_check_static(struct khash *k, struct interface *ifp) { struct rt_entry *rt; - naddr int_addr; + struct rt_spare new; if (k->k_metric == 0) return; - int_addr = (ifp != 0) ? ifp->int_addr : loopaddr; + memset(&new, 0, sizeof(new)); + new.rts_ifp = ifp; + new.rts_gate = k->k_gate; + new.rts_router = (ifp != 0) ? ifp->int_addr : loopaddr; + new.rts_metric = k->k_metric; + new.rts_time = now.tv_sec; rt = rtget(k->k_dst, k->k_mask); if (rt != 0) { if (!(rt->rt_state & RS_STATIC)) - rtchange(rt, rt->rt_state | RS_STATIC, - k->k_gate, int_addr, - k->k_metric, 0, ifp, now.tv_sec, 0); + rtchange(rt, rt->rt_state | RS_STATIC, &new, 0); } else { - rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr, - k->k_metric, 0, RS_STATIC, ifp); + rtadd(k->k_dst, k->k_mask, RS_STATIC, &new); } } +/* operate on a kernel entry + */ +static void +kern_ioctl(struct khash *k, + int action, /* RTM_DELETE, etc */ + int flags) + +{ + switch (action) { + case RTM_DELETE: + k->k_state &= ~KS_DYNAMIC; + if (k->k_state & KS_DELETED) + return; + k->k_state |= KS_DELETED; + break; + case RTM_ADD: + k->k_state &= ~KS_DELETED; + break; + case RTM_CHANGE: + if (k->k_state & KS_DELETED) { + action = RTM_ADD; + k->k_state &= ~KS_DELETED; + } + break; + } + + rtioctl(action, k->k_dst, k->k_gate, k->k_mask, k->k_metric, flags); +} + + /* add a route the kernel told us */ static void @@ -833,23 +901,29 @@ rtm_add(struct rt_msghdr *rtm, return; } - if (INFO_GATE(info) == 0 - || INFO_GATE(info)->sa_family != AF_INET) { - msglog("ignore %s without gateway", - rtm_type_name(rtm->rtm_type)); - return; - } - k = kern_add(S_ADDR(INFO_DST(info)), mask); if (k->k_state & KS_NEW) k->k_keep = now.tv_sec+keep; - k->k_gate = S_ADDR(INFO_GATE(info)); - k->k_metric = rtm->rtm_rmx.rmx_hopcount; - if (k->k_metric < 0) - k->k_metric = 0; - else if (k->k_metric > HOPCNT_INFINITY) - k->k_metric = HOPCNT_INFINITY; - k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW); + if (INFO_GATE(info) == 0) { + trace_act("note %s without gateway", + rtm_type_name(rtm->rtm_type)); + k->k_metric = HOPCNT_INFINITY; + } else if (INFO_GATE(info)->sa_family != AF_INET) { + trace_act("note %s with gateway AF=%d", + rtm_type_name(rtm->rtm_type), + INFO_GATE(info)->sa_family); + k->k_metric = HOPCNT_INFINITY; + } else { + k->k_gate = S_ADDR(INFO_GATE(info)); + k->k_metric = rtm->rtm_rmx.rmx_hopcount; + if (k->k_metric < 0) + k->k_metric = 0; + else if (k->k_metric > HOPCNT_INFINITY-1) + k->k_metric = HOPCNT_INFINITY-1; + } + k->k_state &= ~(KS_DELETE | KS_ADD | KS_CHANGE | KS_DEL_ADD + | KS_DELETED | KS_GATEWAY | KS_STATIC + | KS_NEW | KS_CHECK); if (rtm->rtm_flags & RTF_GATEWAY) k->k_state |= KS_GATEWAY; if (rtm->rtm_flags & RTF_STATIC) @@ -923,44 +997,104 @@ rtm_lose(struct rt_msghdr *rtm, return; } - if (!supplier) + if (rdisc_ok) rdisc_age(S_ADDR(INFO_GATE(info))); - age(S_ADDR(INFO_GATE(info))); } +/* Make the gateway slot of an info structure point to something + * useful. If it is not already useful, but it specifies an interface, + * then fill in the sockaddr_in provided and point it there. + */ +static int +get_info_gate(struct sockaddr **sap, + struct sockaddr_in *sin) +{ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)*sap; + struct interface *ifp; + + if (sdl == 0) + return 0; + if ((sdl)->sdl_family == AF_INET) + return 1; + if ((sdl)->sdl_family != AF_LINK) + return 0; + + ifp = ifwithindex(sdl->sdl_index, 1); + if (ifp == 0) + return 0; + + sin->sin_addr.s_addr = ifp->int_addr; +#ifdef _HAVE_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + *sap = (struct sockaddr*)sin; + + return 1; +} + + /* Clean the kernel table by copying it to the daemon image. * Eventually the daemon will delete any extra routes. */ void flush_kern(void) { + static char *sysctl_buf; + static size_t sysctl_buf_size = 0; size_t needed; int mib[6]; - char *buf, *next, *lim; + char *next, *lim; struct rt_msghdr *rtm; - struct interface *ifp; - static struct sockaddr_in gate_sa; + struct sockaddr_in gate_sin; struct rt_addrinfo info; + int i; + struct khash *k; + for (i = 0; i < KHASH_SIZE; i++) { + for (k = khash_bins[i]; k != 0; k = k->k_next) { + k->k_state |= KS_CHECK; + } + } + mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = 0; /* wildcard address family */ mib[4] = NET_RT_DUMP; mib[5] = 0; /* no flags */ - if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) { - DBGERR(1,"RT_DUMP-sysctl-estimate"); - return; + for (;;) { + if ((needed = sysctl_buf_size) != 0) { + if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) + break; + if (errno != ENOMEM && errno != EFAULT) + BADERR(1,"flush_kern: sysctl(RT_DUMP)"); + free(sysctl_buf); + needed = 0; + } + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) + BADERR(1,"flush_kern: sysctl(RT_DUMP) estimate"); + /* Kludge around the habit of some systems, such as + * BSD/OS 3.1, to not admit how many routes are in the + * kernel, or at least to be quite wrong. + */ + needed += 50*(sizeof(*rtm)+5*sizeof(struct sockaddr)); + sysctl_buf = rtmalloc(sysctl_buf_size = needed, + "flush_kern sysctl(RT_DUMP)"); } - buf = (char *)rtmalloc(needed, "flush_kern"); - if (sysctl(mib, 6, buf, &needed, 0, 0) < 0) - BADERR(1,"RT_DUMP"); - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { + + lim = sysctl_buf + needed; + for (next = sysctl_buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; + if (rtm->rtm_msglen == 0) { + msglog("zero length kernel route at " + " %#lx in buffer %#lx before %#lx", + (u_long)rtm, (u_long)sysctl_buf, (u_long)lim); + break; + } rt_xaddrs(&info, (struct sockaddr *)(rtm+1), @@ -977,32 +1111,14 @@ flush_kern(void) if (rtm->rtm_flags & RTF_LLINFO) continue; - if (INFO_GATE(&info) == 0) - continue; - if (INFO_GATE(&info)->sa_family != AF_INET) { - if (INFO_GATE(&info)->sa_family != AF_LINK) - continue; - ifp = ifwithindex(((struct sockaddr_dl *) - INFO_GATE(&info))->sdl_index); - if (ifp == 0) - continue; - if ((ifp->int_if_flags & IFF_POINTOPOINT) - || S_ADDR(INFO_DST(&info)) == ifp->int_addr) - gate_sa.sin_addr.s_addr = ifp->int_addr; - else - gate_sa.sin_addr.s_addr = htonl(ifp->int_net); -#ifdef _HAVE_SA_LEN - gate_sa.sin_len = sizeof(gate_sa); -#endif - gate_sa.sin_family = AF_INET; - INFO_GATE(&info) = (struct sockaddr *)&gate_sa; - } - /* ignore multicast addresses */ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) continue; + if (!get_info_gate(&INFO_GATE(&info), &gate_sin)) + continue; + /* Note static routes and interface routes, and also * preload the image of the kernel table so that * we can later clean it, as well as avoid making @@ -1012,7 +1128,17 @@ flush_kern(void) */ rtm_add(rtm,&info,MIN_WAITTIME); } - free(buf); + + for (i = 0; i < KHASH_SIZE; i++) { + for (k = khash_bins[i]; k != 0; k = k->k_next) { + if (k->k_state & KS_CHECK) { + msglog("%s --> %s disappeared from kernel", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + del_static(k->k_dst, k->k_mask, k->k_gate, 1); + } + } + } } @@ -1023,7 +1149,8 @@ read_rt(void) { long cc; struct interface *ifp; - naddr mask; + struct sockaddr_in gate_sin; + naddr mask, gate; union { struct { struct rt_msghdr rtm; @@ -1064,10 +1191,11 @@ read_rt(void) if (m.r.rtm.rtm_type == RTM_IFINFO || m.r.rtm.rtm_type == RTM_NEWADDR || m.r.rtm.rtm_type == RTM_DELADDR) { - ifp = ifwithindex(m.ifm.ifm_index); + ifp = ifwithindex(m.ifm.ifm_index, + m.r.rtm.rtm_type != RTM_DELADDR); if (ifp == 0) trace_act("note %s with flags %#x" - " for index #%d", + " for unknown interface index #%d", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, m.ifm.ifm_index); @@ -1086,7 +1214,7 @@ read_rt(void) if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL || ifp == 0 || ((ifp->int_if_flags ^ m.ifm.ifm_flags) - & IFF_UP_RUNNING) != 0) + & IFF_UP) != 0) ifinit_timer.tv_sec = now.tv_sec; continue; } @@ -1124,10 +1252,17 @@ read_rt(void) continue; } - if (INFO_GATE(&info) != 0 - && INFO_GATE(&info)->sa_family == AF_INET) - strp += sprintf(strp, " --> %s", - saddr_ntoa(INFO_GATE(&info))); + if (m.r.rtm.rtm_flags & RTF_LLINFO) { + trace_act("ignore ARP %s", str); + continue; + } + + if (get_info_gate(&INFO_GATE(&info), &gate_sin)) { + gate = S_ADDR(INFO_GATE(&info)); + strp += sprintf(strp, " --> %s", naddr_ntoa(gate)); + } else { + gate = 0; + } if (INFO_AUTHOR(&info) != 0) strp += sprintf(strp, " by authority of %s", @@ -1147,12 +1282,14 @@ read_rt(void) break; case RTM_DELETE: - if (m.r.rtm.rtm_errno != 0) { + if (m.r.rtm.rtm_errno != 0 + && m.r.rtm.rtm_errno != ESRCH) { trace_act("ignore %s with \"%s\" error", str, strerror(m.r.rtm.rtm_errno)); } else { trace_act("%s", str); - del_static(S_ADDR(INFO_DST(&info)), mask, 1); + del_static(S_ADDR(INFO_DST(&info)), mask, + gate, 1); } break; @@ -1205,12 +1342,20 @@ kern_out(struct ag_info *ag) /* modify existing kernel entry if necessary */ if (k->k_gate != ag->ag_gate || k->k_metric != ag->ag_metric) { + /* Must delete bad interface routes etc. to change them. */ + if (k->k_metric == HOPCNT_INFINITY) + k->k_state |= KS_DEL_ADD; k->k_gate = ag->ag_gate; k->k_metric = ag->ag_metric; k->k_state |= KS_CHANGE; } - if (k->k_state & KS_DYNAMIC) { + /* If the daemon thinks the route should exist, forget + * about any redirections. + * If the daemon thinks the route should exist, eventually + * override manual intervention by the operator. + */ + if ((k->k_state & (KS_DYNAMIC | KS_DELETED)) != 0) { k->k_state &= ~KS_DYNAMIC; k->k_state |= (KS_ADD | KS_DEL_ADD); } @@ -1241,7 +1386,7 @@ kern_out(struct ag_info *ag) /* ARGSUSED */ static int walk_kern(struct radix_node *rn, - struct walkarg *w) + struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) char metric, pref; @@ -1253,15 +1398,33 @@ walk_kern(struct radix_node *rn, return 0; if (!(RT->rt_state & RS_IF)) { - ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + /* This is an ordinary route, not for an interface. + */ + + /* aggregate, ordinary good routes without regard to + * their metric + */ + pref = 1; + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE); + + /* Do not install host routes directly to hosts, to avoid + * interfering with ARP entries in the kernel table. + */ + if (RT_ISHOST(RT) + && ntohl(RT->rt_dst) == RT->rt_gate) + return 0; } else { - /* Do not install routes for "external" remote interfaces. + /* This is an interface route. + * Do not install routes for "external" remote interfaces. */ if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL)) return 0; - ags |= AGS_IF; + /* Interfaces should override received routes. + */ + pref = 0; + ags |= (AGS_IF | AGS_CORS_GATE); /* If it is not an interface, or an alias for an interface, * it must be a "gateway." @@ -1271,19 +1434,23 @@ walk_kern(struct radix_node *rn, */ if (RT->rt_ifp == 0 || (RT->rt_ifp->int_state & IS_REMOTE)) - ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE); } - if (RT->rt_state & RS_RDISC) + /* If RIP is off and IRDP is on, let the route to the discovered + * route suppress any RIP routes. Eventually the RIP routes + * will time-out and be deleted. This reaches the steady-state + * quicker. + */ + if ((RT->rt_state & RS_RDISC) && rip_sock < 0) ags |= AGS_CORS_GATE; - /* aggregate good routes without regard to their metric */ - pref = 1; metric = RT->rt_metric; if (metric == HOPCNT_INFINITY) { /* if the route is dead, so try hard to aggregate. */ pref = HOPCNT_INFINITY; ags |= (AGS_FINE_GATE | AGS_SUPPRESS); + ags &= ~(AGS_IF | AGS_CORS_GATE); } ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, @@ -1298,7 +1465,7 @@ walk_kern(struct radix_node *rn, static void fix_kern(void) { - int i, flags; + int i; struct khash *k, **pk; @@ -1320,46 +1487,37 @@ fix_kern(void) /* check hold on routes deleted by the operator */ if (k->k_keep > now.tv_sec) { + /* ensure we check when the hold is over */ LIM_SEC(need_kern, k->k_keep); + /* mark for the next cycle */ k->k_state |= KS_DELETE; pk = &k->k_next; continue; } - if ((k->k_state & (KS_DELETE | KS_DYNAMIC)) - == KS_DELETE) { - if (!(k->k_state & KS_DELETED)) - rtioctl(RTM_DELETE, - k->k_dst, k->k_gate, k->k_mask, - 0, 0); + if ((k->k_state & KS_DELETE) + && !(k->k_state & KS_DYNAMIC)) { + kern_ioctl(k, RTM_DELETE, 0); *pk = k->k_next; free(k); continue; } - if (0 != (k->k_state&(KS_ADD|KS_CHANGE|KS_DEL_ADD))) { - if (k->k_state & KS_DEL_ADD) { - rtioctl(RTM_DELETE, - k->k_dst,k->k_gate,k->k_mask, - 0, 0); - k->k_state &= ~KS_DYNAMIC; - } - - flags = 0; - if (0 != (k->k_state&(KS_GATEWAY|KS_DYNAMIC))) - flags |= RTF_GATEWAY; - - if (k->k_state & KS_ADD) { - rtioctl(RTM_ADD, - k->k_dst, k->k_gate, k->k_mask, - k->k_metric, flags); - } else if (k->k_state & KS_CHANGE) { - rtioctl(RTM_CHANGE, - k->k_dst,k->k_gate,k->k_mask, - k->k_metric, flags); - } - k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); + if (k->k_state & KS_DEL_ADD) + kern_ioctl(k, RTM_DELETE, 0); + + if (k->k_state & KS_ADD) { + kern_ioctl(k, RTM_ADD, + ((0 != (k->k_state & (KS_GATEWAY + | KS_DYNAMIC))) + ? RTF_GATEWAY : 0)); + } else if (k->k_state & KS_CHANGE) { + kern_ioctl(k, RTM_CHANGE, + ((0 != (k->k_state & (KS_GATEWAY + | KS_DYNAMIC))) + ? RTF_GATEWAY : 0)); } + k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); /* Mark this route to be deleted in the next cycle. * This deletes routes that disappear from the @@ -1379,6 +1537,7 @@ fix_kern(void) void del_static(naddr dst, naddr mask, + naddr gate, int gone) { struct khash *k; @@ -1392,8 +1551,8 @@ del_static(naddr dst, * and add a replacement. */ k = kern_find(dst, mask, 0); - if (k != 0) { - k->k_state &= ~(KS_STATIC | KS_DYNAMIC); + if (k != 0 && (gate == 0 || k->k_gate == gate)) { + k->k_state &= ~(KS_STATIC | KS_DYNAMIC | KS_CHECK); k->k_state |= KS_DELETE; if (gone) { k->k_state |= KS_DELETED; @@ -1442,10 +1601,11 @@ del_redirects(naddr bad_gate, /* Start the daemon tables. */ +extern int max_keylen; + void rtinit(void) { - extern int max_keylen; int i; struct ag_info *ag; @@ -1472,7 +1632,7 @@ static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; #endif -void +static void set_need_flash(void) { if (!need_flash) { @@ -1520,12 +1680,8 @@ rtfind(naddr dst) void rtadd(naddr dst, naddr mask, - naddr gate, /* forward packets here */ - naddr router, /* on the authority of this router */ - int metric, - u_short tag, - u_int state, /* rs_state for the entry */ - struct interface *ifp) + u_int state, /* rt_state for the entry */ + struct rt_spare *new) { struct rt_entry *rt; naddr smask; @@ -1533,7 +1689,7 @@ rtadd(naddr dst, struct rt_spare *rts; rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); - bzero(rt, sizeof(*rt)); + memset(rt, 0, sizeof(*rt)); for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) rts->rts_metric = HOPCNT_INFINITY; @@ -1552,13 +1708,9 @@ rtadd(naddr dst, masktrim(&mask_sock); rt->rt_mask = mask; rt->rt_state = state; - rt->rt_gate = gate; - rt->rt_router = router; + rt->rt_spares[0] = *new; rt->rt_time = now.tv_sec; - rt->rt_metric = metric; rt->rt_poison_metric = HOPCNT_INFINITY; - rt->rt_tag = tag; - rt->rt_ifp = ifp; rt->rt_seqno = update_seqno; if (++total_routes == MAX_ROUTES) @@ -1571,15 +1723,8 @@ rtadd(naddr dst, if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, rhead, rt->rt_nodes)) { -/* - * This will happen if RIP1 and RIP2 routeds talk to one another and - * there are variable subnets. This is only good for filling up your - * syslog. -jkh - */ -#if 0 - msglog("rnh_addaddr() failed for %s mask=%#x", - naddr_ntoa(dst), mask); -#endif + msglog("rnh_addaddr() failed for %s mask=%#lx", + naddr_ntoa(dst), (u_long)mask); } } @@ -1589,29 +1734,24 @@ rtadd(naddr dst, void rtchange(struct rt_entry *rt, u_int state, /* new state bits */ - naddr gate, /* now forward packets here */ - naddr router, /* on the authority of this router */ - int metric, /* new metric */ - u_short tag, - struct interface *ifp, - time_t new_time, + struct rt_spare *new, char *label) { - if (rt->rt_metric != metric) { + if (rt->rt_metric != new->rts_metric) { /* Fix the kernel immediately if it seems the route * has gone bad, since there may be a working route that * aggregates this route. */ - if (metric == HOPCNT_INFINITY) { + if (new->rts_metric == HOPCNT_INFINITY) { need_kern.tv_sec = now.tv_sec; - if (new_time >= now.tv_sec - EXPIRE_TIME) - new_time = now.tv_sec - EXPIRE_TIME; + if (new->rts_time >= now.tv_sec - EXPIRE_TIME) + new->rts_time = now.tv_sec - EXPIRE_TIME; } rt->rt_seqno = update_seqno; set_need_flash(); } - if (rt->rt_gate != gate) { + if (rt->rt_gate != new->rts_gate) { need_kern.tv_sec = now.tv_sec; rt->rt_seqno = update_seqno; set_need_flash(); @@ -1621,21 +1761,15 @@ rtchange(struct rt_entry *rt, /* Keep various things from deciding ageless routes are stale. */ - if (!AGE_RT(state, ifp)) - new_time = now.tv_sec; + if (!AGE_RT(state, new->rts_ifp)) + new->rts_time = now.tv_sec; if (TRACEACTIONS) - trace_change(rt, state, gate, router, metric, tag, ifp, - new_time, + trace_change(rt, state, new, label ? label : "Chg "); rt->rt_state = state; - rt->rt_gate = gate; - rt->rt_router = router; - rt->rt_metric = metric; - rt->rt_tag = tag; - rt->rt_ifp = ifp; - rt->rt_time = new_time; + rt->rt_spares[0] = *new; } @@ -1683,11 +1817,13 @@ rtswitch(struct rt_entry *rt, return; swap = rt->rt_spares[0]; - (void)sprintf(label, "Use #%d", rts - rt->rt_spares); - rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), - rts->rts_gate, rts->rts_router, rts->rts_metric, - rts->rts_tag, rts->rts_ifp, rts->rts_time, label); - *rts = swap; + (void)sprintf(label, "Use #%d", (int)(rts - rt->rt_spares)); + rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), rts, label); + if (swap.rts_metric == HOPCNT_INFINITY) { + *rts = rts_empty; + } else { + *rts = swap; + } } @@ -1719,16 +1855,26 @@ rtdelete(struct rt_entry *rt) } +void +rts_delete(struct rt_entry *rt, + struct rt_spare *rts) +{ + trace_upslot(rt, rts, &rts_empty); + *rts = rts_empty; +} + + /* Get rid of a bad route, and try to switch to a replacement. */ void rtbad(struct rt_entry *rt) { - /* Poison the route */ - rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), - rt->rt_gate, rt->rt_router, HOPCNT_INFINITY, rt->rt_tag, - 0, rt->rt_time, 0); + struct rt_spare new; + /* Poison the route */ + new = rt->rt_spares[0]; + new.rts_metric = HOPCNT_INFINITY; + rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), &new, 0); rtswitch(rt, 0); } @@ -1793,10 +1939,10 @@ rtbad_sub(struct rt_entry *rt) } if (ifp1 != 0 || (state & RS_NET_SYN)) { - rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) - | state), - rt->rt_gate, rt->rt_router, rt->rt_metric, - rt->rt_tag, ifp1, rt->rt_time, 0); + struct rt_spare new = rt->rt_spares[0]; + new.rts_ifp = ifp1; + rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN|RS_LOCAL)) | state), + &new, 0); } else { rtbad(rt); } @@ -1809,12 +1955,11 @@ rtbad_sub(struct rt_entry *rt) /* ARGSUSED */ int walk_bad(struct radix_node *rn, - struct walkarg *w) + struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct rt_spare *rts; int i; - time_t new_time; /* fix any spare routes through the interface @@ -1822,21 +1967,10 @@ walk_bad(struct radix_node *rn, rts = RT->rt_spares; for (i = NUM_SPARES; i != 1; i--) { rts++; - - if (rts->rts_ifp != 0 - && (rts->rts_ifp->int_state & IS_BROKE)) { - /* mark the spare route to be deleted immediately */ - new_time = rts->rts_time; - if (new_time >= now_garbage) - new_time = now_garbage-1; - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, 0, - HOPCNT_INFINITY, rts->rts_tag, - new_time); - rts->rts_ifp = 0; - rts->rts_metric = HOPCNT_INFINITY; - rts->rts_time = new_time; - } + if (rts->rts_metric < HOPCNT_INFINITY + && (rts->rts_ifp == 0 + || (rts->rts_ifp->int_state & IS_BROKE))) + rts_delete(RT, rts); } /* Deal with the main route @@ -1864,7 +1998,7 @@ walk_bad(struct radix_node *rn, /* ARGSUSED */ static int walk_age(struct radix_node *rn, - struct walkarg *w) + struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct interface *ifp; @@ -1905,13 +2039,9 @@ walk_age(struct radix_node *rn, /* trash the spare routes when they go bad */ if (rts->rts_metric < HOPCNT_INFINITY - && now_garbage > rts->rts_time) { - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, rts->rts_ifp, - HOPCNT_INFINITY, rts->rts_tag, - rts->rts_time); - rts->rts_metric = HOPCNT_INFINITY; - } + && now_garbage > rts->rts_time + && i != NUM_SPARES) + rts_delete(RT, rts); } @@ -1929,10 +2059,11 @@ walk_age(struct radix_node *rn, } /* Start poisoning a bad route before deleting it. */ - if (now.tv_sec - RT->rt_time > EXPIRE_TIME) - rtchange(RT, RT->rt_state, RT->rt_gate, RT->rt_router, - HOPCNT_INFINITY, RT->rt_tag, RT->rt_ifp, - RT->rt_time, 0); + if (now.tv_sec - RT->rt_time > EXPIRE_TIME) { + struct rt_spare new = RT->rt_spares[0]; + new.rts_metric = HOPCNT_INFINITY; + rtchange(RT, RT->rt_state, &new, 0); + } return 0; } @@ -1961,6 +2092,7 @@ age(naddr bad_gate) /* ignore unreachable remote interfaces */ if (!check_remote(ifp)) continue; + /* Restore remote interface that has become reachable */ if (ifp->int_state & IS_BROKE) @@ -1969,7 +2101,7 @@ age(naddr bad_gate) if (ifp->int_act_time != NEVER && now.tv_sec - ifp->int_act_time > EXPIRE_TIME) { msglog("remote interface %s to %s timed out after" - " %d:%d", + " %ld:%ld", ifp->int_name, naddr_ntoa(ifp->int_dstaddr), (now.tv_sec - ifp->int_act_time)/60, @@ -1990,6 +2122,11 @@ age(naddr bad_gate) age_bad_gate = bad_gate; (void)rn_walktree(rhead, walk_age, 0); + /* delete old redirected routes to keep the kernel table small + * and prevent blackholes + */ + del_redirects(bad_gate, now.tv_sec-STALE_TIME); + /* Update the kernel routing table. */ fix_kern(); |