OpenSolaris_b135/lib/libnwam/common/libnwam_ncp.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <assert.h>
#include <ctype.h>
#include <libgen.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libdladm.h>

#include "libnwam_impl.h"
#include <libnwam_priv.h>
#include <libnwam.h>

/*
 * Functions to support creating, modifying, destroying, querying the
 * state of and changing the state of NCP (Network Configuration Profiles)
 * and the NCUs (Network Configuration Units) that are contained in those
 * NCP objects.  An NCP is simply a container for a set of NCUs which represent
 * the datalink and interface configuration preferences for the system.
 * An NCP can consist a set of prioritized link NCUs, e.g. wired links preferred
 * over wireless, a set of manually enabled/diasbled NCUs, or a combination
 * of both. Interface NCUs inherit activation from their underlying links,
 * so if wired is preferred over wireless and a cable is plugged in,
 * the wired link NCU will be active, as will the IP interface NCU above it.
 */

/*
 * The NCU property table is used to mapping property types to property name
 * strings, their associated value types etc. The table is used for validation
 * purposes, and for commit()ing and read()ing NCUs.
 */

static nwam_error_t valid_type(nwam_value_t);
static nwam_error_t valid_class(nwam_value_t);
static nwam_error_t valid_ncp(nwam_value_t);
static nwam_error_t valid_priority_mode(nwam_value_t);
static nwam_error_t valid_ncu_activation_mode(nwam_value_t);
static nwam_error_t valid_link_autopush(nwam_value_t);
static nwam_error_t valid_link_mtu(nwam_value_t);
static nwam_error_t valid_ip_version(nwam_value_t);
static nwam_error_t valid_addrsrc_v4(nwam_value_t);
static nwam_error_t valid_addrsrc_v6(nwam_value_t);

struct nwam_prop_table_entry ncu_prop_table_entries[] = {
	{NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1, valid_type,
	    "specifies the NCU type - valid values are \'datalink\' and \'ip\'",
	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
	{NWAM_NCU_PROP_CLASS, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
	    valid_class,
	    "specifies the NCU class - valid values are "
	    "\'phys\' and \'ip\'",
	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
	{NWAM_NCU_PROP_PARENT_NCP, NWAM_VALUE_TYPE_STRING, B_FALSE, 1, 1,
	    valid_ncp,
	    "specifies the parent NCP name",
	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
	{NWAM_NCU_PROP_ACTIVATION_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
	    valid_ncu_activation_mode,
	    "specifies the NCU activation mode - valid values are:\n"
	    "\'prioritized\' and \'manual\'",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_ENABLED, NWAM_VALUE_TYPE_BOOLEAN, B_TRUE, 0, 1,
	    nwam_valid_boolean,
	    "specifies if manual NCU is to be enabled",
	    NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
	{NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
	    nwam_valid_uint64,
	    "specifies the priority grouping of NCUs - lower values are "
	    "prioritized, negative values are invalid",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_PRIORITY_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
	    valid_priority_mode,
	    "specifies the mode of prioritization - valid values are:\n"
	    "\'exclusive\', \'shared\' and \'all\'",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_LINK_MAC_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
	    nwam_valid_mac_addr,
	    "specifies MAC address of form aa:bb:cc:dd:ee:ff for the link",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_LINK_AUTOPUSH, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, valid_link_autopush,
	    "specifies modules to autopush on link",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_LINK_MTU, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
	    valid_link_mtu,
	    "specifies MTU for link",
	    NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
	{NWAM_NCU_PROP_IP_VERSION, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, valid_ip_version,
	    "specifies IP versions for IP NCU - valid values are:\n"
	    "\'ipv4\' and \'ipv6\'",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, valid_addrsrc_v4,
	    "specifies IPv4 address source(s) - valid values are:\n"
	    "\'dhcp\' and \'static\'",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV4_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, nwam_valid_host_v4,
	    "specifies static IPv4 host address(es)",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
	    1, nwam_valid_route_v4,
	    "specifies per-interface default IPv4 route",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, valid_addrsrc_v6,
	    "specifies IPv6 address source(s) - valid values are:\n"
	    "\'dhcp\', \'autoconf\' and \'static\'.\n"
	    "\'dhcp\' and \'autoconf\' are mandatory values.",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV6_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
	    NWAM_MAX_NUM_VALUES, nwam_valid_host_v6,
	    "specifies static IPv6 host address(es)",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
	{NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
	    1, nwam_valid_route_v6,
	    "specifies per-interface default IPv6 route",
	    NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE}
};

#define	NWAM_NUM_NCU_PROPS	(sizeof (ncu_prop_table_entries) / \
				sizeof (*ncu_prop_table_entries))

struct nwam_prop_table ncu_prop_table =
	{ NWAM_NUM_NCU_PROPS, ncu_prop_table_entries };

nwam_error_t
nwam_ncp_get_name(nwam_ncp_handle_t ncph, char **namep)
{
	return (nwam_get_name(ncph, namep));
}

static nwam_error_t
nwam_ncp_name_to_file(const char *name, char **filename)
{
	assert(name != NULL && filename != NULL);

	if ((*filename = malloc(MAXPATHLEN)) == NULL)
		return (NWAM_NO_MEMORY);

	(void) snprintf(*filename, MAXPATHLEN, "%s%s%s%s", NWAM_CONF_DIR,
	    NWAM_NCP_CONF_FILE_PRE, name, NWAM_NCP_CONF_FILE_SUF);

	return (NWAM_SUCCESS);
}

/* ARGSUSED1 */
nwam_error_t
nwam_ncp_create(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
{
	nwam_error_t err;
	char *ncpfile;

	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, name, ncphp))
	    != NWAM_SUCCESS)
		return (err);

	/* Create empty container for NCUs */
	if ((err = nwam_ncp_name_to_file(name, &ncpfile))
	    != NWAM_SUCCESS) {
		nwam_free(*ncphp);
		*ncphp = NULL;
		return (err);
	}

	if ((err = nwam_commit(ncpfile, *ncphp, flags)) != NWAM_SUCCESS) {
		nwam_free(*ncphp);
		*ncphp = NULL;
	}

	free(ncpfile);

	return (err);
}

/* Used by libnwam_files.c */
nwam_error_t
nwam_ncp_file_to_name(const char *path, char **name)
{
	char path_copy[MAXPATHLEN];
	char *filename, *suffix;

	assert(path != NULL && name != NULL);

	/* Make a copy as basename(3c) may modify string */
	(void) strlcpy(path_copy, path, MAXPATHLEN);

	if ((*name = malloc(NWAM_MAX_NAME_LEN)) == NULL)
		return (NWAM_NO_MEMORY);

	if ((filename = basename(path_copy)) == NULL) {
		free(*name);
		return (NWAM_ENTITY_INVALID);
	}

	/* Ensure filename begins/ends with right prefix/suffix */
	if (sscanf(filename, NWAM_NCP_CONF_FILE_PRE "%256[^\n]s", *name) < 1) {
		free(*name);
		return (NWAM_ENTITY_INVALID);
	}
	suffix = *name + strlen(*name) - strlen(NWAM_NCP_CONF_FILE_SUF);
	if (strstr(*name, NWAM_NCP_CONF_FILE_SUF) != suffix) {
		free(*name);
		return (NWAM_ENTITY_INVALID);
	}
	suffix[0] = '\0';

	return (NWAM_SUCCESS);
}

/* ARGSUSED1 */
nwam_error_t
nwam_ncp_read(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
{
	char *filename;
	nwam_error_t err;

	assert(name != NULL && ncphp != NULL);

	/* try to read the associated ncp configuration */
	if ((err = nwam_ncp_name_to_file(name, &filename)) != NWAM_SUCCESS) {
		*ncphp = NULL;
		return (err);
	}

	err = nwam_read(NWAM_OBJECT_TYPE_NCP, filename, name, flags, ncphp);
	free(filename);
	return (err);
}

static nwam_error_t
nwam_ncu_get_parent_ncp_name(nwam_ncu_handle_t ncuh, char **parentnamep)
{
	nwam_value_t parentval = NULL;
	char *parentname;
	nwam_error_t err;

	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_PARENT_NCP,
	    &parentval)) != NWAM_SUCCESS ||
	    (err = nwam_value_get_string(parentval, &parentname))
	    != NWAM_SUCCESS ||
	    (*parentnamep = strdup(parentname)) == NULL) {
		if (parentval != NULL)
			nwam_value_free(parentval);
		*parentnamep = NULL;
		return (err);
	}
	nwam_value_free(parentval);

