llp.c revision b00044a2eb43864b8718585d21949611a2ee59ef
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains the routines that manipulate Link Layer Profiles
* (aka LLPs) and various support functions. This includes parsing and
*
* The daemon maintains a list of llp_t structures that represent the
* provided configuration information for a link. After the llp file
* is read, entries are added to the LLP list for any known links
* (identified by checking the interface list, which is based on the
* v4 interfaces present after 'ifconfig -a plumb') which were not
* represented in the llp file. These entries contain the default
* "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the
* v4 interface, and accept router- and DHCPv6-assigned addresses on
* the v6 interface. The default entries created by the daemon are
* also added to the llp file.
*
* LLP priority is assigned based on two factors: the order within
* the llp file, with earlier entries having higher priority; and
* a preference for wired interfaces before wireless. Entries that
* are added to the file by the daemon are added *after* any existing
* entries; within the added block, wired entries are added before
* wireless. Thus if the llp file is never modified externally, wired
* will generally be ordered before wireless. However, if the
* administrator creates the file with wireless entries before wired,
* that priority order will be respected.
*
* The llp list is protected by the global llp_lock, which must be
* pthread_mutex_lock()'d before reading or writing the list. Only the main
* thread can write to the list; this allows the main thread to deal with read
* access to structure pointers without holding locks and without the
* complexity of reference counts. All other threads must hold llp_lock for
* the duration of any read access to the data, and must not deal directly in
* structure pointers. (A thread may also hold machine_lock to block the main
* thread entirely in order to manipulate the data; such use is isolated to the
* door interface.)
*
* Functions in this file have comments noting where the main thread alone is
* the caller. These functions do not need to acquire the lock.
*
* If you hold both ifs_lock and llp_lock, you must take ifs_lock first.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <syslog.h>
#include <atomic.h>
#include <pthread.h>
#include <signal.h>
#include "defines.h"
#include "structures.h"
#include "functions.h"
#include "variables.h"
/* Lock to protect the llp list. */
/* Accessed only from main thread or with llp_lock held */
static llp_t *locked_llp;
/*
* Global variable to hold the highest priority. Need to use the atomic
* integer arithmetic functions to update it.
*/
static uint32_t llp_highest_pri;
static void print_llp_list(void);
void
initialize_llp(void)
{
}
char *
{
return ("null_llp");
return ("null_lname");
else
}
/*
* This function removes a given LLP from the global list and discards it.
* Called only from the main thread.
*/
void
{
if (pthread_mutex_lock(&llp_lock) == 0) {
if (llp == locked_llp)
locked_llp = NULL;
(void) pthread_mutex_unlock(&llp_lock);
}
}
static void
llp_list_free(void)
{
int retv;
locked_llp = NULL;
/* Something very serious is wrong... */
return;
}
}
(void) pthread_mutex_unlock(&llp_lock);
}
/*
* Called either from main thread or with llp_lock held.
*/
llp_t *
llp_lookup(const char *link)
{
return (NULL);
break;
}
return (llp);
}
/*
* Choose the higher priority llp of the two passed in. If one is
* NULL, the other will be higher priority. If both are NULL, NULL
* is returned.
*
* Assumes that both are available (i.e. doesn't check IFF_RUNNING
* or IF_DHCPFAILED flag values).
*/
llp_t *
{
return (b);
return (a);
/* Check for locked LLP selection for user interface */
if (a == locked_llp)
return (a);
else if (b == locked_llp)
return (b);
if (a->llp_failed && !b->llp_failed)
return (b);
if (!a->llp_failed && b->llp_failed)
return (a);
/*
* Higher priority is represented by a lower number. This seems a
* bit backwards, but for now it makes assigning priorities very easy.
*/
}
/*
* Chooses the highest priority link that corresponds to an available
* interface. Called only in the main thread.
*/
llp_t *
llp_best_avail(void)
{
}
}
return (rtnllp);
}
/*
* Called only by the main thread. Note that this leaves link_layer_profile
* set to NULL only in the case of abject failure, and then leaves llp_failed
* set.
*/
static void
{
char *host;
/*
* Choosing "dhcp" as a hostname is unsupported right now.
* We use hostname="dhcp" as a keyword telling bringupinterface()
* to use dhcp on the interface.
*/
char *dhcpstr = "dhcp";
llp->llp_ipv6onlink)) {
case SUCCESS:
break;
case FAILURE:
break;
case WAITING:
}
}
/*
* Replace the currently active link layer profile with the one
* specified. And since we're changing the lower layer stuff,
* we need to first deactivate the current upper layer profile.
* An upper layer profile will be reactivated later, when we get
* confirmation that the new llp is fully up (has an address
* assigned).
*
* If the new llp is the same as the currently active one, don't
* do anything.
*
* Called only by the main thread.
*/
void
{
int minpri;
if (newllp == link_layer_profile)
return;
if (link_layer_profile != NULL) {
dprintf("taking down current link layer profile (%s)",
}
/*
* Establish the new link layer profile. If we have trouble setting
* it, then try to get another. Note that llp_activate sets llp_failed
* on failure, so this loop is guaranteed to terminate.
*/
dprintf("bringing up new link layer profile (%s)",
if (link_layer_profile == NULL &&
}
/*
* Knock down all interfaces that are at a lower (higher-numbered)
* priority than the new one. If there isn't a new one, then leave
* everything as it is.
*/
if (link_layer_profile == NULL) {
minpri = -1;
if (locked_llp != NULL)
} else {
dprintf("taking down remaining interfaces below priority %d",
minpri);
}
if (newllp == link_layer_profile)
continue;
else
}
}
/*
* Create the named LLP with default settings. Called only in main thread.
*/
llp_t *
{
int retv;
return (NULL);
}
name);
return (NULL);
}
/*
* should be a no-op, but for now, make sure we only
* create llps for wired and wireless interfaces.
*/
return (NULL);
}
/* Something very serious is wrong... */
return (NULL);
}
(void) pthread_mutex_unlock(&llp_lock);
return (llp);
}
/*
* Walker function to pass to walk_interface() to find out if
* an interface description is missing from LLPFILE. If it is,
* add it.
*
* Currently, IF_TUN type interfaces are special-cased: they are
* only handled as user-enabled, layered links (which may be created
* as part of a higher-layer profile, for example). Thus, they
* shouldn't be considered when looking at the llp list, so don't
* add them here.
*
* ifs_lock is held when this function is called. Called only in main thread.
*/
static void
{
case AF_INET:
break;
case AF_INET6:
break;
default:
return;
}
/* If we run out of memory, ignore this interface for now. */
}
}
static void
print_llp_list(void)
{
dprintf("Walking llp list");
}
/*
* profile. The file is line oriented with each line containing tab or space
* delimited fields. Each address family (IPv4, IPv6) is described on a
* separate line.
* The first field is a link name.
* The second field can be either static, dhcp, ipv6, noipv6, or priority.
* If the second field is static then the next field is an ipv4 address which
* can contain a prefix. Previous versions of this file could contain a
* hostname in this field which is no longer supported.
* If the second field is dhcp then dhcp will be used on the interface.
* If the second field is ipv6 then an ipv6 interface is plumbed up. The
* outcome of this is that if offered by the network in.ndpd and dhcp
* will conspire to put addresses on additional ipv6 logical interfaces.
* If the next field is non-null then it is taken to be an IPv6 address
* and possible prefix which are applied to the interface.
* If the second field is noipv6 then no ipv6 interfaces will be put on that
* link.
* If the second field is priority, then the next field is an integer
* specifying the link priority.
*
* Called only in main thread.
*/
void
llp_parse_config(void)
{
static const char STATICSTR[] = "static";
static const char DHCP[] = "dhcp";
static const char IPV6[] = "ipv6";
static const char NOIPV6[] = "noipv6";
static const char PRIORITY[] = "priority";
int lnum;
/* Create the NWAM directory in case it does not exist. */
return;
}
return;
}
return;
}
}
cp++;
continue;
"ignoring entry", lnum);
continue;
}
continue;
}
"for static config", lnum);
lnum);
} else {
}
(void) 0;
lnum);
} else {
}
"llp:%d: missing priority value", lnum);
} else {
}
} else {
srcstr);
}
}
/*
* So we have read in the llp file, is there an interface which
* it does not describe? If yes, we'd better add it to the
* file for future reference.
*/
}
/*
* Called only from the main thread.
*/
void
{
return;
}
/*
* This function rewrites the LLP configuration file entry for a given
* interface and keyword. If the keyword is present, then it is updated if
* removeonly is B_FALSE, otherwise it's removed. If the keyword is not
* present, then it is added immediately after the last entry for that
* interface if removeonly is B_FALSE, otherwise no action is taken. User
* comments are preserved.
*
* To preserve file integrity, this is called only from the main thread.
*/
static void
{
long match_pos;
return;
return;
}
*cp = '\0';
cp++;
if (!matched_if || copying) {
/*
* It's ugly to write through the pointer
* returned as the third argument of strtok_r,
* but doing so saves a data copy.
*/
}
continue;
}
/*
* If we've found the keyword, then process removal or update
* of the value.
*/
if (removeonly)
continue;
/* Value identical; abort update */
goto no_change;
}
keyword);
} else {
}
continue;
}
/* Otherwise, record the last possible insertion point */
matched_if = B_TRUE;
}
if (!copying) {
/* keyword not encountered; we're done if deleting */
if (removeonly)
goto no_change;
/* need to add keyword and value */
} else {
optval);
}
/* copy the rest of the file */
goto restart;
}
(void) unlink(LLPFILETMP);
}
return;
(void) unlink(LLPFILETMP);
}
/*
* This is called back from the main thread by the state machine.
*/
void
{
} else {
char prival[32];
}
}
/*
* Called by the door interface: set LLP priority and schedule an LLP update if
* this interface has changed.
*/
int
{
int retv;
if (prio < 0)
return (EINVAL);
return (retv);
}
retv = 0;
} else {
}
(void) pthread_mutex_unlock(&llp_lock);
return (retv);
}
/*
* Called by the door interface: set a locked LLP and schedule an LLP update if
* the locked LLP has changed.
*/
int
set_locked_llp(const char *ifname)
{
int retv;
return (retv);
if (ifname[0] == '\0') {
if (locked_llp != NULL) {
locked_llp = NULL;
}
locked_llp = llp;
if (llp != link_layer_profile)
} else {
}
(void) pthread_mutex_unlock(&llp_lock);
return (retv);
}
/* Copy string to pre-allocated buffer. */
static void
{
}
}
/*
* Sample the list of LLPs and copy to a single buffer for return through the
* door interface.
*/
llp_t *
{
char *strptr;
int retv;
*lsize = 0;
return (NULL);
}
nllp = 0;
strspace = 0;
nllp++;
}
/* Note that malloc doesn't guarantee a NULL return for zero count */
&strptr);
&strptr);
llpl++;
}
}
(void) pthread_mutex_unlock(&llp_lock);
/* Add in the special door-only state flags */
while (nllp-- > 0) {
&llpl->llp_link_up);
}
llpl++;
}
return (llplist);
}
/*
* This is called for the special case when there are outstanding requests sent
* to the user interface, and the user interface disappears. We handle this
* case by re-running bringupinterface() without deselecting. That function
* will call the wireless and DHCP-related parts again, and they should proceed
* in automatic mode, because the UI is now gone.
*
* Called only by the main thread or by a thread holding machine_lock.
*/
void
llp_reselect(void)
{
const char *host;
/*
* If there's no active profile, or if the active profile isn't waiting
* on the UI, then just return; nothing to do.
*/
return;
llp->llp_ipv6onlink)) {
case SUCCESS:
break;
case FAILURE:
break;
case WAITING:
}
}
/*
* This is used by the wireless module to check on the selected LLP. We don't
* do periodic rescans if a wireless interface is current and if its connection
* state is good.
*/
void
{
*ifname = '\0';
*iftype = IF_UNKNOWN;
if (pthread_mutex_lock(&llp_lock) == 0) {
if (link_layer_profile != NULL) {
ifnlen);
}
(void) pthread_mutex_unlock(&llp_lock);
}
}
/*
* This is called by the interface.c module to check if an interface needs to
* run DHCP. It's intentionally called without ifs_lock held.
*/
llp_get_ipv4src(const char *ifname)
{
if (pthread_mutex_lock(&llp_lock) == 0) {
(void) pthread_mutex_unlock(&llp_lock);
}
return (src);
}
/*
* Dump out the LLP state via debug messages.
*/
void
print_llp_status(void)
{
if (pthread_mutex_lock(&llp_lock) == 0) {
if (link_layer_profile == NULL)
dprintf("no LLP selected");
else
dprintf("LLP %s selected",
if (locked_llp == NULL)
dprintf("no LLP locked");
else
dprintf("LLP %s pri %d file order %d type %d "
"%sfailed %swaiting src %d v4addr %s v6addr %s "
"v6 %son-link",
(int)llp->llp_ipv4src,
}
(void) pthread_mutex_unlock(&llp_lock);
}
}