Home | History | Annotate | Download | only in nwamd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * This file contains the routines that manipulate Link Layer Profiles
     29  * (aka LLPs) and various support functions.  This includes parsing and
     30  * updating of the /etc/nwam/llp file.
     31  *
     32  * The daemon maintains a list of llp_t structures that represent the
     33  * provided configuration information for a link.  After the llp file
     34  * is read, entries are added to the LLP list for any known links
     35  * (identified by checking the interface list, which is based on the
     36  * v4 interfaces present after 'ifconfig -a plumb') which were not
     37  * represented in the llp file.  These entries contain the default
     38  * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the
     39  * v4 interface, and accept router- and DHCPv6-assigned addresses on
     40  * the v6 interface.  The default entries created by the daemon are
     41  * also added to the llp file.
     42  *
     43  * LLP priority is assigned based on two factors: the order within
     44  * the llp file, with earlier entries having higher priority; and
     45  * a preference for wired interfaces before wireless.  Entries that
     46  * are added to the file by the daemon are added *after* any existing
     47  * entries; within the added block, wired entries are added before
     48  * wireless.  Thus if the llp file is never modified externally, wired
     49  * will generally be ordered before wireless.  However, if the
     50  * administrator creates the file with wireless entries before wired,
     51  * that priority order will be respected.
     52  *
     53  * The llp list is protected by the global llp_lock, which must be
     54  * pthread_mutex_lock()'d before reading or writing the list.  Only the main
     55  * thread can write to the list; this allows the main thread to deal with read
     56  * access to structure pointers without holding locks and without the
     57  * complexity of reference counts.  All other threads must hold llp_lock for
     58  * the duration of any read access to the data, and must not deal directly in
     59  * structure pointers.  (A thread may also hold machine_lock to block the main
     60  * thread entirely in order to manipulate the data; such use is isolated to the
     61  * door interface.)
     62  *
     63  * Functions in this file have comments noting where the main thread alone is
     64  * the caller.  These functions do not need to acquire the lock.
     65  *
     66  * If you hold both ifs_lock and llp_lock, you must take ifs_lock first.
     67  */
     68 
     69 #include <stdio.h>
     70 #include <stdlib.h>
     71 #include <unistd.h>
     72 #include <limits.h>
     73 #include <strings.h>
     74 #include <string.h>
     75 #include <ctype.h>
     76 #include <errno.h>
     77 #include <assert.h>
     78 #include <sys/types.h>
     79 #include <sys/stat.h>
     80 #include <syslog.h>
     81 #include <netinet/in.h>
     82 #include <arpa/inet.h>
     83 #include <atomic.h>
     84 #include <pthread.h>
     85 #include <signal.h>
     86 
     87 #include "defines.h"
     88 #include "structures.h"
     89 #include "functions.h"
     90 #include "variables.h"
     91 
     92 /* Lock to protect the llp list. */
     93 static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER;
     94 
     95 /* Accessed only from main thread or with llp_lock held */
     96 llp_t *link_layer_profile;
     97 
     98 static struct qelem llp_list;
     99 static llp_t *locked_llp;
    100 
    101 /*
    102  * Global variable to hold the highest priority.  Need to use the atomic
    103  * integer arithmetic functions to update it.
    104  */
    105 static uint32_t llp_highest_pri;
    106 
    107 static void print_llp_list(void);
    108 
    109 void
    110 initialize_llp(void)
    111 {
    112 	llp_list.q_forw = llp_list.q_back = &llp_list;
    113 }
    114 
    115 char *
    116 llp_prnm(llp_t *llp)
    117 {
    118 	if (llp == NULL)
    119 		return ("null_llp");
    120 	else if (llp->llp_lname == NULL)
    121 		return ("null_lname");
    122 	else
    123 		return (llp->llp_lname);
    124 }
    125 
    126 /*
    127  * This function removes a given LLP from the global list and discards it.
    128  * Called only from the main thread.
    129  */
    130 void
    131 llp_delete(llp_t *llp)
    132 {
    133 	if (pthread_mutex_lock(&llp_lock) == 0) {
    134 		if (llp == locked_llp)
    135 			locked_llp = NULL;
    136 		assert(llp != link_layer_profile);
    137 		remque(&llp->llp_links);
    138 		(void) pthread_mutex_unlock(&llp_lock);
    139 		free(llp->llp_ipv6addrstr);
    140 		free(llp->llp_ipv4addrstr);
    141 		free(llp);
    142 	}
    143 }
    144 
    145 static void
    146 llp_list_free(void)
    147 {
    148 	int retv;
    149 	llp_t *llp;
    150 
    151 	locked_llp = NULL;
    152 	if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
    153 		/* Something very serious is wrong... */
    154 		syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %s",
    155 		    strerror(retv));
    156 		return;
    157 	}
    158 	while (llp_list.q_forw != &llp_list) {
    159 		llp = (llp_t *)llp_list.q_forw;
    160 		remque(&llp->llp_links);
    161 		free(llp->llp_ipv6addrstr);
    162 		free(llp->llp_ipv4addrstr);
    163 		free(llp);
    164 	}
    165 	(void) pthread_mutex_unlock(&llp_lock);
    166 }
    167 
    168 /*
    169  * Called either from main thread or with llp_lock held.
    170  */
    171 llp_t *
    172 llp_lookup(const char *link)
    173 {
    174 	llp_t *llp;
    175 
    176 	if (link == NULL)
    177 		return (NULL);
    178 
    179 	for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
    180 	    llp = (llp_t *)llp->llp_links.q_forw) {
    181 		if (strcmp(link, llp->llp_lname) == 0)
    182 			break;
    183 	}
    184 	if (llp == (llp_t *)&llp_list)
    185 		llp = NULL;
    186 	return (llp);
    187 }
    188 
    189 /*
    190  * Choose the higher priority llp of the two passed in.  If one is
    191  * NULL, the other will be higher priority.  If both are NULL, NULL
    192  * is returned.
    193  *
    194  * Assumes that both are available (i.e. doesn't check IFF_RUNNING
    195  * or IF_DHCPFAILED flag values).
    196  */
    197 llp_t *
    198 llp_high_pri(llp_t *a, llp_t *b)
    199 {
    200 	if (a == NULL || a->llp_links.q_forw == NULL)
    201 		return (b);
    202 	else if (b == NULL || b->llp_links.q_forw == NULL)
    203 		return (a);
    204 
    205 	/* Check for locked LLP selection for user interface */
    206 	if (a == locked_llp)
    207 		return (a);
    208 	else if (b == locked_llp)
    209 		return (b);
    210 
    211 	if (a->llp_failed && !b->llp_failed)
    212 		return (b);
    213 	if (!a->llp_failed && b->llp_failed)
    214 		return (a);
    215 
    216 	/*
    217 	 * Higher priority is represented by a lower number.  This seems a
    218 	 * bit backwards, but for now it makes assigning priorities very easy.
    219 	 */
    220 	return ((a->llp_pri <= b->llp_pri) ? a : b);
    221 }
    222 
    223 /*
    224  * Chooses the highest priority link that corresponds to an available
    225  * interface.  Called only in the main thread.
    226  */
    227 llp_t *
    228 llp_best_avail(void)
    229 {
    230 	llp_t *llp, *rtnllp;
    231 
    232 	if ((rtnllp = locked_llp) == NULL) {
    233 		for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
    234 		    llp = (llp_t *)llp->llp_links.q_forw) {
    235 			if (is_interface_ok(llp->llp_lname))
    236 				rtnllp = llp_high_pri(llp, rtnllp);
    237 		}
    238 	}
    239 
    240 	return (rtnllp);
    241 }
    242 
    243 /*
    244  * Called only by the main thread.  Note that this leaves link_layer_profile
    245  * set to NULL only in the case of abject failure, and then leaves llp_failed
    246  * set.
    247  */
    248 static void
    249 llp_activate(llp_t *llp)
    250 {
    251 	char *host;
    252 	/*
    253 	 * Choosing "dhcp" as a hostname is unsupported right now.
    254 	 * We use hostname="dhcp" as a keyword telling bringupinterface()
    255 	 * to use dhcp on the interface.
    256 	 */
    257 	char *dhcpstr = "dhcp";
    258 
    259 	assert(link_layer_profile == NULL);
    260 
    261 	host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr :
    262 	    llp->llp_ipv4addrstr;
    263 
    264 	report_llp_selected(llp->llp_lname);
    265 	switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
    266 	    llp->llp_ipv6onlink)) {
    267 	case SUCCESS:
    268 		llp->llp_failed = B_FALSE;
    269 		llp->llp_waiting = B_FALSE;
    270 		link_layer_profile = llp;
    271 		dprintf("llp_activate: activated llp for %s", llp_prnm(llp));
    272 		break;
    273 	case FAILURE:
    274 		llp->llp_failed = B_TRUE;
    275 		llp->llp_waiting = B_FALSE;
    276 		dprintf("llp_activate: failed to bring up %s", llp_prnm(llp));
    277 		report_llp_unselected(llp->llp_lname, dcFailed);
    278 		link_layer_profile = NULL;
    279 		break;
    280 	case WAITING:
    281 		llp->llp_failed = B_FALSE;
    282 		llp->llp_waiting = B_TRUE;
    283 		link_layer_profile = llp;
    284 		dprintf("llp_activate: waiting for %s", llp_prnm(llp));
    285 	}
    286 }
    287 
    288 /*
    289  * Replace the currently active link layer profile with the one
    290  * specified.  And since we're changing the lower layer stuff,
    291  * we need to first deactivate the current upper layer profile.
    292  * An upper layer profile will be reactivated later, when we get
    293  * confirmation that the new llp is fully up (has an address
    294  * assigned).
    295  *
    296  * If the new llp is the same as the currently active one, don't
    297  * do anything.
    298  *
    299  * Called only by the main thread.
    300  */
    301 void
    302 llp_swap(llp_t *newllp, libnwam_diag_cause_t cause)
    303 {
    304 	int minpri;
    305 
    306 	if (newllp == link_layer_profile)
    307 		return;
    308 
    309 	deactivate_upper_layer_profile();
    310 
    311 	if (link_layer_profile != NULL) {
    312 		dprintf("taking down current link layer profile (%s)",
    313 		    llp_prnm(link_layer_profile));
    314 		report_llp_unselected(link_layer_profile->llp_lname, cause);
    315 		link_layer_profile->llp_waiting = B_FALSE;
    316 		link_layer_profile = NULL;
    317 	}
    318 
    319 	/*
    320 	 * Establish the new link layer profile.  If we have trouble setting
    321 	 * it, then try to get another.  Note that llp_activate sets llp_failed
    322 	 * on failure, so this loop is guaranteed to terminate.
    323 	 */
    324 	while (newllp != NULL) {
    325 		dprintf("bringing up new link layer profile (%s)",
    326 		    llp_prnm(newllp));
    327 		llp_activate(newllp);
    328 		newllp = NULL;
    329 		if (link_layer_profile == NULL &&
    330 		    (newllp = llp_best_avail()) != NULL &&
    331 		    newllp->llp_failed)
    332 			newllp = NULL;
    333 	}
    334 
    335 	/*
    336 	 * Knock down all interfaces that are at a lower (higher-numbered)
    337 	 * priority than the new one.  If there isn't a new one, then leave
    338 	 * everything as it is.
    339 	 */
    340 	if (link_layer_profile == NULL) {
    341 		minpri = -1;
    342 		if (locked_llp != NULL)
    343 			dprintf("taking down all but %s", llp_prnm(locked_llp));
    344 	} else {
    345 		minpri = link_layer_profile->llp_pri;
    346 		dprintf("taking down remaining interfaces below priority %d",
    347 		    minpri);
    348 	}
    349 	for (newllp = (llp_t *)llp_list.q_forw; newllp != (llp_t *)&llp_list;
    350 	    newllp = (llp_t *)newllp->llp_links.q_forw) {
    351 		if (newllp == link_layer_profile)
    352 			continue;
    353 		if ((link_layer_profile != NULL && newllp->llp_pri > minpri) ||
    354 		    (locked_llp != NULL && newllp != locked_llp))
    355 			takedowninterface(newllp->llp_lname, cause);
    356 		else
    357 			clear_cached_address(newllp->llp_lname);
    358 	}
    359 }
    360 
    361 /*
    362  * Create the named LLP with default settings.  Called only in main thread.
    363  */
    364 llp_t *
    365 llp_add(const char *name)
    366 {
    367 	int retv;
    368 	llp_t *llp;
    369 
    370 	if ((llp = calloc(1, sizeof (llp_t))) == NULL) {
    371 		syslog(LOG_ERR, "cannot allocate LLP: %m");
    372 		return (NULL);
    373 	}
    374 
    375 	if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >=
    376 	    sizeof (llp->llp_lname)) {
    377 		syslog(LOG_ERR, "llp: link name '%s' too long; ignoring entry",
    378 		    name);
    379 		free(llp);
    380 		return (NULL);
    381 	}
    382 
    383 	llp->llp_fileorder = llp->llp_pri =
    384 	    atomic_add_32_nv(&llp_highest_pri, 1);
    385 	llp->llp_ipv4src = IPV4SRC_DHCP;
    386 	llp->llp_type = find_if_type(llp->llp_lname);
    387 	llp->llp_ipv6onlink = B_TRUE;
    388 
    389 	/*
    390 	 * should be a no-op, but for now, make sure we only
    391 	 * create llps for wired and wireless interfaces.
    392 	 */
    393 	if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) {
    394 		syslog(LOG_ERR, "llp: wrong type of interface for %s", name);
    395 		free(llp);
    396 		return (NULL);
    397 	}
    398 
    399 	if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
    400 		/* Something very serious is wrong... */
    401 		syslog(LOG_ERR, "llp: cannot lock mutex: %s", strerror(retv));
    402 		free(llp);
    403 		return (NULL);
    404 	}
    405 
    406 	insque(&llp->llp_links, llp_list.q_back);
    407 
    408 	(void) pthread_mutex_unlock(&llp_lock);
    409 
    410 	dprintf("created llp for link %s, priority %d", llp->llp_lname,
    411 	    llp->llp_pri);
    412 	return (llp);
    413 }
    414 
    415 /*
    416  * Walker function to pass to walk_interface() to find out if
    417  * an interface description is missing from LLPFILE.  If it is,
    418  * add it.
    419  *
    420  * Currently, IF_TUN type interfaces are special-cased: they are
    421  * only handled as user-enabled, layered links (which may be created
    422  * as part of a higher-layer profile, for example).  Thus, they
    423  * shouldn't be considered when looking at the llp list, so don't
    424  * add them here.
    425  *
    426  * ifs_lock is held when this function is called.  Called only in main thread.
    427  */
    428 static void
    429 find_and_add_llp(struct interface *ifp, void *arg)
    430 {
    431 	FILE *fp = arg;
    432 
    433 	if (ifp->if_type != IF_TUN && llp_lookup(ifp->if_name) == NULL) {
    434 		switch (ifp->if_family) {
    435 		case AF_INET:
    436 			(void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
    437 			break;
    438 
    439 		case AF_INET6:
    440 			(void) fprintf(fp, "%s\tipv6\n", ifp->if_name);
    441 			break;
    442 
    443 		default:
    444 			syslog(LOG_ERR, "interface %s family %d?!",
    445 			    ifp->if_name, ifp->if_family);
    446 			return;
    447 		}
    448 		dprintf("Added %s to %s", ifp->if_name, LLPFILE);
    449 		/* If we run out of memory, ignore this interface for now. */
    450 		(void) llp_add(ifp->if_name);
    451 	}
    452 }
    453 
    454 static void
    455 print_llp_list(void)
    456 {
    457 	llp_t *wp;
    458 
    459 	dprintf("Walking llp list");
    460 	for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list;
    461 	    wp = (llp_t *)wp->llp_links.q_forw)
    462 		dprintf("==> %s", wp->llp_lname);
    463 }
    464 
    465 /*
    466  * This function parses /etc/nwam/llp which describes the phase 0 link layer
    467  * profile.  The file is line oriented with each line containing tab or space
    468  * delimited fields.  Each address family (IPv4, IPv6) is described on a
    469  * separate line.
    470  * The first field is a link name.
    471  * The second field can be either static, dhcp, ipv6, noipv6, or priority.
    472  * If the second field is static then the next field is an ipv4 address which
    473  *    can contain a prefix.  Previous versions of this file could contain a
    474  *    hostname in this field which is no longer supported.
    475  * If the second field is dhcp then dhcp will be used on the interface.
    476  * If the second field is ipv6 then an ipv6 interface is plumbed up.  The
    477  *    outcome of this is that if offered by the network in.ndpd and dhcp
    478  *    will conspire to put addresses on additional ipv6 logical interfaces.
    479  *    If the next field is non-null then it is taken to be an IPv6 address
    480  *    and possible prefix which are applied to the interface.
    481  * If the second field is noipv6 then no ipv6 interfaces will be put on that
    482  *    link.
    483  * If the second field is priority, then the next field is an integer
    484  *    specifying the link priority.
    485  *
    486  * Called only in main thread.
    487  */
    488 void
    489 llp_parse_config(void)
    490 {
    491 	static const char STATICSTR[] = "static";
    492 	static const char DHCP[] = "dhcp";
    493 	static const char IPV6[] = "ipv6";
    494 	static const char NOIPV6[] = "noipv6";
    495 	static const char PRIORITY[] = "priority";
    496 	FILE *fp;
    497 	char line[LINE_MAX];
    498 	char *cp, *lasts, *lstr, *srcstr, *addrstr;
    499 	int lnum;
    500 	llp_t *llp;
    501 
    502 	/* Create the NWAM directory in case it does not exist. */
    503 	if (mkdir(LLPDIRNAME, LLPDIRMODE) != 0 &&
    504 	    errno != EEXIST) {
    505 		syslog(LOG_ERR, "could not create %s: %m", LLPDIRNAME);
    506 		return;
    507 	}
    508 
    509 	fp = fopen(LLPFILE, "r+");
    510 	if (fp == NULL) {
    511 		if (errno != ENOENT) {
    512 			syslog(LOG_ERR, "open LLP config file: %m");
    513 			return;
    514 		}
    515 		if ((fp = fopen(LLPFILE, "w+")) == NULL) {
    516 			syslog(LOG_ERR, "create LLP config file: %m");
    517 			return;
    518 		}
    519 	}
    520 
    521 	llp_list_free();
    522 
    523 	for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
    524 		if (line[strlen(line) - 1] == '\n')
    525 			line[strlen(line) - 1] = '\0';
    526 
    527 		cp = line;
    528 		while (isspace(*cp))
    529 			cp++;
    530 
    531 		if (*cp == '#' || *cp == '\0')
    532 			continue;
    533 
    534 		dprintf("parsing llp conf file line %d...", lnum);
    535 
    536 		if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
    537 		    ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
    538 			syslog(LOG_ERR, "llp:%d: not enough tokens; "
    539 			    "ignoring entry", lnum);
    540 			continue;
    541 		}
    542 
    543 		if ((llp = llp_lookup(lstr)) == NULL &&
    544 		    (llp = llp_add(lstr)) == NULL) {
    545 			syslog(LOG_ERR, "llp:%d: cannot add entry", lnum);
    546 			continue;
    547 		}
    548 
    549 		if (strcasecmp(srcstr, STATICSTR) == 0) {
    550 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL ||
    551 			    atoi(addrstr) == 0) { /* crude check for number */
    552 				syslog(LOG_ERR, "llp:%d: missing ipaddr "
    553 				    "for static config", lnum);
    554 			} else if ((addrstr = strdup(addrstr)) == NULL) {
    555 				syslog(LOG_ERR, "llp:%d: cannot save address",
    556 				    lnum);
    557 			} else {
    558 				free(llp->llp_ipv4addrstr);
    559 				llp->llp_ipv4src = IPV4SRC_STATIC;
    560 				llp->llp_ipv4addrstr = addrstr;
    561 			}
    562 
    563 		} else if (strcasecmp(srcstr, DHCP) == 0) {
    564 			llp->llp_ipv4src = IPV4SRC_DHCP;
    565 
    566 		} else if (strcasecmp(srcstr, IPV6) == 0) {
    567 			llp->llp_ipv6onlink = B_TRUE;
    568 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
    569 				(void) 0;
    570 			} else if ((addrstr = strdup(addrstr)) == NULL) {
    571 				syslog(LOG_ERR, "llp:%d: cannot save address",
    572 				    lnum);
    573 			} else {
    574 				free(llp->llp_ipv6addrstr);
    575 				llp->llp_ipv6addrstr = addrstr;
    576 			}
    577 
    578 		} else if (strcasecmp(srcstr, NOIPV6) == 0) {
    579 			llp->llp_ipv6onlink = B_FALSE;
    580 
    581 		} else if (strcasecmp(srcstr, PRIORITY) == 0) {
    582 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
    583 				syslog(LOG_ERR,
    584 				    "llp:%d: missing priority value", lnum);
    585 			} else {
    586 				llp->llp_pri = atoi(addrstr);
    587 			}
    588 
    589 		} else {
    590 			syslog(LOG_ERR, "llp:%d: unrecognized field '%s'", lnum,
    591 			    srcstr);
    592 		}
    593 	}
    594 
    595 	/*
    596 	 * So we have read in the llp file, is there an interface which
    597 	 * it does not describe?  If yes, we'd better add it to the
    598 	 * file for future reference.
    599 	 */
    600 	walk_interface(find_and_add_llp, fp);
    601 
    602 	(void) fclose(fp);
    603 
    604 	print_llp_list();
    605 }
    606 
    607 /*
    608  * Called only from the main thread.
    609  */
    610 void
    611 llp_add_file(const llp_t *llp)
    612 {
    613 	FILE *fp;
    614 
    615 	if ((fp = fopen(LLPFILE, "a")) == NULL)
    616 		return;
    617 	(void) fprintf(fp, "%s\tdhcp\n", llp->llp_lname);
    618 	(void) fclose(fp);
    619 }
    620 
    621 /*
    622  * This function rewrites the LLP configuration file entry for a given
    623  * interface and keyword.  If the keyword is present, then it is updated if
    624  * removeonly is B_FALSE, otherwise it's removed.  If the keyword is not
    625  * present, then it is added immediately after the last entry for that
    626  * interface if removeonly is B_FALSE, otherwise no action is taken.  User
    627  * comments are preserved.
    628  *
    629  * To preserve file integrity, this is called only from the main thread.
    630  */
    631 static void
    632 llp_update_config(const char *ifname, const char *keyword, const char *optval,
    633     boolean_t removeonly)
    634 {
    635 	FILE *fpin, *fpout;
    636 	char line[LINE_MAX];
    637 	char *cp, *lstr, *keystr, *valstr, *lasts;
    638 	boolean_t matched_if, copying;
    639 	long match_pos;
    640 
    641 	if ((fpin = fopen(LLPFILE, "r")) == NULL)
    642 		return;
    643 	if ((fpout = fopen(LLPFILETMP, "w")) == NULL) {
    644 		syslog(LOG_ERR, "create LLP temporary config file: %m");
    645 		(void) fclose(fpin);
    646 		return;
    647 	}
    648 	matched_if = copying = B_FALSE;
    649 restart:
    650 	while (fgets(line, sizeof (line), fpin) != NULL) {
    651 		cp = line + strlen(line) - 1;
    652 		if (cp >= line && *cp == '\n')
    653 			*cp = '\0';
    654 
    655 		cp = line;
    656 		while (isspace(*cp))
    657 			cp++;
    658 
    659 		lstr = NULL;
    660 		if (copying || *cp == '#' ||
    661 		    (lstr = strtok_r(cp, " \t", &lasts)) == NULL ||
    662 		    strcmp(lstr, ifname) != 0) {
    663 			if (!matched_if || copying) {
    664 				/*
    665 				 * It's ugly to write through the pointer
    666 				 * returned as the third argument of strtok_r,
    667 				 * but doing so saves a data copy.
    668 				 */
    669 				if (lstr != NULL && lasts != NULL)
    670 					lasts[-1] = '\t';
    671 				(void) fprintf(fpout, "%s\n", line);
    672 			}
    673 			continue;
    674 		}
    675 
    676 		if (lasts != NULL)
    677 			lasts[-1] = '\t';
    678 
    679 		/*
    680 		 * If we've found the keyword, then process removal or update
    681 		 * of the value.
    682 		 */
    683 		if ((keystr = strtok_r(NULL, " \t", &lasts)) != NULL &&
    684 		    strcmp(keystr, keyword) == 0) {
    685 			matched_if = copying = B_TRUE;
    686 			if (removeonly)
    687 				continue;
    688 			valstr = strtok_r(NULL, " \t", &lasts);
    689 			if ((valstr == NULL && optval == NULL) ||
    690 			    (valstr != NULL && optval != NULL &&
    691 			    strcmp(valstr, optval) == 0)) {
    692 				/* Value identical; abort update */
    693 				goto no_change;
    694 			}
    695 			if (optval == NULL) {
    696 				(void) fprintf(fpout, "%s\t%s\n", ifname,
    697 				    keyword);
    698 			} else {
    699 				(void) fprintf(fpout, "%s\t%s %s\n", ifname,
    700 				    keyword, optval);
    701 			}
    702 			continue;
    703 		}
    704 
    705 		/* Otherwise, record the last possible insertion point */
    706 		matched_if = B_TRUE;
    707 		match_pos = ftell(fpin);
    708 		if (lasts != NULL)
    709 			lasts[-1] = '\t';
    710 		(void) fprintf(fpout, "%s\n", line);
    711 	}
    712 	if (!copying) {
    713 		/* keyword not encountered; we're done if deleting */
    714 		if (removeonly)
    715 			goto no_change;
    716 		/* need to add keyword and value */
    717 		if (optval == NULL) {
    718 			(void) fprintf(fpout, "%s\t%s\n", ifname, keyword);
    719 		} else {
    720 			(void) fprintf(fpout, "%s\t%s %s\n", ifname, keyword,
    721 			    optval);
    722 		}
    723 		/* copy the rest of the file */
    724 		(void) fseek(fpin, match_pos, SEEK_SET);
    725 		copying = B_TRUE;
    726 		goto restart;
    727 	}
    728 	(void) fclose(fpin);
    729 	(void) fclose(fpout);
    730 	if (rename(LLPFILETMP, LLPFILE) != 0) {
    731 		syslog(LOG_ERR, "rename LLP temporary config file: %m");
    732 		(void) unlink(LLPFILETMP);
    733 	}
    734 	return;
    735 
    736 no_change:
    737 	(void) fclose(fpin);
    738 	(void) fclose(fpout);
    739 	(void) unlink(LLPFILETMP);
    740 }
    741 
    742 /*
    743  * This is called back from the main thread by the state machine.
    744  */
    745 void
    746 llp_write_changed_priority(llp_t *llp)
    747 {
    748 	if (llp->llp_pri == llp->llp_fileorder) {
    749 		llp_update_config(llp->llp_lname, "priority", NULL, B_TRUE);
    750 	} else {
    751 		char prival[32];
    752 
    753 		(void) snprintf(prival, sizeof (prival), "%d", llp->llp_pri);
    754 		llp_update_config(llp->llp_lname, "priority", prival, B_FALSE);
    755 	}
    756 }
    757 
    758 /*
    759  * Called by the door interface: set LLP priority and schedule an LLP update if
    760  * this interface has changed.
    761  */
    762 int
    763 set_llp_priority(const char *ifname, int prio)
    764 {
    765 	llp_t *llp;
    766 	int retv;
    767 
    768 	if (prio < 0)
    769 		return (EINVAL);
    770 
    771 	if ((retv = pthread_mutex_lock(&llp_lock)) != 0)
    772 		return (retv);
    773 	if ((llp = llp_lookup(ifname)) != NULL) {
    774 		llp->llp_failed = B_FALSE;
    775 		if (llp->llp_pri != prio) {
    776 			llp->llp_pri = prio;
    777 			(void) np_queue_add_event(EV_USER, ifname);
    778 		}
    779 		retv = 0;
    780 	} else {
    781 		retv = ENXIO;
    782 	}
    783 	(void) pthread_mutex_unlock(&llp_lock);
    784 	return (retv);
    785 }
    786 
    787 /*
    788  * Called by the door interface: set a locked LLP and schedule an LLP update if
    789  * the locked LLP has changed.
    790  */
    791 int
    792 set_locked_llp(const char *ifname)
    793 {
    794 	llp_t *llp;
    795 	int retv;
    796 
    797 	if ((retv = pthread_mutex_lock(&llp_lock)) != 0)
    798 		return (retv);
    799 	if (ifname[0] == '\0') {
    800 		if (locked_llp != NULL) {
    801 			ifname = locked_llp->llp_lname;
    802 			locked_llp = NULL;
    803 			(void) np_queue_add_event(EV_USER, ifname);
    804 		}
    805 	} else if ((llp = llp_lookup(ifname)) != NULL) {
    806 		locked_llp = llp;
    807 		if (llp != link_layer_profile)
    808 			(void) np_queue_add_event(EV_USER, ifname);
    809 	} else {
    810 		retv = ENXIO;
    811 	}
    812 	(void) pthread_mutex_unlock(&llp_lock);
    813 	return (retv);
    814 }
    815 
    816 /* Copy string to pre-allocated buffer. */
    817 static void
    818 strinsert(char **dest, const char *src, char **buf)
    819 {
    820 	if (*dest != NULL) {
    821 		*dest = strcpy(*buf, src);
    822 		*buf += strlen(src) + 1;
    823 	}
    824 }
    825 
    826 /*
    827  * Sample the list of LLPs and copy to a single buffer for return through the
    828  * door interface.
    829  */
    830 llp_t *
    831 get_llp_list(size_t *lsize, uint_t *countp, char *selected, char *locked)
    832 {
    833 	llp_t *llplist, *llpl, *llp;
    834 	char *strptr;
    835 	uint_t nllp;
    836 	size_t strspace;
    837 	int retv;
    838 
    839 	*lsize = 0;
    840 	if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
    841 		errno = retv;
    842 		return (NULL);
    843 	}
    844 	(void) strlcpy(selected, link_layer_profile == NULL ? "" :
    845 	    link_layer_profile->llp_lname, LIFNAMSIZ);
    846 	(void) strlcpy(locked, locked_llp == NULL ? "" :
    847 	    locked_llp->llp_lname, LIFNAMSIZ);
    848 	nllp = 0;
    849 	strspace = 0;
    850 	for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
    851 	    llp = (llp_t *)llp->llp_links.q_forw) {
    852 		nllp++;
    853 		if (llp->llp_ipv4addrstr != NULL)
    854 			strspace += strlen(llp->llp_ipv4addrstr) + 1;
    855 		if (llp->llp_ipv6addrstr != NULL)
    856 			strspace += strlen(llp->llp_ipv6addrstr) + 1;
    857 	}
    858 	*countp = nllp;
    859 	/* Note that malloc doesn't guarantee a NULL return for zero count */
    860 	llplist = nllp == 0 ? NULL :
    861 	    malloc(sizeof (*llplist) * nllp + strspace);
    862 	if (llplist != NULL) {
    863 		*lsize = sizeof (*llplist) * nllp + strspace;
    864 		llpl = llplist;
    865 		strptr = (char *)(llplist + nllp);
    866 		for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
    867 		    llp = (llp_t *)llp->llp_links.q_forw) {
    868 			*llpl = *llp;
    869 			strinsert(&llpl->llp_ipv4addrstr, llp->llp_ipv4addrstr,
    870 			    &strptr);
    871 			strinsert(&llpl->llp_ipv6addrstr, llp->llp_ipv6addrstr,
    872 			    &strptr);
    873 			llpl++;
    874 		}
    875 	}
    876 	(void) pthread_mutex_unlock(&llp_lock);
    877 
    878 	/* Add in the special door-only state flags */
    879 	llpl = llplist;
    880 	while (nllp-- > 0) {
    881 		get_interface_state(llpl->llp_lname, &llpl->llp_dhcp_failed,
    882 		    &llpl->llp_link_up);
    883 		if (llpl->llp_type == IF_WIRELESS) {
    884 			get_wireless_state(llpl->llp_lname,
    885 			    &llpl->llp_need_wlan, &llpl->llp_need_key);
    886 		}
    887 		llpl++;
    888 	}
    889 	return (llplist);
    890 }
    891 
    892 /*
    893  * This is called for the special case when there are outstanding requests sent
    894  * to the user interface, and the user interface disappears.  We handle this
    895  * case by re-running bringupinterface() without deselecting.  That function
    896  * will call the wireless and DHCP-related parts again, and they should proceed
    897  * in automatic mode, because the UI is now gone.
    898  *
    899  * Called only by the main thread or by a thread holding machine_lock.
    900  */
    901 void
    902 llp_reselect(void)
    903 {
    904 	llp_t *llp;
    905 	const char *host;
    906 
    907 	/*
    908 	 * If there's no active profile, or if the active profile isn't waiting
    909 	 * on the UI, then just return; nothing to do.
    910 	 */
    911 	if ((llp = link_layer_profile) == NULL || !llp->llp_waiting)
    912 		return;
    913 
    914 	host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? "dhcp" :
    915 	    llp->llp_ipv4addrstr;
    916 
    917 	dprintf("llp_reselect: bringing up %s", llp_prnm(llp));
    918 	switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
    919 	    llp->llp_ipv6onlink)) {
    920 	case SUCCESS:
    921 		llp->llp_failed = B_FALSE;
    922 		llp->llp_waiting = B_FALSE;
    923 		dprintf("llp_reselect: activated llp for %s", llp_prnm(llp));
    924 		break;
    925 	case FAILURE:
    926 		llp->llp_failed = B_TRUE;
    927 		llp->llp_waiting = B_FALSE;
    928 		dprintf("llp_reselect: failed to bring up %s", llp_prnm(llp));
    929 		report_llp_unselected(llp->llp_lname, dcFailed);
    930 		link_layer_profile = NULL;
    931 		break;
    932 	case WAITING:
    933 		llp->llp_failed = B_FALSE;
    934 		dprintf("llp_reselect: waiting for %s", llp_prnm(llp));
    935 	}
    936 }
    937 
    938 /*
    939  * This is used by the wireless module to check on the selected LLP.  We don't
    940  * do periodic rescans if a wireless interface is current and if its connection
    941  * state is good.
    942  */
    943 void
    944 llp_get_name_and_type(char *ifname, size_t ifnlen,
    945     libnwam_interface_type_t *iftype)
    946 {
    947 	*ifname = '\0';
    948 	*iftype = IF_UNKNOWN;
    949 
    950 	if (pthread_mutex_lock(&llp_lock) == 0) {
    951 		if (link_layer_profile != NULL) {
    952 			(void) strlcpy(ifname, link_layer_profile->llp_lname,
    953 			    ifnlen);
    954 			*iftype = link_layer_profile->llp_type;
    955 		}
    956 		(void) pthread_mutex_unlock(&llp_lock);
    957 	}
    958 }
    959 
    960 /*
    961  * This is called by the interface.c module to check if an interface needs to
    962  * run DHCP.  It's intentionally called without ifs_lock held.
    963  */
    964 libnwam_ipv4src_t
    965 llp_get_ipv4src(const char *ifname)
    966 {
    967 	libnwam_ipv4src_t src = IPV4SRC_DHCP;
    968 	llp_t *llp;
    969 
    970 	if (pthread_mutex_lock(&llp_lock) == 0) {
    971 		if ((llp = llp_lookup(ifname)) != NULL)
    972 			src = llp->llp_ipv4src;
    973 		(void) pthread_mutex_unlock(&llp_lock);
    974 	}
    975 	return (src);
    976 }
    977 
    978 /*
    979  * Dump out the LLP state via debug messages.
    980  */
    981 void
    982 print_llp_status(void)
    983 {
    984 	llp_t *llp;
    985 
    986 	if (pthread_mutex_lock(&llp_lock) == 0) {
    987 		if (link_layer_profile == NULL)
    988 			dprintf("no LLP selected");
    989 		else
    990 			dprintf("LLP %s selected",
    991 			    link_layer_profile->llp_lname);
    992 		if (locked_llp == NULL)
    993 			dprintf("no LLP locked");
    994 		else
    995 			dprintf("LLP %s locked", locked_llp->llp_lname);
    996 		for (llp = (llp_t *)llp_list.q_forw;
    997 		    llp != (llp_t *)&llp_list;
    998 		    llp = (llp_t *)llp->llp_links.q_forw) {
    999 			dprintf("LLP %s pri %d file order %d type %d "
   1000 			    "%sfailed %swaiting src %d v4addr %s v6addr %s "
   1001 			    "v6 %son-link",
   1002 			    llp->llp_lname, llp->llp_pri, llp->llp_fileorder,
   1003 			    (int)llp->llp_type, llp->llp_failed ? "" : "not ",
   1004 			    llp->llp_waiting ? "" : "not ",
   1005 			    (int)llp->llp_ipv4src,
   1006 			    STRING(llp->llp_ipv4addrstr),
   1007 			    STRING(llp->llp_ipv6addrstr),
   1008 			    llp->llp_ipv6onlink ? "not " : "");
   1009 		}
   1010 		(void) pthread_mutex_unlock(&llp_lock);
   1011 	}
   1012 }
   1013