	return (NWAM_SUCCESS);
}

static int
nwam_ncp_copy_callback(nwam_ncu_handle_t oldncuh, void *arg)
{
	nwam_error_t err;
	nwam_ncu_handle_t newncuh = NULL;
	char *oldparent;
	char *oldfilename = NULL, *newfilename = NULL;
	nwam_ncp_handle_t newncph = (nwam_ncp_handle_t)arg;
	nwam_value_t newparentval;

	/* Get filenames for the new and old NCU's */
	if ((err = nwam_ncu_get_parent_ncp_name(oldncuh, &oldparent))
	    != NWAM_SUCCESS)
		return (err);
	err = nwam_ncp_name_to_file(oldparent, &oldfilename);
	free(oldparent);
	if (err != NWAM_SUCCESS)
		return (err);
	if ((err = nwam_ncp_name_to_file(newncph->nwh_name, &newfilename))
	    != NWAM_SUCCESS)
		goto fail;

	/* new NCU name (and typedname) is the same as the old name */
	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, oldncuh->nwh_name,
	    &newncuh)) != NWAM_SUCCESS)
		goto fail;
	/* Duplicate the old NCU's data */
	if ((err = nwam_dup_object_list(oldncuh->nwh_data,
	    &(newncuh->nwh_data))) != NWAM_SUCCESS)
		goto fail;

	/* Update the parent property for the new NCU */
	if ((err = nwam_value_create_string(newncph->nwh_name, &newparentval))
	    != NWAM_SUCCESS)
		goto fail;
	err = nwam_set_prop_value(newncuh->nwh_data, NWAM_NCU_PROP_PARENT_NCP,
	    newparentval);
	nwam_value_free(newparentval);
	if (err != NWAM_SUCCESS)
		goto fail;

	/* Save the new NCU */
	err = nwam_commit(newfilename, newncuh, 0);

fail:
	free(oldfilename);
	free(newfilename);
	nwam_ncu_free(newncuh);
	return (err);
}

nwam_error_t
nwam_ncp_copy(nwam_ncp_handle_t oldncph, const char *newname,
    nwam_ncp_handle_t *newncphp)
{
	nwam_ncp_handle_t ncph;
	nwam_error_t err;
	int cb_ret;

	assert(oldncph != NULL && newname != NULL && newncphp != NULL);

	/* check if newname NCP already exists */
	if (nwam_ncp_read(newname, 0,  &ncph) == NWAM_SUCCESS) {
		nwam_ncp_free(ncph);
		*newncphp = NULL;
		return (NWAM_ENTITY_EXISTS);
	}

	/* create new handle */
	if ((err = nwam_ncp_create(newname, 0, newncphp)) != NWAM_SUCCESS)
		return (err);

	err = nwam_ncp_walk_ncus(oldncph, nwam_ncp_copy_callback, *newncphp,
	    NWAM_FLAG_NCU_TYPE_CLASS_ALL, &cb_ret);
	if (err != NWAM_SUCCESS) {
		/* remove the NCP even if any NCU's had already been copied */
		(void) nwam_ncp_destroy(*newncphp, 0);
		*newncphp = NULL;
		if (err == NWAM_WALK_HALTED)
			return (cb_ret);
		else
			return (err);
	}

	return (NWAM_SUCCESS);
}

/*
 * Convert type to flag
 */
static uint64_t
nwam_ncu_type_to_flag(nwam_ncu_type_t type)
{
	switch (type) {
	case NWAM_NCU_TYPE_LINK:
		return (NWAM_FLAG_NCU_TYPE_LINK);
	case NWAM_NCU_TYPE_INTERFACE:
		return (NWAM_FLAG_NCU_TYPE_INTERFACE);
	case NWAM_NCU_TYPE_ANY:
		return (NWAM_FLAG_NCU_TYPE_ALL);
	default:
		return (0);
	}
}

/*
 * Convert class to flag
 */
uint64_t
nwam_ncu_class_to_flag(nwam_ncu_class_t class)
{
	switch (class) {
	case NWAM_NCU_CLASS_PHYS:
		return (NWAM_FLAG_NCU_CLASS_PHYS);
	case NWAM_NCU_CLASS_IP:
		return (NWAM_FLAG_NCU_CLASS_IP);
	case NWAM_NCU_CLASS_ANY:
		return (NWAM_FLAG_NCU_CLASS_ALL);
	default:
		return (0);
	}
}

