diff options
Diffstat (limited to 'usr.sbin/routed/table.c')
-rw-r--r-- | usr.sbin/routed/table.c | 764 |
1 files changed, 401 insertions, 363 deletions
diff --git a/usr.sbin/routed/table.c b/usr.sbin/routed/table.c index 9c8b741..c181be4 100644 --- a/usr.sbin/routed/table.c +++ b/usr.sbin/routed/table.c @@ -31,14 +31,13 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1.3.1 $" - #include "defs.h" +static struct rt_spare *rts_better(struct rt_entry *); struct radix_node_head *rhead; /* root of the radix tree */ @@ -111,10 +110,11 @@ ag_out(struct ag_info *ag, naddr bit; - /* If we have both the even and odd twins, then the immediate parent, - * if it is present is redundant, unless it manages to aggregate - * something. On successive calls, this code detects the - * even and odd twins, and marks the parent. + /* 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. + * On successive calls, this code detects the even and odd twins, + * and marks the parent. * * Note that the order in which the radix tree code emits routes * ensures that the twins are seen before the parent is emitted. @@ -219,14 +219,14 @@ ag_flush(naddr lim_dst_h, /* flush routes to here */ * the target. */ if (ag->ag_gate != ag_cors->ag_gate - && !(ag->ag_state & AGS_DEAD) - && !(ag_cors->ag_state & AGS_RDISC)) { + && !(ag->ag_state & AGS_FINE_GATE) + && !(ag_cors->ag_state & AGS_CORS_GATE)) { ag_out(ag, out); break; } - /* If it has a good enough metric, it replaces - * the target. + /* If the coarse route has a good enough + * metric, it suppresses the target. */ if (ag_cors->ag_pref <= ag->ag_pref) { if (ag_cors->ag_seqno > ag->ag_seqno) @@ -238,6 +238,10 @@ ag_flush(naddr lim_dst_h, /* flush routes to here */ else ag_cors->ag_state |= AGS_REDUN1; } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; break; } } @@ -258,6 +262,7 @@ void ag_check(naddr dst, naddr mask, naddr gate, + naddr nhop, char metric, char pref, u_int seqno, @@ -282,6 +287,7 @@ ag_check(naddr dst, nc_ag.ag_dst_h = dst; nc_ag.ag_mask = mask; nc_ag.ag_gate = gate; + nc_ag.ag_nhop = nhop; nc_ag.ag_metric = metric; nc_ag.ag_pref = pref; nc_ag.ag_tag = tag; @@ -298,7 +304,10 @@ ag_check(naddr dst, while (ag != 0) { if (ag->ag_mask >= mask) break; - /* Suppress routes as we look. + + /* Suppress old routes (i.e. combine with compatible routes + * with coarser masks) as we look for the right slot in the + * aggregation table for the new route. * A route to an address less than the current destination * will not be affected by the current route or any route * seen hereafter. That means it is safe to suppress it. @@ -311,8 +320,8 @@ ag_check(naddr dst, && ag_cors->ag_pref <= ag->ag_pref && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h && (ag_cors->ag_gate == ag->ag_gate - || (ag->ag_state & AGS_DEAD) - || (ag_cors->ag_state & AGS_RDISC))) { + || (ag->ag_state & AGS_FINE_GATE) + || (ag_cors->ag_state & AGS_CORS_GATE))) { if (ag_cors->ag_seqno > ag->ag_seqno) ag_cors->ag_seqno = ag->ag_seqno; if (AG_IS_REDUN(ag->ag_state) @@ -322,6 +331,10 @@ ag_check(naddr dst, else ag_cors->ag_state |= AGS_REDUN1; } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; ag_del(ag); CHECK_AG(); } else { @@ -343,40 +356,50 @@ ag_check(naddr dst, && ag->ag_mask == mask && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { - /* When a promoted route encounters the same but explicit - * route, assume the new one has been promoted, and - * so its gateway, metric and tag are right. - * - * Routes are encountered in lexical order, so an even/odd - * pair 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 that was made redundant by - * the pair. - * - * The sequence number only controls flash updating, and - * so should be the smaller of the two. + /* Here we know the target route and the route in the current + * slot have the same netmasks and differ by at most the + * last bit. They are either for the same destination, or + * for an even/odd pair of destinations. */ if (ag->ag_dst_h == dst) { - ag->ag_metric = metric; - ag->ag_pref = pref; - ag->ag_gate = gate; - ag->ag_tag = tag; + /* We have two routes to the same destination. + * 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. + * + * Prefer the best route if their metrics differ, + * or the promoted one if not, following a sort + * of longest-match rule. + */ + if (pref <= ag->ag_pref) { + ag->ag_gate = gate; + ag->ag_nhop = nhop; + ag->ag_tag = tag; + ag->ag_metric = metric; + ag->ag_pref = pref; + x = ag->ag_state; + ag->ag_state = state; + state = x; + } + + /* The sequence number controls flash updating, + * and should be the smaller of the two. + */ if (ag->ag_seqno > seqno) ag->ag_seqno = seqno; - /* some bits are set only if both routes have them */ - ag->ag_state &= ~(~state & (AGS_PROMOTE | AGS_RIPV2)); - /* others are set if they are set on either route */ - ag->ag_state |= (state & (AGS_REDUN0 | AGS_REDUN1 - | AGS_GATEWAY - | AGS_SUPPRESS)); + /* some bits are set if they are set on either route */ + ag->ag_state |= (state & (AGS_PROMOTE_EITHER + | AGS_REDUN0 | AGS_REDUN1)); return; } - /* If one of the routes can be promoted and suppressed - * and the other can at least be suppressed, they - * can be combined. + /* If one of the routes can be promoted and the other can + * be suppressed, it may be possible to combine them or + * worthwhile to promote one. + * * Note that any route that can be promoted is always * marked to be eligible to be suppressed. */ @@ -389,15 +412,12 @@ ag_check(naddr dst, /* A pair of even/odd twin routes can be combined * if either is redundant, or if they are via the * same gateway and have the same metric. - * Except that the kernel does not care about the - * metric. */ if (AG_IS_REDUN(ag->ag_state) || AG_IS_REDUN(state) || (ag->ag_gate == gate && ag->ag_pref == pref - && (state & ag->ag_state & AGS_PROMOTE) != 0 - && ag->ag_tag == tag)) { + && (state & ag->ag_state & AGS_PROMOTE) != 0)) { /* We have both the even and odd pairs. * Since the routes are encountered in order, @@ -413,7 +433,11 @@ ag_check(naddr dst, state |= AGS_REDUN0; else state &= ~AGS_REDUN0; - state |= (ag->ag_state & AGS_RIPV2); + state |= (ag->ag_state & AGS_PROMOTE_EITHER); + if (ag->ag_tag != tag) + tag = 0; + if (ag->ag_nhop != nhop) + nhop = 0; /* Get rid of the even twin that was already * in the slot. @@ -434,6 +458,10 @@ ag_check(naddr dst, ag->ag_gate = gate; gate = xaddr; + xaddr = ag->ag_nhop; + ag->ag_nhop = nhop; + nhop = xaddr; + x = ag->ag_tag; ag->ag_tag = tag; tag = x; @@ -525,6 +553,7 @@ ag_check(naddr dst, nag->ag_dst_h = dst; nag->ag_mask = mask; nag->ag_gate = gate; + nag->ag_nhop = nhop; nag->ag_metric = metric; nag->ag_pref = pref; nag->ag_tag = tag; @@ -595,7 +624,7 @@ masktrim(struct sockaddr_in_new *ap) return; } cp = (char *)(&ap->sin_addr.s_addr+1); - while (*--cp != 0) + while (*--cp == 0) continue; ap->sin_len = cp - (char*)ap + 1; } @@ -661,14 +690,20 @@ again: if (cc == w.w_rtm.rtm_msglen) return; if (cc < 0) { - if (errno == ESRCH && action == RTM_CHANGE) { - trace_msg("route to %s disappeared before CHANGE", - addrname(dst, mask, 0)); - action = RTM_ADD; - goto again; + if (errno == ESRCH + && (action == RTM_CHANGE || action == RTM_DELETE)) { + trace_act("route to %s disappeared before %s\n", + addrname(dst, mask, 0), + rtm_type_name(action)); + if (action == RTM_CHANGE) { + action = RTM_ADD; + goto again; + } + return; } - msglog("write(rt_sock) %s %s: %s", - rtm_type_name(action), addrname(dst, mask, 0), + 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", @@ -696,9 +731,9 @@ static struct khash { #define KS_GATEWAY 0x040 #define KS_DYNAMIC 0x080 #define KS_DELETED 0x100 /* already deleted */ - time_t k_hold; - time_t k_time; -#define K_HOLD_LIM 30 + time_t k_keep; +#define K_KEEP_LIM 30 + time_t k_redirect_time; } *khash_bins[KHASH_SIZE]; @@ -732,121 +767,146 @@ kern_add(naddr dst, naddr mask) k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; - k->k_time = now.tv_sec; - k->k_hold = now.tv_sec; + k->k_redirect_time = now.tv_sec; + k->k_keep = now.tv_sec; *pk = k; return k; } +/* If it has a non-zero metric, check that it is still in the table, not + * having been deleted by interfaces coming and going. + */ +static void +kern_check_static(struct khash *k, + struct interface *ifp) +{ + struct rt_entry *rt; + naddr int_addr; + + if (k->k_metric == 0) + return; + + int_addr = (ifp != 0) ? ifp->int_addr : loopaddr; + + 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); + } else { + rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr, + k->k_metric, 0, RS_STATIC, ifp); + } +} + + /* add a route the kernel told us - * rt_xaddrs() must have already been called. */ static void -rtm_add(struct rt_msghdr *rtm) +rtm_add(struct rt_msghdr *rtm, + struct rt_addrinfo *info, + time_t keep) { struct khash *k; struct interface *ifp; - struct rt_entry *rt; naddr mask; if (rtm->rtm_flags & RTF_HOST) { mask = HOST_MASK; - } else if (RTINFO_NETMASK != 0) { - mask = ntohl(S_ADDR(RTINFO_NETMASK)); + } else if (INFO_MASK(info) != 0) { + mask = ntohl(S_ADDR(INFO_MASK(info))); } else { msglog("punt %s without mask", rtm_type_name(rtm->rtm_type)); return; } - if (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { msglog("punt %s without gateway", rtm_type_name(rtm->rtm_type)); return; } - k = kern_add(S_ADDR(RTINFO_DST), mask); - k->k_gate = S_ADDR(RTINFO_GATE); + 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_NEW | KS_DELETED | KS_GATEWAY | KS_STATIC); + k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW); if (rtm->rtm_flags & RTF_GATEWAY) k->k_state |= KS_GATEWAY; if (rtm->rtm_flags & RTF_STATIC) k->k_state |= KS_STATIC; - if (rtm->rtm_flags & RTF_DYNAMIC) + if (rtm->rtm_flags & RTF_DYNAMIC) { k->k_state |= KS_DYNAMIC; - k->k_time = now.tv_sec; - k->k_hold = now.tv_sec; + k->k_redirect_time = now.tv_sec; + /* Routers are not supposed to listen to redirects, + * so delete it. + */ + if (supplier) { + k->k_keep = now.tv_sec; + trace_act("mark redirected %s --> %s for deletion" + "since this is a router\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + } + } - /* Put static routes with real metrics into the daemon table so - * they can be advertised. + /* If it is not a static route, quite until it is time to delete it. */ - if (!(k->k_state & KS_STATIC)) + if (!(k->k_state & KS_STATIC)) { + k->k_state |= KS_DELETE; + LIM_SEC(need_kern, k->k_keep); return; - - if (RTINFO_IFP != 0 - && RTINFO_IFP->sdl_nlen != 0) { - RTINFO_IFP->sdl_data[RTINFO_IFP->sdl_nlen] = '\0'; - ifp = ifwithname(RTINFO_IFP->sdl_data, k->k_gate); - } else { - ifp = iflookup(k->k_gate); } + + /* Put static routes with real metrics into the daemon table so + * they can be advertised. + * + * Find the interface concerned + */ + ifp = iflookup(k->k_gate); if (ifp == 0) { - msglog("static route %s --> %s impossibly lacks ifp", - addrname(S_ADDR(RTINFO_DST), mask, 0), - naddr_ntoa(k->k_gate)); - return; + /* if there is no interface, maybe it is new + */ + ifinit(); + ifp = iflookup(k->k_gate); + if (ifp == 0) + msglog("static route %s --> %s impossibly lacks ifp", + addrname(S_ADDR(INFO_DST(info)), mask, 0), + naddr_ntoa(k->k_gate)); } - if (k->k_metric == 0) - return; - rt = rtget(k->k_dst, k->k_mask); - if (rt != 0) { - if (rt->rt_ifp != ifp - || 0 != (rt->rt_state & RS_NET_S)) { - rtdelete(rt); - rt = 0; - } else if (!(rt->rt_state & (RS_IF - | RS_LOCAL - | RS_MHOME - | RS_GW))) { - rtchange(rt, RS_STATIC, - k->k_gate, ifp->int_addr, - k->k_metric, 0, ifp, - now.tv_sec, 0); - } - } - if (rt == 0) - rtadd(k->k_dst, k->k_mask, k->k_gate, - ifp->int_addr, k->k_metric, - 0, RS_STATIC, ifp); + kern_check_static(k, ifp); } /* deal with packet loss */ static void -rtm_lose(struct rt_msghdr *rtm) +rtm_lose(struct rt_msghdr *rtm, + struct rt_addrinfo *info) { - if (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { msglog("punt %s without gateway", rtm_type_name(rtm->rtm_type)); return; } if (!supplier) - rdisc_age(S_ADDR(RTINFO_GATE)); + rdisc_age(S_ADDR(INFO_GATE(info))); - age(S_ADDR(RTINFO_GATE)); + age(S_ADDR(INFO_GATE(info))); } @@ -862,6 +922,7 @@ flush_kern(void) struct rt_msghdr *rtm; struct interface *ifp; static struct sockaddr_in gate_sa; + struct rt_addrinfo info; mib[0] = CTL_NET; @@ -881,39 +942,55 @@ flush_kern(void) for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; - rt_xaddrs((struct sockaddr *)(rtm+1), + rt_xaddrs(&info, + (struct sockaddr *)(rtm+1), (struct sockaddr *)(next + rtm->rtm_msglen), rtm->rtm_addrs); - if (RTINFO_DST == 0 - || RTINFO_DST->sa_family != AF_INET) + if (INFO_DST(&info) == 0 + || INFO_DST(&info)->sa_family != AF_INET) continue; - if (RTINFO_GATE == 0) + /* ignore ARP table entries on systems with a merged route + * and ARP table. + */ + if (rtm->rtm_flags & RTF_LLINFO) continue; - if (RTINFO_GATE->sa_family != AF_INET) { - if (RTINFO_GATE->sa_family != AF_LINK) + + 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 *) - RTINFO_GATE)->sdl_index); + INFO_GATE(&info))->sdl_index); if (ifp == 0) continue; - gate_sa.sin_addr.s_addr = ifp->int_addr; + 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; - RTINFO_GATE = (struct sockaddr *)&gate_sa; + INFO_GATE(&info) = (struct sockaddr *)&gate_sa; } /* ignore multicast addresses */ - if (IN_MULTICAST(ntohl(S_ADDR(RTINFO_DST)))) + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) continue; - /* Note static routes and interface routes. + /* 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 + * unneeded changes. Keep the old kernel routes for a + * few seconds to allow a RIP or router-discovery + * response to be heard. */ - rtm_add(rtm); + rtm_add(rtm,&info,MIN_WAITTIME); } free(buf); } @@ -934,7 +1011,8 @@ read_rt(void) } r; struct if_msghdr ifm; } m; - char pid_str[10+19+1]; + char str[100], *strp; + struct rt_addrinfo info; for (;;) { @@ -963,16 +1041,18 @@ read_rt(void) continue; } - if (m.r.rtm.rtm_type == RTM_IFINFO) { + 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); if (ifp == 0) - trace_msg("note %s with flags %#x" + trace_act("note %s with flags %#x" " for index #%d\n", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, m.ifm.ifm_index); else - trace_msg("note %s with flags %#x for %s\n", + trace_act("note %s with flags %#x for %s\n", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, ifp->int_name); @@ -991,90 +1071,78 @@ read_rt(void) continue; } + strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); + strp = &str[strlen(str)]; if (m.r.rtm.rtm_type <= RTM_CHANGE) - (void)sprintf(pid_str," from pid %d",m.r.rtm.rtm_pid); - else - pid_str[0] = '\0'; + strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); - rt_xaddrs(m.r.addrs, &m.r.addrs[RTAX_MAX], + rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], m.r.rtm.rtm_addrs); - if (RTINFO_DST == 0) { - trace_msg("ignore %s%s without dst\n", - rtm_type_name(m.r.rtm.rtm_type), pid_str); + if (INFO_DST(&info) == 0) { + trace_act("ignore %s without dst\n", str); continue; } - if (RTINFO_DST->sa_family != AF_INET) { - trace_msg("ignore %s%s for AF %d\n", - rtm_type_name(m.r.rtm.rtm_type), pid_str, - RTINFO_DST->sa_family); + if (INFO_DST(&info)->sa_family != AF_INET) { + trace_act("ignore %s for AF %d\n", str, + INFO_DST(&info)->sa_family); continue; } - mask = ((RTINFO_NETMASK != 0) - ? ntohl(S_ADDR(RTINFO_NETMASK)) + mask = ((INFO_MASK(&info) != 0) + ? ntohl(S_ADDR(INFO_MASK(&info))) : (m.r.rtm.rtm_flags & RTF_HOST) ? HOST_MASK - : std_mask(S_ADDR(RTINFO_DST))); - - if (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { - trace_msg("%s for %s%s\n", - rtm_type_name(m.r.rtm.rtm_type), - addrname(S_ADDR(RTINFO_DST), mask, 0), - pid_str); - } else { - trace_msg("%s %s --> %s%s\n", - rtm_type_name(m.r.rtm.rtm_type), - addrname(S_ADDR(RTINFO_DST), mask, 0), - saddr_ntoa(RTINFO_GATE), - pid_str); + : std_mask(S_ADDR(INFO_DST(&info)))); + + strp += sprintf(strp, " %s", + addrname(S_ADDR(INFO_DST(&info)), mask, 0)); + + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { + trace_act("ignore %s for multicast %s\n", str); + continue; } + if (INFO_GATE(&info) != 0 + && INFO_GATE(&info)->sa_family == AF_INET) + strp += sprintf(strp, " --> %s", + saddr_ntoa(INFO_GATE(&info))); + + if (INFO_AUTHOR(&info) != 0) + strp += sprintf(strp, " by authority of %s", + saddr_ntoa(INFO_AUTHOR(&info))); + switch (m.r.rtm.rtm_type) { case RTM_ADD: case RTM_CHANGE: - if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s%s with \"%s\" error\n", - rtm_type_name(m.r.rtm.rtm_type), - pid_str, - strerror(m.r.rtm.rtm_errno)); - } else { - rtm_add(&m.r.rtm); - } - break; - case RTM_REDIRECT: if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s with \"%s\" from %s" - " for %s-->%s\n", - rtm_type_name(m.r.rtm.rtm_type), - strerror(m.r.rtm.rtm_errno), - saddr_ntoa(RTINFO_AUTHOR), - saddr_ntoa(RTINFO_GATE), - addrname(S_ADDR(RTINFO_DST), - mask, 0)); + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); } else { - rtm_add(&m.r.rtm); + trace_act("%s\n", str); + rtm_add(&m.r.rtm,&info,0); } break; case RTM_DELETE: if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s%s with \"%s\" error\n", - rtm_type_name(m.r.rtm.rtm_type), - pid_str, - strerror(m.r.rtm.rtm_errno)); + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); } else { - del_static(S_ADDR(RTINFO_DST), mask, 1); + trace_act("%s\n", str); + del_static(S_ADDR(INFO_DST(&info)), mask, 1); } break; case RTM_LOSING: - rtm_lose(&m.r.rtm); + trace_act("%s\n", str); + rtm_lose(&m.r.rtm,&info); break; + default: + trace_act("ignore %s\n", str); break; } } @@ -1090,7 +1158,7 @@ kern_out(struct ag_info *ag) /* Do not install bad routes if they are not already present. - * This includes routes that had RS_NET_S for interfaces that + * This includes routes that had RS_NET_SYN for interfaces that * recently died. */ if (ag->ag_metric == HOPCNT_INFINITY @@ -1099,8 +1167,8 @@ kern_out(struct ag_info *ag) k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); - /* will need to add new entry */ if (k->k_state & KS_NEW) { + /* will need to add new entry to the kernel table */ k->k_state = KS_ADD; if (ag->ag_state & AGS_GATEWAY) k->k_state |= KS_GATEWAY; @@ -1109,8 +1177,11 @@ kern_out(struct ag_info *ag) return; } + if (k->k_state & KS_STATIC) + return; + /* modify existing kernel entry if necessary */ - k->k_state &= ~(KS_DELETE | KS_DYNAMIC); + k->k_state &= ~KS_DELETE; if (k->k_gate != ag->ag_gate || k->k_metric != ag->ag_metric) { k->k_gate = ag->ag_gate; @@ -1118,6 +1189,11 @@ kern_out(struct ag_info *ag) k->k_state |= KS_CHANGE; } + if (k->k_state & KS_DYNAMIC) { + k->k_state &= ~KS_DYNAMIC; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } + if ((k->k_state & KS_GATEWAY) && !(ag->ag_state & AGS_GATEWAY)) { k->k_state &= ~KS_GATEWAY; @@ -1137,45 +1213,51 @@ walk_kern(struct radix_node *rn, struct walkarg *w) { #define RT ((struct rt_entry *)rn) - char pref; + char metric, pref; u_int ags = 0; - /* Do not install synthetic routes */ - if (0 != (RT->rt_state & RS_NET_S)) - return 0; - /* Do not install routes for "external" remote interfaces. - */ - if ((RT->rt_state & RS_IF) - && RT->rt_ifp != 0 - && (RT->rt_ifp->int_state & IS_EXTERNAL)) + /* Do not install synthetic routes */ + if (RT->rt_state & RS_NET_SYN) return 0; - /* If it is not an interface, or an alias for an interface, - * it must be a "gateway." - * - * If it is a "remote" interface, it is also a "gateway" to - * the kernel if is not a alias. - */ - if (!(RT->rt_state & RS_IF) - || RT->rt_ifp == 0 - || ((RT->rt_ifp->int_state & IS_REMOTE) - && RT->rt_ifp->int_metric == 0)) + if (!(RT->rt_state & RS_IF)) { ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); - if (RT->rt_metric == HOPCNT_INFINITY) { - pref = HOPCNT_INFINITY; - ags |= (AGS_DEAD | AGS_SUPPRESS); } else { - pref = 1; + /* 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; + + /* If it is not an interface, or an alias for an interface, + * it must be a "gateway." + * + * If it is a "remote" interface, it is also a "gateway" to + * the kernel if is not a alias. + */ + if (RT->rt_ifp == 0 + || ((RT->rt_ifp->int_state & IS_REMOTE) + && RT->rt_ifp->int_metric == 0)) + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); } if (RT->rt_state & RS_RDISC) - ags |= AGS_RDISC; + 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); + } - ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, - RT->rt_metric, pref, - 0, 0, ags, kern_out); + ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, + metric,pref, 0, 0, ags, kern_out); return 0; #undef RT } @@ -1183,7 +1265,7 @@ walk_kern(struct radix_node *rn, /* Update the kernel table to match the daemon table. */ -void +static void fix_kern(void) { int i, flags; @@ -1201,13 +1283,15 @@ fix_kern(void) for (pk = &khash_bins[i]; (k = *pk) != 0; ) { /* Do not touch static routes */ if (k->k_state & KS_STATIC) { + kern_check_static(k,0); pk = &k->k_next; continue; } /* check hold on routes deleted by the operator */ - if (k->k_hold > now.tv_sec) { - LIM_SEC(need_kern, k->k_hold); + if (k->k_keep > now.tv_sec) { + LIM_SEC(need_kern, k->k_keep); + k->k_state |= KS_DELETE; pk = &k->k_next; continue; } @@ -1238,17 +1322,13 @@ fix_kern(void) } k->k_state &= ~(KS_ADD | KS_CHANGE | KS_DEL_ADD); - /* Unless it seems something else is handling the - * routes in the kernel, mark this route to be - * deleted in the next cycle. + /* Mark this route to be deleted in the next cycle. * This deletes routes that disappear from the * daemon table, since the normal aging code * will clear the bit for routes that have not - * disappeard from the daemon table. + * disappeared from the daemon table. */ - if (now.tv_sec >= EPOCH+MIN_WAITTIME-1 - && (rip_interfaces != 0 || !supplier)) - k->k_state |= KS_DELETE; + k->k_state |= KS_DELETE; pk = &k->k_next; } } @@ -1268,7 +1348,7 @@ del_static(naddr dst, /* Just mark it in the table to be deleted next time the kernel * table is updated. * If it has already been deleted, mark it as such, and set its - * hold timer so that it will not be deleted again for a while. + * keep-timer so that it will not be deleted again for a while. * This lets the operator delete a route added by the daemon * and add a replacement. */ @@ -1278,7 +1358,7 @@ del_static(naddr dst, k->k_state |= KS_DELETE; if (gone) { k->k_state |= KS_DELETED; - k->k_hold = now.tv_sec + K_HOLD_LIM; + k->k_keep = now.tv_sec + K_KEEP_LIM; } } @@ -1289,7 +1369,7 @@ del_static(naddr dst, /* Delete all routes generated from ICMP Redirects that use a given - * gateway. + * gateway, as well as all old redirected routes. */ void del_redirects(naddr bad_gate, @@ -1302,20 +1382,19 @@ del_redirects(naddr bad_gate, for (i = 0; i < KHASH_SIZE; i++) { for (k = khash_bins[i]; k != 0; k = k->k_next) { if (!(k->k_state & KS_DYNAMIC) - || 0 != (k->k_state & (KS_STATIC | KS_DELETE))) + || (k->k_state & KS_STATIC)) continue; if (k->k_gate != bad_gate - && k->k_time > old) + && k->k_redirect_time > old + && !supplier) continue; k->k_state |= KS_DELETE; need_kern.tv_sec = now.tv_sec; - if (TRACEACTIONS) - trace_msg("mark redirected %s --> %s" - " for deletion\n", - addrname(k->k_dst, k->k_mask, 0), - naddr_ntoa(k->k_gate)); + trace_act("mark redirected %s --> %s for deletion\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); } } } @@ -1405,7 +1484,7 @@ rtadd(naddr dst, naddr router, /* on the authority of this router */ int metric, u_short tag, - u_int state, /* RS_ for our table */ + u_int state, /* rs_state for the entry */ struct interface *ifp) { struct rt_entry *rt; @@ -1440,18 +1519,11 @@ rtadd(naddr dst, rt->rt_gate = gate; rt->rt_router = router; rt->rt_time = now.tv_sec; - if (metric == HOPCNT_INFINITY) { - rt->rt_time -= POISON_SECS; - rt->rt_hold_down = now.tv_sec+HOLD_TIME; - } rt->rt_metric = metric; - if ((rt->rt_state & RS_NET_S) == 0) - rt->rt_hold_metric = metric; - else - rt->rt_hold_metric = HOPCNT_INFINITY; + rt->rt_poison_metric = HOPCNT_INFINITY; rt->rt_tag = tag; rt->rt_ifp = ifp; - rt->rt_seqno = update_seqno+1; + rt->rt_seqno = update_seqno; if (TRACEACTIONS) trace_add_del("Add", rt); @@ -1488,31 +1560,19 @@ rtchange(struct rt_entry *rt, char *label) { if (rt->rt_metric != metric) { - /* Hold down the route if it is bad, but only long enough - * for neighors that do not implement poison-reverse or - * split horizon to hear the bad news. + /* 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_time > now.tv_sec - POISON_SECS) - new_time = now.tv_sec - POISON_SECS; - if (!(rt->rt_state & RS_RDISC) - && rt->rt_hold_down < now.tv_sec+HOLD_TIME) - rt->rt_hold_down = now.tv_sec+HOLD_TIME; - if (now.tv_sec < rt->rt_hold_down) - LIM_SEC(age_timer, rt->rt_hold_down+1); - } else { - rt->rt_hold_down = 0; - if ((rt->rt_state & RS_NET_S) == 0) - rt->rt_hold_metric = metric; - } - - rt->rt_seqno = update_seqno+1; + if (metric == HOPCNT_INFINITY) + need_kern.tv_sec = now.tv_sec; + rt->rt_seqno = update_seqno; set_need_flash(); } if (rt->rt_gate != gate) { need_kern.tv_sec = now.tv_sec; - rt->rt_seqno = update_seqno+1; + rt->rt_seqno = update_seqno; set_need_flash(); } @@ -1533,58 +1593,58 @@ rtchange(struct rt_entry *rt, } +/* check for a better route among the spares + */ +static struct rt_spare * +rts_better(struct rt_entry *rt) +{ + struct rt_spare *rts, *rts1; + int i; + + /* find the best alternative among the spares */ + rts = rt->rt_spares+1; + for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { + if (BETTER_LINK(rt,rts1,rts)) + rts = rts1; + } + + return rts; +} + + /* switch to a backup route */ void rtswitch(struct rt_entry *rt, struct rt_spare *rts) { - struct rt_spare *rts1, swap; + struct rt_spare swap; char label[10]; - int i; /* Do not change permanent routes */ - if (0 != (rt->rt_state & (RS_GW | RS_MHOME | RS_STATIC | RS_IF))) + if (0 != (rt->rt_state & RS_PERMANENT)) return; /* Do not discard synthetic routes until they go bad */ - if (0 != (rt->rt_state & RS_NET_S) + if ((rt->rt_state & RS_NET_SYN) && rt->rt_metric < HOPCNT_INFINITY) return; - if (rts == 0) { - /* find the best alternative among the spares */ - rts = rt->rt_spares+1; - for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { - if (BETTER_LINK(rts1,rts)) - rts = rts1; - } - } + /* find the best alternative among the spares */ + if (rts == 0) + rts = rts_better(rt); /* Do not bother if it is not worthwhile. */ - if (!BETTER_LINK(rts, rt->rt_spares)) - return; - - /* Do not change the route if it is being held down. - * Honor the hold-down to counter systems that do not support - * split horizon or for other causes of counting to infinity, - * and so only for routes worse than our last good route. - */ - if (now.tv_sec < rt->rt_hold_down - && rts->rts_metric > rt->rt_hold_metric) { - LIM_SEC(age_timer, rt->rt_hold_down+1); + if (!BETTER_LINK(rt, rts, rt->rt_spares)) return; - } swap = rt->rt_spares[0]; - (void)sprintf(label, "Use #%d", rts - rt->rt_spares); - rtchange(rt, rt->rt_state & ~(RS_NET_S | RS_RDISC), + 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; } @@ -1630,7 +1690,8 @@ rtbad(struct rt_entry *rt) } -/* Junk a RS_NET_S route, but save if if it is needed by another interface. +/* Junk a RS_NET_SYN or RS_LOCAL route, + * unless it is needed by another interface. */ void rtbad_sub(struct rt_entry *rt) @@ -1645,14 +1706,11 @@ rtbad_sub(struct rt_entry *rt) if (rt->rt_state & RS_LOCAL) { /* Is this the route through loopback for the interface? - * If so, see if it is used by any other interfaces, a - * point-to-point interface with the same local address. + * If so, see if it is used by any other interfaces, such + * as a point-to-point interface with the same local address. */ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (ifp->int_metric == HOPCNT_INFINITY) - continue; - - /* Save it if another interface needs it + /* Retain it if another interface needs it. */ if (ifp->int_addr == rt->rt_ifp->int_addr) { state |= RS_LOCAL; @@ -1663,48 +1721,38 @@ rtbad_sub(struct rt_entry *rt) } - if (!(state & RS_LOCAL) - && (rt->rt_state & RS_NET_S)) { - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (ifp->int_metric == HOPCNT_INFINITY) - continue; - - /* Retain RIPv1 logical network route if - * there is another interface that justifies - * it. - */ - if ((ifp->int_state & IS_NEED_NET_SUB) - && rt->rt_mask == ifp->int_std_mask - && rt->rt_dst == ifp->int_std_addr) { - state |= RS_NET_SUB; - ifp1 = ifp; - - } else if ((ifp->int_if_flags & IFF_POINTOPOINT) - && rt->rt_mask == ifp->int_host_mask - && rt->rt_dst == ifp->int_host_addr - && ridhosts) { - state |= RS_NET_HOST; - ifp1 = ifp; + if (!(state & RS_LOCAL)) { + /* Retain RIPv1 logical network route if there is another + * interface that justifies it. + */ + if (rt->rt_state & RS_NET_SYN) { + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if ((ifp->int_state & IS_NEED_NET_SYN) + && rt->rt_mask == ifp->int_std_mask + && rt->rt_dst == ifp->int_std_addr) { + state |= RS_NET_SYN; + ifp1 = ifp; + break; + } } } - if (ifp1 == 0) { - for (intnetp = intnets; - intnetp != 0; - intnetp = intnetp->intnet_next) { - if (intnetp->intnet_addr == rt->rt_dst - && intnetp->intnet_mask == rt->rt_mask) { - state |= RS_NET_SUB; - break; - } + /* or if there is an authority route that needs it. */ + for (intnetp = intnets; + intnetp != 0; + intnetp = intnetp->intnet_next) { + if (intnetp->intnet_addr == rt->rt_dst + && intnetp->intnet_mask == rt->rt_mask) { + state |= (RS_NET_SYN | RS_NET_INT); + break; } } } - - if (ifp1 != 0) { - rtchange(rt, (rt->rt_state & ~(RS_NET_S | RS_LOCAL)) | state, - rt->rt_gate, rt->rt_router, NET_S_METRIC, + if (ifp1 != 0 || (state & RS_NET_SYN)) { + rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) + | state), + rt->rt_gate, rt->rt_router, 1, rt->rt_tag, ifp1, rt->rt_time, 0); } else { rtbad(rt); @@ -1737,11 +1785,10 @@ walk_bad(struct radix_node *rn, new_time = rts->rts_time; if (new_time >= now_garbage) new_time = now_garbage-1; - if (TRACEACTIONS) - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, 0, - HOPCNT_INFINITY, rts->rts_tag, - new_time); + 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; @@ -1757,7 +1804,7 @@ walk_bad(struct radix_node *rn, /* Bad routes for other than interfaces are easy. */ - if (!(RT->rt_state & RS_IF)) { + if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { rtbad(RT); return 0; } @@ -1781,7 +1828,9 @@ walk_age(struct radix_node *rn, int i; - /* age the spare routes */ + /* age all of the spare routes, including the primary route + * currently in use + */ rts = RT->rt_spares; for (i = NUM_SPARES; i != 0; i--, rts++) { @@ -1796,37 +1845,26 @@ walk_age(struct radix_node *rn, /* forget RIP routes after RIP has been turned off. */ - if (rip_sock < 0 && !(RT->rt_state & RS_RDISC)) { + if (rip_sock < 0) { rtdelete(RT); return 0; } } + /* age failing routes + */ if (age_bad_gate == rts->rts_gate && rts->rts_time >= now_stale) { - /* age failing routes - */ rts->rts_time -= SUPPLY_INTERVAL; - - } else if (ppp_noage - && ifp != 0 - && (ifp->int_if_flags & IFF_POINTOPOINT) - && (ifp->int_state & IS_QUIET)) { - /* optionally do not age routes through quiet - * point-to-point interfaces - */ - rts->rts_time = now.tv_sec; - continue; } /* trash the spare routes when they go bad */ if (rts->rts_metric < HOPCNT_INFINITY && now_garbage > rts->rts_time) { - if (TRACEACTIONS) - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, rts->rts_ifp, - HOPCNT_INFINITY, rts->rts_tag, - 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; } } @@ -1837,12 +1875,7 @@ walk_age(struct radix_node *rn, return 0; /* try to switch to an alternative */ - if (now.tv_sec < RT->rt_hold_down) { - LIM_SEC(age_timer, RT->rt_hold_down+1); - return 0; - } else { - rtswitch(RT, 0); - } + rtswitch(RT, 0); /* Delete a dead route after it has been publically mourned. */ if (now_garbage > RT->rt_time) { @@ -1878,11 +1911,16 @@ age(naddr bad_gate) if ((ifp->int_state & IS_REMOTE) && !(ifp->int_state & IS_PASSIVE) && (ifp->int_state & IS_ACTIVE)) { - LIM_SEC(age_timer, now.tv_sec+SUPPLY_INTERVAL); - if (now.tv_sec - ifp->int_act_time > EXPIRE_TIME) - ifbad(ifp, - "remote interface %s to %s timed out"); + + if (now.tv_sec - ifp->int_act_time > EXPIRE_TIME + && !(ifp->int_state & IS_BROKE)) { + msglog("remote interface %s to %s timed out" + "--turned off", + ifp->int_name, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + } } } |