diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c')
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c | 1937 |
1 files changed, 1937 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c new file mode 100644 index 0000000000..0349b2ba4c --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c @@ -0,0 +1,1937 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * 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. + * + * $FreeBSD: src/sbin/routed/if.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "defs.h" +#include "pathnames.h" +#include <sys/sockio.h> +#include <inet/ip.h> +#include <kstat.h> +#include <stropts.h> +#include <fcntl.h> +#include <stddef.h> +#include <assert.h> + +/* linked list of all interfaces */ +struct interface *ifnet; + +/* + * Acceptable sizes (in number of interfaces) for the interface hash + * tables. These must all be prime. The interface hash tables all + * start with a size of hash_table_sizes[0], and increase as needed. + */ +size_t hash_table_sizes[] = { 67, 131, 257, 521, 1031, 2053, 4099, 0 }; + +struct htbl { + void **htbl_ptrs; + uint_t (*htbl_hash)(const void *, size_t); + size_t htbl_link_off; /* offset of the linkage structure */ + size_t htbl_key_off; /* offset of the key value (rehash) */ + size_t htbl_size; /* size of the hash */ + uint_t htbl_size_index; + uint_t htbl_ifcount; /* count of entries */ + boolean_t htbl_grow; /* growth allowed */ +}; + +/* Get first element -- for iteration */ +#define HFIRST(htbl, arg) \ + ((htbl)->htbl_ptrs[(htbl)->htbl_hash((arg), 0) % (htbl)->htbl_size]) + +/* Add an element to a hash */ +#define HADD(htbl, strp) \ + hash_link((htbl), (htbl)->htbl_hash((strp), (htbl)->htbl_key_off), \ + (strp)) + +uint_t tot_interfaces; /* # of remote and local interfaces */ +uint_t rip_interfaces; /* # of interfaces doing RIP */ +uint_t ripout_interfaces; /* # of interfaces advertising RIP */ +uint_t fwd_interfaces; /* # of interfaces ip_forwarding=1 */ +static boolean_t foundloopback; /* valid flag for loopaddr */ +in_addr_t loopaddr; /* our address on loopback */ +static struct rt_spare loop_rts; + +struct timeval ifscan_timer; +static struct timeval last_ifscan; +#define IF_RESCAN_DELAY() \ + (last_ifscan.tv_sec == now.tv_sec && \ + last_ifscan.tv_usec == now.tv_usec && \ + timercmp(&ifscan_timer, &now, > /* */)) + +boolean_t have_ripv1_out; /* have a RIPv1 interface */ +static boolean_t have_ripv1_in; + +static void if_bad(struct interface *, boolean_t); +static boolean_t addrouteforif(struct interface *); +static int get_if_kstats(struct interface *, struct phyi_data *); +static uint_t ahash(const void *, uint_t); +static uint_t ihash(const void *, uint_t); +static uint_t nhash(const void *, uint_t); +static void htbl_grow(struct htbl *); + +/* + * Table of all interfaces, hashed by interface address. For remote + * interfaces, the gateway address is used. + */ +static struct htbl ahash_tbl = { + NULL, ahash, offsetof(struct interface, int_ahash), + offsetof(struct interface, int_addr), + 0, 0, 0, _B_TRUE }; +/* + * Table of broadcast capable interfaces, hashed by interface broadcast + * address. + */ +static struct htbl bhash_tbl = { + NULL, ahash, offsetof(struct interface, int_bhash), + offsetof(struct interface, int_brdaddr), + 0, 0, 0, _B_TRUE }; +/* + * Table of physical_interface structures (lists of interfaces by ifIndex), + * hashed by interface index. + */ +static struct htbl ihash_tbl = { + NULL, ihash, offsetof(struct physical_interface, phyi_link), + offsetof(struct physical_interface, phyi_index), + 0, 0, 0, _B_TRUE }; +/* + * Table of all interfaces, hashed by interface name. + */ +static struct htbl nhash_tbl = { + NULL, nhash, offsetof(struct interface, int_nhash), + offsetof(struct interface, int_name), + 0, 0, 0, _B_TRUE }; + +static struct physical_interface dummy_phyi; +struct interface dummy_ifp; + +/* Hash based on an IP address. */ +static uint_t +ahash(const void *arg, size_t voffs) +{ + /* LINTED */ + return ((uint_t)*(const in_addr_t *)((const char *)arg + voffs)); +} + +static uint_t +ihash(const void *arg, size_t voffs) +{ + /* LINTED */ + return ((uint_t)*(const uint32_t *)((const char *)arg + voffs)); +} + +static uint_t +nhash(const void *arg, size_t voffs) +{ + const char *cp = (const char *)arg + voffs; + uint_t i; + + for (i = 0; *cp != '\0'; cp++) { + i = ((i<<1) & 0x7fffffff) | ((i>>30) & 0x00000003); + i ^= *cp; + } + return (i); +} + +/* + * Add an element to the head of the list. + */ +static void +link_in(void **head, void *strp, size_t loffs) +{ + struct hlinkage *hlp; + + /* LINTED: alignment known to be good. */ + hlp = (struct hlinkage *)((char *)strp + loffs); + hlp->hl_prev = head; + if ((hlp->hl_next = *head) != NULL) { + /* LINTED */ + ((struct hlinkage *)((char *)*head + loffs))->hl_prev = + &hlp->hl_next; + } + *head = strp; +} + +/* Remove from a list */ +static void +link_out(void *strp, size_t loffs) +{ + struct hlinkage *hlp; + + /* LINTED: alignment known to be good. */ + hlp = (struct hlinkage *)((char *)strp + loffs); + if ((*hlp->hl_prev = hlp->hl_next) != NULL) { + /* LINTED */ + ((struct hlinkage *)((char *)hlp->hl_next + loffs))->hl_prev = + hlp->hl_prev; + } +} + +/* Add to a hash */ +static void +hash_link(struct htbl *htbl, uint_t hval, void *strp) +{ + void **hep; + + if (htbl->htbl_grow && htbl->htbl_ifcount >= htbl->htbl_size * 5) + htbl_grow(htbl); + + hep = &htbl->htbl_ptrs[hval % htbl->htbl_size]; + link_in(hep, strp, htbl->htbl_link_off); + htbl->htbl_ifcount++; +} + +/* Remove from a hash */ +static void +hash_unlink(struct htbl *htbl, void *strp) +{ + link_out(strp, htbl->htbl_link_off); + htbl->htbl_ifcount--; +} + +static void +dummy_ifp_init(void) +{ + dummy_phyi.phyi_interface = &dummy_ifp; + dummy_ifp.int_phys = &dummy_phyi; + (void) strcpy(dummy_phyi.phyi_name, "wildcard"); + (void) strcpy(dummy_ifp.int_name, "wildcard"); + dummy_ifp.int_dstaddr = dummy_ifp.int_addr = INADDR_NONE; + dummy_ifp.int_mask = IP_HOST_MASK; + dummy_ifp.int_metric = HOPCNT_INFINITY; + dummy_ifp.int_state = (IS_BROKE|IS_PASSIVE|IS_NO_RIP|IS_NO_RDISC); + dummy_ifp.int_std_mask = std_mask(dummy_ifp.int_addr); + dummy_ifp.int_std_net = dummy_ifp.int_net & dummy_ifp.int_std_mask; + dummy_ifp.int_std_addr = htonl(dummy_ifp.int_std_net); +} + +/* allocate the interface hash tables */ +void +iftbl_alloc(void) +{ + size_t initial_size = hash_table_sizes[0]; + + errno = 0; + ahash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *)); + bhash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *)); + ihash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *)); + nhash_tbl.htbl_ptrs = calloc(initial_size, sizeof (void *)); + + if (errno != 0) + BADERR(_B_FALSE, "Unable to allocate interface tables"); + + ahash_tbl.htbl_size = initial_size; + bhash_tbl.htbl_size = initial_size; + ihash_tbl.htbl_size = initial_size; + nhash_tbl.htbl_size = initial_size; + + dummy_ifp_init(); +} + + +static void +htbl_grow(struct htbl *htbl) +{ + void *strp; + void **new_ptrs, **saved_old_ptrs, **old_ptrs; + size_t new_size, old_size; + static uint_t failed_count; + + if ((new_size = hash_table_sizes[htbl->htbl_size_index + 1]) == 0) + return; + + if ((new_ptrs = calloc(new_size, sizeof (void *))) == NULL) { + /* + * This is not fatal since we already have a + * functional, yet crowded, interface table. + */ + if (++failed_count % 100 == 1) + msglog("%sunable to grow interface hash table: %s", + failed_count > 1 ? "Still " : "", + rip_strerror(errno)); + return; + } + + failed_count = 0; + + saved_old_ptrs = old_ptrs = htbl->htbl_ptrs; + old_size = htbl->htbl_size; + htbl->htbl_ptrs = new_ptrs; + htbl->htbl_size = new_size; + htbl->htbl_size_index++; + htbl->htbl_ifcount = 0; + + /* + * Go through the list of structures, and re-link each into + * this new table. + */ + htbl->htbl_grow = _B_FALSE; + while (old_size-- > 0) { + strp = *old_ptrs++; + HADD(htbl, strp); + } + + htbl->htbl_grow = _B_TRUE; + free(saved_old_ptrs); +} + +/* Link a new interface into the lists and hash tables. */ +void +if_link(struct interface *ifp, uint32_t ifindex) +{ + struct physical_interface *phyi; + + link_in((void **)&ifnet, ifp, offsetof(struct interface, int_link)); + + HADD(&ahash_tbl, ifp); + HADD(&nhash_tbl, ifp); + + if (ifp->int_if_flags & IFF_BROADCAST) + HADD(&bhash_tbl, ifp); + + if (ifindex != 0) { + for (phyi = HFIRST(&ihash_tbl, &ifindex); + phyi != NULL; phyi = phyi->phyi_link.hl_next) { + if (phyi->phyi_index == ifindex) + break; + } + if (phyi == NULL) { + size_t size; + + phyi = rtmalloc(sizeof (*phyi), "physical_interface"); + (void) memset(phyi, 0, sizeof (*phyi)); + phyi->phyi_index = ifindex; + /* LINTED */ + assert(IF_NAME_LEN >= IF_NAMESIZE); + + size = strcspn(ifp->int_name, ":"); + (void) strncpy(phyi->phyi_name, ifp->int_name, + size); + phyi->phyi_name[size] = '\0'; + HADD(&ihash_tbl, phyi); + } + link_in((void **)&phyi->phyi_interface, ifp, + offsetof(struct interface, int_ilist)); + ifp->int_phys = phyi; + } +} + +/* Find the interface with an address */ +struct interface * +ifwithaddr(in_addr_t addr, + boolean_t bcast, /* notice IFF_BROADCAST address */ + boolean_t remote) /* include IS_REMOTE interfaces */ +{ + struct interface *ifp, *possible = NULL; + uint32_t remote_state; + + remote_state = (!remote ? IS_REMOTE : 0); + + for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL; + ifp = ifp->int_ahash.hl_next) { + if (ifp->int_addr != addr) + continue; + if (ifp->int_state & remote_state) + continue; + if (!(ifp->int_state & (IS_BROKE | IS_PASSIVE))) + return (ifp); + possible = ifp; + } + + if (possible != NULL || !bcast) + return (possible); + + for (ifp = HFIRST(&bhash_tbl, &addr); ifp != NULL; + ifp = ifp->int_bhash.hl_next) { + if (ifp->int_brdaddr != addr) + continue; + if (ifp->int_state & remote_state) + continue; + if (!(ifp->int_state & (IS_BROKE | IS_PASSIVE))) + return (ifp); + possible = ifp; + } + + return (possible); +} + + +/* find the interface with the specified name ("hme0" for example) */ +struct interface * +ifwithname(const char *name) +{ + struct interface *ifp; + + for (;;) { + for (ifp = HFIRST(&nhash_tbl, name); ifp != NULL; + ifp = ifp->int_nhash.hl_next) { + if (strcmp(ifp->int_name, name) == 0) + 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 (NULL); + ifscan(); + } +} + +struct interface * +findremoteif(in_addr_t addr) +{ + struct interface *ifp; + + for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL; + ifp = ifp->int_ahash.hl_next) { + if ((ifp->int_state & IS_REMOTE) && ifp->int_addr == addr) + return (ifp); + } + + return (NULL); +} + +struct interface * +findifaddr(in_addr_t addr) +{ + struct interface *ifp; + + for (ifp = HFIRST(&ahash_tbl, &addr); ifp != NULL; + ifp = ifp->int_ahash.hl_next) { + if (ifp->int_addr == addr) + return (ifp); + } + + return (NULL); +} + +/* + * Return the first interface with the given index. + */ +struct interface * +ifwithindex(ulong_t index, + boolean_t rescan_ok) +{ + struct physical_interface *phyi; + + for (;;) { + for (phyi = HFIRST(&ihash_tbl, &index); phyi != NULL; + phyi = phyi->phyi_link.hl_next) { + if (phyi->phyi_index == index) + return (phyi->phyi_interface); + } + + /* + * 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 (NULL); + rescan_ok = _B_FALSE; + ifscan(); + } +} + + +/* + * Find an interface which should be receiving packets sent from the + * given address. Used as a last ditch effort for figuring out which + * interface a packet came in on. Also used for finding out which + * interface points towards the gateway of static routes learned from + * the kernel. + */ +struct interface * +iflookup(in_addr_t addr) +{ + struct interface *ifp, *maybe; + + maybe = NULL; + for (;;) { + for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { + /* + * Don't return a duplicate interface since + * it is unusable for output. + */ + if (ifp->int_state & IS_DUP) + continue; + + 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) { + if (IS_PASSIVE_IFP(ifp)) + trace_misc("iflookup " + "returning passive intf %s", + ifp->int_name); + return (ifp); + } + + /* Look for the longest approximate match. */ + if (on_net(addr, ifp->int_net, ifp->int_mask) && + (maybe == NULL || + ifp->int_mask > maybe->int_mask)) + maybe = ifp; + } + } + + /* + * If there is no known interface, maybe there is a + * new interface. So just once look for new interfaces. + */ + if (maybe == NULL && !IF_RESCAN_DELAY()) + ifscan(); + else + break; + } + + if (maybe != NULL && IS_PASSIVE_IFP(maybe)) { + trace_misc("iflookup returning passive intf %s", + maybe->int_name); + } + return (maybe); +} + +/* + * 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. + */ +in_addr_t +ripv1_mask_net(in_addr_t addr, /* in network byte order */ + const struct interface *ifp) /* as seen on this interface */ +{ + const struct r1net *r1p; + in_addr_t mask = 0; + + if (addr == 0) /* default always has 0 mask */ + return (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. + */ + for (ifp = ifnet; ifp != NULL; 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; + } + + } + + if (mask == 0) { + /* + * Check to see if the user has supplied an applicable + * netmask as a ripv1_mask option in /etc/gateways. + */ + for (r1p = r1nets; r1p != NULL; r1p = r1p->r1net_next) { + /* + * If the address is is on a matching network + * and we haven't already found a longer match, + * use the matching netmask. + */ + 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); +} + + +in_addr_t +ripv1_mask_host(in_addr_t addr, /* in network byte order */ + const struct interface *ifp) /* as seen on this interface */ +{ + in_addr_t mask = ripv1_mask_net(addr, ifp); + + + /* + * If the computed netmask does not mask all of the set bits + * in 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 */ +boolean_t /* _B_FALSE=bad _B_TRUE=good */ +check_dst(in_addr_t addr) +{ + addr = ntohl(addr); + + if (IN_CLASSA(addr)) { + if (addr == 0) + return (_B_TRUE); /* default */ + + addr >>= IN_CLASSA_NSHIFT; + return (addr != 0 && addr != IN_LOOPBACKNET); + } + + return (IN_CLASSB(addr) || IN_CLASSC(addr)); +} + +/* + * Find an existing interface which has the given parameters, but don't + * return the interface with name "name" if "name" is specified. + */ +struct interface * +check_dup(const char *name, /* Don't return this interface */ + in_addr_t addr, /* IP address, so network byte order */ + in_addr_t dstaddr, /* ditto */ + in_addr_t mask, /* mask, so host byte order */ + int if_flags, /* set IFF_POINTOPOINT to ignore local int_addr */ + boolean_t allowdups) /* set true to include duplicates */ +{ + struct interface *best_ifp = NULL; + struct interface *ifp; + in_addr_t dstaddr_h = ntohl(dstaddr); + int best_pref = 0; + int pref; + + for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { + /* This interface, not a duplicate. */ + if (name != NULL && strcmp(name, ifp->int_name) == 0) + continue; + + /* + * Find an interface which isn't already a duplicate to + * avoid cyclical duplication. (i.e. qfe0:1 is a duplicate + * of qfe0, and qfe0 is a duplicate of qfe0:1. That would + * be bad) + */ + if (!allowdups && (ifp->int_state & IS_DUP)) + continue; + + if (ifp->int_mask != mask) + continue; + + if (!IS_IFF_UP(ifp->int_if_flags)) + continue; + + /* + * The local address can only be shared with a point-to-point + * link. + */ + if ((ifp->int_addr == addr && + ((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0) || + on_net(ifp->int_dstaddr, dstaddr_h, mask)) { + pref = 0; + if (!(ifp->int_state & IS_ALIAS)) + pref++; + if (!IS_RIP_OUT_OFF(ifp->int_state)) + pref += 2; + if (IS_IFF_ROUTING(ifp->int_if_flags)) + pref += 4; + if (pref > best_pref) { + best_pref = pref; + best_ifp = ifp; + } + } + } + return (best_ifp); +} + + +/* + * See that a remote gateway is reachable. + * Note that the answer can change as real interfaces come and go. + */ +boolean_t /* _B_FALSE=bad _B_TRUE=good */ +check_remote(struct interface *ifp) +{ + struct rt_entry *rt; + + /* do not worry about other kinds */ + if (!(ifp->int_state & IS_REMOTE)) + return (_B_TRUE); + + rt = rtfind(ifp->int_addr); + if (rt != NULL && + rt->rt_ifp != NULL && + on_net(ifp->int_addr, + rt->rt_ifp->int_net, rt->rt_ifp->int_mask)) + return (_B_TRUE); + + /* + * 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, _B_FALSE); + } + return (_B_FALSE); +} + +/* Delete an interface. */ +static void +ifdel(struct interface *ifp) +{ + struct rewire_data wire; + boolean_t resurrected; + struct physical_interface *phyi; + + trace_if("Del", ifp); + + ifp->int_state |= IS_BROKE; + + /* unlink the interface */ + link_out(ifp, offsetof(struct interface, int_link)); + hash_unlink(&ahash_tbl, ifp); + hash_unlink(&nhash_tbl, ifp); + if (ifp->int_if_flags & IFF_BROADCAST) + hash_unlink(&bhash_tbl, ifp); + + /* Remove from list of interfaces with this ifIndex */ + if ((phyi = ifp->int_phys) != NULL) { + link_out(ifp, offsetof(struct interface, int_ilist)); + if (phyi->phyi_interface == NULL) { + hash_unlink(&ihash_tbl, phyi); + free(phyi); + } + } + + /* + * If this is a lead interface, then check first for + * duplicates of this interface with an eye towards promoting + * one of them. + */ + resurrected = _B_FALSE; + if (!(ifp->int_state & IS_DUP) && + (wire.if_new = check_dup(ifp->int_name, ifp->int_addr, + ifp->int_dstaddr, ifp->int_mask, ifp->int_if_flags, + _B_TRUE)) != NULL && + !IS_IFF_QUIET(wire.if_new->int_if_flags)) { + + trace_act("promoting duplicate %s in place of %s", + wire.if_new->int_name, ifp->int_name); + + /* Rewire routes with the replacement interface */ + wire.if_old = ifp; + wire.metric_delta = wire.if_new->int_metric - ifp->int_metric; + (void) rn_walktree(rhead, walk_rewire, &wire); + kern_rewire_ifp(wire.if_old, wire.if_new); + if_rewire_rdisc(wire.if_old, wire.if_new); + + /* Mark the replacement as being no longer a duplicate */ + wire.if_new->int_state &= ~IS_DUP; + tot_interfaces++; + if (!IS_RIP_OFF(wire.if_new->int_state)) + rip_interfaces++; + if (!IS_RIP_OUT_OFF(wire.if_new->int_state)) + ripout_interfaces++; + if (IS_IFF_ROUTING(wire.if_new->int_if_flags)) + fwd_interfaces++; + + set_rdisc_mg(wire.if_new, 1); + rip_mcast_on(wire.if_new); + + /* We came out ok; no need to clobber routes over this. */ + resurrected = _B_TRUE; + } + + rip_mcast_off(ifp); + if (rip_sock_interface == ifp) + rip_sock_interface = NULL; + + set_rdisc_mg(ifp, 0); + + /* + * Note that duplicates are not counted in the total number of + * interfaces. + */ + if (!(ifp->int_state & IS_DUP) && !IS_IFF_QUIET(ifp->int_if_flags)) { + tot_interfaces--; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces--; + if (!IS_RIP_OUT_OFF(ifp->int_state)) + ripout_interfaces--; + if (IS_IFF_ROUTING(ifp->int_if_flags)) + fwd_interfaces--; + } + + if (!resurrected) { + /* + * 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, ifp); + kern_flush_ifp(ifp); + + if_bad_rdisc(ifp); + } + + free(ifp); +} + + +/* Mark an interface ill. */ +void +if_sick(struct interface *ifp, boolean_t recurse) +{ + struct interface *ifp1; + + 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(ifscan_timer, now.tv_sec+CHECK_BAD_INTERVAL); + if (recurse && ifp->int_phys != NULL) { + /* If an interface is sick, so are its aliases. */ + for (ifp1 = ifp->int_phys->phyi_interface; + ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) { + if (ifp1 != ifp) + if_sick(ifp1, _B_FALSE); + } + } + } +} + + +/* Mark an interface dead. */ +static void +if_bad(struct interface *ifp, boolean_t recurse) +{ + struct interface *ifp1; + struct rewire_data wire; + + if (ifp->int_state & IS_BROKE) + return; + + LIM_SEC(ifscan_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + ifp->int_state |= (IS_BROKE | IS_SICK); + ifp->int_act_time = NEVER; + ifp->int_query_time = NEVER; + /* Note: don't reset the stats timestamp here */ + + trace_if("Chg", ifp); + + if (recurse && ifp->int_phys != NULL) { + /* If an interface is bad, so are its aliases. */ + for (ifp1 = ifp->int_phys->phyi_interface; + ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) { + if (ifp1 != ifp) + if_bad(ifp1, _B_FALSE); + } + } + + /* If we can find a replacement, then pick it up. */ + if (!(ifp->int_state & IS_DUP) && + (wire.if_new = check_dup(ifp->int_name, ifp->int_addr, + ifp->int_dstaddr, ifp->int_mask, ifp->int_if_flags, + _B_TRUE)) != NULL && + !IS_IFF_QUIET(wire.if_new->int_if_flags)) { + trace_act("promoting duplicate %s in place of %s", + wire.if_new->int_name, ifp->int_name); + wire.if_old = ifp; + wire.metric_delta = wire.if_new->int_metric - ifp->int_metric; + (void) rn_walktree(rhead, walk_rewire, &wire); + if_rewire_rdisc(wire.if_old, wire.if_new); + + /* The broken guy becomes the duplicate */ + wire.if_new->int_state &= ~IS_DUP; + set_rdisc_mg(ifp, 0); + rip_mcast_off(ifp); + ifp->int_state |= IS_DUP; + + /* join the mcast groups for the replacement */ + set_rdisc_mg(wire.if_new, 1); + rip_mcast_on(wire.if_new); + + if (rip_sock_interface == ifp) + rip_sock_interface = NULL; + } else { + (void) rn_walktree(rhead, walk_bad, ifp); + if_bad_rdisc(ifp); + } +} + + +/* Mark an interface alive */ +void +if_ok(struct interface *ifp, const char *type, boolean_t recurse) +{ + struct interface *ifp1; + boolean_t wasbroken = _B_FALSE; + + if (ifp->int_state & IS_BROKE) { + writelog(LOG_WARNING, "%sinterface %s to %s restored", + type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); + ifp->int_state &= ~(IS_BROKE | IS_SICK); + wasbroken = _B_TRUE; + } else 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; + } + + if (recurse && ifp->int_phys != NULL && IS_IFF_UP(ifp->int_if_flags)) { + ifp->int_phys->phyi_data.ts = 0; + + /* Also mark all aliases of this interface as ok */ + for (ifp1 = ifp->int_phys->phyi_interface; + ifp1 != NULL; ifp1 = ifp1->int_ilist.hl_next) { + if (ifp1 != ifp) + if_ok(ifp1, type, _B_FALSE); + } + } + + if (wasbroken) { + if (!(ifp->int_state & IS_DUP)) + if_ok_rdisc(ifp); + + if (ifp->int_state & IS_REMOTE) + (void) addrouteforif(ifp); + } +} + +boolean_t +remote_address_ok(struct interface *ifp, in_addr_t addr) +{ + if (ifp->int_if_flags & IFF_POINTOPOINT) { + if (addr == ifp->int_dstaddr) + return (_B_TRUE); + } else if (on_net(addr, ifp->int_net, ifp->int_mask)) { + return (_B_TRUE); + } + return (_B_FALSE); +} + +/* + * 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 +ifscan(void) +{ + uint_t complaints = 0; + static uint_t prev_complaints = 0; +#define COMP_BADADDR 0x001 +#define COMP_NODST 0x002 +#define COMP_NOBADDR 0x004 +#define COMP_NOMASK 0x008 +#define COMP_BAD_METRIC 0x010 +#define COMP_NETMASK 0x020 +#define COMP_NO_INDEX 0x040 +#define COMP_BAD_FLAGS 0x080 +#define COMP_NO_KSTATS 0x100 +#define COMP_IPFORWARD 0x200 + + struct interface ifs, *ifp, *ifp1; + struct rt_entry *rt; + size_t needed; + static size_t lastneeded = 0; + char *buf; + static char *lastbuf = NULL; + int32_t in, ierr, out, oerr; + struct intnet *intnetp; + int sock; + struct lifnum lifn; + struct lifconf lifc; + struct lifreq *lifrp, *lifrp_lim; + struct sockaddr_in *sinp; + in_addr_t haddr; + static in_addr_t myaddr = 0; + uint32_t ifindex; + struct phyi_data newstats; + struct physical_interface *phyi; + + last_ifscan = now; + ifscan_timer.tv_sec = now.tv_sec + + (supplier || tot_interfaces != 1 ? + CHECK_ACT_INTERVAL : CHECK_QUIET_INTERVAL); + + /* mark all interfaces so we can get rid of those that disappear */ + for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) + ifp->int_state &= ~IS_CHECKED; + + /* Fetch the size of the current interface list */ + if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1) + BADERR(_B_TRUE, "ifscan: socket(SOCK_DGRAM)"); + lifn.lifn_family = AF_INET; /* Only count IPv4 interfaces */ + /* + * Include IFF_NOXMIT interfaces. Such interfaces are exluded + * from protocol operations, but their inclusion in the + * internal table enables us to know when packets arrive on + * such interfaces. + */ + lifn.lifn_flags = LIFC_NOXMIT; +calculate_lifc_len: + if (ioctl(sock, SIOCGLIFNUM, &lifn) == -1) { + BADERR(_B_TRUE, "ifscan: ioctl(SIOCGLIFNUM)"); + } + + /* + * When calculating the buffer size needed, add a small number + * of interfaces to those we counted. We do this to capture + * the interface status of potential interfaces which may have + * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. + * Try to reuse the buffer we already have to avoid heap + * thrash. + */ + needed = (lifn.lifn_count + 4) * sizeof (struct lifreq); + if (needed > lastneeded || needed < lastneeded/2) { + if (lastbuf != NULL) + free(lastbuf); + if ((buf = malloc(needed)) == NULL) { + lastbuf = NULL; + msglog("ifscan: malloc: %s", rip_strerror(errno)); + return; + } + } else { + buf = lastbuf; + } + lastbuf = buf; + lastneeded = needed; + + /* Get the list */ + lifc.lifc_family = AF_INET; /* We only need IPv4 interfaces */ + lifc.lifc_flags = LIFC_NOXMIT; + lifc.lifc_len = needed; + lifc.lifc_buf = buf; + if (ioctl(sock, SIOCGLIFCONF, &lifc) == -1) { + /* + * IP returns EINVAL if the lifc_len we passed in is + * too small. If that's the case, we need to go back + * and recalculate it. + */ + if (errno == EINVAL) + goto calculate_lifc_len; + BADERR(_B_TRUE, "ifscan: ioctl(SIOCGLIFCONF)"); + } + + /* + * If the returned lifc_len is within one lifreq of the + * requested ammount, we may have used a buffer which + * was too small to hold all of the interfaces. In that + * case go back and recalculate needed. + */ + if (lifc.lifc_len >= needed - sizeof (struct lifreq)) + goto calculate_lifc_len; + + lifrp = lifc.lifc_req; + lifrp_lim = lifrp + lifc.lifc_len / sizeof (*lifrp); + for (; lifrp < lifrp_lim; lifrp++) { + + (void) memset(&ifs, 0, sizeof (ifs)); + + (void) strlcpy(ifs.int_name, lifrp->lifr_name, + sizeof (ifs.int_name)); + + /* SIOCGLIFCONF fills in the lifr_addr of each lifreq */ + ifs.int_addr = ((struct sockaddr_in *)&lifrp->lifr_addr)-> + sin_addr.s_addr; + + if (ioctl(sock, SIOCGLIFFLAGS, lifrp) == -1) { + if (!(prev_complaints & COMP_BAD_FLAGS)) + writelog(LOG_NOTICE, + "unable to get interface flags for %s: %s", + ifs.int_name, rip_strerror(errno)); + complaints |= COMP_BAD_FLAGS; + ifs.int_if_flags = 0; + } else { + ifs.int_if_flags = lifrp->lifr_flags; + } + + if (IN_EXPERIMENTAL(ntohl(ifs.int_addr)) || + (ntohl(ifs.int_addr) & IN_CLASSA_NET) == 0) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_BADADDR)) + writelog(LOG_NOTICE, + "%s has a bad address %s", + ifs.int_name, + naddr_ntoa(ifs.int_addr)); + complaints |= COMP_BADADDR; + } + continue; + } + + /* Get the interface index. */ + if (ioctl(sock, SIOCGLIFINDEX, lifrp) == -1) { + ifindex = 0; + ifs.int_if_flags &= ~IFF_UP; + if (!(prev_complaints & COMP_NO_INDEX)) + writelog(LOG_NOTICE, "%s has no ifIndex: %s", + ifs.int_name, rip_strerror(errno)); + complaints |= COMP_NO_INDEX; + } else { + ifindex = lifrp->lifr_index; + } + + /* + * Get the destination address for point-to-point + * interfaces. + */ + if (ifs.int_if_flags & IFF_POINTOPOINT) { + sinp = (struct sockaddr_in *)&lifrp->lifr_dstaddr; + if (ioctl(sock, SIOCGLIFDSTADDR, lifrp) == -1) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + writelog(LOG_NOTICE, + "%s has no destination " + "address : %s", + ifs.int_name, + rip_strerror(errno)); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_net = ntohl(sinp->sin_addr.s_addr); + if (IN_EXPERIMENTAL(ifs.int_net) || + (ifs.int_net != 0 && + (ifs.int_net & IN_CLASSA_NET) == 0)) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + writelog(LOG_NOTICE, + "%s has a bad " + "destination address %s", + ifs.int_name, + naddr_ntoa(ifs.int_net)); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_dstaddr = sinp->sin_addr.s_addr; + } + + /* Get the subnet mask */ + sinp = (struct sockaddr_in *)&lifrp->lifr_addr; + if (ioctl(sock, SIOCGLIFNETMASK, lifrp) == -1) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + writelog(LOG_NOTICE, + "%s has no netmask: %s", + ifs.int_name, rip_strerror(errno)); + complaints |= COMP_NOMASK; + } + continue; + } + if (sinp->sin_addr.s_addr == INADDR_ANY) { + if (!(ifs.int_if_flags & + (IFF_POINTOPOINT|IFF_LOOPBACK))) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + writelog(LOG_NOTICE, + "%s has all-zero netmask", + ifs.int_name); + complaints |= COMP_NOMASK; + } + continue; + } + ifs.int_mask = IP_HOST_MASK; + } else { + ifs.int_mask = ntohl(sinp->sin_addr.s_addr); + } + + /* + * Get the broadcast address on broadcast capable + * interfaces. + */ + if (ifs.int_if_flags & IFF_BROADCAST) { + if (ioctl(sock, SIOCGLIFBRDADDR, lifrp) == -1) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOBADDR)) + writelog(LOG_NOTICE, + "%s has no broadcast " + "address: %s", + ifs.int_name, + rip_strerror(errno)); + complaints |= COMP_NOBADDR; + } + continue; + } + haddr = ntohl(sinp->sin_addr.s_addr); + if (IN_EXPERIMENTAL(haddr) || + (haddr & IN_CLASSA_NET) == 0) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOBADDR)) + writelog(LOG_NOTICE, + "%s has a bad broadcast " + "address %s", + ifs.int_name, + naddr_ntoa(haddr)); + complaints |= COMP_NOBADDR; + } + continue; + } + } + ifs.int_brdaddr = sinp->sin_addr.s_addr; + + /* Get interface metric, if possible. */ + if (ioctl(sock, SIOCGLIFMETRIC, lifrp) == -1) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_BAD_METRIC)) + writelog(LOG_NOTICE, + "%s has no metric: %s", + ifs.int_name, rip_strerror(errno)); + complaints |= COMP_BAD_METRIC; + } + } else { + ifs.int_metric = lifrp->lifr_metric; + if (ifs.int_metric > HOPCNT_INFINITY) { + if (IS_IFF_UP(ifs.int_if_flags)) { + if (!(prev_complaints & + COMP_BAD_METRIC)) + writelog(LOG_NOTICE, + "%s has a metric of %d, " + "defaulting to %d", + ifs.int_name, + ifs.int_metric, + HOPCNT_INFINITY); + complaints |= COMP_BAD_METRIC; + } + ifs.int_metric = HOPCNT_INFINITY; + } + } + + ifs.int_state |= IS_CHECKED; + ifs.int_query_time = NEVER; + + /* + * If this is an alias, then mark it appropriately. + * Do not output RIP or Router-Discovery packets via + * aliases. + */ + if (strchr(ifs.int_name, ':') != NULL) + ifs.int_state |= IS_ALIAS; + + 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 = _B_TRUE; + loopaddr = ifs.int_addr; + loop_rts.rts_gate = loopaddr; + loop_rts.rts_router = loopaddr; + } + + } else if (ifs.int_if_flags & IFF_POINTOPOINT) { + ifs.int_ripv1_mask = ifs.int_mask; + ifs.int_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + + } else { + ifs.int_dstaddr = ifs.int_addr; + 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; + } + ifs.int_std_net = ifs.int_net & ifs.int_std_mask; + ifs.int_std_addr = htonl(ifs.int_std_net); + + /* + * If this interface duplicates another, mark it + * appropriately so that we don't generate duplicate + * packets. + */ + ifp = check_dup(ifs.int_name, ifs.int_addr, ifs.int_dstaddr, + ifs.int_mask, ifs.int_if_flags, _B_FALSE); + if (ifp != NULL) { + trace_misc("%s (%s%s%s) is a duplicate of %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) : "")); + ifs.int_state |= IS_DUP; + } else { + ifs.int_state &= ~IS_DUP; + } + + /* + * 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, or if its forwarding + * status has changed. + */ + ifp = ifwithname(ifs.int_name); + if (ifp != NULL) { + ifp->int_state |= IS_CHECKED; + ifp->int_state = (ifp->int_state & ~IS_DUP) | + (ifs.int_state & IS_DUP); + + if ((ifp->int_phys == NULL && ifindex != 0) || + (ifp->int_phys != NULL && + ifp->int_phys->phyi_index != ifindex) || + 0 != ((ifp->int_if_flags ^ ifs.int_if_flags) + & (IFF_BROADCAST | IFF_LOOPBACK | + IFF_POINTOPOINT | IFF_MULTICAST | + IFF_ROUTER | IFF_NORTEXCH | IFF_NOXMIT)) || + 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 = NULL; + } + } + + if (ifp != NULL) { + /* note interfaces that have been turned off */ + if (!IS_IFF_UP(ifs.int_if_flags)) { + if (IS_IFF_UP(ifp->int_if_flags)) { + writelog(LOG_WARNING, + "interface %s to %s turned off", + ifp->int_name, + naddr_ntoa(ifp->int_dstaddr)); + if_bad(ifp, _B_FALSE); + ifp->int_if_flags &= ~IFF_UP; + } else if (ifp->int_phys != NULL && + now.tv_sec > (ifp->int_phys->phyi_data.ts + + CHECK_BAD_INTERVAL)) { + trace_act("interface %s has been off" + " %ld seconds; forget it", + ifp->int_name, + now.tv_sec - + ifp->int_phys->phyi_data.ts); + ifdel(ifp); + } + continue; + } + /* or that were off and are now ok */ + if (!IS_IFF_UP(ifp->int_if_flags)) { + ifp->int_if_flags |= IFF_UP; + if_ok(ifp, "", _B_FALSE); + } + + /* + * If it has been long enough, + * see if the interface is broken. + */ + if ((phyi = ifp->int_phys) == NULL || + now.tv_sec < phyi->phyi_data.ts + + CHECK_BAD_INTERVAL) + continue; + + (void) memset(&newstats, 0, sizeof (newstats)); + if (get_if_kstats(ifp, &newstats) == -1) { + if (!(prev_complaints & COMP_NO_KSTATS)) + writelog(LOG_WARNING, + "unable to obtain kstats for %s", + phyi->phyi_name); + complaints |= COMP_NO_KSTATS; + } + + /* + * If the interface just awoke, restart the counters. + */ + if (phyi->phyi_data.ts == 0) { + phyi->phyi_data = newstats; + continue; + } + + in = newstats.ipackets - phyi->phyi_data.ipackets; + ierr = newstats.ierrors - phyi->phyi_data.ierrors; + out = newstats.opackets - phyi->phyi_data.opackets; + oerr = newstats.oerrors - phyi->phyi_data.oerrors; + phyi->phyi_data = newstats; + + /* + * Withhold judgment when the short error counters + * wrap, the interface is reset, or if there are + * no kstats. + */ + if (ierr < 0 || in < 0 || oerr < 0 || out < 0 || + newstats.ts == 0) { + LIM_SEC(ifscan_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 at least 25% of input or output on + * an interface results in errors. Require + * presistent problems before marking it dead. + */ + if ((ierr > 0 && ierr >= in/4) || + (oerr > 0 && oerr >= out/4)) { + 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, _B_TRUE); + continue; + } + if (!(ifp->int_state & IS_BROKE)) { + writelog(LOG_WARNING, + "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, _B_TRUE); + } + continue; + } + + /* otherwise, it is active and healthy */ + ifp->int_act_time = now.tv_sec; + if_ok(ifp, "", _B_TRUE); + continue; + } + + /* + * This is a new interface. + * If it is dead, forget it. + */ + if (!IS_IFF_UP(ifs.int_if_flags)) + continue; + + if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | + IFF_BROADCAST | IFF_LOOPBACK)) && + !(ifs.int_state & IS_PASSIVE)) { + if (!(prev_complaints & COMP_BAD_FLAGS)) + trace_act("%s is neither broadcast, " + "point-to-point, nor loopback", + ifs.int_name); + complaints |= COMP_BAD_FLAGS; + if (!(ifs.int_if_flags & IFF_MULTICAST)) + ifs.int_state |= IS_NO_RDISC; + } + + + /* + * It is new and ok. Add it to the list of interfaces + */ + ifp = rtmalloc(sizeof (*ifp), "ifscan ifp"); + (void) memcpy(ifp, &ifs, sizeof (*ifp)); + get_parms(ifp); + if_link(ifp, ifindex); + trace_if("Add", ifp); + + if (ifp->int_phys != NULL && + get_if_kstats(ifp, &ifp->int_phys->phyi_data) == -1) { + if (!(prev_complaints & COMP_NO_KSTATS)) + writelog(LOG_NOTICE, + "unable to obtain kstats for %s", + ifp->int_phys->phyi_name); + complaints |= COMP_NO_KSTATS; + } + + /* Detect interfaces that have conflicting netmasks. */ + if (!(ifp->int_if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK))) { + for (ifp1 = ifnet; ifp1 != NULL; + ifp1 = ifp1->int_next) { + if (ifp1->int_mask == ifp->int_mask) + continue; + + /* + * we don't care about point-to-point + * or loopback aliases + */ + if (ifp1->int_if_flags & + (IFF_POINTOPOINT|IFF_LOOPBACK)) { + continue; + } + + /* ignore aliases on the same network */ + if (ifp->int_phys == ifp1->int_phys) + continue; + + if (on_net(ifp->int_addr, + ifp1->int_net, ifp1->int_mask) || + on_net(ifp1->int_addr, + ifp->int_net, ifp->int_mask)) { + writelog(LOG_INFO, + "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_DUP) && + !IS_IFF_QUIET(ifp->int_if_flags)) { + /* Count the # of directly connected networks. */ + tot_interfaces++; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces++; + if (!IS_RIP_OUT_OFF(ifp->int_state)) + ripout_interfaces++; + if (IS_IFF_ROUTING(ifp->int_if_flags)) + fwd_interfaces++; + + if_ok_rdisc(ifp); + rip_on(ifp); + } + } + + (void) close(sock); + + /* + * If we are multi-homed and have at least two interfaces that + * are able to forward, then output RIP by default. + */ + if (!supplier_set) + set_supplier(); + + /* + * If we are multi-homed, optionally advertise a route to + * our main address. + */ + if (advertise_mhome || (tot_interfaces > 1 && mhome)) { + /* lookup myaddr if we haven't done so already */ + if (myaddr == 0) { + char myname[MAXHOSTNAMELEN+1]; + + /* + * If we are unable to resolve our hostname, don't + * bother trying again. + */ + if (gethostname(myname, MAXHOSTNAMELEN) == -1) { + msglog("gethostname: %s", rip_strerror(errno)); + advertise_mhome = _B_FALSE; + mhome = _B_FALSE; + } else if (gethost(myname, &myaddr) == 0) { + writelog(LOG_WARNING, + "unable to resolve local hostname %s", + myname); + advertise_mhome = _B_FALSE; + mhome = _B_FALSE; + } + } + if (myaddr != 0 && + (ifp = ifwithaddr(myaddr, _B_FALSE, _B_FALSE)) != NULL && + foundloopback) { + advertise_mhome = _B_TRUE; + rt = rtget(myaddr, HOST_MASK); + if (rt != NULL) { + if (rt->rt_ifp != ifp || + rt->rt_router != loopaddr) { + rtdelete(rt); + rt = NULL; + } else { + loop_rts.rts_ifp = ifp; + loop_rts.rts_metric = 0; + loop_rts.rts_time = rt->rt_time; + loop_rts.rts_origin = RO_LOOPBCK; + rtchange(rt, rt->rt_state | RS_MHOME, + &loop_rts, NULL); + } + } + if (rt == NULL) { + loop_rts.rts_ifp = ifp; + loop_rts.rts_metric = 0; + loop_rts.rts_origin = RO_LOOPBCK; + rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts); + } + } + } + + for (ifp = ifnet; ifp != NULL; 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(ifscan_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 | IS_DUP)) && + should_supply(ifp)) + have_ripv1_out = _B_TRUE; + if (!(ifp->int_state & IS_NO_RIPV1_IN)) + have_ripv1_in = _B_TRUE; + } + + for (ifp = ifnet; ifp != NULL; 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) == 0) + 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, ifp, 0); + rt = rtget(ifp->int_addr, HOST_MASK); + if (rt != NULL && rt->rt_router != loopaddr) { + rtdelete(rt); + rt = NULL; + } + 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; + loop_rts.rts_origin = RO_LOOPBCK; + 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; + loop_rts.rts_origin = RO_LOOPBCK; + rtadd(ifp->int_addr, HOST_MASK, + (RS_IF | RS_LOCAL), &loop_rts); + } + } + } + + /* add the authority routes */ + for (intnetp = intnets; intnetp != NULL; + intnetp = intnetp->intnet_next) { + rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); + if (rt != NULL && + !(rt->rt_state & RS_NO_NET_SYN) && + !(rt->rt_state & RS_NET_INT)) { + rtdelete(rt); + rt = NULL; + } + if (rt == NULL) { + loop_rts.rts_ifp = NULL; + loop_rts.rts_metric = intnetp->intnet_metric-1; + loop_rts.rts_origin = RO_LOOPBCK; + 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; + 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 != NULL && + 0 == (rt->rt_state & RS_NO_NET_SYN) && + (!(rt->rt_state & RS_NET_SYN) || + rt->rt_metric > ifp->int_metric)) { + rtdelete(rt); + rt = NULL; + } + if (rt == NULL) { + (void) memset(&new, 0, sizeof (new)); + new.rts_ifp = ifp; + new.rts_gate = ifp->int_addr; + new.rts_router = ifp->int_addr; + new.rts_metric = ifp->int_metric; + new.rts_origin = RO_NET_SYN; + 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 != NULL && + (rt->rt_state & RS_NET_SYN) && + rt->rt_ifp == ifp) + rtbad_sub(rt, NULL); + } +} + + +/* + * 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 boolean_t /* _B_FALSE=bad interface */ +addrouteforif(struct interface *ifp) +{ + struct rt_entry *rt; + struct rt_spare new; + in_addr_t dst; + uint16_t rt_newstate = RS_IF; + + + /* skip sick interfaces */ + if (ifp->int_state & IS_BROKE) + return (_B_FALSE); + + /* + * don't install routes for duplicate interfaces, or + * unnumbered point-to-point interfaces. + */ + if ((ifp->int_state & IS_DUP) || + ((ifp->int_if_flags & IFF_POINTOPOINT) && ifp->int_dstaddr == 0)) + return (_B_TRUE); + + /* + * 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)); + + (void) memset(&new, 0, sizeof (new)); + 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 (ifp->int_if_flags & IFF_POINTOPOINT) + new.rts_origin = RO_PTOPT; + else if (ifp->int_if_flags & IFF_LOOPBACK) + new.rts_origin = RO_LOOPBCK; + else + new.rts_origin = RO_IF; + + /* + * 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 (_B_FALSE); + + /* + * 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, ifp, 0); + rt = rtget(dst, ifp->int_mask); + if (!IS_IFF_ROUTING(ifp->int_if_flags)) + rt_newstate |= RS_NOPROPAGATE; + if (rt != NULL) { + if ((rt->rt_ifp != ifp || rt->rt_router != ifp->int_addr) && + (rt->rt_ifp == NULL || + (rt->rt_ifp->int_state & IS_BROKE))) { + rtdelete(rt); + rt = NULL; + } else { + rtchange(rt, ((rt->rt_state | rt_newstate) & + ~(RS_NET_SYN | RS_LOCAL)), &new, 0); + } + } + if (rt == NULL) { + if (ifp->int_transitions++ > 0) + trace_act("re-installing interface %s;" + " went up %d times", + ifp->int_name, ifp->int_transitions); + + rtadd(dst, ifp->int_mask, rt_newstate, &new); + } + + return (_B_TRUE); +} + +/* + * Obtains the named kstat, and places its value in *value. It + * returns 0 for success, -1 for failure. + */ +static int +kstat_named_value(kstat_t *ksp, char *name, uint32_t *value) +{ + kstat_named_t *knp; + + if (ksp == NULL) + return (-1); + + if ((knp = kstat_data_lookup(ksp, name)) == NULL) { + return (-1); + } else if (knp->data_type != KSTAT_DATA_UINT32) { + return (-1); + } else { + *value = knp->value.ui32; + return (0); + } +} + +static int +get_if_kstats(struct interface *ifp, struct phyi_data *newdata) +{ + struct physical_interface *phyi = ifp->int_phys; + kstat_ctl_t *kc; + kstat_t *ksp; + + /* We did this recently; don't do it again. */ + if (phyi->phyi_data.ts == now.tv_sec) { + if (newdata != &phyi->phyi_data) + *newdata = phyi->phyi_data; + return (0); + } + + if ((kc = kstat_open()) == NULL) + return (-1); + + if ((ksp = kstat_lookup(kc, NULL, -1, phyi->phyi_name)) == NULL) { + (void) kstat_close(kc); + return (-1); + } + + if (kstat_read(kc, ksp, NULL) == -1) { + (void) kstat_close(kc); + return (-1); + } + + if ((kstat_named_value(ksp, "ipackets", &newdata->ipackets) == -1) || + (kstat_named_value(ksp, "opackets", &newdata->opackets) == -1)) { + newdata->ts = 0; + (void) kstat_close(kc); + return (-1); + } + + /* The loopback interface does not keep track of errors */ + if (!(ifp->int_if_flags & IFF_LOOPBACK)) { + if ((kstat_named_value(ksp, "ierrors", + &newdata->ierrors) == -1) || + (kstat_named_value(ksp, "oerrors", + &newdata->oerrors) == -1)) { + newdata->ts = 0; + (void) kstat_close(kc); + return (-1); + } + } + + newdata->ts = now.tv_sec; + (void) kstat_close(kc); + return (0); +} + +/* + * Returns true if we should supply routes to other systems. If the + * user has forced us to be a supplier (by the command line) or if we + * have more than one forwarding interface and this is one of the + * forwarding interfaces, then behave as a RIP supplier (supply rdisc + * advertisements and RIP responses). + */ +boolean_t +should_supply(struct interface *ifp) +{ + if (ifp != NULL && !IS_IFF_ROUTING(ifp->int_if_flags)) + return (_B_FALSE); + return ((supplier_set && supplier) || + (!supplier_set && fwd_interfaces > 1)); +} |