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