xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/llp.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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