OpenSolaris_b135/lib/libnwam/common/libnwam_util.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 <arpa/inet.h>
#include <assert.h>
#include <atomic.h>
#include <ctype.h>
#include <errno.h>
#include <inet/ip.h>
#include <libintl.h>
#include <libproc.h>
#include <libscf.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

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

/*
 * Utility functions for door access, common validation functions etc.
 */

pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
int nwam_door_fd = -1;

static int
open_door(const char *door_name, int *door_fdp)
{
	struct door_info dinfo;
	int err = 0;

	(void) pthread_mutex_lock(&door_mutex);

	if (*door_fdp != -1) {
		/* Check door fd is not old (from previous nwamd). */
		if (door_info(*door_fdp, &dinfo) != 0 ||
		    (dinfo.di_attributes & DOOR_REVOKED) != 0) {
			(void) close(*door_fdp);
			*door_fdp = -1;
		}
	}
	if (*door_fdp == -1) {
		*door_fdp = open(door_name, 0);
		if (*door_fdp == -1)
			err = errno;
	}

	(void) pthread_mutex_unlock(&door_mutex);

	return (err);
}

int
nwam_make_door_call(const char *door_name, int *door_fdp,
    void *request, size_t request_size)
{
	int err;
	door_arg_t door_args;

	door_args.data_ptr = (void *)request;
	door_args.data_size = request_size;
	door_args.desc_ptr = NULL;
	door_args.desc_num = 0;
	door_args.rbuf = (void *)request;
	door_args.rsize = request_size;

	if ((err = open_door(door_name, door_fdp)) != 0)
		return (err);

	if (door_call(*door_fdp, &door_args) == -1)
		return (errno);

	return (0);
}

static nwam_error_t
send_msg_to_nwam(nwamd_door_arg_t *request)
{
	int err;

	if ((err = nwam_make_door_call(NWAM_DOOR, &nwam_door_fd,
	    request, sizeof (nwamd_door_arg_t))) != 0) {
		if (err == ENOENT)
			return (NWAM_ERROR_BIND);
		return (nwam_errno_to_nwam_error(err));
	}

	switch (request->nwda_status) {
	case NWAM_REQUEST_STATUS_OK:
		return (NWAM_SUCCESS);
	case NWAM_REQUEST_STATUS_UNKNOWN:
		return (NWAM_INVALID_ARG);
	case NWAM_REQUEST_STATUS_ALREADY:
		return (NWAM_ENTITY_IN_USE);
	case NWAM_REQUEST_STATUS_FAILED:
		return (request->nwda_error);
	default:
		return (NWAM_ERROR_INTERNAL);
	}
}

nwam_error_t
nwam_request_register_unregister(nwam_request_type_t type,
    const char *event_msg_file)
{
	nwamd_door_arg_t req;

	req.nwda_type = type;

	(void) strlcpy(req.nwda_data.nwdad_register_info.nwdad_name,
	    event_msg_file,
	    sizeof (req.nwda_data.nwdad_register_info.nwdad_name));

	return (send_msg_to_nwam(&req));
}

nwam_error_t
nwam_request_action(nwam_object_type_t object_type,
    const char *name, const char *parent, nwam_action_t action)
{
	nwamd_door_arg_t req;

	assert(name != NULL);

	req.nwda_type = NWAM_REQUEST_TYPE_ACTION;
	req.nwda_data.nwdad_object_action.nwdad_object_type = object_type;
	req.nwda_data.nwdad_object_action.nwdad_action = action;
	(void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_name, name,
	    sizeof (req.nwda_data.nwdad_object_action.nwdad_name));
	if (parent != NULL) {
		(void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_parent,
		    parent,
		    sizeof (req.nwda_data.nwdad_object_action.nwdad_parent));
	} else {
		req.nwda_data.nwdad_object_action.nwdad_parent[0] = '\0';
	}

	return (send_msg_to_nwam(&req));
}

