if.c revision 2a9459bdd821c1cf59590a7a9069ac9c591e8a6b
/*
* Copyright 2007 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
* 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.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include "pathnames.h"
#include <kstat.h>
#include <stropts.h>
#include <fcntl.h>
#include <stddef.h>
#include <assert.h>
/* linked list of all interfaces */
/*
* 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.
*/
struct htbl {
void **htbl_ptrs;
};
/* Get first element -- for iteration */
/* Add an element to a hash */
(strp))
struct timeval ifscan_timer;
static struct timeval last_ifscan;
#define IF_RESCAN_DELAY() \
static boolean_t have_ripv1_in;
/*
* Table of all interfaces, hashed by interface address. For remote
* interfaces, the gateway address is used.
*/
0, 0, 0, _B_TRUE };
/*
* Table of broadcast capable interfaces, hashed by interface broadcast
* address.
*/
0, 0, 0, _B_TRUE };
/*
* Table of physical_interface structures (lists of interfaces by ifIndex),
* hashed by interface index.
*/
0, 0, 0, _B_TRUE };
/*
* Table of all interfaces, hashed by interface name.
*/
0, 0, 0, _B_TRUE };
static struct physical_interface dummy_phyi;
/* Hash based on an IP address. */
static uint_t
{
/* LINTED */
}
static uint_t
{
/* LINTED */
}
static uint_t
{
uint_t i;
i = ((i<<1) & 0x7fffffff) | ((i>>30) & 0x00000003);
i ^= *cp;
}
return (i);
}
/*
* Add an element to the head of the list.
*/
static void
{
/* LINTED: alignment known to be good. */
/* LINTED */
}
}
/* Remove from a list */
static void
{
/* LINTED: alignment known to be good. */
/* LINTED */
}
}
/* Add to a hash */
static void
{
void **hep;
htbl->htbl_ifcount++;
}
/* Remove from a hash */
static void
{
htbl->htbl_ifcount--;
}
static void
dummy_ifp_init(void)
{
}
/* allocate the interface hash tables */
void
iftbl_alloc(void)
{
errno = 0;
if (errno != 0)
}
static void
{
void *strp;
static uint_t failed_count;
return;
/*
* This is not fatal since we already have a
* functional, yet crowded, interface table.
*/
msglog("%sunable to grow interface hash table: %s",
return;
}
failed_count = 0;
htbl->htbl_size_index++;
htbl->htbl_ifcount = 0;
/*
* Go through the list of structures, and re-link each into
* this new table.
*/
while (old_size-- > 0) {
}
}
/* Link a new interface into the lists and hash tables. */
void
{
struct physical_interface *phyi;
if (ifindex != 0) {
break;
}
/* LINTED */
size);
}
}
}
/* Find the interface with an address */
struct interface *
{
continue;
continue;
return (ifp);
}
return (possible);
continue;
continue;
return (ifp);
}
return (possible);
}
/* find the interface with the specified name ("hme0" for example) */
struct interface *
ifwithname(const char *name)
{
for (;;) {
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 *
{
return (ifp);
}
return (NULL);
}
struct interface *
{
return (ifp);
}
return (NULL);
}
/*
* Return the first interface with the given index.
*/
struct interface *
{
struct physical_interface *phyi;
for (;;) {
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);
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 *
{
for (;;) {
/*
* Don't return a duplicate interface since
* it is unusable for output.
*/
continue;
/* finished with a match */
return (ifp);
} else {
/* finished with an exact match */
if (IS_PASSIVE_IFP(ifp))
trace_misc("iflookup "
"returning passive intf %s",
return (ifp);
}
/* Look for the longest approximate match. */
}
}
}
/*
* If there is no known interface, maybe there is a
* new interface. So just once look for new interfaces.
*/
ifscan();
else
break;
}
trace_misc("iflookup returning passive intf %s",
}
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.
*/
{
if (addr == 0) /* default always has 0 mask */
return (mask);
/*
* If the target network is that of the associated interface
* on which it arrived, then use the netmask of the interface.
*/
} 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.
*/
}
}
if (mask == 0) {
/*
* Check to see if the user has supplied an applicable
*/
/*
* If the address is is on a matching network
* and we haven't already found a longer match,
* use the matching netmask.
*/
}
/* Otherwise, make the classic A/B/C guess. */
if (mask == 0)
}
return (mask);
}
{
/*
* If the computed netmask does not mask all of the set bits
* in the address, then assume it is a host address
*/
return (mask);
}
/* See if a IP address looks reasonable as a destination */
boolean_t /* _B_FALSE=bad _B_TRUE=good */
{
if (addr == 0)
return (_B_TRUE); /* default */
addr >>= IN_CLASSA_NSHIFT;
}
/* Must not allow destination to be link local address. */
if (IN_LINKLOCAL(addr))
return (_B_FALSE);
return (_B_TRUE);
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 *
{
int best_pref = 0;
int pref;
/* This interface, not a duplicate. */
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)
*/
continue;
continue;
continue;
/*
* The local address can only be shared with a point-to-point
* link.
*/
pref = 0;
pref++;
pref += 2;
pref += 4;
}
}
}
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 */
{
/* do not worry about other kinds */
return (_B_TRUE);
return (_B_TRUE);
}
/*
* the gateway cannot be reached directly from one of our
* interfaces
*/
}
return (_B_FALSE);
}
/* Delete an interface. */
static void
{
struct rewire_data wire;
struct physical_interface *phyi;
/* unlink the interface */
/* Remove from list of interfaces with this ifIndex */
}
}
/*
* If this is a lead interface, then check first for
* duplicates of this interface with an eye towards promoting
* one of them.
*/
trace_act("promoting duplicate %s in place of %s",
/* Rewire routes with the replacement interface */
/* Mark the replacement as being no longer a duplicate */
/* We came out ok; no need to clobber routes over this. */
}
if (rip_sock_interface == ifp)
set_rdisc_mg(ifp, 0);
/*
* Note that duplicates are not counted in the total number of
* 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.
*/
}
}
/* Mark an interface ill. */
void
{
/* If an interface is sick, so are its aliases. */
}
}
}
}
/* Mark an interface dead. */
static void
{
struct rewire_data wire;
return;
/* Note: don't reset the stats timestamp here */
/* If an interface is bad, so are its aliases. */
}
}
/* If we can find a replacement, then pick it up. */
trace_act("promoting duplicate %s in place of %s",
/* The broken guy becomes the duplicate */
set_rdisc_mg(ifp, 0);
/* join the mcast groups for the replacement */
if (rip_sock_interface == ifp)
} else {
}
}
/* Mark an interface alive */
void
{
trace_act("%sinterface %s to %s working better",
}
/* Also mark all aliases of this interface as ok */
}
}
if (wasbroken) {
(void) addrouteforif(ifp);
}
}
{
return (_B_TRUE);
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
static size_t lastneeded = 0;
char *buf;
int sock;
struct sockaddr_in *sinp;
struct physical_interface *phyi;
last_ifscan = now;
/* mark all interfaces so we can get rid of those that disappear */
/* Fetch the size of the current interface list */
/*
* 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.
*/
}
/*
* 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.
*/
return;
}
} else {
}
lastneeded = needed;
/* Get the list */
/*
* 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.
*/
goto calculate_lifc_len;
}
/*
* 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.
*/
goto calculate_lifc_len;
/* SIOCGLIFCONF fills in the lifr_addr of each lifreq */
if (!(prev_complaints & COMP_BAD_FLAGS))
"unable to get interface flags for %s: %s",
ifs.int_if_flags = 0;
} else {
}
if (!(prev_complaints & COMP_BADADDR))
"%s has a bad address %s",
}
continue;
}
/* Ignore interface with IPv4 link local address. */
continue;
/* Get the interface index. */
ifindex = 0;
if (!(prev_complaints & COMP_NO_INDEX))
} else {
}
/*
* Get the destination address for point-to-point
* interfaces.
*/
if (!(prev_complaints & COMP_NODST))
"%s has no destination "
"address : %s",
complaints |= COMP_NODST;
}
continue;
}
if (!(prev_complaints & COMP_NODST))
"%s has a bad "
"destination address %s",
complaints |= COMP_NODST;
}
continue;
}
}
/* Get the subnet mask */
if (!(prev_complaints & COMP_NOMASK))
"%s has no netmask: %s",
}
continue;
}
if (!(ifs.int_if_flags &
(IFF_POINTOPOINT|IFF_LOOPBACK))) {
if (!(prev_complaints & COMP_NOMASK))
"%s has all-zero netmask",
}
continue;
}
} else {
}
/*
* Get the broadcast address on broadcast capable
* interfaces.
*/
if (!(prev_complaints & COMP_NOBADDR))
"%s has no broadcast "
"address: %s",
}
continue;
}
(haddr & IN_CLASSA_NET) == 0) {
if (!(prev_complaints & COMP_NOBADDR))
"%s has a bad broadcast "
"address %s",
naddr_ntoa(haddr));
}
continue;
}
}
/* Get interface metric, if possible. */
if (!(prev_complaints & COMP_BAD_METRIC))
"%s has no metric: %s",
}
} else {
if (!(prev_complaints &
"%s has a metric of %d, "
"defaulting to %d",
}
}
}
/*
* If this is an alias, then mark it appropriately.
* Do not output RIP or Router-Discovery packets via
* aliases.
*/
if (!foundloopback) {
}
} else {
}
/*
* If this interface duplicates another, mark it
* appropriately so that we don't generate duplicate
* packets.
*/
trace_misc("%s (%s%s%s) is a duplicate of %s (%s%s%s)",
"-->" : ""),
"-->" : ""),
} else {
}
/*
* 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.
*/
& (IFF_BROADCAST | IFF_LOOPBACK |
/*
* Forget old information about
* a changed interface.
*/
trace_act("interface %s has changed",
}
}
/* note interfaces that have been turned off */
"interface %s to %s turned off",
trace_act("interface %s has been off"
" %ld seconds; forget it",
}
continue;
}
/* or that were off and are now ok */
}
/*
* If it has been long enough,
* see if the interface is broken.
*/
continue;
if (!(prev_complaints & COMP_NO_KSTATS))
"unable to obtain kstats for %s",
}
/*
* If the interface just awoke, restart the counters.
*/
continue;
}
/*
* Withhold judgment when the short error counters
* wrap, the interface is reset, or if there are
* no kstats.
*/
continue;
}
/* Withhold judgement when there is no traffic */
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.
*/
trace_act("interface %s to %s"
" sick: in=%d ierr=%d"
" out=%d oerr=%d",
continue;
}
"interface %s to %s broken:"
" in=%d ierr=%d out=%d oerr=%d",
}
continue;
}
/* otherwise, it is active and healthy */
continue;
}
/*
* This is a new interface.
* If it is dead, forget it.
*/
continue;
IFF_BROADCAST | IFF_LOOPBACK)) &&
if (!(prev_complaints & COMP_BAD_FLAGS))
trace_act("%s is neither broadcast, "
"point-to-point, nor loopback",
}
/*
* It is new and ok. Add it to the list of interfaces
*/
if (!(prev_complaints & COMP_NO_KSTATS))
"unable to obtain kstats for %s",
}
/* Detect interfaces that have conflicting netmasks. */
continue;
/*
* we don't care about point-to-point
* or loopback aliases
*/
if (ifp1->int_if_flags &
continue;
}
/* ignore aliases on the same network */
continue;
"possible netmask problem"
" between %s:%s and %s:%s",
}
}
}
/* Count the # of directly connected networks. */
}
}
/*
* 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.
*/
/* lookup myaddr if we haven't done so already */
if (myaddr == 0) {
/*
* If we are unable to resolve our hostname, don't
* bother trying again.
*/
"unable to resolve local hostname %s",
myname);
}
}
if (myaddr != 0 &&
} else {
loop_rts.rts_metric = 0;
}
}
loop_rts.rts_metric = 0;
}
}
}
/* Forget any interfaces that have disappeared. */
trace_act("interface %s has disappeared",
continue;
}
/*
* If we ever have a RIPv1 interface, assume we always will.
* It might come back if it ever goes away.
*/
}
/*
* 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.
*/
/*
* Delete any routes to the network address through
* foreign routers. Remove even static routes.
*/
}
} else {
}
loop_rts.rts_metric = 0;
} else {
loop_rts.rts_metric = 0;
}
}
}
/* add the authority routes */
}
}
}
}
static void
{
/*
* 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) {
}
RS_NET_SYN, &new);
}
} else {
}
}
/*
* 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 */
{
/* skip sick interfaces */
return (_B_FALSE);
/*
* don't install routes for duplicate interfaces, or
* unnumbered point-to-point interfaces.
*/
return (_B_TRUE);
/*
* If the interface on a subnet, then install a RIPv1 route to
* the network as well (unless it is sick).
*/
else
/*
* If we are going to send packets to the gateway,
* it must be reachable using our physical interfaces
*/
!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.
*/
} else {
}
}
if (ifp->int_transitions++ > 0)
trace_act("re-installing interface %s;"
" went up %d times",
}
return (_B_TRUE);
}
/*
* Obtains the named kstat, and places its value in *value. It
* returns 0 for success, -1 for failure.
*/
static int
{
return (-1);
return (-1);
return (-1);
} else {
return (0);
}
}
static int
{
/* We did this recently; don't do it again. */
return (0);
}
return (-1);
(void) kstat_close(kc);
return (-1);
}
(void) kstat_close(kc);
return (-1);
}
(void) kstat_close(kc);
return (-1);
}
/* The loopback interface does not keep track of errors */
(void) kstat_close(kc);
return (-1);
}
}
(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).
*/
{
return (_B_FALSE);
return ((supplier_set && supplier) ||
}