/* $NetBSD: if.c,v 1.22 2001/03/10 23:52:45 christos Exp $ */ /* * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgment: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * 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. */ #include "defs.h" #include "pathnames.h" #ifdef __NetBSD__ __RCSID("$NetBSD: if.c,v 1.22 2001/03/10 23:52:45 christos Exp $"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("Revision: 2.24 "); #ident "Revision: 2.24 " #endif struct interface *ifnet; /* all 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] struct interface *ahash_tbl[AHASH_LEN]; #define BHASH_LEN 211 /* must be prime */ #define BHASH(a) &bhash_tbl[(a)%BHASH_LEN] struct interface *bhash_tbl[BHASH_LEN]; struct interface *remote_if; /* remote interfaces */ /* 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 struct interface *nhash_tbl[NHASH_LEN]; int tot_interfaces; /* # of remote and local interfaces */ int rip_interfaces; /* # of interfaces doing RIP */ int foundloopback; /* valid flag for loopaddr */ naddr loopaddr; /* our address on loopback */ 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 */ int have_ripv1_in; 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; ifp->int_prev = &ifnet; ifp->int_next = ifnet; if (ifnet != 0) ifnet->int_prev = &ifp->int_next; ifnet = ifp; hifp = AHASH(ifp->int_addr); ifp->int_ahash_prev = hifp; if ((ifp->int_ahash = *hifp) != 0) (*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) (*hifp)->int_bhash_prev = &ifp->int_bhash; *hifp = ifp; } if (ifp->int_state & IS_REMOTE) { ifp->int_rlink_prev = &remote_if; ifp->int_rlink = remote_if; if (remote_if != 0) remote_if->int_rlink_prev = &ifp->int_rlink; remote_if = ifp; } hifp = nhash(ifp->int_name); if (ifp->int_state & IS_ALIAS) { /* put aliases on the end of the hash chain */ while (*hifp != 0) hifp = &(*hifp)->int_nhash; } ifp->int_nhash_prev = hifp; if ((ifp->int_nhash = *hifp) != 0) (*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; 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 */ 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) { /* 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 index, int rescan_ok) { struct interface *ifp; for (;;) { for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { if (ifp->int_index == index) 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; for (;;) { for (ifp = ifnet; ifp; ifp = ifp->int_next) { 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 || ifp->int_mask > maybe->int_mask)) maybe = ifp; } } if (maybe != 0 || 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 */ { 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 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. */ for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { 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) { 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 a IP address looks reasonable as a destination */ int /* 0=bad */ check_dst(naddr 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; for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { 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 && 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 ip_mreq m; struct interface *ifp1; trace_if("Del", ifp); ifp->int_state |= IS_BROKE; /* unlink the interface */ *ifp->int_prev = ifp->int_next; if (ifp->int_next != 0) ifp->int_next->int_prev = ifp->int_prev; *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) { *ifp->int_rlink_prev = ifp->int_rlink; if (ifp->int_rlink != 0) ifp->int_rlink->int_rlink_prev = ifp->int_rlink_prev; } if (!(ifp->int_state & IS_ALIAS)) { /* delete aliases when the main interface dies */ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { if (ifp1 != ifp && !strcmp(ifp->int_name, ifp1->int_name)) ifdel(ifp1); } if ((ifp->int_if_flags & IFF_MULTICAST) #ifdef MCAST_PPP_BUG && !(ifp->int_if_flags & IFF_POINTOPOINT) #endif && rip_sock >= 0) { m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); #ifdef MCAST_IFINDEX m.imr_interface.s_addr = htonl(ifp->int_index); #else m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) ? ifp->int_dstaddr : ifp->int_addr); #endif if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0 && errno != EADDRNOTAVAIL && !TRACEACTIONS) LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)"); if (rip_sock_mcast == ifp) rip_sock_mcast = 0; } 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. */ 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)) { for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { 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)) { for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { 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 #ifdef sgi #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ : sizeof(__uint64_t)) #else #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ : sizeof(long)) #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; #ifdef _HAVE_SA_LEN info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(sa->sa_len)); #else info->rti_info[i] = sa; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(_FAKE_SA_LEN_DST(sa))); #endif } } /* 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 char *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; struct ifa_msghdr *ifam, *ifam_lim, *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 */ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) 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"); } ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); for (ifam = (struct ifa_msghdr *)sysctl_buf; 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_PASSIVE | IS_NO_RIP | IS_NO_RDISC; 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) { 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; } } if (ifp != 0) { /* 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" " %ld seconds; forget it", ifp->int_name, 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) { /* 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)) && !(ifs.int_state & IS_PASSIVE)) { 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) { for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { 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 && foundloopback)) { advertise_mhome = 1; rt = rtget(myaddr, HOST_MASK); if (rt != 0) { if (rt->rt_ifp != ifp || rt->rt_router != loopaddr) { rtdelete(rt); rt = 0; } 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) { loop_rts.rts_ifp = ifp; loop_rts.rts_metric = 0; rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts); } } for (ifp = ifnet; ifp != 0; ifp = ifp1) { ifp1 = ifp->int_next; /* because we may delete it */ /* 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; } for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { /* 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) { rtdelete(rt); rt = 0; } if (rt != 0) { 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) { rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); if (rt != 0 && !(rt->rt_state & RS_NO_NET_SYN) && !(rt->rt_state & RS_NET_INT)) { rtdelete(rt); rt = 0; } if (rt == 0) { loop_rts.rts_ifp = 0; 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 && 0 == (rt->rt_state & RS_NO_NET_SYN) && (!(rt->rt_state & RS_NET_SYN) || rt->rt_metric > ifp->int_metric)) { rtdelete(rt); rt = 0; } if (rt == 0) { 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 && (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. */ 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->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; } else { rtchange(rt, ((rt->rt_state | RS_IF) & ~(RS_NET_SYN | RS_LOCAL)), &new, 0); } } if (rt == 0) { if (ifp->int_transitions++ > 0) trace_act("re-install interface %s", ifp->int_name); rtadd(dst, ifp->int_mask, RS_IF, &new); } return 1; }