/*
* 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) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <assert.h>
#include <errno.h>
#include <priv.h>
#include <s10_brand.h>
#include <string.h>
#include <zone.h>
#include "libnetcfg.h"
#include "libnetcfg_impl.h"
/*
* Utility functions for libnetcfg
*/
/*
* Restrict privileges to those needed for basic operations.
*/
int
netcfg_drop_privileges(void)
{
priv_set_t *pset;
int err = 0;
if ((pset = priv_allocset()) == NULL)
return (errno);
priv_basicset(pset);
if (priv_delset(pset, PRIV_NET_ACCESS) == 0)
(void) setppriv(PRIV_SET, PRIV_EFFECTIVE, pset);
err = errno;
priv_freeset(pset);
return (err);
}
/*
* Elevate privileges so we can zone_enter() in order to perform a non-global
* zone op from the global zone. Only one thread will elevate privileges at a
* time which is guaranteed by netcfg_db_lock() in
* usr/src/cmd/cmd-inet/lib/netcfgd.c:door_handler().
*/
int
netcfg_elevate_privileges(void)
{
priv_set_t *pset;
int err = 0;
if ((pset = priv_str_to_set("zone", ",", NULL)) == NULL)
return (errno);
if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) == -1)
err = errno;
priv_freeset(pset);
return (err);
}
/*
* Attempt to realloc the passed-in array to double its size. On
* success, pass out new element count and buf pointer and return
* NETCFG_SUCCESS; on failure, the passed-in pointers are unchanged
* and an error code is returned.
*/
netcfg_error_t
grow_buf(void **buf, int *elemcnt, size_t elemsize)
{
void *newbuf;
int newcnt = *elemcnt * 2;
size_t newsize = newcnt * elemsize;
if ((newbuf = realloc(*buf, newsize)) == NULL)
return (NETCFG_NO_MEMORY);
*buf = newbuf;
*elemcnt = newcnt;
return (NETCFG_SUCCESS);
}
/*
* If the given nvlist contains a string value named NETCFG_OBJECT_DB_NAME,
* returns a pointer to the value associated with that name. Otherwise,
* returns NULL.
*/
char *
dbname_from_idlist(nvlist_t *idl)
{
char *val = NULL;
(void) nvlist_lookup_string(idl, NETCFG_OBJECT_DB_NAME, &val);
return (val);
}
/*
* If the given nvlist contains an int64 value named NETCFG_OBJECT_DB_ZONE,
* returns the value associated with that name. Otherwise, returns
* MAX_ZONEID + 1 (-1 and 0 both have meaning as zoneids).
*/
zoneid_t
zoneid_from_idlist(nvlist_t *idl)
{
int64_t val = (MAX_ZONEID + 1);
(void) nvlist_lookup_int64(idl, NETCFG_OBJECT_DB_ZONE, &val);
return (val);
}
/*
* If in a non-global zone, checks the brand name and sets the passed-in
* arg 'is_s10' appropriately.
*/
netcfg_error_t
check_s10_brand(boolean_t *is_s10)
{
zoneid_t zoneid = getzoneid();
char brand[MAXNAMELEN];
/*
* ZONE_ATTR_BRAND doesn't exist for the global zone,
* so we need to special-case it.
*/
if (zoneid == GLOBAL_ZONEID) {
*is_s10 = B_FALSE;
return (0);
}
if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand, sizeof (brand)) < 0)
return (NETCFG_FAILURE);
*is_s10 = (strcmp(brand, S10_BRANDNAME) == 0);
return (NETCFG_SUCCESS);
}
/*
* Compare two nvlists containing object IDs. The match must be exact,
* i.e. the lists must have exactly the same pairs, with exceptions for
* non-string and NETCFG_OBJECT_DB_NAME and NETCFG_OBJECT_DB_ZONE pairs.
*
* Return B_TRUE if the lists match, B_FALSE otherwise.
*/
boolean_t
netcfg_idlist_match(nvlist_t *a, nvlist_t *b)
{
nvpair_t *nvp = NULL;
char *aval, *bval;
uint_t acnt = 0, bcnt = 0;
/*
* Make sure every entry in list b appears in list a, and that
* each string value matches. db-name and zoneid pairs do not
* matter.
*/
while ((nvp = nvlist_next_nvpair(b, nvp)) != NULL) {
if (!NETCFG_IDLIST_NVPAIR_IS_ID(nvp))
continue;
if (nvpair_value_string(nvp, &bval) != 0 ||
nvlist_lookup_string(a, nvpair_name(nvp), &aval) != 0 ||
strcasecmp(aval, bval) != 0)
return (B_FALSE);
bcnt++;
}
nvp = NULL;
while ((nvp = nvlist_next_nvpair(a, nvp)) != NULL) {
if (NETCFG_IDLIST_NVPAIR_IS_ID(nvp))
acnt++;
}
return (acnt == bcnt);
}
/*
* For each pair in list dst, look up the name in list src. If the name
* exists in src, change the value in dst to match the value in src.
*
* Only modifies pairs that are of type string.
*
* Assumes that NV_UNIQUE_NAME was specified when dst was allocated.
*/
void
netcfg_alignvals(nvlist_t *dst, nvlist_t *src)
{
nvpair_t *dnvp = NULL;
char *dname, *sval;
while ((dnvp = nvlist_next_nvpair(dst, dnvp)) != NULL) {
if (nvpair_type(dnvp) != DATA_TYPE_STRING)
continue;
dname = nvpair_name(dnvp);
if (nvlist_lookup_string(src, dname, &sval) == 0)
(void) nvlist_add_string(dst, dname, sval);
}
}
#define NVL_GET_ONE(nvl, name, val, arr, getfunc) \
int err; \
uint_t nelem; \
err = getfunc(nvl, name, &arr, &nelem); \
if (err != 0) \
return (err); \
if (nelem == 0) \
return (ENOENT); \
if (nelem != 1) \
return (EINVAL); \
*val = arr[0]; \
return (0);
/*
* Given an nvlist, extract a single value from the named nvpair. The
* pair type must be an array type, and there must be one (and only one)
* value in the array.
*/
int
netcfg_nvl_get_one_boolean(nvlist_t *nvl, const char *name, boolean_t *val)
{
boolean_t *bools;
NVL_GET_ONE(nvl, name, val, bools, nvlist_lookup_boolean_array);
}
int
netcfg_nvl_get_one_int32(nvlist_t *nvl, const char *name, int32_t *val)
{
int32_t *int32s;
NVL_GET_ONE(nvl, name, val, int32s, nvlist_lookup_int32_array);
}
int
netcfg_nvl_get_one_int64(nvlist_t *nvl, const char *name, int64_t *val)
{
int64_t *int64s;
NVL_GET_ONE(nvl, name, val, int64s, nvlist_lookup_int64_array);
}
int
netcfg_nvl_get_one_uint32(nvlist_t *nvl, const char *name, uint32_t *val)
{
uint32_t *uint32;
NVL_GET_ONE(nvl, name, val, uint32, nvlist_lookup_uint32_array);
}
int
netcfg_nvl_get_one_uint64(nvlist_t *nvl, const char *name, uint64_t *val)
{
uint64_t *uint64s;
NVL_GET_ONE(nvl, name, val, uint64s, nvlist_lookup_uint64_array);
}
int
netcfg_nvl_get_one_string(nvlist_t *nvl, const char *name, char **val)
{
char **strs;
NVL_GET_ONE(nvl, name, val, strs, nvlist_lookup_string_array);
}
#define NVP_GET_ONE(nvp, val, arr, getfunc) \
int err; \
uint_t nelem; \
err = getfunc(nvp, &arr, &nelem); \
if (err != 0) \
return (err); \
if (nelem == 0) \
return (ENOENT); \
if (nelem != 1) \
return (EINVAL); \
*val = arr[0]; \
return (0);
/*
* Given an nvpair, extract a single value. The pair type must be an array
* type, and there must be one (and only one) value in the array.
*/
int
netcfg_nvp_get_one_boolean(nvpair_t *nvp, boolean_t *val)
{
boolean_t *bools;
NVP_GET_ONE(nvp, val, bools, nvpair_value_boolean_array);
}
int
netcfg_nvp_get_one_int32(nvpair_t *nvp, int32_t *val)
{
int32_t *i32s;
NVP_GET_ONE(nvp, val, i32s, nvpair_value_int32_array);
}
int
netcfg_nvp_get_one_int64(nvpair_t *nvp, int64_t *val)
{
int64_t *i64s;
NVP_GET_ONE(nvp, val, i64s, nvpair_value_int64_array);
}
int
netcfg_nvp_get_one_uint32(nvpair_t *nvp, uint32_t *val)
{
uint32_t *ui32s;
NVP_GET_ONE(nvp, val, ui32s, nvpair_value_uint32_array);
}
int
netcfg_nvp_get_one_uint64(nvpair_t *nvp, uint64_t *val)
{
uint64_t *ui64s;
NVP_GET_ONE(nvp, val, ui64s, nvpair_value_uint64_array);
}
int
netcfg_nvp_get_one_string(nvpair_t *nvp, char **val)
{
char **strs;
NVP_GET_ONE(nvp, val, strs, nvpair_value_string_array);
}
#define NVL_ADD_ONE(nvl, name, val, arr, size, addfunc) \
int err; \
if ((arr = calloc(1, size)) == NULL) \
return (ENOMEM); \
arr[0] = val; \
err = addfunc(nvl, name, arr, 1); \
(void) free(arr); \
return (err);
/*
* Given an nvlist, add a single value to the named nvpair. The pair
* type must be an array type.
*/
int
netcfg_nvl_add_one_boolean(nvlist_t *nvl, const char *name, boolean_t val)
{
boolean_t *array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (boolean_t),
nvlist_add_boolean_array);
}
int
netcfg_nvl_add_one_int32(nvlist_t *nvl, const char *name, int32_t val)
{
int32_t *array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (int32_t),
nvlist_add_int32_array);
}
int
netcfg_nvl_add_one_int64(nvlist_t *nvl, const char *name, int64_t val)
{
int64_t *array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (int64_t),
nvlist_add_int64_array);
}
int
netcfg_nvl_add_one_uint32(nvlist_t *nvl, const char *name, uint32_t val)
{
uint32_t *array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (uint32_t),
nvlist_add_uint32_array);
}
int
netcfg_nvl_add_one_uint64(nvlist_t *nvl, const char *name, uint64_t val)
{
uint64_t *array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (uint64_t),
nvlist_add_uint64_array);
}
int
netcfg_nvl_add_one_string(nvlist_t *nvl, const char *name, char *val)
{
char **array;
NVL_ADD_ONE(nvl, name, val, array, sizeof (char *),
nvlist_add_string_array);
}