/*
 * Infer NCU type from NCU class
 */
nwam_ncu_type_t
nwam_ncu_class_to_type(nwam_ncu_class_t class)
{
	switch (class) {
	case NWAM_NCU_CLASS_PHYS:
		return (NWAM_NCU_TYPE_LINK);
	case NWAM_NCU_CLASS_IP:
		return (NWAM_NCU_TYPE_INTERFACE);
	case NWAM_NCU_CLASS_ANY:
		return (NWAM_NCU_TYPE_ANY);
	default:
		return (NWAM_NCU_TYPE_UNKNOWN);
	}
}

/*
 * Make ncp active, deactivating any other active ncp.
 */
nwam_error_t
nwam_ncp_enable(nwam_ncp_handle_t ncph)
{
	nwam_error_t err;
	char *name;

	assert(ncph != NULL);

	err = nwam_enable(NULL, ncph);

	if (err == NWAM_ERROR_BIND) {
		/*
		 * nwamd is not running, set active_ncp property so when
		 * nwamd is next started, this NCP will be used.
		 */
		if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
			return (err);

		err = nwam_set_smf_string_property(NWAM_FMRI, NWAM_PG,
		    NWAM_PROP_ACTIVE_NCP, name);
		free(name);
	}

	return (err);
}

/* Compare NCP names c1 and c2 using strcasecmp() */
static int
ncpname_cmp(const void *c1, const void *c2)
{
	return (strcasecmp(*(const char **)c1, *(const char **)c2));
}

/* ARGSUSED1 */
nwam_error_t
nwam_walk_ncps(int (*cb)(nwam_ncp_handle_t, void *), void *data,
    uint64_t flags, int *retp)
{
	char *ncpname, **ncpfiles;
	nwam_ncp_handle_t ncph;
	nwam_error_t err;
	nwam_value_t value;
	void *objlist;
	uint_t i, num_ncpfiles;
	int ret = 0;

	assert(cb != NULL);

	if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
		return (err);
	/*
	 * To get list of NCP files, call nwam_read_object_from_backend()
	 * with "parent" argument set to NULL. We get back an object list
	 * consisting of string arrays for each object type - NCP, ENM
	 * and location. We retrieve the NCP list, which corresponds to
	 * the set of NCP backend parent objects (these are files at present).
	 */
	if ((err = nwam_read_object_from_backend(NULL, NULL, flags,
	    &objlist)) != NWAM_SUCCESS)
		return (err);

	if ((err = nwam_get_prop_value(objlist, NWAM_NCP_OBJECT_STRING, &value))
	    != NWAM_SUCCESS) {
		nwam_free_object_list(objlist);
		return (err);
	}
	if ((err = nwam_value_get_string_array(value, &ncpfiles,
	    &num_ncpfiles)) != NWAM_SUCCESS) {
		nwam_value_free(value);
		nwam_free_object_list(objlist);
		return (err);
	}

	/* sort the NCP names alphabetically */
	qsort(ncpfiles, num_ncpfiles, sizeof (char *), ncpname_cmp);

	for (i = 0; i < num_ncpfiles; i++) {
		if (nwam_ncp_file_to_name(ncpfiles[i], &ncpname)
		    != NWAM_SUCCESS)
			continue;
		if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, ncpname,
		    &ncph)) != NWAM_SUCCESS) {
			free(ncpname);
			break;
		}
		ret = cb(ncph, data);
		free(ncph);
		free(ncpname);
		if (ret != 0) {
			err = NWAM_WALK_HALTED;
			break;
		}
	}
	nwam_value_free(value);
	nwam_free_object_list(objlist);

	if (retp != NULL)
		*retp = ret;
	return (err);
}

/*
 * Checks if NCP is read-only.  Only NWAM_NCP_NAME_AUTOMATIC is read-only
 * for all but the netadm user (which nwamd runs as).
 */
nwam_error_t
nwam_ncp_get_read_only(nwam_ncp_handle_t ncph, boolean_t *readp)
{
	nwam_error_t err;
	char *name;

	assert(ncph != NULL && readp != NULL);

	if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
		return (err);

	if (NWAM_NCP_AUTOMATIC(name))
		*readp = !nwam_uid_is_netadm();
	else
		*readp = B_FALSE;

	free(name);
	return (NWAM_SUCCESS);
}

/* Checks if NCU is writable depending on its parent */
nwam_error_t
nwam_ncu_get_read_only(nwam_ncu_handle_t ncuh, boolean_t *readp)
{
	nwam_error_t err;
	nwam_ncp_handle_t ncph;

	assert(ncuh != NULL && readp != NULL);

	if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
		return (err);

	err = nwam_ncp_get_read_only(ncph, readp);
	nwam_ncp_free(ncph);
	return (err);
}

/* Returns true if the NCP is active */
static boolean_t
nwam_ncp_is_active(nwam_ncp_handle_t ncph)
{
	char *active_ncp, *name;
	boolean_t ret;

	assert(ncph != NULL);

	/*
	 * Determine which NCP is active via the nwamd/active_ncp property
	 * value.  This allows us to determine which NCP is active even
	 * if nwamd is not running.
	 */
	if (nwam_ncp_get_name(ncph, &name) != NWAM_SUCCESS ||
	    nwam_get_smf_string_property(NWAM_FMRI, NWAM_PG,
	    NWAM_PROP_ACTIVE_NCP, &active_ncp) != NWAM_SUCCESS)
		return (B_FALSE);

	ret = (strcmp(name, active_ncp) == 0);

	free(active_ncp);
	free(name);

	return (ret);
}

nwam_error_t
nwam_ncp_destroy(nwam_ncp_handle_t ncph, uint64_t flags)
{
	char *filename;
	nwam_error_t err;
	boolean_t read_only;

	assert(ncph != NULL);

	if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_NOT_DESTROYABLE);

	if (nwam_ncp_is_active(ncph))
		return (NWAM_ENTITY_IN_USE);

	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &filename))
	    != NWAM_SUCCESS)
		return (err);

	err = nwam_destroy(filename, ncph, flags);
	free(filename);

	return (NWAM_SUCCESS);
}