nwam_error_t
nwam_request_state(nwam_object_type_t object_type, const char *name,
    const char *parent, nwam_state_t *statep, nwam_aux_state_t *auxp)
{
	nwamd_door_arg_t req;
	nwam_error_t err;

	assert(name != NULL && statep != NULL && auxp != NULL);

	req.nwda_type = NWAM_REQUEST_TYPE_STATE;

	req.nwda_data.nwdad_object_state.nwdad_object_type = object_type;

	(void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_name, name,
	    sizeof (req.nwda_data.nwdad_object_state.nwdad_name));
	if (parent != NULL) {
		(void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_parent,
		    parent,
		    sizeof (req.nwda_data.nwdad_object_state.nwdad_parent));
	}

	err = send_msg_to_nwam(&req);

	if (err == NWAM_SUCCESS) {
		*statep = req.nwda_data.nwdad_object_state.nwdad_state;
		*auxp = req.nwda_data.nwdad_object_state.nwdad_aux_state;
	}

	return (err);
}

nwam_error_t
nwam_request_wlan(nwam_request_type_t type, const char *name,
    const char *essid, const char *bssid, uint32_t security_mode,
    uint_t keyslot, const char *key, boolean_t add_to_known_wlans)
{
	nwamd_door_arg_t req;

	assert(name != NULL);

	req.nwda_type = type;

	(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
	    sizeof (req.nwda_data.nwdad_wlan_info));
	if (essid != NULL) {
		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_essid, essid,
		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_essid));
	} else {
		req.nwda_data.nwdad_wlan_info.nwdad_essid[0] = '\0';
	}
	if (bssid != NULL) {
		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_bssid, bssid,
		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_bssid));
	} else {
		req.nwda_data.nwdad_wlan_info.nwdad_bssid[0] = '\0';
	}
	if (key != NULL) {
		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_key, key,
		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_key));
		req.nwda_data.nwdad_wlan_info.nwdad_keyslot = keyslot;
	} else {
		req.nwda_data.nwdad_wlan_info.nwdad_key[0] = '\0';
	}

	req.nwda_data.nwdad_wlan_info.nwdad_security_mode = security_mode;
	req.nwda_data.nwdad_wlan_info.nwdad_add_to_known_wlans =
	    add_to_known_wlans;

	return (send_msg_to_nwam(&req));
}

nwam_error_t
nwam_request_wlan_scan_results(const char *name, uint_t *num_wlansp,
    nwam_wlan_t **wlansp)
{
	nwamd_door_arg_t req;
	nwam_error_t err;

	assert(name != NULL && num_wlansp != NULL && wlansp != NULL);

	req.nwda_type = NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS;

	(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
	    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_name));

	if ((err = send_msg_to_nwam(&req)) != NWAM_SUCCESS)
		return (err);

	*num_wlansp = req.nwda_data.nwdad_wlan_info.nwdad_num_wlans;

	*wlansp = calloc(*num_wlansp, sizeof (nwam_wlan_t));
	if (*wlansp == NULL)
		return (NWAM_NO_MEMORY);

	(void) memcpy(*wlansp, req.nwda_data.nwdad_wlan_info.nwdad_wlans,
	    *num_wlansp * sizeof (nwam_wlan_t));

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_request_active_priority_group(int64_t *priorityp)
{
	nwamd_door_arg_t req;
	nwam_error_t err;

	assert(priorityp != NULL);

	req.nwda_type = NWAM_REQUEST_TYPE_PRIORITY_GROUP;
	err = send_msg_to_nwam(&req);

	if (err == NWAM_SUCCESS)
		*priorityp =
		    req.nwda_data.nwdad_priority_group_info.nwdad_priority;

	return (err);
}

/* String conversion functions */

const char *
nwam_value_type_to_string(nwam_value_type_t type)
{
	switch (type) {
	case NWAM_VALUE_TYPE_BOOLEAN:
		return ("boolean");
	case NWAM_VALUE_TYPE_INT64:
		return ("int64");
	case NWAM_VALUE_TYPE_UINT64:
		return ("uint64");
	case NWAM_VALUE_TYPE_STRING:
		return ("string");
	default:
		return ("unknown");
	}
}

