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 #include <assert.h>
28 #include <ctype.h>
29 #include <libgen.h>
30 #include <netdb.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <strings.h>
40 #include <unistd.h>
41 #include <libdladm.h>
42 
43 #include "libnwam_impl.h"
44 #include <libnwam_priv.h>
45 #include <libnwam.h>
46 
47 /*
48  * Functions to support creating, modifying, destroying, querying the
49  * state of and changing the state of NCP (Network Configuration Profiles)
50  * and the NCUs (Network Configuration Units) that are contained in those
51  * NCP objects.  An NCP is simply a container for a set of NCUs which represent
52  * the datalink and interface configuration preferences for the system.
53  * An NCP can consist a set of prioritized link NCUs, e.g. wired links preferred
54  * over wireless, a set of manually enabled/diasbled NCUs, or a combination
55  * of both. Interface NCUs inherit activation from their underlying links,
56  * so if wired is preferred over wireless and a cable is plugged in,
57  * the wired link NCU will be active, as will the IP interface NCU above it.
58  */
59 
60 /*
61  * The NCU property table is used to mapping property types to property name
62  * strings, their associated value types etc. The table is used for validation
63  * purposes, and for commit()ing and read()ing NCUs.
64  */
65 
66 static nwam_error_t valid_type(nwam_value_t);
67 static nwam_error_t valid_class(nwam_value_t);
68 static nwam_error_t valid_ncp(nwam_value_t);
69 static nwam_error_t valid_priority_mode(nwam_value_t);
70 static nwam_error_t valid_ncu_activation_mode(nwam_value_t);
71 static nwam_error_t valid_link_autopush(nwam_value_t);
72 static nwam_error_t valid_link_mtu(nwam_value_t);
73 static nwam_error_t valid_ip_version(nwam_value_t);
74 static nwam_error_t valid_addrsrc_v4(nwam_value_t);
75 static nwam_error_t valid_addrsrc_v6(nwam_value_t);
76 
77 struct nwam_prop_table_entry ncu_prop_table_entries[] = {
78 	{NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1, valid_type,
79 	    "specifies the NCU type - valid values are \'datalink\' and \'ip\'",
80 	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
81 	{NWAM_NCU_PROP_CLASS, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
82 	    valid_class,
83 	    "specifies the NCU class - valid values are "
84 	    "\'phys\' and \'ip\'",
85 	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
86 	{NWAM_NCU_PROP_PARENT_NCP, NWAM_VALUE_TYPE_STRING, B_FALSE, 1, 1,
87 	    valid_ncp,
88 	    "specifies the parent NCP name",
89 	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
90 	{NWAM_NCU_PROP_ACTIVATION_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
91 	    valid_ncu_activation_mode,
92 	    "specifies the NCU activation mode - valid values are:\n"
93 	    "\'prioritized\' and \'manual\'",
94 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
95 	{NWAM_NCU_PROP_ENABLED, NWAM_VALUE_TYPE_BOOLEAN, B_TRUE, 0, 1,
96 	    nwam_valid_boolean,
97 	    "specifies if manual NCU is to be enabled",
98 	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
99 	{NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
100 	    nwam_valid_uint64,
101 	    "specifies the priority grouping of NCUs - lower values are "
102 	    "prioritized, negative values are invalid",
103 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
104 	{NWAM_NCU_PROP_PRIORITY_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
105 	    valid_priority_mode,
106 	    "specifies the mode of prioritization - valid values are:\n"
107 	    "\'exclusive\', \'shared\' and \'all\'",
108 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
109 	{NWAM_NCU_PROP_LINK_MAC_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
110 	    nwam_valid_mac_addr,
111 	    "specifies MAC address of form aa:bb:cc:dd:ee:ff for the link",
112 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
113 	{NWAM_NCU_PROP_LINK_AUTOPUSH, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
114 	    NWAM_MAX_NUM_VALUES, valid_link_autopush,
115 	    "specifies modules to autopush on link",
116 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
117 	{NWAM_NCU_PROP_LINK_MTU, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
118 	    valid_link_mtu,
119 	    "specifies MTU for link",
120 	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
121 	{NWAM_NCU_PROP_IP_VERSION, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
122 	    NWAM_MAX_NUM_VALUES, valid_ip_version,
123 	    "specifies IP versions for IP NCU - valid values are:\n"
124 	    "\'ipv4\' and \'ipv6\'",
125 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
126 	{NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
127 	    NWAM_MAX_NUM_VALUES, valid_addrsrc_v4,
128 	    "specifies IPv4 address source(s) - valid values are:\n"
129 	    "\'dhcp\' and \'static\'",
130 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
131 	{NWAM_NCU_PROP_IPV4_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
132 	    NWAM_MAX_NUM_VALUES, nwam_valid_host_v4,
133 	    "specifies static IPv4 host address(es)",
134 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
135 	{NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
136 	    1, nwam_valid_route_v4,
137 	    "specifies per-interface default IPv4 route",
138 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
139 	{NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
140 	    NWAM_MAX_NUM_VALUES, valid_addrsrc_v6,
141 	    "specifies IPv6 address source(s) - valid values are:\n"
142 	    "\'dhcp\', \'autoconf\' and \'static\'.\n"
143 	    "\'dhcp\' and \'autoconf\' are mandatory values.",
144 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
145 	{NWAM_NCU_PROP_IPV6_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
146 	    NWAM_MAX_NUM_VALUES, nwam_valid_host_v6,
147 	    "specifies static IPv6 host address(es)",
148 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
149 	{NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
150 	    1, nwam_valid_route_v6,
151 	    "specifies per-interface default IPv6 route",
152 	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE}
153 };
154 
155 #define	NWAM_NUM_NCU_PROPS	(sizeof (ncu_prop_table_entries) / \
156 				sizeof (*ncu_prop_table_entries))
157 
158 struct nwam_prop_table ncu_prop_table =
159 	{ NWAM_NUM_NCU_PROPS, ncu_prop_table_entries };
160 
161 nwam_error_t
162 nwam_ncp_get_name(nwam_ncp_handle_t ncph, char **namep)
163 {
164 	return (nwam_get_name(ncph, namep));
165 }
166 
167 static nwam_error_t
168 nwam_ncp_name_to_file(const char *name, char **filename)
169 {
170 	assert(name != NULL && filename != NULL);
171 
172 	if ((*filename = malloc(MAXPATHLEN)) == NULL)
173 		return (NWAM_NO_MEMORY);
174 
175 	(void) snprintf(*filename, MAXPATHLEN, "%s%s%s%s", NWAM_CONF_DIR,
176 	    NWAM_NCP_CONF_FILE_PRE, name, NWAM_NCP_CONF_FILE_SUF);
177 
178 	return (NWAM_SUCCESS);
179 }
180 
181 /* ARGSUSED1 */
182 nwam_error_t
183 nwam_ncp_create(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
184 {
185 	nwam_error_t err;
186 	char *ncpfile;
187 
188 	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, name, ncphp))
189 	    != NWAM_SUCCESS)
190 		return (err);
191 
192 	/* Create empty container for NCUs */
193 	if ((err = nwam_ncp_name_to_file(name, &ncpfile))
194 	    != NWAM_SUCCESS) {
195 		nwam_free(*ncphp);
196 		*ncphp = NULL;
197 		return (err);
198 	}
199 
200 	if ((err = nwam_commit(ncpfile, *ncphp, flags)) != NWAM_SUCCESS) {
201 		nwam_free(*ncphp);
202 		*ncphp = NULL;
203 	}
204 
205 	free(ncpfile);
206 
207 	return (err);
208 }
209 
210 /* Used by libnwam_files.c */
211 nwam_error_t
212 nwam_ncp_file_to_name(const char *path, char **name)
213 {
214 	char path_copy[MAXPATHLEN];
215 	char *filename, *suffix;
216 
217 	assert(path != NULL && name != NULL);
218 
219 	/* Make a copy as basename(3c) may modify string */
220 	(void) strlcpy(path_copy, path, MAXPATHLEN);
221 
222 	if ((*name = malloc(NWAM_MAX_NAME_LEN)) == NULL)
223 		return (NWAM_NO_MEMORY);
224 
225 	if ((filename = basename(path_copy)) == NULL) {
226 		free(*name);
227 		return (NWAM_ENTITY_INVALID);
228 	}
229 
230 	/* Ensure filename begins/ends with right prefix/suffix */
231 	if (sscanf(filename, NWAM_NCP_CONF_FILE_PRE "%256[^\n]s", *name) < 1) {
232 		free(*name);
233 		return (NWAM_ENTITY_INVALID);
234 	}
235 	suffix = *name + strlen(*name) - strlen(NWAM_NCP_CONF_FILE_SUF);
236 	if (strstr(*name, NWAM_NCP_CONF_FILE_SUF) != suffix) {
237 		free(*name);
238 		return (NWAM_ENTITY_INVALID);
239 	}
240 	suffix[0] = '\0';
241 
242 	return (NWAM_SUCCESS);
243 }
244 
245 /* ARGSUSED1 */
246 nwam_error_t
247 nwam_ncp_read(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
248 {
249 	char *filename;
250 	nwam_error_t err;
251 
252 	assert(name != NULL && ncphp != NULL);
253 
254 	/* try to read the associated ncp configuration */
255 	if ((err = nwam_ncp_name_to_file(name, &filename)) != NWAM_SUCCESS) {
256 		*ncphp = NULL;
257 		return (err);
258 	}
259 
260 	err = nwam_read(NWAM_OBJECT_TYPE_NCP, filename, name, flags, ncphp);
261 	free(filename);
262 	return (err);
263 }
264 
265 static nwam_error_t
266 nwam_ncu_get_parent_ncp_name(nwam_ncu_handle_t ncuh, char **parentnamep)
267 {
268 	nwam_value_t parentval = NULL;
269 	char *parentname;
270 	nwam_error_t err;
271 
272 	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_PARENT_NCP,
273 	    &parentval)) != NWAM_SUCCESS ||
274 	    (err = nwam_value_get_string(parentval, &parentname))
275 	    != NWAM_SUCCESS ||
276 	    (*parentnamep = strdup(parentname)) == NULL) {
277 		if (parentval != NULL)
278 			nwam_value_free(parentval);
279 		*parentnamep = NULL;
280 		return (err);
281 	}
282 	nwam_value_free(parentval);
283 
284 	return (NWAM_SUCCESS);
285 }
286 
287 static int
288 nwam_ncp_copy_callback(nwam_ncu_handle_t oldncuh, void *arg)
289 {
290 	nwam_error_t err;
291 	nwam_ncu_handle_t newncuh = NULL;
292 	char *oldparent;
293 	char *oldfilename = NULL, *newfilename = NULL;
294 	nwam_ncp_handle_t newncph = (nwam_ncp_handle_t)arg;
295 	nwam_value_t newparentval;
296 
297 	/* Get filenames for the new and old NCU's */
298 	if ((err = nwam_ncu_get_parent_ncp_name(oldncuh, &oldparent))
299 	    != NWAM_SUCCESS)
300 		return (err);
301 	err = nwam_ncp_name_to_file(oldparent, &oldfilename);
302 	free(oldparent);
303 	if (err != NWAM_SUCCESS)
304 		return (err);
305 	if ((err = nwam_ncp_name_to_file(newncph->nwh_name, &newfilename))
306 	    != NWAM_SUCCESS)
307 		goto fail;
308 
309 	/* new NCU name (and typedname) is the same as the old name */
310 	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, oldncuh->nwh_name,
311 	    &newncuh)) != NWAM_SUCCESS)
312 		goto fail;
313 	/* Duplicate the old NCU's data */
314 	if ((err = nwam_dup_object_list(oldncuh->nwh_data,
315 	    &(newncuh->nwh_data))) != NWAM_SUCCESS)
316 		goto fail;
317 
318 	/* Update the parent property for the new NCU */
319 	if ((err = nwam_value_create_string(newncph->nwh_name, &newparentval))
320 	    != NWAM_SUCCESS)
321 		goto fail;
322 	err = nwam_set_prop_value(newncuh->nwh_data, NWAM_NCU_PROP_PARENT_NCP,
323 	    newparentval);
324 	nwam_value_free(newparentval);
325 	if (err != NWAM_SUCCESS)
326 		goto fail;
327 
328 	/* Save the new NCU */
329 	err = nwam_commit(newfilename, newncuh, 0);
330 
331 fail:
332 	free(oldfilename);
333 	free(newfilename);
334 	nwam_ncu_free(newncuh);
335 	return (err);
336 }
337 
338 nwam_error_t
339 nwam_ncp_copy(nwam_ncp_handle_t oldncph, const char *newname,
340     nwam_ncp_handle_t *newncphp)
341 {
342 	nwam_ncp_handle_t ncph;
343 	nwam_error_t err;
344 	int cb_ret;
345 
346 	assert(oldncph != NULL && newname != NULL && newncphp != NULL);
347 
348 	/* check if newname NCP already exists */
349 	if (nwam_ncp_read(newname, 0,  &ncph) == NWAM_SUCCESS) {
350 		nwam_ncp_free(ncph);
351 		*newncphp = NULL;
352 		return (NWAM_ENTITY_EXISTS);
353 	}
354 
355 	/* create new handle */
356 	if ((err = nwam_ncp_create(newname, 0, newncphp)) != NWAM_SUCCESS)
357 		return (err);
358 
359 	err = nwam_ncp_walk_ncus(oldncph, nwam_ncp_copy_callback, *newncphp,
360 	    NWAM_FLAG_NCU_TYPE_CLASS_ALL, &cb_ret);
361 	if (err != NWAM_SUCCESS) {
362 		/* remove the NCP even if any NCU's had already been copied */
363 		(void) nwam_ncp_destroy(*newncphp, 0);
364 		*newncphp = NULL;
365 		if (err == NWAM_WALK_HALTED)
366 			return (cb_ret);
367 		else
368 			return (err);
369 	}
370 
371 	return (NWAM_SUCCESS);
372 }
373 
374 /*
375  * Convert type to flag
376  */
377 static uint64_t
378 nwam_ncu_type_to_flag(nwam_ncu_type_t type)
379 {
380 	switch (type) {
381 	case NWAM_NCU_TYPE_LINK:
382 		return (NWAM_FLAG_NCU_TYPE_LINK);
383 	case NWAM_NCU_TYPE_INTERFACE:
384 		return (NWAM_FLAG_NCU_TYPE_INTERFACE);
385 	case NWAM_NCU_TYPE_ANY:
386 		return (NWAM_FLAG_NCU_TYPE_ALL);
387 	default:
388 		return (0);
389 	}
390 }
391 
392 /*
393  * Convert class to flag
394  */
395 uint64_t
396 nwam_ncu_class_to_flag(nwam_ncu_class_t class)
397 {
398 	switch (class) {
399 	case NWAM_NCU_CLASS_PHYS:
400 		return (NWAM_FLAG_NCU_CLASS_PHYS);
401 	case NWAM_NCU_CLASS_IP:
402 		return (NWAM_FLAG_NCU_CLASS_IP);
403 	case NWAM_NCU_CLASS_ANY:
404 		return (NWAM_FLAG_NCU_CLASS_ALL);
405 	default:
406 		return (0);
407 	}
408 }
409 
410 /*
411  * Infer NCU type from NCU class
412  */
413 nwam_ncu_type_t
414 nwam_ncu_class_to_type(nwam_ncu_class_t class)
415 {
416 	switch (class) {
417 	case NWAM_NCU_CLASS_PHYS:
418 		return (NWAM_NCU_TYPE_LINK);
419 	case NWAM_NCU_CLASS_IP:
420 		return (NWAM_NCU_TYPE_INTERFACE);
421 	case NWAM_NCU_CLASS_ANY:
422 		return (NWAM_NCU_TYPE_ANY);
423 	default:
424 		return (NWAM_NCU_TYPE_UNKNOWN);
425 	}
426 }
427 
428 /*
429  * Make ncp active, deactivating any other active ncp.
430  */
431 nwam_error_t
432 nwam_ncp_enable(nwam_ncp_handle_t ncph)
433 {
434 	nwam_error_t err;
435 	char *name;
436 
437 	assert(ncph != NULL);
438 
439 	err = nwam_enable(NULL, ncph);
440 
441 	if (err == NWAM_ERROR_BIND) {
442 		/*
443 		 * nwamd is not running, set active_ncp property so when
444 		 * nwamd is next started, this NCP will be used.
445 		 */
446 		if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
447 			return (err);
448 
449 		err = nwam_set_smf_string_property(NWAM_FMRI, NWAM_PG,
450 		    NWAM_PROP_ACTIVE_NCP, name);
451 		free(name);
452 	}
453 
454 	return (err);
455 }
456 
457 /* Compare NCP names c1 and c2 using strcasecmp() */
458 static int
459 ncpname_cmp(const void *c1, const void *c2)
460 {
461 	return (strcasecmp(*(const char **)c1, *(const char **)c2));
462 }
463 
464 /* ARGSUSED1 */
465 nwam_error_t
466 nwam_walk_ncps(int (*cb)(nwam_ncp_handle_t, void *), void *data,
467     uint64_t flags, int *retp)
468 {
469 	char *ncpname, **ncpfiles;
470 	nwam_ncp_handle_t ncph;
471 	nwam_error_t err;
472 	nwam_value_t value;
473 	void *objlist;
474 	uint_t i, num_ncpfiles;
475 	int ret = 0;
476 
477 	assert(cb != NULL);
478 
479 	if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
480 		return (err);
481 	/*
482 	 * To get list of NCP files, call nwam_read_object_from_backend()
483 	 * with "parent" argument set to NULL. We get back an object list
484 	 * consisting of string arrays for each object type - NCP, ENM
485 	 * and location. We retrieve the NCP list, which corresponds to
486 	 * the set of NCP backend parent objects (these are files at present).
487 	 */
488 	if ((err = nwam_read_object_from_backend(NULL, NULL, flags,
489 	    &objlist)) != NWAM_SUCCESS)
490 		return (err);
491 
492 	if ((err = nwam_get_prop_value(objlist, NWAM_NCP_OBJECT_STRING, &value))
493 	    != NWAM_SUCCESS) {
494 		nwam_free_object_list(objlist);
495 		return (err);
496 	}
497 	if ((err = nwam_value_get_string_array(value, &ncpfiles,
498 	    &num_ncpfiles)) != NWAM_SUCCESS) {
499 		nwam_value_free(value);
500 		nwam_free_object_list(objlist);
501 		return (err);
502 	}
503 
504 	/* sort the NCP names alphabetically */
505 	qsort(ncpfiles, num_ncpfiles, sizeof (char *), ncpname_cmp);
506 
507 	for (i = 0; i < num_ncpfiles; i++) {
508 		if (nwam_ncp_file_to_name(ncpfiles[i], &ncpname)
509 		    != NWAM_SUCCESS)
510 			continue;
511 		if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, ncpname,
512 		    &ncph)) != NWAM_SUCCESS) {
513 			free(ncpname);
514 			break;
515 		}
516 		ret = cb(ncph, data);
517 		free(ncph);
518 		free(ncpname);
519 		if (ret != 0) {
520 			err = NWAM_WALK_HALTED;
521 			break;
522 		}
523 	}
524 	nwam_value_free(value);
525 	nwam_free_object_list(objlist);
526 
527 	if (retp != NULL)
528 		*retp = ret;
529 	return (err);
530 }
531 
532 /*
533  * Checks if NCP is read-only.  Only NWAM_NCP_NAME_AUTOMATIC is read-only
534  * for all but the netadm user (which nwamd runs as).
535  */
536 nwam_error_t
537 nwam_ncp_get_read_only(nwam_ncp_handle_t ncph, boolean_t *readp)
538 {
539 	nwam_error_t err;
540 	char *name;
541 
542 	assert(ncph != NULL && readp != NULL);
543 
544 	if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
545 		return (err);
546 
547 	if (NWAM_NCP_AUTOMATIC(name))
548 		*readp = !nwam_uid_is_special();
549 	else
550 		*readp = B_FALSE;
551 
552 	free(name);
553 	return (NWAM_SUCCESS);
554 }
555 
556 /* Checks if NCU is writable depending on its parent */
557 nwam_error_t
558 nwam_ncu_get_read_only(nwam_ncu_handle_t ncuh, boolean_t *readp)
559 {
560 	nwam_error_t err;
561 	nwam_ncp_handle_t ncph;
562 
563 	assert(ncuh != NULL && readp != NULL);
564 
565 	if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
566 		return (err);
567 
568 	err = nwam_ncp_get_read_only(ncph, readp);
569 	nwam_ncp_free(ncph);
570 	return (err);
571 }
572 
573 /* Returns true if the NCP is active */
574 static boolean_t
575 nwam_ncp_is_active(nwam_ncp_handle_t ncph)
576 {
577 	char *active_ncp, *name;
578 	boolean_t ret;
579 
580 	assert(ncph != NULL);
581 
582 	/*
583 	 * Determine which NCP is active via the nwamd/active_ncp property
584 	 * value.  This allows us to determine which NCP is active even
585 	 * if nwamd is not running.
586 	 */
587 	if (nwam_ncp_get_name(ncph, &name) != NWAM_SUCCESS ||
588 	    nwam_get_smf_string_property(NWAM_FMRI, NWAM_PG,
589 	    NWAM_PROP_ACTIVE_NCP, &active_ncp) != NWAM_SUCCESS)
590 		return (B_FALSE);
591 
592 	ret = (strcmp(name, active_ncp) == 0);
593 
594 	free(active_ncp);
595 	free(name);
596 
597 	return (ret);
598 }
599 
600 nwam_error_t
601 nwam_ncp_destroy(nwam_ncp_handle_t ncph, uint64_t flags)
602 {
603 	char *filename;
604 	nwam_error_t err;
605 	boolean_t read_only;
606 
607 	assert(ncph != NULL);
608 
609 	if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
610 		return (err);
611 	if (read_only)
612 		return (NWAM_ENTITY_NOT_DESTROYABLE);
613 
614 	if (nwam_ncp_is_active(ncph))
615 		return (NWAM_ENTITY_IN_USE);
616 
617 	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &filename))
618 	    != NWAM_SUCCESS)
619 		return (err);
620 
621 	err = nwam_destroy(filename, ncph, flags);
622 	free(filename);
623 
624 	return (NWAM_SUCCESS);
625 }
626 
627 static nwam_error_t
628 nwam_ncu_internal_name_to_name(const char *internalname,
629     nwam_ncu_type_t *typep, char **namep)
630 {
631 	char *prefixstr;
632 
633 	assert(internalname != NULL && namep != NULL);
634 
635 	if (strncasecmp(internalname, NWAM_NCU_LINK_NAME_PRE,
636 	    strlen(NWAM_NCU_LINK_NAME_PRE)) == 0) {
637 		prefixstr = NWAM_NCU_LINK_NAME_PRE;
638 		*typep = NWAM_NCU_TYPE_LINK;
639 	} else if (strncasecmp(internalname, NWAM_NCU_INTERFACE_NAME_PRE,
640 	    strlen(NWAM_NCU_INTERFACE_NAME_PRE)) == 0) {
641 		prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
642 		*typep = NWAM_NCU_TYPE_INTERFACE;
643 	} else {
644 		return (NWAM_INVALID_ARG);
645 	}
646 
647 	*namep = strdup(internalname + strlen(prefixstr));
648 	if (*namep == NULL)
649 		return (NWAM_NO_MEMORY);
650 	return (NWAM_SUCCESS);
651 }
652 
653 /* ARGSUSED2 */
654 static int
655 ncu_selectcb(struct nwam_handle *hp, uint64_t flags, void *data)
656 {
657 	nwam_ncu_handle_t ncuh = hp;
658 	nwam_value_t typeval = NULL, classval = NULL;
659 	uint64_t type, class, matchflags, walkfilter;
660 
661 	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
662 	    != NWAM_SUCCESS ||
663 	    nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
664 	    != NWAM_SUCCESS) {
665 		if (typeval != NULL)
666 			nwam_value_free(typeval);
667 		return (NWAM_INVALID_ARG);
668 	}
669 	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS ||
670 	    nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
671 		nwam_value_free(typeval);
672 		nwam_value_free(classval);
673 		return (NWAM_INVALID_ARG);
674 	}
675 
676 	matchflags = nwam_ncu_type_to_flag(type) |
677 	    nwam_ncu_class_to_flag(class);
678 	nwam_value_free(typeval);
679 	nwam_value_free(classval);
680 
681 	if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
682 		walkfilter = NWAM_FLAG_NCU_TYPE_CLASS_ALL;
683 
684 	if (matchflags & walkfilter)
685 		return (NWAM_SUCCESS);
686 	return (NWAM_INVALID_ARG);
687 }
688 
689 nwam_error_t
690 nwam_ncp_walk_ncus(nwam_ncp_handle_t ncph,
691     int(*cb)(nwam_ncu_handle_t, void *), void *data, uint64_t flags, int *retp)
692 {
693 	char *ncpfile;
694 	nwam_error_t err;
695 
696 	assert(ncph != NULL && cb != NULL);
697 
698 	if ((err = nwam_valid_flags(flags,
699 	    NWAM_FLAG_NCU_TYPE_CLASS_ALL | NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
700 		return (err);
701 
702 	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
703 	    != NWAM_SUCCESS)
704 		return (err);
705 
706 	err = nwam_walk(NWAM_OBJECT_TYPE_NCU, ncpfile, cb, data, flags,
707 	    retp, ncu_selectcb);
708 	free(ncpfile);
709 
710 	return (err);
711 }
712 
713 void
714 nwam_ncp_free(nwam_ncp_handle_t ncph)
715 {
716 	nwam_free(ncph);
717 }
718 
719 /*
720  * Are ncu type and class compatible?
721  */
722 static boolean_t
723 nwam_ncu_type_class_compatible(nwam_ncu_type_t type, nwam_ncu_class_t class)
724 {
725 	switch (type) {
726 	case NWAM_NCU_TYPE_LINK:
727 		return (class == NWAM_NCU_CLASS_PHYS);
728 	case NWAM_NCU_TYPE_INTERFACE:
729 		return (class == NWAM_NCU_CLASS_IP);
730 	default:
731 		return (B_FALSE);
732 	}
733 }
734 
735 /* Name to validate may be internal name. If so, convert it before validating */
736 static boolean_t
737 valid_ncu_name(const char *name)
738 {
739 	char *n;
740 	boolean_t ret;
741 	nwam_ncu_type_t type;
742 
743 	if (nwam_ncu_internal_name_to_name(name, &type, &n) == NWAM_SUCCESS) {
744 
745 		ret = dladm_valid_linkname(n);
746 		free(n);
747 	} else {
748 		ret = dladm_valid_linkname(name);
749 	}
750 
751 	return (ret);
752 }
753 
754 nwam_error_t
755 nwam_ncu_create(nwam_ncp_handle_t ncph, const char *name,
756     nwam_ncu_type_t type, nwam_ncu_class_t class, nwam_ncu_handle_t *ncuhp)
757 {
758 	nwam_ncu_handle_t ncuh;
759 	nwam_value_t typeval = NULL, classval = NULL, parentval = NULL;
760 	nwam_value_t enabledval = NULL;
761 	nwam_error_t err;
762 	boolean_t read_only;
763 	char *typedname;
764 
765 	assert(ncph != NULL && name != NULL && ncuhp != NULL);
766 
767 	if (!valid_ncu_name(name))
768 		return (NWAM_INVALID_ARG);
769 
770 	if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
771 		return (err);
772 	if (read_only)
773 		return (NWAM_ENTITY_READ_ONLY);
774 
775 	if (nwam_ncu_read(ncph, name, type, 0, &ncuh) == NWAM_SUCCESS) {
776 		nwam_ncu_free(ncuh);
777 		return (NWAM_ENTITY_EXISTS);
778 	}
779 
780 	if (!valid_ncu_name(name) ||
781 	    !nwam_ncu_type_class_compatible(type, class))
782 		return (NWAM_INVALID_ARG);
783 
784 	if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname))
785 	    != NWAM_SUCCESS)
786 		return (err);
787 
788 	/* Create handle */
789 	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typedname, ncuhp))
790 	    != NWAM_SUCCESS)
791 		return (err);
792 	free(typedname);
793 
794 	/*
795 	 * Create new object list for NCU.  The new NCU is initialized with
796 	 * the appropriate type and class.
797 	 */
798 	if ((err = nwam_alloc_object_list(&(*ncuhp)->nwh_data)) != NWAM_SUCCESS)
799 		goto finish;
800 
801 	if ((err = nwam_value_create_uint64(type, &typeval))
802 	    != NWAM_SUCCESS ||
803 	    (err = nwam_value_create_uint64(class, &classval))
804 	    != NWAM_SUCCESS ||
805 	    (err = nwam_value_create_string(ncph->nwh_name, &parentval))
806 	    != NWAM_SUCCESS ||
807 	    (err = nwam_value_create_boolean(B_TRUE, &enabledval))
808 	    != NWAM_SUCCESS) {
809 		goto finish;
810 	}
811 	if ((err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_TYPE,
812 	    typeval)) != NWAM_SUCCESS ||
813 	    (err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_CLASS,
814 	    classval)) != NWAM_SUCCESS ||
815 	    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
816 	    NWAM_NCU_PROP_PARENT_NCP, parentval)) != NWAM_SUCCESS ||
817 	    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
818 	    NWAM_NCU_PROP_ENABLED, enabledval)) != NWAM_SUCCESS) {
819 		goto finish;
820 	}
821 
822 	/* Set default IP, datalink properties */
823 	if (type == NWAM_NCU_TYPE_INTERFACE && class == NWAM_NCU_CLASS_IP) {
824 
825 		uint64_t ver[] = { IPV4_VERSION, IPV6_VERSION };
826 		uint64_t v6src[] = { NWAM_ADDRSRC_DHCP, NWAM_ADDRSRC_AUTOCONF };
827 		uint_t vercnt = 2, v6srccnt = 2;
828 		nwam_value_t ipver = NULL, v4addrsrc = NULL, v6addrsrc = NULL;
829 
830 		if ((err = nwam_value_create_uint64_array(ver, vercnt, &ipver))
831 		    != NWAM_SUCCESS ||
832 		    (err = nwam_value_create_uint64(NWAM_ADDRSRC_DHCP,
833 		    &v4addrsrc)) != NWAM_SUCCESS ||
834 		    (err = nwam_value_create_uint64_array(v6src, v6srccnt,
835 		    &v6addrsrc)) != NWAM_SUCCESS) {
836 			nwam_value_free(ipver);
837 			nwam_value_free(v4addrsrc);
838 			goto finish;
839 		}
840 		if ((err = nwam_set_prop_value((*ncuhp)->nwh_data,
841 		    NWAM_NCU_PROP_IP_VERSION, ipver)) == NWAM_SUCCESS &&
842 		    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
843 		    NWAM_NCU_PROP_IPV4_ADDRSRC, v4addrsrc)) == NWAM_SUCCESS) {
844 			err = nwam_set_prop_value((*ncuhp)->nwh_data,
845 			    NWAM_NCU_PROP_IPV6_ADDRSRC, v6addrsrc);
846 		}
847 		nwam_value_free(ipver);
848 		nwam_value_free(v4addrsrc);
849 		nwam_value_free(v6addrsrc);
850 	} else {
851 		nwam_value_t actval = NULL;
852 		if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL,
853 		    &actval)) != NWAM_SUCCESS)
854 			goto finish;
855 		err = nwam_set_prop_value((*ncuhp)->nwh_data,
856 		    NWAM_NCU_PROP_ACTIVATION_MODE, actval);
857 		nwam_value_free(actval);
858 	}
859 
860 finish:
861 	nwam_value_free(typeval);
862 	nwam_value_free(classval);
863 	nwam_value_free(parentval);
864 	nwam_value_free(enabledval);
865 	if (err != NWAM_SUCCESS) {
866 		nwam_ncu_free(*ncuhp);
867 		*ncuhp = NULL;
868 	}
869 	return (err);
870 }
871 
872 nwam_error_t
873 nwam_ncu_read(nwam_ncp_handle_t ncph, const char *name,
874     nwam_ncu_type_t type, uint64_t flags, nwam_ncu_handle_t *ncuhp)
875 {
876 	char *ncpfile, *typedname;
877 	nwam_error_t err, err_ip, err_link;
878 	nwam_ncu_handle_t ncuh_ip, ncuh_link;
879 
880 	assert(ncph != NULL && name != NULL && ncuhp != NULL);
881 
882 	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
883 	    != NWAM_SUCCESS)
884 		return (err);
885 
886 	if (type == NWAM_NCU_TYPE_ANY) {
887 
888 		free(ncpfile);
889 
890 		/*
891 		 * If we get to this point, we have discovered that no
892 		 * NCU type is discernable from name or type arguments.
893 		 * Either exactly one NCU called name must exist of either
894 		 * type, or the operation should fail.
895 		 */
896 		err_ip = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_INTERFACE,
897 		    flags, &ncuh_ip);
898 		err_link = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK,
899 		    flags, &ncuh_link);
900 
901 		*ncuhp = NULL;
902 
903 		if (err_ip == NWAM_SUCCESS && err_link == NWAM_SUCCESS) {
904 			nwam_ncu_free(ncuh_ip);
905 			nwam_ncu_free(ncuh_link);
906 			err = NWAM_ENTITY_MULTIPLE_VALUES;
907 		} else if (err_ip != NWAM_SUCCESS && err_link != NWAM_SUCCESS) {
908 			err = NWAM_ENTITY_NOT_FOUND;
909 		} else {
910 			if (err_ip == NWAM_SUCCESS) {
911 				*ncuhp = ncuh_ip;
912 			} else {
913 				*ncuhp = ncuh_link;
914 			}
915 			err = NWAM_SUCCESS;
916 		}
917 
918 		return (err);
919 	}
920 	if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname)) !=
921 	    NWAM_SUCCESS) {
922 		free(ncpfile);
923 		return (err);
924 	}
925 	err = nwam_read(NWAM_OBJECT_TYPE_NCU, ncpfile, typedname, flags, ncuhp);
926 
927 	free(typedname);
928 	free(ncpfile);
929 
930 	return (err);
931 }
932 
933 nwam_error_t
934 nwam_ncu_get_name(nwam_ncu_handle_t ncuh, char **namep)
935 {
936 	nwam_ncu_type_t type;
937 
938 	assert(ncuh != NULL && namep != NULL);
939 
940 	return (nwam_ncu_internal_name_to_name(ncuh->nwh_name, &type, namep));
941 }
942 
943 nwam_error_t
944 nwam_ncu_name_to_typed_name(const char *name, nwam_ncu_type_t type,
945     char **typednamep)
946 {
947 	char *prefixstr;
948 	size_t typednamesz;
949 
950 	assert(name != NULL && typednamep != NULL);
951 
952 	switch (type) {
953 	case NWAM_NCU_TYPE_INTERFACE:
954 		prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
955 		break;
956 	case NWAM_NCU_TYPE_LINK:
957 		prefixstr = NWAM_NCU_LINK_NAME_PRE;
958 		break;
959 	default:
960 		return (NWAM_INVALID_ARG);
961 	}
962 	typednamesz = strlen(name) + strlen(prefixstr) + 1;
963 	if ((*typednamep = malloc(typednamesz)) == NULL)
964 		return (NWAM_NO_MEMORY);
965 
966 	/* Name may be already qualified by type */
967 	if (strncasecmp(prefixstr, name, strlen(prefixstr)) == 0) {
968 		(void) snprintf(*typednamep, typednamesz, "%s", name);
969 	} else {
970 		(void) snprintf(*typednamep, typednamesz, "%s%s",
971 		    prefixstr, name);
972 	}
973 
974 	return (NWAM_SUCCESS);
975 }
976 
977 nwam_error_t
978 nwam_ncu_typed_name_to_name(const char *typed_name, nwam_ncu_type_t *typep,
979     char **name)
980 {
981 	return (nwam_ncu_internal_name_to_name(typed_name, typep, name));
982 }
983 
984 void
985 nwam_ncu_free(nwam_ncu_handle_t ncuh)
986 {
987 	nwam_free(ncuh);
988 }
989 
990 nwam_error_t
991 nwam_ncu_copy(nwam_ncu_handle_t oldncuh, const char *newname,
992     nwam_ncu_handle_t *newncuhp)
993 {
994 	nwam_ncp_handle_t ncph;
995 	nwam_ncu_handle_t ncuh;
996 	nwam_error_t err;
997 	nwam_value_t typeval;
998 	uint64_t type;
999 	char *typednewname;
1000 
1001 	assert(oldncuh != NULL && newname != NULL && newncuhp != NULL);
1002 
1003 	if (nwam_ncu_get_prop_value(oldncuh, NWAM_NCU_PROP_TYPE,
1004 	    &typeval) != NWAM_SUCCESS) {
1005 		return (NWAM_INVALID_ARG);
1006 	}
1007 	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
1008 		nwam_value_free(typeval);
1009 		return (NWAM_INVALID_ARG);
1010 	}
1011 	nwam_value_free(typeval);
1012 
1013 	/* check if newname NCU already exists */
1014 	if ((err = nwam_ncu_get_ncp(oldncuh, &ncph)) != NWAM_SUCCESS)
1015 		return (err);
1016 	if (nwam_ncu_read(ncph, newname, type, 0, &ncuh) == NWAM_SUCCESS) {
1017 		nwam_ncu_free(ncuh);
1018 		nwam_ncp_free(ncph);
1019 		return (NWAM_ENTITY_EXISTS);
1020 	}
1021 	nwam_ncp_free(ncph);
1022 
1023 	if ((err = nwam_ncu_name_to_typed_name(newname, type, &typednewname))
1024 	    != NWAM_SUCCESS)
1025 		return (err);
1026 
1027 	err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typednewname, newncuhp);
1028 	free(typednewname);
1029 	if (err != NWAM_SUCCESS)
1030 		return (err);
1031 	if ((err = nwam_dup_object_list(oldncuh->nwh_data,
1032 	    &((*newncuhp)->nwh_data))) != NWAM_SUCCESS) {
1033 		free(*newncuhp);
1034 		*newncuhp = NULL;
1035 		return (err);
1036 	}
1037 
1038 	return (NWAM_SUCCESS);
1039 }
1040 
1041 nwam_error_t
1042 nwam_ncu_delete_prop(nwam_ncu_handle_t ncuh, const char *propname)
1043 {
1044 	boolean_t ro_ncu, ro_prop;
1045 	nwam_error_t err;
1046 	void *olddata;
1047 
1048 	assert(ncuh != NULL && propname != NULL);
1049 
1050 	if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
1051 	    (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
1052 		return (err);
1053 	if (ro_ncu || ro_prop)
1054 		return (NWAM_ENTITY_READ_ONLY);
1055 
1056 	/*
1057 	 * Duplicate data, remove property and validate. If validation
1058 	 * fails, revert to data duplicated prior to remove.
1059 	 */
1060 	if ((err = nwam_dup_object_list(ncuh->nwh_data, &olddata))
1061 	    != NWAM_SUCCESS)
1062 		return (err);
1063 	if ((err = nwam_delete_prop(ncuh->nwh_data, propname))
1064 	    != NWAM_SUCCESS) {
1065 		nwam_free_object_list(ncuh->nwh_data);
1066 		ncuh->nwh_data = olddata;
1067 		return (err);
1068 	}
1069 	if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS) {
1070 		nwam_free_object_list(ncuh->nwh_data);
1071 		ncuh->nwh_data = olddata;
1072 		return (err);
1073 	}
1074 	nwam_free_object_list(olddata);
1075 
1076 	return (NWAM_SUCCESS);
1077 }
1078 
1079 nwam_error_t
1080 nwam_ncu_set_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
1081     nwam_value_t value)
1082 {
1083 	boolean_t ro_ncu, ro_prop;
1084 	nwam_error_t err;
1085 	nwam_ncp_handle_t ncph;
1086 
1087 	assert(ncuh != NULL && propname != NULL && value != NULL);
1088 
1089 	if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
1090 	    (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
1091 		return (err);
1092 	if (ro_ncu || ro_prop)
1093 		return (NWAM_ENTITY_READ_ONLY);
1094 
1095 	err = nwam_ncu_get_ncp(ncuh, &ncph);
1096 	if (err != NWAM_SUCCESS && err != NWAM_INVALID_ARG) {
1097 		/*
1098 		 * If "parent" property doesn't exist, NWAM_INVALID_ARG
1099 		 * is returned.  Allow the setting to continue.
1100 		 */
1101 		return (err);
1102 	}
1103 	nwam_ncp_free(ncph);
1104 
1105 	/* Need to ensure property, type and value are valid */
1106 	if ((err = nwam_ncu_validate_prop(ncuh, propname, value))
1107 	    != NWAM_SUCCESS)
1108 		return (err);
1109 
1110 	return (nwam_set_prop_value(ncuh->nwh_data, propname, value));
1111 }
1112 
1113 nwam_error_t
1114 nwam_ncu_get_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
1115     nwam_value_t *valuep)
1116 {
1117 	assert(ncuh != NULL && propname != NULL && valuep != NULL);
1118 
1119 	return (nwam_get_prop_value(ncuh->nwh_data, propname, valuep));
1120 }
1121 
1122 nwam_error_t
1123 nwam_ncu_walk_props(nwam_ncu_handle_t ncuh,
1124     int (*cb)(const char *, nwam_value_t, void *),
1125     void *data, uint64_t flags, int *retp)
1126 {
1127 	return (nwam_walk_props(ncuh, cb, data, flags, retp));
1128 }
1129 
1130 nwam_error_t
1131 nwam_ncu_get_ncp(nwam_ncu_handle_t ncuh, nwam_ncp_handle_t *ncphp)
1132 {
1133 	nwam_error_t err;
1134 	char *parentname = NULL;
1135 
1136 	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &parentname))
1137 	    != NWAM_SUCCESS ||
1138 	    (err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, parentname, ncphp))
1139 	    != NWAM_SUCCESS) {
1140 		if (parentname != NULL)
1141 			free(parentname);
1142 		return (err);
1143 	}
1144 	free(parentname);
1145 
1146 	return (NWAM_SUCCESS);
1147 }
1148 
1149 nwam_error_t
1150 nwam_ncu_commit(nwam_ncu_handle_t ncuh, uint64_t flags)
1151 {
1152 	nwam_error_t err;
1153 	boolean_t read_only;
1154 	char *ncpfile, *ncpname;
1155 
1156 	assert(ncuh != NULL && ncuh->nwh_data != NULL);
1157 
1158 	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
1159 		return (err);
1160 	if (read_only)
1161 		return (NWAM_ENTITY_READ_ONLY);
1162 
1163 	if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS ||
1164 	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
1165 	    != NWAM_SUCCESS)
1166 		return (err);
1167 
1168 	if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile)) != NWAM_SUCCESS) {
1169 		free(ncpname);
1170 		return (err);
1171 	}
1172 
1173 	err = nwam_commit(ncpfile, ncuh, flags);
1174 
1175 	free(ncpname);
1176 	free(ncpfile);
1177 
1178 	return (err);
1179 }
1180 /* Get the NCU type */
1181 nwam_error_t
1182 nwam_ncu_get_ncu_type(nwam_ncu_handle_t ncuh, nwam_ncu_type_t *typep)
1183 {
1184 	nwam_error_t err;
1185 	nwam_value_t typeval;
1186 	uint64_t type;
1187 
1188 	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval))
1189 	    != NWAM_SUCCESS)
1190 		return (err);
1191 	err = nwam_value_get_uint64(typeval, &type);
1192 	nwam_value_free(typeval);
1193 	if (err != NWAM_SUCCESS)
1194 		return (err);
1195 
1196 	*typep = type;
1197 	return (NWAM_SUCCESS);
1198 }
1199 
1200 /* Get the NCU class */
1201 nwam_error_t
1202 nwam_ncu_get_ncu_class(nwam_ncu_handle_t ncuh, nwam_ncu_class_t *classp)
1203 {
1204 	nwam_error_t err;
1205 	nwam_value_t classval;
1206 	uint64_t class;
1207 
1208 	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS,
1209 	    &classval)) != NWAM_SUCCESS)
1210 		return (err);
1211 	err = nwam_value_get_uint64(classval, &class);
1212 	nwam_value_free(classval);
1213 	if (err != NWAM_SUCCESS)
1214 		return (err);
1215 
1216 	*classp = class;
1217 	return (NWAM_SUCCESS);
1218 }
1219 
1220 /*
1221  * Determine if the NCU has manual activation-mode or not.
1222  */
1223 nwam_error_t
1224 nwam_ncu_is_manual(nwam_ncu_handle_t ncuh, boolean_t *manualp)
1225 {
1226 	nwam_error_t err;
1227 	nwam_value_t actval;
1228 	uint64_t activation;
1229 
1230 	assert(ncuh != NULL);
1231 
1232 	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ACTIVATION_MODE,
1233 	    &actval)) != NWAM_SUCCESS)
1234 		return (err);
1235 	err = nwam_value_get_uint64(actval, &activation);
1236 	nwam_value_free(actval);
1237 	if (err != NWAM_SUCCESS)
1238 		return (err);
1239 
1240 	if (activation == NWAM_ACTIVATION_MODE_MANUAL)
1241 		*manualp = B_TRUE;
1242 	else
1243 		*manualp = B_FALSE;
1244 	return (NWAM_SUCCESS);
1245 }
1246 
1247 /* Determine if NCU is enabled or not */
1248 static nwam_error_t
1249 nwam_ncu_is_enabled(nwam_ncu_handle_t ncuh, boolean_t *enabledp)
1250 {
1251 	nwam_error_t err;
1252 	nwam_value_t enabledval;
1253 
1254 	assert(ncuh != NULL);
1255 
1256 	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
1257 	    &enabledval)) != NWAM_SUCCESS)
1258 		return (err);
1259 	err = nwam_value_get_boolean(enabledval, enabledp);
1260 	nwam_value_free(enabledval);
1261 	return (err);
1262 }
1263 
1264 /* Update the enabled property */
1265 static nwam_error_t
1266 nwam_ncu_update_enabled(nwam_ncu_handle_t ncuh, boolean_t enabled)
1267 {
1268 	nwam_error_t err;
1269 	nwam_value_t enabledval;
1270 
1271 	if ((err = nwam_value_create_boolean(enabled, &enabledval))
1272 	    != NWAM_SUCCESS)
1273 		return (err);
1274 	err = nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_ENABLED,
1275 	    enabledval);
1276 	nwam_value_free(enabledval);
1277 	if (err != NWAM_SUCCESS)
1278 		return (err);
1279 	return (nwam_ncu_commit(ncuh, NWAM_FLAG_ENTITY_ENABLE));
1280 }
1281 
1282 /*
1283  * Make ncu active; fails if the NCU's parent NCP is not active.
1284  */
1285 nwam_error_t
1286 nwam_ncu_enable(nwam_ncu_handle_t ncuh)
1287 {
1288 	char *ncpname = NULL;
1289 	nwam_error_t err;
1290 	nwam_ncu_type_t type;
1291 	boolean_t read_only, enabled, manual;
1292 
1293 	assert(ncuh != NULL);
1294 
1295 	/* Don't allow NCUs of Automatic NCP to be enabled */
1296 	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
1297 		return (err);
1298 	if (read_only)
1299 		return (NWAM_ENTITY_NOT_MANUAL);
1300 
1301 	/* Link NCUs with manual activation-mode or IP NCUs can be enabled */
1302 	if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
1303 		return (err);
1304 
1305 	if (type == NWAM_NCU_TYPE_LINK) {
1306 		if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
1307 			return (err);
1308 		if (!manual)
1309 			return (NWAM_ENTITY_NOT_MANUAL);
1310 	}
1311 
1312 	/* Make sure NCU is not enabled */
1313 	if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
1314 	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
1315 	    != NWAM_SUCCESS)
1316 		return (err);
1317 
1318 	if (enabled) {
1319 		free(ncpname);
1320 		return (NWAM_SUCCESS);
1321 	}
1322 
1323 	if ((err = nwam_ncu_update_enabled(ncuh, B_TRUE)) != NWAM_SUCCESS) {
1324 		free(ncpname);
1325 		return (err);
1326 	}
1327 
1328 	err = nwam_enable(ncpname, ncuh);
1329 	free(ncpname);
1330 
1331 	/* nwamd may not be running, that's okay. */
1332 	if (err == NWAM_ERROR_BIND)
1333 		return (NWAM_SUCCESS);
1334 	else
1335 		return (err);
1336 }
1337 
1338 /*
1339  * Disable ncu; fails if the NCU's parent NCP is not active, or if the
1340  * NCU is not currently active.
1341  */
1342 nwam_error_t
1343 nwam_ncu_disable(nwam_ncu_handle_t ncuh)
1344 {
1345 	char *ncpname = NULL;
1346 	nwam_error_t err;
1347 	nwam_ncu_type_t type;
1348 	boolean_t read_only, enabled, manual;
1349 
1350 	assert(ncuh != NULL);
1351 
1352 	/* Don't allow NCUs of Automatic NCP to be disabled */
1353 	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
1354 		return (err);
1355 	if (read_only)
1356 		return (NWAM_ENTITY_NOT_MANUAL);
1357 
1358 	/* Link NCUs with manual activation-mode or IP NCUs can be disabled */
1359 	if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
1360 		return (err);
1361 
1362 	if (type == NWAM_NCU_TYPE_LINK) {
1363 		if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
1364 			return (err);
1365 		if (!manual)
1366 			return (NWAM_ENTITY_NOT_MANUAL);
1367 	}
1368 
1369 	/* Make sure NCU is enabled */
1370 	if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
1371 	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
1372 	    != NWAM_SUCCESS)
1373 		return (err);
1374 
1375 	if (!enabled) {
1376 		free(ncpname);
1377 		return (NWAM_SUCCESS);
1378 	}
1379 
1380 	if ((err = nwam_ncu_update_enabled(ncuh, B_FALSE)) != NWAM_SUCCESS) {
1381 		free(ncpname);
1382 		return (err);
1383 	}
1384 
1385 	err = nwam_disable(ncpname, ncuh);
1386 	free(ncpname);
1387 
1388 	/* nwamd may not be running, that's okay. */
1389 	if (err == NWAM_ERROR_BIND)
1390 		return (NWAM_SUCCESS);
1391 	else
1392 		return (err);
1393 }
1394 
1395 nwam_error_t
1396 nwam_ncu_destroy(nwam_ncu_handle_t ncuh, uint64_t flags)
1397 {
1398 	char *ncpname, *ncpfile;
1399 	boolean_t read_only;
1400 	nwam_error_t err;
1401 
1402 	assert(ncuh != NULL);
1403 
1404 	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
1405 		return (err);
1406 	if (read_only)
1407 		return (NWAM_ENTITY_NOT_DESTROYABLE);
1408 
1409 	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
1410 	    != NWAM_SUCCESS)
1411 		return (err);
1412 	if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile))
1413 	    != NWAM_SUCCESS) {
1414 		free(ncpname);
1415 		return (err);
1416 	}
1417 
1418 	err = nwam_destroy(ncpfile, ncuh, flags);
1419 
1420 	free(ncpname);
1421 	free(ncpfile);
1422 
1423 	return (err);
1424 }
1425 
1426 nwam_error_t
1427 nwam_ncu_get_prop_description(const char *propname, const char **descriptionp)
1428 {
1429 	return (nwam_get_prop_description(ncu_prop_table, propname,
1430 	    descriptionp));
1431 }
1432 
1433 /* Get expected property data type */
1434 nwam_error_t
1435 nwam_ncu_get_prop_type(const char *propname, nwam_value_type_t *typep)
1436 {
1437 	return (nwam_get_prop_type(ncu_prop_table, propname, typep));
1438 }
1439 
1440 nwam_error_t
1441 nwam_ncu_prop_read_only(const char *propname, boolean_t *readp)
1442 {
1443 	if ((*readp = NWAM_NCU_PROP_SETONCE(propname)) == B_TRUE)
1444 		return (NWAM_SUCCESS);
1445 
1446 	return (nwam_prop_read_only(ncu_prop_table, propname, readp));
1447 }
1448 
1449 nwam_error_t
1450 nwam_ncu_prop_multivalued(const char *propname, boolean_t *multip)
1451 {
1452 	return (nwam_prop_multivalued(ncu_prop_table, propname, multip));
1453 }
1454 
1455 /*
1456  * Ensure that the properties in the ncu, determined by that ncu's
1457  * type and class, belong there.
1458  */
1459 static nwam_error_t
1460 nwam_ncu_validate_prop_membership(nwam_ncu_handle_t ncuh, const char *propname)
1461 {
1462 	struct nwam_prop_table_entry *pte;
1463 	nwam_value_t typeval, classval;
1464 	uint64_t type, class;
1465 	uint64_t typeflags = 0, classflags = 0;
1466 
1467 	/* Get type/class from ncu */
1468 	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
1469 	    != NWAM_SUCCESS)
1470 		return (NWAM_ENTITY_INVALID);
1471 	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
1472 		nwam_value_free(typeval);
1473 		return (NWAM_ENTITY_INVALID);
1474 	}
1475 	typeflags = nwam_ncu_type_to_flag((nwam_ncu_type_t)type);
1476 	nwam_value_free(typeval);
1477 
1478 	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
1479 	    != NWAM_SUCCESS)
1480 		return (NWAM_ENTITY_INVALID);
1481 	if (nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
1482 		nwam_value_free(classval);
1483 		return (NWAM_ENTITY_INVALID);
1484 	}
1485 	classflags = nwam_ncu_class_to_flag((nwam_ncu_class_t)class);
1486 	nwam_value_free(classval);
1487 
1488 	if ((pte = nwam_get_prop_table_entry(ncu_prop_table, propname)) == NULL)
1489 		return (NWAM_INVALID_ARG);
1490 
1491 	if (typeflags & pte->prop_type_membership &&
1492 	    classflags & pte->prop_class_membership) {
1493 		return (NWAM_SUCCESS);
1494 	} else {
1495 		return (NWAM_ENTITY_INVALID_MEMBER);
1496 	}
1497 }
1498 
1499 /* Validate property's ncu membership and type, number and range of values */
1500 nwam_error_t
1501 nwam_ncu_validate_prop(nwam_ncu_handle_t ncuh, const char *propname,
1502     nwam_value_t value)
1503 {
1504 	nwam_error_t err;
1505 
1506 	assert(ncuh != NULL && propname != NULL);
1507 
1508 	/* First, determine if this property is valid for this ncu */
1509 	if ((err = nwam_ncu_validate_prop_membership(ncuh, propname))
1510 	    != NWAM_SUCCESS)
1511 		return (err);
1512 
1513 	return (nwam_validate_prop(ncu_prop_table, ncuh, propname, value));
1514 }
1515 
1516 /* Property-specific value validation functions follow */
1517 
1518 static nwam_error_t
1519 valid_type(nwam_value_t value)
1520 {
1521 	uint64_t type;
1522 
1523 	if (nwam_value_get_uint64(value, &type) != NWAM_SUCCESS ||
1524 	    type > NWAM_NCU_TYPE_INTERFACE)
1525 		return (NWAM_ENTITY_INVALID_VALUE);
1526 	return (NWAM_SUCCESS);
1527 }
1528 
1529 static nwam_error_t
1530 valid_class(nwam_value_t value)
1531 {
1532 	uint64_t class;
1533 
1534 	if (nwam_value_get_uint64(value, &class) != NWAM_SUCCESS ||
1535 	    class > NWAM_NCU_CLASS_IP)
1536 		return (NWAM_ENTITY_INVALID_VALUE);
1537 	return (NWAM_SUCCESS);
1538 }
1539 
1540 static nwam_error_t
1541 valid_ncp(nwam_value_t value)
1542 {
1543 	char *ncp;
1544 
1545 	if (nwam_value_get_string(value, &ncp) != NWAM_SUCCESS)
1546 		return (NWAM_ENTITY_INVALID_VALUE);
1547 	return (NWAM_SUCCESS);
1548 }
1549 
1550 static nwam_error_t
1551 valid_priority_mode(nwam_value_t value)
1552 {
1553 	uint64_t priority_mode;
1554 
1555 	if (nwam_value_get_uint64(value, &priority_mode) != NWAM_SUCCESS ||
1556 	    priority_mode > NWAM_PRIORITY_MODE_ALL)
1557 		return (NWAM_ENTITY_INVALID_VALUE);
1558 	return (NWAM_SUCCESS);
1559 }
1560 
1561 static nwam_error_t
1562 valid_ncu_activation_mode(nwam_value_t value)
1563 {
1564 	uint64_t activation_mode;
1565 
1566 	if (nwam_value_get_uint64(value, &activation_mode) != NWAM_SUCCESS)
1567 		return (NWAM_ENTITY_INVALID_VALUE);
1568 
1569 	switch (activation_mode) {
1570 	case NWAM_ACTIVATION_MODE_MANUAL:
1571 	case NWAM_ACTIVATION_MODE_PRIORITIZED:
1572 		return (NWAM_SUCCESS);
1573 	}
1574 	return (NWAM_ENTITY_INVALID_VALUE);
1575 }
1576 
1577 /* ARGSUSED0 */
1578 static nwam_error_t
1579 valid_link_autopush(nwam_value_t value)
1580 {
1581 	return (NWAM_SUCCESS);
1582 }
1583 
1584 static nwam_error_t
1585 valid_ip_version(nwam_value_t value)
1586 {
1587 	uint64_t *versions;
1588 	uint_t i, numvalues;
1589 
1590 	if (nwam_value_get_uint64_array(value, &versions, &numvalues)
1591 	    != NWAM_SUCCESS)
1592 		return (NWAM_ENTITY_INVALID_VALUE);
1593 
1594 	for (i = 0; i < numvalues; i++) {
1595 		if (versions[i] != IPV4_VERSION &&
1596 		    versions[i] != IPV6_VERSION)
1597 		return (NWAM_ENTITY_INVALID_VALUE);
1598 	}
1599 	return (NWAM_SUCCESS);
1600 }
1601 
1602 static nwam_error_t
1603 valid_addrsrc_v4(nwam_value_t value)
1604 {
1605 	uint64_t *addrsrc;
1606 	uint_t i, numvalues;
1607 
1608 	if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
1609 	    != NWAM_SUCCESS)
1610 		return (NWAM_ENTITY_INVALID_VALUE);
1611 
1612 	for (i = 0; i < numvalues; i++) {
1613 		if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
1614 		    addrsrc[i] != NWAM_ADDRSRC_STATIC)
1615 			return (NWAM_ENTITY_INVALID_VALUE);
1616 	}
1617 	return (NWAM_SUCCESS);
1618 }
1619 
1620 static nwam_error_t
1621 valid_addrsrc_v6(nwam_value_t value)
1622 {
1623 	uint64_t *addrsrc;
1624 	uint_t i, numvalues;
1625 	boolean_t dhcp_found = B_FALSE, autoconf_found = B_FALSE;
1626 
1627 	if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
1628 	    != NWAM_SUCCESS)
1629 		return (NWAM_ENTITY_INVALID_VALUE);
1630 
1631 	for (i = 0; i < numvalues; i++) {
1632 		if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
1633 		    addrsrc[i] != NWAM_ADDRSRC_STATIC &&
1634 		    addrsrc[i] != NWAM_ADDRSRC_AUTOCONF)
1635 			return (NWAM_ENTITY_INVALID_VALUE);
1636 		if (addrsrc[i] == NWAM_ADDRSRC_DHCP)
1637 			dhcp_found = B_TRUE;
1638 		if (addrsrc[i] == NWAM_ADDRSRC_AUTOCONF)
1639 			autoconf_found = B_TRUE;
1640 	}
1641 	/*
1642 	 * DHCP and AUTOCONF need to be specified as v6 address sources
1643 	 * since there is no way to switch them off in NWAM at present.
1644 	 */
1645 	if (dhcp_found && autoconf_found)
1646 		return (NWAM_SUCCESS);
1647 	else
1648 		return (NWAM_ENTITY_INVALID_VALUE);
1649 }
1650 
1651 /* ARGSUSED0 */
1652 static nwam_error_t
1653 valid_link_mtu(nwam_value_t value)
1654 {
1655 	return (NWAM_SUCCESS);
1656 }
1657 
1658 nwam_error_t
1659 nwam_ncu_validate(nwam_ncu_handle_t ncuh, const char **errpropp)
1660 {
1661 	return (nwam_validate(ncu_prop_table, ncuh, errpropp));
1662 }
1663 
1664 /*
1665  * Given the ncu type and ncu class, return the list of properties that needs
1666  * to be set. Note this list is a complete property list that includes both
1667  * the required ones and the optional ones. Caller needs to free prop_list.
1668  */
1669 nwam_error_t
1670 nwam_ncu_get_default_proplist(nwam_ncu_type_t type, nwam_ncu_class_t class,
1671     const char ***prop_list, uint_t *numvalues)
1672 {
1673 	uint64_t typeflags = nwam_ncu_type_to_flag(type);
1674 	uint64_t classflags = nwam_ncu_class_to_flag(class);
1675 
1676 	return (nwam_get_default_proplist(ncu_prop_table, typeflags,
1677 	    classflags, prop_list, numvalues));
1678 }
1679 
1680 nwam_error_t
1681 nwam_ncp_get_state(nwam_ncp_handle_t ncph, nwam_state_t *statep,
1682     nwam_aux_state_t *auxp)
1683 {
1684 	return (nwam_get_state(ncph->nwh_name, ncph, statep, auxp));
1685 }
1686 
1687 nwam_error_t
1688 nwam_ncu_get_state(nwam_ncu_handle_t ncuh, nwam_state_t *statep,
1689     nwam_aux_state_t *auxp)
1690 {
1691 	nwam_ncp_handle_t ncph;
1692 	char *ncpname;
1693 	nwam_error_t err;
1694 
1695 	assert(ncuh != NULL);
1696 
1697 	if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
1698 		return (err);
1699 	if (!nwam_ncp_is_active(ncph)) {
1700 		nwam_ncp_free(ncph);
1701 		return (NWAM_ENTITY_INVALID);
1702 	}
1703 	nwam_ncp_free(ncph);
1704 
1705 	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
1706 	    != NWAM_SUCCESS)
1707 		return (err);
1708 
1709 	err = nwam_request_state(NWAM_OBJECT_TYPE_NCU, ncuh->nwh_name, ncpname,
1710 	    statep, auxp);
1711 	free(ncpname);
1712 	return (err);
1713 }
1714 
1715 nwam_error_t
1716 nwam_ncp_get_active_priority_group(int64_t *priorityp)
1717 {
1718 	return (nwam_request_active_priority_group(priorityp));
1719 }
1720