static nwam_error_t
nwam_ncu_internal_name_to_name(const char *internalname,
    nwam_ncu_type_t *typep, char **namep)
{
	char *prefixstr;

	assert(internalname != NULL && namep != NULL);

	if (strncasecmp(internalname, NWAM_NCU_LINK_NAME_PRE,
	    strlen(NWAM_NCU_LINK_NAME_PRE)) == 0) {
		prefixstr = NWAM_NCU_LINK_NAME_PRE;
		*typep = NWAM_NCU_TYPE_LINK;
	} else if (strncasecmp(internalname, NWAM_NCU_INTERFACE_NAME_PRE,
	    strlen(NWAM_NCU_INTERFACE_NAME_PRE)) == 0) {
		prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
		*typep = NWAM_NCU_TYPE_INTERFACE;
	} else {
		return (NWAM_INVALID_ARG);
	}

	*namep = strdup(internalname + strlen(prefixstr));
	if (*namep == NULL)
		return (NWAM_NO_MEMORY);
	return (NWAM_SUCCESS);
}

/* ARGSUSED2 */
static int
ncu_selectcb(struct nwam_handle *hp, uint64_t flags, void *data)
{
	nwam_ncu_handle_t ncuh = hp;
	nwam_value_t typeval = NULL, classval = NULL;
	uint64_t type, class, matchflags, walkfilter;

	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
	    != NWAM_SUCCESS ||
	    nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
	    != NWAM_SUCCESS) {
		if (typeval != NULL)
			nwam_value_free(typeval);
		return (NWAM_INVALID_ARG);
	}
	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS ||
	    nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
		nwam_value_free(typeval);
		nwam_value_free(classval);
		return (NWAM_INVALID_ARG);
	}

	matchflags = nwam_ncu_type_to_flag(type) |
	    nwam_ncu_class_to_flag(class);
	nwam_value_free(typeval);
	nwam_value_free(classval);

	if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
		walkfilter = NWAM_FLAG_NCU_TYPE_CLASS_ALL;

	if (matchflags & walkfilter)
		return (NWAM_SUCCESS);
	return (NWAM_INVALID_ARG);
}

nwam_error_t
nwam_ncp_walk_ncus(nwam_ncp_handle_t ncph,
    int(*cb)(nwam_ncu_handle_t, void *), void *data, uint64_t flags, int *retp)
{
	char *ncpfile;
	nwam_error_t err;

	assert(ncph != NULL && cb != NULL);

	if ((err = nwam_valid_flags(flags,
	    NWAM_FLAG_NCU_TYPE_CLASS_ALL | NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
		return (err);

	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
	    != NWAM_SUCCESS)
		return (err);

	err = nwam_walk(NWAM_OBJECT_TYPE_NCU, ncpfile, cb, data, flags,
	    retp, ncu_selectcb);
	free(ncpfile);

	return (err);
}

void
nwam_ncp_free(nwam_ncp_handle_t ncph)
{
	nwam_free(ncph);
}

/*
 * Are ncu type and class compatible?
 */
static boolean_t
nwam_ncu_type_class_compatible(nwam_ncu_type_t type, nwam_ncu_class_t class)
{
	switch (type) {
	case NWAM_NCU_TYPE_LINK:
		return (class == NWAM_NCU_CLASS_PHYS);
	case NWAM_NCU_TYPE_INTERFACE:
		return (class == NWAM_NCU_CLASS_IP);
	default:
		return (B_FALSE);
	}
}

/* Name to validate may be internal name. If so, convert it before validating */
static boolean_t
valid_ncu_name(const char *name)
{
	char *n;
	boolean_t ret;
	nwam_ncu_type_t type;

	if (nwam_ncu_internal_name_to_name(name, &type, &n) == NWAM_SUCCESS) {

		ret = dladm_valid_linkname(n);
		free(n);
	} else {
		ret = dladm_valid_linkname(name);
	}

	return (ret);
}

nwam_error_t
nwam_ncu_create(nwam_ncp_handle_t ncph, const char *name,
    nwam_ncu_type_t type, nwam_ncu_class_t class, nwam_ncu_handle_t *ncuhp)
{
	nwam_ncu_handle_t ncuh;
	nwam_value_t typeval = NULL, classval = NULL, parentval = NULL;
	nwam_value_t enabledval = NULL;
	nwam_error_t err;
	boolean_t read_only;
	char *typedname;

	assert(ncph != NULL && name != NULL && ncuhp != NULL);

	if (!valid_ncu_name(name))
		return (NWAM_INVALID_ARG);

	if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_READ_ONLY);

	if (nwam_ncu_read(ncph, name, type, 0, &ncuh) == NWAM_SUCCESS) {
		nwam_ncu_free(ncuh);
		return (NWAM_ENTITY_EXISTS);
	}

	if (!valid_ncu_name(name) ||
	    !nwam_ncu_type_class_compatible(type, class))
		return (NWAM_INVALID_ARG);

	if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname))
	    != NWAM_SUCCESS)
		return (err);

	/* Create handle */
	if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typedname, ncuhp))
	    != NWAM_SUCCESS)
		return (err);
	free(typedname);

	/*
	 * Create new object list for NCU.  The new NCU is initialized with
	 * the appropriate type and class.
	 */
	if ((err = nwam_alloc_object_list(&(*ncuhp)->nwh_data)) != NWAM_SUCCESS)
		goto finish;

	if ((err = nwam_value_create_uint64(type, &typeval))
	    != NWAM_SUCCESS ||
	    (err = nwam_value_create_uint64(class, &classval))
	    != NWAM_SUCCESS ||
	    (err = nwam_value_create_string(ncph->nwh_name, &parentval))
	    != NWAM_SUCCESS ||
	    (err = nwam_value_create_boolean(B_TRUE, &enabledval))
	    != NWAM_SUCCESS) {
		goto finish;
	}
	if ((err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_TYPE,
	    typeval)) != NWAM_SUCCESS ||
	    (err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_CLASS,
	    classval)) != NWAM_SUCCESS ||
	    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
	    NWAM_NCU_PROP_PARENT_NCP, parentval)) != NWAM_SUCCESS ||
	    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
	    NWAM_NCU_PROP_ENABLED, enabledval)) != NWAM_SUCCESS) {
		goto finish;
	}

	/* Set default IP, datalink properties */
	if (type == NWAM_NCU_TYPE_INTERFACE && class == NWAM_NCU_CLASS_IP) {

		uint64_t ver[] = { IPV4_VERSION, IPV6_VERSION };
		uint64_t v6src[] = { NWAM_ADDRSRC_DHCP, NWAM_ADDRSRC_AUTOCONF };
		uint_t vercnt = 2, v6srccnt = 2;
		nwam_value_t ipver = NULL, v4addrsrc = NULL, v6addrsrc = NULL;

		if ((err = nwam_value_create_uint64_array(ver, vercnt, &ipver))
		    != NWAM_SUCCESS ||
		    (err = nwam_value_create_uint64(NWAM_ADDRSRC_DHCP,
		    &v4addrsrc)) != NWAM_SUCCESS ||
		    (err = nwam_value_create_uint64_array(v6src, v6srccnt,
		    &v6addrsrc)) != NWAM_SUCCESS) {
			nwam_value_free(ipver);
			nwam_value_free(v4addrsrc);
			goto finish;
		}
		if ((err = nwam_set_prop_value((*ncuhp)->nwh_data,
		    NWAM_NCU_PROP_IP_VERSION, ipver)) == NWAM_SUCCESS &&
		    (err = nwam_set_prop_value((*ncuhp)->nwh_data,
		    NWAM_NCU_PROP_IPV4_ADDRSRC, v4addrsrc)) == NWAM_SUCCESS) {
			err = nwam_set_prop_value((*ncuhp)->nwh_data,
			    NWAM_NCU_PROP_IPV6_ADDRSRC, v6addrsrc);
		}
		nwam_value_free(ipver);
		nwam_value_free(v4addrsrc);
		nwam_value_free(v6addrsrc);
	} else {
		nwam_value_t actval = NULL;
		if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL,
		    &actval)) != NWAM_SUCCESS)
			goto finish;
		err = nwam_set_prop_value((*ncuhp)->nwh_data,
		    NWAM_NCU_PROP_ACTIVATION_MODE, actval);
		nwam_value_free(actval);
	}