nwam_value_type_t
nwam_string_to_value_type(const char *typestr)
{
	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN),
	    strlen(typestr)) == 0)
		return (NWAM_VALUE_TYPE_BOOLEAN);
	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64),
	    strlen(typestr)) == 0)
		return (NWAM_VALUE_TYPE_INT64);
	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64),
	    strlen(typestr)) == 0)
		return (NWAM_VALUE_TYPE_UINT64);
	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING),
	    strlen(typestr)) == 0)
		return (NWAM_VALUE_TYPE_STRING);
	return (NWAM_VALUE_TYPE_UNKNOWN);
}

const char *
nwam_action_to_string(nwam_action_t action)
{
	switch (action) {
	case NWAM_ACTION_ADD:
		return ("add");
	case NWAM_ACTION_REMOVE:
		return ("remove");
	case NWAM_ACTION_REFRESH:
		return ("refresh");
	case NWAM_ACTION_ENABLE:
		return ("enable");
	case NWAM_ACTION_DISABLE:
		return ("disable");
	case NWAM_ACTION_DESTROY:
		return ("destroy");
	default:
		return ("unknown");
	}
}

const char *
nwam_event_type_to_string(int event_type)
{
	switch (event_type) {
	case NWAM_EVENT_TYPE_NOOP:
		return ("NOOP");
	case NWAM_EVENT_TYPE_INIT:
		return ("INIT");
	case NWAM_EVENT_TYPE_SHUTDOWN:
		return ("SHUTDOWN");
	case NWAM_EVENT_TYPE_OBJECT_ACTION:
		return ("OBJECT_ACTION");
	case NWAM_EVENT_TYPE_OBJECT_STATE:
		return ("OBJECT_STATE");
	case NWAM_EVENT_TYPE_PRIORITY_GROUP:
		return ("PRIORITY_GROUP");
	case NWAM_EVENT_TYPE_INFO:
		return ("INFO");
	case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
		return ("WLAN_SCAN_REPORT");
	case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
		return ("WLAN_NEED_CHOICE");
	case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
		return ("WLAN_NEED_KEY");
	case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
		return ("WLAN_CONNECTION_REPORT");
	case NWAM_EVENT_TYPE_IF_ACTION:
		return ("IF_ACTION");
	case NWAM_EVENT_TYPE_IF_STATE:
		return ("IF_STATE");
	case NWAM_EVENT_TYPE_LINK_ACTION:
		return ("LINK_ACTION");
	case NWAM_EVENT_TYPE_LINK_STATE:
		return ("LINK_STATE");
	default:
		return ("UNKNOWN");
	}
}

const char *
nwam_state_to_string(nwam_state_t state)
{
	switch (state) {
	case NWAM_STATE_UNINITIALIZED:
		return ("uninitialized");
	case NWAM_STATE_INITIALIZED:
		return ("initialized");
	case NWAM_STATE_OFFLINE:
		return ("offline");
	case NWAM_STATE_OFFLINE_TO_ONLINE:
		return ("offline*");
	case NWAM_STATE_ONLINE_TO_OFFLINE:
		return ("online*");
	case NWAM_STATE_ONLINE:
		return ("online");
	case NWAM_STATE_MAINTENANCE:
		return ("maintenance");
	case NWAM_STATE_DEGRADED:
		return ("degraded");
	case NWAM_STATE_DISABLED:
		return ("disabled");
	default:
		return ("unknown");
	}
}

