Index: head/sbin/routed/if.c =================================================================== --- head/sbin/routed/if.c (revision 299767) +++ head/sbin/routed/if.c (revision 299768) @@ -1,1383 +1,1384 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include #include "defs.h" #include "pathnames.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif struct ifhead ifnet = LIST_HEAD_INITIALIZER(ifnet); /* all interfaces */ struct ifhead remote_if = LIST_HEAD_INITIALIZER(remote_if); /* remote interfaces */ /* hash table for all interfaces, big enough to tolerate ridiculous * numbers of IP aliases. Crazy numbers of aliases such as 7000 * still will not do well, but not just in looking up interfaces * by name or address. */ #define AHASH_LEN 211 /* must be prime */ #define AHASH(a) &ahash_tbl[(a)%AHASH_LEN] static struct interface *ahash_tbl[AHASH_LEN]; #define BHASH_LEN 211 /* must be prime */ #define BHASH(a) &bhash_tbl[(a)%BHASH_LEN] static struct interface *bhash_tbl[BHASH_LEN]; /* hash for physical interface names. * Assume there are never more 100 or 200 real interfaces, and that * aliases are put on the end of the hash chains. */ #define NHASH_LEN 97 static struct interface *nhash_tbl[NHASH_LEN]; int tot_interfaces; /* # of remote and local interfaces */ int rip_interfaces; /* # of interfaces doing RIP */ static int foundloopback; /* valid flag for loopaddr */ naddr loopaddr; /* our address on loopback */ static struct rt_spare loop_rts; struct timeval ifinit_timer; static struct timeval last_ifinit; #define IF_RESCAN_DELAY() (last_ifinit.tv_sec == now.tv_sec \ && last_ifinit.tv_usec == now.tv_usec \ && timercmp(&ifinit_timer, &now, >)) int have_ripv1_out; /* have a RIPv1 interface */ static int have_ripv1_in; static void if_bad(struct interface *); static int addrouteforif(struct interface *); static struct interface** nhash(char *p) { u_int i; for (i = 0; *p != '\0'; p++) { i = ((i<<1) & 0x7fffffff) | ((i>>31) & 1); i ^= *p; } return &nhash_tbl[i % NHASH_LEN]; } /* Link a new interface into the lists and hash tables. */ void if_link(struct interface *ifp) { struct interface **hifp; LIST_INSERT_HEAD(&ifnet, ifp, int_list); hifp = AHASH(ifp->int_addr); ifp->int_ahash_prev = hifp; - if ((ifp->int_ahash = *hifp) != 0) + if ((ifp->int_ahash = *hifp) != NULL) (*hifp)->int_ahash_prev = &ifp->int_ahash; *hifp = ifp; if (ifp->int_if_flags & IFF_BROADCAST) { hifp = BHASH(ifp->int_brdaddr); ifp->int_bhash_prev = hifp; - if ((ifp->int_bhash = *hifp) != 0) + if ((ifp->int_bhash = *hifp) != NULL) (*hifp)->int_bhash_prev = &ifp->int_bhash; *hifp = ifp; } if (ifp->int_state & IS_REMOTE) LIST_INSERT_HEAD(&remote_if, ifp, remote_list); hifp = nhash(ifp->int_name); if (ifp->int_state & IS_ALIAS) { /* put aliases on the end of the hash chain */ - while (*hifp != 0) + while (*hifp != NULL) hifp = &(*hifp)->int_nhash; } ifp->int_nhash_prev = hifp; - if ((ifp->int_nhash = *hifp) != 0) + if ((ifp->int_nhash = *hifp) != NULL) (*hifp)->int_nhash_prev = &ifp->int_nhash; *hifp = ifp; } /* Find the interface with an address */ struct interface * ifwithaddr(naddr addr, int bcast, /* notice IFF_BROADCAST address */ int remote) /* include IS_REMOTE interfaces */ { - struct interface *ifp, *possible = 0; + struct interface *ifp, *possible = NULL; remote = (remote == 0) ? IS_REMOTE : 0; for (ifp = *AHASH(addr); ifp; ifp = ifp->int_ahash) { if (ifp->int_addr != addr) continue; if ((ifp->int_state & remote) != 0) continue; if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0) return ifp; possible = ifp; } if (possible || !bcast) return possible; for (ifp = *BHASH(addr); ifp; ifp = ifp->int_bhash) { if (ifp->int_brdaddr != addr) continue; if ((ifp->int_state & remote) != 0) continue; if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0) return ifp; possible = ifp; } return possible; } /* find the interface with a name */ static struct interface * ifwithname(char *name, /* "ec0" or whatever */ naddr addr) /* 0 or network address */ { struct interface *ifp; for (;;) { - for (ifp = *nhash(name); ifp != 0; ifp = ifp->int_nhash) { + for (ifp = *nhash(name); ifp != NULL; ifp = ifp->int_nhash) { /* If the network address is not specified, * ignore any alias interfaces. Otherwise, look * for the interface with the target name and address. */ if (!strcmp(ifp->int_name, name) && ((addr == 0 && !(ifp->int_state & IS_ALIAS)) || (ifp->int_addr == addr))) return ifp; } /* If there is no known interface, maybe there is a * new interface. So just once look for new interfaces. */ if (IF_RESCAN_DELAY()) return 0; ifinit(); } } struct interface * ifwithindex(u_short ifindex, int rescan_ok) { struct interface *ifp; for (;;) { LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_index == ifindex) return ifp; } /* If there is no known interface, maybe there is a * new interface. So just once look for new interfaces. */ if (!rescan_ok || IF_RESCAN_DELAY()) return 0; ifinit(); } } /* Find an interface from which the specified address * should have come from. Used for figuring out which * interface a packet came in on. */ struct interface * iflookup(naddr addr) { struct interface *ifp, *maybe; int once = 0; - maybe = 0; + maybe = NULL; for (;;) { LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_if_flags & IFF_POINTOPOINT) { /* finished with a match */ if (ifp->int_dstaddr == addr) return ifp; } else { /* finished with an exact match */ if (ifp->int_addr == addr) return ifp; /* Look for the longest approximate match. */ if (on_net(addr, ifp->int_net, ifp->int_mask) - && (maybe == 0 + && (maybe == NULL || ifp->int_mask > maybe->int_mask)) maybe = ifp; } } - if (maybe != 0 || once || IF_RESCAN_DELAY()) + if (maybe != NULL || once || IF_RESCAN_DELAY()) return maybe; once = 1; /* If there is no known interface, maybe there is a * new interface. So just once look for new interfaces. */ ifinit(); } } /* Return the classical netmask for an IP address. */ naddr /* host byte order */ std_mask(naddr addr) /* network byte order */ { addr = ntohl(addr); /* was a host, not a network */ if (addr == 0) /* default route has mask 0 */ return 0; if (IN_CLASSA(addr)) return IN_CLASSA_NET; if (IN_CLASSB(addr)) return IN_CLASSB_NET; return IN_CLASSC_NET; } /* Find the netmask that would be inferred by RIPv1 listeners * on the given interface for a given network. * If no interface is specified, look for the best fitting interface. */ naddr ripv1_mask_net(naddr addr, /* in network byte order */ struct interface *ifp) /* as seen on this interface */ { struct r1net *r1p; naddr mask = 0; if (addr == 0) /* default always has 0 mask */ return mask; - if (ifp != 0 && ifp->int_ripv1_mask != HOST_MASK) { + if (ifp != NULL && ifp->int_ripv1_mask != HOST_MASK) { /* If the target network is that of the associated interface * on which it arrived, then use the netmask of the interface. */ if (on_net(addr, ifp->int_net, ifp->int_std_mask)) mask = ifp->int_ripv1_mask; } else { /* Examine all interfaces, and if it the target seems * to have the same network number of an interface, use the * netmask of that interface. If there is more than one * such interface, prefer the interface with the longest * match. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (on_net(addr, ifp->int_std_net, ifp->int_std_mask) && ifp->int_ripv1_mask > mask && ifp->int_ripv1_mask != HOST_MASK) mask = ifp->int_ripv1_mask; } } /* check special definitions */ if (mask == 0) { - for (r1p = r1nets; r1p != 0; r1p = r1p->r1net_next) { + for (r1p = r1nets; r1p != NULL; r1p = r1p->r1net_next) { if (on_net(addr, r1p->r1net_net, r1p->r1net_match) && r1p->r1net_mask > mask) mask = r1p->r1net_mask; } /* Otherwise, make the classic A/B/C guess. */ if (mask == 0) mask = std_mask(addr); } return mask; } naddr ripv1_mask_host(naddr addr, /* in network byte order */ struct interface *ifp) /* as seen on this interface */ { naddr mask = ripv1_mask_net(addr, ifp); /* If the computed netmask does not mask the address, * then assume it is a host address */ if ((ntohl(addr) & ~mask) != 0) mask = HOST_MASK; return mask; } /* See if an IP address looks reasonable as a destination. */ int /* 0=bad */ check_dst(naddr addr) { addr = ntohl(addr); if (IN_CLASSA(addr)) { if (addr == 0) return 1; /* default */ addr >>= IN_CLASSA_NSHIFT; return (addr != 0 && addr != IN_LOOPBACKNET); } return (IN_CLASSB(addr) || IN_CLASSC(addr)); } /* See a new interface duplicates an existing interface. */ struct interface * check_dup(naddr addr, /* IP address, so network byte order */ naddr dstaddr, /* ditto */ naddr mask, /* mask, so host byte order */ int if_flags) { struct interface *ifp; LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_mask != mask) continue; if (!iff_up(ifp->int_if_flags)) continue; /* The local address can only be shared with a point-to-point * link. */ if ((!(ifp->int_state & IS_REMOTE) || !(if_flags & IS_REMOTE)) && ifp->int_addr == addr && (((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0)) return ifp; if (on_net(ifp->int_dstaddr, ntohl(dstaddr),mask)) return ifp; } return 0; } /* See that a remote gateway is reachable. * Note that the answer can change as real interfaces come and go. */ int /* 0=bad */ check_remote(struct interface *ifp) { struct rt_entry *rt; /* do not worry about other kinds */ if (!(ifp->int_state & IS_REMOTE)) return 1; rt = rtfind(ifp->int_addr); - if (rt != 0 + if (rt != NULL && rt->rt_ifp != 0 &&on_net(ifp->int_addr, rt->rt_ifp->int_net, rt->rt_ifp->int_mask)) return 1; /* the gateway cannot be reached directly from one of our * interfaces */ if (!(ifp->int_state & IS_BROKE)) { msglog("unreachable gateway %s in "_PATH_GATEWAYS, naddr_ntoa(ifp->int_addr)); if_bad(ifp); } return 0; } /* Delete an interface. */ static void ifdel(struct interface *ifp) { struct interface *ifp1; trace_if("Del", ifp); ifp->int_state |= IS_BROKE; LIST_REMOVE(ifp, int_list); *ifp->int_ahash_prev = ifp->int_ahash; if (ifp->int_ahash != 0) ifp->int_ahash->int_ahash_prev = ifp->int_ahash_prev; *ifp->int_nhash_prev = ifp->int_nhash; if (ifp->int_nhash != 0) ifp->int_nhash->int_nhash_prev = ifp->int_nhash_prev; if (ifp->int_if_flags & IFF_BROADCAST) { *ifp->int_bhash_prev = ifp->int_bhash; if (ifp->int_bhash != 0) ifp->int_bhash->int_bhash_prev = ifp->int_bhash_prev; } if (ifp->int_state & IS_REMOTE) LIST_REMOVE(ifp, remote_list); if (!(ifp->int_state & IS_ALIAS)) { /* delete aliases when the main interface dies */ LIST_FOREACH(ifp1, &ifnet, int_list) { if (ifp1 != ifp && !strcmp(ifp->int_name, ifp1->int_name)) ifdel(ifp1); } if ((ifp->int_if_flags & IFF_MULTICAST) && rip_sock >= 0) { struct group_req gr; struct sockaddr_in *sin; memset(&gr, 0, sizeof(gr)); gr.gr_interface = ifp->int_index; sin = (struct sockaddr_in *)&gr.gr_group; sin->sin_family = AF_INET; #ifdef _HAVE_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_addr.s_addr = htonl(INADDR_RIP_GROUP); if (setsockopt(rip_sock, IPPROTO_IP, MCAST_LEAVE_GROUP, &gr, sizeof(gr)) < 0 && errno != EADDRNOTAVAIL && !TRACEACTIONS) LOGERR("setsockopt(MCAST_LEAVE_GROUP RIP)"); if (rip_sock_mcast == ifp) - rip_sock_mcast = 0; + rip_sock_mcast = NULL; } if (ifp->int_rip_sock >= 0) { (void)close(ifp->int_rip_sock); ifp->int_rip_sock = -1; fix_select(); } tot_interfaces--; if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces--; /* Zap all routes associated with this interface. * Assume routes just using gateways beyond this interface * will timeout naturally, and have probably already died. */ (void)rn_walktree(rhead, walk_bad, 0); set_rdisc_mg(ifp, 0); if_bad_rdisc(ifp); } free(ifp); } /* Mark an interface ill. */ void if_sick(struct interface *ifp) { if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) { ifp->int_state |= IS_SICK; ifp->int_act_time = NEVER; trace_if("Chg", ifp); LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); } } /* Mark an interface dead. */ static void if_bad(struct interface *ifp) { struct interface *ifp1; if (ifp->int_state & IS_BROKE) return; LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); ifp->int_state |= (IS_BROKE | IS_SICK); ifp->int_act_time = NEVER; ifp->int_query_time = NEVER; ifp->int_data.ts = now.tv_sec; trace_if("Chg", ifp); if (!(ifp->int_state & IS_ALIAS)) { LIST_FOREACH(ifp1, &ifnet, int_list) { if (ifp1 != ifp && !strcmp(ifp->int_name, ifp1->int_name)) if_bad(ifp1); } (void)rn_walktree(rhead, walk_bad, 0); if_bad_rdisc(ifp); } } /* Mark an interface alive */ int /* 1=it was dead */ if_ok(struct interface *ifp, const char *type) { struct interface *ifp1; if (!(ifp->int_state & IS_BROKE)) { if (ifp->int_state & IS_SICK) { trace_act("%sinterface %s to %s working better", type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); ifp->int_state &= ~IS_SICK; } return 0; } msglog("%sinterface %s to %s restored", type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); ifp->int_state &= ~(IS_BROKE | IS_SICK); ifp->int_data.ts = 0; if (!(ifp->int_state & IS_ALIAS)) { LIST_FOREACH(ifp1, &ifnet, int_list) { if (ifp1 != ifp && !strcmp(ifp->int_name, ifp1->int_name)) if_ok(ifp1, type); } if_ok_rdisc(ifp); } if (ifp->int_state & IS_REMOTE) { if (!addrouteforif(ifp)) return 0; } return 1; } /* disassemble routing message */ void rt_xaddrs(struct rt_addrinfo *info, struct sockaddr *sa, struct sockaddr *lim, int addrs) { int i; #ifdef _HAVE_SA_LEN static struct sockaddr sa_zero; #endif memset(info, 0, sizeof(*info)); info->rti_addrs = addrs; for (i = 0; i < RTAX_MAX && sa < lim; i++) { if ((addrs & (1 << i)) == 0) continue; info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; sa = (struct sockaddr *)((char*)(sa) + SA_SIZE(sa)); } } /* Find the network interfaces which have configured themselves. * This must be done regularly, if only for extra addresses * that come and go on interfaces. */ void ifinit(void) { static struct ifa_msghdr *sysctl_buf; static size_t sysctl_buf_size = 0; uint complaints = 0; static u_int prev_complaints = 0; # define COMP_NOT_INET 0x001 # define COMP_NOADDR 0x002 # define COMP_BADADDR 0x004 # define COMP_NODST 0x008 # define COMP_NOBADR 0x010 # define COMP_NOMASK 0x020 # define COMP_DUP 0x040 # define COMP_BAD_METRIC 0x080 # define COMP_NETMASK 0x100 struct interface ifs, ifs0, *ifp, *ifp1; struct rt_entry *rt; size_t needed; int mib[6]; struct if_msghdr *ifm; void *ifam_lim; struct ifa_msghdr *ifam, *ifam2; int in, ierr, out, oerr; struct intnet *intnetp; struct rt_addrinfo info; #ifdef SIOCGIFMETRIC struct ifreq ifr; #endif last_ifinit = now; ifinit_timer.tv_sec = now.tv_sec + (supplier ? CHECK_ACT_INTERVAL : CHECK_QUIET_INTERVAL); /* mark all interfaces so we can get rid of those that disappear */ LIST_FOREACH(ifp, &ifnet, int_list) ifp->int_state &= ~(IS_CHECKED | IS_DUP); /* Fetch the interface list, without too many system calls * since we do it repeatedly. */ mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_IFLIST; mib[5] = 0; for (;;) { if ((needed = sysctl_buf_size) != 0) { if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) break; /* retry if the table grew */ if (errno != ENOMEM && errno != EFAULT) BADERR(1, "ifinit: sysctl(RT_IFLIST)"); free(sysctl_buf); needed = 0; } if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) BADERR(1,"ifinit: sysctl(RT_IFLIST) estimate"); sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit sysctl"); } /* XXX: thanks to malloc(3), alignment can be presumed OK */ ifam_lim = (char *)sysctl_buf + needed; for (ifam = sysctl_buf; (void *)ifam < ifam_lim; ifam = ifam2) { ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); #ifdef RTM_OIFINFO if (ifam->ifam_type == RTM_OIFINFO) continue; /* just ignore compat message */ #endif if (ifam->ifam_type == RTM_IFINFO) { struct sockaddr_dl *sdl; ifm = (struct if_msghdr *)ifam; /* make prototype structure for the IP aliases */ memset(&ifs0, 0, sizeof(ifs0)); ifs0.int_rip_sock = -1; ifs0.int_index = ifm->ifm_index; ifs0.int_if_flags = ifm->ifm_flags; ifs0.int_state = IS_CHECKED; ifs0.int_query_time = NEVER; ifs0.int_act_time = now.tv_sec; ifs0.int_data.ts = now.tv_sec; ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets; ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors; ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets; ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors; #ifdef sgi ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops; #endif sdl = (struct sockaddr_dl *)(ifm + 1); sdl->sdl_data[sdl->sdl_nlen] = 0; strncpy(ifs0.int_name, sdl->sdl_data, MIN(sizeof(ifs0.int_name), sdl->sdl_nlen)); continue; } if (ifam->ifam_type != RTM_NEWADDR) { logbad(1,"ifinit: out of sync"); continue; } rt_xaddrs(&info, (struct sockaddr *)(ifam+1), (struct sockaddr *)ifam2, ifam->ifam_addrs); /* Prepare for the next address of this interface, which * will be an alias. * Do not output RIP or Router-Discovery packets via aliases. */ memcpy(&ifs, &ifs0, sizeof(ifs)); ifs0.int_state |= (IS_ALIAS | IS_NO_RIP_OUT | IS_NO_RDISC); if (INFO_IFA(&info) == 0) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOADDR)) msglog("%s has no address", ifs.int_name); complaints |= COMP_NOADDR; } continue; } if (INFO_IFA(&info)->sa_family != AF_INET) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOT_INET)) trace_act("%s: not AF_INET", ifs.int_name); complaints |= COMP_NOT_INET; } continue; } ifs.int_addr = S_ADDR(INFO_IFA(&info)); if (ntohl(ifs.int_addr)>>24 == 0 || ntohl(ifs.int_addr)>>24 == 0xff) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_BADADDR)) msglog("%s has a bad address", ifs.int_name); complaints |= COMP_BADADDR; } continue; } if (ifs.int_if_flags & IFF_LOOPBACK) { ifs.int_state |= IS_NO_RIP | IS_NO_RDISC; if (ifs.int_addr == htonl(INADDR_LOOPBACK)) ifs.int_state |= IS_PASSIVE; ifs.int_dstaddr = ifs.int_addr; ifs.int_mask = HOST_MASK; ifs.int_ripv1_mask = HOST_MASK; ifs.int_std_mask = std_mask(ifs.int_dstaddr); ifs.int_net = ntohl(ifs.int_dstaddr); if (!foundloopback) { foundloopback = 1; loopaddr = ifs.int_addr; loop_rts.rts_gate = loopaddr; loop_rts.rts_router = loopaddr; } } else if (ifs.int_if_flags & IFF_POINTOPOINT) { if (INFO_BRD(&info) == 0 || INFO_BRD(&info)->sa_family != AF_INET) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NODST)) msglog("%s has a bad" " destination address", ifs.int_name); complaints |= COMP_NODST; } continue; } ifs.int_dstaddr = S_ADDR(INFO_BRD(&info)); if (ntohl(ifs.int_dstaddr)>>24 == 0 || ntohl(ifs.int_dstaddr)>>24 == 0xff) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NODST)) msglog("%s has a bad" " destination address", ifs.int_name); complaints |= COMP_NODST; } continue; } ifs.int_mask = HOST_MASK; ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info))); ifs.int_std_mask = std_mask(ifs.int_dstaddr); ifs.int_net = ntohl(ifs.int_dstaddr); } else { if (INFO_MASK(&info) == 0) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOMASK)) msglog("%s has no netmask", ifs.int_name); complaints |= COMP_NOMASK; } continue; } ifs.int_dstaddr = ifs.int_addr; ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); ifs.int_ripv1_mask = ifs.int_mask; ifs.int_std_mask = std_mask(ifs.int_addr); ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; if (ifs.int_mask != ifs.int_std_mask) ifs.int_state |= IS_SUBNET; if (ifs.int_if_flags & IFF_BROADCAST) { if (INFO_BRD(&info) == 0) { if (iff_up(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOBADR)) msglog("%s has" "no broadcast address", ifs.int_name); complaints |= COMP_NOBADR; } continue; } ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); } } ifs.int_std_net = ifs.int_net & ifs.int_std_mask; ifs.int_std_addr = htonl(ifs.int_std_net); /* Use a minimum metric of one. Treat the interface metric * (default 0) as an increment to the hop count of one. * * The metric obtained from the routing socket dump of * interface addresses is wrong. It is not set by the * SIOCSIFMETRIC ioctl. */ #ifdef SIOCGIFMETRIC strncpy(ifr.ifr_name, ifs.int_name, sizeof(ifr.ifr_name)); if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { DBGERR(1, "ioctl(SIOCGIFMETRIC)"); ifs.int_metric = 0; } else { ifs.int_metric = ifr.ifr_metric; } #else ifs.int_metric = ifam->ifam_metric; #endif if (ifs.int_metric > HOPCNT_INFINITY) { ifs.int_metric = 0; if (!(prev_complaints & COMP_BAD_METRIC) && iff_up(ifs.int_if_flags)) { complaints |= COMP_BAD_METRIC; msglog("%s has a metric of %d", ifs.int_name, ifs.int_metric); } } /* See if this is a familiar interface. * If so, stop worrying about it if it is the same. * Start it over if it now is to somewhere else, as happens * frequently with PPP and SLIP. */ ifp = ifwithname(ifs.int_name, ((ifs.int_state & IS_ALIAS) ? ifs.int_addr : 0)); - if (ifp != 0) { + if (ifp != NULL) { ifp->int_state |= IS_CHECKED; if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags) & (IFF_BROADCAST | IFF_LOOPBACK | IFF_POINTOPOINT | IFF_MULTICAST)) || 0 != ((ifp->int_state ^ ifs.int_state) & IS_ALIAS) || ifp->int_addr != ifs.int_addr || ifp->int_brdaddr != ifs.int_brdaddr || ifp->int_dstaddr != ifs.int_dstaddr || ifp->int_mask != ifs.int_mask || ifp->int_metric != ifs.int_metric) { /* Forget old information about * a changed interface. */ trace_act("interface %s has changed", ifp->int_name); ifdel(ifp); - ifp = 0; + ifp = NULL; } } - if (ifp != 0) { + if (ifp != NULL) { /* The primary representative of an alias worries * about how things are working. */ if (ifp->int_state & IS_ALIAS) continue; /* note interfaces that have been turned off */ if (!iff_up(ifs.int_if_flags)) { if (iff_up(ifp->int_if_flags)) { msglog("interface %s to %s turned off", ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); if_bad(ifp); ifp->int_if_flags &= ~IFF_UP; } else if (now.tv_sec>(ifp->int_data.ts + CHECK_BAD_INTERVAL)) { trace_act("interface %s has been off" " %jd seconds; forget it", ifp->int_name, (intmax_t)now.tv_sec - ifp->int_data.ts); ifdel(ifp); } continue; } /* or that were off and are now ok */ if (!iff_up(ifp->int_if_flags)) { ifp->int_if_flags |= IFF_UP; (void)if_ok(ifp, ""); } /* If it has been long enough, * see if the interface is broken. */ if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL) continue; in = ifs.int_data.ipackets - ifp->int_data.ipackets; ierr = ifs.int_data.ierrors - ifp->int_data.ierrors; out = ifs.int_data.opackets - ifp->int_data.opackets; oerr = ifs.int_data.oerrors - ifp->int_data.oerrors; #ifdef sgi /* Through at least IRIX 6.2, PPP and SLIP * count packets dropped by the filters. * But FDDI rings stuck non-operational count * dropped packets as they wait for improvement. */ if (!(ifp->int_if_flags & IFF_POINTOPOINT)) oerr += (ifs.int_data.odrops - ifp->int_data.odrops); #endif /* If the interface just awoke, restart the counters. */ if (ifp->int_data.ts == 0) { ifp->int_data = ifs.int_data; continue; } ifp->int_data = ifs.int_data; /* Withhold judgment when the short error * counters wrap or the interface is reset. */ if (ierr < 0 || in < 0 || oerr < 0 || out < 0) { LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); continue; } /* Withhold judgement when there is no traffic */ if (in == 0 && out == 0 && ierr == 0 && oerr == 0) continue; /* It is bad if input or output is not working. * Require presistent problems before marking it dead. */ if ((in <= ierr && ierr > 0) || (out <= oerr && oerr > 0)) { if (!(ifp->int_state & IS_SICK)) { trace_act("interface %s to %s" " sick: in=%d ierr=%d" " out=%d oerr=%d", ifp->int_name, naddr_ntoa(ifp->int_dstaddr), in, ierr, out, oerr); if_sick(ifp); continue; } if (!(ifp->int_state & IS_BROKE)) { msglog("interface %s to %s broken:" " in=%d ierr=%d out=%d oerr=%d", ifp->int_name, naddr_ntoa(ifp->int_dstaddr), in, ierr, out, oerr); if_bad(ifp); } continue; } /* otherwise, it is active and healthy */ ifp->int_act_time = now.tv_sec; (void)if_ok(ifp, ""); continue; } /* This is a new interface. * If it is dead, forget it. */ if (!iff_up(ifs.int_if_flags)) continue; /* If it duplicates an existing interface, * complain about it, mark the other one * duplicated, and forget this one. */ ifp = check_dup(ifs.int_addr,ifs.int_dstaddr,ifs.int_mask, ifs.int_if_flags); - if (ifp != 0) { + if (ifp != NULL) { /* Ignore duplicates of itself, caused by having * IP aliases on the same network. */ if (!strcmp(ifp->int_name, ifs.int_name)) continue; if (!(prev_complaints & COMP_DUP)) { complaints |= COMP_DUP; msglog("%s (%s%s%s) is duplicated by" " %s (%s%s%s)", ifs.int_name, addrname(ifs.int_addr,ifs.int_mask,1), ((ifs.int_if_flags & IFF_POINTOPOINT) ? "-->" : ""), ((ifs.int_if_flags & IFF_POINTOPOINT) ? naddr_ntoa(ifs.int_dstaddr) : ""), ifp->int_name, addrname(ifp->int_addr,ifp->int_mask,1), ((ifp->int_if_flags & IFF_POINTOPOINT) ? "-->" : ""), ((ifp->int_if_flags & IFF_POINTOPOINT) ? naddr_ntoa(ifp->int_dstaddr) : "")); } ifp->int_state |= IS_DUP; continue; } if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | IFF_BROADCAST | IFF_LOOPBACK))) { trace_act("%s is neither broadcast, point-to-point," " nor loopback", ifs.int_name); if (!(ifs.int_state & IFF_MULTICAST)) ifs.int_state |= IS_NO_RDISC; } /* It is new and ok. Add it to the list of interfaces */ ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit ifp"); memcpy(ifp, &ifs, sizeof(*ifp)); get_parms(ifp); if_link(ifp); trace_if("Add", ifp); /* Notice likely bad netmask. */ if (!(prev_complaints & COMP_NETMASK) && !(ifp->int_if_flags & IFF_POINTOPOINT) && ifp->int_addr != RIP_DEFAULT) { LIST_FOREACH(ifp1, &ifnet, int_list) { if (ifp1->int_mask == ifp->int_mask) continue; if (ifp1->int_if_flags & IFF_POINTOPOINT) continue; if (ifp1->int_dstaddr == RIP_DEFAULT) continue; /* ignore aliases on the right network */ if (!strcmp(ifp->int_name, ifp1->int_name)) continue; if (on_net(ifp->int_dstaddr, ifp1->int_net, ifp1->int_mask) || on_net(ifp1->int_dstaddr, ifp->int_net, ifp->int_mask)) { msglog("possible netmask problem" " between %s:%s and %s:%s", ifp->int_name, addrname(htonl(ifp->int_net), ifp->int_mask, 1), ifp1->int_name, addrname(htonl(ifp1->int_net), ifp1->int_mask, 1)); complaints |= COMP_NETMASK; } } } if (!(ifp->int_state & IS_ALIAS)) { /* Count the # of directly connected networks. */ if (!(ifp->int_if_flags & IFF_LOOPBACK)) tot_interfaces++; if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces++; /* turn on router discovery and RIP If needed */ if_ok_rdisc(ifp); rip_on(ifp); } } /* If we are multi-homed and have at least two interfaces * listening to RIP, then output by default. */ if (!supplier_set && rip_interfaces > 1) set_supplier(); /* If we are multi-homed, optionally advertise a route to * our main address. */ if (advertise_mhome || (tot_interfaces > 1 && mhome - && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 + && (ifp = ifwithaddr(myaddr, 0, 0)) != NULL && foundloopback)) { advertise_mhome = 1; rt = rtget(myaddr, HOST_MASK); - if (rt != 0) { + if (rt != NULL) { if (rt->rt_ifp != ifp || rt->rt_router != loopaddr) { rtdelete(rt); - rt = 0; + rt = NULL; } else { loop_rts.rts_ifp = ifp; loop_rts.rts_metric = 0; loop_rts.rts_time = rt->rt_time; rtchange(rt, rt->rt_state | RS_MHOME, &loop_rts, 0); } } - if (rt == 0) { + if (rt == NULL) { loop_rts.rts_ifp = ifp; loop_rts.rts_metric = 0; rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts); } } LIST_FOREACH_SAFE(ifp, &ifnet, int_list, ifp1) { /* Forget any interfaces that have disappeared. */ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { trace_act("interface %s has disappeared", ifp->int_name); ifdel(ifp); continue; } if ((ifp->int_state & IS_BROKE) && !(ifp->int_state & IS_PASSIVE)) LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); /* If we ever have a RIPv1 interface, assume we always will. * It might come back if it ever goes away. */ if (!(ifp->int_state & IS_NO_RIPV1_OUT) && supplier) have_ripv1_out = 1; if (!(ifp->int_state & IS_NO_RIPV1_IN)) have_ripv1_in = 1; } LIST_FOREACH(ifp, &ifnet, int_list) { /* Ensure there is always a network route for interfaces, * after any dead interfaces have been deleted, which * might affect routes for point-to-point links. */ if (!addrouteforif(ifp)) continue; /* Add routes to the local end of point-to-point interfaces * using loopback. */ if ((ifp->int_if_flags & IFF_POINTOPOINT) && !(ifp->int_state & IS_REMOTE) && foundloopback) { /* Delete any routes to the network address through * foreign routers. Remove even static routes. */ del_static(ifp->int_addr, HOST_MASK, 0, 0); rt = rtget(ifp->int_addr, HOST_MASK); - if (rt != 0 && rt->rt_router != loopaddr) { + if (rt != NULL && rt->rt_router != loopaddr) { rtdelete(rt); - rt = 0; + rt = NULL; } - if (rt != 0) { + if (rt != NULL) { if (!(rt->rt_state & RS_LOCAL) || rt->rt_metric > ifp->int_metric) { ifp1 = ifp; } else { ifp1 = rt->rt_ifp; } loop_rts.rts_ifp = ifp1; loop_rts.rts_metric = 0; loop_rts.rts_time = rt->rt_time; rtchange(rt, ((rt->rt_state & ~RS_NET_SYN) | (RS_IF|RS_LOCAL)), &loop_rts, 0); } else { loop_rts.rts_ifp = ifp; loop_rts.rts_metric = 0; rtadd(ifp->int_addr, HOST_MASK, (RS_IF | RS_LOCAL), &loop_rts); } } } /* add the authority routes */ - for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { + for (intnetp = intnets; intnetp != NULL; + intnetp = intnetp->intnet_next) { rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); - if (rt != 0 + if (rt != NULL && !(rt->rt_state & RS_NO_NET_SYN) && !(rt->rt_state & RS_NET_INT)) { rtdelete(rt); - rt = 0; + rt = NULL; } - if (rt == 0) { - loop_rts.rts_ifp = 0; + if (rt == NULL) { + loop_rts.rts_ifp = NULL; loop_rts.rts_metric = intnetp->intnet_metric-1; rtadd(intnetp->intnet_addr, intnetp->intnet_mask, RS_NET_SYN | RS_NET_INT, &loop_rts); } } prev_complaints = complaints; } static void check_net_syn(struct interface *ifp) { struct rt_entry *rt; static struct rt_spare new; /* Turn on the need to automatically synthesize a network route * for this interface only if we are running RIPv1 on some other * interface that is on a different class-A,B,or C network. */ if (have_ripv1_out || have_ripv1_in) { ifp->int_state |= IS_NEED_NET_SYN; rt = rtget(ifp->int_std_addr, ifp->int_std_mask); - if (rt != 0 + if (rt != NULL && 0 == (rt->rt_state & RS_NO_NET_SYN) && (!(rt->rt_state & RS_NET_SYN) || rt->rt_metric > ifp->int_metric)) { rtdelete(rt); - rt = 0; + rt = NULL; } - if (rt == 0) { + if (rt == NULL) { new.rts_ifp = ifp; new.rts_gate = ifp->int_addr; new.rts_router = ifp->int_addr; new.rts_metric = ifp->int_metric; rtadd(ifp->int_std_addr, ifp->int_std_mask, RS_NET_SYN, &new); } } else { ifp->int_state &= ~IS_NEED_NET_SYN; rt = rtget(ifp->int_std_addr, ifp->int_std_mask); - if (rt != 0 + if (rt != NULL && (rt->rt_state & RS_NET_SYN) && rt->rt_ifp == ifp) rtbad_sub(rt); } } /* Add route for interface if not currently installed. * Create route to other end if a point-to-point link, * otherwise a route to this (sub)network. */ static int /* 0=bad interface */ addrouteforif(struct interface *ifp) { struct rt_entry *rt; static struct rt_spare new; naddr dst; /* skip sick interfaces */ if (ifp->int_state & IS_BROKE) return 0; /* If the interface on a subnet, then install a RIPv1 route to * the network as well (unless it is sick). */ if (ifp->int_state & IS_SUBNET) check_net_syn(ifp); dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) ? ifp->int_dstaddr : htonl(ifp->int_net)); new.rts_ifp = ifp; new.rts_router = ifp->int_addr; new.rts_gate = ifp->int_addr; new.rts_metric = ifp->int_metric; new.rts_time = now.tv_sec; /* If we are going to send packets to the gateway, * it must be reachable using our physical interfaces */ if ((ifp->int_state & IS_REMOTE) && !(ifp->int_state & IS_EXTERNAL) && !check_remote(ifp)) return 0; /* We are finished if the correct main interface route exists. * The right route must be for the right interface, not synthesized * from a subnet, be a "gateway" or not as appropriate, and so forth. */ del_static(dst, ifp->int_mask, 0, 0); rt = rtget(dst, ifp->int_mask); - if (rt != 0) { + if (rt != NULL) { if ((rt->rt_ifp != ifp || rt->rt_router != ifp->int_addr) && (!(ifp->int_state & IS_DUP) || rt->rt_ifp == 0 || (rt->rt_ifp->int_state & IS_BROKE))) { rtdelete(rt); - rt = 0; + rt = NULL; } else { rtchange(rt, ((rt->rt_state | RS_IF) & ~(RS_NET_SYN | RS_LOCAL)), &new, 0); } } - if (rt == 0) { + if (rt == NULL) { if (ifp->int_transitions++ > 0) trace_act("re-install interface %s", ifp->int_name); rtadd(dst, ifp->int_mask, RS_IF, &new); } return 1; } Index: head/sbin/routed/input.c =================================================================== --- head/sbin/routed/input.c (revision 299767) +++ head/sbin/routed/input.c (revision 299768) @@ -1,1034 +1,1035 @@ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.26 $"); #ident "$Revision: 2.26 $" #endif static void input(struct sockaddr_in *, struct interface *, struct interface *, struct rip *, int); static void input_route(naddr, naddr, struct rt_spare *, struct netinfo *); static int ck_passwd(struct interface *, struct rip *, void *, naddr, struct msg_limit *); /* process RIP input */ void read_rip(int sock, struct interface *sifp) { struct sockaddr_in from; struct interface *aifp; socklen_t fromlen; int cc; #ifdef USE_PASSIFNAME static struct msg_limit bad_name; struct { char ifname[IFNAMSIZ]; union pkt_buf pbuf; } inbuf; #else struct { union pkt_buf pbuf; } inbuf; #endif 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", (int)fromlen); /* aifp is the "authenticated" interface via which the packet * arrived. In fact, it is only the interface on which * the packet should have arrived based on is source * address. * sifp is interface associated with the socket through which * the packet was received. */ #ifdef USE_PASSIFNAME if ((cc -= sizeof(inbuf.ifname)) < 0) logbad(0,"missing USE_PASSIFNAME; only %d bytes", cc+sizeof(inbuf.ifname)); /* check the remote interfaces first */ LIST_FOREACH(aifp, &remote_if, remote_list) { if (aifp->int_addr == from.sin_addr.s_addr) break; } - if (aifp == 0) { + if (aifp == NULL) { aifp = ifwithname(inbuf.ifname, 0); - if (aifp == 0) { + if (aifp == NULL) { msglim(&bad_name, from.sin_addr.s_addr, "impossible interface name %.*s", IFNAMSIZ, inbuf.ifname); } else if (((aifp->int_if_flags & IFF_POINTOPOINT) && aifp->int_dstaddr!=from.sin_addr.s_addr) || (!(aifp->int_if_flags & IFF_POINTOPOINT) && !on_net(from.sin_addr.s_addr, aifp->int_net, aifp->int_mask))) { /* If it came via the wrong interface, do not * trust it. */ - aifp = 0; + aifp = NULL; } } #else aifp = iflookup(from.sin_addr.s_addr); #endif - if (sifp == 0) + if (sifp == NULL) sifp = aifp; input(&from, sifp, aifp, &inbuf.pbuf.rip, cc); } } /* Process a RIP packet */ static void input(struct sockaddr_in *from, /* received from this IP address */ struct interface *sifp, /* interface of incoming socket */ struct interface *aifp, /* "authenticated" interface */ struct rip *rip, int cc) { # define FROM_NADDR from->sin_addr.s_addr static struct msg_limit use_auth, bad_len, bad_mask; static struct msg_limit unk_router, bad_router, bad_nhop; struct rt_entry *rt; struct rt_spare new; struct netinfo *n, *lim; struct interface *ifp1; naddr gate, mask, v1_mask, dst, ddst_h = 0; struct auth *ap; - struct tgate *tg = 0; + struct tgate *tg = NULL; struct tgate_net *tn; int i, j; /* Notice when we hear from a remote gateway */ - if (aifp != 0 + if (aifp != NULL && (aifp->int_state & IS_REMOTE)) aifp->int_act_time = now.tv_sec; trace_rip("Recv", "from", from, sifp, rip, cc); - if (sifp == 0) { + if (sifp == NULL) { trace_pkt(" discard a request from an indirect router" " (possibly an attack)"); return; } if (rip->rip_vers == 0) { msglim(&bad_router, FROM_NADDR, "RIP version 0, cmd %d, packet received from %s", rip->rip_cmd, naddr_ntoa(FROM_NADDR)); return; } else if (rip->rip_vers > RIPv2) { rip->rip_vers = RIPv2; } if (cc > (int)OVER_MAXPACKETSIZE) { msglim(&bad_router, FROM_NADDR, "packet at least %d bytes too long received from %s", cc-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); return; } n = rip->rip_nets; lim = (struct netinfo *)((char*)rip + cc); /* 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. Why authenticate queries? * Why should a RIPv2 implementation with authentication disabled * not be able to listen to RIPv2 packets with authentication, while * RIPv1 systems will listen? Crazy! */ if (!auth_ok && rip->rip_vers == RIPv2 && n < lim && n->n_family == RIP_AF_AUTH) { msglim(&use_auth, FROM_NADDR, "RIPv2 message with authentication from %s discarded", naddr_ntoa(FROM_NADDR)); return; } switch (rip->rip_cmd) { case RIPCMD_REQUEST: /* For mere requests, be a little sloppy about the source */ - if (aifp == 0) + if (aifp == NULL) aifp = sifp; /* Are we talking to ourself or a remote gateway? */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { if (ifp1->int_state & IS_REMOTE) { /* remote gateway */ aifp = ifp1; if (check_remote(aifp)) { aifp->int_act_time = now.tv_sec; (void)if_ok(aifp, "remote "); } } else if (from->sin_port == htons(RIP_PORT)) { trace_pkt(" discard our own RIP request"); return; } } /* did the request come from a router? */ if (from->sin_port == htons(RIP_PORT)) { /* yes, ignore the request if RIP is off so that * the router does not depend on us. */ if (rip_sock < 0 - || (aifp != 0 + || (aifp != NULL && IS_RIP_OUT_OFF(aifp->int_state))) { trace_pkt(" discard request while RIP off"); return; } } /* According to RFC 1723, we should ignore unauthenticated * 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? When RIP * is still the most common router-discovery protocol * and so hosts need to send queries that will be answered? * What about `rtquery`? * Maybe on firewalls you'd care, but not enough to * give up the diagnostic facilities of remote probing. */ if (n >= lim) { msglim(&bad_len, FROM_NADDR, "empty request from %s", naddr_ntoa(FROM_NADDR)); return; } if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { msglim(&bad_len, FROM_NADDR, "request of bad length (%d) from %s", cc, naddr_ntoa(FROM_NADDR)); } if (rip->rip_vers == RIPv2 - && (aifp == 0 || (aifp->int_state & IS_NO_RIPV1_OUT))) { + && (aifp == NULL || (aifp->int_state & IS_NO_RIPV1_OUT))) { v12buf.buf->rip_vers = RIPv2; /* If we have a secret but it is a cleartext secret, * do not disclose our secret unless the other guy * already knows it. */ ap = find_auth(aifp); - if (ap != 0 && ap->type == RIP_AUTH_PW + if (ap != NULL && ap->type == RIP_AUTH_PW && n->n_family == RIP_AF_AUTH && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) - ap = 0; + ap = NULL; } else { v12buf.buf->rip_vers = RIPv1; - ap = 0; + ap = NULL; } clr_ws_buf(&v12buf, ap); do { 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 * (i.e. a query). */ if (n->n_family == RIP_AF_UNSPEC && n->n_metric == HOPCNT_INFINITY) { /* Answer a query from a utility program * with all we know. */ if (aifp == NULL) { trace_pkt("ignore remote query"); return; } if (from->sin_port != htons(RIP_PORT)) { /* * insecure: query from non-router node * > 1: allow from distant node * > 0: allow from neighbor node * == 0: deny */ if ((aifp != NULL && insecure > 0) || (aifp == NULL && insecure > 1)) supply(from, aifp, OUT_QUERY, 0, - rip->rip_vers, ap != 0); + rip->rip_vers, + ap != NULL); else trace_pkt("Warning: " "possible attack detected"); return; } /* A router trying to prime its tables. * Filter the answer 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 as a router. */ - if (aifp == 0) { + if (aifp == NULL) { trace_pkt("ignore distant router"); return; } if (!supplier || IS_RIP_OFF(aifp->int_state)) { trace_pkt("ignore; not supplying"); return; } /* Do not answer a RIPv1 router if * we are sending RIPv2. But do offer * poor man's router discovery. */ if ((aifp->int_state & IS_NO_RIPV1_OUT) && rip->rip_vers == RIPv1) { if (!(aifp->int_state & IS_PM_RDISC)) { trace_pkt("ignore; sending RIPv2"); return; } v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = RIP_DEFAULT; i = aifp->int_d_metric; - if (0 != (rt = rtget(RIP_DEFAULT, 0))) { + if (NULL != (rt = rtget(RIP_DEFAULT, 0))) { j = (rt->rt_metric +aifp->int_metric +aifp->int_adj_outmetric +1); if (i > j) i = j; } v12buf.n->n_metric = htonl(i); v12buf.n++; break; } /* Respond with RIPv1 instead of RIPv2 if * that is what we are broadcasting on the * interface to keep the remote router from * getting the wrong initial idea of the * routes we send. */ supply(from, aifp, OUT_UNICAST, 0, (aifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1, - ap != 0); + ap != NULL); return; } /* Ignore authentication */ if (n->n_family == RIP_AF_AUTH) continue; if (n->n_family != RIP_AF_INET) { msglim(&bad_router, FROM_NADDR, "request from %s for unsupported" " (af %d) %s", naddr_ntoa(FROM_NADDR), ntohs(n->n_family), naddr_ntoa(n->n_dst)); return; } /* We are being asked about a specific destination. */ dst = n->n_dst; if (!check_dst(dst)) { msglim(&bad_router, FROM_NADDR, "bad queried destination %s from %s", naddr_ntoa(dst), naddr_ntoa(FROM_NADDR)); return; } /* decide what mask was intended */ if (rip->rip_vers == RIPv1 || 0 == (mask = ntohl(n->n_mask)) || 0 != (ntohl(dst) & ~mask)) mask = ripv1_mask_host(dst, aifp); /* try to find the answer */ rt = rtget(dst, mask); if (!rt && dst != RIP_DEFAULT) rt = rtfind(n->n_dst); if (v12buf.buf->rip_vers != RIPv1) v12buf.n->n_mask = mask; - if (rt == 0) { + if (rt == NULL) { /* we do not have the answer */ v12buf.n->n_metric = HOPCNT_INFINITY; } else { /* we have the answer, so compute the * right metric and next hop. */ v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = dst; j = rt->rt_metric+1; if (!aifp) ++j; else j += (aifp->int_metric + aifp->int_adj_outmetric); if (j < HOPCNT_INFINITY) v12buf.n->n_metric = j; else v12buf.n->n_metric = HOPCNT_INFINITY; if (v12buf.buf->rip_vers != RIPv1) { v12buf.n->n_tag = rt->rt_tag; v12buf.n->n_mask = mask; - if (aifp != 0 + if (aifp != NULL && on_net(rt->rt_gate, aifp->int_net, aifp->int_mask) && rt->rt_gate != aifp->int_addr) v12buf.n->n_nhop = rt->rt_gate; } } v12buf.n->n_metric = htonl(v12buf.n->n_metric); /* Stop paying attention if we fill the output buffer. */ if (++v12buf.n >= v12buf.lim) break; } while (++n < lim); /* Send the answer about specific routes. */ - if (ap != 0 && ap->type == RIP_AUTH_MD5) + if (ap != NULL && ap->type == RIP_AUTH_MD5) end_md5_auth(&v12buf, ap); if (from->sin_port != htons(RIP_PORT)) { /* query */ (void)output(OUT_QUERY, from, aifp, v12buf.buf, ((char *)v12buf.n - (char*)v12buf.buf)); } else if (supplier) { (void)output(OUT_UNICAST, from, aifp, v12buf.buf, ((char *)v12buf.n - (char*)v12buf.buf)); } else { /* Only answer a router if we are a supplier * to keep an unwary host that is just starting * from picking us an a router. */ ; } return; case RIPCMD_TRACEON: case RIPCMD_TRACEOFF: /* Notice that trace messages are turned off for all possible * abuse if _PATH_TRACE is undefined in pathnames.h. * Notice also that because of the way the trace file is * handled in trace.c, no abuse is plausible even if * _PATH_TRACE_ is defined. * * First verify message came from a privileged port. */ if (ntohs(from->sin_port) > IPPORT_RESERVED) { msglog("trace command from untrusted port on %s", naddr_ntoa(FROM_NADDR)); return; } - if (aifp == 0) { + if (aifp == NULL) { msglog("trace command from unknown router %s", naddr_ntoa(FROM_NADDR)); return; } if (rip->rip_cmd == RIPCMD_TRACEON) { rip->rip_tracefile[cc-4] = '\0'; set_tracefile((char*)rip->rip_tracefile, "trace command: %s\n", 0); } else { trace_off("tracing turned off by %s", naddr_ntoa(FROM_NADDR)); } return; case RIPCMD_RESPONSE: if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { msglim(&bad_len, FROM_NADDR, "response of bad length (%d) from %s", cc, naddr_ntoa(FROM_NADDR)); } /* verify message came from a router */ if (from->sin_port != ntohs(RIP_PORT)) { msglim(&bad_router, FROM_NADDR, " discard RIP response from unknown port" " %d on %s", ntohs(from->sin_port), naddr_ntoa(FROM_NADDR)); return; } if (rip_sock < 0) { trace_pkt(" discard response while RIP off"); return; } /* Are we talking to ourself or a remote gateway? */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { if (ifp1->int_state & IS_REMOTE) { /* remote gateway */ aifp = ifp1; if (check_remote(aifp)) { aifp->int_act_time = now.tv_sec; (void)if_ok(aifp, "remote "); } } else { trace_pkt(" discard our own RIP response"); return; } } /* Accept routing packets from routers directly connected * via broadcast or point-to-point networks, and from * those listed in /etc/gateways. */ - if (aifp == 0) { + if (aifp == NULL) { msglim(&unk_router, FROM_NADDR, " discard response from %s" " via unexpected interface", naddr_ntoa(FROM_NADDR)); return; } if (IS_RIP_IN_OFF(aifp->int_state)) { trace_pkt(" discard RIPv%d response" " via disabled interface %s", rip->rip_vers, aifp->int_name); return; } if (n >= lim) { msglim(&bad_len, FROM_NADDR, "empty response from %s", naddr_ntoa(FROM_NADDR)); return; } if (((aifp->int_state & IS_NO_RIPV1_IN) && rip->rip_vers == RIPv1) || ((aifp->int_state & IS_NO_RIPV2_IN) && rip->rip_vers != RIPv1)) { trace_pkt(" discard RIPv%d response", rip->rip_vers); return; } /* Ignore routes via dead interface. */ if (aifp->int_state & IS_BROKE) { trace_pkt("discard response via broken interface %s", aifp->int_name); return; } /* If the interface cares, ignore bad routers. * Trace but do not log this problem, because where it * happens, it happens frequently. */ if (aifp->int_state & IS_DISTRUST) { tg = tgates; while (tg->tgate_addr != FROM_NADDR) { tg = tg->tgate_next; - if (tg == 0) { + if (tg == NULL) { trace_pkt(" discard RIP response" " from untrusted router %s", naddr_ntoa(FROM_NADDR)); return; } } } /* Authenticate the packet if we have a secret. * If we do not have any secrets, ignore the error in * RFC 1723 and accept it regardless. */ if (aifp->int_auth[0].type != RIP_AUTH_NONE && rip->rip_vers != RIPv1 && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) return; do { if (n->n_family == RIP_AF_AUTH) continue; n->n_metric = ntohl(n->n_metric); dst = n->n_dst; if (n->n_family != RIP_AF_INET && (n->n_family != RIP_AF_UNSPEC || dst != RIP_DEFAULT)) { msglim(&bad_router, FROM_NADDR, "route from %s to unsupported" " address family=%d destination=%s", naddr_ntoa(FROM_NADDR), n->n_family, naddr_ntoa(dst)); continue; } if (!check_dst(dst)) { msglim(&bad_router, FROM_NADDR, "bad destination %s from %s", naddr_ntoa(dst), naddr_ntoa(FROM_NADDR)); return; } if (n->n_metric == 0 || n->n_metric > HOPCNT_INFINITY) { msglim(&bad_router, FROM_NADDR, "bad metric %d from %s" " for destination %s", n->n_metric, naddr_ntoa(FROM_NADDR), naddr_ntoa(dst)); return; } /* Notice the next-hop. */ gate = FROM_NADDR; if (n->n_nhop != 0) { if (rip->rip_vers == RIPv1) { n->n_nhop = 0; } else { /* Use it only if it is valid. */ if (on_net(n->n_nhop, aifp->int_net, aifp->int_mask) && check_dst(n->n_nhop)) { gate = n->n_nhop; } else { msglim(&bad_nhop, FROM_NADDR, "router %s to %s" " has bad next hop %s", naddr_ntoa(FROM_NADDR), naddr_ntoa(dst), naddr_ntoa(n->n_nhop)); n->n_nhop = 0; } } } if (rip->rip_vers == RIPv1 || 0 == (mask = ntohl(n->n_mask))) { mask = ripv1_mask_host(dst,aifp); } else if ((ntohl(dst) & ~mask) != 0) { msglim(&bad_mask, FROM_NADDR, "router %s sent bad netmask" " %#lx with %s", naddr_ntoa(FROM_NADDR), (u_long)mask, naddr_ntoa(dst)); continue; } if (rip->rip_vers == RIPv1) n->n_tag = 0; /* Adjust metric according to incoming interface.. */ n->n_metric += (aifp->int_metric + aifp->int_adj_inmetric); if (n->n_metric > HOPCNT_INFINITY) n->n_metric = HOPCNT_INFINITY; /* Should we trust this route from this router? */ if (tg && (tn = tg->tgate_nets)->mask != 0) { for (i = 0; i < MAX_TGATE_NETS; i++, tn++) { if (on_net(dst, tn->net, tn->mask) && tn->mask <= mask) break; } if (i >= MAX_TGATE_NETS || tn->mask == 0) { trace_pkt(" ignored unauthorized %s", addrname(dst,mask,0)); continue; } } /* 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 (aifp->int_d_metric != 0 && dst == RIP_DEFAULT && (int)n->n_metric >= aifp->int_d_metric) continue; /* 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 (have_ripv1_out - && (((rt = rtget(dst,mask)) == 0 + && (((rt = rtget(dst,mask)) == NULL || !(rt->rt_state & RS_NET_SYN))) && (v1_mask = ripv1_mask_net(dst,0)) > mask) { 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. */ if (TRACECONTENTS) trace_misc("accept %s-->%s as 1" " instead of %d routes", addrname(dst,mask,0), naddr_ntoa(FROM_NADDR), i+1); i = 0; } else { mask = v1_mask; } } else { i = 0; } new.rts_gate = gate; new.rts_router = FROM_NADDR; new.rts_metric = n->n_metric; new.rts_tag = n->n_tag; new.rts_time = now.tv_sec; new.rts_ifp = aifp; new.rts_de_ag = i; j = 0; for (;;) { input_route(dst, mask, &new, n); if (++j > i) break; dst = htonl(ntohl(dst) + ddst_h); } } while (++n < lim); break; } #undef FROM_NADDR } /* Process a single input route. */ static void input_route(naddr dst, /* network order */ naddr mask, struct rt_spare *new, struct netinfo *n) { int i; struct rt_entry *rt; struct rt_spare *rts, *rts0; struct interface *ifp1; /* 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. */ ifp1 = ifwithaddr(dst, 1, 1); - if (ifp1 != 0 + if (ifp1 != NULL && (!(ifp1->int_state & IS_BROKE) || (ifp1->int_state & IS_PASSIVE))) 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) { + if (rt == NULL) { /* Ignore unknown routes being poisoned. */ if (new->rts_metric == HOPCNT_INFINITY) return; /* Ignore the route if it points to us */ if (n->n_nhop != 0 - && 0 != ifwithaddr(n->n_nhop, 1, 0)) + && ifwithaddr(n->n_nhop, 1, 0) != NULL) 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, 0, new); 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 never * aggregating 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 == new->rts_router) 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 a route from the router already in the table. */ /* If the new route is a route broken down from an * aggregated route, and if the previous route is either * not a broken down route or was broken down from a finer * netmask, and if the previous route is current, * then forget this one. */ if (new->rts_de_ag > rts->rts_de_ag && now_stale <= rts->rts_time) return; /* Keep poisoned routes around only long enough to pass * the poison on. Use a new timestamp for good routes. */ if (rts->rts_metric == HOPCNT_INFINITY && new->rts_metric == HOPCNT_INFINITY) new->rts_time = rts->rts_time; /* If this is an update for the router we currently prefer, * then note it. */ if (i == NUM_SPARES) { rtchange(rt, rt->rt_state, new, 0); /* If the route got worse, check for something better. */ if (new->rts_metric > rts->rts_metric) rtswitch(rt, 0); return; } /* This is an update for a spare route. * Finished if the route is unchanged. */ if (rts->rts_gate == new->rts_gate && rts->rts_metric == new->rts_metric && rts->rts_tag == new->rts_tag) { trace_upslot(rt, rts, new); *rts = *new; return; } /* Forget it if it has gone bad. */ if (new->rts_metric == HOPCNT_INFINITY) { rts_delete(rt, rts); 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)) + && NULL != ifwithaddr(n->n_nhop, 1, 0)) return; /* the loop above set rts0=worst spare */ 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 (new->rts_metric >= rts->rts_metric) return; } trace_upslot(rt, rts, new); *rts = *new; /* try to switch to a better route */ rtswitch(rt, rts); } static int /* 0 if bad */ ck_passwd(struct interface *aifp, struct rip *rip, void *lim, naddr from, struct msg_limit *use_authp) { # define NA (rip->rip_auths) struct netauth *na2; struct auth *ap; MD5_CTX md5_ctx; u_char hash[RIP_AUTH_PW_LEN]; int i, len; assert(aifp != NULL); if ((void *)NA >= lim || NA->a_family != RIP_AF_AUTH) { msglim(use_authp, from, "missing password from %s", naddr_ntoa(from)); return 0; } /* accept any current (+/- 24 hours) password */ for (ap = aifp->int_auth, i = 0; i < MAX_AUTH_KEYS; i++, ap++) { if (ap->type != NA->a_type || (u_long)ap->start > (u_long)clk.tv_sec+DAY || (u_long)ap->end+DAY < (u_long)clk.tv_sec) continue; if (NA->a_type == RIP_AUTH_PW) { if (!memcmp(NA->au.au_pw, ap->key, RIP_AUTH_PW_LEN)) return 1; } else { /* accept MD5 secret with the right key ID */ if (NA->au.a_md5.md5_keyid != ap->keyid) continue; len = ntohs(NA->au.a_md5.md5_pkt_len); if ((len-sizeof(*rip)) % sizeof(*NA) != 0 || len != (char *)lim-(char*)rip-(int)sizeof(*NA)) { msglim(use_authp, from, "wrong MD5 RIPv2 packet length of %d" " instead of %d from %s", len, (int)((char *)lim-(char *)rip -sizeof(*NA)), naddr_ntoa(from)); return 0; } na2 = (struct netauth *)((char *)rip+len); /* Given a good hash value, these are not security * problems so be generous and accept the routes, * after complaining. */ if (TRACEPACKETS) { if (NA->au.a_md5.md5_auth_len != RIP_AUTH_MD5_HASH_LEN) msglim(use_authp, from, "unknown MD5 RIPv2 auth len %#x" " instead of %#x from %s", NA->au.a_md5.md5_auth_len, (unsigned)RIP_AUTH_MD5_HASH_LEN, naddr_ntoa(from)); if (na2->a_family != RIP_AF_AUTH) msglim(use_authp, from, "unknown MD5 RIPv2 family %#x" " instead of %#x from %s", na2->a_family, RIP_AF_AUTH, naddr_ntoa(from)); if (na2->a_type != ntohs(1)) msglim(use_authp, from, "MD5 RIPv2 hash has %#x" " instead of %#x from %s", na2->a_type, ntohs(1), naddr_ntoa(from)); } MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)rip, len + RIP_AUTH_MD5_HASH_XTRA); MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN); MD5Final(hash, &md5_ctx); if (!memcmp(hash, na2->au.au_pw, sizeof(hash))) return 1; } } msglim(use_authp, from, "bad password from %s", naddr_ntoa(from)); return 0; #undef NA } Index: head/sbin/routed/main.c =================================================================== --- head/sbin/routed/main.c (revision 299767) +++ head/sbin/routed/main.c (revision 299768) @@ -1,969 +1,969 @@ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #include "pathnames.h" #ifdef sgi #include "math.h" #endif #include #include #include __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993 " "The Regents of the University of California." " All rights reserved."); #ifdef __NetBSD__ __RCSID("$NetBSD$"); #include #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.31 $"); #ident "$Revision: 2.31 $" #endif pid_t mypid; naddr myaddr; /* system address */ static char myname[MAXHOSTNAMELEN+1]; static int verbose; int supplier; /* supply or broadcast updates */ int supplier_set; static int ipforwarding = 1; /* kernel forwarding on */ static int default_gateway; /* 1=advertise default */ static int background = 1; int ridhosts; /* 1=reduce host routes */ int mhome; /* 1=want multi-homed host route */ int advertise_mhome; /* 1=must continue advertising it */ int auth_ok = 1; /* 1=ignore auth if we do not care */ int insecure; /* Reply to special queries or not */ struct timeval epoch; /* when started */ struct timeval clk; static struct timeval prev_clk; static int usec_fudge; struct timeval now; /* current idea of time */ time_t now_stale; time_t now_expire; time_t now_garbage; static struct timeval next_bcast; /* next general broadcast */ struct timeval no_flash = { /* inhibit flash update */ EPOCH+SUPPLY_INTERVAL, 0 }; static struct timeval flush_kern_timer; static fd_set fdbits; static int sock_max; int rip_sock = -1; /* RIP socket */ const struct interface *rip_sock_mcast; /* current multicast interface */ int rt_sock; /* routing socket */ int rt_sock_seqno; static int get_rip_sock(naddr, int); static void timevalsub(struct timeval *, struct timeval *, struct timeval *); static void sigalrm(int s UNUSED); static void sigterm(int sig); int main(int argc, char *argv[]) { int n, mib[4], off; size_t len; char *p, *q; const char *cp; struct timeval wtime, t2; time_t dt; fd_set ibits; naddr p_net, p_mask; struct interface *ifp; struct parm parm; char *tracename = 0; /* Some shells are badly broken and send SIGHUP to backgrounded * processes. */ signal(SIGHUP, SIG_IGN); openlog("routed", LOG_PID, LOG_DAEMON); ftrace = stdout; gettimeofday(&clk, 0); prev_clk = clk; epoch = clk; epoch.tv_sec -= EPOCH; now.tv_sec = EPOCH; now_stale = EPOCH - STALE_TIME; now_expire = EPOCH - EXPIRE_TIME; now_garbage = EPOCH - GARBAGE_TIME; wtime.tv_sec = 0; (void)gethostname(myname, sizeof(myname)-1); (void)gethost(myname, &myaddr); while ((n = getopt(argc, argv, "isqdghmAtvT:F:P:")) != -1) { switch (n) { case 'i': insecure++; break; case 's': supplier = 1; supplier_set = 1; break; case 'q': supplier = 0; supplier_set = 1; break; case 'd': background = 0; break; case 'g': memset(&parm, 0, sizeof(parm)); parm.parm_d_metric = 1; cp = check_parms(&parm); if (cp != 0) msglog("bad -g: %s", cp); else default_gateway = 1; break; case 'h': /* suppress extra host routes */ ridhosts = 1; break; case 'm': /* advertise host route */ mhome = 1; /* on multi-homed hosts */ break; case 'A': /* Ignore authentication if we do not care. * Crazy as it is, that is what RFC 1723 requires. */ auth_ok = 0; break; case 't': new_tracelevel++; break; case 'T': tracename = optarg; break; case 'F': /* minimal routes for SLIP */ n = FAKE_METRIC; p = strchr(optarg,','); if (p && *p != '\0') { n = (int)strtoul(p+1, &q, 0); if (*q == '\0' && n <= HOPCNT_INFINITY-1 && n >= 1) *p = '\0'; } if (!getnet(optarg, &p_net, &p_mask)) { msglog("bad network; \"-F %s\"", optarg); break; } memset(&parm, 0, sizeof(parm)); parm.parm_net = p_net; parm.parm_mask = p_mask; parm.parm_d_metric = n; cp = check_parms(&parm); if (cp != 0) msglog("bad -F: %s", cp); break; case 'P': /* handle arbitrary parameters. */ q = strdup(optarg); cp = parse_parms(q, 0); if (cp != 0) msglog("%s in \"-P %s\"", cp, optarg); free(q); break; case 'v': /* display version */ verbose++; msglog("version 2.31"); break; default: goto usage; } } argc -= optind; argv += optind; if (tracename == 0 && argc >= 1) { tracename = *argv++; argc--; } if (tracename != 0 && tracename[0] == '\0') goto usage; if (argc != 0) { usage: logbad(0, "usage: routed [-sqdghmAtv] [-T tracefile]" " [-F net[,metric]] [-P parms]"); } if (geteuid() != 0) { if (verbose) exit(0); logbad(0, "requires UID 0"); } mib[0] = CTL_NET; mib[1] = PF_INET; mib[2] = IPPROTO_IP; mib[3] = IPCTL_FORWARDING; len = sizeof(ipforwarding); if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) LOGERR("sysctl(IPCTL_FORWARDING)"); if (!ipforwarding) { if (supplier) msglog("-s incompatible with ipforwarding=0"); if (default_gateway) { msglog("-g incompatible with ipforwarding=0"); default_gateway = 0; } supplier = 0; supplier_set = 1; } if (default_gateway) { if (supplier_set && !supplier) { msglog("-g and -q incompatible"); } else { supplier = 1; supplier_set = 1; } } signal(SIGALRM, sigalrm); if (!background) signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ signal(SIGTERM, sigterm); signal(SIGINT, sigterm); signal(SIGUSR1, sigtrace_on); signal(SIGUSR2, sigtrace_off); /* get into the background */ #ifdef sgi if (0 > _daemonize(background ? 0 : (_DF_NOCHDIR|_DF_NOFORK), STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO)) BADERR(0, "_daemonize()"); #else if (background && daemon(0, 1) < 0) BADERR(0,"daemon()"); #endif #if defined(__NetBSD__) pidfile(0); #endif mypid = getpid(); #ifdef __FreeBSD__ srandomdev(); #else srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid)); #endif /* prepare socket connected to the kernel. */ rt_sock = socket(AF_ROUTE, SOCK_RAW, 0); if (rt_sock < 0) BADERR(1,"rt_sock = socket()"); if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1) logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno)); off = 0; if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, &off,sizeof(off)) < 0) LOGERR("setsockopt(SO_USELOOPBACK,0)"); fix_select(); if (tracename != 0) { strncpy(inittracename, tracename, sizeof(inittracename)-1); set_tracefile(inittracename, "%s", -1); } else { tracelevel_msg("%s", -1); /* turn on tracing to stdio */ } bufinit(); /* initialize radix tree */ rtinit(); /* Pick a random part of the second for our output to minimize * collisions. * * Start broadcasting after hearing from other routers, and * at a random time so a bunch of systems do not get synchronized * after a power failure. */ intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); age_timer.tv_usec = next_bcast.tv_usec; age_timer.tv_sec = EPOCH+MIN_WAITTIME; rdisc_timer = next_bcast; ifinit_timer.tv_usec = next_bcast.tv_usec; /* Collect an initial view of the world by checking the interface * configuration and the kludge file. */ gwkludge(); ifinit(); /* Ask for routes */ rip_query(); rdisc_sol(); /* Now turn off stdio if not tracing */ if (new_tracelevel == 0) trace_close(background); /* Loop forever, listening and broadcasting. */ for (;;) { prev_clk = clk; gettimeofday(&clk, 0); if (prev_clk.tv_sec == clk.tv_sec && prev_clk.tv_usec == clk.tv_usec+usec_fudge) { /* Much of `routed` depends on time always advancing. * On systems that do not guarantee that gettimeofday() * produces unique timestamps even if called within * a single tick, use trickery like that in classic * BSD kernels. */ clk.tv_usec += ++usec_fudge; } else { usec_fudge = 0; timevalsub(&t2, &clk, &prev_clk); if (t2.tv_sec < 0 || t2.tv_sec > wtime.tv_sec + 5) { /* Deal with time changes before other * housekeeping to keep everything straight. */ dt = t2.tv_sec; if (dt > 0) dt -= wtime.tv_sec; trace_act("time changed by %d sec", (int)dt); epoch.tv_sec += dt; } } timevalsub(&now, &clk, &epoch); now_stale = now.tv_sec - STALE_TIME; now_expire = now.tv_sec - EXPIRE_TIME; now_garbage = now.tv_sec - GARBAGE_TIME; /* deal with signals that should affect tracing */ set_tracelevel(); if (stopint != 0) { rip_bcast(0); rdisc_adv(); trace_off("exiting with signal %d", stopint); exit(stopint | 128); } /* look for new or dead interfaces */ timevalsub(&wtime, &ifinit_timer, &now); if (wtime.tv_sec <= 0) { wtime.tv_sec = 0; ifinit(); rip_query(); continue; } /* Check the kernel table occasionally for mysteriously * evaporated routes */ timevalsub(&t2, &flush_kern_timer, &now); if (t2.tv_sec <= 0) { flush_kern(); flush_kern_timer.tv_sec = (now.tv_sec + CHECK_QUIET_INTERVAL); continue; } if (timercmp(&t2, &wtime, <)) wtime = t2; /* If it is time, then broadcast our routes. */ if (supplier || advertise_mhome) { timevalsub(&t2, &next_bcast, &now); if (t2.tv_sec <= 0) { /* Synchronize the aging and broadcast * timers to minimize awakenings */ age(0); rip_bcast(0); /* It is desirable to send routing updates * regularly. So schedule the next update * 30 seconds after the previous one was * scheduled, instead of 30 seconds after * the previous update was finished. * Even if we just started after discovering * a 2nd interface or were otherwise delayed, * pick a 30-second anniversary of the * original broadcast time. */ n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL; next_bcast.tv_sec += n*SUPPLY_INTERVAL; continue; } if (timercmp(&t2, &wtime, <)) wtime = t2; } /* If we need a flash update, either do it now or * set the delay to end when it is time. * * If we are within MIN_WAITTIME seconds of a full update, * do not bother. */ if (need_flash && supplier && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { /* accurate to the millisecond */ if (!timercmp(&no_flash, &now, >)) rip_bcast(1); timevalsub(&t2, &no_flash, &now); if (timercmp(&t2, &wtime, <)) wtime = t2; } /* trigger the main aging timer. */ timevalsub(&t2, &age_timer, &now); if (t2.tv_sec <= 0) { age(0); continue; } if (timercmp(&t2, &wtime, <)) wtime = t2; /* update the kernel routing table */ timevalsub(&t2, &need_kern, &now); if (t2.tv_sec <= 0) { age(0); continue; } if (timercmp(&t2, &wtime, <)) wtime = t2; /* take care of router discovery, * but do it in the correct the millisecond */ if (!timercmp(&rdisc_timer, &now, >)) { rdisc_age(0); continue; } timevalsub(&t2, &rdisc_timer, &now); if (timercmp(&t2, &wtime, <)) wtime = t2; /* wait for input or a timer to expire. */ trace_flush(); ibits = fdbits; n = select(sock_max, &ibits, 0, 0, &wtime); if (n <= 0) { if (n < 0 && errno != EINTR && errno != EAGAIN) BADERR(1,"select"); continue; } if (FD_ISSET(rt_sock, &ibits)) { read_rt(); n--; } if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) { read_d(); n--; } if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) { read_rip(rip_sock, 0); n--; } LIST_FOREACH(ifp, &ifnet, int_list) { if (n <= 0) break; if (ifp->int_rip_sock >= 0 && FD_ISSET(ifp->int_rip_sock, &ibits)) { read_rip(ifp->int_rip_sock, ifp); n--; } } } } /* ARGSUSED */ static void sigalrm(int s UNUSED) { /* Historically, SIGALRM would cause the daemon to check for * new and broken interfaces. */ ifinit_timer.tv_sec = now.tv_sec; trace_act("SIGALRM"); } /* watch for fatal signals */ static void sigterm(int sig) { stopint = sig; (void)signal(sig, SIG_DFL); /* catch it only once */ } void fix_select(void) { struct interface *ifp; FD_ZERO(&fdbits); sock_max = 0; FD_SET(rt_sock, &fdbits); if (sock_max <= rt_sock) sock_max = rt_sock+1; if (rip_sock >= 0) { FD_SET(rip_sock, &fdbits); if (sock_max <= rip_sock) sock_max = rip_sock+1; } LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_rip_sock >= 0) { FD_SET(ifp->int_rip_sock, &fdbits); if (sock_max <= ifp->int_rip_sock) sock_max = ifp->int_rip_sock+1; } } if (rdisc_sock >= 0) { FD_SET(rdisc_sock, &fdbits); if (sock_max <= rdisc_sock) sock_max = rdisc_sock+1; } } void fix_sock(int sock, const char *name) { int on; #define MIN_SOCKBUF (4*1024) static int rbuf; if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) logbad(1, "fcntl(%s) O_NONBLOCK: %s", name, strerror(errno)); on = 1; if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, &on,sizeof(on)) < 0) msglog("setsockopt(%s,SO_BROADCAST): %s", name, strerror(errno)); #ifdef USE_PASSIFNAME on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSIFNAME, &on,sizeof(on)) < 0) msglog("setsockopt(%s,SO_PASSIFNAME): %s", name, strerror(errno)); #endif if (rbuf >= MIN_SOCKBUF) { if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbuf, sizeof(rbuf)) < 0) msglog("setsockopt(%s,SO_RCVBUF=%d): %s", name, rbuf, strerror(errno)); } else { for (rbuf = 60*1024; ; rbuf -= 4096) { if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbuf, sizeof(rbuf)) == 0) { trace_act("RCVBUF=%d", rbuf); break; } if (rbuf < MIN_SOCKBUF) { msglog("setsockopt(%s,SO_RCVBUF = %d): %s", name, rbuf, strerror(errno)); break; } } } } /* get a rip socket */ static int /* <0 or file descriptor */ get_rip_sock(naddr addr, int serious) /* 1=failure to bind is serious */ { struct sockaddr_in rsin; unsigned char ttl; int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) BADERR(1,"rip_sock = socket()"); memset(&rsin, 0, sizeof(rsin)); #ifdef _HAVE_SIN_LEN rsin.sin_len = sizeof(rsin); #endif rsin.sin_family = AF_INET; rsin.sin_port = htons(RIP_PORT); rsin.sin_addr.s_addr = addr; if (bind(s, (struct sockaddr *)&rsin, sizeof(rsin)) < 0) { if (serious) BADERR(errno != EADDRINUSE, "bind(rip_sock)"); return -1; } fix_sock(s,"rip_sock"); ttl = 1; if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)"); return s; } /* turn off main RIP socket */ void rip_off(void) { struct interface *ifp; naddr addr; if (rip_sock >= 0 && !mhome) { trace_act("turn off RIP"); (void)close(rip_sock); rip_sock = -1; /* get non-broadcast sockets to listen to queries. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_state & IS_REMOTE) continue; if (ifp->int_rip_sock < 0) { addr = ((ifp->int_if_flags & IFF_POINTOPOINT) ? ifp->int_dstaddr : ifp->int_addr); ifp->int_rip_sock = get_rip_sock(addr, 0); } } fix_select(); age(0); } } /* turn on RIP multicast input via an interface */ static void rip_mcast_on(struct interface *ifp) { struct group_req gr; struct sockaddr_in *sin; if (!IS_RIP_IN_OFF(ifp->int_state) && (ifp->int_if_flags & IFF_MULTICAST) && !(ifp->int_state & IS_ALIAS)) { memset(&gr, 0, sizeof(gr)); gr.gr_interface = ifp->int_index; sin = (struct sockaddr_in *)&gr.gr_group; sin->sin_family = AF_INET; #ifdef _HAVE_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_addr.s_addr = htonl(INADDR_RIP_GROUP); if (setsockopt(rip_sock, IPPROTO_IP, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0) LOGERR("setsockopt(MCAST_JOIN_GROUP RIP)"); } } /* Prepare socket used for RIP. */ void rip_on(struct interface *ifp) { /* If the main RIP socket is already alive, only start receiving * multicasts for this interface. */ if (rip_sock >= 0) { - if (ifp != 0) + if (ifp != NULL) rip_mcast_on(ifp); return; } /* If the main RIP socket is off and it makes sense to turn it on, * then turn it on for all of the interfaces. * It makes sense if either router discovery is off, or if * router discover is on and at most one interface is doing RIP. */ if (rip_interfaces > 0 && (!rdisc_ok || rip_interfaces > 1)) { trace_act("turn on RIP"); /* Close all of the query sockets so that we can open * the main socket. SO_REUSEPORT is not a solution, * since that would let two daemons bind to the broadcast * socket. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_rip_sock >= 0) { (void)close(ifp->int_rip_sock); ifp->int_rip_sock = -1; } } rip_sock = get_rip_sock(INADDR_ANY, 1); - rip_sock_mcast = 0; + rip_sock_mcast = NULL; /* Do not advertise anything until we have heard something */ if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; LIST_FOREACH(ifp, &ifnet, int_list) { ifp->int_query_time = NEVER; rip_mcast_on(ifp); } ifinit_timer.tv_sec = now.tv_sec; - } else if (ifp != 0 + } else if (ifp != NULL && !(ifp->int_state & IS_REMOTE) && ifp->int_rip_sock < 0) { /* RIP is off, so ensure there are sockets on which * to listen for queries. */ ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); } fix_select(); } /* die if malloc(3) fails */ void * rtmalloc(size_t size, const char *msg) { void *p = malloc(size); - if (p == 0) + if (p == NULL) logbad(1,"malloc(%lu) failed in %s", (u_long)size, msg); return p; } /* get a random instant in an interval */ void intvl_random(struct timeval *tp, /* put value here */ u_long lo, /* value is after this second */ u_long hi) /* and before this */ { tp->tv_sec = (time_t)(hi == lo ? lo : (lo + random() % ((hi - lo)))); tp->tv_usec = random() % 1000000; } void timevaladd(struct timeval *t1, struct timeval *t2) { t1->tv_sec += t2->tv_sec; if ((t1->tv_usec += t2->tv_usec) >= 1000000) { t1->tv_sec++; t1->tv_usec -= 1000000; } } /* t1 = t2 - t3 */ static void timevalsub(struct timeval *t1, struct timeval *t2, struct timeval *t3) { t1->tv_sec = t2->tv_sec - t3->tv_sec; if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) { t1->tv_sec--; t1->tv_usec += 1000000; } } /* put a message into the system log */ void msglog(const char *p, ...) { va_list args; trace_flush(); va_start(args, p); vsyslog(LOG_ERR, p, args); va_end(args); - if (ftrace != 0) { + if (ftrace != NULL) { if (ftrace == stdout) (void)fputs("routed: ", ftrace); va_start(args, p); (void)vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n', ftrace); } } /* Put a message about a bad system into the system log if * we have not complained about it recently. * * It is desirable to complain about all bad systems, but not too often. * In the worst case, it is not practical to keep track of all bad systems. * For example, there can be many systems with the wrong password. */ void msglim(struct msg_limit *lim, naddr addr, const char *p, ...) { va_list args; int i; struct msg_sub *ms1, *ms; const char *p1; /* look for the oldest slot in the table * or the slot for the bad router. */ ms = ms1 = lim->subs; for (i = MSG_SUBJECT_N; ; i--, ms1++) { if (i == 0) { /* Reuse a slot at most once every 10 minutes. */ if (lim->reuse > now.tv_sec) { - ms = 0; + ms = NULL; } else { ms = ms1; lim->reuse = now.tv_sec + 10*60; } break; } if (ms->addr == addr) { /* Repeat a complaint about a given system at * most once an hour. */ if (ms->until > now.tv_sec) - ms = 0; + ms = NULL; break; } if (ms->until < ms1->until) ms = ms1; } - if (ms != 0) { + if (ms != NULL) { ms->addr = addr; ms->until = now.tv_sec + 60*60; /* 60 minutes */ trace_flush(); for (p1 = p; *p1 == ' '; p1++) continue; va_start(args, p); vsyslog(LOG_ERR, p1, args); va_end(args); } /* always display the message if tracing */ - if (ftrace != 0) { + if (ftrace != NULL) { va_start(args, p); (void)vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n', ftrace); } } void logbad(int dump, const char *p, ...) { va_list args; trace_flush(); va_start(args, p); vsyslog(LOG_ERR, p, args); va_end(args); (void)fputs("routed: ", stderr); va_start(args, p); (void)vfprintf(stderr, p, args); va_end(args); (void)fputs("; giving up\n",stderr); (void)fflush(stderr); if (dump) abort(); exit(1); } Index: head/sbin/routed/output.c =================================================================== --- head/sbin/routed/output.c (revision 299767) +++ head/sbin/routed/output.c (revision 299768) @@ -1,974 +1,974 @@ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif u_int update_seqno; /* walk the tree of routes with this for output */ static struct { struct sockaddr_in to; naddr to_mask; naddr to_net; naddr to_std_mask; naddr to_std_net; struct interface *ifp; /* usually output interface */ struct auth *a; char metric; /* adjust metrics by interface */ int npackets; int gen_limit; u_int state; #define WS_ST_FLASH 0x001 /* send only changed routes */ #define WS_ST_RIP2_ALL 0x002 /* send full featured RIPv2 */ #define WS_ST_AG 0x004 /* ok to aggregate subnets */ #define WS_ST_SUPER_AG 0x008 /* ok to aggregate networks */ #define WS_ST_QUERY 0x010 /* responding to a query */ #define WS_ST_TO_ON_NET 0x020 /* sending onto one of our nets */ #define WS_ST_DEFAULT 0x040 /* faking a default */ } ws; /* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ struct ws_buf v12buf; static union pkt_buf ripv12_buf; /* Another for only RIPv2 listeners */ static struct ws_buf v2buf; static union pkt_buf rip_v2_buf; void bufinit(void) { ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; v12buf.buf = &ripv12_buf.rip; v12buf.base = &v12buf.buf->rip_nets[0]; rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; rip_v2_buf.rip.rip_vers = RIPv2; v2buf.buf = &rip_v2_buf.rip; v2buf.base = &v2buf.buf->rip_nets[0]; } /* Send the contents of the global buffer via the non-multicast socket */ int /* <0 on failure */ output(enum output_type type, struct sockaddr_in *dst, /* send to here */ struct interface *ifp, struct rip *buf, int size) /* this many bytes */ { struct sockaddr_in osin; int flags; const char *msg; int res; int soc; int serrno; assert(ifp != NULL); osin = *dst; if (osin.sin_port == 0) osin.sin_port = htons(RIP_PORT); #ifdef _HAVE_SIN_LEN if (osin.sin_len == 0) osin.sin_len = sizeof(osin); #endif soc = rip_sock; flags = 0; switch (type) { case OUT_QUERY: msg = "Answer Query"; if (soc < 0) soc = ifp->int_rip_sock; break; case OUT_UNICAST: msg = "Send"; if (soc < 0) soc = ifp->int_rip_sock; flags = MSG_DONTROUTE; break; case OUT_BROADCAST: if (ifp->int_if_flags & IFF_POINTOPOINT) { msg = "Send"; } else { msg = "Send bcast"; } flags = MSG_DONTROUTE; break; case OUT_MULTICAST: if ((ifp->int_if_flags & (IFF_POINTOPOINT|IFF_MULTICAST)) == IFF_POINTOPOINT) { msg = "Send pt-to-pt"; } else if (ifp->int_state & IS_DUP) { trace_act("abort multicast output via %s" " with duplicate address", ifp->int_name); return 0; } else { msg = "Send mcast"; if (rip_sock_mcast != ifp) { struct ip_mreqn mreqn; memset(&mreqn, 0, sizeof(struct ip_mreqn)); mreqn.imr_ifindex = ifp->int_index; if (0 > setsockopt(rip_sock, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) { serrno = errno; LOGERR("setsockopt(rip_sock, " "IP_MULTICAST_IF)"); errno = serrno; - ifp = 0; + ifp = NULL; return -1; } rip_sock_mcast = ifp; } osin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); } break; case NO_OUT_MULTICAST: case NO_OUT_RIPV2: default: #ifdef DEBUG abort(); #endif return -1; } trace_rip(msg, "to", &osin, ifp, buf, size); res = sendto(soc, buf, size, flags, (struct sockaddr *)&osin, sizeof(osin)); if (res < 0 - && (ifp == 0 || !(ifp->int_state & IS_BROKE))) { + && (ifp == NULL || !(ifp->int_state & IS_BROKE))) { serrno = errno; msglog("%s sendto(%s%s%s.%d): %s", msg, - ifp != 0 ? ifp->int_name : "", - ifp != 0 ? ", " : "", + ifp != NULL ? ifp->int_name : "", + ifp != NULL ? ", " : "", inet_ntoa(osin.sin_addr), ntohs(osin.sin_port), strerror(errno)); errno = serrno; } return res; } /* Find the first key for a packet to send. * Try for a key that is eligible and has not expired, but settle for * the last key if they have all expired. * If no key is ready yet, give up. */ struct auth * find_auth(struct interface *ifp) { struct auth *ap, *res; int i; - if (ifp == 0) + if (ifp == NULL) return 0; - res = 0; + res = NULL; ap = ifp->int_auth; for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) { /* stop looking after the last key */ if (ap->type == RIP_AUTH_NONE) break; /* ignore keys that are not ready yet */ if ((u_long)ap->start > (u_long)clk.tv_sec) continue; if ((u_long)ap->end < (u_long)clk.tv_sec) { /* note best expired password as a fall-back */ - if (res == 0 || (u_long)ap->end > (u_long)res->end) + if (res == NULL || (u_long)ap->end > (u_long)res->end) res = ap; continue; } /* note key with the best future */ - if (res == 0 || (u_long)res->end < (u_long)ap->end) + if (res == NULL || (u_long)res->end < (u_long)ap->end) res = ap; } return res; } void clr_ws_buf(struct ws_buf *wb, struct auth *ap) { struct netauth *na; wb->lim = wb->base + NETS_LEN; wb->n = wb->base; memset(wb->n, 0, NETS_LEN*sizeof(*wb->n)); /* (start to) install authentication if appropriate */ - if (ap == 0) + if (ap == NULL) return; na = (struct netauth*)wb->n; if (ap->type == RIP_AUTH_PW) { na->a_family = RIP_AF_AUTH; na->a_type = RIP_AUTH_PW; memcpy(na->au.au_pw, ap->key, sizeof(na->au.au_pw)); wb->n++; } else if (ap->type == RIP_AUTH_MD5) { na->a_family = RIP_AF_AUTH; na->a_type = RIP_AUTH_MD5; na->au.a_md5.md5_keyid = ap->keyid; na->au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN; na->au.a_md5.md5_seqno = htonl(clk.tv_sec); wb->n++; wb->lim--; /* make room for trailer */ } } void end_md5_auth(struct ws_buf *wb, struct auth *ap) { struct netauth *na, *na2; MD5_CTX md5_ctx; int len; na = (struct netauth*)wb->base; na2 = (struct netauth*)wb->n; len = (char *)na2-(char *)wb->buf; na2->a_family = RIP_AF_AUTH; na2->a_type = htons(1); na->au.a_md5.md5_pkt_len = htons(len); MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)wb->buf, len + RIP_AUTH_MD5_HASH_XTRA); MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN); MD5Final(na2->au.au_pw, &md5_ctx); wb->n++; } /* Send the buffer */ static void supply_write(struct ws_buf *wb) { /* Output multicast only if legal. * If we would multicast and it would be illegal, then discard the * packet. */ switch (wb->type) { case NO_OUT_MULTICAST: trace_pkt("skip multicast to %s because impossible", naddr_ntoa(ws.to.sin_addr.s_addr)); break; case NO_OUT_RIPV2: break; default: - if (ws.a != 0 && ws.a->type == RIP_AUTH_MD5) + if (ws.a != NULL && ws.a->type == RIP_AUTH_MD5) end_md5_auth(wb,ws.a); if (output(wb->type, &ws.to, ws.ifp, wb->buf, ((char *)wb->n - (char*)wb->buf)) < 0 - && ws.ifp != 0) + && ws.ifp != NULL) if_sick(ws.ifp); ws.npackets++; break; } clr_ws_buf(wb,ws.a); } /* put an entry into the packet */ static void supply_out(struct ag_info *ag) { int i; naddr mask, v1_mask, dst_h, ddst_h = 0; struct ws_buf *wb; /* Skip this route if doing a flash update and it and the routes * it aggregates have not changed recently. */ if (ag->ag_seqno < update_seqno && (ws.state & WS_ST_FLASH)) return; dst_h = ag->ag_dst_h; mask = ag->ag_mask; v1_mask = ripv1_mask_host(htonl(dst_h), (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0); i = 0; /* If we are sending RIPv2 packets that cannot (or must not) be * heard by RIPv1 listeners, do not worry about sub- or supernets. * Subnets (from other networks) can only be sent via multicast. * A pair of subnet routes might have been promoted so that they * are legal to send by RIPv1. * If RIPv1 is off, use the multicast buffer. */ if ((ws.state & WS_ST_RIP2_ALL) || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { /* use the RIPv2-only buffer */ wb = &v2buf; } else { /* use the RIPv1-or-RIPv2 buffer */ wb = &v12buf; /* Convert supernet route into corresponding set of network * routes for RIPv1, but leave non-contiguous netmasks * to ag_check(). */ if (v1_mask > mask && mask + (mask & -mask) == 0) { ddst_h = v1_mask & -v1_mask; i = (v1_mask & ~mask)/ddst_h; if (i > ws.gen_limit) { /* Punt if we would have to generate an * unreasonable number of routes. */ if (TRACECONTENTS) trace_misc("sending %s-->%s as 1" " instead of %d routes", addrname(htonl(dst_h), mask, 1), naddr_ntoa(ws.to.sin_addr .s_addr), i+1); i = 0; } else { mask = v1_mask; ws.gen_limit -= i; } } } do { wb->n->n_family = RIP_AF_INET; wb->n->n_dst = htonl(dst_h); /* If the route is from router-discovery or we are * shutting down, admit only a bad metric. */ wb->n->n_metric = ((stopint || ag->ag_metric < 1) ? HOPCNT_INFINITY : ag->ag_metric); wb->n->n_metric = htonl(wb->n->n_metric); /* Any non-zero bits in the supposedly unused RIPv1 fields * cause the old `routed` to ignore the route. * That means the mask and so forth cannot be sent * in the hybrid RIPv1/RIPv2 mode. */ if (ws.state & WS_ST_RIP2_ALL) { if (ag->ag_nhop != 0 && ((ws.state & WS_ST_QUERY) || (ag->ag_nhop != ws.ifp->int_addr && on_net(ag->ag_nhop, ws.ifp->int_net, ws.ifp->int_mask)))) wb->n->n_nhop = ag->ag_nhop; wb->n->n_mask = htonl(mask); wb->n->n_tag = ag->ag_tag; } dst_h += ddst_h; if (++wb->n >= wb->lim) supply_write(wb); } while (i-- != 0); } /* supply one route from the table */ /* ARGSUSED */ static int walk_supply(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) u_short ags; char metric, pref; naddr dst, nhop; struct rt_spare *rts; int i; /* Do not advertise external remote interfaces or passive interfaces. */ if ((RT->rt_state & RS_IF) && RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_PASSIVE) && !(RT->rt_state & RS_MHOME)) return 0; /* If being quiet about our ability to forward, then * do not say anything unless responding to a query, * except about our main interface. */ if (!supplier && !(ws.state & WS_ST_QUERY) && !(RT->rt_state & RS_MHOME)) return 0; dst = RT->rt_dst; /* do not collide with the fake default route */ if (dst == RIP_DEFAULT && (ws.state & WS_ST_DEFAULT)) return 0; if (RT->rt_state & RS_NET_SYN) { if (RT->rt_state & RS_NET_INT) { /* Do not send manual synthetic network routes * into the subnet. */ if (on_net(ws.to.sin_addr.s_addr, ntohl(dst), RT->rt_mask)) return 0; } else { /* Do not send automatic synthetic network routes * if they are not needed because no RIPv1 listeners * can hear them. */ if (ws.state & WS_ST_RIP2_ALL) return 0; /* Do not send automatic synthetic network routes to * the real subnet. */ if (on_net(ws.to.sin_addr.s_addr, ntohl(dst), RT->rt_mask)) return 0; } nhop = 0; } else { /* Advertise the next hop if this is not a route for one * of our interfaces and the next hop is on the same * network as the target. * The final determination is made by supply_out(). */ if (!(RT->rt_state & RS_IF) && RT->rt_gate != myaddr && RT->rt_gate != loopaddr) nhop = RT->rt_gate; else nhop = 0; } metric = RT->rt_metric; ags = 0; if (RT->rt_state & RS_MHOME) { /* retain host route of multi-homed servers */ ; } else if (RT_ISHOST(RT)) { /* We should always suppress (into existing network routes) * the host routes for the local end of our point-to-point * links. * If we are suppressing host routes in general, then do so. * Avoid advertising host routes onto their own network, * where they should be handled by proxy-ARP. */ if ((RT->rt_state & RS_LOCAL) || ridhosts || on_net(dst, ws.to_net, ws.to_mask)) ags |= AGS_SUPPRESS; /* Aggregate stray host routes into network routes if allowed. * We cannot aggregate host routes into small network routes * without confusing RIPv1 listeners into thinking the * network routes are host routes. */ if ((ws.state & WS_ST_AG) && (ws.state & WS_ST_RIP2_ALL)) ags |= AGS_AGGREGATE; } else { /* Always suppress network routes into other, existing * network routes */ ags |= AGS_SUPPRESS; /* Generate supernets if allowed. * If we can be heard by RIPv1 systems, we will * later convert back to ordinary nets. * This unifies dealing with received supernets. */ if ((ws.state & WS_ST_AG) && ((RT->rt_state & RS_SUBNET) || (ws.state & WS_ST_SUPER_AG))) ags |= AGS_AGGREGATE; } /* Do not send RIPv1 advertisements of subnets to other * networks. If possible, multicast them by RIPv2. */ if ((RT->rt_state & RS_SUBNET) && !(ws.state & WS_ST_RIP2_ALL) && !on_net(dst, ws.to_std_net, ws.to_std_mask)) ags |= AGS_RIPV2 | AGS_AGGREGATE; /* Do not send a route back to where it came from, except in * response to a query. This is "split-horizon". That means not * advertising back to the same network and so via the same interface. * * We want to suppress routes that might have been fragmented * from this route by a RIPv1 router and sent back to us, and so we * cannot forget this route here. Let the split-horizon route * suppress the fragmented routes and then itself be forgotten. * * Include the routes for both ends of point-to-point interfaces * among those suppressed by split-horizon, since the other side * should knows them as well as we do. * * Notice spare routes with the same metric that we are about to * advertise, to split the horizon on redundant, inactive paths. * * Do not suppress advertisements of interface-related addresses on * non-point-to-point interfaces. This ensures that we have something * to say every 30 seconds to help detect broken Ethernets or * other interfaces where one packet every 30 seconds costs nothing. */ - if (ws.ifp != 0 + if (ws.ifp != NULL && !(ws.state & WS_ST_QUERY) && (ws.state & WS_ST_TO_ON_NET) && (!(RT->rt_state & RS_IF) || ws.ifp->int_if_flags & IFF_POINTOPOINT)) { for (rts = RT->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) { if (rts->rts_metric > metric || rts->rts_ifp != ws.ifp) continue; /* If we do not mark the route with AGS_SPLIT_HZ here, * it will be poisoned-reverse, or advertised back * toward its source with an infinite metric. * If we have recently advertised the route with a * better metric than we now have, then we should * poison-reverse the route before suppressing it for * split-horizon. * * In almost all cases, if there is no spare for the * route then it is either old and dead or a brand * new route. If it is brand new, there is no need * for poison-reverse. If it is old and dead, it * is already poisoned. */ if (RT->rt_poison_time < now_expire || RT->rt_poison_metric >= metric || RT->rt_spares[1].rts_gate == 0) { ags |= AGS_SPLIT_HZ; ags &= ~AGS_SUPPRESS; } metric = HOPCNT_INFINITY; break; } } /* Keep track of the best metric with which the * route has been advertised recently. */ if (RT->rt_poison_metric >= metric || RT->rt_poison_time < now_expire) { RT->rt_poison_time = now.tv_sec; RT->rt_poison_metric = metric; } /* Adjust the outgoing metric by the cost of the link. * Avoid aggregation when a route is counting to infinity. */ pref = RT->rt_poison_metric + ws.metric; metric += ws.metric; /* Do not advertise stable routes that will be ignored, * unless we are answering a query. * If the route recently was advertised with a metric that * would have been less than infinity through this interface, * we need to continue to advertise it in order to poison it. */ if (metric >= HOPCNT_INFINITY) { if (!(ws.state & WS_ST_QUERY) && (pref >= HOPCNT_INFINITY || RT->rt_poison_time < now_garbage)) return 0; metric = HOPCNT_INFINITY; } ag_check(dst, RT->rt_mask, 0, nhop, metric, pref, RT->rt_seqno, RT->rt_tag, ags, supply_out); return 0; #undef RT } /* Supply dst with the contents of the routing tables. * If this won't fit in one packet, chop it up into several. */ void supply(struct sockaddr_in *dst, struct interface *ifp, /* output interface */ enum output_type type, int flash, /* 1=flash update */ int vers, /* RIP version */ int passwd_ok) /* OK to include cleartext password */ { struct rt_entry *rt; int def_metric; ws.state = 0; ws.gen_limit = 1024; ws.to = *dst; ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask; - if (ifp != 0) { + if (ifp != NULL) { ws.to_mask = ifp->int_mask; ws.to_net = ifp->int_net; if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask)) ws.state |= WS_ST_TO_ON_NET; } else { ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0); ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask; rt = rtfind(dst->sin_addr.s_addr); if (rt) ifp = rt->rt_ifp; } ws.npackets = 0; if (flash) ws.state |= WS_ST_FLASH; - if ((ws.ifp = ifp) == 0) { + if ((ws.ifp = ifp) == NULL) { ws.metric = 1; } else { /* Adjust the advertised metric by the outgoing interface * metric. */ ws.metric = ifp->int_metric + 1 + ifp->int_adj_outmetric; } ripv12_buf.rip.rip_vers = vers; switch (type) { case OUT_MULTICAST: if (ifp->int_if_flags & IFF_MULTICAST) v2buf.type = OUT_MULTICAST; else v2buf.type = NO_OUT_MULTICAST; v12buf.type = OUT_BROADCAST; break; case OUT_QUERY: ws.state |= WS_ST_QUERY; /* FALLTHROUGH */ case OUT_BROADCAST: case OUT_UNICAST: v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; v12buf.type = type; break; case NO_OUT_MULTICAST: case NO_OUT_RIPV2: break; /* no output */ } if (vers == RIPv2) { /* full RIPv2 only if cannot be heard by RIPv1 listeners */ if (type != OUT_BROADCAST) ws.state |= WS_ST_RIP2_ALL; if ((ws.state & WS_ST_QUERY) || !(ws.state & WS_ST_TO_ON_NET)) { ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); - } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) { + } else if (ifp == NULL || !(ifp->int_state & IS_NO_AG)) { ws.state |= WS_ST_AG; if (type != OUT_BROADCAST - && (ifp == 0 + && (ifp == NULL || !(ifp->int_state & IS_NO_SUPER_AG))) ws.state |= WS_ST_SUPER_AG; } } ws.a = (vers == RIPv2) ? find_auth(ifp) : 0; - if (!passwd_ok && ws.a != 0 && ws.a->type == RIP_AUTH_PW) - ws.a = 0; + if (!passwd_ok && ws.a != NULL && ws.a->type == RIP_AUTH_PW) + ws.a = NULL; clr_ws_buf(&v12buf,ws.a); clr_ws_buf(&v2buf,ws.a); /* Fake a default route if asked and if there is not already * a better, real default route. */ if (supplier && (def_metric = ifp->int_d_metric) != 0) { - if (0 == (rt = rtget(RIP_DEFAULT, 0)) + if ((rt = rtget(RIP_DEFAULT, 0)) == NULL || rt->rt_metric+ws.metric >= def_metric) { ws.state |= WS_ST_DEFAULT; ag_check(0, 0, 0, 0, def_metric, def_metric, 0, 0, 0, supply_out); } else { def_metric = rt->rt_metric+ws.metric; } /* If both RIPv2 and the poor-man's router discovery * kludge are on, arrange to advertise an extra * default route via RIPv1. */ if ((ws.state & WS_ST_RIP2_ALL) && (ifp->int_state & IS_PM_RDISC)) { ripv12_buf.rip.rip_vers = RIPv1; v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = htonl(RIP_DEFAULT); v12buf.n->n_metric = htonl(def_metric); v12buf.n++; } } (void)rn_walktree(rhead, walk_supply, 0); ag_flush(0,0,supply_out); /* Flush the packet buffers, provided they are not empty and * do not contain only the password. */ if (v12buf.n != v12buf.base && (v12buf.n > v12buf.base+1 || v12buf.base->n_family != RIP_AF_AUTH)) supply_write(&v12buf); if (v2buf.n != v2buf.base && (v2buf.n > v2buf.base+1 || v2buf.base->n_family != RIP_AF_AUTH)) supply_write(&v2buf); /* If we sent nothing and this is an answer to a query, send * an empty buffer. */ if (ws.npackets == 0 && (ws.state & WS_ST_QUERY)) supply_write(&v12buf); } /* send all of the routing table or just do a flash update */ void rip_bcast(int flash) { #ifdef _HAVE_SIN_LEN static struct sockaddr_in dst = {sizeof(dst), AF_INET, 0, {0}, {0}}; #else static struct sockaddr_in dst = {AF_INET}; #endif struct interface *ifp; enum output_type type; int vers; struct timeval rtime; need_flash = 0; intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME); no_flash = rtime; timevaladd(&no_flash, &now); if (rip_sock < 0) return; trace_act("send %s and inhibit dynamic updates for %.3f sec", flash ? "dynamic update" : "all routes", rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0); LIST_FOREACH(ifp, &ifnet, int_list) { /* Skip interfaces not doing RIP. * Do try broken interfaces to see if they have healed. */ if (IS_RIP_OUT_OFF(ifp->int_state)) continue; /* skip turned off interfaces */ if (!iff_up(ifp->int_if_flags)) continue; vers = (ifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1; if (ifp->int_if_flags & IFF_BROADCAST) { /* ordinary, hardware interface */ dst.sin_addr.s_addr = ifp->int_brdaddr; if (vers == RIPv2 && !(ifp->int_state & IS_NO_RIP_MCAST)) { type = OUT_MULTICAST; } else { type = OUT_BROADCAST; } } else if (ifp->int_if_flags & IFF_POINTOPOINT) { /* point-to-point hardware interface */ dst.sin_addr.s_addr = ifp->int_dstaddr; if (vers == RIPv2 && ifp->int_if_flags & IFF_MULTICAST && !(ifp->int_state & IS_NO_RIP_MCAST)) { type = OUT_MULTICAST; } else { type = OUT_UNICAST; } } else if (ifp->int_state & IS_REMOTE) { /* remote interface */ dst.sin_addr.s_addr = ifp->int_addr; type = OUT_UNICAST; } else { /* ATM, HIPPI, etc. */ continue; } supply(&dst, ifp, type, flash, vers, 1); } update_seqno++; /* all routes are up to date */ } /* Ask for routes * Do it only once to an interface, and not even after the interface * was broken and recovered. */ void rip_query(void) { #ifdef _HAVE_SIN_LEN static struct sockaddr_in dst = {sizeof(dst), AF_INET, 0, {0}, {0}}; #else static struct sockaddr_in dst = {AF_INET}; #endif struct interface *ifp; struct rip buf; enum output_type type; if (rip_sock < 0) return; memset(&buf, 0, sizeof(buf)); LIST_FOREACH(ifp, &ifnet, int_list) { /* Skip interfaces those already queried. * Do not ask via interfaces through which we don't * accept input. Do not ask via interfaces that cannot * send RIP packets. * Do try broken interfaces to see if they have healed. */ if (IS_RIP_IN_OFF(ifp->int_state) || ifp->int_query_time != NEVER) continue; /* skip turned off interfaces */ if (!iff_up(ifp->int_if_flags)) continue; buf.rip_vers = (ifp->int_state&IS_NO_RIPV1_OUT) ? RIPv2:RIPv1; buf.rip_cmd = RIPCMD_REQUEST; buf.rip_nets[0].n_family = RIP_AF_UNSPEC; buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); /* Send a RIPv1 query only if allowed and if we will * listen to RIPv1 routers. */ if ((ifp->int_state & IS_NO_RIPV1_OUT) || (ifp->int_state & IS_NO_RIPV1_IN)) { buf.rip_vers = RIPv2; } else { buf.rip_vers = RIPv1; } if (ifp->int_if_flags & IFF_BROADCAST) { /* ordinary, hardware interface */ dst.sin_addr.s_addr = ifp->int_brdaddr; /* Broadcast RIPv1 queries and RIPv2 queries * when the hardware cannot multicast. */ if (buf.rip_vers == RIPv2 && (ifp->int_if_flags & IFF_MULTICAST) && !(ifp->int_state & IS_NO_RIP_MCAST)) { type = OUT_MULTICAST; } else { type = OUT_BROADCAST; } } else if (ifp->int_if_flags & IFF_POINTOPOINT) { /* point-to-point hardware interface */ dst.sin_addr.s_addr = ifp->int_dstaddr; type = OUT_UNICAST; } else if (ifp->int_state & IS_REMOTE) { /* remote interface */ dst.sin_addr.s_addr = ifp->int_addr; type = OUT_UNICAST; } else { /* ATM, HIPPI, etc. */ continue; } ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL; if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) if_sick(ifp); } } Index: head/sbin/routed/parms.c =================================================================== --- head/sbin/routed/parms.c (revision 299767) +++ head/sbin/routed/parms.c (revision 299768) @@ -1,1038 +1,1038 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #include "pathnames.h" #include #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.26 $"); #ident "$Revision: 2.26 $" #endif static struct parm *parms; struct intnet *intnets; struct r1net *r1nets; struct tgate *tgates; /* use configured parameters */ void get_parms(struct interface *ifp) { static int warned_auth_in, warned_auth_out; struct parm *parmp; int i, num_passwds = 0; /* get all relevant parameters */ - for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + for (parmp = parms; parmp != NULL; parmp = parmp->parm_next) { if (parmp->parm_name[0] == '\0' || !strcmp(ifp->int_name, parmp->parm_name) || (parmp->parm_name[0] == '\n' && on_net(ifp->int_addr, parmp->parm_net, parmp->parm_mask))) { /* This group of parameters is relevant, * so get its settings */ ifp->int_state |= parmp->parm_int_state; for (i = 0; i < MAX_AUTH_KEYS; i++) { if (parmp->parm_auth[0].type == RIP_AUTH_NONE || num_passwds >= MAX_AUTH_KEYS) break; memcpy(&ifp->int_auth[num_passwds++], &parmp->parm_auth[i], sizeof(ifp->int_auth[0])); } if (parmp->parm_rdisc_pref != 0) ifp->int_rdisc_pref = parmp->parm_rdisc_pref; if (parmp->parm_rdisc_int != 0) ifp->int_rdisc_int = parmp->parm_rdisc_int; if (parmp->parm_adj_inmetric != 0) ifp->int_adj_inmetric = parmp->parm_adj_inmetric; if (parmp->parm_adj_outmetric != 0) ifp->int_adj_outmetric = parmp->parm_adj_outmetric; } } /* Set general defaults. * * Default poor-man's router discovery to a metric that will * be heard by old versions of `routed`. They ignored received * routes with metric 15. */ if ((ifp->int_state & IS_PM_RDISC) && ifp->int_d_metric == 0) ifp->int_d_metric = FAKE_METRIC; if (ifp->int_rdisc_int == 0) ifp->int_rdisc_int = DefMaxAdvertiseInterval; if (!(ifp->int_if_flags & IFF_MULTICAST) && !(ifp->int_state & IS_REMOTE)) ifp->int_state |= IS_BCAST_RDISC; if (ifp->int_if_flags & IFF_POINTOPOINT) { ifp->int_state |= IS_BCAST_RDISC; /* By default, point-to-point links should be passive * about router-discovery for the sake of demand-dialing. */ if (0 == (ifp->int_state & GROUP_IS_SOL_OUT)) ifp->int_state |= IS_NO_SOL_OUT; if (0 == (ifp->int_state & GROUP_IS_ADV_OUT)) ifp->int_state |= IS_NO_ADV_OUT; } if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE))) ifp->int_state |= IS_NO_RDISC; if (ifp->int_state & IS_PASSIVE) ifp->int_state |= IS_NO_RIP; if (!IS_RIP_IN_OFF(ifp->int_state) && ifp->int_auth[0].type != RIP_AUTH_NONE && !(ifp->int_state & IS_NO_RIPV1_IN) && !warned_auth_in) { msglog("Warning: RIPv1 input via %s" " will be accepted without authentication", ifp->int_name); warned_auth_in = 1; } if (!IS_RIP_OUT_OFF(ifp->int_state) && ifp->int_auth[0].type != RIP_AUTH_NONE && !(ifp->int_state & IS_NO_RIPV1_OUT)) { if (!warned_auth_out) { msglog("Warning: RIPv1 output via %s" " will be sent without authentication", ifp->int_name); warned_auth_out = 1; } } } /* Read a list of gateways from /etc/gateways and add them to our tables. * * This file contains a list of "remote" gateways. That is usually * a gateway which we cannot immediately determine if it is present or * not as we can do for those provided by directly connected hardware. * * If a gateway is marked "passive" in the file, then we assume it * does not understand RIP and assume it is always present. Those * not marked passive are treated as if they were directly connected * and assumed to be broken if they do not send us advertisements. * All remote interfaces are added to our list, and those not marked * passive are sent routing updates. * * A passive interface can also be local, hardware interface exempt * from RIP. */ void gwkludge(void) { FILE *fp; char *p, *lptr; const char *cp; char lbuf[200], net_host[5], dname[64+1+64+1]; char gname[GNAME_LEN+1], qual[9]; struct interface *ifp; naddr dst, netmask, gate; int metric, n, lnum; struct stat sb; u_int state; const char *type; fp = fopen(_PATH_GATEWAYS, "r"); - if (fp == 0) + if (fp == NULL) return; if (0 > fstat(fileno(fp), &sb)) { msglog("could not stat() "_PATH_GATEWAYS); (void)fclose(fp); return; } for (lnum = 1; ; lnum++) { if (fgets(lbuf, sizeof(lbuf), fp) == NULL) break; lptr = lbuf; while (*lptr == ' ') lptr++; p = lptr+strlen(lptr)-1; while (*p == '\n' || (*p == ' ' && (p == lptr+1 || *(p-1) != '\\'))) *p-- = '\0'; if (*lptr == '\0' /* ignore null and comment lines */ || *lptr == '#') continue; /* notice newfangled parameter lines */ if (strncasecmp("net", lptr, 3) && strncasecmp("host", lptr, 4)) { cp = parse_parms(lptr, (sb.st_uid == 0 && !(sb.st_mode&(S_IRWXG|S_IRWXO)))); - if (cp != 0) + if (cp != NULL) msglog("%s in line %d of "_PATH_GATEWAYS, cp, lnum); continue; } /* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ qual[0] = '\0'; /* the '64' here must be GNAME_LEN */ n = sscanf(lptr, "%4s %129[^ \t] gateway" " %64[^ / \t] metric %u %8s\n", net_host, dname, gname, &metric, qual); if (n != 4 && n != 5) { msglog("bad "_PATH_GATEWAYS" entry \"%s\"; %d values", lptr, n); continue; } if (metric >= HOPCNT_INFINITY) { msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"", lptr); continue; } if (!strcasecmp(net_host, "host")) { if (!gethost(dname, &dst)) { msglog("bad host \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", dname, lptr); continue; } netmask = HOST_MASK; } else if (!strcasecmp(net_host, "net")) { if (!getnet(dname, &dst, &netmask)) { msglog("bad net \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", dname, lptr); continue; } if (dst == RIP_DEFAULT) { msglog("bad net \"%s\" in "_PATH_GATEWAYS " entry \"%s\"--cannot be default", dname, lptr); continue; } /* Turn network # into IP address. */ dst = htonl(dst); } else { msglog("bad \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", net_host, lptr); continue; } if (!gethost(gname, &gate)) { msglog("bad gateway \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", gname, lptr); continue; } if (!strcasecmp(qual, type = "passive")) { /* Passive entries are not placed in our tables, * only the kernel's, so we don't copy all of the * external routing information within a net. * Internal machines should use the default * route to a suitable gateway (like us). */ state = IS_REMOTE | IS_PASSIVE; if (metric == 0) metric = 1; } else if (!strcasecmp(qual, type = "external")) { /* External entries are handled by other means * such as EGP, and are placed only in the daemon * tables to prevent overriding them with something * else. */ strcpy(qual,"external"); state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; if (metric == 0) metric = 1; } else if (!strcasecmp(qual, "active") || qual[0] == '\0') { if (metric != 0) { /* Entries that are neither "passive" nor * "external" are "remote" and must behave * like physical interfaces. If they are not * heard from regularly, they are deleted. */ state = IS_REMOTE; type = "remote"; } else { /* "remote" entries with a metric of 0 * are aliases for our own interfaces */ state = IS_REMOTE | IS_PASSIVE | IS_ALIAS; type = "alias"; } } else { msglog("bad "_PATH_GATEWAYS" entry \"%s\";" " unknown type %s", lptr, qual); continue; } if (0 != (state & (IS_PASSIVE | IS_REMOTE))) state |= IS_NO_RDISC; if (state & IS_PASSIVE) state |= IS_NO_RIP; ifp = check_dup(gate,dst,netmask,state); - if (ifp != 0) { + if (ifp != NULL) { msglog("duplicate "_PATH_GATEWAYS" entry \"%s\"",lptr); continue; } ifp = (struct interface *)rtmalloc(sizeof(*ifp), "gwkludge()"); memset(ifp, 0, sizeof(*ifp)); ifp->int_state = state; if (netmask == HOST_MASK) ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP; else ifp->int_if_flags = IFF_UP; ifp->int_act_time = NEVER; ifp->int_addr = gate; ifp->int_dstaddr = dst; ifp->int_mask = netmask; ifp->int_ripv1_mask = netmask; ifp->int_std_mask = std_mask(gate); ifp->int_net = ntohl(dst); ifp->int_std_net = ifp->int_net & ifp->int_std_mask; ifp->int_std_addr = htonl(ifp->int_std_net); ifp->int_metric = metric; if (!(state & IS_EXTERNAL) && ifp->int_mask != ifp->int_std_mask) ifp->int_state |= IS_SUBNET; (void)sprintf(ifp->int_name, "%s(%s)", type, gname); ifp->int_index = -1; if_link(ifp); } /* After all of the parameter lines have been read, * apply them to any remote interfaces. */ LIST_FOREACH(ifp, &ifnet, int_list) { get_parms(ifp); tot_interfaces++; if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces++; trace_if("Add", ifp); } (void)fclose(fp); } /* like strtok(), but honoring backslash and not changing the source string */ static int /* 0=ok, -1=bad */ parse_quote(char **linep, /* look here */ const char *delims, /* for these delimiters */ char *delimp, /* 0 or put found delimiter here */ char *buf, /* copy token to here */ int lim) /* at most this many bytes */ { char c = '\0', *pc; const char *p; pc = *linep; if (*pc == '\0') return -1; while (lim != 0) { c = *pc++; if (c == '\0') break; if (c == '\\' && *pc != '\0') { if ((c = *pc++) == 'n') { c = '\n'; } else if (c == 'r') { c = '\r'; } else if (c == 't') { c = '\t'; } else if (c == 'b') { c = '\b'; } else if (c >= '0' && c <= '7') { c -= '0'; if (*pc >= '0' && *pc <= '7') { c = (c<<3)+(*pc++ - '0'); if (*pc >= '0' && *pc <= '7') c = (c<<3)+(*pc++ - '0'); } } } else { for (p = delims; *p != '\0'; ++p) { if (*p == c) goto exit; } } *buf++ = c; --lim; } exit: if (lim == 0) return -1; *buf = '\0'; /* terminate copy of token */ - if (delimp != 0) + if (delimp != NULL) *delimp = c; /* return delimiter */ *linep = pc-1; /* say where we ended */ return 0; } /* Parse password timestamp */ static char * parse_ts(time_t *tp, char **valp, char *val0, char *delimp, char *buf, u_int bufsize) { struct tm tm; char *ptr; if (0 > parse_quote(valp, "| ,\n\r", delimp, buf,bufsize) || buf[bufsize-1] != '\0' || buf[bufsize-2] != '\0') { sprintf(buf,"bad timestamp %.25s", val0); return buf; } strcat(buf,"\n"); memset(&tm, 0, sizeof(tm)); ptr = strptime(buf, "%y/%m/%d@%H:%M\n", &tm); if (ptr == NULL || *ptr != '\0') { sprintf(buf,"bad timestamp %.25s", val0); return buf; } if ((*tp = mktime(&tm)) == -1) { sprintf(buf,"bad timestamp %.25s", val0); return buf; } return 0; } /* Get a password, key ID, and expiration date in the format * passwd|keyID|year/mon/day@hour:min|year/mon/day@hour:min */ static const char * /* 0 or error message */ get_passwd(char *tgt, char *val, struct parm *parmp, u_int16_t type, int safe) /* 1=from secure file */ { static char buf[80]; char *val0, *p, delim; struct auth k, *ap, *ap2; int i; u_long l; assert(val != NULL); if (!safe) return "ignore unsafe password"; for (ap = parmp->parm_auth, i = 0; ap->type != RIP_AUTH_NONE; i++, ap++) { if (i >= MAX_AUTH_KEYS) return "too many passwords"; } memset(&k, 0, sizeof(k)); k.type = type; k.end = -1-DAY; val0 = val; if (0 > parse_quote(&val, "| ,\n\r", &delim, (char *)k.key, sizeof(k.key))) return tgt; if (delim != '|') { if (type == RIP_AUTH_MD5) return "missing Keyid"; } else { val0 = ++val; buf[sizeof(buf)-1] = '\0'; if (0 > parse_quote(&val, "| ,\n\r", &delim, buf,sizeof(buf)) || buf[sizeof(buf)-1] != '\0' || (l = strtoul(buf,&p,0)) > 255 || *p != '\0') { sprintf(buf,"bad KeyID \"%.20s\"", val0); return buf; } for (ap2 = parmp->parm_auth; ap2 < ap; ap2++) { if (ap2->keyid == l) { sprintf(buf,"duplicate KeyID \"%.20s\"", val0); return buf; } } k.keyid = (int)l; if (delim == '|') { val0 = ++val; - if (0 != (p = parse_ts(&k.start,&val,val0,&delim, - buf,sizeof(buf)))) + if (NULL != (p = parse_ts(&k.start,&val,val0,&delim, + buf,sizeof(buf)))) return p; if (delim != '|') return "missing second timestamp"; val0 = ++val; - if (0 != (p = parse_ts(&k.end,&val,val0,&delim, - buf,sizeof(buf)))) + if (NULL != (p = parse_ts(&k.end,&val,val0,&delim, + buf,sizeof(buf)))) return p; if ((u_long)k.start > (u_long)k.end) { sprintf(buf,"out of order timestamp %.30s", val0); return buf; } } } if (delim != '\0') return tgt; memmove(ap, &k, sizeof(*ap)); return 0; } static const char * bad_str(const char *estr) { static char buf[100+8]; sprintf(buf, "bad \"%.100s\"", estr); return buf; } /* Parse a set of parameters for an interface. */ const char * /* 0 or error message */ parse_parms(char *line, int safe) /* 1=from secure file */ { #define PARS(str) (!strcasecmp(tgt, str)) #define PARSEQ(str) (!strncasecmp(tgt, str"=", sizeof(str))) #define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \ parm.parm_int_state |= (b);} struct parm parm; struct intnet *intnetp; struct r1net *r1netp; struct tgate *tg; naddr addr, mask; - char delim, *val0 = 0, *tgt, *val, *p; + char delim, *val0 = NULL, *tgt, *val, *p; const char *msg; char buf[BUFSIZ], buf2[BUFSIZ]; int i; /* "subnet=x.y.z.u/mask[,metric]" must be alone on the line */ if (!strncasecmp(line, "subnet=", sizeof("subnet=")-1) && *(val = &line[sizeof("subnet=")-1]) != '\0') { if (0 > parse_quote(&val, ",", &delim, buf, sizeof(buf))) return bad_str(line); intnetp = (struct intnet*)rtmalloc(sizeof(*intnetp), "parse_parms subnet"); intnetp->intnet_metric = 1; if (delim == ',') { intnetp->intnet_metric = (int)strtol(val+1,&p,0); if (*p != '\0' || intnetp->intnet_metric <= 0 || intnetp->intnet_metric >= HOPCNT_INFINITY) return bad_str(line); } if (!getnet(buf, &intnetp->intnet_addr, &intnetp->intnet_mask) || intnetp->intnet_mask == HOST_MASK || intnetp->intnet_addr == RIP_DEFAULT) { free(intnetp); return bad_str(line); } intnetp->intnet_addr = htonl(intnetp->intnet_addr); intnetp->intnet_next = intnets; intnets = intnetp; return 0; } /* "ripv1_mask=x.y.z.u/mask1,mask2" must be alone on the line. * This requires that x.y.z.u/mask1 be considered a subnet of * x.y.z.u/mask2, as if x.y.z.u/mask2 were a class-full network. */ if (!strncasecmp(line, "ripv1_mask=", sizeof("ripv1_mask=")-1) && *(val = &line[sizeof("ripv1_mask=")-1]) != '\0') { if (0 > parse_quote(&val, ",", &delim, buf, sizeof(buf)) || delim == '\0') return bad_str(line); if ((i = (int)strtol(val+1, &p, 0)) <= 0 || i > 32 || *p != '\0') return bad_str(line); r1netp = (struct r1net *)rtmalloc(sizeof(*r1netp), "parse_parms ripv1_mask"); r1netp->r1net_mask = HOST_MASK << (32-i); if (!getnet(buf, &r1netp->r1net_net, &r1netp->r1net_match) || r1netp->r1net_net == RIP_DEFAULT || r1netp->r1net_mask > r1netp->r1net_match) { free(r1netp); return bad_str(line); } r1netp->r1net_next = r1nets; r1nets = r1netp; return 0; } memset(&parm, 0, sizeof(parm)); for (;;) { tgt = line + strspn(line, " ,\n\r"); if (*tgt == '\0' || *tgt == '#') break; line = tgt+strcspn(tgt, "= #,\n\r"); delim = *line; if (delim == '=') { val0 = ++line; if (0 > parse_quote(&line, " #,\n\r",&delim, buf,sizeof(buf))) return bad_str(tgt); } else { val0 = NULL; } if (delim != '\0') { for (;;) { *line = '\0'; if (delim == '#') break; ++line; if (delim != ' ' || (delim = *line) != ' ') break; } } if (PARSEQ("if")) { if (parm.parm_name[0] != '\0' || strlen(buf) > IF_NAME_LEN) return bad_str(tgt); strcpy(parm.parm_name, buf); } else if (PARSEQ("addr")) { /* This is a bad idea, because the address based * sets of parameters cannot be checked for * consistency with the interface name parameters. * The parm_net stuff is needed to allow several * -F settings. */ if (!getnet(val0, &addr, &mask) || parm.parm_name[0] != '\0') return bad_str(tgt); parm.parm_net = addr; parm.parm_mask = mask; parm.parm_name[0] = '\n'; } else if (PARSEQ("passwd")) { /* since cleartext passwords are so weak allow * them anywhere */ msg = get_passwd(tgt,val0,&parm,RIP_AUTH_PW,1); if (msg) { *val0 = '\0'; return bad_str(msg); } } else if (PARSEQ("md5_passwd")) { msg = get_passwd(tgt,val0,&parm,RIP_AUTH_MD5,safe); if (msg) { *val0 = '\0'; return bad_str(msg); } } else if (PARS("no_ag")) { parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG); } else if (PARS("no_super_ag")) { parm.parm_int_state |= IS_NO_SUPER_AG; } else if (PARS("no_rip_out")) { parm.parm_int_state |= IS_NO_RIP_OUT; } else if (PARS("no_ripv1_in")) { parm.parm_int_state |= IS_NO_RIPV1_IN; } else if (PARS("no_ripv2_in")) { parm.parm_int_state |= IS_NO_RIPV2_IN; } else if (PARS("ripv2_out")) { if (parm.parm_int_state & IS_NO_RIPV2_OUT) return bad_str(tgt); parm.parm_int_state |= IS_NO_RIPV1_OUT; } else if (PARS("ripv2")) { if ((parm.parm_int_state & IS_NO_RIPV2_OUT) || (parm.parm_int_state & IS_NO_RIPV2_IN)) return bad_str(tgt); parm.parm_int_state |= (IS_NO_RIPV1_IN | IS_NO_RIPV1_OUT); } else if (PARS("no_rip")) { CKF(IS_PM_RDISC, IS_NO_RIP); } else if (PARS("no_rip_mcast")) { parm.parm_int_state |= IS_NO_RIP_MCAST; } else if (PARS("no_rdisc")) { CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC); } else if (PARS("no_solicit")) { CKF(GROUP_IS_SOL_OUT, IS_NO_SOL_OUT); } else if (PARS("send_solicit")) { CKF(GROUP_IS_SOL_OUT, IS_SOL_OUT); } else if (PARS("no_rdisc_adv")) { CKF(GROUP_IS_ADV_OUT, IS_NO_ADV_OUT); } else if (PARS("rdisc_adv")) { CKF(GROUP_IS_ADV_OUT, IS_ADV_OUT); } else if (PARS("bcast_rdisc")) { parm.parm_int_state |= IS_BCAST_RDISC; } else if (PARS("passive")) { CKF((GROUP_IS_SOL_OUT|GROUP_IS_ADV_OUT), IS_NO_RDISC); parm.parm_int_state |= IS_NO_RIP | IS_PASSIVE; } else if (PARSEQ("rdisc_pref")) { if (parm.parm_rdisc_pref != 0 || (parm.parm_rdisc_pref = (int)strtol(buf,&p,0), *p != '\0')) return bad_str(tgt); } else if (PARS("pm_rdisc")) { if (IS_RIP_OUT_OFF(parm.parm_int_state)) return bad_str(tgt); parm.parm_int_state |= IS_PM_RDISC; } else if (PARSEQ("rdisc_interval")) { if (parm.parm_rdisc_int != 0 || (parm.parm_rdisc_int = (int)strtoul(buf,&p,0), *p != '\0') || parm.parm_rdisc_int < MinMaxAdvertiseInterval || parm.parm_rdisc_int > MaxMaxAdvertiseInterval) return bad_str(tgt); } else if (PARSEQ("fake_default")) { if (parm.parm_d_metric != 0 || IS_RIP_OUT_OFF(parm.parm_int_state) || (i = strtoul(buf,&p,0), *p != '\0') || i > HOPCNT_INFINITY-1) return bad_str(tgt); parm.parm_d_metric = i; } else if (PARSEQ("adj_inmetric")) { if (parm.parm_adj_inmetric != 0 || (i = strtoul(buf,&p,0), *p != '\0') || i > HOPCNT_INFINITY-1) return bad_str(tgt); parm.parm_adj_inmetric = i; } else if (PARSEQ("adj_outmetric")) { if (parm.parm_adj_outmetric != 0 || (i = strtoul(buf,&p,0), *p != '\0') || i > HOPCNT_INFINITY-1) return bad_str(tgt); parm.parm_adj_outmetric = i; } else if (PARSEQ("trust_gateway")) { /* look for trust_gateway=x.y.z|net/mask|...) */ p = buf; if (0 > parse_quote(&p, "|", &delim, buf2, sizeof(buf2)) || !gethost(buf2,&addr)) return bad_str(tgt); tg = (struct tgate *)rtmalloc(sizeof(*tg), "parse_parms" "trust_gateway"); memset(tg, 0, sizeof(*tg)); tg->tgate_addr = addr; i = 0; /* The default is to trust all routes. */ while (delim == '|') { p++; if (i >= MAX_TGATE_NETS || 0 > parse_quote(&p, "|", &delim, buf2, sizeof(buf2)) || !getnet(buf2, &tg->tgate_nets[i].net, &tg->tgate_nets[i].mask) || tg->tgate_nets[i].net == RIP_DEFAULT || tg->tgate_nets[i].mask == 0) return bad_str(tgt); i++; } tg->tgate_next = tgates; tgates = tg; parm.parm_int_state |= IS_DISTRUST; } else if (PARS("redirect_ok")) { parm.parm_int_state |= IS_REDIRECT_OK; } else { return bad_str(tgt); /* error */ } } return check_parms(&parm); #undef PARS #undef PARSEQ } /* check for duplicate parameter specifications */ const char * /* 0 or error message */ check_parms(struct parm *new) { struct parm *parmp, **parmpp; int i, num_passwds; /* set implicit values */ if (new->parm_int_state & IS_NO_ADV_IN) new->parm_int_state |= IS_NO_SOL_OUT; if (new->parm_int_state & IS_NO_SOL_OUT) new->parm_int_state |= IS_NO_ADV_IN; for (i = num_passwds = 0; i < MAX_AUTH_KEYS; i++) { if (new->parm_auth[i].type != RIP_AUTH_NONE) num_passwds++; } /* compare with existing sets of parameters */ for (parmpp = &parms; - (parmp = *parmpp) != 0; + (parmp = *parmpp) != NULL; parmpp = &parmp->parm_next) { if (strcmp(new->parm_name, parmp->parm_name)) continue; if (!on_net(htonl(parmp->parm_net), new->parm_net, new->parm_mask) && !on_net(htonl(new->parm_net), parmp->parm_net, parmp->parm_mask)) continue; for (i = 0; i < MAX_AUTH_KEYS; i++) { if (parmp->parm_auth[i].type != RIP_AUTH_NONE) num_passwds++; } if (num_passwds > MAX_AUTH_KEYS) return "too many conflicting passwords"; if ((0 != (new->parm_int_state & GROUP_IS_SOL_OUT) && 0 != (parmp->parm_int_state & GROUP_IS_SOL_OUT) && 0 != ((new->parm_int_state ^ parmp->parm_int_state) & GROUP_IS_SOL_OUT)) || (0 != (new->parm_int_state & GROUP_IS_ADV_OUT) && 0 != (parmp->parm_int_state & GROUP_IS_ADV_OUT) && 0 != ((new->parm_int_state ^ parmp->parm_int_state) & GROUP_IS_ADV_OUT)) || (new->parm_rdisc_pref != 0 && parmp->parm_rdisc_pref != 0 && new->parm_rdisc_pref != parmp->parm_rdisc_pref) || (new->parm_rdisc_int != 0 && parmp->parm_rdisc_int != 0 && new->parm_rdisc_int != parmp->parm_rdisc_int)) { return ("conflicting, duplicate router discovery" " parameters"); } if (new->parm_d_metric != 0 && parmp->parm_d_metric != 0 && new->parm_d_metric != parmp->parm_d_metric) { return ("conflicting, duplicate poor man's router" " discovery or fake default metric"); } if (new->parm_adj_inmetric != 0 && parmp->parm_adj_inmetric != 0 && new->parm_adj_inmetric != parmp->parm_adj_inmetric) { return ("conflicting interface input " "metric adjustments"); } if (new->parm_adj_outmetric != 0 && parmp->parm_adj_outmetric != 0 && new->parm_adj_outmetric != parmp->parm_adj_outmetric) { return ("conflicting interface output " "metric adjustments"); } } /* link new entry on the list so that when the entries are scanned, * they affect the result in the order the operator specified. */ parmp = (struct parm*)rtmalloc(sizeof(*parmp), "check_parms"); memcpy(parmp, new, sizeof(*parmp)); *parmpp = parmp; return 0; } /* get a network number as a name or a number, with an optional "/xx" * netmask. */ int /* 0=bad */ getnet(char *name, naddr *netp, /* network in host byte order */ naddr *maskp) /* masks are always in host order */ { int i; struct netent *np; naddr mask; /* in host byte order */ struct in_addr in; /* a network and so host byte order */ char hname[MAXHOSTNAMELEN+1]; char *mname, *p; /* Detect and separate "1.2.3.4/24" */ - if (0 != (mname = strrchr(name,'/'))) { + if (NULL != (mname = strrchr(name,'/'))) { i = (int)(mname - name); if (i > (int)sizeof(hname)-1) /* name too long */ return 0; memmove(hname, name, i); hname[i] = '\0'; mname++; name = hname; } np = getnetbyname(name); - if (np != 0) { + if (np != NULL) { in.s_addr = (naddr)np->n_net; if (0 == (in.s_addr & 0xff000000)) in.s_addr <<= 8; if (0 == (in.s_addr & 0xff000000)) in.s_addr <<= 8; if (0 == (in.s_addr & 0xff000000)) in.s_addr <<= 8; } else if (inet_aton(name, &in) == 1) { in.s_addr = ntohl(in.s_addr); } else if (!mname && !strcasecmp(name,"default")) { in.s_addr = RIP_DEFAULT; } else { return 0; } if (!mname) { /* we cannot use the interfaces here because we have not * looked at them yet. */ mask = std_mask(htonl(in.s_addr)); if ((~mask & in.s_addr) != 0) mask = HOST_MASK; } else { mask = (naddr)strtoul(mname, &p, 0); if (*p != '\0' || mask > 32) return 0; if (mask != 0) mask = HOST_MASK << (32-mask); } /* must have mask of 0 with default */ if (mask != 0 && in.s_addr == RIP_DEFAULT) return 0; /* no host bits allowed in a network number */ if ((~mask & in.s_addr) != 0) return 0; /* require non-zero network number */ if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT) return 0; if (in.s_addr>>24 == 0 && in.s_addr != RIP_DEFAULT) return 0; if (in.s_addr>>24 == 0xff) return 0; *netp = in.s_addr; *maskp = mask; return 1; } int /* 0=bad */ gethost(char *name, naddr *addrp) { struct hostent *hp; struct in_addr in; /* Try for a number first, even in IRIX where gethostbyname() * is smart. This avoids hitting the name server which * might be sick because routing is. */ if (inet_aton(name, &in) == 1) { /* get a good number, but check that it it makes some * sense. */ if (ntohl(in.s_addr)>>24 == 0 || ntohl(in.s_addr)>>24 == 0xff) return 0; *addrp = in.s_addr; return 1; } hp = gethostbyname(name); if (hp) { memcpy(addrp, hp->h_addr, sizeof(*addrp)); return 1; } return 0; } Index: head/sbin/routed/radix.c =================================================================== --- head/sbin/routed/radix.c (revision 299767) +++ head/sbin/routed/radix.c (revision 299768) @@ -1,897 +1,898 @@ /* * Copyright (c) 1988, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * @(#)radix.c 8.4 (Berkeley) 11/2/94 * * $FreeBSD$ */ /* * Routines to build and maintain radix trees for routing lookups. */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.23 $"); #ident "$Revision: 2.23 $" #endif #define log(x, msg) syslog(x, msg) #define panic(s) {log(LOG_ERR,s); exit(1);} #define min(a,b) (((a)<(b))?(a):(b)) int max_keylen; static struct radix_mask *rn_mkfreelist; static struct radix_node_head *mask_rnhead; static char *addmask_key; static const uint8_t normal_chars[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; static char *rn_zeros, *rn_ones; #define rn_masktop (mask_rnhead->rnh_treetop) #define Bcmp(a, b, l) (l == 0 ? 0 \ : memcmp((caddr_t)(a), (caddr_t)(b), (size_t)l)) static int rn_satisfies_leaf(char *, struct radix_node *, int); static struct radix_node *rn_addmask(void *n_arg, int search, int skip); static struct radix_node *rn_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, struct radix_node treenodes[2]); static struct radix_node *rn_match(void *v_arg, struct radix_node_head *head); /* * The data structure for the keys is a radix tree with one way * branching removed. The index rn_b at an internal node n represents a bit * position to be tested. The tree is arranged so that all descendants * of a node n have keys whose bits all agree up to position rn_b - 1. * (We say the index of n is rn_b.) * * There is at least one descendant which has a one bit at position rn_b, * and at least one with a zero there. * * A route is determined by a pair of key and mask. We require that the * bit-wise logical and of the key and mask to be the key. * We define the index of a route to associated with the mask to be * the first bit number in the mask where 0 occurs (with bit number 0 * representing the highest order bit). * * We say a mask is normal if every bit is 0, past the index of the mask. * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, * and m is a normal mask, then the route applies to every descendant of n. * If the index(m) < rn_b, this implies the trailing last few bits of k * before bit b are all 0, (and hence consequently true of every descendant * of n), so the route applies to all descendants of the node as well. * * Similar logic shows that a non-normal mask m such that * index(m) <= index(n) could potentially apply to many children of n. * Thus, for each non-host route, we attach its mask to a list at an internal * node as high in the tree as we can go. * * The present version of the code makes use of normal routes in short- * circuiting an explicit mask and compare operation when testing whether * a key satisfies a normal route, and also in remembering the unique leaf * that governs a subtree. */ static struct radix_node * rn_search(void *v_arg, struct radix_node *head) { struct radix_node *x; caddr_t v; for (x = head, v = v_arg; x->rn_b >= 0;) { if (x->rn_bmask & v[x->rn_off]) x = x->rn_r; else x = x->rn_l; } return (x); } static struct radix_node * rn_search_m(void *v_arg, struct radix_node *head, void *m_arg) { struct radix_node *x; caddr_t v = v_arg, m = m_arg; for (x = head; x->rn_b >= 0;) { if ((x->rn_bmask & m[x->rn_off]) && (x->rn_bmask & v[x->rn_off])) x = x->rn_r; else x = x->rn_l; } return x; } static int rn_refines(void* m_arg, void *n_arg) { caddr_t m = m_arg, n = n_arg; caddr_t lim, lim2 = lim = n + *(u_char *)n; int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); int masks_are_equal = 1; if (longer > 0) lim -= longer; while (n < lim) { if (*n & ~(*m)) return 0; if (*n++ != *m++) masks_are_equal = 0; } while (n < lim2) if (*n++) return 0; if (masks_are_equal && (longer < 0)) for (lim2 = m - longer; m < lim2; ) if (*m++) return 1; return (!masks_are_equal); } static struct radix_node * rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head) { struct radix_node *x; caddr_t netmask = 0; if (m_arg) { - if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0) + if ((x = rn_addmask(m_arg, 1, + head->rnh_treetop->rn_off)) == NULL) return (0); netmask = x->rn_key; } x = rn_match(v_arg, head); if (x && netmask) { while (x && x->rn_mask != netmask) x = x->rn_dupedkey; } return x; } static int rn_satisfies_leaf(char *trial, struct radix_node *leaf, int skip) { char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; char *cplim; int length = min(*(u_char *)cp, *(u_char *)cp2); - if (cp3 == 0) + if (cp3 == NULL) cp3 = rn_ones; else length = min(length, *(u_char *)cp3); cplim = cp + length; cp3 += skip; cp2 += skip; for (cp += skip; cp < cplim; cp++, cp2++, cp3++) if ((*cp ^ *cp2) & *cp3) return 0; return 1; } static struct radix_node * rn_match(void *v_arg, struct radix_node_head *head) { caddr_t v = v_arg; struct radix_node *t = head->rnh_treetop, *x; caddr_t cp = v, cp2; caddr_t cplim; struct radix_node *saved_t, *top = t; int off = t->rn_off, vlen = *(u_char *)cp, matched_off; int test, b, rn_b; /* * Open code rn_search(v, top) to avoid overhead of extra * subroutine call. */ for (; t->rn_b >= 0; ) { if (t->rn_bmask & cp[t->rn_off]) t = t->rn_r; else t = t->rn_l; } /* * See if we match exactly as a host destination * or at least learn how many bits match, for normal mask finesse. * * It doesn't hurt us to limit how many bytes to check * to the length of the mask, since if it matches we had a genuine * match and the leaf we have is the most specific one anyway; * if it didn't match with a shorter length it would fail * with a long one. This wins big for class B&C netmasks which * are probably the most common case... */ if (t->rn_mask) vlen = *(u_char *)t->rn_mask; cp += off; cp2 = t->rn_key + off; cplim = v + vlen; for (; cp < cplim; cp++, cp2++) if (*cp != *cp2) goto on1; /* * This extra grot is in case we are explicitly asked * to look up the default. Ugh! * Or 255.255.255.255 * * In this case, we have a complete match of the key. Unless * the node is one of the roots, we are finished. * If it is the zeros root, then take what we have, preferring * any real data. * If it is the ones root, then pretend the target key was followed * by a byte of zeros. */ if (!(t->rn_flags & RNF_ROOT)) return t; /* not a root */ if (t->rn_dupedkey) { t = t->rn_dupedkey; return t; /* have some real data */ } if (*(cp-1) == 0) return t; /* not the ones root */ b = 0; /* fake a zero after 255.255.255.255 */ goto on2; on1: test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ for (b = 7; (test >>= 1) > 0;) b--; on2: matched_off = cp - v; b += matched_off << 3; rn_b = -1 - b; /* * If there is a host route in a duped-key chain, it will be first. */ if ((saved_t = t)->rn_mask == 0) t = t->rn_dupedkey; for (; t; t = t->rn_dupedkey) { /* * Even if we don't match exactly as a host, * we may match if the leaf we wound up at is * a route to a net. */ if (t->rn_flags & RNF_NORMAL) { if (rn_b <= t->rn_b) return t; } else if (rn_satisfies_leaf(v, t, matched_off)) { return t; } } t = saved_t; /* start searching up the tree */ do { struct radix_mask *m; t = t->rn_p; if ((m = t->rn_mklist)) { /* * If non-contiguous masks ever become important * we can restore the masking and open coding of * the search and satisfaction test and put the * calculation of "off" back before the "do". */ do { if (m->rm_flags & RNF_NORMAL) { if (rn_b <= m->rm_b) return (m->rm_leaf); } else { off = min(t->rn_off, matched_off); x = rn_search_m(v, t, m->rm_mask); while (x && x->rn_mask != m->rm_mask) x = x->rn_dupedkey; if (x && rn_satisfies_leaf(v, x, off)) return x; } } while ((m = m->rm_mklist)); } } while (t != top); return 0; } #ifdef RN_DEBUG int rn_nodenum; struct radix_node *rn_clist; int rn_saveinfo; int rn_debug = 1; #endif static struct radix_node * rn_newpair(void *v, int b, struct radix_node nodes[2]) { struct radix_node *tt = nodes, *t = tt + 1; t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7); t->rn_l = tt; t->rn_off = b >> 3; tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t; tt->rn_flags = t->rn_flags = RNF_ACTIVE; #ifdef RN_DEBUG tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; #endif return t; } static struct radix_node * rn_insert(void* v_arg, struct radix_node_head *head, int *dupentry, struct radix_node nodes[2]) { caddr_t v = v_arg; struct radix_node *top = head->rnh_treetop; int head_off = top->rn_off, vlen = (int)*((u_char *)v); struct radix_node *t = rn_search(v_arg, top); caddr_t cp = v + head_off; int b; struct radix_node *tt; /* * Find first bit at which v and t->rn_key differ */ { caddr_t cp2 = t->rn_key + head_off; int cmp_res; caddr_t cplim = v + vlen; while (cp < cplim) if (*cp2++ != *cp++) goto on1; /* handle adding 255.255.255.255 */ if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) { *dupentry = 1; return t; } on1: *dupentry = 0; cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; for (b = (cp - v) << 3; cmp_res; b--) cmp_res >>= 1; } { struct radix_node *p, *x = top; cp = v; do { p = x; if (cp[x->rn_off] & x->rn_bmask) x = x->rn_r; else x = x->rn_l; } while ((unsigned)b > (unsigned)x->rn_b); #ifdef RN_DEBUG if (rn_debug) log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p); #endif t = rn_newpair(v_arg, b, nodes); tt = t->rn_l; if ((cp[p->rn_off] & p->rn_bmask) == 0) p->rn_l = t; else p->rn_r = t; x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */ if ((cp[t->rn_off] & t->rn_bmask) == 0) { t->rn_r = x; } else { t->rn_r = tt; t->rn_l = x; } #ifdef RN_DEBUG if (rn_debug) log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p); #endif } return (tt); } static struct radix_node * rn_addmask(void *n_arg, int search, int skip) { caddr_t netmask = (caddr_t)n_arg; struct radix_node *x; caddr_t cp, cplim; int b = 0, mlen, j; int maskduplicated, m0, isnormal; struct radix_node *saved_x; static int last_zeroed = 0; if ((mlen = *(u_char *)netmask) > max_keylen) mlen = max_keylen; if (skip == 0) skip = 1; if (mlen <= skip) return (mask_rnhead->rnh_nodes); if (skip > 1) Bcopy(rn_ones + 1, addmask_key + 1, skip - 1); if ((m0 = mlen) > skip) Bcopy(netmask + skip, addmask_key + skip, mlen - skip); /* * Trim trailing zeroes. */ for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;) cp--; mlen = cp - addmask_key; if (mlen <= skip) { if (m0 >= last_zeroed) last_zeroed = mlen; return (mask_rnhead->rnh_nodes); } if (m0 < last_zeroed) Bzero(addmask_key + m0, last_zeroed - m0); *addmask_key = last_zeroed = mlen; x = rn_search(addmask_key, rn_masktop); if (Bcmp(addmask_key, x->rn_key, mlen) != 0) - x = 0; + x = NULL; if (x || search) return (x); x = (struct radix_node *)rtmalloc(max_keylen + 2*sizeof(*x), "rn_addmask"); saved_x = x; Bzero(x, max_keylen + 2 * sizeof (*x)); netmask = cp = (caddr_t)(x + 2); Bcopy(addmask_key, cp, mlen); x = rn_insert(cp, mask_rnhead, &maskduplicated, x); if (maskduplicated) { log(LOG_ERR, "rn_addmask: mask impossibly already in tree"); Free(saved_x); return (x); } /* * Calculate index of mask, and check for normalcy. */ cplim = netmask + mlen; isnormal = 1; for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) cp++; if (cp != cplim) { for (j = 0x80; (j & *cp) != 0; j >>= 1) b++; if (*cp != normal_chars[b] || cp != (cplim - 1)) isnormal = 0; } b += (cp - netmask) << 3; x->rn_b = -1 - b; if (isnormal) x->rn_flags |= RNF_NORMAL; return (x); } static int /* XXX: arbitrary ordering for non-contiguous masks */ rn_lexobetter(void *m_arg, void *n_arg) { u_char *mp = m_arg, *np = n_arg, *lim; if (*mp > *np) return 1; /* not really, but need to check longer one first */ if (*mp == *np) for (lim = mp + *mp; mp < lim;) if (*mp++ > *np++) return 1; return 0; } static struct radix_mask * rn_new_radix_mask(struct radix_node *tt, struct radix_mask *next) { struct radix_mask *m; MKGet(m); - if (m == 0) { + if (m == NULL) { log(LOG_ERR, "Mask for route not entered\n"); return (0); } Bzero(m, sizeof *m); m->rm_b = tt->rn_b; m->rm_flags = tt->rn_flags; if (tt->rn_flags & RNF_NORMAL) m->rm_leaf = tt; else m->rm_mask = tt->rn_mask; m->rm_mklist = next; tt->rn_mklist = m; return m; } static struct radix_node * rn_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, struct radix_node treenodes[2]) { caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; - struct radix_node *t, *x = 0, *tt; + struct radix_node *t, *x = NULL, *tt; struct radix_node *saved_tt, *top = head->rnh_treetop; short b = 0, b_leaf = 0; int keyduplicated; caddr_t mmask; struct radix_mask *m, **mp; /* * In dealing with non-contiguous masks, there may be * many different routes which have the same mask. * We will find it useful to have a unique pointer to * the mask to speed avoiding duplicate references at * nodes and possibly save time in calculating indices. */ if (netmask) { - if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0) + if ((x = rn_addmask(netmask, 0, top->rn_off)) == NULL) return (0); b_leaf = x->rn_b; b = -1 - x->rn_b; netmask = x->rn_key; } /* * Deal with duplicated keys: attach node to previous instance */ saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); if (keyduplicated) { for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) { if (tt->rn_mask == netmask) return (0); if (netmask == 0 || (tt->rn_mask && ((b_leaf < tt->rn_b) || /* index(netmask) > node */ rn_refines(netmask, tt->rn_mask) || rn_lexobetter(netmask, tt->rn_mask)))) break; } /* * If the mask is not duplicated, we wouldn't * find it among possible duplicate key entries * anyway, so the above test doesn't hurt. * * We sort the masks for a duplicated key the same way as * in a masklist -- most specific to least specific. * This may require the unfortunate nuisance of relocating * the head of the list. */ if (tt == saved_tt) { struct radix_node *xx = x; /* link in at head of list */ (tt = treenodes)->rn_dupedkey = t; tt->rn_flags = t->rn_flags; tt->rn_p = x = t->rn_p; if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt; saved_tt = tt; x = xx; } else { (tt = treenodes)->rn_dupedkey = t->rn_dupedkey; t->rn_dupedkey = tt; } #ifdef RN_DEBUG t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; #endif tt->rn_key = (caddr_t) v; tt->rn_b = -1; tt->rn_flags = RNF_ACTIVE; } /* * Put mask in tree. */ if (netmask) { tt->rn_mask = netmask; tt->rn_b = x->rn_b; tt->rn_flags |= x->rn_flags & RNF_NORMAL; } t = saved_tt->rn_p; if (keyduplicated) goto on2; b_leaf = -1 - t->rn_b; if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r; /* Promote general routes from below */ if (x->rn_b < 0) { for (mp = &t->rn_mklist; x; x = x->rn_dupedkey) if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { if ((*mp = m = rn_new_radix_mask(x, 0))) mp = &m->rm_mklist; } } else if (x->rn_mklist) { /* * Skip over masks whose index is > that of new node */ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) if (m->rm_b >= b_leaf) break; - t->rn_mklist = m; *mp = 0; + t->rn_mklist = m; *mp = NULL; } on2: /* Add new route to highest possible ancestor's list */ if ((netmask == 0) || (b > t->rn_b )) return tt; /* can't lift at all */ b_leaf = tt->rn_b; do { x = t; t = t->rn_p; } while (b <= t->rn_b && x != top); /* * Search through routes associated with node to * insert new route according to index. * Need same criteria as when sorting dupedkeys to avoid * double loop on deletion. */ for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) { if (m->rm_b < b_leaf) continue; if (m->rm_b > b_leaf) break; if (m->rm_flags & RNF_NORMAL) { mmask = m->rm_leaf->rn_mask; if (tt->rn_flags & RNF_NORMAL) { log(LOG_ERR, "Non-unique normal route, mask not entered"); return tt; } } else mmask = m->rm_mask; if (mmask == netmask) { m->rm_refs++; tt->rn_mklist = m; return tt; } if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask)) break; } *mp = rn_new_radix_mask(tt, *mp); return tt; } static struct radix_node * rn_delete(void *v_arg, void *netmask_arg, struct radix_node_head *head) { struct radix_node *t, *p, *x, *tt; struct radix_mask *m, *saved_m, **mp; struct radix_node *dupedkey, *saved_tt, *top; caddr_t v, netmask; int b, head_off, vlen; v = v_arg; netmask = netmask_arg; x = head->rnh_treetop; tt = rn_search(v, x); head_off = x->rn_off; vlen = *(u_char *)v; saved_tt = tt; top = x; - if (tt == 0 || + if (tt == NULL || Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) return (0); /* * Delete our route from mask lists. */ if (netmask) { - if ((x = rn_addmask(netmask, 1, head_off)) == 0) + if ((x = rn_addmask(netmask, 1, head_off)) == NULL) return (0); netmask = x->rn_key; while (tt->rn_mask != netmask) - if ((tt = tt->rn_dupedkey) == 0) + if ((tt = tt->rn_dupedkey) == NULL) return (0); } - if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) + if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == NULL) goto on1; if (tt->rn_flags & RNF_NORMAL) { if (m->rm_leaf != tt || m->rm_refs > 0) { log(LOG_ERR, "rn_delete: inconsistent annotation\n"); return 0; /* dangling ref could cause disaster */ } } else { if (m->rm_mask != tt->rn_mask) { log(LOG_ERR, "rn_delete: inconsistent annotation\n"); goto on1; } if (--m->rm_refs >= 0) goto on1; } b = -1 - tt->rn_b; t = saved_tt->rn_p; if (b > t->rn_b) goto on1; /* Wasn't lifted at all */ do { x = t; t = t->rn_p; } while (b <= t->rn_b && x != top); for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) if (m == saved_m) { *mp = m->rm_mklist; MKFree(m); break; } - if (m == 0) { + if (m == NULL) { log(LOG_ERR, "rn_delete: couldn't find our annotation\n"); if (tt->rn_flags & RNF_NORMAL) return (0); /* Dangling ref to us */ } on1: /* * Eliminate us from tree */ if (tt->rn_flags & RNF_ROOT) return (0); #ifdef RN_DEBUG /* Get us out of the creation list */ for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {} if (t) t->rn_ybro = tt->rn_ybro; #endif t = tt->rn_p; if ((dupedkey = saved_tt->rn_dupedkey)) { if (tt == saved_tt) { x = dupedkey; x->rn_p = t; if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x; } else { for (x = p = saved_tt; p && p->rn_dupedkey != tt;) p = p->rn_dupedkey; if (p) p->rn_dupedkey = tt->rn_dupedkey; else log(LOG_ERR, "rn_delete: couldn't find us\n"); } t = tt + 1; if (t->rn_flags & RNF_ACTIVE) { #ifndef RN_DEBUG *++x = *t; p = t->rn_p; #else b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p; #endif if (p->rn_l == t) p->rn_l = x; else p->rn_r = x; x->rn_l->rn_p = x; x->rn_r->rn_p = x; } goto out; } if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l; p = t->rn_p; if (p->rn_r == t) p->rn_r = x; else p->rn_l = x; x->rn_p = p; /* * Demote routes attached to us. */ if (t->rn_mklist) { if (x->rn_b >= 0) { for (mp = &x->rn_mklist; (m = *mp);) mp = &m->rm_mklist; *mp = t->rn_mklist; } else { /* If there are any key,mask pairs in a sibling duped-key chain, some subset will appear sorted in the same order attached to our mklist */ for (m = t->rn_mklist; m && x; x = x->rn_dupedkey) if (m == x->rn_mklist) { struct radix_mask *mm = m->rm_mklist; x->rn_mklist = 0; if (--(m->rm_refs) < 0) MKFree(m); m = mm; } if (m) syslog(LOG_ERR, "%s 0x%lx at 0x%lx\n", "rn_delete: Orphaned Mask", (unsigned long)m, (unsigned long)x); } } /* * We may be holding an active internal node in the tree. */ x = tt + 1; if (t != x) { #ifndef RN_DEBUG *t = *x; #else b = t->rn_info; *t = *x; t->rn_info = b; #endif t->rn_l->rn_p = t; t->rn_r->rn_p = t; p = x->rn_p; if (p->rn_l == x) p->rn_l = t; else p->rn_r = t; } out: tt->rn_flags &= ~RNF_ACTIVE; tt[1].rn_flags &= ~RNF_ACTIVE; return (tt); } int rn_walktree(struct radix_node_head *h, int (*f)(struct radix_node *, struct walkarg *), struct walkarg *w) { int error; struct radix_node *base, *next; struct radix_node *rn = h->rnh_treetop; /* * This gets complicated because we may delete the node * while applying the function f to it, so we need to calculate * the successor node in advance. */ /* First time through node, go left */ while (rn->rn_b >= 0) rn = rn->rn_l; for (;;) { base = rn; /* If at right child go back up, otherwise, go right */ while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0) rn = rn->rn_p; /* Find the next *leaf* since next node might vanish, too */ for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;) rn = rn->rn_l; next = rn; /* Process leaves */ while ((rn = base)) { base = rn->rn_dupedkey; if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w))) return (error); } rn = next; if (rn->rn_flags & RNF_ROOT) return (0); } /* NOTREACHED */ } int rn_inithead(struct radix_node_head **head, int off) { struct radix_node_head *rnh; struct radix_node *t, *tt, *ttt; if (*head) return (1); rnh = (struct radix_node_head *)rtmalloc(sizeof(*rnh), "rn_inithead"); Bzero(rnh, sizeof (*rnh)); *head = rnh; t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); ttt = rnh->rnh_nodes + 2; t->rn_r = ttt; t->rn_p = t; tt = t->rn_l; tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; tt->rn_b = -1 - off; *ttt = *tt; ttt->rn_key = rn_ones; rnh->rnh_addaddr = rn_addroute; rnh->rnh_deladdr = rn_delete; rnh->rnh_matchaddr = rn_match; rnh->rnh_lookup = rn_lookup; rnh->rnh_walktree = rn_walktree; rnh->rnh_treetop = t; return (1); } void rn_init(void) { char *cp, *cplim; if (max_keylen == 0) { printf("rn_init: radix functions require max_keylen be set\n"); return; } rn_zeros = (char *)rtmalloc(3 * max_keylen, "rn_init"); Bzero(rn_zeros, 3 * max_keylen); rn_ones = cp = rn_zeros + max_keylen; addmask_key = cplim = rn_ones + max_keylen; while (cp < cplim) *cp++ = -1; if (rn_inithead(&mask_rnhead, 0) == 0) panic("rn_init 2"); } Index: head/sbin/routed/rdisc.c =================================================================== --- head/sbin/routed/rdisc.c (revision 299767) +++ head/sbin/routed/rdisc.c (revision 299768) @@ -1,1062 +1,1062 @@ /* * Copyright (c) 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #include #include #include #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif /* router advertisement ICMP packet */ struct icmp_ad { u_int8_t icmp_type; /* type of message */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement cksum of struct */ u_int8_t icmp_ad_num; /* # of following router addresses */ u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ u_int16_t icmp_ad_life; /* seconds of validity */ struct icmp_ad_info { n_long icmp_ad_addr; n_long icmp_ad_pref; } icmp_ad_info[1]; }; /* router solicitation ICMP packet */ struct icmp_so { u_int8_t icmp_type; /* type of message */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement cksum of struct */ n_long icmp_so_rsvd; }; union ad_u { struct icmp icmp; struct icmp_ad ad; struct icmp_so so; }; int rdisc_sock = -1; /* router-discovery raw socket */ static const struct interface *rdisc_sock_mcast; /* current multicast interface */ struct timeval rdisc_timer; int rdisc_ok; /* using solicited route */ #define MAX_ADS 16 /* at least one per interface */ struct dr { /* accumulated advertisements */ struct interface *dr_ifp; naddr dr_gate; /* gateway */ time_t dr_ts; /* when received */ time_t dr_life; /* lifetime in host byte order */ n_long dr_recv_pref; /* received but biased preference */ n_long dr_pref; /* preference adjusted by metric */ }; static const struct dr *cur_drp; static struct dr drs[MAX_ADS]; /* convert between signed, balanced around zero, * and unsigned zero-based preferences */ #define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel) #define UNSIGN_PREF(p) SIGN_PREF(p) /* adjust unsigned preference by interface metric, * without driving it to infinity */ #define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\ ? ((p) != 0 ? 1 : 0) \ : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric)) static void rdisc_sort(void); /* dump an ICMP Router Discovery Advertisement Message */ static void trace_rdisc(const char *act, naddr from, naddr to, struct interface *ifp, union ad_u *p, u_int len) { int i; n_long *wp, *lim; - if (!TRACEPACKETS || ftrace == 0) + if (!TRACEPACKETS || ftrace == NULL) return; lastlog(); if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { (void)fprintf(ftrace, "%s Router Ad" " from %s to %s via %s life=%d\n", act, naddr_ntoa(from), naddr_ntoa(to), ifp ? ifp->int_name : "?", ntohs(p->ad.icmp_ad_life)); if (!TRACECONTENTS) return; wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { (void)fprintf(ftrace, "\t%s preference=%d", naddr_ntoa(wp[0]), (int)ntohl(wp[1])); wp += p->ad.icmp_ad_asize; } (void)fputc('\n',ftrace); } else { trace_act("%s Router Solic. from %s to %s via %s value=%#x", act, naddr_ntoa(from), naddr_ntoa(to), ifp ? ifp->int_name : "?", (int)ntohl(p->so.icmp_so_rsvd)); } } /* prepare Router Discovery socket. */ static void get_rdisc_sock(void) { if (rdisc_sock < 0) { rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (rdisc_sock < 0) BADERR(1,"rdisc_sock = socket()"); fix_sock(rdisc_sock,"rdisc_sock"); fix_select(); } } /* Pick multicast group for router-discovery socket */ void set_rdisc_mg(struct interface *ifp, int on) /* 0=turn it off */ { struct group_req gr; struct sockaddr_in *sin; assert(ifp != NULL); if (rdisc_sock < 0) { /* Create the raw socket so that we can hear at least * broadcast router discovery packets. */ if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC || !on) return; get_rdisc_sock(); } if (!(ifp->int_if_flags & IFF_MULTICAST)) { ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); return; } memset(&gr, 0, sizeof(gr)); gr.gr_interface = ifp->int_index; sin = (struct sockaddr_in *)&gr.gr_group; sin->sin_family = AF_INET; #ifdef _HAVE_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif if (supplier || (ifp->int_state & IS_NO_ADV_IN) || !on) { /* stop listening to advertisements */ if (ifp->int_state & IS_ALL_HOSTS) { sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_LEAVE_GROUP, &gr, sizeof(gr)) < 0) LOGERR("MCAST_LEAVE_GROUP ALLHOSTS"); ifp->int_state &= ~IS_ALL_HOSTS; } } else if (!(ifp->int_state & IS_ALL_HOSTS)) { /* start listening to advertisements */ sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0) { LOGERR("MCAST_JOIN_GROUP ALLHOSTS"); } else { ifp->int_state |= IS_ALL_HOSTS; } } if (!supplier || (ifp->int_state & IS_NO_ADV_OUT) || !on) { /* stop listening to solicitations */ if (ifp->int_state & IS_ALL_ROUTERS) { sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP); if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_LEAVE_GROUP, &gr, sizeof(gr)) < 0) LOGERR("MCAST_LEAVE_GROUP ALLROUTERS"); ifp->int_state &= ~IS_ALL_ROUTERS; } } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { /* start hearing solicitations */ sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP); if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0) { LOGERR("MCAST_JOIN_GROUP ALLROUTERS"); } else { ifp->int_state |= IS_ALL_ROUTERS; } } } /* start supplying routes */ void set_supplier(void) { struct interface *ifp; struct dr *drp; if (supplier_set) return; trace_act("start supplying routes"); /* Forget discovered routes. */ for (drp = drs; drp < &drs[MAX_ADS]; drp++) { drp->dr_recv_pref = 0; drp->dr_life = 0; } rdisc_age(0); supplier_set = 1; supplier = 1; /* Do not start advertising until we have heard some RIP routes */ LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); /* Switch router discovery multicast groups from soliciting * to advertising. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_state & IS_BROKE) continue; ifp->int_rdisc_cnt = 0; ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; set_rdisc_mg(ifp, 1); } /* get rid of any redirects */ del_redirects(0,0); } /* age discovered routes and find the best one */ void rdisc_age(naddr bad_gate) { time_t sec; struct dr *drp; /* If only advertising, then do only that. */ if (supplier) { /* If switching from client to server, get rid of old * default routes. */ - if (cur_drp != 0) + if (cur_drp != NULL) rdisc_sort(); rdisc_adv(); return; } /* If we are being told about a bad router, * then age the discovered default route, and if there is * no alternative, solicit a replacement. */ if (bad_gate != 0) { /* Look for the bad discovered default route. * Age it and note its interface. */ for (drp = drs; drp < &drs[MAX_ADS]; drp++) { if (drp->dr_ts == 0) continue; /* When we find the bad router, then age the route * to at most SUPPLY_INTERVAL. * This is contrary to RFC 1256, but defends against * black holes. */ if (drp->dr_gate == bad_gate) { sec = (now.tv_sec - drp->dr_life + SUPPLY_INTERVAL); if (drp->dr_ts > sec) { trace_act("age 0.0.0.0 --> %s via %s", naddr_ntoa(drp->dr_gate), drp->dr_ifp->int_name); drp->dr_ts = sec; } break; } } } rdisc_sol(); rdisc_sort(); /* Delete old redirected routes to keep the kernel table small, * and to prevent black holes. Check that the kernel table * matches the daemon table (i.e. has the default route). * But only if RIP is not running and we are not dealing with * a bad gateway, since otherwise age() will be called. */ if (rip_sock < 0 && bad_gate == 0) age(0); } /* Zap all routes discovered via an interface that has gone bad * This should only be called when !(ifp->int_state & IS_ALIAS) */ void if_bad_rdisc(struct interface *ifp) { struct dr *drp; for (drp = drs; drp < &drs[MAX_ADS]; drp++) { if (drp->dr_ifp != ifp) continue; drp->dr_recv_pref = 0; drp->dr_ts = 0; drp->dr_life = 0; } /* make a note to re-solicit, turn RIP on or off, etc. */ rdisc_timer.tv_sec = 0; } /* mark an interface ok for router discovering. */ void if_ok_rdisc(struct interface *ifp) { set_rdisc_mg(ifp, 1); ifp->int_rdisc_cnt = 0; ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier ? MIN_WAITTIME : MAX_SOLICITATION_DELAY); if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) rdisc_timer = ifp->int_rdisc_timer; } /* get rid of a dead discovered router */ static void del_rdisc(struct dr *drp) { struct interface *ifp; naddr gate; int i; del_redirects(gate = drp->dr_gate, 0); drp->dr_ts = 0; drp->dr_life = 0; /* Count the other discovered routes on the interface. */ i = 0; ifp = drp->dr_ifp; for (drp = drs; drp < &drs[MAX_ADS]; drp++) { if (drp->dr_ts != 0 && drp->dr_ifp == ifp) i++; } /* If that was the last good discovered router on the interface, * then solicit a new one. * This is contrary to RFC 1256, but defends against black holes. */ if (i != 0) { trace_act("discovered router %s via %s" " is bad--have %d remaining", naddr_ntoa(gate), ifp->int_name, i); } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { trace_act("last discovered router %s via %s" " is bad--re-solicit", naddr_ntoa(gate), ifp->int_name); ifp->int_rdisc_cnt = 0; ifp->int_rdisc_timer.tv_sec = 0; rdisc_sol(); } else { trace_act("last discovered router %s via %s" " is bad--wait to solicit", naddr_ntoa(gate), ifp->int_name); } } /* Find the best discovered route, * and discard stale routers. */ static void rdisc_sort(void) { struct dr *drp, *new_drp; struct rt_entry *rt; struct rt_spare new; struct interface *ifp; u_int new_st = 0; n_long new_pref = 0; /* Find the best discovered route. */ - new_drp = 0; + new_drp = NULL; for (drp = drs; drp < &drs[MAX_ADS]; drp++) { if (drp->dr_ts == 0) continue; ifp = drp->dr_ifp; /* Get rid of expired discovered routers. */ if (drp->dr_ts + drp->dr_life <= now.tv_sec) { del_rdisc(drp); continue; } LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); /* Update preference with possibly changed interface * metric. */ drp->dr_pref = PREF(drp->dr_recv_pref, ifp); /* Prefer the current route to prevent thrashing. * Prefer shorter lifetimes to speed the detection of * bad routers. * Avoid sick interfaces. */ - if (new_drp == 0 + if (new_drp == NULL || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) && (new_pref < drp->dr_pref || (new_pref == drp->dr_pref && (drp == cur_drp || (new_drp != cur_drp && new_drp->dr_life > drp->dr_life))))) || ((new_st & IS_SICK) && !(drp->dr_ifp->int_state & IS_SICK))) { new_drp = drp; new_st = drp->dr_ifp->int_state; new_pref = drp->dr_pref; } } /* switch to a better default route */ if (new_drp != cur_drp) { rt = rtget(RIP_DEFAULT, 0); /* Stop using discovered routes if they are all bad */ - if (new_drp == 0) { + if (new_drp == NULL) { trace_act("turn off Router Discovery client"); rdisc_ok = 0; - if (rt != 0 + if (rt != NULL && (rt->rt_state & RS_RDISC)) { new = rt->rt_spares[0]; new.rts_metric = HOPCNT_INFINITY; new.rts_time = now.tv_sec - GARBAGE_TIME; rtchange(rt, rt->rt_state & ~RS_RDISC, &new, 0); rtswitch(rt, 0); } } else { - if (cur_drp == 0) { + if (cur_drp == NULL) { trace_act("turn on Router Discovery client" " using %s via %s", naddr_ntoa(new_drp->dr_gate), new_drp->dr_ifp->int_name); rdisc_ok = 1; } else { trace_act("switch Router Discovery from" " %s via %s to %s via %s", naddr_ntoa(cur_drp->dr_gate), cur_drp->dr_ifp->int_name, naddr_ntoa(new_drp->dr_gate), new_drp->dr_ifp->int_name); } memset(&new, 0, sizeof(new)); new.rts_ifp = new_drp->dr_ifp; new.rts_gate = new_drp->dr_gate; new.rts_router = new_drp->dr_gate; new.rts_metric = HOPCNT_INFINITY-1; new.rts_time = now.tv_sec; - if (rt != 0) { + if (rt != NULL) { rtchange(rt, rt->rt_state | RS_RDISC, &new, 0); } else { rtadd(RIP_DEFAULT, 0, RS_RDISC, &new); } } cur_drp = new_drp; } /* turn RIP on or off */ if (!rdisc_ok || rip_interfaces > 1) { rip_on(0); } else { rip_off(); } } /* handle a single address in an advertisement */ static void parse_ad(naddr from, naddr gate, n_long pref, /* signed and in network order */ u_short life, /* in host byte order */ struct interface *ifp) { static struct msg_limit bad_gate; struct dr *drp, *new_drp; if (gate == RIP_DEFAULT || !check_dst(gate)) { msglim(&bad_gate, from,"router %s advertising bad gateway %s", naddr_ntoa(from), naddr_ntoa(gate)); return; } /* ignore pointers to ourself and routes via unreachable networks */ - if (ifwithaddr(gate, 1, 0) != 0) { + if (ifwithaddr(gate, 1, 0) != NULL) { trace_pkt(" discard Router Discovery Ad pointing at us"); return; } if (!on_net(gate, ifp->int_net, ifp->int_mask)) { trace_pkt(" discard Router Discovery Ad" " toward unreachable net"); return; } /* Convert preference to an unsigned value * and later bias it by the metric of the interface. */ pref = UNSIGN_PREF(ntohl(pref)); if (pref == 0 || life < MinMaxAdvertiseInterval) { pref = 0; life = 0; } - for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { + for (new_drp = NULL, drp = drs; drp < &drs[MAX_ADS]; drp++) { /* accept new info for a familiar entry */ if (drp->dr_gate == gate) { new_drp = drp; break; } if (life == 0) continue; /* do not worry about dead ads */ if (drp->dr_ts == 0) { new_drp = drp; /* use unused entry */ - } else if (new_drp == 0) { + } else if (new_drp == NULL) { /* look for an entry worse than the new one to * reuse. */ if ((!(ifp->int_state & IS_SICK) && (drp->dr_ifp->int_state & IS_SICK)) || (pref > drp->dr_pref && !((ifp->int_state ^ drp->dr_ifp->int_state) & IS_SICK))) new_drp = drp; } else if (new_drp->dr_ts != 0) { /* look for the least valuable entry to reuse */ if ((!(new_drp->dr_ifp->int_state & IS_SICK) && (drp->dr_ifp->int_state & IS_SICK)) || (new_drp->dr_pref > drp->dr_pref && !((new_drp->dr_ifp->int_state ^ drp->dr_ifp->int_state) & IS_SICK))) new_drp = drp; } } /* forget it if all of the current entries are better */ - if (new_drp == 0) + if (new_drp == NULL) return; new_drp->dr_ifp = ifp; new_drp->dr_gate = gate; new_drp->dr_ts = now.tv_sec; new_drp->dr_life = life; new_drp->dr_recv_pref = pref; /* bias functional preference by metric of the interface */ new_drp->dr_pref = PREF(pref,ifp); /* after hearing a good advertisement, stop asking */ if (!(ifp->int_state & IS_SICK)) ifp->int_rdisc_cnt = MAX_SOLICITATIONS; } /* Compute the IP checksum * This assumes the packet is less than 32K long. */ static u_short in_cksum(u_short *p, u_int len) { u_int sum = 0; int nwords = len >> 1; while (nwords-- != 0) sum += *p++; if (len & 1) sum += *(u_char *)p; /* end-around-carry */ sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (~sum); } /* Send a router discovery advertisement or solicitation ICMP packet. */ static void send_rdisc(union ad_u *p, int p_size, struct interface *ifp, naddr dst, /* 0 or unicast destination */ int type) /* 0=unicast, 1=bcast, 2=mcast */ { struct sockaddr_in rsin; int flags; const char *msg; memset(&rsin, 0, sizeof(rsin)); rsin.sin_addr.s_addr = dst; rsin.sin_family = AF_INET; #ifdef _HAVE_SIN_LEN rsin.sin_len = sizeof(rsin); #endif flags = MSG_DONTROUTE; switch (type) { case 0: /* unicast */ default: msg = "Send"; break; case 1: /* broadcast */ if (ifp->int_if_flags & IFF_POINTOPOINT) { msg = "Send pt-to-pt"; rsin.sin_addr.s_addr = ifp->int_dstaddr; } else { msg = "Send broadcast"; rsin.sin_addr.s_addr = ifp->int_brdaddr; } break; case 2: /* multicast */ msg = "Send multicast"; if (ifp->int_state & IS_DUP) { trace_act("abort multicast output via %s" " with duplicate address", ifp->int_name); return; } if (rdisc_sock_mcast != ifp) { /* select the right interface. */ struct ip_mreqn mreqn; memset(&mreqn, 0, sizeof(struct ip_mreqn)); mreqn.imr_ifindex = ifp->int_index; if (0 > setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) { LOGERR("setsockopt(rdisc_sock," "IP_MULTICAST_IF)"); - rdisc_sock_mcast = 0; + rdisc_sock_mcast = NULL; return; } rdisc_sock_mcast = ifp; } flags = 0; break; } if (rdisc_sock < 0) get_rdisc_sock(); trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp, p, p_size); if (0 > sendto(rdisc_sock, p, p_size, flags, (struct sockaddr *)&rsin, sizeof(rsin))) { - if (ifp == 0 || !(ifp->int_state & IS_BROKE)) + if (ifp == NULL || !(ifp->int_state & IS_BROKE)) msglog("sendto(%s%s%s): %s", - ifp != 0 ? ifp->int_name : "", - ifp != 0 ? ", " : "", + ifp != NULL ? ifp->int_name : "", + ifp != NULL ? ", " : "", inet_ntoa(rsin.sin_addr), strerror(errno)); - if (ifp != 0) + if (ifp != NULL) if_sick(ifp); } } /* Send an advertisement */ static void send_adv(struct interface *ifp, naddr dst, /* 0 or unicast destination */ int type) /* 0=unicast, 1=bcast, 2=mcast */ { union ad_u u; n_long pref; memset(&u, 0, sizeof(u.ad)); u.ad.icmp_type = ICMP_ROUTERADVERT; u.ad.icmp_ad_num = 1; u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); /* Convert the configured preference to an unsigned value, * bias it by the interface metric, and then send it as a * signed, network byte order value. */ pref = UNSIGN_PREF(ifp->int_rdisc_pref); u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp))); u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); send_rdisc(&u, sizeof(u.ad), ifp, dst, type); } /* Advertise for Router Discovery */ void rdisc_adv(void) { struct interface *ifp; if (!supplier) return; rdisc_timer.tv_sec = now.tv_sec + NEVER; LIST_FOREACH(ifp, &ifnet, int_list) { if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE))) continue; if (!timercmp(&ifp->int_rdisc_timer, &now, >) || stopint) { send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); ifp->int_rdisc_cnt++; intvl_random(&ifp->int_rdisc_timer, (ifp->int_rdisc_int*3)/4, ifp->int_rdisc_int); if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS && (ifp->int_rdisc_timer.tv_sec > MAX_INITIAL_ADVERT_INTERVAL)) { ifp->int_rdisc_timer.tv_sec = MAX_INITIAL_ADVERT_INTERVAL; } timevaladd(&ifp->int_rdisc_timer, &now); } if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) rdisc_timer = ifp->int_rdisc_timer; } } /* Solicit for Router Discovery */ void rdisc_sol(void) { struct interface *ifp; union ad_u u; if (supplier) return; rdisc_timer.tv_sec = now.tv_sec + NEVER; LIST_FOREACH(ifp, &ifnet, int_list) { if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) continue; if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { memset(&u, 0, sizeof(u.so)); u.so.icmp_type = ICMP_ROUTERSOLICIT; u.so.icmp_cksum = in_cksum((u_short*)&u.so, sizeof(u.so)); send_rdisc(&u, sizeof(u.so), ifp, htonl(INADDR_ALLROUTERS_GROUP), ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) continue; ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; ifp->int_rdisc_timer.tv_usec = 0; timevaladd(&ifp->int_rdisc_timer, &now); } if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) rdisc_timer = ifp->int_rdisc_timer; } } /* check the IP header of a possible Router Discovery ICMP packet */ static struct interface * /* 0 if bad */ ck_icmp(const char *act, naddr from, struct interface *ifp, naddr to, union ad_u *p, u_int len) { const char *type; if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { type = "advertisement"; } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { type = "solicitation"; } else { return 0; } if (p->icmp.icmp_code != 0) { trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to)); return 0; } trace_rdisc(act, from, to, ifp, p, len); - if (ifp == 0) + if (ifp == NULL) trace_pkt("unknown interface for router-discovery %s" " from %s to %s", type, naddr_ntoa(from), naddr_ntoa(to)); return ifp; } /* read packets from the router discovery socket */ void read_d(void) { static struct msg_limit bad_asize, bad_len; #ifdef USE_PASSIFNAME static struct msg_limit bad_name; #endif struct sockaddr_in from; int n, fromlen, cc, hlen; struct { #ifdef USE_PASSIFNAME char ifname[IFNAMSIZ]; #endif union { struct ip ip; u_char b[512]; } pkt; } buf; union ad_u *p; n_long *wp; struct interface *ifp; for (;;) { fromlen = sizeof(from); cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (cc <= 0) { if (cc < 0 && errno != EWOULDBLOCK) LOGERR("recvfrom(rdisc_sock)"); break; } if (fromlen != sizeof(struct sockaddr_in)) logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", fromlen); #ifdef USE_PASSIFNAME if ((cc -= sizeof(buf.ifname)) < 0) logbad(0,"missing USE_PASSIFNAME; only %d bytes", cc+sizeof(buf.ifname)); #endif hlen = buf.pkt.ip.ip_hl << 2; if (cc < hlen + ICMP_MINLEN) continue; p = (union ad_u *)&buf.pkt.b[hlen]; cc -= hlen; #ifdef USE_PASSIFNAME ifp = ifwithname(buf.ifname, 0); - if (ifp == 0) + if (ifp == NULL) msglim(&bad_name, from.sin_addr.s_addr, "impossible rdisc if_ name %.*s", IFNAMSIZ, buf.ifname); #else /* If we could tell the interface on which a packet from * address 0 arrived, we could deal with such solicitations. */ ifp = ((from.sin_addr.s_addr == 0) ? 0 : iflookup(from.sin_addr.s_addr)); #endif ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp, buf.pkt.ip.ip_dst.s_addr, p, cc); - if (ifp == 0) + if (ifp == NULL) continue; if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { trace_pkt(" " "discard our own Router Discovery message"); continue; } switch (p->icmp.icmp_type) { case ICMP_ROUTERADVERT: if (p->ad.icmp_ad_asize*4 < (int)sizeof(p->ad.icmp_ad_info[0])) { msglim(&bad_asize, from.sin_addr.s_addr, "intolerable rdisc address size=%d", p->ad.icmp_ad_asize); continue; } if (p->ad.icmp_ad_num == 0) { trace_pkt(" empty?"); continue; } if (cc != (int)(sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) + (p->ad.icmp_ad_num * sizeof(p->ad.icmp_ad_info[0])))) { msglim(&bad_len, from.sin_addr.s_addr, "rdisc length %d does not match ad_num" " %d", cc, p->ad.icmp_ad_num); continue; } if (supplier) continue; if (ifp->int_state & IS_NO_ADV_IN) continue; wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; for (n = 0; n < p->ad.icmp_ad_num; n++) { parse_ad(from.sin_addr.s_addr, wp[0], wp[1], ntohs(p->ad.icmp_ad_life), ifp); wp += p->ad.icmp_ad_asize; } break; case ICMP_ROUTERSOLICIT: if (!supplier) continue; if (ifp->int_state & IS_NO_ADV_OUT) continue; if (stopint) continue; /* XXX * We should handle messages from address 0. */ /* Respond with a point-to-point advertisement */ send_adv(ifp, from.sin_addr.s_addr, 0); break; } } rdisc_sort(); } Index: head/sbin/routed/rtquery/rtquery.c =================================================================== --- head/sbin/routed/rtquery/rtquery.c (revision 299767) +++ head/sbin/routed/rtquery/rtquery.c (revision 299768) @@ -1,919 +1,919 @@ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #define RIPVERSION RIPv2 #include #include #include #include #include #include #include #include #ifdef sgi #include #include #endif #define UNUSED __attribute__((unused)) #ifndef __RCSID #define __RCSID(_s) static const char rcsid[] UNUSED = _s #endif #ifndef __COPYRIGHT #define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s #endif __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\n" "The Regents of the University of California." " All rights reserved.\n"); #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.26 $"); #ident "$Revision: 2.26 $" #endif #ifndef sgi #define _HAVE_SIN_LEN #endif #ifdef __NetBSD__ #include #else #define MD5_DIGEST_LEN 16 typedef struct { u_int32_t state[4]; /* state (ABCD) */ u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; extern void MD5Init(MD5_CTX*); extern void MD5Update(MD5_CTX*, u_char*, u_int); extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*); #endif #define WTIME 15 /* Time to wait for all responses */ #define STIME (250*1000) /* usec to wait for another response */ int soc; const char *pgmname; union { struct rip rip; char packet[MAXPACKETSIZE+MAXPATHLEN]; } omsg_buf; #define OMSG omsg_buf.rip int omsg_len = sizeof(struct rip); union { struct rip rip; char packet[MAXPACKETSIZE+1024]; } imsg_buf; #define IMSG imsg_buf.rip int nflag; /* numbers, no names */ int pflag; /* play the `gated` game */ int ripv2 = 1; /* use RIP version 2 */ int wtime = WTIME; int rflag; /* 1=ask about a particular route */ int trace, not_trace; /* send trace command or not */ int auth_type = RIP_AUTH_NONE; char passwd[RIP_AUTH_PW_LEN]; u_long keyid; struct timeval sent; /* when query sent */ static char localhost_str[] = "localhost"; static char *default_argv[] = {localhost_str, 0}; static void rip_input(struct sockaddr_in*, int); static int out(const char *); static void trace_loop(char *argv[]) __attribute((__noreturn__)); static void query_loop(char *argv[], int) __attribute((__noreturn__)); static int getnet(char *, struct netinfo *); static u_int std_mask(u_int); static int parse_quote(char **, const char *, char *, char *, int); static void usage(void); int main(int argc, char *argv[]) { int ch, bsize; char *p, *options, *value, delim; const char *result; OMSG.rip_nets[0].n_dst = RIP_DEFAULT; OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); pgmname = argv[0]; while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != -1) switch (ch) { case 'n': not_trace = 1; nflag = 1; break; case 'p': not_trace = 1; pflag = 1; break; case '1': ripv2 = 0; break; case 'w': not_trace = 1; wtime = (int)strtoul(optarg, &p, 0); if (*p != '\0' || wtime <= 0) usage(); break; case 'r': not_trace = 1; if (rflag) usage(); rflag = getnet(optarg, &OMSG.rip_nets[0]); if (!rflag) { struct hostent *hp = gethostbyname(optarg); - if (hp == 0) { + if (hp == NULL) { fprintf(stderr, "%s: %s:", pgmname, optarg); herror(0); exit(1); } memcpy(&OMSG.rip_nets[0].n_dst, hp->h_addr, sizeof(OMSG.rip_nets[0].n_dst)); OMSG.rip_nets[0].n_family = RIP_AF_INET; OMSG.rip_nets[0].n_mask = -1; rflag = 1; } break; case 't': trace = 1; options = optarg; while (*options != '\0') { /* messy complications to make -W -Wall happy */ static char on_str[] = "on"; static char more_str[] = "more"; static char off_str[] = "off"; static char dump_str[] = "dump"; static char *traceopts[] = { # define TRACE_ON 0 on_str, # define TRACE_MORE 1 more_str, # define TRACE_OFF 2 off_str, # define TRACE_DUMP 3 dump_str, 0 }; result = ""; switch (getsubopt(&options,traceopts,&value)) { case TRACE_ON: OMSG.rip_cmd = RIPCMD_TRACEON; if (!value || strlen(value) > MAXPATHLEN) usage(); result = value; break; case TRACE_MORE: if (value) usage(); OMSG.rip_cmd = RIPCMD_TRACEON; break; case TRACE_OFF: if (value) usage(); OMSG.rip_cmd = RIPCMD_TRACEOFF; break; case TRACE_DUMP: if (value) usage(); OMSG.rip_cmd = RIPCMD_TRACEON; result = "dump/../table"; break; default: usage(); } strcpy((char*)OMSG.rip_tracefile, result); omsg_len += strlen(result) - sizeof(OMSG.ripun); } break; case 'a': not_trace = 1; p = strchr(optarg,'='); if (!p) usage(); *p++ = '\0'; if (!strcasecmp("passwd",optarg)) auth_type = RIP_AUTH_PW; else if (!strcasecmp("md5_passwd",optarg)) auth_type = RIP_AUTH_MD5; else usage(); if (0 > parse_quote(&p,"|",&delim, passwd, sizeof(passwd))) usage(); if (auth_type == RIP_AUTH_MD5 && delim == '|') { keyid = strtoul(p+1,&p,0); if (keyid > 255 || *p != '\0') usage(); } else if (delim != '\0') { usage(); } break; default: usage(); } argv += optind; argc -= optind; if (not_trace && trace) usage(); if (argc == 0) { argc = 1; argv = default_argv; } soc = socket(AF_INET, SOCK_DGRAM, 0); if (soc < 0) { perror("socket"); exit(2); } /* be prepared to receive a lot of routes */ for (bsize = 127*1024; ; bsize -= 1024) { if (setsockopt(soc, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == 0) break; if (bsize <= 4*1024) { perror("setsockopt SO_RCVBUF"); break; } } if (trace) trace_loop(argv); else query_loop(argv, argc); /* NOTREACHED */ return 0; } static void usage(void) { fprintf(stderr, "usage: rtquery [-np1] [-r tgt_rt] [-w wtime]" " [-a type=passwd] host1 [host2 ...]\n" "\trtquery -t {on=filename|more|off|dump}" " host1 [host2 ...]\n"); exit(1); } /* tell the target hosts about tracing */ static void trace_loop(char *argv[]) { struct sockaddr_in myaddr; int res; if (geteuid() != 0) { (void)fprintf(stderr, "-t requires UID 0\n"); exit(1); } if (ripv2) { OMSG.rip_vers = RIPv2; } else { OMSG.rip_vers = RIPv1; } memset(&myaddr, 0, sizeof(myaddr)); myaddr.sin_family = AF_INET; #ifdef _HAVE_SIN_LEN myaddr.sin_len = sizeof(myaddr); #endif myaddr.sin_port = htons(IPPORT_RESERVED-1); while (bind(soc, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { if (errno != EADDRINUSE || myaddr.sin_port == 0) { perror("bind"); exit(2); } myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1); } res = 1; - while (*argv != 0) { + while (*argv != NULL) { if (out(*argv++) <= 0) res = 0; } exit(res); } /* query all of the listed hosts */ static void query_loop(char *argv[], int argc) { # define NA0 (OMSG.rip_auths[0]) # define NA2 (OMSG.rip_auths[2]) struct seen { struct seen *next; struct in_addr addr; } *seen, *sp; int answered = 0; int cc; fd_set bits; struct timeval now, delay; struct sockaddr_in from; int fromlen; MD5_CTX md5_ctx; OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; if (ripv2) { OMSG.rip_vers = RIPv2; if (auth_type == RIP_AUTH_PW) { OMSG.rip_nets[1] = OMSG.rip_nets[0]; NA0.a_family = RIP_AF_AUTH; NA0.a_type = RIP_AUTH_PW; memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN); omsg_len += sizeof(OMSG.rip_nets[0]); } else if (auth_type == RIP_AUTH_MD5) { OMSG.rip_nets[1] = OMSG.rip_nets[0]; NA0.a_family = RIP_AF_AUTH; NA0.a_type = RIP_AUTH_MD5; NA0.au.a_md5.md5_keyid = (int8_t)keyid; NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN; NA0.au.a_md5.md5_seqno = 0; cc = (char *)&NA2-(char *)&OMSG; NA0.au.a_md5.md5_pkt_len = htons(cc); NA2.a_family = RIP_AF_AUTH; NA2.a_type = htons(1); MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)&OMSG, cc); MD5Update(&md5_ctx, (u_char *)passwd, RIP_AUTH_MD5_HASH_LEN); MD5Final(NA2.au.au_pw, &md5_ctx); omsg_len += 2*sizeof(OMSG.rip_nets[0]); } } else { OMSG.rip_vers = RIPv1; OMSG.rip_nets[0].n_mask = 0; } /* ask the first (valid) host */ - seen = 0; + seen = NULL; while (0 > out(*argv++)) { - if (*argv == 0) + if (*argv == NULL) exit(1); answered++; } FD_ZERO(&bits); for (;;) { FD_SET(soc, &bits); delay.tv_sec = 0; delay.tv_usec = STIME; cc = select(soc+1, &bits, 0,0, &delay); if (cc > 0) { fromlen = sizeof(from); cc = recvfrom(soc, imsg_buf.packet, sizeof(imsg_buf.packet), 0, (struct sockaddr *)&from, &fromlen); if (cc < 0) { perror("recvfrom"); exit(1); } /* count the distinct responding hosts. * You cannot match responding hosts with * addresses to which queries were transmitted, * because a router might respond with a * different source address. */ - for (sp = seen; sp != 0; sp = sp->next) { + for (sp = seen; sp != NULL; sp = sp->next) { if (sp->addr.s_addr == from.sin_addr.s_addr) break; } - if (sp == 0) { + if (sp == NULL) { sp = malloc(sizeof(*sp)); - if (sp == 0) { + if (sp == NULL) { fprintf(stderr, "rtquery: malloc failed\n"); exit(1); } sp->addr = from.sin_addr; sp->next = seen; seen = sp; answered++; } rip_input(&from, cc); continue; } if (cc < 0) { if (errno == EINTR) continue; perror("select"); exit(1); } /* After a pause in responses, probe another host. * This reduces the intermingling of answers. */ - while (*argv != 0 && 0 > out(*argv++)) + while (*argv != NULL && out(*argv++) < 0) answered++; /* continue until no more packets arrive * or we have heard from all hosts */ if (answered >= argc) break; /* or until we have waited a long time */ if (gettimeofday(&now, 0) < 0) { perror("gettimeofday(now)"); exit(1); } if (sent.tv_sec + wtime <= now.tv_sec) break; } /* fail if there was no answer */ exit (answered >= argc ? 0 : 1); } /* send to one host */ static int out(const char *host) { struct sockaddr_in router; struct hostent *hp; if (gettimeofday(&sent, 0) < 0) { perror("gettimeofday(sent)"); return -1; } memset(&router, 0, sizeof(router)); router.sin_family = AF_INET; #ifdef _HAVE_SIN_LEN router.sin_len = sizeof(router); #endif if (!inet_aton(host, &router.sin_addr)) { hp = gethostbyname(host); - if (hp == 0) { + if (hp == NULL) { herror(host); return -1; } memcpy(&router.sin_addr, hp->h_addr, sizeof(router.sin_addr)); } router.sin_port = htons(RIP_PORT); if (sendto(soc, &omsg_buf, omsg_len, 0, (struct sockaddr *)&router, sizeof(router)) < 0) { perror(host); return -1; } return 0; } /* * Convert string to printable characters */ static char * qstring(u_char *s, int len) { static char buf[8*20+1]; char *p; u_char *s2, c; for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) { c = *s++; if (c == '\0') { for (s2 = s+1; s2 < &s[len]; s2++) { if (*s2 != '\0') break; } if (s2 >= &s[len]) goto exit; } if (c >= ' ' && c < 0x7f && c != '\\') { *p++ = c; continue; } *p++ = '\\'; switch (c) { case '\\': *p++ = '\\'; break; case '\n': *p++= 'n'; break; case '\r': *p++= 'r'; break; case '\t': *p++ = 't'; break; case '\b': *p++ = 'b'; break; default: p += sprintf(p,"%o",c); break; } } exit: *p = '\0'; return buf; } /* * Handle an incoming RIP packet. */ static void rip_input(struct sockaddr_in *from, int size) { struct netinfo *n, *lim; struct in_addr in; const char *name; char net_buf[80]; u_char hash[RIP_AUTH_MD5_KEY_LEN]; MD5_CTX md5_ctx; u_char md5_authed = 0; u_int mask, dmask; char *sp; int i; struct hostent *hp; struct netent *np; struct netauth *na; if (nflag) { printf("%s:", inet_ntoa(from->sin_addr)); } else { hp = gethostbyaddr((char*)&from->sin_addr, sizeof(struct in_addr), AF_INET); - if (hp == 0) { + if (hp == NULL) { printf("%s:", inet_ntoa(from->sin_addr)); } else { printf("%s (%s):", hp->h_name, inet_ntoa(from->sin_addr)); } } if (IMSG.rip_cmd != RIPCMD_RESPONSE) { printf("\n unexpected response type %d\n", IMSG.rip_cmd); return; } printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers, (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", size); if (size > MAXPACKETSIZE) { if (size > (int)sizeof(imsg_buf) - (int)sizeof(*n)) { printf(" at least %d bytes too long\n", size-MAXPACKETSIZE); size = (int)sizeof(imsg_buf) - (int)sizeof(*n); } else { printf(" %d bytes too long\n", size-MAXPACKETSIZE); } } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { printf(" response of bad length=%d\n", size); } n = IMSG.rip_nets; lim = (struct netinfo *)((char*)n + size) - 1; for (; n <= lim; n++) { name = ""; if (n->n_family == RIP_AF_INET) { in.s_addr = n->n_dst; (void)strcpy(net_buf, inet_ntoa(in)); mask = ntohl(n->n_mask); dmask = mask & -mask; if (mask != 0) { sp = &net_buf[strlen(net_buf)]; if (IMSG.rip_vers == RIPv1) { (void)sprintf(sp," mask=%#x ? ",mask); mask = 0; } else if (mask + dmask == 0) { for (i = 0; (i != 32 && ((1<n_name; else if (in.s_addr == 0) name = "default"; } if (name[0] == '\0' && ((in.s_addr & ~mask) != 0 || mask == 0xffffffff)) { hp = gethostbyaddr((char*)&in, sizeof(in), AF_INET); - if (hp != 0) + if (hp != NULL) name = hp->h_name; } } } else if (n->n_family == RIP_AF_AUTH) { na = (struct netauth*)n; if (na->a_type == RIP_AUTH_PW && n == IMSG.rip_nets) { (void)printf(" Password Authentication:" " \"%s\"\n", qstring(na->au.au_pw, RIP_AUTH_PW_LEN)); continue; } if (na->a_type == RIP_AUTH_MD5 && n == IMSG.rip_nets) { (void)printf(" MD5 Auth" " len=%d KeyID=%d" " auth_len=%d" " seqno=%#x" " rsvd=%#x,%#x\n", ntohs(na->au.a_md5.md5_pkt_len), na->au.a_md5.md5_keyid, na->au.a_md5.md5_auth_len, (int)ntohl(na->au.a_md5.md5_seqno), na->au.a_md5.rsvd[0], na->au.a_md5.rsvd[1]); md5_authed = 1; continue; } (void)printf(" Authentication type %d: ", ntohs(na->a_type)); for (i = 0; i < (int)sizeof(na->au.au_pw); i++) (void)printf("%02x ", na->au.au_pw[i]); putc('\n', stdout); if (md5_authed && n+1 > lim && na->a_type == ntohs(1)) { MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)&IMSG, (char *)na-(char *)&IMSG +RIP_AUTH_MD5_HASH_XTRA); MD5Update(&md5_ctx, (u_char *)passwd, RIP_AUTH_MD5_KEY_LEN); MD5Final(hash, &md5_ctx); (void)printf(" %s hash\n", memcmp(hash, na->au.au_pw, sizeof(hash)) ? "WRONG" : "correct"); } continue; } else { (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", ntohs(n->n_family), (u_char)(n->n_dst >> 24), (u_char)(n->n_dst >> 16), (u_char)(n->n_dst >> 8), (u_char)n->n_dst); } (void)printf(" %-18s metric %2d %-10s", net_buf, (int)ntohl(n->n_metric), name); if (n->n_nhop != 0) { in.s_addr = n->n_nhop; if (nflag) - hp = 0; + hp = NULL; else hp = gethostbyaddr((char*)&in, sizeof(in), AF_INET); (void)printf(" nhop=%-15s%s", - (hp != 0) ? hp->h_name : inet_ntoa(in), + (hp != NULL) ? hp->h_name : inet_ntoa(in), (IMSG.rip_vers == RIPv1) ? " ?" : ""); } if (n->n_tag != 0) (void)printf(" tag=%#x%s", n->n_tag, (IMSG.rip_vers == RIPv1) ? " ?" : ""); putc('\n', stdout); } } /* Return the classical netmask for an IP address. */ static u_int std_mask(u_int addr) /* in network order */ { addr = ntohl(addr); /* was a host, not a network */ if (addr == 0) /* default route has mask 0 */ return 0; if (IN_CLASSA(addr)) return IN_CLASSA_NET; if (IN_CLASSB(addr)) return IN_CLASSB_NET; return IN_CLASSC_NET; } /* get a network number as a name or a number, with an optional "/xx" * netmask. */ static int /* 0=bad */ getnet(char *name, struct netinfo *rt) { int i; struct netent *nentp; u_int mask; struct in_addr in; char hname[MAXHOSTNAMELEN+1]; char *mname, *p; /* Detect and separate "1.2.3.4/24" */ - if (0 != (mname = strrchr(name,'/'))) { + if (NULL != (mname = strrchr(name,'/'))) { i = (int)(mname - name); if (i > (int)sizeof(hname)-1) /* name too long */ return 0; memmove(hname, name, i); hname[i] = '\0'; mname++; name = hname; } nentp = getnetbyname(name); - if (nentp != 0) { + if (nentp != NULL) { in.s_addr = nentp->n_net; } else if (inet_aton(name, &in) == 1) { in.s_addr = ntohl(in.s_addr); } else { return 0; } - if (mname == 0) { + if (mname == NULL) { mask = std_mask(in.s_addr); if ((~mask & in.s_addr) != 0) mask = 0xffffffff; } else { mask = (u_int)strtoul(mname, &p, 0); if (*p != '\0' || mask > 32) return 0; mask = 0xffffffff << (32-mask); } rt->n_dst = htonl(in.s_addr); rt->n_family = RIP_AF_INET; rt->n_mask = htonl(mask); return 1; } /* strtok(), but honoring backslash */ static int /* -1=bad */ parse_quote(char **linep, const char *delims, char *delimp, char *buf, int lim) { char c, *pc; const char *p; pc = *linep; if (*pc == '\0') return -1; for (;;) { if (lim == 0) return -1; c = *pc++; if (c == '\0') break; if (c == '\\' && *pc != '\0') { if ((c = *pc++) == 'n') { c = '\n'; } else if (c == 'r') { c = '\r'; } else if (c == 't') { c = '\t'; } else if (c == 'b') { c = '\b'; } else if (c >= '0' && c <= '7') { c -= '0'; if (*pc >= '0' && *pc <= '7') { c = (c<<3)+(*pc++ - '0'); if (*pc >= '0' && *pc <= '7') c = (c<<3)+(*pc++ - '0'); } } } else { for (p = delims; *p != '\0'; ++p) { if (*p == c) goto exit; } } *buf++ = c; --lim; } exit: - if (delimp != 0) + if (delimp != NULL) *delimp = c; *linep = pc-1; if (lim != 0) *buf = '\0'; return 0; } Index: head/sbin/routed/table.c =================================================================== --- head/sbin/routed/table.c (revision 299767) +++ head/sbin/routed/table.c (revision 299768) @@ -1,2155 +1,2155 @@ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif 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 static void rtbad(struct rt_entry *); struct radix_node_head *rhead; /* root of the radix tree */ int need_flash = 1; /* flash update needed * start =1 to suppress the 1st */ struct timeval age_timer; /* next check of old routes */ struct timeval need_kern = { /* need to update kernel table */ EPOCH+MIN_WAITTIME-1, 0 }; int stopint; int total_routes; /* zap any old routes through this gateway */ static naddr age_bad_gate; /* It is desirable to "aggregate" routes, to combine differing routes of * the same metric and next hop into a common route with a smaller netmask * or to suppress redundant routes, routes that add no information to * routes with smaller netmasks. * * A route is redundant if and only if any and all routes with smaller * but matching netmasks and nets are the same. Since routes are * kept sorted in the radix tree, redundant routes always come second. * * There are two kinds of aggregations. First, two routes of the same bit * mask and differing only in the least significant bit of the network * number can be combined into a single route with a coarser mask. * * Second, a route can be suppressed in favor of another route with a more * coarse mask provided no incompatible routes with intermediate masks * are present. The second kind of aggregation involves suppressing routes. * A route must not be suppressed if an incompatible route exists with * an intermediate mask, since the suppressed route would be covered * by the intermediate. * * This code relies on the radix tree walk encountering routes * sorted first by address, with the smallest address first. */ static struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest; /* #define DEBUG_AG */ #ifdef DEBUG_AG #define CHECK_AG() {int acnt = 0; struct ag_info *cag; \ - for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \ + for (cag = ag_avail; cag != NULL; cag = cag->ag_fine) \ acnt++; \ - for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \ + for (cag = ag_corsest; cag != NULL; cag = cag->ag_fine) \ acnt++; \ if (acnt != NUM_AG_SLOTS) { \ (void)fflush(stderr); \ abort(); \ } \ } #else #define CHECK_AG() #endif /* Output the contents of an aggregation table slot. * This function must always be immediately followed with the deletion * of the target slot. */ static void ag_out(struct ag_info *ag, void (*out)(struct ag_info *)) { struct ag_info *ag_cors; 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. * 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. */ ag_cors = ag->ag_cors; - if (ag_cors != 0 + if (ag_cors != NULL && ag_cors->ag_mask == ag->ag_mask<<1 && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) { ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) ? AGS_REDUN0 : AGS_REDUN1); } /* Skip it if this route is itself redundant. * * It is ok to change the contents of the slot here, since it is * always deleted next. */ if (ag->ag_state & AGS_REDUN0) { if (ag->ag_state & AGS_REDUN1) 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; } out(ag); } static void ag_del(struct ag_info *ag) { CHECK_AG(); - if (ag->ag_cors == 0) + if (ag->ag_cors == NULL) ag_corsest = ag->ag_fine; else ag->ag_cors->ag_fine = ag->ag_fine; - if (ag->ag_fine == 0) + if (ag->ag_fine == NULL) ag_finest = ag->ag_cors; else ag->ag_fine->ag_cors = ag->ag_cors; ag->ag_fine = ag_avail; ag_avail = ag; CHECK_AG(); } /* Flush routes waiting for aggregation. * This must not suppress a route unless it is known that among all * routes with coarser masks that match it, the one with the longest * mask is appropriate. This is ensured by scanning the routes * in lexical order, and with the most restrictive mask first * among routes to the same destination. */ void ag_flush(naddr lim_dst_h, /* flush routes to here */ naddr lim_mask, /* matching this mask */ void (*out)(struct ag_info *)) { struct ag_info *ag, *ag_cors; naddr dst_h; for (ag = ag_finest; - ag != 0 && ag->ag_mask >= lim_mask; + ag != NULL && ag->ag_mask >= lim_mask; ag = ag_cors) { ag_cors = ag->ag_cors; /* work on only the specified routes */ dst_h = ag->ag_dst_h; if ((dst_h & lim_mask) != lim_dst_h) continue; if (!(ag->ag_state & AGS_SUPPRESS)) ag_out(ag, out); else for ( ; ; ag_cors = ag_cors->ag_cors) { /* Look for a route that can suppress the * current route */ - if (ag_cors == 0) { + if (ag_cors == NULL) { /* failed, so output it and look for * another route to work on */ ag_out(ag, out); break; } if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) { /* We found a route with a coarser mask that * aggregates the current target. * * If it has a different next hop, it * cannot replace the target, so output * the target. */ if (ag->ag_gate != ag_cors->ag_gate && !(ag->ag_state & AGS_FINE_GATE) && !(ag_cors->ag_state & AGS_CORS_GATE)) { ag_out(ag, out); break; } /* 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_IS_REDUN(ag->ag_state) && ag_cors->ag_mask==ag->ag_mask<<1) { if (ag_cors->ag_dst_h == dst_h) ag_cors->ag_state |= AGS_REDUN0; 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; } } } /* That route has either been output or suppressed */ ag_cors = ag->ag_cors; ag_del(ag); } CHECK_AG(); } /* Try to aggregate a route with previous routes. */ void ag_check(naddr dst, naddr mask, naddr gate, naddr nhop, char metric, char pref, u_int new_seqno, u_short tag, u_short state, void (*out)(struct ag_info *)) /* output using this */ { struct ag_info *ag, *nag, *ag_cors; naddr xaddr; int x; dst = ntohl(dst); /* Punt non-contiguous subnet masks. * * (X & -X) contains a single bit if and only if X is a power of 2. * (X + (X & -X)) == 0 if and only if X is a power of 2. */ if ((mask & -mask) + mask != 0) { struct ag_info nc_ag; 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; nc_ag.ag_state = state; nc_ag.ag_seqno = new_seqno; out(&nc_ag); return; } /* Search for the right slot in the aggregation table. */ - ag_cors = 0; + ag_cors = NULL; ag = ag_corsest; - while (ag != 0) { + while (ag != NULL) { if (ag->ag_mask >= mask) break; /* 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. * This check keeps poor routes (e.g. with large hop counts) * from preventing suppression of finer routes. */ - if (ag_cors != 0 + if (ag_cors != NULL && ag->ag_dst_h < dst && (ag->ag_state & AGS_SUPPRESS) && 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_FINE_GATE) || (ag_cors->ag_state & AGS_CORS_GATE))) { /* 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) ag_cors->ag_state |= AGS_REDUN0; 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 { ag_cors = ag; } ag = ag_cors->ag_fine; } /* If we find the even/odd twin of the new route, and if the * masks and so forth are equal, we can aggregate them. * We can probably promote one of the pair. * * Since the routes are encountered in lexical order, * the new route must be odd. However, the second or later * times around this loop, it could be the even twin promoted * from the even/odd pair of twins of the finer route. */ - while (ag != 0 + while (ag != NULL && ag->ag_mask == mask && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { /* 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) { /* 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 (or aggregated) pair and the route * already in the slot is the explicit route. * * Prefer the best route if their metrics differ, * or the aggregated 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; if (ag->ag_seqno < new_seqno) ag->ag_seqno = new_seqno; x = ag->ag_state; ag->ag_state = state; state = x; } /* 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; } /* 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. * * Any route that can be promoted is always * marked to be eligible to be suppressed. */ if (!((state & AGS_AGGREGATE) && (ag->ag_state & AGS_SUPPRESS)) && !((ag->ag_state & AGS_AGGREGATE) && (state & AGS_SUPPRESS))) break; /* 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. */ if (AG_IS_REDUN(ag->ag_state) || AG_IS_REDUN(state) || (ag->ag_gate == gate && ag->ag_pref == pref && (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 (aggregate) the pair of routes. */ if (new_seqno < ag->ag_seqno) new_seqno = ag->ag_seqno; if (!AG_IS_REDUN(state)) state &= ~AGS_REDUN1; if (AG_IS_REDUN(ag->ag_state)) state |= AGS_REDUN0; else state &= ~AGS_REDUN0; state |= (ag->ag_state & AGS_AGGREGATE_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. */ ag_del(ag); } else if (ag->ag_pref >= pref && (ag->ag_state & AGS_AGGREGATE)) { /* If we cannot combine the pair, maybe the route * with the worse metric can be promoted. * * Promote the old, even twin, by giving its slot * in the table to the new, odd twin. */ ag->ag_dst_h = dst; xaddr = ag->ag_gate; 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; /* 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; x = ag->ag_metric; ag->ag_metric = metric; metric = x; x = ag->ag_pref; ag->ag_pref = pref; pref = x; /* take the newest sequence number */ if (new_seqno <= ag->ag_seqno) new_seqno = ag->ag_seqno; else ag->ag_seqno = new_seqno; } else { if (!(state & AGS_AGGREGATE)) break; /* cannot promote either twin */ /* 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 (new_seqno < ag->ag_seqno) new_seqno = ag->ag_seqno; else ag->ag_seqno = new_seqno; } mask <<= 1; dst &= mask; - if (ag_cors == 0) { + if (ag_cors == NULL) { ag = ag_corsest; break; } ag = ag_cors; ag_cors = ag->ag_cors; } /* When we can no longer promote and combine routes, * flush the old route in the target slot. Also flush * any finer routes that we know will never be aggregated by * the new route. * * In case we moved toward coarser masks, * get back where we belong */ - if (ag != 0 + if (ag != NULL && ag->ag_mask < mask) { ag_cors = ag; ag = ag->ag_fine; } /* Empty the target slot */ - if (ag != 0 && ag->ag_mask == mask) { + if (ag != NULL && ag->ag_mask == mask) { ag_flush(ag->ag_dst_h, ag->ag_mask, out); - ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine; + ag = (ag_cors == NULL) ? ag_corsest : ag_cors->ag_fine; } #ifdef DEBUG_AG (void)fflush(stderr); - if (ag == 0 && ag_cors != ag_finest) + if (ag == NULL && ag_cors != ag_finest) abort(); - if (ag_cors == 0 && ag != ag_corsest) + if (ag_cors == NULL && ag != ag_corsest) abort(); - if (ag != 0 && ag->ag_cors != ag_cors) + if (ag != NULL && ag->ag_cors != ag_cors) abort(); - if (ag_cors != 0 && ag_cors->ag_fine != ag) + if (ag_cors != NULL && ag_cors->ag_fine != ag) abort(); CHECK_AG(); #endif /* Save the new route on the end of the table. */ nag = ag_avail; ag_avail = nag->ag_fine; 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; nag->ag_state = state; nag->ag_seqno = new_seqno; nag->ag_fine = ag; - if (ag != 0) + if (ag != NULL) ag->ag_cors = nag; else ag_finest = nag; nag->ag_cors = ag_cors; - if (ag_cors == 0) + if (ag_cors == NULL) ag_corsest = nag; else ag_cors->ag_fine = nag; CHECK_AG(); } static const char * rtm_type_name(u_char type) { static const char * const rtm_types[] = { "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", "RTM_NEWADDR", "RTM_DELADDR", #ifdef RTM_OIFINFO "RTM_OIFINFO", #endif "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; #define NEW_RTM_PAT "RTM type %#x" static char name0[sizeof(NEW_RTM_PAT)+2]; if (type > sizeof(rtm_types)/sizeof(rtm_types[0]) || type == 0) { snprintf(name0, sizeof(name0), NEW_RTM_PAT, type); return name0; } else { return rtm_types[type-1]; } #undef NEW_RTM_PAT } /* Trim a mask in a sockaddr * Produce a length of 0 for an address of 0. * Otherwise produce the index of the first zero byte. */ void #ifdef _HAVE_SIN_LEN masktrim(struct sockaddr_in *ap) #else masktrim(struct sockaddr_in_new *ap) #endif { char *cp; 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_len = cp - (char*)ap + 1; } /* Tell the kernel to add, delete or change a route */ static void rtioctl(int action, /* RTM_DELETE, etc */ naddr dst, naddr gate, naddr mask, int metric, int flags) { struct { struct rt_msghdr w_rtm; struct sockaddr_in w_dst; struct sockaddr_in w_gate; #ifdef _HAVE_SA_LEN struct sockaddr_in w_mask; #else struct sockaddr_in_new w_mask; #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: 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 || action == RTM_CHANGE) { w.w_rtm.rtm_rmx.rmx_hopcount = metric; w.w_rtm.rtm_inits |= RTV_HOPCOUNT; } w.w_dst.sin_family = AF_INET; w.w_dst.sin_addr.s_addr = dst; w.w_gate.sin_family = AF_INET; w.w_gate.sin_addr.s_addr = gate; #ifdef _HAVE_SA_LEN w.w_dst.sin_len = sizeof(w.w_dst); w.w_gate.sin_len = sizeof(w.w_gate); #endif if (mask == HOST_MASK) { w.w_rtm.rtm_flags |= RTF_HOST; w.w_rtm.rtm_msglen -= sizeof(w.w_mask); } else { w.w_rtm.rtm_addrs |= RTA_NETMASK; 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 } #ifndef NO_INSTALL cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); if (cc < 0) { if (errno == ESRCH && (action == RTM_CHANGE || action == RTM_DELETE)) { trace_act("route disappeared before" PAT, ARGS); if (action == RTM_CHANGE) { action = RTM_ADD; goto again; } return; } 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 } #define KHASH_SIZE 71 /* should be prime */ #define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE] static struct khash { struct khash *k_next; naddr k_dst; naddr k_mask; naddr k_gate; short k_metric; u_short k_state; #define KS_NEW 0x001 #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 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 */ } *khash_bins[KHASH_SIZE]; static struct khash* kern_find(naddr dst, naddr mask, struct khash ***ppk) { struct khash *k, **pk; - for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) { + for (pk = &KHASH(dst,mask); (k = *pk) != NULL; pk = &k->k_next) { if (k->k_dst == dst && k->k_mask == mask) break; } - if (ppk != 0) + if (ppk != NULL) *ppk = pk; return k; } static struct khash* kern_add(naddr dst, naddr mask) { struct khash *k, **pk; k = kern_find(dst, mask, &pk); - if (k != 0) + if (k != NULL) return k; k = (struct khash *)rtmalloc(sizeof(*k), "kern_add"); memset(k, 0, sizeof(*k)); k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; k->k_keep = now.tv_sec; *pk = k; return k; } /* If a kernel route has a non-zero metric, check that it is still in the * daemon table, and not deleted by interfaces coming and going. */ static void kern_check_static(struct khash *k, struct interface *ifp) { struct rt_entry *rt; struct rt_spare new; if (k->k_metric == 0) return; 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_router = (ifp != NULL) ? 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 != NULL) { if (!(rt->rt_state & RS_STATIC)) rtchange(rt, rt->rt_state | RS_STATIC, &new, 0); } else { 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 rtm_add(struct rt_msghdr *rtm, struct rt_addrinfo *info, time_t keep) { struct khash *k; struct interface *ifp; naddr mask; if (rtm->rtm_flags & RTF_HOST) { mask = HOST_MASK; } else if (INFO_MASK(info) != 0) { mask = ntohl(S_ADDR(INFO_MASK(info))); } else { msglog("ignore %s without mask", 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; 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) k->k_state |= KS_STATIC; if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { if (INFO_AUTHOR(info) != 0 && INFO_AUTHOR(info)->sa_family == AF_INET) ifp = iflookup(S_ADDR(INFO_AUTHOR(info))); else - ifp = 0; + ifp = NULL; if (supplier - && (ifp == 0 || !(ifp->int_state & IS_REDIRECT_OK))) { + && (ifp == NULL || !(ifp->int_state & IS_REDIRECT_OK))) { /* Routers are not supposed to listen to redirects, * so delete it if it came via an unknown interface * or the interface does not have special permission. */ k->k_state &= ~KS_DYNAMIC; k->k_state |= KS_DELETE; LIM_SEC(need_kern, 0); trace_act("mark for deletion redirected %s --> %s" " via %s", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate), ifp ? ifp->int_name : "unknown interface"); } else { k->k_state |= KS_DYNAMIC; k->k_redirect_time = now.tv_sec; trace_act("accept redirected %s --> %s via %s", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate), ifp ? ifp->int_name : "unknown interface"); } return; } /* If it is not a static route, quit until the next comparison * between the kernel and daemon tables, when it will be deleted. */ if (!(k->k_state & KS_STATIC)) { k->k_state |= KS_DELETE; LIM_SEC(need_kern, k->k_keep); return; } /* Put static routes with real metrics into the daemon table so * they can be advertised. * * Find the interface toward the gateway. */ ifp = iflookup(k->k_gate); - if (ifp == 0) + if (ifp == NULL) msglog("static route %s --> %s impossibly lacks ifp", addrname(S_ADDR(INFO_DST(info)), mask, 0), naddr_ntoa(k->k_gate)); kern_check_static(k, ifp); } /* deal with packet loss */ static void rtm_lose(struct rt_msghdr *rtm, struct rt_addrinfo *info) { if (INFO_GATE(info) == 0 || INFO_GATE(info)->sa_family != AF_INET) { trace_act("ignore %s without gateway", rtm_type_name(rtm->rtm_type)); return; } 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 *rsin) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)*sap; struct interface *ifp; - if (sdl == 0) + if (sdl == NULL) 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) + if (ifp == NULL) return 0; rsin->sin_addr.s_addr = ifp->int_addr; #ifdef _HAVE_SA_LEN rsin->sin_len = sizeof(*rsin); #endif rsin->sin_family = AF_INET; *sap = (struct sockaddr*)rsin; 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 *next, *lim; struct rt_msghdr *rtm; 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) { + for (k = khash_bins[i]; k != NULL; 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 */ 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)"); } 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), (struct sockaddr *)(next + rtm->rtm_msglen), rtm->rtm_addrs); if (INFO_DST(&info) == 0 || INFO_DST(&info)->sa_family != AF_INET) continue; #if defined (RTF_LLINFO) /* ignore ARP table entries on systems with a merged route * and ARP table. */ if (rtm->rtm_flags & RTF_LLINFO) continue; #endif #if defined(RTF_WASCLONED) && defined(__FreeBSD__) /* ignore cloned routes */ if (rtm->rtm_flags & RTF_WASCLONED) continue; #endif /* 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 * 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,&info,MIN_WAITTIME); } for (i = 0; i < KHASH_SIZE; i++) { - for (k = khash_bins[i]; k != 0; k = k->k_next) { + for (k = khash_bins[i]; k != NULL; 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); } } } } /* Listen to announcements from the kernel */ void read_rt(void) { long cc; struct interface *ifp; struct sockaddr_in gate_sin; naddr mask, gate; union { struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; struct if_msghdr ifm; } m; char str[100], *strp; struct rt_addrinfo info; for (;;) { cc = read(rt_sock, &m, sizeof(m)); if (cc <= 0) { if (cc < 0 && errno != EWOULDBLOCK) LOGERR("read(rt_sock)"); return; } if (m.r.rtm.rtm_version != RTM_VERSION) { msglog("bogus routing message version %d", m.r.rtm.rtm_version); continue; } /* Ignore our own results. */ if (m.r.rtm.rtm_type <= RTM_CHANGE && m.r.rtm.rtm_pid == mypid) { static int complained = 0; if (!complained) { msglog("receiving our own change messages"); complained = 1; } continue; } 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, m.r.rtm.rtm_type != RTM_DELADDR); - if (ifp == 0) + if (ifp == NULL) trace_act("note %s with flags %#x" " for unknown interface index #%d", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, m.ifm.ifm_index); else trace_act("note %s with flags %#x for %s", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, ifp->int_name); /* After being informed of a change to an interface, * check them all now if the check would otherwise * be a long time from now, if the interface is * not known, or if the interface has been turned * off or on. */ if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL - || ifp == 0 + || ifp == NULL || ((ifp->int_if_flags ^ m.ifm.ifm_flags) & IFF_UP) != 0) ifinit_timer.tv_sec = now.tv_sec; continue; } #ifdef RTM_OIFINFO if (m.r.rtm.rtm_type == RTM_OIFINFO) continue; /* ignore compat message */ #endif strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); strp = &str[strlen(str)]; if (m.r.rtm.rtm_type <= RTM_CHANGE) strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], m.r.rtm.rtm_addrs); if (INFO_DST(&info) == 0) { trace_act("ignore %s without dst", str); continue; } if (INFO_DST(&info)->sa_family != AF_INET) { trace_act("ignore %s for AF %d", str, INFO_DST(&info)->sa_family); continue; } mask = ((INFO_MASK(&info) != 0) ? ntohl(S_ADDR(INFO_MASK(&info))) : (m.r.rtm.rtm_flags & RTF_HOST) ? HOST_MASK : 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 multicast %s", str); continue; } #if defined(RTF_LLINFO) if (m.r.rtm.rtm_flags & RTF_LLINFO) { trace_act("ignore ARP %s", str); continue; } #endif #if defined(RTF_WASCLONED) && defined(__FreeBSD__) if (m.r.rtm.rtm_flags & RTF_WASCLONED) { trace_act("ignore cloned %s", str); continue; } #endif 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", saddr_ntoa(INFO_AUTHOR(&info))); switch (m.r.rtm.rtm_type) { case RTM_ADD: case RTM_CHANGE: case RTM_REDIRECT: if (m.r.rtm.rtm_errno != 0) { trace_act("ignore %s with \"%s\" error", str, strerror(m.r.rtm.rtm_errno)); } else { trace_act("%s", str); rtm_add(&m.r.rtm,&info,0); } break; case RTM_DELETE: 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, gate, 1); } break; case RTM_LOSING: trace_act("%s", str); rtm_lose(&m.r.rtm,&info); break; default: trace_act("ignore %s", str); break; } } } /* after aggregating, note routes that belong in the kernel */ static void kern_out(struct ag_info *ag) { struct khash *k; /* Do not install bad routes if they are not already present. * This includes routes that had RS_NET_SYN for interfaces that * recently died. */ if (ag->ag_metric == HOPCNT_INFINITY) { k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0); - if (k == 0) + if (k == NULL) return; } else { k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); } 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; k->k_gate = ag->ag_gate; k->k_metric = ag->ag_metric; return; } if (k->k_state & KS_STATIC) return; /* 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 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); } if ((k->k_state & KS_GATEWAY) && !(ag->ag_state & AGS_GATEWAY)) { k->k_state &= ~KS_GATEWAY; k->k_state |= (KS_ADD | KS_DEL_ADD); } else if (!(k->k_state & KS_GATEWAY) && (ag->ag_state & AGS_GATEWAY)) { k->k_state |= KS_GATEWAY; k->k_state |= (KS_ADD | KS_DEL_ADD); } /* Deleting-and-adding is necessary to change aspects of a route. * Just delete instead of deleting and then adding a bad route. * Otherwise, we want to keep the route in the kernel. */ if (k->k_metric == HOPCNT_INFINITY && (k->k_state & KS_DEL_ADD)) k->k_state |= KS_DELETE; else k->k_state &= ~KS_DELETE; #undef RT } /* ARGSUSED */ static int walk_kern(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) char metric, pref; u_int ags = 0; /* Do not install synthetic routes */ if (RT->rt_state & RS_NET_SYN) return 0; if (!(RT->rt_state & RS_IF)) { /* 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 { /* 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; /* 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." * * If it is a "remote" interface, it is also a "gateway" to * the kernel if is not an alias. */ if (RT->rt_ifp == 0 || (RT->rt_ifp->int_state & IS_REMOTE)) ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE); } /* 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; 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, metric,pref, 0, 0, ags, kern_out); return 0; #undef RT } /* Update the kernel table to match the daemon table. */ static void fix_kern(void) { int i; struct khash *k, **pk; need_kern = age_timer; /* Walk daemon table, updating the copy of the kernel table. */ (void)rn_walktree(rhead, walk_kern, 0); ag_flush(0,0,kern_out); for (i = 0; i < KHASH_SIZE; i++) { - for (pk = &khash_bins[i]; (k = *pk) != 0; ) { + for (pk = &khash_bins[i]; (k = *pk) != NULL; ) { /* 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_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) && !(k->k_state & KS_DYNAMIC)) { kern_ioctl(k, RTM_DELETE, 0); *pk = k->k_next; free(k); continue; } 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 * daemon table, since the normal aging code * will clear the bit for routes that have not * disappeared from the daemon table. */ k->k_state |= KS_DELETE; pk = &k->k_next; } } } /* Delete a static route in the image of the kernel table. */ void del_static(naddr dst, naddr mask, naddr gate, int gone) { struct khash *k; struct rt_entry *rt; /* 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 * 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. */ k = kern_find(dst, mask, 0); - if (k != 0 && (gate == 0 || k->k_gate == gate)) { + if (k != NULL && (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; k->k_keep = now.tv_sec + K_KEEP_LIM; } } rt = rtget(dst, mask); - if (rt != 0 && (rt->rt_state & RS_STATIC)) + if (rt != NULL && (rt->rt_state & RS_STATIC)) rtbad(rt); } /* Delete all routes generated from ICMP Redirects that use a given gateway, * as well as old redirected routes. */ void del_redirects(naddr bad_gate, time_t old) { int i; struct khash *k; for (i = 0; i < KHASH_SIZE; i++) { - for (k = khash_bins[i]; k != 0; k = k->k_next) { + for (k = khash_bins[i]; k != NULL; k = k->k_next) { if (!(k->k_state & KS_DYNAMIC) || (k->k_state & KS_STATIC)) continue; if (k->k_gate != bad_gate && k->k_redirect_time > old && !supplier) continue; k->k_state |= KS_DELETE; k->k_state &= ~KS_DYNAMIC; need_kern.tv_sec = now.tv_sec; trace_act("mark redirected %s --> %s for deletion", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate)); } } } /* Start the daemon tables. */ extern int max_keylen; void rtinit(void) { int i; struct ag_info *ag; /* Initialize the radix trees */ max_keylen = sizeof(struct sockaddr_in); rn_init(); rn_inithead(&rhead, 32); /* mark all of the slots in the table free */ ag_avail = ag_slots; for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) { ag->ag_fine = ag+1; ag++; } } #ifdef _HAVE_SIN_LEN static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET, 0, {0}, {0}}; static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET, 0, {0}, {0}}; #else static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET}; static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; #endif static void set_need_flash(void) { if (!need_flash) { need_flash = 1; /* Do not send the flash update immediately. Wait a little * while to hear from other routers. */ no_flash.tv_sec = now.tv_sec + MIN_WAITTIME; } } /* Get a particular routing table entry */ struct rt_entry * rtget(naddr dst, naddr mask) { struct rt_entry *rt; dst_sock.sin_addr.s_addr = dst; mask_sock.sin_addr.s_addr = htonl(mask); masktrim(&mask_sock); rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead); if (!rt || rt->rt_dst != dst || rt->rt_mask != mask) return 0; return rt; } /* Find a route to dst as the kernel would. */ struct rt_entry * rtfind(naddr dst) { dst_sock.sin_addr.s_addr = dst; return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead); } /* add a route to the table */ void rtadd(naddr dst, naddr mask, u_int state, /* rt_state for the entry */ struct rt_spare *new) { struct rt_entry *rt; naddr smask; int i; struct rt_spare *rts; rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); memset(rt, 0, sizeof(*rt)); for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) rts->rts_metric = HOPCNT_INFINITY; rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock; rt->rt_dst = dst; rt->rt_dst_sock.sin_family = AF_INET; #ifdef _HAVE_SIN_LEN rt->rt_dst_sock.sin_len = dst_sock.sin_len; #endif if (mask != HOST_MASK) { smask = std_mask(dst); if ((smask & ~mask) == 0 && mask > smask) state |= RS_SUBNET; } mask_sock.sin_addr.s_addr = htonl(mask); masktrim(&mask_sock); rt->rt_mask = mask; rt->rt_state = state; rt->rt_spares[0] = *new; rt->rt_time = now.tv_sec; rt->rt_poison_metric = HOPCNT_INFINITY; rt->rt_seqno = update_seqno; if (++total_routes == MAX_ROUTES) msglog("have maximum (%d) routes", total_routes); if (TRACEACTIONS) trace_add_del("Add", rt); need_kern.tv_sec = now.tv_sec; set_need_flash(); if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, rhead, rt->rt_nodes)) { msglog("rnh_addaddr() failed for %s mask=%#lx", naddr_ntoa(dst), (u_long)mask); free(rt); } } /* notice a changed route */ void rtchange(struct rt_entry *rt, u_int state, /* new state bits */ struct rt_spare *new, char *label) { 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 (new->rts_metric == HOPCNT_INFINITY) { need_kern.tv_sec = now.tv_sec; 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 != new->rts_gate) { need_kern.tv_sec = now.tv_sec; rt->rt_seqno = update_seqno; set_need_flash(); } state |= (rt->rt_state & RS_SUBNET); /* Keep various things from deciding ageless routes are stale. */ if (!AGE_RT(state, new->rts_ifp)) new->rts_time = now.tv_sec; if (TRACEACTIONS) trace_change(rt, state, new, label ? label : "Chg "); rt->rt_state = state; rt->rt_spares[0] = *new; } /* 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 swap; char label[10]; /* Do not change permanent routes */ if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC | RS_NET_SYN | RS_IF))) return; /* find the best alternative among the spares */ - if (rts == 0) + if (rts == NULL) rts = rts_better(rt); /* Do not bother if it is not worthwhile. */ if (!BETTER_LINK(rt, rts, rt->rt_spares)) return; swap = rt->rt_spares[0]; (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; } } void rtdelete(struct rt_entry *rt) { struct khash *k; if (TRACEACTIONS) trace_add_del("Del", rt); k = kern_find(rt->rt_dst, rt->rt_mask, 0); - if (k != 0) { + if (k != NULL) { k->k_state |= KS_DELETE; need_kern.tv_sec = now.tv_sec; } dst_sock.sin_addr.s_addr = rt->rt_dst; mask_sock.sin_addr.s_addr = htonl(rt->rt_mask); masktrim(&mask_sock); if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock, rhead)) { msglog("rnh_deladdr() failed"); } else { free(rt); total_routes--; } } 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. */ static void rtbad(struct rt_entry *rt) { 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); } /* Junk a RS_NET_SYN or RS_LOCAL route, * unless it is needed by another interface. */ void rtbad_sub(struct rt_entry *rt) { struct interface *ifp, *ifp1; struct intnet *intnetp; u_int state; - ifp1 = 0; + ifp1 = NULL; state = 0; 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, such * as a point-to-point interface with the same local address. */ LIST_FOREACH(ifp, &ifnet, int_list) { /* Retain it if another interface needs it. */ if (ifp->int_addr == rt->rt_ifp->int_addr) { state |= RS_LOCAL; ifp1 = ifp; break; } } } if (!(state & RS_LOCAL)) { /* Retain RIPv1 logical network route if there is another * interface that justifies it. */ if (rt->rt_state & RS_NET_SYN) { LIST_FOREACH(ifp, &ifnet, int_list) { 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; } } } /* or if there is an authority route that needs it. */ for (intnetp = intnets; - intnetp != 0; + intnetp != NULL; 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 || (state & RS_NET_SYN)) { + if (ifp1 != NULL || (state & RS_NET_SYN)) { 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); } } /* Called while walking the table looking for sick interfaces * or after a time change. */ /* ARGSUSED */ int walk_bad(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct rt_spare *rts; int i; /* fix any spare routes through the interface */ rts = RT->rt_spares; for (i = NUM_SPARES; i != 1; i--) { rts++; if (rts->rts_metric < HOPCNT_INFINITY - && (rts->rts_ifp == 0 + && (rts->rts_ifp == NULL || (rts->rts_ifp->int_state & IS_BROKE))) rts_delete(RT, rts); } /* Deal with the main route */ /* finished if it has been handled before or if its interface is ok */ if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE)) return 0; /* Bad routes for other than interfaces are easy. */ if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { rtbad(RT); return 0; } rtbad_sub(RT); return 0; #undef RT } /* Check the age of an individual route. */ /* ARGSUSED */ static int walk_age(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct interface *ifp; struct rt_spare *rts; int i; /* 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++) { ifp = rts->rts_ifp; if (i == NUM_SPARES) { if (!AGE_RT(RT->rt_state, ifp)) { /* Keep various things from deciding ageless * routes are stale */ rts->rts_time = now.tv_sec; continue; } /* forget RIP routes after RIP has been turned off. */ if (rip_sock < 0) { rtdelete(RT); return 0; } } /* age failing routes */ if (age_bad_gate == rts->rts_gate && rts->rts_time >= now_stale) { rts->rts_time -= SUPPLY_INTERVAL; } /* trash the spare routes when they go bad */ if (rts->rts_metric < HOPCNT_INFINITY && now_garbage > rts->rts_time && i != NUM_SPARES) rts_delete(RT, rts); } /* finished if the active route is still fresh */ if (now_stale <= RT->rt_time) return 0; /* try to switch to an alternative */ rtswitch(RT, 0); /* Delete a dead route after it has been publicly mourned. */ if (now_garbage > RT->rt_time) { rtdelete(RT); return 0; } /* Start poisoning a bad route before deleting it. */ 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; } /* Watch for dead routes and interfaces. */ void age(naddr bad_gate) { struct interface *ifp; int need_query = 0; /* If not listening to RIP, there is no need to age the routes in * the table. */ age_timer.tv_sec = (now.tv_sec + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL)); /* Check for dead IS_REMOTE interfaces by timing their * transmissions. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (!(ifp->int_state & IS_REMOTE)) continue; /* ignore unreachable remote interfaces */ if (!check_remote(ifp)) continue; /* Restore remote interface that has become reachable */ if (ifp->int_state & IS_BROKE) if_ok(ifp, "remote "); if (ifp->int_act_time != NEVER && now.tv_sec - ifp->int_act_time > EXPIRE_TIME) { msglog("remote interface %s to %s timed out after" " %ld:%ld", ifp->int_name, naddr_ntoa(ifp->int_dstaddr), (long)(now.tv_sec - ifp->int_act_time)/60, (long)(now.tv_sec - ifp->int_act_time)%60); if_sick(ifp); } /* If we have not heard from the other router * recently, ask it. */ if (now.tv_sec >= ifp->int_query_time) { ifp->int_query_time = NEVER; need_query = 1; } } /* Age routes. */ 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(); /* poke reticent remote gateways */ if (need_query) rip_query(); } Index: head/sbin/routed/trace.c =================================================================== --- head/sbin/routed/trace.c (revision 299767) +++ head/sbin/routed/trace.c (revision 299768) @@ -1,1021 +1,1021 @@ /* * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * $FreeBSD$ */ #define RIPCMDS #include "defs.h" #include "pathnames.h" #include #include #include #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif #ifdef sgi /* use *stat64 for files on large file systems */ #define stat stat64 #endif int tracelevel, new_tracelevel; FILE *ftrace; /* output trace file */ static const char *sigtrace_pat = "%s"; static char savetracename[PATH_MAX]; char inittracename[PATH_MAX]; static int file_trace; /* 1=tracing to file, not stdout */ static void trace_dump(void); static void tmsg(const char *, ...) PATTRIB(1,2); /* convert string to printable characters */ static char * qstring(u_char *s, int len) { static char buf[8*20+1]; char *p; u_char *s2, c; for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) { c = *s++; if (c == '\0') { for (s2 = s+1; s2 < &s[len]; s2++) { if (*s2 != '\0') break; } if (s2 >= &s[len]) goto exit; } if (c >= ' ' && c < 0x7f && c != '\\') { *p++ = c; continue; } *p++ = '\\'; switch (c) { case '\\': *p++ = '\\'; break; case '\n': *p++= 'n'; break; case '\r': *p++= 'r'; break; case '\t': *p++ = 't'; break; case '\b': *p++ = 'b'; break; default: p += sprintf(p,"%o",c); break; } } exit: *p = '\0'; return buf; } /* convert IP address to a string, but not into a single buffer */ char * naddr_ntoa(naddr a) { #define NUM_BUFS 4 static int bufno; static struct { char str[16]; /* xxx.xxx.xxx.xxx\0 */ } bufs[NUM_BUFS]; char *s; struct in_addr addr; addr.s_addr = a; s = strcpy(bufs[bufno].str, inet_ntoa(addr)); bufno = (bufno+1) % NUM_BUFS; return s; #undef NUM_BUFS } const char * saddr_ntoa(struct sockaddr *sa) { - return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa)); + return (sa == NULL) ? "?" : naddr_ntoa(S_ADDR(sa)); } static char * ts(time_t secs) { static char s[20]; secs += epoch.tv_sec; #ifdef sgi (void)cftime(s, "%T", &secs); #else memcpy(s, ctime(&secs)+11, 8); s[8] = '\0'; #endif return s; } /* On each event, display a time stamp. * This assumes that 'now' is update once for each event, and * that at least now.tv_usec changes. */ static struct timeval lastlog_time; void lastlog(void) { if (lastlog_time.tv_sec != now.tv_sec || lastlog_time.tv_usec != now.tv_usec) { (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec)); lastlog_time = now; } } static void tmsg(const char *p, ...) { va_list args; - if (ftrace != 0) { + if (ftrace != NULL) { lastlog(); va_start(args, p); vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n',ftrace); fflush(ftrace); } } void trace_close(int zap_stdio) { int fd; fflush(stdout); fflush(stderr); - if (ftrace != 0 && zap_stdio) { + if (ftrace != NULL && zap_stdio) { if (ftrace != stdout) fclose(ftrace); - ftrace = 0; + ftrace = NULL; fd = open(_PATH_DEVNULL, O_RDWR); if (isatty(STDIN_FILENO)) (void)dup2(fd, STDIN_FILENO); if (isatty(STDOUT_FILENO)) (void)dup2(fd, STDOUT_FILENO); if (isatty(STDERR_FILENO)) (void)dup2(fd, STDERR_FILENO); (void)close(fd); } lastlog_time.tv_sec = 0; } void trace_flush(void) { - if (ftrace != 0) { + if (ftrace != NULL) { fflush(ftrace); if (ferror(ftrace)) trace_off("tracing off: %s", strerror(ferror(ftrace))); } } void trace_off(const char *p, ...) { va_list args; - if (ftrace != 0) { + if (ftrace != NULL) { lastlog(); va_start(args, p); vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n',ftrace); } trace_close(file_trace); new_tracelevel = tracelevel = 0; } /* log a change in tracing */ void tracelevel_msg(const char *pat, int dump) /* -1=no dump, 0=default, 1=force */ { static const char * const off_msgs[MAX_TRACELEVEL] = { "Tracing actions stopped", "Tracing packets stopped", "Tracing packet contents stopped", "Tracing kernel changes stopped", }; static const char * const on_msgs[MAX_TRACELEVEL] = { "Tracing actions started", "Tracing packets started", "Tracing packet contents started", "Tracing kernel changes started", }; u_int old_tracelevel = tracelevel; if (new_tracelevel < 0) new_tracelevel = 0; else if (new_tracelevel > MAX_TRACELEVEL) new_tracelevel = MAX_TRACELEVEL; if (new_tracelevel < tracelevel) { if (new_tracelevel <= 0) { trace_off(pat, off_msgs[0]); } else do { tmsg(pat, off_msgs[tracelevel]); } while (--tracelevel != new_tracelevel); } else if (new_tracelevel > tracelevel) { do { tmsg(pat, on_msgs[tracelevel++]); } while (tracelevel != new_tracelevel); } if (dump > 0 || (dump == 0 && old_tracelevel == 0 && tracelevel != 0)) trace_dump(); } void set_tracefile(const char *filename, const char *pat, int dump) /* -1=no dump, 0=default, 1=force */ { struct stat stbuf; FILE *n_ftrace; const char *fn; /* Allow a null filename to increase the level if the trace file * is already open or if coming from a trusted source, such as * a signal or the command line. */ - if (filename == 0 || filename[0] == '\0') { - filename = 0; - if (ftrace == 0) { + if (filename == NULL || filename[0] == '\0') { + filename = NULL; + if (ftrace == NULL) { if (inittracename[0] == '\0') { msglog("missing trace file name"); return; } fn = inittracename; } else { - fn = 0; + fn = NULL; } } else if (!strcmp(filename,"dump/../table")) { trace_dump(); return; } else { /* Allow the file specified with "-T file" to be reopened, * but require all other names specified over the net to * match the official path. The path can specify a directory * in which the file is to be created. */ if (strcmp(filename, inittracename) #ifdef _PATH_TRACE && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1) || strstr(filename,"../") || 0 > stat(_PATH_TRACE, &stbuf)) #endif ) { msglog("wrong trace file \"%s\"", filename); return; } /* If the new tracefile exists, it must be a regular file. */ if (stat(filename, &stbuf) >= 0 && !S_ISREG(stbuf.st_mode)) { msglog("wrong type (%#x) of trace file \"%s\"", stbuf.st_mode, filename); return; } fn = filename; } - if (fn != 0) { + if (fn != NULL) { n_ftrace = fopen(fn, "a"); - if (n_ftrace == 0) { + if (n_ftrace == NULL) { msglog("failed to open trace file \"%s\" %s", fn, strerror(errno)); if (fn == inittracename) inittracename[0] = '\0'; return; } tmsg("switch to trace file %s", fn); trace_close(file_trace = 1); if (fn != savetracename) strncpy(savetracename, fn, sizeof(savetracename)-1); ftrace = n_ftrace; fflush(stdout); fflush(stderr); dup2(fileno(ftrace), STDOUT_FILENO); dup2(fileno(ftrace), STDERR_FILENO); } - if (new_tracelevel == 0 || filename == 0) + if (new_tracelevel == 0 || filename == NULL) new_tracelevel++; - tracelevel_msg(pat, dump != 0 ? dump : (filename != 0)); + tracelevel_msg(pat, dump != 0 ? dump : (filename != NULL)); } /* ARGSUSED */ void sigtrace_on(int s UNUSED) { new_tracelevel++; sigtrace_pat = "SIGUSR1: %s"; } /* ARGSUSED */ void sigtrace_off(int s UNUSED) { new_tracelevel--; sigtrace_pat = "SIGUSR2: %s"; } /* Set tracing after a signal. */ void set_tracelevel(void) { if (new_tracelevel == tracelevel) return; /* If tracing entirely off, and there was no tracefile specified * on the command line, then leave it off. */ - if (new_tracelevel > tracelevel && ftrace == 0) { + if (new_tracelevel > tracelevel && ftrace == NULL) { if (savetracename[0] != '\0') { set_tracefile(savetracename,sigtrace_pat,0); } else if (inittracename[0] != '\0') { set_tracefile(inittracename,sigtrace_pat,0); } else { new_tracelevel = 0; return; } } else { tracelevel_msg(sigtrace_pat, 0); } } /* display an address */ char * addrname(naddr addr, /* in network byte order */ naddr mask, int force) /* 0=show mask if nonstandard, */ { /* 1=always show mask, 2=never */ #define NUM_BUFS 4 static int bufno; static struct { char str[15+20]; } bufs[NUM_BUFS]; char *s, *sp; naddr dmask; int i; s = strcpy(bufs[bufno].str, naddr_ntoa(addr)); bufno = (bufno+1) % NUM_BUFS; if (force == 1 || (force == 0 && mask != std_mask(addr))) { sp = &s[strlen(s)]; dmask = mask & -mask; if (mask + dmask == 0) { for (i = 0; i != 32 && ((1<bits_mask) != 0) { if ((b & field) == b) { if (tbl->bits_name[0] != '\0') { if (c) (void)putc(c, ftrace); (void)fprintf(ftrace, "%s", tbl->bits_name); c = '|'; } if (0 == (field &= ~(b | tbl->bits_clear))) break; } tbl++; } - if (field != 0 && tbl->bits_name != 0) { + if (field != 0 && tbl->bits_name != NULL) { if (c) (void)putc(c, ftrace); (void)fprintf(ftrace, tbl->bits_name, field); c = '|'; } if (c != '<' || force) (void)fputs("> ", ftrace); } char * rtname(naddr dst, naddr mask, naddr gate) { static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */ +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */ int i; i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0)); (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), naddr_ntoa(gate)); return buf; } static void print_rts(struct rt_spare *rts, int force_metric, /* -1=suppress, 0=default */ int force_ifp, /* -1=suppress, 0=default */ int force_router, /* -1=suppress, 0=default, 1=display */ int force_tag, /* -1=suppress, 0=default, 1=display */ int force_time) /* 0=suppress, 1=display */ { int i; if (force_metric >= 0) (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric); if (force_ifp >= 0) - (void)fprintf(ftrace, "%s ", (rts->rts_ifp == 0 ? + (void)fprintf(ftrace, "%s ", (rts->rts_ifp == NULL ? "if?" : rts->rts_ifp->int_name)); if (force_router > 0 || (force_router == 0 && rts->rts_router != rts->rts_gate)) (void)fprintf(ftrace, "router=%s ", naddr_ntoa(rts->rts_router)); if (force_time > 0) (void)fprintf(ftrace, "%s ", ts(rts->rts_time)); if (force_tag > 0 || (force_tag == 0 && rts->rts_tag != 0)) (void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag)); if (rts->rts_de_ag != 0) { for (i = 1; (u_int)(1 << i) <= rts->rts_de_ag; i++) continue; (void)fprintf(ftrace, "de_ag=%d ", i); } } void trace_if(const char *act, struct interface *ifp) { - if (!TRACEACTIONS || ftrace == 0) + if (!TRACEACTIONS || ftrace == NULL) return; lastlog(); (void)fprintf(ftrace, "%-3s interface %-4s ", act, ifp->int_name); (void)fprintf(ftrace, "%-15s-->%-15s ", naddr_ntoa(ifp->int_addr), addrname(((ifp->int_if_flags & IFF_POINTOPOINT) ? ifp->int_dstaddr : htonl(ifp->int_net)), ifp->int_mask, 1)); if (ifp->int_metric != 0) (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); if (ifp->int_adj_inmetric != 0) (void)fprintf(ftrace, "adj_inmetric=%u ", ifp->int_adj_inmetric); if (ifp->int_adj_outmetric != 0) (void)fprintf(ftrace, "adj_outmetric=%u ", ifp->int_adj_outmetric); if (!IS_RIP_OUT_OFF(ifp->int_state) && ifp->int_d_metric != 0) (void)fprintf(ftrace, "fake_default=%u ", ifp->int_d_metric); trace_bits(if_bits, ifp->int_if_flags, 0); trace_bits(is_bits, ifp->int_state, 0); (void)fputc('\n',ftrace); } void trace_upslot(struct rt_entry *rt, struct rt_spare *rts, struct rt_spare *new) { - if (!TRACEACTIONS || ftrace == 0) + if (!TRACEACTIONS || ftrace == NULL) return; if (rts->rts_gate == new->rts_gate && rts->rts_router == new->rts_router && rts->rts_metric == new->rts_metric && rts->rts_tag == new->rts_tag && rts->rts_de_ag == new->rts_de_ag) return; lastlog(); if (new->rts_gate == 0) { (void)fprintf(ftrace, "Del #%d %-35s ", (int)(rts - rt->rt_spares), rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate)); print_rts(rts, 0,0,0,0, (rts != rt->rt_spares || AGE_RT(rt->rt_state,new->rts_ifp))); } else if (rts->rts_gate != RIP_DEFAULT) { (void)fprintf(ftrace, "Chg #%d %-35s ", (int)(rts - rt->rt_spares), rtname(rt->rt_dst, rt->rt_mask, rts->rts_gate)); print_rts(rts, 0,0, rts->rts_gate != new->rts_gate, rts->rts_tag != new->rts_tag, rts != rt->rt_spares || AGE_RT(rt->rt_state, rt->rt_ifp)); (void)fprintf(ftrace, "\n %19s%-16s ", "", (new->rts_gate != rts->rts_gate ? naddr_ntoa(new->rts_gate) : "")); print_rts(new, -(new->rts_metric == rts->rts_metric), -(new->rts_ifp == rts->rts_ifp), 0, rts->rts_tag != new->rts_tag, (new->rts_time != rts->rts_time && (rts != rt->rt_spares || AGE_RT(rt->rt_state, new->rts_ifp)))); } else { (void)fprintf(ftrace, "Add #%d %-35s ", (int)(rts - rt->rt_spares), rtname(rt->rt_dst, rt->rt_mask, new->rts_gate)); print_rts(new, 0,0,0,0, (rts != rt->rt_spares || AGE_RT(rt->rt_state,new->rts_ifp))); } (void)fputc('\n',ftrace); } /* miscellaneous message checked by the caller */ void trace_misc(const char *p, ...) { va_list args; - if (ftrace == 0) + if (ftrace == NULL) return; lastlog(); va_start(args, p); vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n',ftrace); } /* display a message if tracing actions */ void trace_act(const char *p, ...) { va_list args; - if (!TRACEACTIONS || ftrace == 0) + if (!TRACEACTIONS || ftrace == NULL) return; lastlog(); va_start(args, p); vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n',ftrace); } /* display a message if tracing packets */ void trace_pkt(const char *p, ...) { va_list args; - if (!TRACEPACKETS || ftrace == 0) + if (!TRACEPACKETS || ftrace == NULL) return; lastlog(); va_start(args, p); vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n',ftrace); } void trace_change(struct rt_entry *rt, u_int state, struct rt_spare *new, const char *label) { - if (ftrace == 0) + if (ftrace == NULL) return; if (rt->rt_metric == new->rts_metric && rt->rt_gate == new->rts_gate && rt->rt_router == new->rts_router && rt->rt_state == state && rt->rt_tag == new->rts_tag && rt->rt_de_ag == new->rts_de_ag) return; lastlog(); (void)fprintf(ftrace, "%s %-35s ", label, rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate)); print_rts(rt->rt_spares, 0,0,0,0, AGE_RT(rt->rt_state, rt->rt_ifp)); trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); (void)fprintf(ftrace, "\n%*s %19s%-16s ", (int)strlen(label), "", "", (rt->rt_gate != new->rts_gate ? naddr_ntoa(new->rts_gate) : "")); print_rts(new, -(new->rts_metric == rt->rt_metric), -(new->rts_ifp == rt->rt_ifp), 0, rt->rt_tag != new->rts_tag, (rt->rt_time != new->rts_time && AGE_RT(rt->rt_state,new->rts_ifp))); if (rt->rt_state != state) trace_bits(rs_bits, state, 1); (void)fputc('\n',ftrace); } void trace_add_del(const char * action, struct rt_entry *rt) { - if (ftrace == 0) + if (ftrace == NULL) return; lastlog(); (void)fprintf(ftrace, "%s %-35s ", action, rtname(rt->rt_dst, rt->rt_mask, rt->rt_gate)); print_rts(rt->rt_spares, 0,0,0,0,AGE_RT(rt->rt_state,rt->rt_ifp)); trace_bits(rs_bits, rt->rt_state, 0); (void)fputc('\n',ftrace); } /* ARGSUSED */ static int walk_trace(struct radix_node *rn, struct walkarg *w UNUSED) { #define RT ((struct rt_entry *)rn) struct rt_spare *rts; int i; (void)fprintf(ftrace, " %-35s ", rtname(RT->rt_dst, RT->rt_mask, RT->rt_gate)); print_rts(&RT->rt_spares[0], 0,0,0,0, AGE_RT(RT->rt_state, RT->rt_ifp)); trace_bits(rs_bits, RT->rt_state, 0); if (RT->rt_poison_time >= now_garbage && RT->rt_poison_metric < RT->rt_metric) (void)fprintf(ftrace, "pm=%d@%s", RT->rt_poison_metric, ts(RT->rt_poison_time)); rts = &RT->rt_spares[1]; for (i = 1; i < NUM_SPARES; i++, rts++) { if (rts->rts_gate != RIP_DEFAULT) { (void)fprintf(ftrace,"\n #%d%15s%-16s ", i, "", naddr_ntoa(rts->rts_gate)); print_rts(rts, 0,0,0,0,1); } } (void)fputc('\n',ftrace); return 0; } static void trace_dump(void) { struct interface *ifp; - if (ftrace == 0) + if (ftrace == NULL) return; lastlog(); (void)fputs("current daemon state:\n", ftrace); LIST_FOREACH(ifp, &ifnet, int_list) trace_if("", ifp); (void)rn_walktree(rhead, walk_trace, 0); } void trace_rip(const char *dir1, const char *dir2, struct sockaddr_in *who, struct interface *ifp, struct rip *msg, int size) /* total size of message */ { struct netinfo *n, *lim; # define NA ((struct netauth*)n) int i, seen_route; - if (!TRACEPACKETS || ftrace == 0) + if (!TRACEPACKETS || ftrace == NULL) return; lastlog(); if (msg->rip_cmd >= RIPCMD_MAX || msg->rip_vers == 0) { (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s" " %s.%d size=%d\n", dir1, msg->rip_vers, msg->rip_cmd, dir2, naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port), size); return; } (void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n", dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2, naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port), ifp ? " via " : "", ifp ? ifp->int_name : ""); if (!TRACECONTENTS) return; seen_route = 0; switch (msg->rip_cmd) { case RIPCMD_REQUEST: case RIPCMD_RESPONSE: n = msg->rip_nets; lim = (struct netinfo *)((char*)msg + size); for (; n < lim; n++) { if (!seen_route && n->n_family == RIP_AF_UNSPEC && ntohl(n->n_metric) == HOPCNT_INFINITY && msg->rip_cmd == RIPCMD_REQUEST && (n+1 == lim || (n+2 == lim && (n+1)->n_family == RIP_AF_AUTH))) { (void)fputs("\tQUERY ", ftrace); if (n->n_dst != 0) (void)fprintf(ftrace, "%s ", naddr_ntoa(n->n_dst)); if (n->n_mask != 0) (void)fprintf(ftrace, "mask=%#x ", (u_int)ntohl(n->n_mask)); if (n->n_nhop != 0) (void)fprintf(ftrace, "nhop=%s ", naddr_ntoa(n->n_nhop)); if (n->n_tag != 0) (void)fprintf(ftrace, "tag=%#x ", ntohs(n->n_tag)); (void)fputc('\n',ftrace); continue; } if (n->n_family == RIP_AF_AUTH) { if (NA->a_type == RIP_AUTH_PW && n == msg->rip_nets) { (void)fprintf(ftrace, "\tPassword" " Authentication:" " \"%s\"\n", qstring(NA->au.au_pw, RIP_AUTH_PW_LEN)); continue; } if (NA->a_type == RIP_AUTH_MD5 && n == msg->rip_nets) { (void)fprintf(ftrace, "\tMD5 Auth" " pkt_len=%d KeyID=%u" " auth_len=%d" " seqno=%#x" " rsvd=%#x,%#x\n", ntohs(NA->au.a_md5.md5_pkt_len), NA->au.a_md5.md5_keyid, NA->au.a_md5.md5_auth_len, (int)ntohl(NA->au.a_md5.md5_seqno), (int)ntohs(NA->au.a_md5.rsvd[0]), (int)ntohs(NA->au.a_md5.rsvd[1])); continue; } (void)fprintf(ftrace, "\tAuthentication type %d: ", ntohs(NA->a_type)); for (i = 0; i < (int)sizeof(NA->au.au_pw); i++) (void)fprintf(ftrace, "%02x ", NA->au.au_pw[i]); (void)fputc('\n',ftrace); continue; } seen_route = 1; if (n->n_family != RIP_AF_INET) { (void)fprintf(ftrace, "\t(af %d) %-18s mask=%#x ", ntohs(n->n_family), naddr_ntoa(n->n_dst), (u_int)ntohl(n->n_mask)); } else if (msg->rip_vers == RIPv1) { (void)fprintf(ftrace, "\t%-18s ", addrname(n->n_dst, ntohl(n->n_mask), n->n_mask==0 ? 2 : 1)); } else { (void)fprintf(ftrace, "\t%-18s ", addrname(n->n_dst, ntohl(n->n_mask), n->n_mask==0 ? 2 : 0)); } (void)fprintf(ftrace, "metric=%-2d ", (u_int)ntohl(n->n_metric)); if (n->n_nhop != 0) (void)fprintf(ftrace, " nhop=%s ", naddr_ntoa(n->n_nhop)); if (n->n_tag != 0) (void)fprintf(ftrace, "tag=%#x", ntohs(n->n_tag)); (void)fputc('\n',ftrace); } if (size != (char *)n - (char *)msg) (void)fprintf(ftrace, "truncated record, len %d\n", size); break; case RIPCMD_TRACEON: fprintf(ftrace, "\tfile=\"%.*s\"\n", size-4, msg->rip_tracefile); break; case RIPCMD_TRACEOFF: break; } }