finish:
	nwam_value_free(typeval);
	nwam_value_free(classval);
	nwam_value_free(parentval);
	nwam_value_free(enabledval);
	if (err != NWAM_SUCCESS) {
		nwam_ncu_free(*ncuhp);
		*ncuhp = NULL;
	}
	return (err);
}

nwam_error_t
nwam_ncu_read(nwam_ncp_handle_t ncph, const char *name,
    nwam_ncu_type_t type, uint64_t flags, nwam_ncu_handle_t *ncuhp)
{
	char *ncpfile, *typedname;
	nwam_error_t err, err_ip, err_link;
	nwam_ncu_handle_t ncuh_ip, ncuh_link;

	assert(ncph != NULL && name != NULL && ncuhp != NULL);

	if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
	    != NWAM_SUCCESS)
		return (err);

	if (type == NWAM_NCU_TYPE_ANY) {

		free(ncpfile);

		/*
		 * If we get to this point, we have discovered that no
		 * NCU type is discernable from name or type arguments.
		 * Either exactly one NCU called name must exist of either
		 * type, or the operation should fail.
		 */
		err_ip = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_INTERFACE,
		    flags, &ncuh_ip);
		err_link = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK,
		    flags, &ncuh_link);

		*ncuhp = NULL;

		if (err_ip == NWAM_SUCCESS && err_link == NWAM_SUCCESS) {
			nwam_ncu_free(ncuh_ip);
			nwam_ncu_free(ncuh_link);
			err = NWAM_ENTITY_MULTIPLE_VALUES;
		} else if (err_ip != NWAM_SUCCESS && err_link != NWAM_SUCCESS) {
			err = NWAM_ENTITY_NOT_FOUND;
		} else {
			if (err_ip == NWAM_SUCCESS) {
				*ncuhp = ncuh_ip;
			} else {
				*ncuhp = ncuh_link;
			}
			err = NWAM_SUCCESS;
		}

		return (err);
	}
	if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname)) !=
	    NWAM_SUCCESS) {
		free(ncpfile);
		return (err);
	}
	err = nwam_read(NWAM_OBJECT_TYPE_NCU, ncpfile, typedname, flags, ncuhp);

	free(typedname);
	free(ncpfile);

	return (err);
}

nwam_error_t
nwam_ncu_get_name(nwam_ncu_handle_t ncuh, char **namep)
{
	nwam_ncu_type_t type;

	assert(ncuh != NULL && namep != NULL);

	return (nwam_ncu_internal_name_to_name(ncuh->nwh_name, &type, namep));
}

