#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "ipmgmt_impl.h"
#define IPMGMTD_APP_PG "ipmgmtd"
#define IPMGMTD_PROP_FBD "first_boot_done"
#define IPMGMTD_PROP_DBVER "datastore_version"
#define IPMGMTD_TRUESTR "true"
#define ATYPE "_atype"
#define FLAGS "_flags"
#define IPMGMT_ATYPE_V6ACONF 0x1
extern pthread_rwlock_t ipmgmt_dbconf_lock;
static boolean_t ipmgmt_rdonly_root = B_FALSE;
typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t);
static ipmgmt_if_updater_func_t ipmgmt_if_family_updater;
static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater;
static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl);
typedef struct {
const char *name;
ipmgmt_if_updater_func_t *func;
} ipmgmt_if_updater_ent_t;
static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = {
{IPADM_NVP_FAMILIES, ipmgmt_if_family_updater},
{IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater},
{NULL, NULL}
};
static ipmgmt_if_updater_ent_t *
ipmgmt_find_if_field_updater(const char *field_name)
{
int i;
for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) {
if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) {
break;
}
}
return (&ipmgmt_if_updater_ent[i]);
}
static int
ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp,
uint_t flags)
{
char **members;
char *member;
char **out_members;
uint_t nelem = 0, cnt = 0;
int err;
if ((err = nvpair_value_string(member_nvp, &member)) != 0)
return (err);
err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES,
&members, &nelem);
if (err != 0 && (flags & IPMGMT_REMOVE))
return (ENOENT);
out_members = calloc(nelem + 1, sizeof (char *));
if (out_members == NULL)
return (ENOMEM);
while (nelem-- > 0) {
if ((flags & IPMGMT_REMOVE) &&
(strcmp(member, members[nelem]) == 0))
continue;
if ((out_members[cnt] = strdup(members[nelem])) == NULL) {
err = ENOMEM;
goto fail;
}
cnt++;
}
if (flags & IPMGMT_APPEND) {
if ((out_members[cnt] = strdup(member)) == NULL) {
err = ENOMEM;
goto fail;
}
cnt++;
}
if (cnt == 0) {
err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES,
DATA_TYPE_STRING_ARRAY);
} else {
err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES,
out_members, cnt);
}
fail:
while (cnt--)
free(out_members[cnt]);
free(out_members);
return (err);
}
static int
ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags)
{
uint16_t *families;
uint_t nelem = 0;
int err;
if ((err = nvpair_value_uint16_array(families_nvp, &families,
&nelem)) != 0)
return (err);
return (ipmgmt_update_family_nvp(db_nvl, families[0], flags));
}
int
ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags)
{
uint16_t *families = NULL;
uint16_t out_families[2];
uint_t nelem = 0, cnt;
int err;
err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
&families, &nelem);
if (err != 0 && (flags & IPMGMT_REMOVE)) {
return (ENOENT);
}
if (flags & IPMGMT_APPEND) {
if (families != NULL) {
if (nelem == 2 || families[0] == af) {
return (EEXIST);
}
out_families[0] = families[0];
out_families[1] = af;
cnt = 2;
} else {
out_families[0] = af;
cnt = 1;
}
} else {
assert(nelem == 1 || nelem == 2);
cnt = 0;
while (nelem-- > 0) {
if (families[nelem] != af) {
out_families[cnt] = families[nelem];
cnt++;
}
}
}
if (cnt != 0) {
return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES,
out_families, cnt));
}
return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY));
}
static boolean_t
ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
const char *aobjname)
{
char *db_proto = NULL, *db_ifname = NULL;
char *db_aobjname = NULL;
nvpair_t *nvp;
char *name;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
(void) nvpair_value_string(nvp, &db_proto);
else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
(void) nvpair_value_string(nvp, &db_ifname);
else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
(void) nvpair_value_string(nvp, &db_aobjname);
}
if (proto != NULL && proto[0] == '\0')
proto = NULL;
if (ifname != NULL && ifname[0] == '\0')
ifname = NULL;
if (aobjname != NULL && aobjname[0] == '\0')
aobjname = NULL;
if ((proto == NULL && db_proto != NULL) ||
(proto != NULL && db_proto == NULL) ||
(proto != NULL && db_proto != NULL &&
strcmp(proto, db_proto) != 0)) {
return (B_FALSE);
}
if ((ifname == NULL && db_ifname != NULL) ||
(ifname != NULL && db_ifname == NULL) ||
(ifname != NULL && db_ifname != NULL &&
strcmp(ifname, db_ifname) != 0)) {
return (B_FALSE);
}
if ((aobjname == NULL && db_aobjname != NULL) ||
(aobjname != NULL && db_aobjname == NULL) ||
(aobjname != NULL && db_aobjname != NULL &&
strcmp(aobjname, db_aobjname) != 0)) {
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
{
nvpair_t *nvp;
char *name;
char *proto = NULL, *ifname = NULL, *aobjname = NULL;
for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(in_nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
(void) nvpair_value_string(nvp, &proto);
else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
(void) nvpair_value_string(nvp, &ifname);
else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
(void) nvpair_value_string(nvp, &aobjname);
}
return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
}
static boolean_t
ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
const char *ifname, char *aobjname)
{
char *db_ifname = NULL, *db_proto = NULL;
char *db_aobjname = NULL;
nvpair_t *nvp;
char *name;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
(void) nvpair_value_string(nvp, &db_proto);
else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
(void) nvpair_value_string(nvp, &db_ifname);
else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
(void) nvpair_value_string(nvp, &db_aobjname);
}
if (proto != NULL && proto[0] != '\0') {
if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
return (B_FALSE);
}
if (ifname != NULL && ifname[0] != '\0') {
if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
return (B_FALSE);
}
if (aobjname != NULL && aobjname[0] != '\0') {
if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
return (B_FALSE);
}
return (B_TRUE);
}
boolean_t
ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_prop_arg_t *pargp = arg;
boolean_t cont = B_TRUE;
char *pval;
int err = 0;
*errp = 0;
if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
pargp->ia_ifname, pargp->ia_aobjname))
return (B_TRUE);
if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
&pval)) == 0) {
(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
cont = B_FALSE;
} else {
if (err == ENOENT)
err = 0;
*errp = err;
}
return (cont);
}
boolean_t
ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_prop_arg_t *pargp = arg;
*errp = 0;
if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
pargp->ia_ifname, pargp->ia_aobjname))
return (B_TRUE);
if (!nvlist_exists(db_nvl, pargp->ia_pname))
return (B_TRUE);
if (pargp->ia_flags & IPMGMT_REMOVE) {
char *dbpval = NULL;
char *inpval = pargp->ia_pval;
char pval[MAXPROPVALLEN];
char *val, *lasts;
*errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
if (*errp != 0)
return (B_FALSE);
bzero(pval, sizeof (pval));
if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
if (strcmp(val, inpval) != 0)
(void) strlcat(pval, val, MAXPROPVALLEN);
while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
if (strcmp(val, inpval) != 0) {
if (pval[0] != '\0')
(void) strlcat(pval, ",",
MAXPROPVALLEN);
(void) strlcat(pval, val,
MAXPROPVALLEN);
}
}
} else {
if (strcmp(dbpval, inpval) != 0)
*errp = ENOENT;
else
buf[0] = '\0';
return (B_FALSE);
}
*errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
if (*errp != 0)
return (B_FALSE);
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
*errp = ENOBUFS;
}
} else {
buf[0] = '\0';
}
return (B_FALSE);
}
boolean_t
ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_get_cbarg_t *cbarg = arg;
char *db_aobjname = NULL;
char *db_ifname = NULL;
nvlist_t *db_addr = NULL;
char name[IPMGMT_STRSIZE];
nvpair_t *nvp;
boolean_t add_nvl = B_FALSE;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
(void) nvpair_value_nvlist(nvp, &db_addr);
else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
(void) nvpair_value_string(nvp, &db_ifname);
else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
(void) nvpair_value_string(nvp, &db_aobjname);
}
if (db_aobjname == NULL)
return (B_TRUE);
if (cbarg->cb_aobjname[0] != '\0') {
if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
add_nvl = B_TRUE;
} else if (cbarg->cb_ifname[0] != '\0') {
if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
add_nvl = B_TRUE;
} else {
add_nvl = B_TRUE;
}
if (add_nvl) {
(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
cbarg->cb_ocnt);
*errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
if (*errp == 0)
cbarg->cb_ocnt++;
}
return (B_TRUE);
}
static void *
ipmgmt_db_restore_thread(void *arg)
{
int err;
for (;;) {
(void) sleep(5);
(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
if (!ipmgmt_rdonly_root)
break;
err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
if (err == 0) {
ipmgmt_rdonly_root = B_FALSE;
break;
}
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
}
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
return (NULL);
}
extern int
ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
{
int err;
boolean_t writeop;
mode_t mode;
pthread_t tid;
pthread_attr_t attr;
writeop = (db_op != IPADM_DB_READ);
if (writeop) {
(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
mode = IPADM_FILE_MODE;
} else {
(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
mode = 0;
}
if (!ipmgmt_rdonly_root) {
err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
mode, db_op);
if (err != EROFS)
goto done;
}
if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
assert(writeop);
err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
if (err != 0)
goto done;
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
(void) pthread_attr_setname_np(&attr, "db_restore");
err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
NULL);
(void) pthread_attr_destroy(&attr);
if (err != 0) {
(void) unlink(IPADM_VOL_DB_FILE);
goto done;
}
ipmgmt_rdonly_root = B_TRUE;
}
err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
mode, db_op);
done:
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
return (err);
}
boolean_t
ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
{
return (B_TRUE);
}
boolean_t
ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipadm_dbwrite_cbarg_t *cb = arg;
uint_t flags = cb->dbw_flags;
nvlist_t *in_nvl = cb->dbw_nvl;
nvpair_t *nvp;
char *name, *instrval = NULL, *dbstrval = NULL;
char pval[MAXPROPVALLEN];
*errp = 0;
if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
return (B_TRUE);
for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(in_nvl, nvp)) {
name = nvpair_name(nvp);
if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
break;
}
if (nvp == NULL)
return (B_TRUE);
assert(nvpair_type(nvp) == DATA_TYPE_STRING);
if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
return (B_FALSE);
if (flags & IPMGMT_APPEND) {
if ((*errp = nvlist_lookup_string(db_nvl, name,
&dbstrval)) != 0)
return (B_FALSE);
(void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
instrval);
if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
return (B_FALSE);
} else {
if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
return (B_FALSE);
}
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
*errp = ENOBUFS;
}
return (B_FALSE);
}
boolean_t
ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipadm_dbwrite_cbarg_t *cb = arg;
ipmgmt_if_updater_ent_t *updater;
nvlist_t *in_nvl = cb->dbw_nvl;
uint_t flags = cb->dbw_flags;
nvpair_t *nvp;
char *name;
char *db_ifname;
char *gifname = NULL;
char *mifname = NULL;
*errp = 0;
if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 ||
((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) {
*errp = EINVAL;
return (B_FALSE);
}
if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES))
return (B_TRUE);
if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) &&
nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 &&
nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 &&
strcmp(db_ifname, mifname) == 0) {
if (flags & IPMGMT_APPEND) {
if ((*errp = nvlist_add_string(db_nvl,
IPADM_NVP_GIFNAME, gifname)) != 0)
return (B_FALSE);
} else {
if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME,
DATA_TYPE_STRING)) != 0)
return (B_FALSE);
}
cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP;
goto done;
}
if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
return (B_TRUE);
for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(in_nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_FAMILIES) != 0 &&
strcmp(name, IPADM_NVP_MIFNAMES) != 0)
continue;
updater = ipmgmt_find_if_field_updater(name);
assert(updater != NULL);
*errp = (*updater->func)(db_nvl, nvp, flags);
if (*errp != 0)
return (B_FALSE);
}
cb->dbw_flags &= ~IPMGMT_UPDATE_IF;
done:
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
*errp = EOVERFLOW;
return (B_FALSE);
}
if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0)
return (B_FALSE);
return (B_TRUE);
}
boolean_t
ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_get_cbarg_t *cbarg = arg;
char *ifname = cbarg->cb_ifname;
nvpair_t *nvp;
char *db_ifname = NULL;
boolean_t families = B_FALSE;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) {
(void) nvpair_value_string(nvp, &db_ifname);
} else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) {
families = B_TRUE;
}
}
if (db_ifname == NULL || !families)
return (B_TRUE);
if (ifname != NULL && ifname[0] != '\0' &&
strcmp(ifname, db_ifname) != 0)
return (B_TRUE);
*errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl);
if (*errp == 0)
cbarg->cb_ocnt++;
if (ifname != NULL && ifname[0] != '\0')
return (B_FALSE);
return (B_TRUE);
}
boolean_t
ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_if_cbarg_t *cbarg = arg;
boolean_t isv6 = (cbarg->cb_family == AF_INET6);
char *ifname = cbarg->cb_ifname;
char *modstr = NULL;
char *aobjname;
uint_t proto;
ipmgmt_aobjmap_t *head;
boolean_t aobjfound = B_FALSE;
*errp = 0;
if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
return (B_TRUE);
if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family,
IPMGMT_REMOVE)) != 0) {
return (B_FALSE);
}
if (cbarg->cb_family == AF_INET) {
cbarg->cb_ipv4exists = B_FALSE;
} else {
assert(cbarg->cb_family == AF_INET6);
cbarg->cb_ipv6exists = B_FALSE;
}
if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
cbarg->cb_ipv4exists = B_FALSE;
cbarg->cb_ipv6exists = B_FALSE;
goto delete;
}
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
*errp = EOVERFLOW;
return (B_FALSE);
}
return (B_TRUE);
}
if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
goto delete;
}
if (!isv6 &&
(nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
goto delete;
}
if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
head = aobjmap.aobjmap_head;
while (head != NULL) {
if (strcmp(head->am_aobjname, aobjname) == 0) {
aobjfound = B_TRUE;
if (head->am_family == cbarg->cb_family)
goto delete;
}
head = head->am_next;
}
if (!aobjfound)
goto delete;
}
if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
proto = ipadm_str2proto(modstr);
switch (proto) {
case MOD_PROTO_IPV6:
if (isv6)
goto delete;
break;
case MOD_PROTO_IPV4:
if (!isv6)
goto delete;
break;
case MOD_PROTO_IP:
if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists)
goto delete;
break;
}
}
return (B_TRUE);
delete:
buf[0] = '\0';
return (B_TRUE);
}
boolean_t
ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_resetaddr_cbarg_t *cbarg = arg;
char *aobjname = cbarg->cb_aobjname;
*errp = 0;
if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
return (B_TRUE);
buf[0] = '\0';
return (B_TRUE);
}
boolean_t
ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipmgmt_initif_cbarg_t *cbarg = arg;
nvlist_t *onvl = cbarg->cb_onvl;
nvlist_t *invl = cbarg->cb_invl;
sa_family_t in_af = cbarg->cb_family;
char *db_ifname;
*errp = 0;
if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
nvlist_exists(invl, db_ifname)) {
char name[IPMGMT_STRSIZE];
sa_family_t db_af = in_af;
uint_t proto;
char *pstr;
if (in_af != AF_UNSPEC) {
if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
&pstr) == 0) {
proto = ipadm_str2proto(pstr);
if (proto == MOD_PROTO_IPV4)
db_af = AF_INET;
else if (proto == MOD_PROTO_IPV6)
db_af = AF_INET6;
else
db_af = in_af;
} else {
if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
nvlist_exists(db_nvl, IPADM_NVP_DHCP))
db_af = AF_INET;
else
db_af = AF_INET6;
}
}
if (in_af == db_af) {
(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
cbarg->cb_ocnt);
*errp = nvlist_add_nvlist(onvl, name, db_nvl);
if (*errp == 0)
cbarg->cb_ocnt++;
}
}
return (B_TRUE);
}
static int
i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
{
ipmgmt_aobjmap_t *new, *head;
head = aobjmap.aobjmap_head;
if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
return (ENOMEM);
*new = *nodep;
new->am_next = NULL;
if (head == NULL) {
aobjmap.aobjmap_head = new;
} else {
new->am_next = aobjmap.aobjmap_head;
aobjmap.aobjmap_head = new;
}
return (0);
}
static void
i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
{
if (num >= 26)
i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
if (*cp != endp) {
*cp[0] = 'a' + (num % 26);
(*cp)++;
}
}
static int
i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
{
ipmgmt_aobjmap_t *head;
uint32_t nextnum;
for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
break;
nextnum = (head == NULL ? 0 : head->am_nextnum);
if (nodep->am_aobjname[0] == '\0') {
char tmpstr[IPADM_AOBJ_USTRSIZ - 1];
char *cp = tmpstr;
char *endp = tmpstr + sizeof (tmpstr);
i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
if (cp == endp)
return (EINVAL);
cp[0] = '\0';
if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
return (EINVAL);
}
nodep->am_nextnum = ++nextnum;
} else {
for (head = aobjmap.aobjmap_head; head != NULL;
head = head->am_next) {
if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
return (EEXIST);
}
nodep->am_nextnum = nextnum;
}
return (i_ipmgmt_add_amnode(nodep));
}
int
ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
{
ipmgmt_aobjmap_t *head, *prev, *matched = NULL;
boolean_t update = B_TRUE;
int err = 0;
ipadm_db_op_t db_op = IPADM_DB_READ;
(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
head = aobjmap.aobjmap_head;
switch (op) {
case ADDROBJ_ADD:
for (; head != NULL; head = head->am_next) {
if (strcmp(head->am_aobjname,
nodep->am_aobjname) == 0 &&
(head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
head->ipmgmt_am_linklocal ==
nodep->ipmgmt_am_linklocal))
break;
}
if (head != NULL) {
(void) strlcpy(head->am_ifname, nodep->am_ifname,
sizeof (head->am_ifname));
head->am_lnum = nodep->am_lnum;
head->am_family = nodep->am_family;
head->am_flags = nodep->am_flags;
head->am_atype = nodep->am_atype;
head->am_atype_cache = nodep->am_atype_cache;
} else {
for (head = aobjmap.aobjmap_head; head != NULL;
head = head->am_next) {
if (strcmp(head->am_ifname,
nodep->am_ifname) == 0)
break;
}
nodep->am_nextnum = (head == NULL ? 0 :
head->am_nextnum);
err = i_ipmgmt_add_amnode(nodep);
}
db_op = IPADM_DB_WRITE;
break;
case ADDROBJ_DELETE:
prev = head;
while (head != NULL) {
if (strcmp(head->am_aobjname,
nodep->am_aobjname) == 0) {
nodep->am_atype = head->am_atype;
if (head->am_atype !=
IPADM_ADDR_IPV6_ADDRCONF ||
nodep->am_lnum == head->am_lnum)
break;
}
prev = head;
head = head->am_next;
}
if (head != NULL) {
if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
nodep->am_flags == IPMGMT_ACTIVE) {
head->am_flags &= ~IPMGMT_ACTIVE;
head->am_lnum = -1;
db_op = IPADM_DB_WRITE;
*nodep = *head;
} else {
(void) strlcpy(nodep->am_ifname,
head->am_ifname,
sizeof (nodep->am_ifname));
if (head == aobjmap.aobjmap_head)
aobjmap.aobjmap_head = head->am_next;
else
prev->am_next = head->am_next;
free(head);
db_op = IPADM_DB_DELETE;
}
} else {
err = ENOENT;
}
break;
case ADDROBJ_LOOKUPADD:
err = i_ipmgmt_lookupadd_amnode(nodep);
update = B_FALSE;
break;
case ADDROBJ_SETLIFNUM:
update = B_FALSE;
for (; head != NULL; head = head->am_next) {
if (strcmp(head->am_ifname,
nodep->am_ifname) == 0 &&
head->am_family == nodep->am_family &&
head->am_lnum == nodep->am_lnum) {
err = EEXIST;
break;
}
if (strcmp(head->am_aobjname,
nodep->am_aobjname) == 0) {
matched = head;
}
}
if (err == EEXIST)
break;
if (matched != NULL) {
matched->am_lnum = nodep->am_lnum;
} else {
err = ENOENT;
}
break;
default:
assert(0);
}
if (err == 0 && update)
err = ipmgmt_persist_aobjmap(nodep, db_op);
(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
return (err);
}
static int
i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
{
int err;
char strval[IPMGMT_STRSIZE];
*nvl = NULL;
if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
goto fail;
if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
np->am_aobjname)) != 0)
goto fail;
if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
np->am_ifname)) != 0)
goto fail;
(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
goto fail;
(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
goto fail;
(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
goto fail;
(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
goto fail;
switch (np->am_atype) {
case IPADM_ADDR_IPV6_ADDRCONF: {
struct sockaddr_in6 *in6;
in6 = &np->ipmgmt_am_ifid;
if (np->ipmgmt_am_linklocal &&
IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
if ((err = nvlist_add_string(*nvl,
IPADM_NVP_IPNUMADDR, "default")) != 0) {
goto fail;
}
} else {
if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
IPMGMT_STRSIZE) == NULL) {
err = errno;
goto fail;
}
if ((err = nvlist_add_string(*nvl,
IPADM_NVP_IPNUMADDR, strval)) != 0) {
goto fail;
}
}
}
break;
case IPADM_ADDR_DHCP: {
if (*np->ipmgmt_am_reqhost != '\0' &&
(err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST,
np->ipmgmt_am_reqhost)) != 0)
goto fail;
}
default:
if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
"")) != 0)
goto fail;
break;
}
return (err);
fail:
nvlist_free(*nvl);
return (err);
}
extern boolean_t
ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
nvpair_t *nvp = NULL;
char *name, *strval = NULL;
ipmgmt_aobjmap_t node;
struct sockaddr_in6 *in6;
*errp = 0;
node.am_next = NULL;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
name = nvpair_name(nvp);
if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
return (B_TRUE);
if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
(void) strlcpy(node.am_aobjname, strval,
sizeof (node.am_aobjname));
} else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
(void) strlcpy(node.am_ifname, strval,
sizeof (node.am_ifname));
} else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
node.am_lnum = atoi(strval);
} else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
node.am_family = (sa_family_t)atoi(strval);
} else if (strcmp(FLAGS, name) == 0) {
node.am_flags = atoi(strval);
} else if (strcmp(ATYPE, name) == 0) {
node.am_atype = (ipadm_addr_type_t)atoi(strval);
} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
in6 = &node.ipmgmt_am_ifid;
if (strcmp(strval, "default") == 0) {
bzero(in6,
sizeof (node.ipmgmt_am_ifid));
node.ipmgmt_am_linklocal = B_TRUE;
} else {
(void) inet_pton(AF_INET6, strval,
&in6->sin6_addr);
if (IN6_IS_ADDR_UNSPECIFIED(
&in6->sin6_addr))
node.ipmgmt_am_linklocal =
B_TRUE;
}
}
}
}
*errp = i_ipmgmt_add_amnode(&node);
return (B_TRUE);
}
static boolean_t
ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
size_t buflen, int *errp)
{
ipadm_dbwrite_cbarg_t *cb = arg;
nvlist_t *in_nvl = cb->dbw_nvl;
uint32_t flags = cb->dbw_flags;
char *db_lifnumstr = NULL, *in_lifnumstr = NULL;
*errp = 0;
if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
return (B_TRUE);
if (flags & IPMGMT_ATYPE_V6ACONF) {
if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
&db_lifnumstr) != 0 ||
nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
&in_lifnumstr) != 0 ||
(atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
strcmp(db_lifnumstr, in_lifnumstr) != 0))
return (B_TRUE);
}
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
*errp = ENOBUFS;
}
return (B_FALSE);
}
static boolean_t
ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
size_t buflen, int *errp)
{
ipmgmt_aobjmap_t *nodep = arg;
char *db_lifnumstr = NULL;
*errp = 0;
if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
nodep->am_aobjname))
return (B_TRUE);
if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
&db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
return (B_TRUE);
}
buf[0] = '\0';
return (B_FALSE);
}
extern int
ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
{
int err;
ipadm_dbwrite_cbarg_t cb;
nvlist_t *nvl = NULL;
if (op == IPADM_DB_WRITE) {
if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
return (err);
cb.dbw_nvl = nvl;
if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
else
cb.dbw_flags = 0;
err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
nvlist_free(nvl);
} else {
assert(op == IPADM_DB_DELETE);
err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
}
return (err);
}
boolean_t
ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
nvpair_t *nvp;
char *name, *pname = NULL, *protostr = NULL, *pval = NULL;
uint_t proto, nproto;
char nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
*errp = 0;
if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
return (B_TRUE);
}
assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
if (nvpair_value_string(nvp, &protostr) != 0)
return (B_TRUE);
} else {
assert(!IPADM_PRIV_NVP(name));
pname = name;
if (nvpair_value_string(nvp, &pval) != 0)
return (B_TRUE);
}
}
if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
return (B_TRUE);
}
nproto = proto = ipadm_str2proto(protostr);
if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
&nproto) != 0) {
return (B_TRUE);
}
if (nproto != proto) {
protostr = ipadm_proto2str(nproto);
if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
protostr) != 0) {
return (B_TRUE);
}
}
(void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
return (B_TRUE);
}
(void) memset(buf, 0, buflen);
if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
*errp = ENOBUFS;
}
return (B_TRUE);
}
static boolean_t
ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
int *errp)
{
ipadm_handle_t iph = cbarg;
nvpair_t *nvp, *pnvp = NULL;
char *strval = NULL, *name, *mod = NULL, *pname;
char tmpstr[IPMGMT_STRSIZE];
uint_t proto;
for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(db_nvl, nvp)) {
name = nvpair_name(nvp);
if (IPADM_PRIV_NVP(name)) {
if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
strcmp(name, IPADM_NVP_AOBJNAME) == 0)
return (B_TRUE);
else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
nvpair_value_string(nvp, &mod) != 0)
return (B_TRUE);
} else {
pnvp = nvp;
}
}
assert(mod != NULL);
assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
proto = ipadm_str2proto(mod);
name = nvpair_name(pnvp);
if (nvpair_value_string(pnvp, &strval) == 0) {
if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
pname = &name[1];
} else if (ipadm_legacy2new_propname(name, tmpstr,
sizeof (tmpstr), &proto) == 0) {
pname = tmpstr;
} else {
pname = name;
}
if (ipadm_set_prop(iph, pname, strval, proto,
IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
pname);
}
}
return (B_TRUE);
}
void
ipmgmt_init_prop()
{
ipadm_handle_t iph = NULL;
if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
"persisted protocol properties");
return;
}
(void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
ipadm_close(iph);
}
void
ipmgmt_release_scf_resources(scf_resources_t *res)
{
scf_entry_destroy(res->sr_ent);
scf_transaction_destroy(res->sr_tx);
scf_value_destroy(res->sr_val);
scf_property_destroy(res->sr_prop);
scf_pg_destroy(res->sr_pg);
scf_instance_destroy(res->sr_inst);
(void) scf_handle_unbind(res->sr_handle);
scf_handle_destroy(res->sr_handle);
}
int
ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
{
res->sr_tx = NULL;
res->sr_ent = NULL;
res->sr_inst = NULL;
res->sr_pg = NULL;
res->sr_prop = NULL;
res->sr_val = NULL;
if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
return (-1);
if (scf_handle_bind(res->sr_handle) != 0) {
scf_handle_destroy(res->sr_handle);
return (-1);
}
if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
goto failure;
if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
goto failure;
}
return (0);
failure:
ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
scf_strerror(scf_error()));
ipmgmt_release_scf_resources(res);
return (-1);
}
static int
ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
scf_type_t ptype)
{
int result = -1;
boolean_t new;
if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
goto failure;
switch (ptype) {
case SCF_TYPE_INTEGER:
scf_value_set_integer(res->sr_val, *(int64_t *)pval);
break;
case SCF_TYPE_ASTRING:
if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
ipmgmt_log(LOG_WARNING, "Error setting string value %s "
"for property %s: %s", pval, pname,
scf_strerror(scf_error()));
goto failure;
}
break;
default:
goto failure;
}
if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
goto failure;
if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
goto failure;
if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
goto failure;
retry:
new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
goto failure;
if (new) {
if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
pname, ptype) == -1) {
goto failure;
}
} else {
if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
pname, ptype) == -1) {
goto failure;
}
}
if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
goto failure;
result = scf_transaction_commit(res->sr_tx);
if (result == 0) {
scf_transaction_reset(res->sr_tx);
if (scf_pg_update(res->sr_pg) == -1) {
goto failure;
}
goto retry;
}
if (result == -1)
goto failure;
return (0);
failure:
ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
scf_strerror(scf_error()));
return (-1);
}
static int
ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
void *pval, scf_type_t ptype)
{
ssize_t numvals;
scf_simple_prop_t *prop;
prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
numvals = scf_simple_prop_numvalues(prop);
if (numvals <= 0)
goto ret;
switch (ptype) {
case SCF_TYPE_INTEGER:
*(int64_t **)pval = scf_simple_prop_next_integer(prop);
break;
case SCF_TYPE_ASTRING:
*(char **)pval = scf_simple_prop_next_astring(prop);
break;
default:
break;
}
ret:
scf_simple_prop_free(prop);
return (numvals);
}
static int
ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
void *pval, scf_type_t ptype)
{
scf_error_t err;
if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
scf_strerror(scf_error()));
return (-1);
}
if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
0, res->sr_pg) != 0) {
if ((err = scf_error()) != SCF_ERROR_EXISTS) {
ipmgmt_log(LOG_WARNING,
"Error adding property group '%s/%s': %s",
pgname, pname, scf_strerror(err));
return (-1);
}
if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
res->sr_pg) != 0) {
ipmgmt_log(LOG_WARNING, "Error getting composed view "
"of the property group '%s/%s': %s", pgname, pname,
scf_strerror(scf_error()));
return (-1);
}
}
return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
}
boolean_t
ipmgmt_ngz_firstboot_postinstall()
{
scf_resources_t res;
boolean_t bval = B_TRUE;
char *strval;
if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
return (bval);
if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
SCF_TYPE_ASTRING) > 0) {
bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
B_FALSE : B_TRUE);
} else {
(void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
}
ipmgmt_release_scf_resources(&res);
return (bval);
}
boolean_t
ipmgmt_needs_upgrade(scf_resources_t *res)
{
boolean_t bval = B_TRUE;
int64_t *verp;
if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
&verp, SCF_TYPE_INTEGER) > 0) {
if (*verp == IPADM_DB_VERSION)
bval = B_FALSE;
}
return (bval);
}
void
ipmgmt_update_dbver(scf_resources_t *res)
{
int64_t version = IPADM_DB_VERSION;
(void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
}
boolean_t
ipmgmt_persist_if_exists(const char *ifname, sa_family_t af)
{
boolean_t exists = B_FALSE;
nvlist_t *if_info_nvl;
uint16_t *families = NULL;
sa_family_t af_db;
uint_t nelem = 0;
if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0)
goto done;
if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES,
&families, &nelem) != 0)
goto done;
while (nelem-- > 0) {
af_db = families[nelem];
if (af_db == af || (af == AF_UNSPEC &&
(af_db == AF_INET || af_db == AF_INET6))) {
exists = B_TRUE;
break;
}
}
done:
if (if_info_nvl != NULL)
nvlist_free(if_info_nvl);
return (exists);
}
void
ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size)
{
char *gif_name_from_nvl;
nvlist_t *if_info_nvl;
gif_name[0] = '\0';
if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0)
goto done;
if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME,
&gif_name_from_nvl) != 0)
goto done;
(void) strlcpy(gif_name, gif_name_from_nvl, size);
done:
if (if_info_nvl != NULL)
nvlist_free(if_info_nvl);
}
static int
ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl)
{
ipmgmt_get_cbarg_t cbarg;
nvpair_t *nvp;
nvlist_t *nvl;
int err;
cbarg.cb_ifname = NULL;
cbarg.cb_aobjname = NULL;
cbarg.cb_ocnt = 0;
*if_info_nvl = NULL;
if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0)
goto done;
err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
if (err == ENOENT && cbarg.cb_ocnt > 0)
err = 0;
if (err != 0)
goto done;
for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) {
if (strcmp(nvpair_name(nvp), ifname) != 0)
continue;
if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 ||
(err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0)
*if_info_nvl = NULL;
break;
}
done:
nvlist_free(cbarg.cb_onvl);
return (err);
}