const char *
nwam_aux_state_to_string(nwam_aux_state_t aux_state)
{
	switch (aux_state) {
	case NWAM_AUX_STATE_UNINITIALIZED:
		return ("uninitialized");
	case NWAM_AUX_STATE_INITIALIZED:
		return ("(re)initialized but not configured");
	case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
		return ("conditions for activation are unmet");
	case NWAM_AUX_STATE_MANUAL_DISABLE:
		return ("disabled by administrator");
	case NWAM_AUX_STATE_METHOD_FAILED:
		return ("method/service failed");
	case NWAM_AUX_STATE_METHOD_MISSING:
		return ("method or FMRI not specified");
	case NWAM_AUX_STATE_INVALID_CONFIG:
		return ("invalid configuration values");
	case NWAM_AUX_STATE_METHOD_RUNNING:
		return ("method/service executing");
	case NWAM_AUX_STATE_ACTIVE:
		return ("active");
	case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
		return ("scanning for WiFi networks");
	case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
		return ("need WiFi network selection");
	case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
		return ("need WiFi security key");
	case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
		return ("connecting to WiFi network");
	case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
		return ("waiting for IP address to be set");
	case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
		return ("DHCP wait timeout, still trying...");
	case NWAM_AUX_STATE_IF_DUPLICATE_ADDR:
		return ("duplicate address detected");
	case NWAM_AUX_STATE_UP:
		return ("interface/link is up");
	case NWAM_AUX_STATE_DOWN:
		return ("interface/link is down");
	case NWAM_AUX_STATE_NOT_FOUND:
		return ("interface/link not found");
	default:
		return ("unknown");
	}
}

const char *
nwam_object_type_to_string(nwam_object_type_t type)
{
	switch (type) {
	case NWAM_OBJECT_TYPE_NCP:
		return ("ncp");
	case NWAM_OBJECT_TYPE_NCU:
		return ("ncu");
	case NWAM_OBJECT_TYPE_LOC:
		return ("loc");
	case NWAM_OBJECT_TYPE_ENM:
		return ("enm");
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		return ("known wlan");
	default:
		return ("unknown");
	}
}

nwam_object_type_t
nwam_string_to_object_type(const char *typestr)
{
	if (strcasecmp(typestr,
	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCP)) == 0)
		return (NWAM_OBJECT_TYPE_NCP);
	if (strcasecmp(typestr,
	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCU)) == 0)
		return (NWAM_OBJECT_TYPE_NCU);
	if (strcasecmp(typestr,
	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_LOC)) == 0)
		return (NWAM_OBJECT_TYPE_LOC);
	if (strcasecmp(typestr,
	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_ENM)) == 0)
		return (NWAM_OBJECT_TYPE_ENM);
	if (strcasecmp(typestr,
	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_KNOWN_WLAN)) == 0)
		return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
	return (NWAM_OBJECT_TYPE_UNKNOWN);
}

nwam_error_t
nwam_errno_to_nwam_error(int errnum)
{
	switch (errnum) {
	case 0:
		return (NWAM_SUCCESS);
	case EBADF:
		return (NWAM_ERROR_BIND);
	case EPERM:
	case EACCES:
		return (NWAM_PERMISSION_DENIED);
	case ENOENT:
		return (NWAM_ENTITY_NOT_FOUND);
	case EIDRM:
		return (NWAM_ENTITY_INVALID);
	case EEXIST:
		return (NWAM_ENTITY_EXISTS);
	case EAGAIN:
	case EBUSY:
		return (NWAM_ENTITY_IN_USE);
	case ENOMEM:
	case ENOSPC:
		return (NWAM_NO_MEMORY);
	case EINVAL:
	case E2BIG:
		return (NWAM_INVALID_ARG);
	default:
		return (NWAM_ERROR_INTERNAL);
	}
}

/* Common validation functions */

/*
 * Do the flags represent a subset of valid_flags?
 */