nwam_error_t
nwam_ncu_name_to_typed_name(const char *name, nwam_ncu_type_t type,
    char **typednamep)
{
	char *prefixstr;
	size_t typednamesz;

	assert(name != NULL && typednamep != NULL);

	switch (type) {
	case NWAM_NCU_TYPE_INTERFACE:
		prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
		break;
	case NWAM_NCU_TYPE_LINK:
		prefixstr = NWAM_NCU_LINK_NAME_PRE;
		break;
	default:
		return (NWAM_INVALID_ARG);
	}
	typednamesz = strlen(name) + strlen(prefixstr) + 1;
	if ((*typednamep = malloc(typednamesz)) == NULL)
		return (NWAM_NO_MEMORY);

	/* Name may be already qualified by type */
	if (strncasecmp(prefixstr, name, strlen(prefixstr)) == 0) {
		(void) snprintf(*typednamep, typednamesz, "%s", name);
	} else {
		(void) snprintf(*typednamep, typednamesz, "%s%s",
		    prefixstr, name);
	}

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_ncu_typed_name_to_name(const char *typed_name, nwam_ncu_type_t *typep,
    char **name)
{
	return (nwam_ncu_internal_name_to_name(typed_name, typep, name));
}

void
nwam_ncu_free(nwam_ncu_handle_t ncuh)
{
	nwam_free(ncuh);
}

nwam_error_t
nwam_ncu_copy(nwam_ncu_handle_t oldncuh, const char *newname,
    nwam_ncu_handle_t *newncuhp)
{
	nwam_ncp_handle_t ncph;
	nwam_ncu_handle_t ncuh;
	nwam_error_t err;
	nwam_value_t typeval;
	uint64_t type;
	char *typednewname;

	assert(oldncuh != NULL && newname != NULL && newncuhp != NULL);

	if (nwam_ncu_get_prop_value(oldncuh, NWAM_NCU_PROP_TYPE,
	    &typeval) != NWAM_SUCCESS) {
		return (NWAM_INVALID_ARG);
	}
	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
		nwam_value_free(typeval);
		return (NWAM_INVALID_ARG);
	}
	nwam_value_free(typeval);

	/* check if newname NCU already exists */
	if ((err = nwam_ncu_get_ncp(oldncuh, &ncph)) != NWAM_SUCCESS)
		return (err);
	if (nwam_ncu_read(ncph, newname, type, 0, &ncuh) == NWAM_SUCCESS) {
		nwam_ncu_free(ncuh);
		nwam_ncp_free(ncph);
		return (NWAM_ENTITY_EXISTS);
	}
	nwam_ncp_free(ncph);

	if ((err = nwam_ncu_name_to_typed_name(newname, type, &typednewname))
	    != NWAM_SUCCESS)
		return (err);

	err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typednewname, newncuhp);
	free(typednewname);
	if (err != NWAM_SUCCESS)
		return (err);
	if ((err = nwam_dup_object_list(oldncuh->nwh_data,
	    &((*newncuhp)->nwh_data))) != NWAM_SUCCESS) {
		free(*newncuhp);
		*newncuhp = NULL;
		return (err);
	}

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_ncu_delete_prop(nwam_ncu_handle_t ncuh, const char *propname)
{
	boolean_t ro_ncu, ro_prop;
	nwam_error_t err;
	void *olddata;

	assert(ncuh != NULL && propname != NULL);

	if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
	    (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
		return (err);
	if (ro_ncu || ro_prop)
		return (NWAM_ENTITY_READ_ONLY);

	/*
	 * Duplicate data, remove property and validate. If validation
	 * fails, revert to data duplicated prior to remove.
	 */
	if ((err = nwam_dup_object_list(ncuh->nwh_data, &olddata))
	    != NWAM_SUCCESS)
		return (err);
	if ((err = nwam_delete_prop(ncuh->nwh_data, propname))
	    != NWAM_SUCCESS) {
		nwam_free_object_list(ncuh->nwh_data);
		ncuh->nwh_data = olddata;
		return (err);
	}
	if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS) {
		nwam_free_object_list(ncuh->nwh_data);
		ncuh->nwh_data = olddata;
		return (err);
	}
	nwam_free_object_list(olddata);

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_ncu_set_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
    nwam_value_t value)
{
	boolean_t ro_ncu, ro_prop;
	nwam_error_t err;
	nwam_ncp_handle_t ncph;

	assert(ncuh != NULL && propname != NULL && value != NULL);

	if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
	    (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
		return (err);
	if (ro_ncu || ro_prop)
		return (NWAM_ENTITY_READ_ONLY);

	err = nwam_ncu_get_ncp(ncuh, &ncph);
	if (err != NWAM_SUCCESS && err != NWAM_INVALID_ARG) {
		/*
		 * If "parent" property doesn't exist, NWAM_INVALID_ARG
		 * is returned.  Allow the setting to continue.
		 */
		return (err);
	}
	nwam_ncp_free(ncph);

	/* Need to ensure property, type and value are valid */
	if ((err = nwam_ncu_validate_prop(ncuh, propname, value))
	    != NWAM_SUCCESS)
		return (err);

	return (nwam_set_prop_value(ncuh->nwh_data, propname, value));
}

nwam_error_t
nwam_ncu_get_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
    nwam_value_t *valuep)
{
	assert(ncuh != NULL && propname != NULL && valuep != NULL);

	return (nwam_get_prop_value(ncuh->nwh_data, propname, valuep));
}

nwam_error_t
nwam_ncu_walk_props(nwam_ncu_handle_t ncuh,
    int (*cb)(const char *, nwam_value_t, void *),
    void *data, uint64_t flags, int *retp)
{
	return (nwam_walk_props(ncuh, cb, data, flags, retp));
}

nwam_error_t
nwam_ncu_get_ncp(nwam_ncu_handle_t ncuh, nwam_ncp_handle_t *ncphp)
{
	nwam_error_t err;
	char *parentname = NULL;

	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &parentname))
	    != NWAM_SUCCESS ||
	    (err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, parentname, ncphp))
	    != NWAM_SUCCESS) {
		if (parentname != NULL)
			free(parentname);
		return (err);
	}
	free(parentname);

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_ncu_commit(nwam_ncu_handle_t ncuh, uint64_t flags)
{
	nwam_error_t err;
	boolean_t read_only;
	char *ncpfile, *ncpname;

	assert(ncuh != NULL && ncuh->nwh_data != NULL);

	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_READ_ONLY);

	if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS ||
	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
	    != NWAM_SUCCESS)
		return (err);

	if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile)) != NWAM_SUCCESS) {
		free(ncpname);
		return (err);
	}

	err = nwam_commit(ncpfile, ncuh, flags);

	free(ncpname);
	free(ncpfile);

	return (err);
}
/* Get the NCU type */
nwam_error_t
nwam_ncu_get_ncu_type(nwam_ncu_handle_t ncuh, nwam_ncu_type_t *typep)
{
	nwam_error_t err;
	nwam_value_t typeval;
	uint64_t type;

	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval))
	    != NWAM_SUCCESS)
		return (err);
	err = nwam_value_get_uint64(typeval, &type);
	nwam_value_free(typeval);
	if (err != NWAM_SUCCESS)
		return (err);

	*typep = type;
	return (NWAM_SUCCESS);
}

/* Get the NCU class */
nwam_error_t
nwam_ncu_get_ncu_class(nwam_ncu_handle_t ncuh, nwam_ncu_class_t *classp)
{
	nwam_error_t err;
	nwam_value_t classval;
	uint64_t class;

	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS,
	    &classval)) != NWAM_SUCCESS)
		return (err);
	err = nwam_value_get_uint64(classval, &class);
	nwam_value_free(classval);
	if (err != NWAM_SUCCESS)
		return (err);

	*classp = class;
	return (NWAM_SUCCESS);
}

/*
 * Determine if the NCU has manual activation-mode or not.
 */
