/* * 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 acknowledgement: * 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. */ #ifndef lint static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ #ident "$Revision: 1.1 $" #include "defs.h" #include "pathnames.h" struct interface *ifnet; /* all interfaces */ 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 timeval ifinit_timer; int have_ripv1; /* have a RIPv1 interface */ /* 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; for (ifp = ifnet; ifp; ifp = ifp->int_next) { if ((ifp->int_state & IS_REMOTE) && !remote) continue; if (ifp->int_addr == addr || ((ifp->int_if_flags & IFF_BROADCAST) && ifp->int_brdaddr == addr && bcast)) { if (!(ifp->int_state & IS_BROKE)) return ifp; possible = ifp; } } return possible; } /* find the interface with a name */ struct interface * ifwithname(char *name, /* enp0 or whatever */ naddr addr) /* 0 or network address */ { struct interface *ifp; for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { if (!strcmp(ifp->int_name, name) && ((addr == 0 && !(ifp->int_state & IS_ALIAS) || ifp->int_addr == addr))) return ifp; } return 0; } struct interface * ifwithindex(u_short index) { struct interface *ifp; for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { if (ifp->int_index == index) return ifp; } return 0; } /* Find an interface from which the specified address * should have come from. Used for figuring out which * interface a packet came in on -- for tracing. */ struct interface * iflookup(naddr addr) { struct interface *ifp, *maybe; int twice; twice = 0; maybe = 0; do { for (ifp = ifnet; ifp; ifp = ifp->int_next) { /* finished with an exact match */ if (ifp->int_addr == addr) return ifp; if ((ifp->int_if_flags & IFF_BROADCAST) && ifp->int_brdaddr == addr) return ifp; if ((ifp->int_if_flags & IFF_POINTOPOINT) && ifp->int_dstaddr == 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) return maybe; /* See if an interface has come up since we checked. */ ifinit(); } while (twice++ == 0); return 0; } /* Return the classical netmask for an IP address. */ naddr std_mask(naddr 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 */ naddr ripv1_mask_net(naddr addr, /* in network byte order */ struct interface *ifp1, /* as seen on this interface */ struct interface *ifp2) /* but not this interface */ { naddr mask = 0; if (addr == 0) /* default always has 0 mask */ return mask; if (ifp1 != 0) { /* If the target is that of the associated interface on which * it arrived, then use the netmask of the interface. */ if (on_net(addr, ifp1->int_net, ifp1->int_std_mask)) mask = ifp1->int_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 (ifp1 = ifnet; ifp1 != 0; ifp1 = ifp1->int_next) { if (ifp1 != ifp2 && !(ifp1->int_if_flags & IFF_POINTOPOINT) && on_net(addr, ifp1->int_std_net, ifp1->int_std_mask) && ifp1->int_mask > mask) mask = ifp1->int_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 *ifp1, /* as seen on this interface */ struct interface *ifp2) /* but not this interface */ { naddr mask = ripv1_mask_net(addr, ifp1, ifp2); /* 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)); } /* Delete an interface. */ static void ifdel(struct interface *ifp) { struct ip_mreq m; struct interface *ifp1; if (TRACEACTIONS) trace_if("Del", ifp); if (!(ifp->int_state & IS_ALIAS)) { 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); m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) ? ifp->int_dstaddr : ifp->int_addr); if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) DBGERR(1,"setsockopt(IP_DROP_MEMBERSHIP" " RIP)"); } if (ifp->int_rip_sock >= 0) { (void)close(ifp->int_rip_sock); ifp->int_rip_sock = -1; fix_select(); } set_rdisc_mg(ifp, 0); } /* Zap all routes associated with this interface. * Assume routes just using gateways beyond this interface will * timeout naturally, and have probably already died. */ ifp->int_state |= IS_BROKE; (void)rn_walktree(rhead, walk_bad, 0); ifbad_rdisc(ifp); if (!(ifp->int_state & IS_ALIAS)) { tot_interfaces--; if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) rip_interfaces--; } /* unlink and forget the interface */ if (rip_sock_mcast == ifp) rip_sock_mcast = 0; if (ifp->int_next != 0) ifp->int_next->int_prev = ifp->int_prev; if (ifp->int_prev != 0) ifp->int_prev->int_next = ifp->int_next; else ifnet = ifp->int_next; if (!(ifp->int_state & IS_ALIAS)) { /* delete aliases of primary interface */ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { if (!strcmp(ifp->int_name, ifp1->int_name)) ifdel(ifp1); } } free(ifp); } /* Mark an interface dead. */ void ifbad(struct interface *ifp, char *pat) { if (ifp->int_state & IS_BROKE) return; if (pat) msglog(pat, ifp->int_name, naddr_ntoa(ifp->int_addr)); LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); ifp->int_state |= IS_BROKE; ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE | IS_QUIET); ifp->int_quiet_time = now.tv_sec - MaxMaxAdvertiseInterval; ifp->int_data_ts = 0; trace_if("Chg", ifp); (void)rn_walktree(rhead, walk_bad, 0); ifbad_rdisc(ifp); } /* Mark an interface alive */ int /* 1=it was dead */ ifok(struct interface *ifp, char *type) { if (!(ifp->int_state & IS_BROKE)) return 0; msglog("%sinterface %s to %s restored", type, ifp->int_name, naddr_ntoa(ifp->int_addr)); ifp->int_state &= ~IS_BROKE; ifp->int_data_ts = 0; ifok_rdisc(ifp); return 1; } struct rt_addrinfo rtinfo; /* disassemble routing message */ void rt_xaddrs(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 bzero(rtinfo.rti_info, sizeof(rtinfo.rti_info)); rtinfo.rti_addrs = addrs; for (i = 0; i < RTAX_MAX && sa < lim; i++) { if ((addrs & (1 << i)) == 0) continue; #ifdef _HAVE_SA_LEN rtinfo.rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(sa->sa_len)); #else rtinfo.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 0x01 # define COMP_WIERD 0x02 # define COMP_NOADDR 0x04 # define COMP_NODST 0x08 # define COMP_NOBADR 0x10 # define COMP_NOMASK 0x20 # define COMP_DUP 0x40 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; struct sockaddr_dl *sdl; int in, ierr, out, oerr; struct intnet *intnetp; #ifdef SIOCGIFMETRIC struct ifreq ifr; #endif ifinit_timer.tv_sec = now.tv_sec + (supplier ? CHECK_ACT_INTERVAL : CHECK_QUIET_INTERVAL); /* mark all interfaces so we can get rid of thost that disappear */ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) ifp->int_state &= ~IS_CHECKED; /* 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; if (errno != ENOMEM && errno != EFAULT) BADERR(1, "ifinit: get interface table"); free(sysctl_buf); needed = 0; } if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) BADERR(1,"ifinit: route-sysctl-estimate"); if ((sysctl_buf = malloc(sysctl_buf_size = needed)) == 0) BADERR(1,"ifinit: malloc"); } 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); if (ifam->ifam_type == RTM_IFINFO) { ifm = (struct if_msghdr *)ifam; bzero(&ifs0, 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_act_time = now.tv_sec; ifs0.int_quiet_time = (now.tv_sec - MaxMaxAdvertiseInterval); 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; continue; } if (ifam->ifam_type != RTM_NEWADDR) { DBGERR(1,"ifinit: out of sync"); continue; } rt_xaddrs((struct sockaddr *)(ifam+1), (struct sockaddr *)ifam2, ifam->ifam_addrs); if (RTINFO_IFA == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOADDR)) msglog("%s has a bad address", sdl->sdl_data); complaints |= COMP_NOADDR; } continue; } if (RTINFO_IFA->sa_family != AF_INET) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOT_INET)) trace_msg("%s: not AF_INET", sdl->sdl_data); complaints |= COMP_NOT_INET; } continue; } bcopy(&ifs0, &ifs, sizeof(ifs0)); ifs0.int_state |= IS_ALIAS; /* next will be an alias */ ifs.int_addr = S_ADDR(RTINFO_IFA); if (ifs.int_if_flags & IFF_BROADCAST) { if (RTINFO_NETMASK == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOMASK)) msglog("%s has no netmask", sdl->sdl_data); complaints |= COMP_NOMASK; } continue; } ifs.int_mask = ntohl(S_ADDR(RTINFO_NETMASK)); ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; ifs.int_std_mask = std_mask(ifs.int_addr); if (ifs.int_mask != ifs.int_std_mask) ifs.int_state |= IS_SUBNET; if (RTINFO_BRD == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOBADR)) msglog("%s has no" " broadcast address", sdl->sdl_data); complaints |= COMP_NOBADR; } continue; } ifs.int_brdaddr = S_ADDR(RTINFO_BRD); } else if (ifs.int_if_flags & IFF_POINTOPOINT) { if (RTINFO_BRD == 0 || RTINFO_BRD->sa_family != AF_INET) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NODST)) msglog("%s has a bad" " destination address", sdl->sdl_data); complaints |= COMP_NODST; } continue; } ifs.int_dstaddr = S_ADDR(RTINFO_BRD); ifs.int_net = ntohl(ifs.int_dstaddr); ifs.int_mask = HOST_MASK; ifs.int_std_mask = std_mask(ifs.int_dstaddr); } else if (ifs.int_if_flags & IFF_LOOPBACK) { ifs.int_state |= IS_PASSIVE; ifs.int_dstaddr = ifs.int_addr; ifs.int_net = ntohl(ifs.int_dstaddr); ifs.int_mask = HOST_MASK; ifs.int_std_mask = std_mask(ifs.int_dstaddr); if (!foundloopback) { foundloopback = 1; loopaddr = ifs.int_addr; } } else { if (TRACEACTIONS && !(prev_complaints & COMP_WIERD)) msglog("%s is neither broadcast" " nor point-to-point nor loopback", sdl->sdl_data); complaints |= COMP_WIERD; continue; } 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, sdl->sdl_data, sizeof(ifr.ifr_name)); if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { DBGERR(1, "ioctl(SIOCGIFMETRIC)"); ifs.int_metric = HOPCNT_INFINITY; } else { ifs.int_metric = ifr.ifr_metric+1; } #else ifs.int_metric = ifam->ifam_metric+1; #endif if (ifs.int_metric >= HOPCNT_INFINITY) ifs.int_metric = HOPCNT_INFINITY; /* 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(sdl->sdl_data, ((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_msg("interface %s has changed", ifp->int_name); ifdel(ifp); ifp = 0; } } if (ifp != 0) { /* note interfaces that have been turned off */ if (!iff_alive(ifs.int_if_flags)) { if (iff_alive(ifp->int_if_flags)) ifbad(ifp, "interface %s to %s" " turned off"); ifp->int_if_flags &= ~IFF_UP_RUNNING; continue; } /* or that were off and are now ok */ if (!iff_alive(ifp->int_if_flags)) { ifp->int_if_flags |= IFF_UP_RUNNING; (void)ifok(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; #ifdef sgi oerr = (ifs.int_data_oerrors - ifp->int_data_oerrors + ifs.int_data_odrops - ifp->int_data_odrops); #else oerr = ifs.int_data_oerrors - ifp->int_data_oerrors; #endif ifp->int_data_ipackets = ifs.int_data_ipackets; ifp->int_data_ierrors = ifs.int_data_ierrors; ifp->int_data_opackets = ifs.int_data_opackets; ifp->int_data_oerrors = ifs.int_data_oerrors; #ifdef sgi ifp->int_data_odrops = ifs.int_data_odrops; #endif /* If the interface just awoke, restart the counters. */ if (ifp->int_data_ts == 0) { ifp->int_data_ts = now.tv_sec; continue; } ifp->int_data_ts = now.tv_sec; /* Withhold judgement when the short error * counters wrap or the interface is reset. */ if (ierr < 0 || in < 0 || oerr < 0 || out < 0) continue; /* Withhold judgement when there is no traffic */ if (in == 0 && out == 0 && ierr == 0 && oerr == 0) { if (!(ifp->int_state & IS_QUIET)) { ifp->int_state |= IS_QUIET; ifp->int_quiet_time = now.tv_sec; } continue; } /* It is bad if input or output is not working */ if ((in <= ierr && ierr > 0) || (out <= oerr && oerr > 0)) { ifbad(ifp,"interface %s to %s not working"); continue; } /* otherwise, it is active and healthy */ ifp->int_act_time = now.tv_sec; ifp->int_state &= ~IS_QUIET; if (ifok(ifp, "")) addrouteforif(ifp); continue; } /* See if this new interface duplicates an existing * interface. */ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { if (ifp->int_addr == ifs.int_addr && ifp->int_mask == ifs.int_mask) break; } if (ifp != 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_DUP)) msglog("%s is duplicated by %s at %s", sdl->sdl_data, ifp->int_name, naddr_ntoa(ifp->int_addr)); complaints |= COMP_DUP; } continue; } strncpy(ifs.int_name, sdl->sdl_data, MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); get_parms(&ifs); ifok_rdisc(&ifs); /* create the interface */ ifp = (struct interface *)malloc(sizeof(*ifp)); if (ifp == 0) BADERR(1,"ifinit: out of memory"); bcopy(&ifs, ifp, sizeof(*ifp)); if (ifnet != 0) { ifp->int_next = ifnet; ifnet->int_prev = ifp; } ifnet = ifp; /* Count the # of directly connected networks. */ if (!(ifp->int_state & IS_ALIAS)) { if (!(ifp->int_if_flags & IFF_LOOPBACK)) tot_interfaces++; if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) rip_interfaces++; } /* note dead interfaces */ if (iff_alive(ifs.int_if_flags)) { set_rdisc_mg(ifp, 1); } else { LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); ifp->int_state |= IS_BROKE; } if (TRACEACTIONS) trace_if("Add", ifp); rip_on(ifp); } /* If we are multi-homed and have at least one interface * 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; del_static(myaddr, HOST_MASK, 0); rt = rtget(myaddr, HOST_MASK); if (rt != 0) { if (rt->rt_ifp != ifp || rt->rt_router != loopaddr) { rtdelete(rt); rt = 0; } else { rtchange(rt, rt->rt_state | RS_MHOME, loopaddr, loopaddr, ifp->int_metric, 0, ifp, rt->rt_time, 0); } } if (rt == 0) rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, ifp->int_metric, 0, RS_MHOME, ifp); } 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_msg("interface %s has disappeared", ifp->int_name); ifdel(ifp); continue; } if (ifp->int_state & IS_BROKE) 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_RIPV2_OUT) && !(ifp->int_if_flags & IFF_LOOPBACK)) have_ripv1 = 1; } /* add the authority interfaces */ for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); if (rt != 0 && !(rt->rt_state & RS_IF) && !(rt->rt_state & RS_NET_INT)) { rtdelete(rt); rt = 0; } if (rt == 0) rtadd(intnetp->intnet_addr, intnetp->intnet_mask, loopaddr, loopaddr, 1, 0, RS_NET_INT, 0); } 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. */ addrouteforif(ifp); /* 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); 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; } rtchange(rt,((rt->rt_state | (RS_IF|RS_LOCAL)) & ~RS_NET_S), loopaddr, loopaddr, ifp1->int_metric, 0, ifp1, rt->rt_time, 0); } else { rtadd(ifp->int_addr, HOST_MASK, loopaddr, loopaddr, ifp->int_metric, 0, (RS_IF | RS_LOCAL), ifp); } } } prev_complaints = complaints; } static void add_net_sub(struct interface *ifp, naddr dst, naddr mask, u_int state) { struct rt_entry *rt; rt = rtget(dst, mask); if (rt != 0) { if (0 != (rt->rt_state & (RS_STATIC | RS_LOCAL | RS_MHOME | RS_GW))) return; if ((rt->rt_state & state) != state || rt->rt_metric != NET_S_METRIC) { rtchange(rt, rt->rt_state | state, rt->rt_gate, rt->rt_router, NET_S_METRIC, rt->rt_tag, rt->rt_ifp, rt->rt_time, 0); } return; } rtadd(dst, mask, ifp->int_addr, ifp->int_addr, NET_S_METRIC, 0, state, ifp); } static void check_net_sub(struct interface *ifp) { struct interface *ifp2; struct rt_entry *rt; /* See if there are any RIPv1 listeners, to determine if * we need to synthesize a network route for an interface * on a subnet. */ for (ifp2 = ifnet; ifp2; ifp2 = ifp2->int_next) { if (ifp2 != ifp && !(ifp->int_state & IS_PASSIVE) && !(ifp->int_state & IS_NO_RIPV1_OUT) && !on_net(ifp->int_addr, ifp2->int_std_net, ifp2->int_std_mask)) break; } /* only if running RIPv1 somewhere */ if (ifp2 != 0) { ifp->int_state |= IS_NEED_NET_SUB; add_net_sub(ifp, ifp->int_std_addr, ifp->int_std_mask, RS_IF | RS_NET_SUB); } else { ifp->int_state &= ~IS_NEED_NET_SUB; rt = rtget(ifp->int_std_addr, ifp->int_std_mask); if (rt != 0 && 0 != (rt->rt_state & RS_NET_S) && 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. */ void addrouteforif(struct interface *ifp) { struct rt_entry *rt; naddr dst, mask; /* skip sick interfaces */ if (ifp->int_state & IS_BROKE) return; /* If the interface on a subnet, then install a RIPv1 route to * the network as well (unless it is sick). */ if (ifp->int_metric != HOPCNT_INFINITY && !(ifp->int_state & IS_PASSIVE)) { if (ifp->int_state & IS_SUBNET) { check_net_sub(ifp); } else if ((ifp->int_if_flags & IFF_POINTOPOINT) && ridhosts) { /* The (dis)appearance of other interfaces can change * the parent (sub)net. */ mask = ripv1_mask_net(ifp->int_dstaddr,0,ifp); if (mask != ifp->int_host_mask) { rt = rtget(ifp->int_host_addr, ifp->int_host_mask); ifp->int_host_addr = htonl(ntohl(ifp->int_dstaddr) & mask); ifp->int_host_mask = mask; if (rt != 0 && (rt->rt_state & RS_NET_S) && rt->rt_ifp == ifp) rtbad_sub(rt); } add_net_sub(ifp, ifp->int_host_addr, ifp->int_host_mask, RS_IF | RS_NET_HOST); } } dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) ? ifp->int_dstaddr : htonl(ifp->int_net)); /* We are finished if the right, 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); rt = rtget(dst, ifp->int_mask); if (rt != 0) { if (rt->rt_ifp != ifp || rt->rt_router != ifp->int_addr) { rtdelete(rt); rt = 0; } else { rtchange(rt, ((rt->rt_state | RS_IF) & ~(RS_NET_S | RS_LOCAL)), ifp->int_addr, ifp->int_addr, ifp->int_metric, 0, ifp, now.tv_sec, 0); } } if (rt == 0) { if (ifp->int_transitions++ > 0) trace_msg("re-install interface %s", ifp->int_name); rtadd(dst, ifp->int_mask, ifp->int_addr, ifp->int_addr, ifp->int_metric, 0, RS_IF, ifp); } }