nwam_error_t
nwam_valid_flags(uint64_t flags, uint64_t valid_flags)
{

	if ((flags | valid_flags) != valid_flags)
		return (NWAM_INVALID_ARG);
	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_valid_condition(nwam_value_t value)
{
	char **conditions;
	uint_t i, numvalues;
	nwam_condition_object_type_t object_type;
	nwam_condition_t condition;

	if (nwam_value_get_string_array(value, &conditions, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		char *object_name = NULL;

		if (nwam_condition_string_to_condition(conditions[i],
		    &object_type, &condition, &object_name) != NWAM_SUCCESS)
			return (NWAM_ENTITY_INVALID_VALUE);
		if (object_name != NULL)
			free(object_name);
	}
	return (NWAM_SUCCESS);
}

/* check if boolean values are correct, generalize for array of booleans */
nwam_error_t
nwam_valid_boolean(nwam_value_t value)
{
	boolean_t *val;
	uint_t i, numvalues;

	if (nwam_value_get_boolean_array(value, &val, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		if (val[i] != B_TRUE && val[i] != B_FALSE)
			return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

/* check if uint64 values are correct, generalize for array of ints */
nwam_error_t
nwam_valid_uint64(nwam_value_t value)
{
	int64_t *val;
	uint_t i, numvalues;

	if (nwam_value_get_int64_array(value, &val, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		if (val[i] < 0)
			return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

/* check if domain names are correct, generalize for array of domains */
nwam_error_t
nwam_valid_domain(nwam_value_t value)
{
	char **domainvalues, *domain;
	uint_t i, numvalues;
	int len, j;

	if (nwam_value_get_string_array(value, &domainvalues, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		/*
		 * First and last character must be alphanumeric.
		 * Only '.' and '-' are allowed.
		 */
		domain = domainvalues[i];
		len = strlen(domain);
		if (!isalnum(domain[0]) || !isalnum(domain[len-1]))
			return (NWAM_ENTITY_INVALID_VALUE);
		for (j = 0; j < len; j++) {
			if (!isalnum(domain[j]) &&
			    domain[j] != '.' && domain[j] != '-')
				return (NWAM_ENTITY_INVALID_VALUE);
		}
	}
	return (NWAM_SUCCESS);
}

/* check if address prefix is valid */
static nwam_error_t
nwam_valid_prefix(char *addr, int max_plen)
{
	char *prefix, *end;
	int prefixlen;

	if ((prefix = strchr(addr, '/')) != NULL) {
		prefix++;
		prefixlen = strtol(prefix, &end, 10);
		if (prefix == end || prefixlen < 0 || prefixlen > max_plen)
			return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

/* check if IPv4 addresses are correct, generalize for array of addresses */
nwam_error_t
nwam_valid_host_v4(nwam_value_t value)
{
	char **addrvalues, *addr;
	uint_t i, numvalues;
	struct sockaddr_in sa;

	if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		addr = strdup(addrvalues[i]);
		if (nwam_valid_prefix(addr, IP_ABITS) != NWAM_SUCCESS) {
			free(addr);
			return (NWAM_ENTITY_INVALID_VALUE);
		}
		/* replace '/' with '\0' */
		addr = strsep(&addr, "/");
		if (inet_pton(AF_INET, addr, &(sa.sin_addr)) != 1) {
			free(addr);
			return (NWAM_ENTITY_INVALID_VALUE);
		}
		free(addr);
	}
	return (NWAM_SUCCESS);
}

/* Check if IPv4 address for default route is valid */
nwam_error_t
nwam_valid_route_v4(nwam_value_t value)
{
	char *addrvalue;
	struct sockaddr_in sa;

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

	if (inet_pton(AF_INET, addrvalue, &(sa.sin_addr)) != 1)
		return (NWAM_ENTITY_INVALID_VALUE);

	return (NWAM_SUCCESS);
}

/* check if IPv6 addresses are correct, generalize for array of addresses */
nwam_error_t
nwam_valid_host_v6(nwam_value_t value)
{
	char **addrvalues, *addr;
	uint_t i, numvalues;
	struct sockaddr_in6 sa;

	if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		addr = strdup(addrvalues[i]);
		if (nwam_valid_prefix(addr, IPV6_ABITS) != NWAM_SUCCESS) {
			free(addr);
			return (NWAM_ENTITY_INVALID_VALUE);
		}
		/* replace '/' with '\0' */
		addr = strsep(&addr, "/");
		if (inet_pton(AF_INET6, addr, &(sa.sin6_addr)) != 1) {
			free(addr);
			return (NWAM_ENTITY_INVALID_VALUE);
		}
		free(addr);
	}
	return (NWAM_SUCCESS);
}

/* Check if IPv4 address for default route is valid */
nwam_error_t
nwam_valid_route_v6(nwam_value_t value)
{
	char *addrvalue;
	struct sockaddr_in6 sa;

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

	if (inet_pton(AF_INET6, addrvalue, &(sa.sin6_addr)) != 1)
		return (NWAM_ENTITY_INVALID_VALUE);

	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_valid_host_any(nwam_value_t value)
{
	if (nwam_valid_host_v4(value) != NWAM_SUCCESS &&
	    nwam_valid_host_v6(value) != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

nwam_error_t
nwam_valid_host_or_domain(nwam_value_t value)
{
	if (nwam_valid_host_any(value) != NWAM_SUCCESS &&
	    nwam_valid_domain(value) != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);
	return (NWAM_SUCCESS);
}

/* We do not validate file existence, merely that it is an absolute path. */
nwam_error_t
nwam_valid_file(nwam_value_t value)
{
	char **files;
	uint_t i, numvalues;

	if (nwam_value_get_string_array(value, &files, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		int j = 0;
		while (isspace(files[i][j]))
			j++;
		if (files[i][j] != '/')
			return (NWAM_ENTITY_INVALID_VALUE);
	}
	return (NWAM_SUCCESS);
}

/*
 * We do not validate existence of the object pointed to by the FMRI
 * but merely ensure that it is a valid FMRI.  We do this by
 * using scf_handle_decode_fmri(), but ignore all errors bar
 * SCF_ERROR_INVALID_ARGUMENT (which indicates the FMRI is invalid).
 */
nwam_error_t
nwam_valid_fmri(nwam_value_t value)
{
	char **valstr;
	scf_handle_t *h = NULL;
	scf_service_t *svc = NULL;
	uint_t i, numvalues;
	nwam_error_t err = NWAM_SUCCESS;

	if ((err = nwam_value_get_string_array(value, &valstr, &numvalues))
	    != NWAM_SUCCESS)
		return (err);

	h = scf_handle_create(SCF_VERSION);
	if (h == NULL)
		return (NWAM_ERROR_INTERNAL);

	if (scf_handle_bind(h) != 0) {
		err = NWAM_ERROR_INTERNAL;
		goto out;
	}

	if ((svc = scf_service_create(h)) == NULL) {
		err = NWAM_ERROR_INTERNAL;
		goto out;
	}


	for (i = 0; i < numvalues; i++) {
		if (scf_handle_decode_fmri(h, valstr[i], NULL, svc,
		    NULL, NULL, NULL, SCF_DECODE_FMRI_TRUNCATE) == 0 ||
		    scf_error() != SCF_ERROR_INVALID_ARGUMENT) {
			err = NWAM_SUCCESS;
			continue;
		}
		err = NWAM_ENTITY_INVALID_VALUE;
		break;
	}
out:
	scf_service_destroy(svc);
	scf_handle_destroy(h);
	return (err);
}

/* verifies mac-address and bssids */
nwam_error_t
nwam_valid_mac_addr(nwam_value_t value)
{
	char **mac_addrs, *addr;
	uchar_t	*hwaddr;
	int hwaddrlen, j;
	uint_t i, numvalues;

	if (nwam_value_get_string_array(value, &mac_addrs, &numvalues)
	    != NWAM_SUCCESS)
		return (NWAM_ENTITY_INVALID_VALUE);

	for (i = 0; i < numvalues; i++) {
		addr = mac_addrs[i];
		j = 0;

		/* validate that a-fA-F0-9 and ':' only */
		while (addr[j] != 0) {
			if (!isxdigit(addr[j]) && addr[j] != ':')
				return (NWAM_ENTITY_INVALID_VALUE);
			j++;
		}

		if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL)
			return (NWAM_ENTITY_INVALID_VALUE);
		free(hwaddr);
	}

	return (NWAM_SUCCESS);
}

boolean_t
nwam_uid_is_netadm(void)
{
	return (getuid() == UID_NETADM);
}

nwam_error_t
nwam_get_smf_string_property(const char *fmri, const char *pgname,
    const char *propname, char **valuep)
{
	scf_handle_t *h = NULL;
	scf_snapshot_t *snap = NULL;
	scf_instance_t *inst = NULL;
	scf_propertygroup_t *pg = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *val = NULL;
	nwam_error_t err = NWAM_SUCCESS;

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

	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
	    scf_handle_bind(h) != 0 ||
	    (inst = scf_instance_create(h)) == NULL ||
	    (snap = scf_snapshot_create(h)) == NULL ||
	    (pg = scf_pg_create(h)) == NULL ||
	    (prop = scf_property_create(h)) == NULL ||
	    (val = scf_value_create(h)) == NULL) {
		err = NWAM_ERROR_INTERNAL;
		goto out;
	}
	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
	    NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
		err = NWAM_ENTITY_NOT_FOUND;
		goto out;
	}
	/* Retrieve value from running snapshot (if present) */
	if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
		scf_snapshot_destroy(snap);
		snap = NULL;
	}
	if (scf_instance_get_pg_composed(inst, snap, pgname, pg) != 0 ||
	    scf_pg_get_property(pg, propname, prop) != 0 ||
	    scf_property_get_value(prop, val) != 0 ||
	    scf_value_get_astring(val, *valuep, NWAM_MAX_NAME_LEN) == -1) {
		err = NWAM_ENTITY_NOT_FOUND;
	}
out:
	if (err != NWAM_SUCCESS)
		free(*valuep);

	scf_value_destroy(val);
	scf_property_destroy(prop);
	scf_pg_destroy(pg);
	if (snap != NULL)
		scf_snapshot_destroy(snap);
	scf_instance_destroy(inst);
	scf_handle_destroy(h);

	return (err);
}

nwam_error_t
nwam_set_smf_string_property(const char *fmri, const char *pgname,
    const char *propname, const char *propval)
{
	scf_handle_t *h = NULL;
	scf_instance_t *inst = NULL;
	scf_propertygroup_t *pg = NULL;
	scf_property_t *prop = NULL;
	scf_value_t *val = NULL;
	scf_transaction_t *tx = NULL;
	scf_transaction_entry_t *ent = NULL;
	nwam_error_t err = NWAM_SUCCESS;
	int result;

	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
	    scf_handle_bind(h) != 0 ||
	    (inst = scf_instance_create(h)) == NULL ||
	    (pg = scf_pg_create(h)) == NULL ||
	    (prop = scf_property_create(h)) == NULL ||
	    (val = scf_value_create(h)) == NULL ||
	    scf_value_set_astring(val, propval) != 0 ||
	    (tx = scf_transaction_create(h)) == NULL ||
	    (ent = scf_entry_create(h)) == NULL) {
		err = NWAM_ERROR_INTERNAL;
		goto out;
	}
	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
	    NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0 ||
	    scf_instance_get_pg_composed(inst, NULL, pgname, pg) != 0) {
		err = NWAM_ENTITY_NOT_FOUND;
		goto out;
	}

retry:
	if (scf_transaction_start(tx, pg) == -1 ||
	    scf_transaction_property_change(tx, ent, propname, SCF_TYPE_ASTRING)
	    == -1 || scf_entry_add_value(ent, val) != 0) {
		err = NWAM_ERROR_INTERNAL;
		goto out;
	}

	result = scf_transaction_commit(tx);
	switch (result) {
	case 1:
		(void) smf_refresh_instance(fmri);
		break;
	case 0:
		scf_transaction_reset(tx);
		if (scf_pg_update(pg) == -1) {
			err = NWAM_ERROR_INTERNAL;
			goto out;
		}
		goto retry;
	default:
		err = NWAM_ERROR_INTERNAL;
		break;
	}
out:
	scf_value_destroy(val);
	scf_property_destroy(prop);
	scf_pg_destroy(pg);
	scf_instance_destroy(inst);
	scf_handle_destroy(h);

	return (err);
}