nwam_error_t
nwam_ncu_is_manual(nwam_ncu_handle_t ncuh, boolean_t *manualp)
{
	nwam_error_t err;
	nwam_value_t actval;
	uint64_t activation;

	assert(ncuh != NULL);

	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ACTIVATION_MODE,
	    &actval)) != NWAM_SUCCESS)
		return (err);
	err = nwam_value_get_uint64(actval, &activation);
	nwam_value_free(actval);
	if (err != NWAM_SUCCESS)
		return (err);

	if (activation == NWAM_ACTIVATION_MODE_MANUAL)
		*manualp = B_TRUE;
	else
		*manualp = B_FALSE;
	return (NWAM_SUCCESS);
}

/* Determine if NCU is enabled or not */
static nwam_error_t
nwam_ncu_is_enabled(nwam_ncu_handle_t ncuh, boolean_t *enabledp)
{
	nwam_error_t err;
	nwam_value_t enabledval;

	assert(ncuh != NULL);

	if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
	    &enabledval)) != NWAM_SUCCESS)
		return (err);
	err = nwam_value_get_boolean(enabledval, enabledp);
	nwam_value_free(enabledval);
	return (err);
}

/* Update the enabled property */
static nwam_error_t
nwam_ncu_update_enabled(nwam_ncu_handle_t ncuh, boolean_t enabled)
{
	nwam_error_t err;
	nwam_value_t enabledval;

	if ((err = nwam_value_create_boolean(enabled, &enabledval))
	    != NWAM_SUCCESS)
		return (err);
	err = nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_ENABLED,
	    enabledval);
	nwam_value_free(enabledval);
	if (err != NWAM_SUCCESS)
		return (err);
	return (nwam_ncu_commit(ncuh, NWAM_FLAG_ENTITY_ENABLE));
}

/*
 * Make ncu active; fails if the NCU's parent NCP is not active.
 */
nwam_error_t
nwam_ncu_enable(nwam_ncu_handle_t ncuh)
{
	char *ncpname = NULL;
	nwam_error_t err;
	nwam_ncu_type_t type;
	boolean_t read_only, enabled, manual;

	assert(ncuh != NULL);

	/* Don't allow NCUs of Automatic NCP to be enabled */
	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_NOT_MANUAL);

	/* Link NCUs with manual activation-mode or IP NCUs can be enabled */
	if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
		return (err);

	if (type == NWAM_NCU_TYPE_LINK) {
		if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
			return (err);
		if (!manual)
			return (NWAM_ENTITY_NOT_MANUAL);
	}

	/* Make sure NCU is not enabled */
	if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
	    != NWAM_SUCCESS)
		return (err);

	if (enabled) {
		free(ncpname);
		return (NWAM_SUCCESS);
	}

	if ((err = nwam_ncu_update_enabled(ncuh, B_TRUE)) != NWAM_SUCCESS) {
		free(ncpname);
		return (err);
	}

	err = nwam_enable(ncpname, ncuh);
	free(ncpname);

	/* nwamd may not be running, that's okay. */
	if (err == NWAM_ERROR_BIND)
		return (NWAM_SUCCESS);
	else
		return (err);
}

/*
 * Disable ncu; fails if the NCU's parent NCP is not active, or if the
 * NCU is not currently active.
 */
nwam_error_t
nwam_ncu_disable(nwam_ncu_handle_t ncuh)
{
	char *ncpname = NULL;
	nwam_error_t err;
	nwam_ncu_type_t type;
	boolean_t read_only, enabled, manual;

	assert(ncuh != NULL);

	/* Don't allow NCUs of Automatic NCP to be disabled */
	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_NOT_MANUAL);

	/* Link NCUs with manual activation-mode or IP NCUs can be disabled */
	if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
		return (err);

	if (type == NWAM_NCU_TYPE_LINK) {
		if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
			return (err);
		if (!manual)
			return (NWAM_ENTITY_NOT_MANUAL);
	}

	/* Make sure NCU is enabled */
	if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
	    (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
	    != NWAM_SUCCESS)
		return (err);

	if (!enabled) {
		free(ncpname);
		return (NWAM_SUCCESS);
	}

	if ((err = nwam_ncu_update_enabled(ncuh, B_FALSE)) != NWAM_SUCCESS) {
		free(ncpname);
		return (err);
	}

	err = nwam_disable(ncpname, ncuh);
	free(ncpname);

	/* nwamd may not be running, that's okay. */
	if (err == NWAM_ERROR_BIND)
		return (NWAM_SUCCESS);
	else
		return (err);
}

nwam_error_t
nwam_ncu_destroy(nwam_ncu_handle_t ncuh, uint64_t flags)
{
	char *ncpname, *ncpfile;
	boolean_t read_only;
	nwam_error_t err;

	assert(ncuh != NULL);

	if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
		return (err);
	if (read_only)
		return (NWAM_ENTITY_NOT_DESTROYABLE);

	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
	    != NWAM_SUCCESS)
		return (err);
	if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile))
	    != NWAM_SUCCESS) {
		free(ncpname);
		return (err);
	}

	err = nwam_destroy(ncpfile, ncuh, flags);

	free(ncpname);
	free(ncpfile);

	return (err);
}

nwam_error_t
nwam_ncu_get_prop_description(const char *propname, const char **descriptionp)
{
	return (nwam_get_prop_description(ncu_prop_table, propname,
	    descriptionp));
}

/* Get expected property data type */
nwam_error_t
nwam_ncu_get_prop_type(const char *propname, nwam_value_type_t *typep)
{
	return (nwam_get_prop_type(ncu_prop_table, propname, typep));
}

nwam_error_t
nwam_ncu_prop_read_only(const char *propname, boolean_t *readp)
{
	if ((*readp = NWAM_NCU_PROP_SETONCE(propname)) == B_TRUE)
		return (NWAM_SUCCESS);

	return (nwam_prop_read_only(ncu_prop_table, propname, readp));
}

nwam_error_t
nwam_ncu_prop_multivalued(const char *propname, boolean_t *multip)
{
	return (nwam_prop_multivalued(ncu_prop_table, propname, multip));
}

/*
 * Ensure that the properties in the ncu, determined by that ncu's
 * type and class, belong there.
 */
