/*
* 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 (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libinetutil.h>
#include <libipadm.h>
#include <libnetcfg.h>
#include <libgen.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <libnwam.h>
#include "libnwam_impl.h"
#include <libnwam_priv.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.
*/
#define NWAM_MAX_LINE_LEN 4096
#define IPADM_PROP_IFNAME "_ifname"
#define IPADM_PROP_FAMILY "_family"
#define IPADM_PROP_CLASS "_class"
#define STATIC_ROUTES_PREFIX "/etc/inet/static_routes"
/*
* The NCP and 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 NCPs and NCUs.
*/
static nwam_error_t valid_ncp_mgmt_type(nwam_value_t);
static nwam_error_t valid_ncu_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_autopush(nwam_value_t);
static nwam_error_t valid_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 ncp_prop_table_entries[] = {
{NWAM_NCP_PROP_MGMT_TYPE, NWAM_VALUE_TYPE_UINT64, B_TRUE, 1, 1,
valid_ncp_mgmt_type,
"specifies the NCP management type - valid values are "
"\'fixed\' and \'reactive\'",
NWAM_TYPE_ANY, NWAM_CLASS_ANY},
};
#define NWAM_NUM_NCP_PROPS (sizeof (ncp_prop_table_entries) / \
sizeof (*ncp_prop_table_entries))
struct nwam_prop_table ncp_prop_table =
{ NWAM_NUM_NCP_PROPS, ncp_prop_table_entries };
struct nwam_prop_table_entry ncu_prop_table_entries[] = {
{NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
valid_ncu_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_PHYS},
{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_SIMPLE},
{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_PHYS},
{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_PHYS},
{NWAM_NCU_PROP_MAC_ADDRESS, 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_PHYS},
{NWAM_NCU_PROP_AUTOPUSH, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
valid_autopush,
"specifies modules to autopush on link, separated by '.'",
NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_PHYS},
{NWAM_NCU_PROP_MTU, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1, valid_mtu,
"specifies MTU for link",
NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_PHYS},
{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_IP},
{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_IP}
};
#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 };
/* Used in NCP walk for DR offline/online requests. */
typedef struct ncp_dr_arg_s {
const char *ncu_name;
nwam_ncu_type_t ncu_type;
boolean_t ncu_online;
nwam_error_t ncu_err;
uint64_t ncu_flags;
} ncp_dr_arg_t;
#define NCU_DR_RETRIES 5
#define NCU_DR_SLEEP 5
nwam_error_t
nwam_ncp_get_name(nwam_ncp_handle_t ncph, char **namep)
{
return (nwam_get_name(ncph, namep));
}
/*
* Returns the configuration filename for the given NCU type.
*/
static nwam_error_t
nwam_ncu_filename(nwam_ncu_type_t type, const char *ncpname, char **filename)
{
assert(ncpname != NULL && filename != NULL);
if (type != NWAM_NCU_TYPE_LINK && type != NWAM_NCU_TYPE_INTERFACE)
return (NWAM_INVALID_ARG);
if ((*filename = malloc(MAXPATHLEN)) == NULL)
return (NWAM_NO_MEMORY);
(void) snprintf(*filename, MAXPATHLEN, "%s%s%s",
type == NWAM_NCU_TYPE_LINK ? NWAM_DATALINK_CONF_FILE_PRE :
NWAM_IPADM_CONF_FILE_PRE, ncpname, NWAM_CONF_FILE_SUF);
return (NWAM_SUCCESS);
}
/*
* Creates IPv4 and IPv6 loopback addresses. Creating a loopback address also
* creates the interface entry.
*/
static nwam_error_t
nwam_create_loopbacks(const char *ncpname)
{
ipadm_handle_t iph;
ipadm_addrobj_t ipaddr = NULL;
ipadm_status_t status;
char aobj[IPADM_AOBJSIZ], loopaddr[MAXPROPVALLEN];
uint64_t flags = IPADM_OPT_UP | IPADM_OPT_PERSIST;
if ((status = ipadm_open(&iph, ncpname, 0, NULL)) != IPADM_SUCCESS)
return (NWAM_ERROR_IPADM);
/* IPv4 loopback address first */
(void) snprintf(aobj, sizeof (aobj), "%s/%s", LOOPBACK_IF, "v4");
(void) strlcpy(loopaddr, "127.0.0.1/8", sizeof (loopaddr));
if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobj, &ipaddr))
!= IPADM_SUCCESS ||
(status = ipadm_set_addr(ipaddr, loopaddr, AF_UNSPEC))
!= IPADM_SUCCESS ||
(status = ipadm_create_addr(iph, ipaddr, flags)) != IPADM_SUCCESS)
goto done;
ipadm_destroy_addrobj(ipaddr);
ipaddr = NULL;
/* and then IPv6 loopback address */
(void) snprintf(aobj, sizeof (aobj), "%s/%s", LOOPBACK_IF, "v6");
(void) strlcpy(loopaddr, "::1/128", sizeof (loopaddr));
if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobj, &ipaddr))
!= IPADM_SUCCESS ||
(status = ipadm_set_addr(ipaddr, loopaddr, AF_UNSPEC))
!= IPADM_SUCCESS ||
(status = ipadm_create_addr(iph, ipaddr, flags)) != IPADM_SUCCESS)
goto done;
done:
ipadm_destroy_addrobj(ipaddr);
ipadm_close(iph);
if (status != IPADM_SUCCESS)
return (NWAM_ERROR_IPADM);
else
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_ncp_create(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
{
nwam_value_t mtval = NULL;
nwam_error_t err;
assert(ncphp != NULL && name != NULL);
/* Create empty container for NCUs */
if ((err = nwam_create(NWAM_OBJECT_TYPE_NCP, NWAM_NCP_CONF_FILE, name,
ncphp)) != NWAM_SUCCESS)
return (err);
/* default management-type is reactive */
if ((err = nwam_value_create_uint64(NWAM_NCP_DEFAULT_FIXED(name) ?
NWAM_MGMT_TYPE_FIXED : NWAM_MGMT_TYPE_REACTIVE, &mtval))
!= NWAM_SUCCESS ||
(err = nwam_set_prop_value((*ncphp)->nwh_data,
NWAM_NCP_PROP_MGMT_TYPE, mtval)) != NWAM_SUCCESS)
goto rtn;
/* Implicitly, create loopback addresses for this NCP */
if ((err = nwam_create_loopbacks(name)) != NWAM_SUCCESS)
goto rtn;
err = nwam_ncp_commit(*ncphp, flags);
rtn:
nwam_value_free(mtval);
if (err != NWAM_SUCCESS) {
nwam_ncp_free(*ncphp);
*ncphp = NULL;
}
return (err);
}
nwam_error_t
nwam_ncp_read(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
{
return (nwam_read(NWAM_OBJECT_TYPE_NCP, NWAM_NCP_CONF_FILE, name,
flags, ncphp));
}
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);
}
/*
* Make a copy of the NCU with the following changes:
* - change "link:name" to "name" in the handle
* - map libnwam properties to libdladm properties
* The caller is reponsible free()ing the returned handle.
*
* The NCU returned by this function is written out to persistent store with
* property names that libdladm/dlmgmtd can parse. This function does the
* reverse of what nwam_make_ncu_from_dlmgmt() does.
*/
static nwam_error_t
nwam_make_dlmgmt_ncu(nwam_ncu_handle_t ncuh, nwam_ncu_handle_t *copyncuhp)
{
nwam_error_t err;
nwam_ncu_handle_t copyncuh;
char *linkname, tmpname[NWAM_MAX_NAME_LEN];
if ((err = nwam_ncu_get_name(ncuh, &linkname)) != NWAM_SUCCESS)
return (err);
/* copying checks for the existence of the new NCU, use temp name */
(void) snprintf(tmpname, sizeof (tmpname), "%s.copy", linkname);
if ((err = nwam_ncu_copy(ncuh, tmpname, copyncuhp)) != NWAM_SUCCESS) {
free(linkname);
return (err);
}
copyncuh = *copyncuhp;
/* change "link:name" to "name" */
(void) strlcpy(copyncuh->nwh_name, linkname,
sizeof (copyncuh->nwh_name));
free(linkname);
return (NWAM_SUCCESS);
}
/*
* Write out a link NCU. Convert properties as libdladm/dlmgmtd expects.
*/
static nwam_error_t
nwam_ncu_link_commit(nwam_ncu_handle_t ncuh, const char *ncpname,
uint64_t flags)
{
nwam_error_t err;
char *ncufile;
nwam_ncu_handle_t copyncuh;
/*
* Make a copy of the NCU with properties that libdladm/dlmgmtd
* understands and commit that NCU.
*/
if ((err = nwam_make_dlmgmt_ncu(ncuh, &copyncuh)) != NWAM_SUCCESS)
return (err);
if ((err = nwam_ncu_filename(NWAM_NCU_TYPE_LINK, ncpname, &ncufile))
!= NWAM_SUCCESS) {
nwam_ncu_free(copyncuh);
return (err);
}
err = nwam_commit(ncufile, copyncuh, flags);
free(ncufile);
nwam_ncu_free(copyncuh);
return (err);
}
static int
nwam_ncp_copy_callback(nwam_ncu_handle_t oldncuh, void *arg)
{
nwam_error_t err;
nwam_ncu_handle_t newncuh = NULL;
nwam_ncp_handle_t newncph = (nwam_ncp_handle_t)arg;
nwam_value_t newparentval;
char *ncpname = NULL;
nwam_ncu_type_t type;
/* 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;
if ((err = nwam_ncu_get_ncu_type(oldncuh, &type)) != NWAM_SUCCESS ||
(err = nwam_ncu_get_parent_ncp_name(newncuh, &ncpname))
!= NWAM_SUCCESS)
goto fail;
/*
* Save the new NCU, Don't use nwam_ncu_commit() as it will prevent
* advanced NCUs from being committed. These NCUs should be allowed
* to be copied here as we are copying the entire NCP. However, for
* interface NCUs, use nwam_ncu_commit() as it requires multiple
* libipadm function calls to write out one NCU.
*/
if (type == NWAM_NCU_TYPE_INTERFACE)
err = nwam_ncu_commit(newncuh, 0);
else
err = nwam_ncu_link_commit(newncuh, ncpname, 0);
fail:
free(ncpname);
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_error_t err;
int cb_ret;
boolean_t fixed;
assert(oldncph != NULL && newname != NULL && newncphp != NULL);
/* NCPs of "fixed" type cannot be copied */
if ((err = nwam_ncp_is_fixed(oldncph, &fixed)) != NWAM_SUCCESS)
return (err);
if (fixed)
return (NWAM_ENTITY_FIXED);
/* copy the data */
if ((err = nwam_copy(NWAM_NCP_CONF_FILE, oldncph, newname, newncphp))
!= NWAM_SUCCESS)
return (err);
/* Create loopback addresses in the new NCP, they are not walked */
if ((err = nwam_create_loopbacks(newname)) != NWAM_SUCCESS)
goto fail;
/* copy the NCUs */
err = nwam_ncp_walk_ncus(oldncph, nwam_ncp_copy_callback, *newncphp,
NWAM_FLAG_NCU_TYPE_CLASS_ALL, &cb_ret);
if (err != NWAM_SUCCESS)
goto fail;
err = nwam_ncp_commit(*newncphp, 0);
fail:
if (err != NWAM_SUCCESS) {
/* remove the NCP even if some NCUs have 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);
}
}
/*
* 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:
switch (class) {
case DATALINK_CLASS_PHYS:
case DATALINK_CLASS_AGGR:
case DATALINK_CLASS_ETHERSTUB:
case DATALINK_CLASS_VLAN:
case DATALINK_CLASS_VNIC:
case DATALINK_CLASS_IPTUN:
case DATALINK_CLASS_SIMNET:
case DATALINK_CLASS_BRIDGE:
case DATALINK_CLASS_PART:
return (B_TRUE);
}
break;
case NWAM_NCU_TYPE_INTERFACE:
switch (class) {
case IPADMIF_CLASS_IP:
case IPADMIF_CLASS_IPMP:
case IPADMIF_CLASS_LOOPBACK:
case IPADMIF_CLASS_VNI:
return (B_TRUE);
}
break;
case NWAM_NCU_TYPE_ANY:
return (class == 0);
}
return (B_FALSE);
}
/*
* Convert the given type and class to a class flag
*/
uint64_t
nwam_ncu_type_class_to_flag(nwam_ncu_type_t type, nwam_ncu_class_t class)
{
if (!nwam_ncu_type_class_compatible(type, class))
return (0);
switch (type) {
case NWAM_NCU_TYPE_LINK:
switch (class) {
case DATALINK_CLASS_PHYS:
return (NWAM_FLAG_NCU_CLASS_PHYS);
case DATALINK_CLASS_AGGR:
return (NWAM_FLAG_NCU_CLASS_AGGR);
case DATALINK_CLASS_ETHERSTUB:
return (NWAM_FLAG_NCU_CLASS_ETHERSTUB);
case DATALINK_CLASS_VLAN:
return (NWAM_FLAG_NCU_CLASS_VLAN);
case DATALINK_CLASS_VNIC:
return (NWAM_FLAG_NCU_CLASS_VNIC);
case DATALINK_CLASS_IPTUN:
return (NWAM_FLAG_NCU_CLASS_IPTUN);
case DATALINK_CLASS_SIMNET:
return (NWAM_FLAG_NCU_CLASS_SIMNET);
case DATALINK_CLASS_BRIDGE:
return (NWAM_FLAG_NCU_CLASS_BRIDGE);
case DATALINK_CLASS_PART:
return (NWAM_FLAG_NCU_CLASS_PART);
default:
return (NWAM_FLAG_NCU_CLASS_ALL_LINK);
}
case NWAM_NCU_TYPE_INTERFACE:
switch (class) {
case IPADMIF_CLASS_IP:
return (NWAM_FLAG_NCU_CLASS_IP);
case IPADMIF_CLASS_IPMP:
return (NWAM_FLAG_NCU_CLASS_IPMP);
case IPADMIF_CLASS_LOOPBACK:
return (NWAM_FLAG_NCU_CLASS_LOOPBACK);
case IPADMIF_CLASS_VNI:
return (NWAM_FLAG_NCU_CLASS_VNI);
default:
return (NWAM_FLAG_NCU_CLASS_ALL_INTERFACE);
}
}
return (0);
}
const char *
nwam_ncu_class_to_string(nwam_ncu_type_t type, nwam_ncu_class_t class,
char *buf, uint_t bufsize)
{
const char *s;
switch (type) {
case NWAM_NCU_TYPE_LINK:
return (dladm_class2str(class, buf));
case NWAM_NCU_TYPE_INTERFACE:
s = ipadm_class2str(class);
break;
default:
s = "unknown";
}
(void) snprintf(buf, bufsize, "%s", s);
return (buf);
}
/*
* 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_NWAMD_BIND) {
/*
* nwamd is not running, set netcfg/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(NP_DEFAULT_FMRI, NETCFG_PG,
NETCFG_ACTIVE_NCP_PROP, name);
free(name);
}
return (err);
}
/* ARGSUSED2 */
static int
ncp_selectcb(nvlist_t *idp, nvlist_t *objp, uint64_t flags, void **cbobj)
{
nwam_ncp_handle_t ncph;
nwam_error_t err;
if ((err = nwam_lists_to_handle(idp, objp, NWAM_OBJECT_TYPE_NCP, &ncph))
!= NWAM_SUCCESS)
return (err);
*cbobj = ncph;
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_walk_ncps(int (*cb)(nwam_ncp_handle_t, void *), void *data,
uint64_t flags, int *retp)
{
nwam_error_t err;
netcfg_walkcb_t *nccb = (netcfg_walkcb_t *)cb;
assert(cb != NULL);
if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS)
return (err);
return (nwam_walk(NWAM_NCP_CONF_FILE, nccb, data, flags, retp,
ncp_selectcb));
}
/* Checks if NCP is read-only. Only NWAM_NCP_NAME_AUTOMATIC is read-only. */
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);
*readp = NWAM_NCP_AUTOMATIC(name);
free(name);
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_ncp_prop_read_only(const char *propname, boolean_t *readp)
{
return (nwam_prop_read_only(ncp_prop_table, propname, readp));
}
/* 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);
}
nwam_error_t
nwam_ncp_commit(nwam_ncp_handle_t ncph, uint64_t flags)
{
boolean_t ro;
nwam_error_t err;
assert(ncph != NULL && ncph->nwh_data != NULL);
if ((err = nwam_ncp_get_read_only(ncph, &ro)) != NWAM_SUCCESS)
return (err);
if (ro && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
if ((err = nwam_ncp_validate(ncph, NULL)) != NWAM_SUCCESS)
return (err);
return (nwam_commit(NWAM_NCP_CONF_FILE, ncph, flags));
}
/* 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 netcfg/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(NP_DEFAULT_FMRI, NETCFG_PG,
NETCFG_ACTIVE_NCP_PROP, &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)
{
nwam_error_t err;
boolean_t read_only;
char *linkfile = NULL, *ipadmfile = NULL, routefile[MAXPATHLEN];
assert(ncph != NULL);
if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
return (err);
if ((read_only && !nwam_override_readonly(flags)) ||
NWAM_NCP_DEFAULT_FIXED(ncph->nwh_name))
return (NWAM_ENTITY_NOT_DESTROYABLE);
if (nwam_ncp_is_active(ncph))
return (NWAM_ENTITY_IN_USE);
/* determine the datalink and ipadm configuration filenames */
if ((err = nwam_ncu_filename(NWAM_NCU_TYPE_LINK, ncph->nwh_name,
&linkfile)) != NWAM_SUCCESS ||
(err = nwam_ncu_filename(NWAM_NCU_TYPE_INTERFACE, ncph->nwh_name,
&ipadmfile)) != NWAM_SUCCESS)
goto done;
(void) snprintf(routefile, sizeof (routefile), "%s-%s",
STATIC_ROUTES_PREFIX, ncph->nwh_name);
if ((err = nwam_destroy(NWAM_NCP_CONF_FILE, ncph, flags))
!= NWAM_SUCCESS)
goto done;
/*
* Remove the associated static route, datalink and ipadm
* configuration files now that the NCP entry in libnwam has been
* removed.
*/
(void) unlink(routefile);
(void) unlink(linkfile);
(void) unlink(ipadmfile);
done:
free(linkfile);
free(ipadmfile);
return (err);
}
nwam_error_t
nwam_ncp_delete_prop(nwam_ncp_handle_t ncph, const char *propname,
uint64_t flags)
{
nwam_error_t err;
boolean_t ro_prop, ro_ncp;
void *olddata;
assert(ncph != NULL && propname != NULL);
if ((err = nwam_ncp_prop_read_only(propname, &ro_prop))
!= NWAM_SUCCESS ||
(err = nwam_ncp_get_read_only(ncph, &ro_ncp)) != NWAM_SUCCESS)
return (err);
if ((ro_prop || ro_ncp) && !nwam_override_readonly(flags))
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(ncph->nwh_data, &olddata))
!= NWAM_SUCCESS)
return (err);
if ((err = nwam_delete_prop(ncph->nwh_data, propname)) != NWAM_SUCCESS)
goto fail;
if ((err = nwam_ncp_validate(ncph, NULL)) != NWAM_SUCCESS)
goto fail;
nwam_free_object_list(olddata);
return (NWAM_SUCCESS);
fail:
nwam_free_object_list(ncph->nwh_data);
ncph->nwh_data = olddata;
return (err);
}
nwam_error_t
nwam_ncp_set_prop_value(nwam_ncp_handle_t ncph, const char *propname,
nwam_value_t value, uint64_t flags)
{
nwam_error_t err;
boolean_t ro;
assert(ncph != NULL && propname != NULL && value != NULL);
if ((err = nwam_ncp_validate_prop(ncph, propname, value))
!= NWAM_SUCCESS ||
(err = nwam_ncp_prop_read_only(propname, &ro)) != NWAM_SUCCESS)
return (err);
if (ro && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
return (nwam_set_prop_value(ncph->nwh_data, propname, value));
}
nwam_error_t
nwam_ncp_get_prop_value(nwam_ncp_handle_t ncph, const char *propname,
nwam_value_t *valuep)
{
assert(ncph != NULL);
return (nwam_get_prop_value(ncph->nwh_data, propname, valuep));
}
nwam_error_t
nwam_ncp_walk_props(nwam_ncp_handle_t ncph,
int (*cb)(const char *, nwam_value_t, void*), void *data, uint64_t flags,
int *retp)
{
return (nwam_walk_props(ncph, cb, data, flags, retp));
}
/* Given a property, return expected property data type */
nwam_error_t
nwam_ncp_get_prop_type(const char *propname, nwam_value_type_t *typep)
{
return (nwam_get_prop_type(ncp_prop_table, propname, typep));
}
nwam_error_t
nwam_ncp_prop_multivalued(const char *propname, boolean_t *multip)
{
return (nwam_prop_multivalued(ncp_prop_table, propname, multip));
}
nwam_error_t
nwam_ncp_get_default_proplist(const char ***prop_list, uint_t *numvaluesp)
{
return (nwam_get_default_proplist(ncp_prop_table, NWAM_TYPE_ANY,
NWAM_CLASS_ANY, prop_list, numvaluesp));
}
nwam_error_t
nwam_ncp_get_prop_description(const char *propname, const char **descp)
{
return (nwam_get_prop_description(ncp_prop_table, propname, descp));
}
nwam_error_t
nwam_ncp_is_fixed(nwam_ncp_handle_t ncph, boolean_t *fixedp)
{
nwam_error_t err;
nwam_value_t fixedval;
uint64_t enumval;
*fixedp = B_FALSE;
err = nwam_ncp_get_prop_value(ncph, NWAM_NCP_PROP_MGMT_TYPE,
&fixedval);
if (err == NWAM_ENTITY_NOT_FOUND)
return (NWAM_SUCCESS);
else if (err != NWAM_SUCCESS)
return (err);
err = nwam_value_get_uint64(fixedval, &enumval);
nwam_value_free(fixedval);
if (err == NWAM_SUCCESS)
*fixedp = (enumval == NWAM_MGMT_TYPE_FIXED);
return (err);
}
/*
* Makes a libnwam-readable link NCU from one read in by nwam_read() by making
* the following changes:
* - change "name" to "link:name" in the handle
* - map libdladm properties to libnwam properties
*/
static nwam_error_t
nwam_make_ncu_from_dlmgmt(nwam_ncu_handle_t ncuh, const char *ncpname)
{
char *typedname;
nwam_error_t err;
nwam_value_t val = NULL;
if ((err = nwam_ncu_name_to_typed_name(ncuh->nwh_name,
NWAM_NCU_TYPE_LINK, &typedname)) != NWAM_SUCCESS)
return (err);
/* change "name" to "link:name" */
(void) strlcpy(ncuh->nwh_name, typedname, sizeof (ncuh->nwh_name));
free(typedname);
/*
* Non-physical links will have a 'class' property value. If a link
* does not have the the "class" property, then it must be a physical
* link, which has its class property stored in the global
* datalink.conf file.
*/
err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &val);
if (err != NWAM_SUCCESS) {
(void) nwam_value_create_uint64(DATALINK_CLASS_PHYS, &val);
(void) nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_CLASS,
val);
}
nwam_value_free(val);
/* If the "type" property does not exist, add it */
err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &val);
if (err != NWAM_SUCCESS) {
(void) nwam_value_create_uint64(NWAM_NCU_TYPE_LINK, &val);
(void) nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_TYPE,
val);
}
nwam_value_free(val);
/* If the "parent" property does not exist, add it */
if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_PARENT_NCP, &val)
!= NWAM_SUCCESS && ncpname != NULL) {
(void) nwam_value_create_string((char *)ncpname, &val);
(void) nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_PARENT_NCP, val);
}
nwam_value_free(val);
return (NWAM_SUCCESS);
}
/*
* Makes a libnwam-readable interface NCU from one read in by nwam_read() by
* makeing the following changes:
* - only deal with the interface line from ipadm-<profile>.conf, which has
* "_family" property)
* - change "name" to "interface:name"
* - retrieve the address objects for this interface
* - add the info from the address object to the NCU.
*/
static nwam_error_t
nwam_make_ncu_from_ipmgmt(nwam_ncu_handle_t ncuh, const char *ncpname)
{
ipadm_handle_t iph = NULL;
ipadm_status_t ipstat = IPADM_SUCCESS;
ipadm_addr_info_t *addrinfo = NULL, *ptr;
char ifname[NWAM_MAX_NAME_LEN], *typedname = NULL;
nwam_error_t err;
nwam_value_t vals = NULL;
uint64_t *uintvals;
char **strvals;
uint_t i, num, nv4 = 0, nv6 = 0;
boolean_t v4_dhcp = B_FALSE, v6_dhcp = B_FALSE;
char v4_addr[NWAM_MAX_VALUE_LEN * NWAM_MAX_NUM_VALUES];
char v6_addr[NWAM_MAX_VALUE_LEN * NWAM_MAX_NUM_VALUES];
char *v4ptr = v4_addr, *v6ptr = v6_addr;
/*
* Look for the "_family" property to determine that this is the
* interface line.
*/
if ((err = nwam_get_prop_value(ncuh->nwh_data, IPADM_PROP_FAMILY,
&vals)) != NWAM_SUCCESS)
return (err);
/* change "name" to "interface:name" */
(void) strlcpy(ifname, ncuh->nwh_name, sizeof (ifname));
if ((err = nwam_ncu_name_to_typed_name(ifname, NWAM_NCU_TYPE_INTERFACE,
&typedname)) != NWAM_SUCCESS)
goto done;
(void) strlcpy(ncuh->nwh_name, typedname, sizeof (ncuh->nwh_name));
free(typedname);
if ((ipstat = ipadm_open(&iph, ncpname, 0, NULL)) != IPADM_SUCCESS)
goto done;
/*
* The properties type, class, parent, enabled, ip{v4,v6}-default-route
* are already present. Convert the "_family" values to "ip-version"
* and remove it.
*/
if ((err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS)
goto done;
if ((uintvals = calloc(num, sizeof (uint64_t))) == NULL) {
err = NWAM_NO_MEMORY;
goto done;
}
for (i = 0; i < num; i++)
uintvals[i] = atoi(strvals[i]) == AF_INET ? IPV4_VERSION :
IPV6_VERSION;
nwam_value_free(vals);
vals = NULL;
if ((err = nwam_value_create_uint64_array(uintvals, num, &vals))
!= NWAM_SUCCESS ||
(err = nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IP_VERSION,
vals)) != NWAM_SUCCESS ||
(err = nwam_delete_prop(ncuh->nwh_data, IPADM_PROP_FAMILY))
!= NWAM_SUCCESS) {
free(uintvals);
goto done;
}
free(uintvals);
nwam_value_free(vals);
vals = NULL;
/* If the "type" property doesn't exist, add it */
if (nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_TYPE, &vals)
!= NWAM_SUCCESS) {
(void) nwam_value_create_uint64(NWAM_NCU_TYPE_INTERFACE, &vals);
(void) nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_TYPE,
vals);
}
nwam_value_free(vals);
vals = NULL;
/* rename the "_class" property name to "class" */
if (nwam_get_prop_value(ncuh->nwh_data, IPADM_PROP_CLASS, &vals)
== NWAM_SUCCESS) {
(void) nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_CLASS,
vals);
(void) nwam_delete_prop(ncuh->nwh_data, IPADM_PROP_CLASS);
nwam_value_free(vals);
vals = NULL;
}
/* If the "parent" property does not exist, add it */
if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_PARENT_NCP, &vals)
!= NWAM_SUCCESS && ncpname != NULL) {
(void) nwam_value_create_string((char *)ncpname, &vals);
(void) nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_PARENT_NCP, vals);
}
nwam_value_free(vals);
vals = NULL;
/* If the "enabled" property does not exist, add it as B_TRUE */
if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED, &vals)
!= NWAM_SUCCESS) {
(void) nwam_value_create_boolean(B_TRUE, &vals);
(void) nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_ENABLED, vals);
}
nwam_value_free(vals);
vals = NULL;
/* Retrieve all the address objects for this profile */
ipstat = ipadm_addr_info(iph, ifname, &addrinfo, IPADM_OPT_PERSIST, 0);
if (ipstat != IPADM_SUCCESS) {
if (ipstat == IPADM_NOSUCH_IF)
ipstat = IPADM_SUCCESS;
goto done;
}
for (ptr = addrinfo; ptr != NULL; ptr = IA_NEXT(ptr)) {
/* determine what addresses are configured */
if (ptr->ia_atype == IPADM_ADDR_DHCP) {
v4_dhcp = B_TRUE;
} else if (ptr->ia_atype == IPADM_ADDR_IPV6_ADDRCONF) {
v6_dhcp = B_TRUE;
} else if (ptr->ia_atype == IPADM_ADDR_STATIC) {
/* create comma-separated addresses, also count them */
struct ifaddrs *ifa = &ptr->ia_ifa;
if (ifa->ifa_addr->sa_family == AF_INET) {
nv4++;
v4ptr += snprintf(v4ptr,
sizeof (v4_addr) - strlen(v4ptr), "%s",
ptr->ia_sname);
if (ifa->ifa_netmask != NULL &&
mask2plen(ifa->ifa_netmask) > 0) {
v4ptr += snprintf(v4ptr,
sizeof (v4_addr) - strlen(v4ptr),
"/%d,",
mask2plen(ifa->ifa_netmask));
} else {
v4ptr += snprintf(v4ptr,
sizeof (v4_addr) - strlen(v4ptr),
",");
}
} else {
/* skip linklocal addresses */
/*LINTED*/
if (IN6_IS_ADDR_LINKLOCAL(
&((struct sockaddr_in6 *)ifa->ifa_addr)->
sin6_addr))
continue;
nv6++;
v6ptr += snprintf(v6ptr,
sizeof (v6_addr) - strlen(v6ptr), "%s",
ptr->ia_sname);
if (ifa->ifa_netmask != NULL &&
mask2plen(ifa->ifa_netmask) > 0) {
v6ptr += snprintf(v6ptr,
sizeof (v6_addr) - strlen(v6ptr),
"/%d,",
mask2plen(ifa->ifa_netmask));
} else {
v6ptr += snprintf(v6ptr,
sizeof (v6_addr) - strlen(v6ptr),
",");
}
}
}
}
/* create appropriate values for "ipv4-addrsrc" property */
if (v4_dhcp && nv4 > 0) {
num = 2;
if ((uintvals = calloc(num, sizeof (uint64_t))) != NULL) {
uintvals[0] = NWAM_ADDRSRC_DHCP;
uintvals[1] = NWAM_ADDRSRC_STATIC;
}
} else if (v4_dhcp || nv4 > 0) {
num = 1;
if ((uintvals = calloc(num, sizeof (uint64_t))) != NULL)
uintvals[0] = v4_dhcp ? NWAM_ADDRSRC_DHCP :
NWAM_ADDRSRC_STATIC;
} else {
goto skip_v4;
}
if (uintvals == NULL) {
err = NWAM_NO_MEMORY;
goto done;
}
if ((err = nwam_value_create_uint64_array(uintvals, num, &vals))
!= NWAM_SUCCESS ||
(err = nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV4_ADDRSRC, vals)) != NWAM_SUCCESS) {
free(uintvals);
goto done;
}
free(uintvals);
nwam_value_free(vals);
vals = NULL;
/* create appropriate values for "ipv4-addr" property */
if (nv4 > 0) {
char *p, **addrs = calloc(nv4, sizeof (char *));
if (addrs == NULL) {
err = NWAM_NO_MEMORY;
goto done;
}
p = strtok(v4_addr, ",");
for (i = 0; p != NULL; i++, p = strtok(NULL, ","))
addrs[i] = p;
if ((err = nwam_value_create_string_array(addrs, nv4, &vals))
!= NWAM_SUCCESS ||
(err = nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV4_ADDR, vals)) != NWAM_SUCCESS) {
free(addrs);
goto done;
}
free(addrs);
nwam_value_free(vals);
vals = NULL;
}
skip_v4:
/* create appropriate values for "ipv6-addrsrc" property */
if (v6_dhcp && nv6 > 0) {
num = 3;
if ((uintvals = calloc(num, sizeof (uint64_t))) != NULL) {
uintvals[0] = NWAM_ADDRSRC_DHCP;
uintvals[1] = NWAM_ADDRSRC_AUTOCONF;
uintvals[2] = NWAM_ADDRSRC_STATIC;
}
} else if (v6_dhcp) {
num = 2;
if ((uintvals = calloc(num, sizeof (uint64_t))) != NULL) {
uintvals[0] = NWAM_ADDRSRC_DHCP;
uintvals[1] = NWAM_ADDRSRC_AUTOCONF;
}
} else if (nv6 != 0) {
/* If nv6 is not 0, then there is at least one static address */
num = 1;
if ((uintvals = calloc(num, sizeof (uint64_t))) != NULL)
uintvals[0] = NWAM_ADDRSRC_STATIC;
} else {
goto skip_v6;
}
if (uintvals == NULL) {
err = NWAM_NO_MEMORY;
goto done;
}
if ((err = nwam_value_create_uint64_array(uintvals, num, &vals))
!= NWAM_SUCCESS ||
(err = nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV6_ADDRSRC, vals)) != NWAM_SUCCESS) {
free(uintvals);
goto done;
}
free(uintvals);
nwam_value_free(vals);
vals = NULL;
/* create appropriate values for "ipv6-addr" property */
if (nv6 > 0) {
char *p, **addrs = calloc(nv6, sizeof (char *));
if (addrs == NULL)
goto done;
p = strtok(v6_addr, ",");
for (i = 0; p != NULL; i++, p = strtok(NULL, ","))
addrs[i] = p;
err = nwam_value_create_string_array(addrs, nv6, &vals);
if (err == NWAM_SUCCESS) {
err = nwam_set_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV6_ADDR, vals);
}
free(addrs);
nwam_value_free(vals);
vals = NULL;
}
skip_v6:
done:
nwam_value_free(vals);
ipadm_free_addr_info(addrinfo);
ipadm_close(iph);
if (ipstat != IPADM_SUCCESS)
err = NWAM_ERROR_IPADM;
return (err);
}
/*
* Returns the NCP name from the given datalink or ipadm configuration filename.
*/
char *
nwam_ncp_name_from_dbname(const char *dbname)
{
size_t len;
char *ncp, *cp;
if (dbname == NULL)
return (NULL);
if (strncasecmp(dbname, NWAM_DATALINK_CONF_FILE_PRE,
strlen(NWAM_DATALINK_CONF_FILE_PRE)) == 0)
len = strlen(NWAM_DATALINK_CONF_FILE_PRE);
else if (strncasecmp(dbname, NWAM_IPADM_CONF_FILE_PRE,
strlen(NWAM_IPADM_CONF_FILE_PRE)) == 0)
len = strlen(NWAM_IPADM_CONF_FILE_PRE);
else
return (NULL);
if ((ncp = strdup(dbname + len)) == NULL)
return (NULL);
/* replace the "." from ".conf" with NULL, if it exists */
cp = strchr(ncp, '.');
*cp = '\0';
return (ncp);
}
nwam_ncu_type_t
nwam_dbname_to_ncu_type(const char *dbname)
{
if (strncasecmp(dbname, NWAM_DATALINK_CONF_FILE_PRE,
strlen(NWAM_DATALINK_CONF_FILE_PRE)) == 0)
return (NWAM_NCU_TYPE_LINK);
else if (strncasecmp(dbname, NWAM_IPADM_CONF_FILE_PRE,
strlen(NWAM_IPADM_CONF_FILE_PRE)) == 0)
return (NWAM_NCU_TYPE_INTERFACE);
else
return (NWAM_NCU_TYPE_UNKNOWN);
}
/*
* Returns B_TRUE if nwam should not be touching the given link. This is the
* case only for the Automatic NCP and links with the "allow-autoconf"
* datalink property unset (set to 0, default value is 1).
*/
static boolean_t
ncu_no_autoconf(const char *ncpname, const char *link)
{
char allowbuf[DLADM_PROP_VAL_MAX], *allowval;
uint_t cnt = 1;
dladm_handle_t dlh;
datalink_id_t linkid;
/* Only skip in Automatic NCP */
if (!NWAM_NCP_AUTOMATIC(ncpname))
return (B_FALSE);
allowval = allowbuf;
if (dladm_open(&dlh, ncpname) != DLADM_STATUS_OK)
return (B_FALSE);
if (dladm_name2info(dlh, link, &linkid, NULL, NULL, NULL)
!= DLADM_STATUS_OK)
goto fail;
if (dladm_get_linkprop(dlh, linkid, DLADM_PROP_VAL_CURRENT,
DLADM_ALLOW_AUTOCONF_PROP, &allowval, &cnt) != DLADM_STATUS_OK)
goto fail;
dladm_close(dlh);
return (strcmp(allowval, "0") == 0);
fail:
dladm_close(dlh);
return (B_FALSE);
}
static int
link_ncu_selectcb(nvlist_t *idp, nvlist_t *objp, uint64_t flags, void **cbobj)
{
nwam_ncu_handle_t ncuh;
nwam_ncu_type_t type = NWAM_NCU_TYPE_LINK;
nwam_ncu_class_t class = DATALINK_CLASS_PHYS; /* default to physical */
uint64_t matchflags, walkfilter;
nwam_error_t err;
char *ncpname;
if ((err = nwam_lists_to_handle(idp, objp, NWAM_OBJECT_TYPE_NCU, &ncuh))
!= NWAM_SUCCESS)
return (err);
/* Ignore IP Tunnels links, check the "class" property in the nvlist */
if (netcfg_nvl_get_one_uint64(objp, NWAM_NCU_PROP_CLASS, &class) == 0 &&
class == DATALINK_CLASS_IPTUN)
return (NWAM_INVALID_ARG);
/* retrieve the parent NCP name from the dbname */
ncpname = nwam_ncp_name_from_dbname(dbname_from_idlist(idp));
/*
* For Automatic NCPs, if the allow-autoconf property is set for the
* link of this NCU, then it should not be walked.
*/
if (ncu_no_autoconf(ncpname, ncuh->nwh_name)) {
free(ncpname);
return (NWAM_INVALID_ARG);
}
/*
* convert the dlmgmt properties to ones libnwam consumers understand,
* and extract the class for filter matching
*/
err = nwam_make_ncu_from_dlmgmt(ncuh, ncpname);
free(ncpname);
if (err != NWAM_SUCCESS) {
nwam_free(ncuh, B_FALSE);
return (err);
}
matchflags = nwam_ncu_type_to_flag(type) |
nwam_ncu_type_class_to_flag(type, class);
if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
walkfilter = NWAM_FLAG_NCU_TYPE_CLASS_ALL;
if (matchflags & walkfilter) {
*cbobj = ncuh;
return (NWAM_SUCCESS);
}
nwam_free(ncuh, B_FALSE);
return (NWAM_INVALID_ARG);
}
static int
intf_ncu_selectcb(nvlist_t *idp, nvlist_t *objp, uint64_t flags, void **cbobj)
{
nwam_ncu_handle_t ncuh;
nwam_value_t classval = NULL;
uint64_t type = NWAM_NCU_TYPE_INTERFACE, class, matchflags, walkfilter;
nwam_error_t err;
char *ncpname;
dladm_handle_t dlh;
datalink_class_t dlclass = DATALINK_CLASS_PHYS;
if ((err = nwam_lists_to_handle(idp, objp, NWAM_OBJECT_TYPE_NCU, &ncuh))
!= NWAM_SUCCESS)
return (err);
/*
* Skip NCUs for the loopback interface. Here, the NCU handle
* contains the untyped interface name.
*/
if (ipadm_is_loopback(ncuh->nwh_name)) {
err = NWAM_INVALID_ARG;
goto fail;
}
/* retrieve the parent NCP name from the dbname */
ncpname = nwam_ncp_name_from_dbname(dbname_from_idlist(idp));
/*
* Skip IP configuration created over IP Tunnels.
*/
if (dladm_open(&dlh, ncpname) != DLADM_STATUS_OK) {
free(ncpname);
return (NWAM_INVALID_ARG);
}
if (dladm_name2info(dlh, ncuh->nwh_name, NULL, NULL, &dlclass, NULL)
== DLADM_STATUS_OK && dlclass == DATALINK_CLASS_IPTUN) {
free(ncpname);
dladm_close(dlh);
return (NWAM_INVALID_ARG);
}
dladm_close(dlh);
/*
* For Automatic NCPs, if the allow-autoconf property is set for the
* link of this NCU, then it should not be walked.
*/
if (ncu_no_autoconf(ncpname, ncuh->nwh_name)) {
free(ncpname);
return (NWAM_INVALID_ARG);
}
err = nwam_make_ncu_from_ipmgmt(ncuh, ncpname);
free(ncpname);
if (err != NWAM_SUCCESS)
goto fail;
if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS,
&classval)) != NWAM_SUCCESS ||
(err = nwam_value_get_uint64(classval, &class)) != NWAM_SUCCESS) {
nwam_value_free(classval);
goto fail;
}
nwam_value_free(classval);
matchflags = nwam_ncu_type_to_flag(type) |
nwam_ncu_type_class_to_flag(type, class);
if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
walkfilter = NWAM_FLAG_NCU_TYPE_CLASS_ALL;
if (matchflags & walkfilter) {
*cbobj = ncuh;
return (NWAM_SUCCESS);
}
err = NWAM_INVALID_ARG;
fail:
nwam_free(ncuh, B_FALSE);
return (err);
}
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)
{
netcfg_walkcb_t *nccb = (netcfg_walkcb_t *)cb;
char *ncufile;
nwam_error_t err;
assert(ncph != NULL && cb != NULL);
if ((err = nwam_valid_flags(flags, NWAM_FLAG_NCU_TYPE_CLASS_ALL))
!= NWAM_SUCCESS)
return (err);
/* walk link NCUs */
if (flags & (NWAM_FLAG_NCU_TYPE_LINK | NWAM_FLAG_NCU_CLASS_ALL_LINK)) {
if ((err = nwam_ncu_filename(NWAM_NCU_TYPE_LINK, ncph->nwh_name,
&ncufile)) != NWAM_SUCCESS)
return (err);
err = nwam_walk(ncufile, nccb, data, flags, retp,
link_ncu_selectcb);
free(ncufile);
if (err != NWAM_SUCCESS && err != NWAM_ENTITY_NOT_FOUND)
return (err);
err = NWAM_SUCCESS;
}
/* and then interface NCUs */
if (flags & (NWAM_FLAG_NCU_TYPE_INTERFACE |
NWAM_FLAG_NCU_CLASS_ALL_INTERFACE)) {
if ((err = nwam_ncu_filename(NWAM_NCU_TYPE_INTERFACE,
ncph->nwh_name, &ncufile)) != NWAM_SUCCESS)
return (err);
err = nwam_walk(ncufile, nccb, data, flags, retp,
intf_ncu_selectcb);
free(ncufile);
if (err == NWAM_ENTITY_NOT_FOUND)
err = NWAM_SUCCESS;
}
return (err);
}
void
nwam_ncp_free(nwam_ncp_handle_t ncph)
{
nwam_free(ncph, B_TRUE);
}
/* 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_typed_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, uint64_t flags, 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, fixed;
char *typedname, *ncufile;
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 && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
/* NCUs cannot be created in a 'fixed' NCP */
if ((err = nwam_ncp_is_fixed(ncph, &fixed)) != NWAM_SUCCESS)
return (err);
if (fixed && !nwam_override_readonly(flags))
return (NWAM_ENTITY_FIXED);
/* check if an advanced NCU class is being created */
if (nwam_ncu_type_class_to_flag(type, class) &
NWAM_FLAG_NCU_CLASS_ADVANCED)
return (NWAM_ENTITY_ADVANCED);
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);
if ((err = nwam_ncu_filename(type, ncph->nwh_name, &ncufile))
!= NWAM_SUCCESS) {
free(typedname);
return (err);
}
/* Create handle */
err = nwam_create(NWAM_OBJECT_TYPE_NCU, ncufile, typedname, ncuhp);
free(typedname);
free(ncufile);
if (err != NWAM_SUCCESS)
return (err);
/* The new NCU is initialized with the appropriate type and class */
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 NCU properties */
if (type == NWAM_NCU_TYPE_INTERFACE && class == IPADMIF_CLASS_IP) {
/* ip-version is ipv4,ipv6 */
uint64_t ipver[] = { IPV4_VERSION, IPV6_VERSION };
nwam_value_t ipverval = NULL;
if ((err = nwam_value_create_uint64_array(ipver, 2, &ipverval))
!= NWAM_SUCCESS)
goto finish;
err = nwam_set_prop_value((*ncuhp)->nwh_data,
NWAM_NCU_PROP_IP_VERSION, ipverval);
nwam_value_free(ipverval);
} else if (type == NWAM_NCU_TYPE_LINK && class == DATALINK_CLASS_PHYS) {
/* activation-mode is manual */
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 *ncufile, *untypedname = NULL;
nwam_error_t err, err_ip, err_link;
nwam_ncu_handle_t ncuh_ip, ncuh_link;
nwam_ncu_type_t tmptype;
boolean_t fixed;
assert(ncph != NULL && name != NULL && ncuhp != NULL);
/* NCUs for the 'fixed' NCP cannot be read */
if ((err = nwam_ncp_is_fixed(ncph, &fixed)) != NWAM_SUCCESS)
return (err);
if (fixed && !nwam_override_readonly(flags))
return (NWAM_ENTITY_FIXED);
if (type == NWAM_NCU_TYPE_ANY) {
/*
* 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_filename(type, ncph->nwh_name, &ncufile))
!= NWAM_SUCCESS)
return (err);
/* Input name (bge0) may be a typedname (link:bge0) */
err = nwam_ncu_typed_name_to_name(name, &tmptype, &untypedname);
if (err != NWAM_SUCCESS && err != NWAM_INVALID_ARG) {
free(ncufile);
return (err);
} else if (err == NWAM_SUCCESS) {
/*
* If a typedname was given, make sure it is the same as the
* requested type.
*/
if (type != tmptype) {
free(ncufile);
free(untypedname);
return (NWAM_ENTITY_NOT_FOUND);
}
}
err = nwam_read(NWAM_OBJECT_TYPE_NCU, ncufile,
err == NWAM_INVALID_ARG ? name : untypedname, flags, ncuhp);
free(ncufile);
free(untypedname);
if (err != NWAM_SUCCESS)
return (err);
/* convert the handle to one that libnwam understands */
if (type == NWAM_NCU_TYPE_LINK)
err = nwam_make_ncu_from_dlmgmt(*ncuhp, ncph->nwh_name);
else
err = nwam_make_ncu_from_ipmgmt(*ncuhp, ncph->nwh_name);
if (err != NWAM_SUCCESS) {
nwam_ncu_free(*ncuhp);
*ncuhp = NULL;
}
return (err);
}
nwam_error_t
nwam_ncu_add_data(nwam_ncu_handle_t ncuh, nvlist_t *data)
{
int err = 0;
assert(ncuh != NULL);
if (ncuh->nwh_data != NULL)
err = nvlist_merge(ncuh->nwh_data, data, 0);
else
ncuh->nwh_data = data;
return (nwam_errno_to_nwam_error(err));
}
nwam_error_t
nwam_ncu_get_data(nwam_ncu_handle_t ncuh, nvlist_t **data)
{
assert(ncuh != NULL);
*data = ncuh->nwh_data;
return (NWAM_SUCCESS);
}
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_typed_name_to_name(ncuh->nwh_name, &type, namep));
}
/*
* Generate the link:name or interface:name from the given name and type.
*/
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);
}
/*
* Retrieve the NCU name and type from the given link:name or interface:name.
*/
nwam_error_t
nwam_ncu_typed_name_to_name(const char *typedname, nwam_ncu_type_t *typep,
char **namep)
{
char *prefixstr;
assert(typedname != NULL && namep != NULL);
if (strncasecmp(typedname, 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(typedname, 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(typedname + strlen(prefixstr));
if (*namep == NULL)
return (NWAM_NO_MEMORY);
return (NWAM_SUCCESS);
}
void
nwam_ncu_free(nwam_ncu_handle_t ncuh)
{
nwam_free(ncuh, B_TRUE);
}
nwam_error_t
nwam_ncu_copy(nwam_ncu_handle_t oldncuh, const char *newname,
nwam_ncu_handle_t *newncuhp)
{
nwam_error_t err;
nwam_value_t typeval;
uint64_t type;
char *typednewname, *ncufile;
boolean_t advanced;
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);
if ((err = nwam_ncu_is_advanced(oldncuh, &advanced)) == NWAM_SUCCESS &&
advanced)
return (NWAM_ENTITY_ADVANCED);
if ((err = nwam_ncu_name_to_typed_name(newname, type, &typednewname))
!= NWAM_SUCCESS)
return (err);
if ((err = nwam_ncu_filename(type, newname, &ncufile))
!= NWAM_SUCCESS) {
free(typednewname);
return (err);
}
err = nwam_copy(ncufile, oldncuh, typednewname, newncuhp);
free(typednewname);
free(ncufile);
return (err);
}
nwam_error_t
nwam_ncu_delete_prop(nwam_ncu_handle_t ncuh, const char *propname,
uint64_t flags)
{
boolean_t ro_ncu, ro_prop, advanced;
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) && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
if ((err = nwam_ncu_is_advanced(ncuh, &advanced)) == NWAM_SUCCESS &&
advanced)
return (NWAM_ENTITY_ADVANCED);
/*
* 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, uint64_t flags)
{
boolean_t ro_ncu, ro_prop, advanced;
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) && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
if ((err = nwam_ncu_is_advanced(ncuh, &advanced)) == NWAM_SUCCESS &&
advanced)
return (NWAM_ENTITY_ADVANCED);
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);
}
/*
* Do not allow the MTU to be changed on the active NCP since libdladm
* returns "link busy" when trying to change the MTU.
*/
if (err == NWAM_SUCCESS && nwam_ncp_is_active(ncph) &&
strcasecmp(propname, NWAM_NCU_PROP_MTU) == 0) {
nwam_ncp_free(ncph);
return (NWAM_ENTITY_IN_USE);
}
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);
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);
}
/*
* Generate address object names like net0/v4a, net0/v4b, net0/v4aa, net0/v6a,
* etc using given ifname and family. The num is used to generate translate 1
* to 'a', ..., 26 to 'z', 27 to 'aa', ... for the aobjnames.
*/
static int
generate_aobjname(char *aobjname, size_t len, const char *ifname,
sa_family_t af, int32_t num)
{
char tmpstr[IPADM_AOBJ_USTRSIZ - 2]; /* 2 for leading "v#" */
char *cp = tmpstr, *endp = tmpstr + sizeof (tmpstr);
bzero(aobjname, len);
bzero(tmpstr, sizeof (tmpstr));
num2alpha(num, &cp, endp);
if (cp == endp)
return (-1);
cp[0] = '\0';
(void) snprintf(aobjname, len, "%s/v%d%s", ifname,
(af == AF_INET ? 4 : 6), tmpstr);
return (0);
}
/*
* Write out an interface NCU. The interface and addresses are written out
* separately by libipadm. Work through the NCU properties and call
* appropriate libipadm functions.
*/
/* ARGSUSED */
static nwam_error_t
nwam_ncu_interface_commit(nwam_ncu_handle_t ncuh, const char *ncpname,
uint64_t flags)
{
ipadm_handle_t iph;
ipadm_addrobj_t addr = NULL;
ipadm_status_t ipstat = IPADM_SUCCESS;
char aobjname[IPADM_AOBJSIZ];
char *fnstr;
nvlist_t *backup_idl = NULL, *idl = NULL, *objl = NULL;
int nverr = 0;
netcfg_error_t nerr = NETCFG_SUCCESS;
char *ifname = NULL;
nwam_value_t vals = NULL;
nwam_error_t err = NWAM_SUCCESS;
char *ncufile = NULL;
boolean_t backed_up = B_FALSE;
uint64_t *uintvals;
boolean_t *boolvals;
char **strvals;
uint_t i, num;
sa_family_t family;
boolean_t stateful = B_FALSE, stateless = B_FALSE;
/* the following is used to generate 'a', 'b', etc for v4a, v4b, etc */
int32_t v4suf = 0, v6suf = 0;
if ((ipstat = ipadm_open(&iph, ncpname, 0, NULL)) != IPADM_SUCCESS) {
(void) nvlist_add_string(ncuh->nwh_data, "ipadm-error",
ipadm_status2str(ipstat));
(void) nvlist_add_string(ncuh->nwh_data, "ipadm-call",
"ipadm_open");
return (NWAM_ERROR_IPADM);
}
if ((err = nwam_ncu_get_name(ncuh, &ifname)) != NWAM_SUCCESS)
goto done;
/*
* We will call multiple libipadm commands to write this NCU. Backup
* the existing NCU file so that it can be restored if there is any
* failure.
*/
if ((err = nwam_ncu_filename(NWAM_NCU_TYPE_INTERFACE, ncpname,
&ncufile)) != NWAM_SUCCESS)
goto done;
if ((nerr = netcfg_init_idlist(&backup_idl, ncufile))
!= NETCFG_SUCCESS ||
(nerr = netcfg_backup_db(&backup_idl)) != NETCFG_SUCCESS) {
goto done;
} else {
backed_up = B_TRUE;
}
/*
* Because multiple entries have to be made to the configuration file,
* remove existing configuration for this interface.
*/
ipstat = ipadm_delete_ip(iph, ifname, AF_UNSPEC, IPADM_OPT_PERSIST);
if (ipstat == IPADM_OBJ_NOTFOUND)
ipstat = IPADM_SUCCESS;
else if (ipstat != IPADM_SUCCESS) {
fnstr = "ipadm_delete_ip";
goto done;
}
/*
* Check "ip-version" and call ipadm_create_ip() to create the
* interface entries.
*/
if ((err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IP_VERSION,
&vals)) != NWAM_SUCCESS ||
(err = nwam_value_get_uint64_array(vals, &uintvals, &num))
!= NWAM_SUCCESS)
goto done;
family = num == 2 ? AF_UNSPEC :
(uintvals[0] == IPV4_VERSION ? AF_INET : AF_INET6);
if ((ipstat = ipadm_create_ip(iph, ifname, family, IPADM_OPT_PERSIST))
!= IPADM_SUCCESS) {
fnstr = "ipadm_create_ip";
goto done;
}
nwam_value_free(vals);
vals = NULL;
/*
* Read back the just created "_ifname" entry so that we can add other
* non-address related properties to it. Read the relevant property
* from the NCU data and add to the nvlist to write out.
*/
if ((nerr = netcfg_init_idlist(&idl, ncufile)) != NETCFG_SUCCESS ||
(nerr = netcfg_add_idlist(idl, IPADM_PROP_IFNAME, ifname))
!= NETCFG_SUCCESS ||
(nerr = netcfg_read_object(&idl, 0, &objl)) != NETCFG_SUCCESS)
goto done;
/* type */
if ((err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_TYPE,
&vals)) != NWAM_SUCCESS ||
(err = nwam_value_get_uint64_array(vals, &uintvals, &num))
!= NWAM_SUCCESS ||
(nverr = nvlist_add_uint64_array(objl, NWAM_NCU_PROP_TYPE, uintvals,
num)) != 0)
goto done;
nwam_value_free(vals);
vals = NULL;
/* parent */
if ((err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_PARENT_NCP,
&vals)) != NWAM_SUCCESS ||
(err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS ||
(nverr = nvlist_add_string_array(objl, NWAM_NCU_PROP_PARENT_NCP,
strvals, num)) != 0)
goto done;
nwam_value_free(vals);
vals = NULL;
/* enabled */
if ((err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_ENABLED,
&vals)) != NWAM_SUCCESS ||
(err = nwam_value_get_boolean_array(vals, &boolvals, &num))
!= NWAM_SUCCESS ||
(nverr = nvlist_add_boolean_array(objl, NWAM_NCU_PROP_ENABLED,
boolvals, num)) != 0)
goto done;
nwam_value_free(vals);
vals = NULL;
/* ipv4-default-route */
err = nwam_get_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, &vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v4_route;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS ||
(nverr = nvlist_add_string_array(objl,
NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, strvals, num)) != 0)
goto done;
nwam_value_free(vals);
vals = NULL;
skip_v4_route:
/* ipv6-default-route */
err = nwam_get_prop_value(ncuh->nwh_data,
NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, &vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v6_route;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS ||
(nverr = nvlist_add_string_array(objl,
NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, strvals, num)) != 0)
goto done;
nwam_value_free(vals);
vals = NULL;
skip_v6_route:
/* write out the "_ifname" line */
if ((nerr = netcfg_update_object(&idl, 0, &objl)) != NETCFG_SUCCESS)
goto done;
/* add v4 addresses */
err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IPV4_ADDRSRC,
&vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v4_dhcp;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_uint64_array(vals, &uintvals, &num))
!= NWAM_SUCCESS)
goto done;
/* DHCP address, if any */
for (i = 0; i < num; i++) {
if (uintvals[i] != NWAM_ADDRSRC_DHCP)
continue;
if (generate_aobjname(aobjname, sizeof (aobjname), ifname,
AF_INET, v4suf++) != 0) {
fnstr = "generating v4 dhcp aobjname";
goto done;
}
if ((ipstat = ipadm_create_addrobj(IPADM_ADDR_DHCP, aobjname,
&addr)) != IPADM_SUCCESS) {
fnstr = "ipadm_create_addrobj dhcp";
goto done;
}
ipstat = ipadm_create_addr(iph, addr, IPADM_OPT_PERSIST);
ipadm_destroy_addrobj(addr);
if (ipstat != IPADM_SUCCESS) {
fnstr = "ipadm_create_addr dhcp";
goto done;
}
}
nwam_value_free(vals);
vals = NULL;
skip_v4_dhcp:
/* static addresses, if any */
err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IPV4_ADDR,
&vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v4_static;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS)
goto done;
for (i = 0; i < num; i++) {
if (generate_aobjname(aobjname, sizeof (aobjname), ifname,
AF_INET, v4suf++) != 0) {
fnstr = "generating v4 static aobjname";
goto done;
}
if ((ipstat = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname,
&addr)) != IPADM_SUCCESS) {
fnstr = "ipadm_create_addrobj static";
goto done;
}
if ((ipstat = ipadm_set_addr(addr, strvals[i], AF_INET))
!= IPADM_SUCCESS) {
ipadm_destroy_addrobj(addr);
fnstr = "ipadm_set_addr";
goto done;
}
ipstat = ipadm_create_addr(iph, addr,
IPADM_OPT_UP | IPADM_OPT_PERSIST);
ipadm_destroy_addrobj(addr);
if (ipstat != IPADM_SUCCESS) {
fnstr = "ipadm_create_addr static";
goto done;
}
}
nwam_value_free(vals);
vals = NULL;
skip_v4_static:
/* add v6 addresses */
err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IPV6_ADDRSRC,
&vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v6_dhcp;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_uint64_array(vals, &uintvals, &num))
!= NWAM_SUCCESS)
goto done;
/* DHCP address, if any */
for (i = 0; i < num; i++) {
if (uintvals[i] == NWAM_ADDRSRC_DHCP)
stateful = B_TRUE;
else if (uintvals[i] == NWAM_ADDRSRC_AUTOCONF)
stateless = B_TRUE;
}
nwam_value_free(vals);
vals = NULL;
if (stateless || stateful) {
if (generate_aobjname(aobjname, sizeof (aobjname), ifname,
AF_INET6, v6suf++) != 0) {
fnstr = "generating v6 addrconf aobjname";
goto done;
}
if ((ipstat = ipadm_create_addrobj(IPADM_ADDR_IPV6_ADDRCONF,
aobjname, &addr)) != IPADM_SUCCESS) {
fnstr = "ipadm_create_addrobj addrconf";
goto done;
}
if (!stateful)
(void) ipadm_set_stateful(addr, B_FALSE);
if (!stateless)
(void) ipadm_set_stateless(addr, B_FALSE);
ipstat = ipadm_create_addr(iph, addr, IPADM_OPT_PERSIST);
ipadm_destroy_addrobj(addr);
if (ipstat != IPADM_SUCCESS) {
fnstr = "ipadm_create_addr addrconf";
goto done;
}
}
skip_v6_dhcp:
/* static addresses, if any */
err = nwam_get_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_IPV6_ADDR,
&vals);
if (err == NWAM_ENTITY_NOT_FOUND)
goto skip_v6_static;
if (err != NWAM_SUCCESS ||
(err = nwam_value_get_string_array(vals, &strvals, &num))
!= NWAM_SUCCESS)
goto done;
for (i = 0; i < num; i++) {
if (generate_aobjname(aobjname, sizeof (aobjname), ifname,
AF_INET6, v6suf++) != 0) {
fnstr = "generating v6 static aobjname";
goto done;
}
if ((ipstat = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname,
&addr)) != IPADM_SUCCESS) {
fnstr = "ipadm_create_addrobj v6 static";
goto done;
}
if ((ipstat = ipadm_set_addr(addr, strvals[i], AF_INET6))
!= IPADM_SUCCESS) {
ipadm_destroy_addrobj(addr);
fnstr = "ipadm_set_addr v6";
goto done;
}
ipstat = ipadm_create_addr(iph, addr,
IPADM_OPT_UP | IPADM_OPT_PERSIST);
ipadm_destroy_addrobj(addr);
if (ipstat != IPADM_SUCCESS) {
fnstr = "ipadm_create_addr v6 static";
goto done;
}
}
nwam_value_free(vals);
vals = NULL;
skip_v6_static:
/* if all is well, then err must be NWAM_SUCCESS from retrieving vals */
done:
if (err == NWAM_ENTITY_NOT_FOUND)
err = NWAM_SUCCESS;
if (ipstat != IPADM_SUCCESS) {
(void) nvlist_add_string(ncuh->nwh_data, "ipadm-error",
ipadm_status2str(ipstat));
(void) nvlist_add_string(ncuh->nwh_data, "ipadm-call", fnstr);
err = NWAM_ERROR_IPADM;
}
if (nerr != NETCFG_SUCCESS)
err = netcfg_error_to_nwam_error(nerr);
if (nverr != 0)
err = nwam_errno_to_nwam_error(nverr);
nwam_value_free(vals);
free(ifname);
nvlist_free(idl);
nvlist_free(objl);
ipadm_close(iph);
if (err != NWAM_SUCCESS) {
if (backed_up)
(void) netcfg_restore_db(&backup_idl);
} else {
if (backed_up)
(void) netcfg_destroy_backup_db(&backup_idl);
}
nvlist_free(backup_idl);
free(ncufile);
/*
* Since this commit of interface NCU avoids nwam_commit(), send
* message to nwamd about this commit.
*/
(void) nwam_request_action(ncuh->nwh_object_type, ncuh->nwh_name,
ncpname, NWAM_ACTION_REFRESH);
return (err);
}
nwam_error_t
nwam_ncu_commit(nwam_ncu_handle_t ncuh, uint64_t flags)
{
nwam_error_t err;
boolean_t read_only, advanced;
char *ncpname = NULL, *ncuname = NULL;
nwam_ncu_type_t type;
assert(ncuh != NULL && ncuh->nwh_data != NULL);
if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
return (err);
if (read_only && !nwam_override_readonly(flags))
return (NWAM_ENTITY_READ_ONLY);
if ((err = nwam_ncu_is_advanced(ncuh, &advanced)) == NWAM_SUCCESS &&
advanced)
return (NWAM_ENTITY_ADVANCED);
if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS ||
(err = nwam_ncu_typed_name_to_name(ncuh->nwh_name, &type, &ncuname))
!= NWAM_SUCCESS ||
(err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
!= NWAM_SUCCESS) {
free(ncuname);
free(ncpname);
return (err);
}
if (type == NWAM_NCU_TYPE_LINK)
err = nwam_ncu_link_commit(ncuh, ncpname, flags);
else if (type == NWAM_NCU_TYPE_INTERFACE)
err = nwam_ncu_interface_commit(ncuh, ncpname, flags);
else
err = NWAM_ENTITY_INVALID;
free(ncpname);
free(ncuname);
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);
}
/*
* 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, advanced, enabled, manual;
assert(ncuh != NULL);
/*
* Don't allow NCUs of Automatic NCP or advanced NCUs to be enabled.
* nwam_ncu_get_read_only() checks the parent NCP name and NCUs of the
* Automatic NCP are considered read only.
*/
if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS ||
(err = nwam_ncu_is_advanced(ncuh, &advanced)) != NWAM_SUCCESS)
return (err);
if (read_only || advanced)
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_ENTITY_ENABLED);
}
err = nwam_enable(ncpname, ncuh);
free(ncpname);
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, advanced, enabled, manual;
assert(ncuh != NULL);
/*
* Don't allow NCUs of Automatic NCP or advanced NCUs to be disabled.
* nwam_ncu_get_read_only() checks the parent NCP name and NCUs of the
* Automatic NCP are considered read only.
*/
if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS ||
(err = nwam_ncu_is_advanced(ncuh, &advanced)) != NWAM_SUCCESS)
return (err);
if (read_only || advanced)
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_ENTITY_DISABLED);
}
err = nwam_disable(ncpname, ncuh);
free(ncpname);
return (err);
}
nwam_error_t
nwam_ncu_offline(nwam_ncu_handle_t ncuh)
{
char *ncpname = NULL;
nwam_error_t err;
boolean_t enabled;
assert(ncuh != NULL);
/* 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_ENTITY_DISABLED);
}
err = nwam_offline(ncpname, ncuh);
free(ncpname);
return (err);
}
nwam_error_t
nwam_ncu_online(nwam_ncu_handle_t ncuh)
{
char *ncpname = NULL;
nwam_error_t err;
boolean_t enabled;
assert(ncuh != NULL);
/* 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_ENTITY_DISABLED);
}
err = nwam_online(ncpname, ncuh);
free(ncpname);
return (err);
}
nwam_error_t
nwam_ncu_destroy(nwam_ncu_handle_t ncuh, uint64_t flags)
{
char *ncpname = NULL, *ncufile = NULL;
boolean_t read_only, advanced;
nwam_error_t err;
nwam_ncu_handle_t copyncuh = NULL;
nwam_ncu_type_t type;
assert(ncuh != NULL);
if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
return (err);
if (read_only && !nwam_override_readonly(flags))
return (NWAM_ENTITY_NOT_DESTROYABLE);
if ((err = nwam_ncu_is_advanced(ncuh, &advanced)) == NWAM_SUCCESS &&
advanced)
return (NWAM_ENTITY_ADVANCED);
if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS ||
(err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
!= NWAM_SUCCESS)
return (err);
/*
* Make a copy of the NCU that maps to dlmgmt and then use that NCU to
* remove the target NCU.
*/
if (type == NWAM_NCU_TYPE_LINK) {
if ((err = nwam_make_dlmgmt_ncu(ncuh, &copyncuh))
!= NWAM_SUCCESS) {
free(ncpname);
return (err);
}
err = nwam_ncu_filename(type, ncpname, &ncufile);
if (err != NWAM_SUCCESS) {
nwam_ncu_free(copyncuh);
return (err);
}
err = nwam_destroy(ncufile, copyncuh, flags);
free(ncufile);
/*
* If NWAM_FLAG_DO_NOT_FREE is specified, then copyncuh has
* not been freed by nwam_destroy(). Free it here instead,
* ncuh is not freed. However, if the flag is not specified,
* copyncuh has been freed. ncuh must also be freed. On
* failure from nwam_destroy(), copyncuh must be freed.
*/
if (err == NWAM_SUCCESS) {
if (flags & NWAM_FLAG_DO_NOT_FREE)
nwam_ncu_free(copyncuh);
else
nwam_ncu_free(ncuh);
} else {
nwam_ncu_free(copyncuh);
}
} else {
ipadm_handle_t iph;
ipadm_status_t ipstat;
char *ifname;
if ((err = nwam_ncu_get_name(ncuh, &ifname)) != NWAM_SUCCESS)
goto done;
ipstat = ipadm_open(&iph, ncpname, 0, NULL);
if (ipstat != IPADM_SUCCESS) {
free(ifname);
err = NWAM_ERROR_IPADM;
goto done;
}
ipstat = ipadm_delete_ip(iph, ifname, AF_UNSPEC,
IPADM_OPT_PERSIST);
free(ifname);
ipadm_close(iph);
if (ipstat == IPADM_SUCCESS)
err = NWAM_SUCCESS;
else
err = NWAM_ERROR_IPADM;
/*
* Since this destroy of interface NCU avoid nwam_destroy(),
* send message to nwamd about this destroy.
*/
(void) nwam_request_action(ncuh->nwh_object_type,
ncuh->nwh_name, ncpname, NWAM_ACTION_DESTROY);
/* If NWAM_FLAG_DO_NOT_FREE is not specified, free ncuh */
if (err == NWAM_SUCCESS && !(flags & NWAM_FLAG_DO_NOT_FREE))
nwam_ncu_free(ncuh);
}
done:
free(ncpname);
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)
{
assert(propname != NULL && readp != NULL);
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_type_class_to_flag(type, (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);
}
}
nwam_error_t
nwam_ncp_validate_prop(nwam_ncp_handle_t ncph, const char *propname,
nwam_value_t value)
{
assert(ncph != NULL && propname != NULL);
return (nwam_validate_prop(ncp_prop_table, ncph, propname, value));
}
/* 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_ncp_mgmt_type(nwam_value_t value)
{
uint64_t mgmt_type;
if (nwam_value_get_uint64(value, &mgmt_type) != NWAM_SUCCESS ||
mgmt_type >= NWAM_MGMT_TYPE_ANY)
return (NWAM_ENTITY_INVALID_VALUE);
return (NWAM_SUCCESS);
}
static nwam_error_t
valid_ncu_type(nwam_value_t value)
{
uint64_t type;
if (nwam_value_get_uint64(value, &type) != NWAM_SUCCESS ||
type >= NWAM_NCU_TYPE_ANY)
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)
return (NWAM_ENTITY_INVALID_VALUE);
if (nwam_ncu_type_class_compatible(NWAM_NCU_TYPE_LINK, class) ||
nwam_ncu_type_class_compatible(NWAM_NCU_TYPE_INTERFACE, class))
return (NWAM_SUCCESS);
return (NWAM_ENTITY_INVALID_VALUE);
}
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_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_mtu(nwam_value_t value)
{
return (NWAM_SUCCESS);
}
nwam_error_t
nwam_ncp_validate(nwam_ncp_handle_t ncph, const char **errpropp)
{
return (nwam_validate(ncp_prop_table, ncph, errpropp));
}
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_type_class_to_flag(type, 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)
{
assert(ncph != NULL);
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));
}
nwam_error_t
nwam_ncu_is_advanced(nwam_ncu_handle_t ncuh, boolean_t *advancedp)
{
nwam_error_t err;
nwam_ncu_type_t type;
nwam_ncu_class_t class;
uint64_t classflags = 0;
if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS ||
(err = nwam_ncu_get_ncu_class(ncuh, &class)) != NWAM_SUCCESS)
return (err);
classflags = nwam_ncu_type_class_to_flag(type, class);
if (classflags & NWAM_FLAG_NCU_CLASS_ADVANCED)
*advancedp = B_TRUE;
else
*advancedp = B_FALSE;
return (NWAM_SUCCESS);
}
/*
* This function is used by dlmgmtd and ipmgmtd to send an action to nwamd
* anytime a link or interface is added or removed. This will allow nwamd to
* read in the NCU created by dladm and ipadm. If any of the underlying PHYS
* NCUs specified in "over" (delimited by ":") do not exist and we are adding
* NCU, they are implicitly created.
*/
void
nwam_ncu_external_action(nwam_ncu_type_t type, const char *name,
const char *ncp, const char *over, boolean_t add)
{
nwam_ncp_handle_t ncph = NULL;
nwam_ncu_handle_t ncuh = NULL;
char *typedname = NULL, *typedover, *cp, *copy = NULL;
/* If it is the Automatic NCP and allow-autoconf is unset, do nothing */
if (ncu_no_autoconf(ncp, name))
return;
/* convert "name" to link:name or interface:name */
if (nwam_ncu_name_to_typed_name(name, type, &typedname) != NWAM_SUCCESS)
return;
/* and send the given action to nwamd */
(void) nwam_request_action(NWAM_OBJECT_TYPE_NCU, typedname, ncp,
add ? NWAM_ACTION_EXT_ADD : NWAM_ACTION_EXT_DESTROY);
free(typedname);
if (!add || over == NULL)
goto done;
/* get the parent NCP handle */
if (nwam_ncp_read(ncp, 0, &ncph) != NWAM_SUCCESS)
goto done;
/* "over" are delimited by ":", the default for "portnames" in aggrs */
if ((copy = strdup(over)) == NULL)
goto done;
cp = strtok(copy, ":");
while (cp != NULL) {
/*
* Generate the typed name and create the NCU. If it already
* exists, create will fail doing nothing.
*/
if (nwam_ncu_name_to_typed_name(cp, NWAM_NCU_TYPE_LINK,
&typedover) == NWAM_SUCCESS) {
if (nwam_ncu_create(ncph, typedover, NWAM_NCU_TYPE_LINK,
DATALINK_CLASS_PHYS, 0, &ncuh) == NWAM_SUCCESS) {
(void) nwam_ncu_commit(ncuh, 0);
nwam_ncu_free(ncuh);
}
free(typedover);
}
cp = strtok(NULL, ":");
}
done:
free(copy);
nwam_ncp_free(ncph);
}
/*
* NCP walker to identify active NCP and desired NCU, [on|off]lining it.
* If NCP is not fixed and is active, and link NCU exists and is not
* disabled, either online or offline the NCU and terminate the walk.
* Otherwise, continue walking NCPs. Notify caller if fixed NCP is active
* as legacy DR handling for the fixed case must be used.
*/
static int
ncp_dr_walker(nwam_ncp_handle_t ncph, void *data)
{
ncp_dr_arg_t *arg = data;
nwam_ncu_handle_t ncuh;
boolean_t fixed = B_TRUE;
nwam_state_t state;
nwam_aux_state_t aux;
nwam_error_t err;
int i;
/* Ignore offline NCPs */
if (nwam_ncp_get_state(ncph, &state, &aux) != NWAM_SUCCESS ||
state != NWAM_STATE_ONLINE)
return (0);
/* If fixed NCP is active, let caller know. */
if (nwam_ncp_is_fixed(ncph, &fixed) == NWAM_SUCCESS && fixed) {
arg->ncu_err = NWAM_ENTITY_FIXED;
return (-1);
}
if (nwam_ncu_read(ncph, arg->ncu_name, arg->ncu_type, 0, &ncuh)
!= NWAM_SUCCESS)
return (0);
if (arg->ncu_flags & NWAM_FLAG_QUERY) {
nwam_ncu_free(ncuh);
return (1);
}
err = arg->ncu_online ? nwam_ncu_online(ncuh) : nwam_ncu_offline(ncuh);
switch (err) {
case NWAM_SUCCESS:
if (!arg->ncu_online) {
/* Wait for NCU to transition offline */
for (i = 0; i < NCU_DR_RETRIES; i++) {
if (nwam_ncu_get_state(ncuh, &state, &aux)
== NWAM_SUCCESS &&
state == NWAM_STATE_OFFLINE)
break;
(void) sleep(NCU_DR_SLEEP);
}
}
nwam_ncu_free(ncuh);
return (1);
case NWAM_ENTITY_DISABLED:
nwam_ncu_free(ncuh);
return (1);
default:
nwam_ncu_free(ncuh);
arg->ncu_err = err;
/* Terminate walk indicating error */
return (-1);
}
}
nwam_error_t
nwam_ncu_dr_offline(nwam_ncu_type_t type, const char *name, uint64_t flags)
{
ncp_dr_arg_t arg;
int ret = 0;
arg.ncu_name = name;
arg.ncu_type = type;
arg.ncu_online = B_FALSE;
arg.ncu_err = NWAM_SUCCESS;
arg.ncu_flags = flags;
(void) nwam_walk_ncps(ncp_dr_walker, &arg, 0, &ret);
switch (ret) {
case 1:
return (NWAM_SUCCESS);
case 0:
return (NWAM_ENTITY_NOT_FOUND);
default:
return (arg.ncu_err);
}
}
nwam_error_t
nwam_ncu_dr_online(nwam_ncu_type_t type, const char *name, uint64_t flags)
{
ncp_dr_arg_t arg;
int ret = 0;
arg.ncu_name = name;
arg.ncu_type = type;
arg.ncu_online = B_TRUE;
arg.ncu_err = NWAM_SUCCESS;
arg.ncu_flags = flags;
(void) nwam_walk_ncps(ncp_dr_walker, &arg, 0, &ret);
switch (ret) {
case 1:
return (NWAM_SUCCESS);
case 0:
return (NWAM_ENTITY_NOT_FOUND);
default:
return (arg.ncu_err);
}
}