xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/llp.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the routines that manipulate Link Layer Profiles
31  * (aka LLPs) and various support functions.  This includes parsing and
32  * updating of the /etc/nwam/llp file.
33  *
34  * The daemon maintains a list of llp_t structures that represent the
35  * provided configuration information for a link.  After the llp file
36  * is read, entries are added to the LLP list for any known links
37  * (identified by checking the interface list, which is based on the
38  * v4 interfaces present after 'ifconfig -a plumb') which were not
39  * represented in the llp file.  These entries contain the default
40  * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the
41  * v4 interface, and accept router- and DHCPv6-assigned addresses on
42  * the v6 interface.  The default entries created by the daemon are
43  * also added to the llp file.
44  *
45  * LLP priority is assigned based on two factors: the order within
46  * the llp file, with earlier entries having higher priority; and
47  * a preference for wired interfaces before wireless.  Entries that
48  * are added to the file by the daemon are added *after* any existing
49  * entries; within the added block, wired entries are added before
50  * wireless.  Thus if the llp file is never modified externally, wired
51  * will generally be ordered before wireless.  However, if the
52  * administrator creates the file with wireless entries before wired,
53  * that priority order will be respected.
54  *
55  * The llp list (pointed to by the global llp_head) is protected by
56  * the global llp_lock, which should be pthread_mutex_lock()'d before
57  * reading or writing the list.
58  */
59 
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <limits.h>
63 #include <strings.h>
64 #include <string.h>
65 #include <ctype.h>
66 #include <errno.h>
67 #include <assert.h>
68 #include <sys/types.h>
69 #include <sys/stat.h>
70 #include <syslog.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
73 #include <atomic.h>
74 #include <pthread.h>
75 #include <signal.h>
76 
77 #include "defines.h"
78 #include "structures.h"
79 #include "functions.h"
80 #include "variables.h"
81 
82 /* Lock to protect the llp list. */
83 static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER;
84 
85 llp_t *llp_head = NULL;
86 llp_t *link_layer_profile = NULL;
87 
88 /*
89  * Global variable to hold the highest priority.  Need to use the atomic
90  * integer arithmetic functions to update it.
91  */
92 static uint32_t llp_highest_pri = 0;
93 
94 static void print_llp_list(void);
95 
96 char *
97 llp_prnm(llp_t *llp)
98 {
99 	if (llp == NULL)
100 		return ("null_llp");
101 	else if (llp->llp_lname == NULL)
102 		return ("null_lname");
103 	else
104 		return (llp->llp_lname);
105 }
106 
107 static void
108 llp_list_free(llp_t *head)
109 {
110 	llp_t **llpp;
111 	llp_t *llpfree;
112 
113 	if (pthread_mutex_lock(&llp_lock) != 0) {
114 		/* Something very serious is wrong... */
115 		syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %m");
116 		return;
117 	}
118 	llpp = &head;
119 	while (*llpp != NULL) {
120 		llpfree = *llpp;
121 		*llpp = llpfree->llp_next;
122 		free(llpfree->llp_ipv4addrstr);
123 		free(llpfree);
124 	}
125 	(void) pthread_mutex_unlock(&llp_lock);
126 }
127 
128 llp_t *
129 llp_lookup(const char *link)
130 {
131 	llp_t *llp;
132 
133 	if (link == NULL)
134 		return (NULL);
135 
136 	/* The name may change.  Better hold the lock. */
137 	if (pthread_mutex_lock(&llp_lock) != 0) {
138 		/* Something very serious is wrong... */
139 		syslog(LOG_ERR, "llp_lookup: cannot lock mutex: %m");
140 		return (NULL);
141 	}
142 	for (llp = llp_head; llp != NULL; llp = llp->llp_next) {
143 		if (strcmp(link, llp->llp_lname) == 0)
144 			break;
145 	}
146 	(void) pthread_mutex_unlock(&llp_lock);
147 	return (llp);
148 }
149 
150 /*
151  * Choose the higher priority llp of the two passed in.  If one is
152  * NULL, the other will be higher priority.  If both are NULL, NULL
153  * is returned.
154  *
155  * Assumes that both are available (i.e. doesn't check IFF_RUNNING
156  * or IF_DHCPFAILED flag values).
157  */
158 llp_t *
159 llp_high_pri(llp_t *a, llp_t *b)
160 {
161 	if (a == NULL)
162 		return (b);
163 	else if (b == NULL)
164 		return (a);
165 
166 	/*
167 	 * Higher priority is represented by a lower number.  This seems a
168 	 * bit backwards, but for now it makes assigning priorities very easy.
169 	 *
170 	 * We shouldn't have ties right now, but just in case, tie goes to a.
171 	 */
172 	return ((a->llp_pri <= b->llp_pri) ? a : b);
173 }
174 
175 /*
176  * Chooses the highest priority link that corresponds to an
177  * available interface.
178  */
179 llp_t *
180 llp_best_avail(void)
181 {
182 	llp_t *p, *rtnllp = NULL;
183 	struct interface *ifp;
184 
185 	/* The priority may change.  Better hold the lock. */
186 	if (pthread_mutex_lock(&llp_lock) != 0) {
187 		/* Something very serious is wrong... */
188 		syslog(LOG_ERR, "llp_best_avail: cannot lock mutex: %m");
189 		return (NULL);
190 	}
191 	for (p = llp_head; p != NULL; p = p->llp_next) {
192 		ifp = get_interface(p->llp_lname);
193 		if (ifp == NULL || !is_plugged_in(ifp) ||
194 		    (ifp->if_lflags & IF_DHCPFAILED) != 0)
195 			continue;
196 		rtnllp = llp_high_pri(p, rtnllp);
197 	}
198 	(void) pthread_mutex_unlock(&llp_lock);
199 
200 	return (rtnllp);
201 }
202 
203 /*
204  * Returns B_TRUE if llp is successfully activated;
205  * B_FALSE if activation fails.
206  */
207 boolean_t
208 llp_activate(llp_t *llp)
209 {
210 	boolean_t rtn;
211 	char *host;
212 	/*
213 	 * Choosing "dhcp" as a hostname is unsupported right now.
214 	 * We use hostname="dhcp" as a keyword telling bringupinterface()
215 	 * to use dhcp on the interface.
216 	 */
217 	char *dhcpstr = "dhcp";
218 
219 	llp_deactivate();
220 
221 	host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr :
222 	    llp->llp_ipv4addrstr;
223 
224 	if (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
225 	    llp->llp_ipv6onlink)) {
226 		link_layer_profile = llp;
227 		dprintf("llp_activate: activated llp for %s", llp_prnm(llp));
228 		rtn = B_TRUE;
229 	} else {
230 		dprintf("llp_activate: failed to bringup %s", llp_prnm(llp));
231 		link_layer_profile = NULL;
232 		rtn = B_FALSE;
233 	}
234 
235 	return (rtn);
236 }
237 
238 /*
239  * Deactivate the current active llp (link_layer_profile)
240  */
241 void
242 llp_deactivate(void)
243 {
244 	if (link_layer_profile == NULL)
245 		return;
246 
247 	takedowninterface(link_layer_profile->llp_lname, B_TRUE,
248 	    link_layer_profile->llp_ipv6onlink);
249 
250 	dprintf("llp_deactivate: setting link_layer_profile(%p) to NULL",
251 	    (void *)link_layer_profile);
252 	link_layer_profile = NULL;
253 }
254 
255 /*
256  * Replace the currently active link layer profile with the one
257  * specified.  And since we're changing the lower layer stuff,
258  * we need to first deactivate the current upper layer profile.
259  * An upper layer profile will be reactivated later, when we get
260  * confirmation that the new llp is fully up (has an address
261  * assigned).
262  *
263  * If the new llp is the same as the currently active one, don't
264  * do anything.
265  *
266  * If the new llp is NULL, just take down the currently active one.
267  */
268 void
269 llp_swap(llp_t *newllp)
270 {
271 	char *upifname;
272 
273 	if (newllp == link_layer_profile)
274 		return;
275 
276 	deactivate_upper_layer_profile();
277 
278 	if (link_layer_profile == NULL) {
279 		/*
280 		 * there shouldn't be anything else running;
281 		 * make sure that's the case!
282 		 */
283 		upifname = (newllp == NULL) ? NULL : newllp->llp_lname;
284 		take_down_all_ifs(upifname);
285 	} else {
286 		dprintf("taking down current link layer profile (%s)",
287 		    llp_prnm(link_layer_profile));
288 		llp_deactivate();
289 	}
290 	if (newllp != NULL) {
291 		dprintf("bringing up new link layer profile (%s)",
292 		    llp_prnm(newllp));
293 		(void) llp_activate(newllp);
294 	}
295 }
296 
297 /*
298  *
299  * ifp->if_family == AF_INET, addr_src == DHCP ==> addr == NULL
300  * ifp->if_family == AF_INET, addr_src == STATIC ==> addr non null sockaddr_in
301  * ifp->if_family == AF_INET6, ipv6onlink == FALSE ==> addr == NULL
302  * ifp->if_family == AF_INET6, ipv6onlink == TRUE,
303  *     if addr non NULL then it is the textual representation of the address
304  *     and prefix.
305  *
306  * The above set of conditions describe what the inputs to this fuction are
307  * expected to be.  Given input which meets those conditions this functions
308  * then outputs a line of configuration describing the inputs.
309  *
310  * Note that it is assumed only one thread can call this function at
311  * any time.  So there is no lock to protect the file writing.  This
312  * is true as the only caller of this function should originate from
313  * llp_parse_config(), which is done at program initialization time.
314  */
315 static void
316 add_if_file(FILE *fp, struct interface *ifp, ipv4src_t addr_src,
317     boolean_t ipv6onlink, void *addr)
318 {
319 	char addr_buf[INET6_ADDRSTRLEN];
320 
321 	switch (ifp->if_family) {
322 	case AF_INET:
323 		switch (addr_src) {
324 		case IPV4SRC_STATIC:
325 			/* This is not supposed to happen... */
326 			if (addr == NULL) {
327 				(void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
328 				break;
329 			}
330 			(void) inet_ntop(AF_INET, addr, addr_buf,
331 			    INET6_ADDRSTRLEN);
332 			(void) fprintf(fp, "%s\tstatic\t%s\n", ifp->if_name,
333 			    addr_buf);
334 			break;
335 		case IPV4SRC_DHCP:
336 			/* Default is DHCP for now. */
337 		default:
338 			(void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
339 			break;
340 		}
341 		break;
342 
343 	case AF_INET6:
344 		if (ipv6onlink)
345 			(void) fprintf(fp, "%s\tipv6\n", ifp->if_name);
346 		break;
347 
348 	default:
349 		syslog(LOG_ERR, "interface %s of type %d?!", ifp->if_name,
350 		    ifp->if_family);
351 		break;
352 	}
353 }
354 
355 /*
356  * Walker function to pass to walk_interface() to add a default
357  * interface description to the LLPFILE.
358  *
359  * Regarding IF_TUN interfaces: see comments before find_and_add_llp()
360  * for an explanation of why we skip them.
361  */
362 static void
363 add_if_default(struct interface *ifp, void *arg)
364 {
365 	FILE *fp = (FILE *)arg;
366 
367 	if (ifp->if_type != IF_TUN)
368 		add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL);
369 }
370 
371 /* Create the LLPFILE using info from the interface list. */
372 static void
373 create_llp_file(void)
374 {
375 	FILE *fp;
376 	int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
377 
378 	/* Create the NWAM directory in case it does not exist. */
379 	if (mkdir(LLPDIR, dirmode) != 0) {
380 		if (errno != EEXIST) {
381 			syslog(LOG_ERR, "create NWAM directory: %m");
382 			return;
383 		}
384 	}
385 	if ((fp = fopen(LLPFILE, "w")) == NULL) {
386 		syslog(LOG_ERR, "create LLP config file: %m");
387 		return;
388 	}
389 	syslog(LOG_INFO, "Creating %s", LLPFILE);
390 	walk_interface(add_if_default, fp);
391 	(void) fclose(fp);
392 }
393 
394 /*
395  * Append an llp struct to the end of the llp list.
396  */
397 static void
398 llp_list_append(llp_t *llp)
399 {
400 	llp_t **wpp = &llp_head;
401 
402 	/*
403 	 * should be a no-op, but for now, make sure we only
404 	 * create llps for wired and wireless interfaces.
405 	 */
406 	if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS)
407 		return;
408 
409 	if (pthread_mutex_lock(&llp_lock) != 0) {
410 		/* Something very serious is wrong... */
411 		syslog(LOG_ERR, "llp_list_append: cannot lock mutex: %m");
412 		return;
413 	}
414 
415 	while (*wpp != NULL)
416 		wpp = &(*wpp)->llp_next;
417 	*wpp = llp;
418 	llp->llp_next = NULL;
419 
420 	(void) pthread_mutex_unlock(&llp_lock);
421 }
422 
423 /*
424  * Create a llp given the parameters and add it to the global list.
425  */
426 static void
427 create_and_add_llp(const char *name, ipv4src_t ipv4src, const char *addrstr,
428     boolean_t ipv6onlink, const char *ipv6addrstr)
429 {
430 	llp_t *newllp;
431 	int lnamelen;
432 
433 	if ((newllp = llp_lookup(name)) != NULL) {
434 		if (ipv6addrstr != NULL) {
435 			newllp->llp_ipv6addrstr = strdup(ipv6addrstr);
436 			if (newllp->llp_ipv6addrstr == NULL) {
437 				syslog(LOG_ERR, "could not save ipv6 static "
438 				    "address for %s", name);
439 			}
440 		}
441 		newllp->llp_ipv6onlink = ipv6onlink;
442 		return;
443 	} else if ((newllp = calloc(1, sizeof (llp_t))) == NULL) {
444 		syslog(LOG_ERR, "calloc llp: %m");
445 		return;
446 	}
447 
448 	lnamelen = sizeof (newllp->llp_lname);
449 	if (strlcpy(newllp->llp_lname, name, lnamelen) >= lnamelen) {
450 		syslog(LOG_ERR, "llp: link name too long; ignoring entry");
451 		free(newllp);
452 		return;
453 	}
454 	if (ipv4src == IPV4SRC_STATIC) {
455 		if ((newllp->llp_ipv4addrstr = strdup(addrstr)) == NULL) {
456 			syslog(LOG_ERR, "malloc ipaddrstr: %m");
457 			free(newllp);
458 			return;
459 		}
460 	} else {
461 		newllp->llp_ipv4addrstr = NULL;
462 	}
463 	newllp->llp_next = NULL;
464 	newllp->llp_pri = atomic_add_32_nv(&llp_highest_pri, 1);
465 	newllp->llp_ipv4src = ipv4src;
466 	newllp->llp_type = find_if_type(newllp->llp_lname);
467 	newllp->llp_ipv6onlink = ipv6onlink;
468 
469 	if (ipv6onlink && ipv6addrstr != NULL) {
470 		newllp->llp_ipv6addrstr = strdup(ipv6addrstr);
471 		if (newllp->llp_ipv6addrstr == NULL)
472 			syslog(LOG_WARNING, "could not store static address %s"
473 			    "on interface %s", ipv6addrstr, newllp->llp_lname);
474 	} else {
475 		newllp->llp_ipv6addrstr = NULL;
476 	}
477 
478 	llp_list_append(newllp);
479 
480 	dprintf("created llp for link %s, pri %d", newllp->llp_lname,
481 	    newllp->llp_pri);
482 }
483 
484 /*
485  * Walker function to pass to walk_interface() to find out if
486  * an interface description is missing from LLPFILE.  If it is,
487  * add it.
488  *
489  * Currently, IF_TUN type interfaces are special-cased: they are
490  * only handled as user-enabled, layered links (which may be created
491  * as part of a higher-layer profile, for example).  Thus, they
492  * shouldn't be considered when looking at the llp list, so don't
493  * add them here.
494  */
495 static void
496 find_and_add_llp(struct interface *ifp, void *arg)
497 {
498 	FILE *fp = (FILE *)arg;
499 
500 	if (ifp->if_type != IF_TUN && (llp_lookup(ifp->if_name) == NULL)) {
501 		dprintf("Adding %s to %s", ifp->if_name, LLPFILE);
502 		add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL);
503 		/* If we run out of memory, ignore this interface for now. */
504 		create_and_add_llp(ifp->if_name, IPV4SRC_DHCP, NULL,
505 		    B_TRUE, NULL);
506 	}
507 }
508 
509 /*
510  * This is a very "slow" function.  It uses walk_interface() to find
511  * out if any of the interface is missing from the LLPFILE.  For the
512  * missing ones, add them to the LLPFILE.
513  */
514 static void
515 add_missing_if_llp(FILE *fp)
516 {
517 	walk_interface(find_and_add_llp, fp);
518 }
519 
520 static void
521 print_llp_list(void)
522 {
523 	llp_t *wp;
524 
525 	dprintf("Walking llp list");
526 	for (wp = llp_head; wp != NULL; wp = wp->llp_next)
527 		dprintf("==> %s", wp->llp_lname);
528 }
529 
530 /*
531  * This function parses /etc/nwam/llp which describes the phase 0 link layer
532  * profile.  The file is line oriented with each line containing tab or space
533  * delimited fields.  Each address family (IPv4, IPv6) is described on a
534  * separate line.
535  * The first field is a link name.
536  * The second field can be either static, dhcp, ipv6, or noipv6.
537  * If the second field is static then the next field is an ipv4 address which
538  *    can contain a prefix.  Previous versions of this file could contain a
539  *    hostname in this field which is no longer supported.
540  * If the second field is dhcp then dhcp will be used on the interface.
541  * If the second field is ipv6 then an ipv6 interface is plumbed up.  The
542  *    outcome of this is that if offered by the network in.ndpd and dhcp
543  *    will conspire to put addresses on additional ipv6 logical interfaces.
544  *    If the next field is non-null then it is taken to be an IPv6 address
545  *    and possible prefix which are applied to the interface.
546  * If the second field is noipv6 then no ipv6 interfaces will be put on that
547  *    link.
548  */
549 void
550 llp_parse_config(void)
551 {
552 	static const char STATICSTR[] = "static";
553 	static const char DHCP[] = "dhcp";
554 	static const char IPV6[] = "ipv6";
555 	static const char NOIPV6[] = "noipv6";
556 	FILE *fp;
557 	char line[LINE_MAX];
558 	char *cp, *lasts, *lstr, *srcstr, *addrstr, *v6addrstr;
559 	int lnum;
560 	ipv4src_t ipv4src;
561 	boolean_t ipv6onlink;
562 
563 	fp = fopen(LLPFILE, "r+");
564 	if (fp == NULL) {
565 		if (errno != ENOENT) {
566 			/*
567 			 * XXX See comment before create_llp_file() re
568 			 * better error handling.
569 			 */
570 			syslog(LOG_ERR, "open LLP config file: %m");
571 			return;
572 		}
573 
574 		/*
575 		 * If there is none, we should create one instead.
576 		 * For now, we will use the order of the interface list
577 		 * for the priority.  We should have a priority field
578 		 * in the llp file eventually...
579 		 */
580 		create_llp_file();
581 
582 		/* Now we can try to reopen the file for processing. */
583 		fp = fopen(LLPFILE, "r+");
584 		if (fp == NULL) {
585 			syslog(LOG_ERR, "2nd open LLP config file: %m");
586 			return;
587 		}
588 	}
589 
590 	if (llp_head != NULL)
591 		llp_list_free(llp_head);
592 	llp_head = NULL;
593 
594 	for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
595 		ipv4src = IPV4SRC_DHCP;
596 		ipv6onlink = B_FALSE;
597 		addrstr = NULL;
598 		v6addrstr = NULL;
599 
600 		if (line[strlen(line) - 1] == '\n')
601 			line[strlen(line) - 1] = '\0';
602 
603 		cp = line;
604 		while (isspace(*cp))
605 			cp++;
606 
607 		if (*cp == '#' || *cp == '\0')
608 			continue;
609 
610 		dprintf("parsing llp conf file line %d...", lnum);
611 
612 		if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
613 		    ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
614 			syslog(LOG_ERR, "llp:%d: not enough tokens; "
615 			    "ignoring entry", lnum);
616 			continue;
617 		}
618 		if (strcasecmp(srcstr, STATICSTR) == 0) {
619 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL ||
620 			    atoi(addrstr) == 0) { /* crude check for number */
621 				syslog(LOG_ERR, "llp:%d: missing ipaddr "
622 				    "for static config; ignoring entry",
623 				    lnum);
624 				continue;
625 			}
626 			ipv4src = IPV4SRC_STATIC;
627 		} else if (strcasecmp(srcstr, DHCP) == 0) {
628 			ipv4src = IPV4SRC_DHCP;
629 		} else if (strcasecmp(srcstr, IPV6) == 0) {
630 			ipv6onlink = B_TRUE;
631 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) != NULL) {
632 				v6addrstr = strdup(addrstr);
633 				if (v6addrstr == NULL) {
634 					syslog(LOG_ERR, "could not store v6 "
635 					    "static address %s for %s",
636 					    v6addrstr, lstr);
637 				}
638 			} else {
639 				v6addrstr = NULL;
640 			}
641 		} else if (strcasecmp(srcstr, NOIPV6) == 0) {
642 			ipv6onlink = B_FALSE;
643 		} else {
644 			syslog(LOG_ERR, "llp:%d: unrecognized "
645 			    "field; ignoring entry", lnum);
646 			continue;
647 		}
648 
649 		create_and_add_llp(lstr, ipv4src, addrstr, ipv6onlink,
650 		    v6addrstr);
651 	}
652 
653 	/*
654 	 * So we have read in the llp file, is there an interface which
655 	 * it does not describe?  If yes, we'd better add it to the
656 	 * file for future reference.  Again, since we don't have a
657 	 * priority field yet, we will add the interface in the order
658 	 * in the interface list.
659 	 */
660 	add_missing_if_llp(fp);
661 
662 	(void) fclose(fp);
663 
664 	print_llp_list();
665 }
666