root/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c
/*
 * Copyright 2008 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 $
 */

#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 *, size_t);
static uint_t   ihash(const void *, size_t);
static uint_t   nhash(const void *, size_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();
        }
}

/* Return physical interface with the specified name */
struct physical_interface *
phys_byname(const char *name)
{
        int nlen;
        size_t i;
        struct physical_interface *phyi;

        nlen = strcspn(name, ":");
        for (i = 0; i < ihash_tbl.htbl_size; i++) {
                for (phyi = ihash_tbl.htbl_ptrs[i]; phyi != NULL;
                    phyi = phyi->phyi_link.hl_next) {
                        if (strncmp(phyi->phyi_name, name, nlen) == 0 &&
                            phyi->phyi_name[nlen] == '\0')
                                return (phyi);
                }
        }
        return (NULL);
}

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();
        }
}

/*
 * Returns true only if given ifp has the exact address else returns
 * false and sets best if an ifp matches partially and is a better
 * match than the previous one passed via best.
 */
boolean_t
addr_on_ifp(in_addr_t addr, struct interface *ifp,
    struct interface **best)
{
        struct interface *p_best = *best;

        /*
         * Don't use a duplicate interface since it is unusable for output.
         */
        if (ifp->int_state & IS_DUP)
                return (_B_FALSE);

        if (ifp->int_if_flags & IFF_POINTOPOINT) {
                if (ifp->int_dstaddr == addr) {
                        *best = NULL;
                        return (_B_TRUE);
                }
        } else {
                if (ifp->int_addr == addr) {
                        if (IS_PASSIVE_IFP(ifp))
                                trace_misc("addr_on_ifp "
                                    "returning passive intf %s",
                                    ifp->int_name);
                        *best = NULL;
                        return (_B_TRUE);
                }

                /* Look for the longest approximate match. */
                if (on_net(addr, ifp->int_net, ifp->int_mask) &&
                    (p_best == NULL ||
                    ifp->int_mask > p_best->int_mask)) {
                        *best = ifp;
                }
        }
        return (_B_FALSE);
}

/*
 * 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) {

                        /* Exact match found */
                        if (addr_on_ifp(addr, ifp, &maybe))
                                return (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);
        }

        /* Must not allow destination to be link local address. */
        if (IN_LINKLOCAL(addr))
                return (_B_FALSE);

        if (IN_CLASSB(addr) || IN_CLASSC(addr))
                return (_B_TRUE);

        if (IN_CLASSD(addr))
                return (_B_FALSE);

        return (_B_TRUE);

}

/*
 * 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 */
    uint64_t 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_CLASSD(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;
                }

                /* Ignore interface with IPv4 link local address. */
                if (IN_LINKLOCAL(ntohl(ifs.int_addr)))
                        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_CLASSD(ntohl(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_CLASSD(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);

        /*
         * First we try to query the "link" kstats in case the link is renamed.
         * If that fails, fallback to legacy ktats for those non-GLDv3 links.
         */
        if (((ksp = kstat_lookup(kc, "link", 0, phyi->phyi_name)) == NULL) &&
            ((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));
}