static nwam_error_t
nwam_ncu_validate_prop_membership(nwam_ncu_handle_t ncuh, const char *propname)
{
	struct nwam_prop_table_entry *pte;
	nwam_value_t typeval, classval;
	uint64_t type, class;
	uint64_t typeflags = 0, classflags = 0;

	/* Get type/class from ncu */
	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID);
	if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
		nwam_value_free(typeval);
		return (NWAM_ENTITY_INVALID);
	}
	typeflags = nwam_ncu_type_to_flag((nwam_ncu_type_t)type);
	nwam_value_free(typeval);

	if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID);
	if (nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
		nwam_value_free(classval);
		return (NWAM_ENTITY_INVALID);
	}
	classflags = nwam_ncu_class_to_flag((nwam_ncu_class_t)class);
	nwam_value_free(classval);

	if ((pte = nwam_get_prop_table_entry(ncu_prop_table, propname)) == NULL)
		return (NWAM_INVALID_ARG);

	if (typeflags & pte->prop_type_membership &&
	    classflags & pte->prop_class_membership) {
		return (NWAM_SUCCESS);
	} else {
		return (NWAM_ENTITY_INVALID_MEMBER);
	}
}

/* Validate property's ncu membership and type, number and range of values */
nwam_error_t
nwam_ncu_validate_prop(nwam_ncu_handle_t ncuh, const char *propname,
    nwam_value_t value)
{
	nwam_error_t err;

	assert(ncuh != NULL && propname != NULL);

	/* First, determine if this property is valid for this ncu */
	if ((err = nwam_ncu_validate_prop_membership(ncuh, propname))
	    != NWAM_SUCCESS)
		return (err);

	return (nwam_validate_prop(ncu_prop_table, ncuh, propname, value));
}

/* Property-specific value validation functions follow */

static nwam_error_t
valid_type(nwam_value_t value)
{
	uint64_t type;

	if (nwam_value_get_uint64(value, &type) != NWAM_SUCCESS ||
	    type > NWAM_NCU_TYPE_INTERFACE)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_class(nwam_value_t value)
{
	uint64_t class;

	if (nwam_value_get_uint64(value, &class) != NWAM_SUCCESS ||
	    class > NWAM_NCU_CLASS_IP)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_ncp(nwam_value_t value)
{
	char *ncp;

	if (nwam_value_get_string(value, &ncp) != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_priority_mode(nwam_value_t value)
{
	uint64_t priority_mode;

	if (nwam_value_get_uint64(value, &priority_mode) != NWAM_SUCCESS ||
	    priority_mode > NWAM_PRIORITY_MODE_ALL)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_ncu_activation_mode(nwam_value_t value)
{
	uint64_t activation_mode;

	if (nwam_value_get_uint64(value, &activation_mode) != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	switch (activation_mode) {
	case NWAM_ACTIVATION_MODE_MANUAL:
	case NWAM_ACTIVATION_MODE_PRIORITIZED:
		return (NWAM_SUCCESS);
	}
	return (NWAM_ENTITY_INVALID_VALUE);
}

/* ARGSUSED0 */
static nwam_error_t
valid_link_autopush(nwam_value_t value)
{
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_ip_version(nwam_value_t value)
{
	uint64_t *versions;
	uint_t i, numvalues;

	if (nwam_value_get_uint64_array(value, &versions, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		if (versions[i] != IPV4_VERSION &&
		    versions[i] != IPV6_VERSION)
		return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_addrsrc_v4(nwam_value_t value)
{
	uint64_t *addrsrc;
	uint_t i, numvalues;

	if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
		    addrsrc[i] != NWAM_ADDRSRC_STATIC)
			return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

static nwam_error_t
valid_addrsrc_v6(nwam_value_t value)
{
	uint64_t *addrsrc;
	uint_t i, numvalues;
	boolean_t dhcp_found = B_FALSE, autoconf_found = B_FALSE;

	if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
		    addrsrc[i] != NWAM_ADDRSRC_STATIC &&
		    addrsrc[i] != NWAM_ADDRSRC_AUTOCONF)
			return (NWAM_ENTITY_INVALID_VALUE);
		if (addrsrc[i] == NWAM_ADDRSRC_DHCP)
			dhcp_found = B_TRUE;
		if (addrsrc[i] == NWAM_ADDRSRC_AUTOCONF)
			autoconf_found = B_TRUE;
	}
	/*
	 * DHCP and AUTOCONF need to be specified as v6 address sources
	 * since there is no way to switch them off in NWAM at present.
	 */
	if (dhcp_found && autoconf_found)
		return (NWAM_SUCCESS);
	else
		return (NWAM_ENTITY_INVALID_VALUE);
}

/* ARGSUSED0 */
static nwam_error_t
valid_link_mtu(nwam_value_t value)
{
	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_ncu_validate(nwam_ncu_handle_t ncuh, const char **errpropp)
{
	return (nwam_validate(ncu_prop_table, ncuh, errpropp));
}

/*
 * Given the ncu type and ncu class, return the list of properties that needs
 * to be set. Note this list is a complete property list that includes both
 * the required ones and the optional ones. Caller needs to free prop_list.
 */
nwam_error_t
nwam_ncu_get_default_proplist(nwam_ncu_type_t type, nwam_ncu_class_t class,
    const char ***prop_list, uint_t *numvalues)
{
	uint64_t typeflags = nwam_ncu_type_to_flag(type);
	uint64_t classflags = nwam_ncu_class_to_flag(class);

	return (nwam_get_default_proplist(ncu_prop_table, typeflags,
	    classflags, prop_list, numvalues));
}

nwam_error_t
nwam_ncp_get_state(nwam_ncp_handle_t ncph, nwam_state_t *statep,
    nwam_aux_state_t *auxp)
{
	return (nwam_get_state(ncph->nwh_name, ncph, statep, auxp));
}

nwam_error_t
nwam_ncu_get_state(nwam_ncu_handle_t ncuh, nwam_state_t *statep,
    nwam_aux_state_t *auxp)
{
	nwam_ncp_handle_t ncph;
	char *ncpname;
	nwam_error_t err;

	assert(ncuh != NULL);

	if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
		return (err);
	if (!nwam_ncp_is_active(ncph)) {
		nwam_ncp_free(ncph);
		return (NWAM_ENTITY_INVALID);
	}
	nwam_ncp_free(ncph);

	if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
	    != NWAM_SUCCESS)
		return (err);

	err = nwam_request_state(NWAM_OBJECT_TYPE_NCU, ncuh->nwh_name, ncpname,
	    statep, auxp);
	free(ncpname);
	return (err);
}

nwam_error_t
nwam_ncp_get_active_priority_group(int64_t *priorityp)
{
	return (nwam_request_active_priority_group(priorityp));
}