interface.c revision afc7d54587eb70585fcc35b5f933cc1ed713d87a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains the routines that manipulate interfaces, the
* list of interfaces present on the system, and upper layer profiles;
* and various support functions. It also contains the functions used
* to display various bits of informations and queries for the user
* values stored in the SMF repository. Finally, it contains the
* functions required for the "gather info" threads.
*
* The daemon maintains a list of structures that represent each IPv4
* interface found on the system (after doing 'ifconfig -a plumb').
* This list represents the objects manipulated by the daemon; while
* the list of llp_t structures represents the configuration details
* requested by the user (either the automatic defaults or entries in
* list; rather, when the decision is made to make an interface active,
* IPv6 is brought up in addition to IPv4 (assuming the LLP configuration
* includes IPv6; this is the default for automatic configuration).
*
* Interfaces are brought up and torn down by a sequence of ifconfig
* commands (currently posix_spawn'd() by nwamd; the longer-term direction
* here is to use libinetcfg).
*
* Upper Layer Profile management is controlled by user-provided scripts,
* /etc/nwam/ulp/check-conditions, checks the current network setup and
* returns the name of the ULP which should be active under the current
* conditions. A ULP is specified by two scripts, found in
* optional; if they do not exist or are not executable, nwamd will
* simply move on.
*
* When an interface has been successfully brought up (signalled by the
* assignment of an IP address to the interface), the daemon will first
* teardown the existing ULP (if there is one) by running the teardown
* script for that ULP. It will then run the check-conditions script;
* if the name of a ULP is returned, it runs the bringup script for that
* ULP.
*
* A "gather info" thread is initiated for an interface when it becomes
* available. For a wired interface, "available" means the IFF_RUNNING
* flag is set; wireless interfaces are considered to always be available,
* so a wireless interface's gather info thread will run once, when it is
* found at startup. This thread will do a scan on a wireless interface,
* and initiate DHCP on a wired interface. It will then generate an event
* for the state machine that indicates the availability of a new interface.
*/
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <syslog.h>
#include <unistd.h>
#include <libscf.h>
#include <utmpx.h>
#include <pwd.h>
#include <inetcfg.h>
#include <locale.h>
#include <libintl.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/sysmacros.h>
#include <libdllink.h>
#include "defines.h"
#include "structures.h"
#include "functions.h"
#include "variables.h"
static char upper_layer_profile[MAXHOSTNAMELEN];
static void print_interface_list();
#define IPFILTER_FMRI "svc:/network/ipfilter:default"
#define LOOPBACK_IF "lo0"
void
{
char cmd[1024];
if (valid_graphical_user(B_FALSE)) {
} else {
}
}
void
show_if_status(const char *ifname)
{
char msg[128];
struct sockaddr_in sin;
int prefixlen = 0;
/* We only display new addr info for v4 interfaces */
return;
}
B_TRUE) != ICFG_SUCCESS) {
icfg_close(h);
return;
}
icfg_close(h);
}
/*
* If this interface matches the currently active llp, return B_TRUE.
* Otherwise, return B_FALSE.
*/
{
return (B_FALSE);
}
/*
* Execute 'ifconfig ifname dhcp wait 0'.
*/
static void
{
int res;
dprintf("start_dhcp: already started; returning");
return;
}
/* start dhcp timer */
if (res == -1)
}
static boolean_t
{
int i;
char *state;
for (i = 1; i <= wait_time; i++) {
} else {
return (B_TRUE);
}
}
(void) sleep(1);
}
return (B_FALSE);
}
ulp_is_active(void)
{
return (upper_layer_profile[0] != '\0');
}
/*
* Inputs:
* res is a pointer to the scf_resources_t to be released.
*/
static void
{
}
/*
* Inputs:
* lpg is the property group to look up
* lprop is the property within that group to look up
* Outputs:
* res is a pointer to an scf_resources_t. This is an internal
* structure that holds all the handles needed to get a specific
* property from the running snapshot; on a successful return it
* contains the scf_value_t that should be passed to the desired
* scf_value_get_foo() function, and must be freed after use by
* calling release_scf_resources(). On a failure return, any
* resources that may have been assigned to res are released, so
* the caller does not need to do any cleanup in the failure case.
* Returns:
* 0 on success
* -1 on failure
*/
static int
{
scf_strerror(scf_error()));
return (-1);
}
scf_strerror(scf_error()));
return (-1);
}
scf_strerror(scf_error()));
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
scf_strerror(scf_error()));
goto failure;
}
return (0);
return (-1);
}
/*
* Inputs:
* lpg is the property group to look up
* lprop is the property within that group to look up
* Outputs:
* answer is a pointer to the property value
* Returns:
* 0 on success
* -1 on failure
* If successful, the property value is retured in *answer.
* Otherwise, *answer is undefined, and it is up to the caller to decide
* how to handle that case.
*/
int
{
int result = -1;
/*
* an error was already logged by get_property_value,
* and it released any resources assigned to res before
* returning.
*/
return (result);
}
scf_strerror(scf_error()));
goto cleanup;
}
result = 0;
return (result);
}
/*
* Inputs:
* lpg is the property group to look up
* lprop is the property within that group to look up
* Outputs:
* answer is a pointer to the property value
* Returns:
* 0 on success
* -1 on failure
* If successful, the property value is retured in *answer.
* Otherwise, *answer is undefined, and it is up to the caller to decide
* how to handle that case.
*/
int
{
int result = -1;
/*
* an error was already logged by get_property_value,
* and it released any resources assigned to res before
* returning.
*/
return (result);
}
scf_strerror(scf_error()));
goto cleanup;
}
*answer);
result = 0;
return (result);
}
void
{
FILE *f;
char buffer[1024];
const char bringup[] = "/bringup";
int res;
/*
* If the look-up failed, try anyway: only avoid this if we
* know for sure not to.
*/
/*
* If doing dhcp, pass in specific interface
* name to net-svc so dhcpinfo can specify it.
*/
} else {
"for %s to come up, start method %s not "
}
}
if (f == NULL)
return;
/*
* We want to build a path to the user's upper layer profile script
* that looks like ULP_DIR "/<string we read here>/bringup". If we
* leave some space at the beginning of this buffer for ULP_DIR "/"
* that saves us some shuffling later.
*/
f) == NULL) {
(void) pclose(f);
return; /* EOF before anything read */
}
(void) pclose(f);
}
void
{
char buffer[1024];
/*
* If ULP wasn't defined...
*/
if (!ulp_is_active())
return;
upper_layer_profile[0] = '\0';
}
/*
* Returns B_TRUE if the interface is successfully brought up;
* B_FALSE if bringup fails.
*/
{
ifname);
return (B_FALSE);
}
/* check current state; no point going on if flags are 0 */
dprintf("bringupinterface(%s): get_ifflags() returned 0",
ifname);
return (B_FALSE);
}
/*
* If the link layer profile says that we want v6 then plumb it and
* bring it up; if there's a static address, configure it as well.
*/
if (ipv6onlink) {
dprintf("bringupinterface: configuring ipv6");
NULL);
if (ipv6addr) {
}
}
/*
* If we need to use DHCP and DHCP is already controlling
* the interface, we don't need to do anything.
*/
dprintf("bringupinterface: nothing to do");
return (B_TRUE);
}
if (!handle_wireless_lan(ifname)) {
"bringing %s up", ifname);
return (B_FALSE);
}
}
if (do_dhcp) {
} else {
}
return (B_TRUE);
}
void
{
dprintf("takedowninterface: can't find interface struct for %s",
ifname);
} else {
/*
* We're here because of a dhcp failure, and
* we actually want dhcp to keep trying. So
* don't take the interface down.
*/
dprintf("takedowninterface: still trying for dhcp on "
"%s, so will not take down interface", ifname);
return;
}
}
if (dhcp) {
if ((flags & IFF_DHCPRUNNING) != 0) {
/*
* We generally prefer doing a release, as that
* tells the server that it can relinquish the
* lease, whereas drop is just a client-side
* operation. But if we never came up, release
* will fail, because dhcpagent does not allow
* an interface which is selecting to release,
* so we have to drop in that circumstance. So
* try release first, then fall back to drop.
*/
NULL) != 0) {
"drop", NULL);
}
}
} else {
/* need to unset a statically configured addr */
}
if (v6onlink) {
/*
* Unplumbing the link local interface causes dhcp and ndpd to
* remove other addresses they have added.
*/
}
(void) dladm_wlan_disconnect(ifname);
dprintf("takedown interface, free cached ip address");
if (popup) {
}
}
/*
* Take down all known interfaces. If ignore_if is non-null, an
* active (IFF_UP) interface whose name matches ignore_if will *not*
* be taken down.
*/
void
take_down_all_ifs(const char *ignore_if)
{
continue;
}
}
}
static struct interface *
{
}
/*
* Add an interface struct to the interface list. The list is
* partially ordered; all the wired interfaces appear first,
* followed by all the wireless interfaces. New interfaces are
* added at the end of the appropriate list section.
*/
static void
{
case IF_WIRELESS:
wpp = &ifs_wireless;
break;
case IF_WIRED:
endp = ifs_wireless;
break;
default:
/* don't add to the list */
return;
}
/* set list head if this is the first entry */
return;
}
}
/* update list head if we just inserted the first wired interface */
if (first_wired)
/* link sections if we just inserted the first wireless interface */
if (first_wireless) {
*wpp = ifs_wireless;
}
}
/*
* Returns the interface structure upon success. Returns NULL and sets
* errno upon error. If lr is null then it will look up the information
* needed.
*
* Note that given the MT nature of this program we are almost certainly
* racing for this structure. That needs to be fixed.
*/
struct interface *
{
struct interface *i;
enum interface_type iftype;
return (NULL);
/*
* we don't track IPv6 interfaces separately from their
* v4 counterparts; a link either has v4 only, or both
* v4 and v6, so we only maintain a v4 interface struct.
*/
return (NULL);
/*
* the classic "shouldn't happen"...
*/
return (NULL);
}
/*
* for now, we're ignoring tunnel interfaces (we expect
* them to be entirely manipulated by higher layer profile
* activation/deactivation scripts)
*/
return (NULL);
}
dprintf("add_interface: malloc failed");
return (NULL);
}
free(i);
dprintf("add_interface: malloc failed");
return (NULL);
}
i->if_lflags = 0;
i->if_timer_expire = 0;
dprintf("added interface %s of type %s af %d; is %savailable",
((i->if_type == IF_WIRELESS) ||
return (i);
}
/*
* Searches for an interface and returns the interface structure if found.
* Returns NULL otherwise. errno is set upon error exit.
*/
struct interface *
get_interface(const char *name)
{
struct interface *i;
return (NULL);
return (i);
}
}
return (NULL);
}
/*
* Checks interface flags and, if IFF_DHCPRUNNING and !IFF_UP, does
* an 'ifconfig ifname dhcp drop'.
*/
void
{
return;
}
}
/*
* For wireless interface, we will try to find out available wireless
* network; for wired, if dhcp should be used, start it now to try to
* avoid delays there.
*
* For the real code, we should pass back the network information
* gathered. Note that the state engine will then use the llp to
* determine which interface should be set up...
*/
static void *
gather_interface_info(void *arg)
{
switch (i->if_type) {
case IF_WIRELESS:
(void) scan_wireless_nets(i);
break;
case IF_WIRED:
/*
* It should not happen as the llp list should be done when
* this function is called. But let the state engine decide
* what to do.
*/
break;
/*
* The following is to avoid locking up the state machine
* as it is currently the choke point. We start dhcp with
* a wait time of 0; later, if we see the link go down
* (IFF_RUNNING is cleared), we will drop the attempt.
*/
start_dhcp(i);
break;
default:
/* For other types, do not do anything. */
return (NULL);
}
gen_newif_event(i);
return (NULL);
}
void
gen_newif_event(struct interface *i)
{
struct np_event *e;
if (e == NULL) {
dprintf("gen_newif_event: calloc failed");
return;
}
dprintf("gen_newif_event: strdup failed");
free(e);
return;
}
e->npe_type = EV_ROUTING;
/*
* This event notifies the state machine that a new interface is
* (at least nominally) available to be brought up. When the state
* machine processes the event, it will look at the entire list of
* interfaces and corresponding LLPs, and make a determination about
* the best available LLP under current conditions.
*/
}
/*
* Caller uses this function to walk through the whole interface list.
* For each interface, the caller provided walker is called with
* the interface and arg as parameters.
*
* XXX There is no lock held right now for accessing the interface
* list. We probably need that in future.
*/
void
{
struct interface *i;
}
static void
print_interface_list(void)
{
dprintf("Walking interface list; starting with wired interfaces");
if (wp == ifs_wireless)
dprintf("Now wireless interfaces");
}
}
/*
* Walker function passed to icfg_iterate_if() below - the icfg_if_it *
* argument is guaranteed to be non-NULL by icfg_iterate_if(),
* since the function it uses to generate the list - icfg_get_if_list()) -
* guarantees this.
*/
/* ARGSUSED */
static int
{
/* We don't touch loopback interface. */
if (flags & IFF_LOOPBACK)
return (ICFG_SUCCESS);
/* If adding fails, just ignore that interface... */
return (ICFG_SUCCESS);
}
/*
* Walker function passed to icfg_iterate_if() below - the icfg_if_it *
* argument is guaranteed to be non-NULL by icfg_iterate_if(),
* since the function it uses to generate the list - icfg_get_if_list()) -
* guarantees this.
*/
/* ARGSUSED */
static int
{
/* We don't touch loopback interface. */
if (flags & IFF_LOOPBACK)
return (ICFG_SUCCESS);
return (ICFG_SUCCESS);
}
void
initialize_interfaces(void)
{
int times;
dprintf("initialize_interfaces: setting link_layer_profile(%p) to NULL",
(void *)link_layer_profile);
upper_layer_profile[0] = '\0';
/*
* Bring down all interfaces bar lo0.
*/
/*
* In case dhcpagent is running... If it is running, when
* we do another DHCP command on the same interface later, it may
* be confused. Just kill dhcpagent to simplify handling.
*/
dprintf("killing dhcpagent");
/*
* Really we should walk the device tree instead of doing
* the 'ifconfig -a plumb'. On the first reconfigure boot
* (after install) 'ifconfig -a plumb' comes back quickly
* without any devices configured if we start before
* dependency loop through 'svc:/system/filesystem/usr'. So
*/
break;
/*
* Assume the di_init problem is the cause: sleep and try
* again, as the DDI has probably not been initialized yet.
*/
(void) sleep(1);
}
if (times > 30)
(void) dladm_init_linkprop();
}
/*
* Walker function used to start info gathering of each interface.
*/
/* ARGSUSED */
void
{
/*
* Only if the cable of the wired interface is
* plugged in, start gathering info from it.
*/
if (!is_plugged_in(ifp))
return;
/*
* This is a "fresh start" for the interface; if dhcp
* previously failed, the flag can now be cleared.
*/
(void) pthread_attr_init(&attr);
(void *)ifp) != 0) {
} else {
}
}
/*
* Walker function used to check timer for each interface.
* If timer has expired, generate a timer event for the
* interface.
*/
/* ARGSUSED */
void
{
if (ifp->if_timer_expire == 0)
return;
return;
}
ifp->if_timer_expire = 0;
dprintf("could not allocate timer event for %s; ignoring timer",
return;
}
dprintf("could not strdup name for timer event on %s; ignoring",
return;
}
}
enum interface_type
find_if_type(const char *name)
{
enum interface_type type;
dprintf("find_if_type: no ifname; returning IF_UNKNOWN");
return (IF_UNKNOWN);
}
/*
* We'll need to update our tunnel detection once
* go back; tunnel names won't necessarily be ip.tunN
*/
} else {
/*
* We didn't recognize it. Try the libdladm function
* to decide if it is wireless or not; if not, assume
* that it's wired.
*/
}
return (type);
}
const char *
{
switch (type) {
case IF_WIRED:
return ("wired");
case IF_WIRELESS:
return ("wireless");
case IF_TUN:
return ("tunnel");
case IF_UNKNOWN:
default:
return ("unknown type");
}
}