xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/llp.c (revision 71ed50cf049ab14d8e0ef8d48ba17d91223e81e7)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * This file is here for legacy support.
28  */
29 
30 #include <atomic.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <libdllink.h>
35 #include <libscf.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 
41 #include <libnwam.h>
42 #include "known_wlans.h"
43 #include "llp.h"
44 #include "ncu.h"
45 #include "util.h"
46 
47 /*
48  * This file formerly contained the routines that manipulate Link Layer
49  * Profiles (aka LLPs) and various support functions.  Now only code
50  * necessary for parsing the legacy /etc/nwam/llp file on upgrade is included,
51  * since this legacy configuration needs to be translated into the User NCP.
52  */
53 
54 #define	OUR_OLD_DHCP_WAIT_TIME_PROP_NAME	"dhcp_wait_time"
55 #define	OUR_OLD_USE_NET_SVC_PROP_NAME		"use_net_svc"
56 #define	OUR_OLD_IDLE_TIME_PROP_NAME		"idle_time"
57 
58 static struct qelem llp_list;
59 
60 /*
61  * Global variable to hold the highest priority.  Need to use the atomic
62  * integer arithmetic functions to update it.
63  */
64 static uint32_t llp_highest_pri;
65 
66 /* Specifies if static address has been configured in /etc/nwam/llp */
67 static boolean_t static_configured = B_FALSE;
68 
69 static enum interface_type
find_if_type(const char * name)70 find_if_type(const char *name)
71 {
72 	uint32_t media;
73 	enum interface_type type;
74 
75 	if (name == NULL) {
76 		nlog(LOG_DEBUG, "find_if_type: no ifname; "
77 		    "returning IF_UNKNOWN");
78 		return (IF_UNKNOWN);
79 	}
80 
81 	type = IF_WIRED;
82 	if (dladm_name2info(dld_handle, name, NULL, NULL, NULL, &media) !=
83 	    DLADM_STATUS_OK) {
84 		if (strncmp(name, "ip.tun", 6) == 0 ||
85 		    strncmp(name, "ip6.tun", 7) == 0 ||
86 		    strncmp(name, "ip.6to4tun", 10) == 0)
87 			/*
88 			 * We'll need to update our tunnel detection once
89 			 * the clearview/tun project is integrated; tunnel
90 			 * names won't necessarily be ip.tunN.
91 			 */
92 			type = IF_TUN;
93 	} else if (media == DL_WIFI) {
94 		type = IF_WIRELESS;
95 	}
96 
97 	return (type);
98 }
99 
100 static void
llp_list_free(void)101 llp_list_free(void)
102 {
103 	llp_t *llp;
104 
105 	while (llp_list.q_forw != &llp_list) {
106 		llp = (llp_t *)llp_list.q_forw;
107 		remque(&llp->llp_links);
108 		free(llp->llp_ipv6addrstr);
109 		free(llp->llp_ipv4addrstr);
110 		free(llp);
111 	}
112 }
113 
114 static void
initialize_llp(void)115 initialize_llp(void)
116 {
117 	llp_list.q_forw = llp_list.q_back = &llp_list;
118 }
119 
120 static llp_t *
llp_lookup(const char * link)121 llp_lookup(const char *link)
122 {
123 	llp_t *llp;
124 
125 	if (link == NULL)
126 		return (NULL);
127 
128 	for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
129 	    llp = (llp_t *)llp->llp_links.q_forw) {
130 		if (strcmp(link, llp->llp_lname) == 0)
131 			break;
132 	}
133 	if (llp == (llp_t *)&llp_list)
134 		llp = NULL;
135 	return (llp);
136 }
137 
138 /*
139  * Create the named LLP with default settings.  Called only in main thread.
140  */
141 static llp_t *
llp_add(const char * name)142 llp_add(const char *name)
143 {
144 	llp_t *llp;
145 
146 	if ((llp = calloc(1, sizeof (llp_t))) == NULL) {
147 		nlog(LOG_ERR, "llp_add: cannot allocate LLP: %m");
148 		return (NULL);
149 	}
150 
151 	if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >=
152 	    sizeof (llp->llp_lname)) {
153 		nlog(LOG_ERR, "llp_add: linkname '%s' too long; ignoring entry",
154 		    name);
155 		free(llp);
156 		return (NULL);
157 	}
158 
159 	llp->llp_fileorder = llp->llp_pri =
160 	    atomic_add_32_nv(&llp_highest_pri, 1);
161 	llp->llp_ipv4src = IPV4SRC_DHCP;
162 	llp->llp_type = find_if_type(llp->llp_lname);
163 	llp->llp_ipv6onlink = B_TRUE;
164 
165 	/*
166 	 * should be a no-op, but for now, make sure we only
167 	 * create llps for wired and wireless interfaces.
168 	 */
169 	if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) {
170 		nlog(LOG_ERR, "llp_add: wrong type of interface for %s", name);
171 		free(llp);
172 		return (NULL);
173 	}
174 	insque(&llp->llp_links, llp_list.q_back);
175 
176 	nlog(LOG_DEBUG, "llp_add: "
177 	    "created llp for link %s, priority %d", llp->llp_lname,
178 	    llp->llp_pri);
179 	return (llp);
180 }
181 
182 static int
parse_llp_config(void)183 parse_llp_config(void)
184 {
185 	static const char STATICSTR[] = "static";
186 	static const char DHCP[] = "dhcp";
187 	static const char IPV6[] = "ipv6";
188 	static const char NOIPV6[] = "noipv6";
189 	static const char PRIORITY[] = "priority";
190 	FILE *fp;
191 	char line[LINE_MAX];
192 	char *cp, *lasts, *lstr, *srcstr, *addrstr;
193 	int lnum;
194 	llp_t *llp;
195 
196 	initialize_llp();
197 
198 	fp = fopen(LLPFILE, "r+");
199 	if (fp == NULL) {
200 		if (errno == ENOENT)
201 			return (errno);
202 		nlog(LOG_ERR, "parse_llp_config: "
203 		    "open legacy LLP config file: %m");
204 		return (-1);
205 	}
206 
207 	for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
208 		if (line[strlen(line) - 1] == '\n')
209 			line[strlen(line) - 1] = '\0';
210 
211 		cp = line;
212 		while (isspace(*cp))
213 			cp++;
214 
215 		if (*cp == '#' || *cp == '\0')
216 			continue;
217 
218 		nlog(LOG_DEBUG, "parse_llp_config: "
219 		    "parsing legacy LLP conf file line %d...", lnum);
220 
221 		if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
222 		    ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
223 			nlog(LOG_ERR, "parse_llp_config: line %d: "
224 			    "not enough tokens; ignoring entry", lnum);
225 			continue;
226 		}
227 
228 		if ((llp = llp_lookup(lstr)) == NULL &&
229 		    (llp = llp_add(lstr)) == NULL) {
230 			nlog(LOG_ERR, "parse_llp_config: line %d: "
231 			    "cannot add entry", lnum);
232 			continue;
233 		}
234 
235 		if (strcasecmp(srcstr, STATICSTR) == 0) {
236 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL ||
237 			    atoi(addrstr) == 0) { /* crude check for number */
238 				nlog(LOG_ERR, "parse_llp_config: line %d: "
239 				    "missing ipaddr for static config", lnum);
240 			} else if ((addrstr = strdup(addrstr)) == NULL) {
241 				nlog(LOG_ERR, "parse_llp_config: line %d: "
242 				    "cannot save address", lnum);
243 			} else {
244 				free(llp->llp_ipv4addrstr);
245 				llp->llp_ipv4src = IPV4SRC_STATIC;
246 				llp->llp_ipv4addrstr = addrstr;
247 			}
248 
249 		} else if (strcasecmp(srcstr, DHCP) == 0) {
250 			llp->llp_ipv4src = IPV4SRC_DHCP;
251 
252 		} else if (strcasecmp(srcstr, IPV6) == 0) {
253 			llp->llp_ipv6onlink = B_TRUE;
254 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
255 				(void) 0;
256 			} else if ((addrstr = strdup(addrstr)) == NULL) {
257 				nlog(LOG_ERR, "parse_llp_config: line %d: "
258 				    "cannot save address", lnum);
259 			} else {
260 				free(llp->llp_ipv6addrstr);
261 				llp->llp_ipv6addrstr = addrstr;
262 			}
263 
264 		} else if (strcasecmp(srcstr, NOIPV6) == 0) {
265 			llp->llp_ipv6onlink = B_FALSE;
266 
267 		} else if (strcasecmp(srcstr, PRIORITY) == 0) {
268 			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
269 				nlog(LOG_ERR,
270 				    "parse_llp_config: line %d: "
271 				    "missing priority value", lnum);
272 			} else {
273 				llp->llp_pri = atoi(addrstr);
274 			}
275 
276 		} else {
277 			nlog(LOG_ERR, "parse_llp_config: line %d: "
278 			    "unrecognized field '%s'", lnum, srcstr);
279 		}
280 	}
281 
282 	(void) fclose(fp);
283 	return (0);
284 }
285 
286 /*
287  * Translate legacy LLP config into the user NCP.
288  */
289 static int
upgrade_llp_config(void)290 upgrade_llp_config(void)
291 {
292 	llp_t *wp;
293 	nwam_ncp_handle_t user_ncp;
294 	nwam_ncu_handle_t phys_ncu = NULL, ip_ncu = NULL;
295 	nwam_error_t err;
296 	uint64_t uintval;
297 	char *strval;
298 	const char *prop;
299 
300 	switch (parse_llp_config()) {
301 	case -1:
302 		return (0);
303 	case ENOENT:
304 		return (ENOENT);
305 	default:
306 		break;
307 	}
308 
309 	err = nwam_ncp_create(NWAM_NCP_NAME_USER, 0, &user_ncp);
310 	switch (err) {
311 	case NWAM_SUCCESS:
312 		break;
313 	case NWAM_ERROR_BIND:
314 	case NWAM_ERROR_INTERNAL:
315 		nlog(LOG_ERR, "upgrade_llp_config: "
316 		    "could not create User NCP: %s", nwam_strerror(err));
317 		llp_list_free();
318 		return (EAGAIN);
319 	default:
320 		nlog(LOG_ERR, "upgrade_llp_config: error creating User NCP: %s",
321 		    nwam_strerror(err));
322 		llp_list_free();
323 		return (0);
324 	}
325 
326 	nlog(LOG_DEBUG, "upgrade_llp_config: walking llp list");
327 
328 	for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list;
329 	    wp = (llp_t *)wp->llp_links.q_forw) {
330 
331 		nlog(LOG_DEBUG, "upgrade_llp_config: "
332 		    "upgrading llp %s", wp->llp_lname);
333 
334 		if (nwam_ncu_create(user_ncp, wp->llp_lname,
335 		    NWAM_NCU_TYPE_INTERFACE, NWAM_NCU_CLASS_IP, &ip_ncu)
336 		    != NWAM_SUCCESS ||
337 		    nwam_ncu_create(user_ncp, wp->llp_lname, NWAM_NCU_TYPE_LINK,
338 		    NWAM_NCU_CLASS_PHYS, &phys_ncu) != NWAM_SUCCESS) {
339 			nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
340 			    "could not create NCUs: %s", wp->llp_lname,
341 			    nwam_strerror(err));
342 			break;
343 		}
344 
345 		/* Link NCU properties */
346 		prop = NWAM_NCU_PROP_ACTIVATION_MODE;
347 		uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
348 		if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
349 		    != NWAM_SUCCESS)
350 			break;
351 
352 		prop = NWAM_NCU_PROP_PRIORITY_MODE;
353 		uintval = NWAM_PRIORITY_MODE_EXCLUSIVE;
354 		if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
355 		    != NWAM_SUCCESS)
356 			break;
357 
358 		prop = NWAM_NCU_PROP_PRIORITY_GROUP;
359 		uintval = wp->llp_pri;
360 		if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
361 		    != NWAM_SUCCESS)
362 			break;
363 
364 		/* IP NCU properties */
365 		if (wp->llp_ipv4addrstr != NULL) {
366 			/* Set v4 address and specify static addrsrc */
367 			prop = NWAM_NCU_PROP_IPV4_ADDRSRC;
368 			uintval = NWAM_ADDRSRC_STATIC;
369 			if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
370 			    prop)) != NWAM_SUCCESS)
371 				break;
372 
373 			prop = NWAM_NCU_PROP_IPV4_ADDR;
374 			strval = wp->llp_ipv4addrstr;
375 			if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
376 			    prop)) != NWAM_SUCCESS)
377 				break;
378 
379 			static_configured = B_TRUE;
380 		}
381 
382 		if (wp->llp_ipv6addrstr != NULL) {
383 			/* Set v6 address and specify static addrsrc */
384 			prop = NWAM_NCU_PROP_IPV6_ADDRSRC;
385 			uintval = NWAM_ADDRSRC_STATIC;
386 			if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
387 			    prop)) != NWAM_SUCCESS)
388 				break;
389 
390 			prop = NWAM_NCU_PROP_IPV6_ADDR;
391 			strval = wp->llp_ipv6addrstr;
392 			if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
393 			    prop)) != NWAM_SUCCESS)
394 				break;
395 
396 			static_configured = B_TRUE;
397 		}
398 
399 		if (!wp->llp_ipv6onlink) {
400 			prop = NWAM_NCU_PROP_IP_VERSION;
401 			uintval = IPV4_VERSION;
402 			if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
403 			    prop)) != NWAM_SUCCESS)
404 				break;
405 		}
406 
407 		if ((err = nwam_ncu_commit(ip_ncu, 0)) != NWAM_SUCCESS ||
408 		    (err = nwam_ncu_commit(phys_ncu, 0)) != NWAM_SUCCESS) {
409 			nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
410 			    "could not commit NCUs: %s", wp->llp_lname,
411 			    nwam_strerror(err));
412 			/* Schedule a retry - root filesystem may be readonly */
413 			llp_list_free();
414 			nwam_ncu_free(ip_ncu);
415 			nwam_ncu_free(phys_ncu);
416 			(void) nwam_ncp_destroy(user_ncp, 0);
417 			return (EAGAIN);
418 		}
419 	}
420 
421 	if (err != NWAM_SUCCESS) {
422 		nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
423 		    "could not set value for property %s: %s", wp->llp_lname,
424 		    prop, nwam_strerror(err));
425 	}
426 	llp_list_free();
427 	nwam_ncu_free(ip_ncu);
428 	nwam_ncu_free(phys_ncu);
429 	nwam_ncp_free(user_ncp);
430 	return (0);
431 }
432 
433 /*
434  * Upgrade legacy llp and known_wifi_nets files. Note - it is possible that
435  * the root filesystem is not writable at this point, so we need to schedule
436  * a retry of the upgrade operation in the event that committing the new
437  * config fails.
438  */
439 /* ARGSUSED0 */
440 void
nwamd_handle_upgrade(nwamd_event_t event)441 nwamd_handle_upgrade(nwamd_event_t event)
442 {
443 	nwamd_event_t upgrade_event;
444 	uint64_t dhcp_wait_time, idle_time;
445 	boolean_t use_net_svc;
446 
447 	switch (upgrade_llp_config()) {
448 	case -1:
449 	case ENOENT:
450 		/* Nothing readable to upgrade */
451 		break;
452 	case EAGAIN:
453 		/*
454 		 * Schedule retry in NWAMD_READONLY_RETRY_INTERVAL seconds
455 		 * as root fs may be readonly.
456 		 *
457 		 * The upgrade event is of type NCU, but has no associated
458 		 * object (we use the event type to map to the appropriate
459 		 * event/method mappings, so to find the NCU upgrade event
460 		 * method we specify type NCU while not specifying an
461 		 * object since all NCUs have to be upgraded.
462 		 */
463 		upgrade_event = nwamd_event_init(NWAM_EVENT_TYPE_UPGRADE,
464 		    NWAM_OBJECT_TYPE_NCP, 0, NULL);
465 		if (upgrade_event == NULL) {
466 			nlog(LOG_ERR, "nwamd_handle_upgrade: "
467 			    "could not create retry event to upgrade "
468 			    "%s configuration", LLPFILE);
469 			return;
470 		}
471 		nwamd_event_enqueue_timed(upgrade_event,
472 		    NWAMD_READONLY_RETRY_INTERVAL);
473 		return;
474 	default:
475 		break;
476 	}
477 
478 	/*
479 	 * If static_configured is set, then at least one static address is
480 	 * configured in /etc/nwam/llp.  Enable the User NCP in this case.
481 	 */
482 	if (static_configured) {
483 		nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
484 		    "static address configured, enabling User NCP");
485 		(void) pthread_mutex_lock(&active_ncp_mutex);
486 		(void) strlcpy(active_ncp, NWAM_NCP_NAME_USER,
487 		    NWAM_MAX_NAME_LEN);
488 		(void) pthread_mutex_unlock(&active_ncp_mutex);
489 	}
490 
491 	/* upgrade /etc/nwam/known_wifi_nets */
492 	upgrade_known_wifi_nets_config();
493 
494 	/*
495 	 * SMF property nwamd/dhcp_wait_time in Phase 0/0.5 has been
496 	 * replaced by nwamd/ncu_wait_time property.  If the dhcp_wait_time
497 	 * property exists (which means it has been changed by the user),
498 	 * set its value to ncu_wait_time and remove the property.
499 	 */
500 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
501 	    OUR_OLD_DHCP_WAIT_TIME_PROP_NAME, &dhcp_wait_time) == 0) {
502 		(void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
503 		    OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
504 		(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
505 		    OUR_OLD_DHCP_WAIT_TIME_PROP_NAME);
506 		nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
507 		    "converted '%s' to '%s' with value of %lld",
508 		    OUR_OLD_DHCP_WAIT_TIME_PROP_NAME,
509 		    OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
510 	}
511 
512 	/*
513 	 * If the user has changed Phase 0/0.5 properties that don't exist in
514 	 * Phase 1, manifest-import reports a warning; but those properties are
515 	 * not removed.  nwamd/use_net_svc and nwamd/idle_time are two
516 	 * properties that don't exist in Phase 1.  If they exist, remove them.
517 	 */
518 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
519 	    OUR_OLD_IDLE_TIME_PROP_NAME, &idle_time) == 0) {
520 		(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
521 		    OUR_OLD_IDLE_TIME_PROP_NAME);
522 	}
523 	if (nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
524 	    OUR_OLD_USE_NET_SVC_PROP_NAME, &use_net_svc) == 0) {
525 		(void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
526 		    OUR_OLD_USE_NET_SVC_PROP_NAME);
527 	}
528 
529 	nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
530 	    "creating version property, setting to 1\n");
531 	(void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
532 	    OUR_VERSION_PROP_NAME, 1U);
533 	(void) smf_refresh_instance(OUR_FMRI);
534 }
535