#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netdb.h>
#include <inet/ip.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <sys/sockio.h>
#include <errno.h>
#include <unistd.h>
#include <stropts.h>
#include <zone.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <ctype.h>
#include <dhcpagent_util.h>
#include <dhcpagent_ipc.h>
#include <dhcp_inittab.h>
#include <dhcp_symbol.h>
#include <ipadm_ndpd.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdliptun.h>
#include <ifaddrs.h>
#include "libipadm_impl.h"
#define SIN6(a) ((struct sockaddr_in6 *)a)
#define SIN(a) ((struct sockaddr_in *)a)
static ipadm_status_t i_ipadm_create_addr(ipadm_handle_t, ipadm_addrobj_t,
uint32_t);
static ipadm_status_t i_ipadm_create_dhcp(ipadm_handle_t, ipadm_addrobj_t,
uint32_t);
static ipadm_status_t i_ipadm_delete_dhcp(ipadm_handle_t, ipadm_addrobj_t,
boolean_t);
static ipadm_status_t i_ipadm_refresh_dhcp(ipadm_addrobj_t);
static ipadm_status_t i_ipadm_get_db_addr(ipadm_handle_t, const char *,
const char *, nvlist_t **);
static ipadm_status_t i_ipadm_op_dhcp(ipadm_addrobj_t, dhcp_ipc_type_t,
int *);
static ipadm_status_t i_ipadm_dhcp_status(ipadm_addrobj_t addr,
dhcp_status_t *status, int *dhcperror);
static ipadm_status_t i_ipadm_validate_create_addr(ipadm_handle_t,
ipadm_addrobj_t, uint32_t);
static ipadm_status_t i_ipadm_addr_persist_nvl(ipadm_handle_t, nvlist_t *,
uint32_t);
static ipadm_status_t i_ipadm_get_default_prefixlen(struct sockaddr_storage *,
uint32_t *);
static ipadm_status_t i_ipadm_get_static_addr_db(ipadm_handle_t,
ipadm_addrobj_t);
static boolean_t i_ipadm_is_user_aobjname_valid(const char *);
static ipadm_prop_desc_t *i_ipadm_get_addrprop_desc(const char *pname);
static ipadm_pd_getf_t i_ipadm_get_prefixlen, i_ipadm_get_addr_flag,
i_ipadm_get_zone, i_ipadm_get_broadcast,
i_ipadm_get_primary, i_ipadm_get_reqhost;
static ipadm_pd_setf_t i_ipadm_set_prefixlen, i_ipadm_set_addr_flag,
i_ipadm_set_zone, i_ipadm_set_reqhost;
static ipadm_status_t i_ipadm_set_aobj_addrprop(ipadm_handle_t iph,
ipadm_addrobj_t ipaddr, uint_t flags, const char *propname);
ipadm_prop_desc_t ipadm_addrprop_table[] = {
{ "broadcast", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
NULL, NULL, i_ipadm_get_broadcast },
{ "deprecated", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_addr_flag, i_ipadm_get_onoff,
i_ipadm_get_addr_flag },
{ IPADM_NVP_PREFIXLEN, NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_prefixlen, i_ipadm_get_prefixlen,
i_ipadm_get_prefixlen },
{ "primary", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
NULL, NULL, i_ipadm_get_primary },
{ "private", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
{ IPADM_NVP_REQHOST, NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_reqhost, NULL, i_ipadm_get_reqhost },
{ "transmit", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
{ "zone", NULL, IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE, 0,
i_ipadm_set_zone, NULL, i_ipadm_get_zone },
{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
};
static ipadm_prop_desc_t up_addrprop = { "up", NULL, IPADMPROP_CLASS_ADDR,
MOD_PROTO_NONE, 0, NULL, NULL, NULL };
void
i_ipadm_init_addr(ipadm_addrobj_t ipaddr, const char *ifname,
const char *aobjname, ipadm_addr_type_t atype)
{
bzero(ipaddr, sizeof (struct ipadm_addrobj_s));
(void) strlcpy(ipaddr->ipadm_ifname, ifname,
sizeof (ipaddr->ipadm_ifname));
(void) strlcpy(ipaddr->ipadm_aobjname, aobjname,
sizeof (ipaddr->ipadm_aobjname));
ipaddr->ipadm_atype = atype;
}
static ipadm_status_t
i_ipadm_pd2permstr(ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize)
{
uint_t perm;
size_t nbytes;
perm = 0;
if (pdp->ipd_set != NULL)
perm |= MOD_PROP_PERM_WRITE;
if (pdp->ipd_get != NULL)
perm |= MOD_PROP_PERM_READ;
nbytes = snprintf(buf, *bufsize, "%c%c",
((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_get_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
{
ipmgmt_aobjop_arg_t larg;
ipmgmt_aobjop_rval_t rval, *rvalp;
int err = 0;
larg.ia_cmd = IPMGMT_CMD_AOBJNAME2ADDROBJ;
(void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
sizeof (larg.ia_aobjname));
rvalp = &rval;
err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
sizeof (rval), B_FALSE);
if (err != 0)
return (ipadm_errno2status(err));
(void) strlcpy(ipaddr->ipadm_ifname, rval.ir_ifname,
sizeof (ipaddr->ipadm_ifname));
ipaddr->ipadm_lifnum = rval.ir_lnum;
ipaddr->ipadm_atype = rval.ir_atype;
ipaddr->ipadm_af = rval.ir_family;
ipaddr->ipadm_flags = rval.ir_flags;
switch (rval.ir_atype) {
case IPADM_ADDR_IPV6_ADDRCONF:
ipaddr->ipadm_intfid = rval.ipmgmt_ir_intfid;
break;
case IPADM_ADDR_DHCP:
if (strlcpy(ipaddr->ipadm_reqhost, rval.ipmgmt_ir_reqhost,
sizeof (ipaddr->ipadm_reqhost)) >=
sizeof (ipaddr->ipadm_reqhost)) {
return (IPADM_FAILURE);
}
break;
default:
break;
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_static_addr_db(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
{
ipadm_status_t status;
nvlist_t *onvl;
nvlist_t *anvl = NULL;
nvlist_t *nvladdr;
nvpair_t *nvp;
char *name;
char *aobjname = ipaddr->ipadm_aobjname;
char *sname;
sa_family_t af = AF_UNSPEC;
status = i_ipadm_get_db_addr(iph, NULL, aobjname, &onvl);
if (status != IPADM_SUCCESS)
return (status);
for (nvp = nvlist_next_nvpair(onvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(onvl, NULL)) {
if (nvpair_value_nvlist(nvp, &anvl) != 0)
continue;
if (nvlist_exists(anvl, IPADM_NVP_IPV4ADDR) ||
nvlist_exists(anvl, IPADM_NVP_IPV6ADDR))
break;
}
nvlist_free(onvl);
if (nvp == NULL)
return (IPADM_NOTFOUND);
for (nvp = nvlist_next_nvpair(anvl, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(anvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0) {
af = AF_INET;
break;
} else if (strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
af = AF_INET6;
break;
}
}
assert(af != AF_UNSPEC);
if (nvpair_value_nvlist(nvp, &nvladdr) != 0 ||
nvlist_lookup_string(nvladdr, IPADM_NVP_IPADDRHNAME, &sname) != 0 ||
ipadm_set_addr(ipaddr, sname, af) != IPADM_SUCCESS)
return (IPADM_NOTFOUND);
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_get_lif2addrobj(ipadm_handle_t iph, ipadm_addrobj_t addrobj)
{
ipmgmt_aobjop_arg_t larg;
ipmgmt_aobjop_rval_t rval, *rvalp;
int err;
larg.ia_cmd = IPMGMT_CMD_LIF2ADDROBJ;
(void) strlcpy(larg.ia_ifname, addrobj->ipadm_ifname,
sizeof (larg.ia_ifname));
larg.ia_lnum = addrobj->ipadm_lifnum;
larg.ia_family = addrobj->ipadm_af;
rvalp = &rval;
err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
sizeof (rval), B_FALSE);
if (err != 0)
return (ipadm_errno2status(err));
(void) strlcpy(addrobj->ipadm_aobjname, rval.ir_aobjname,
sizeof (addrobj->ipadm_aobjname));
addrobj->ipadm_atype = rval.ir_atype;
addrobj->ipadm_flags = rval.ir_flags;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_add_aobjname(ipadm_handle_t iph, const char *ifname, sa_family_t af,
const char *aobjname, ipadm_addr_type_t atype, int lnum)
{
ipmgmt_aobjop_arg_t larg;
int err;
larg.ia_cmd = IPMGMT_CMD_ADDROBJ_ADD;
(void) strlcpy(larg.ia_ifname, ifname, sizeof (larg.ia_ifname));
(void) strlcpy(larg.ia_aobjname, aobjname, sizeof (larg.ia_aobjname));
larg.ia_atype = atype;
larg.ia_lnum = lnum;
larg.ia_family = af;
err = ipadm_door_call(iph, &larg, sizeof (larg), NULL, 0, B_FALSE);
return (ipadm_errno2status(err));
}
ipadm_status_t
ipadm_delete_aobjname(ipadm_handle_t iph, const char *ifname, sa_family_t af,
const char *aobjname, ipadm_addr_type_t atype, int lnum)
{
struct ipadm_addrobj_s aobj;
i_ipadm_init_addr(&aobj, ifname, aobjname, atype);
aobj.ipadm_af = af;
aobj.ipadm_lifnum = lnum;
return (i_ipadm_delete_addrobj(iph, &aobj, IPADM_OPT_ACTIVE));
}
ipadm_status_t
i_ipadm_active_addr_info(ipadm_handle_t iph, const char *ifname,
ipadm_addr_info_t **addrinfo, uint32_t ipadm_flags, int64_t lifc_flags)
{
ipadm_status_t status;
struct ifaddrs *ifap, *ifa;
ipadm_addr_info_t *curr, *prev = NULL;
struct ifaddrs *cifaddr;
struct lifreq lifr;
int sock;
uint64_t flags;
char cifname[LIFNAMSIZ];
struct sockaddr_in6 *sin6;
struct ipadm_addrobj_s ipaddr;
char *sep;
int lnum;
retry:
*addrinfo = NULL;
if (getallifaddrs(AF_UNSPEC, &ifa, lifc_flags) < 0)
return (ipadm_errno2status(errno));
if (ifa == NULL)
return (IPADM_SUCCESS);
bzero(&lifr, sizeof (lifr));
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
struct sockaddr_storage data;
if (ifap->ifa_addr->sa_family == AF_LINK)
continue;
(void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
lnum = 0;
if ((sep = strrchr(cifname, ':')) != NULL) {
*sep++ = '\0';
lnum = atoi(sep);
}
if (ifname != NULL && strcmp(cifname, ifname) != 0)
continue;
if (!(ipadm_flags & IPADM_OPT_ZEROADDR) &&
sockaddrunspec(ifap->ifa_addr) &&
!(ifap->ifa_flags & IFF_DHCPRUNNING))
continue;
if ((curr = calloc(1, sizeof (ipadm_addr_info_t))) == NULL)
goto fail;
if (prev != NULL)
prev->ia_ifa.ifa_next = &curr->ia_ifa;
else
*addrinfo = curr;
prev = curr;
cifaddr = &curr->ia_ifa;
if ((cifaddr->ifa_name = strdup(ifap->ifa_name)) == NULL)
goto fail;
cifaddr->ifa_flags = ifap->ifa_flags;
cifaddr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
if (cifaddr->ifa_addr == NULL)
goto fail;
(void) memcpy(cifaddr->ifa_addr, ifap->ifa_addr,
sizeof (struct sockaddr_storage));
cifaddr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
if (cifaddr->ifa_netmask == NULL)
goto fail;
(void) memcpy(cifaddr->ifa_netmask, ifap->ifa_netmask,
sizeof (struct sockaddr_storage));
if (ifap->ifa_flags & IFF_POINTOPOINT) {
cifaddr->ifa_dstaddr = malloc(
sizeof (struct sockaddr_storage));
if (cifaddr->ifa_dstaddr == NULL)
goto fail;
(void) memcpy(cifaddr->ifa_dstaddr, ifap->ifa_dstaddr,
sizeof (struct sockaddr_storage));
} else if (ifap->ifa_flags & IFF_BROADCAST) {
cifaddr->ifa_broadaddr = malloc(
sizeof (struct sockaddr_storage));
if (cifaddr->ifa_broadaddr == NULL)
goto fail;
(void) memcpy(cifaddr->ifa_broadaddr,
ifap->ifa_broadaddr,
sizeof (struct sockaddr_storage));
}
ipaddr.ipadm_aobjname[0] = '\0';
(void) strlcpy(ipaddr.ipadm_ifname, cifname,
sizeof (ipaddr.ipadm_ifname));
ipaddr.ipadm_lifnum = lnum;
ipaddr.ipadm_af = ifap->ifa_addr->sa_family;
status = i_ipadm_get_lif2addrobj(iph, &ipaddr);
(void) memcpy(&data, ifap->ifa_addr,
sizeof (struct sockaddr_in6));
sin6 = SIN6(&data);
flags = ifap->ifa_flags;
if (status == IPADM_SUCCESS) {
(void) strlcpy(curr->ia_aobjname, ipaddr.ipadm_aobjname,
sizeof (curr->ia_aobjname));
curr->ia_atype = ipaddr.ipadm_atype;
} else if ((flags & IFF_DHCPRUNNING) && (!(flags & IFF_IPV6) ||
!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
curr->ia_atype = IPADM_ADDR_DHCP;
} else if (flags & IFF_ADDRCONF) {
curr->ia_atype = IPADM_ADDR_IPV6_ADDRCONF;
} else {
curr->ia_atype = IPADM_ADDR_STATIC;
}
if (!(flags & IFF_UP)) {
if (flags & IFF_DUPLICATE)
curr->ia_state = IFA_DUPLICATE;
else
curr->ia_state = IFA_DOWN;
} else {
curr->ia_cflags |= IA_UP;
if (flags & IFF_RUNNING) {
(void) strlcpy(lifr.lifr_name, ifap->ifa_name,
sizeof (lifr.lifr_name));
sock = (ifap->ifa_addr->sa_family == AF_INET) ?
iph->iph_sock : iph->iph_sock6;
if (ioctl(sock, SIOCGLIFDADSTATE,
(caddr_t)&lifr) < 0) {
if (errno == ENXIO) {
freeifaddrs(ifa);
ipadm_free_addr_info(*addrinfo);
goto retry;
}
goto fail;
}
if (lifr.lifr_dadstate == DAD_IN_PROGRESS)
curr->ia_state = IFA_TENTATIVE;
else
curr->ia_state = IFA_OK;
} else {
curr->ia_state = IFA_INACCESSIBLE;
}
}
if (flags & IFF_UNNUMBERED)
curr->ia_cflags |= IA_UNNUMBERED;
if (flags & IFF_PRIVATE)
curr->ia_cflags |= IA_PRIVATE;
if (flags & IFF_TEMPORARY)
curr->ia_cflags |= IA_TEMPORARY;
if (flags & IFF_DEPRECATED)
curr->ia_cflags |= IA_DEPRECATED;
}
freeifaddrs(ifa);
return (IPADM_SUCCESS);
fail:
ipadm_free_addr_info(*addrinfo);
*addrinfo = NULL;
freeifaddrs(ifa);
return (ipadm_errno2status(errno));
}
boolean_t
i_ipadm_name2atype(const char *name, sa_family_t *af, ipadm_addr_type_t *type)
{
boolean_t is_addr = B_TRUE;
if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0) {
*af = AF_INET;
*type = IPADM_ADDR_STATIC;
} else if (strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
*af = AF_INET6;
*type = IPADM_ADDR_STATIC;
} else if (strcmp(name, IPADM_NVP_DHCP) == 0) {
*af = AF_INET;
*type = IPADM_ADDR_DHCP;
} else if (strcmp(name, IPADM_NVP_INTFID) == 0) {
*af = AF_INET6;
*type = IPADM_ADDR_IPV6_ADDRCONF;
} else {
is_addr = B_FALSE;
}
return (is_addr);
}
static ipadm_status_t
i_ipadm_nvl2ainfo_common(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
{
nvlist_t *nvladdr;
char *name;
char *propstr = NULL;
char *sname, *dname;
nvpair_t *nvp;
sa_family_t af;
ipadm_addr_type_t atype;
boolean_t is_addr = B_FALSE;
int err;
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
name = nvpair_name(nvp);
if (i_ipadm_name2atype(name, &af, &atype)) {
err = nvpair_value_nvlist(nvp, &nvladdr);
is_addr = B_TRUE;
} else if (IPADM_PRIV_NVP(name)) {
continue;
} else {
err = nvpair_value_string(nvp, &propstr);
}
if (err != 0)
return (ipadm_errno2status(err));
}
if (is_addr) {
switch (atype) {
case IPADM_ADDR_STATIC:
if (strcmp(name, "up") == 0 &&
strcmp(propstr, "yes") == 0) {
ainfo->ia_pflags |= IA_UP;
}
err = nvlist_lookup_string(nvladdr,
IPADM_NVP_IPADDRHNAME, &sname);
if (err != 0)
return (ipadm_errno2status(err));
(void) strlcpy(ainfo->ia_sname, sname,
sizeof (ainfo->ia_sname));
err = nvlist_lookup_string(nvladdr,
IPADM_NVP_IPDADDRHNAME, &dname);
if (err == 0) {
(void) strlcpy(ainfo->ia_dname, dname,
sizeof (ainfo->ia_dname));
}
break;
case IPADM_ADDR_DHCP:
case IPADM_ADDR_IPV6_ADDRCONF:
ainfo->ia_pflags |= IA_UP;
break;
default:
return (IPADM_FAILURE);
}
} else {
if (strcmp(name, "deprecated") == 0) {
if (strcmp(propstr, IPADM_ONSTR) == 0)
ainfo->ia_pflags |= IA_DEPRECATED;
} else if (strcmp(name, "private") == 0) {
if (strcmp(propstr, IPADM_ONSTR) == 0)
ainfo->ia_pflags |= IA_PRIVATE;
}
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_nvl2ainfo_active(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
{
return (i_ipadm_nvl2ainfo_common(nvl, ainfo));
}
static ipadm_status_t
i_ipadm_nvl2ainfo_persist(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
{
nvlist_t *nvladdr;
struct ifaddrs *ifa;
char *name;
char *ifname = NULL;
char *aobjname = NULL;
char *propstr = NULL;
nvpair_t *nvp;
sa_family_t af;
ipadm_addr_type_t atype;
boolean_t is_addr = B_FALSE;
size_t size = sizeof (struct sockaddr_storage);
uint32_t plen = 0;
int err;
ipadm_status_t status;
status = i_ipadm_nvl2ainfo_common(nvl, ainfo);
if (status != IPADM_SUCCESS)
return (status);
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_IFNAME) == 0) {
err = nvpair_value_string(nvp, &ifname);
} else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) {
err = nvpair_value_string(nvp, &aobjname);
} else if (i_ipadm_name2atype(name, &af, &atype)) {
err = nvpair_value_nvlist(nvp, &nvladdr);
is_addr = B_TRUE;
} else {
err = nvpair_value_string(nvp, &propstr);
}
if (err != 0)
return (ipadm_errno2status(err));
}
ifa = &ainfo->ia_ifa;
(void) strlcpy(ainfo->ia_aobjname, aobjname,
sizeof (ainfo->ia_aobjname));
if (ifa->ifa_name == NULL && (ifa->ifa_name = strdup(ifname)) == NULL)
return (IPADM_NO_MEMORY);
if (is_addr) {
struct sockaddr_in6 data;
ainfo->ia_atype = atype;
if ((ifa->ifa_addr = calloc(1, size)) == NULL)
return (IPADM_NO_MEMORY);
switch (atype) {
case IPADM_ADDR_STATIC:
ifa->ifa_addr->sa_family = af;
break;
case IPADM_ADDR_DHCP:
ifa->ifa_addr->sa_family = AF_INET;
break;
case IPADM_ADDR_IPV6_ADDRCONF:
data.sin6_family = AF_INET6;
if (i_ipadm_nvl2in6_addr(nvladdr, IPADM_NVP_IPNUMADDR,
&data.sin6_addr) != IPADM_SUCCESS)
return (IPADM_NO_MEMORY);
err = nvlist_lookup_uint32(nvladdr, IPADM_NVP_PREFIXLEN,
&plen);
if (err != 0)
return (ipadm_errno2status(err));
if ((ifa->ifa_netmask = malloc(size)) == NULL)
return (IPADM_NO_MEMORY);
if ((err = plen2mask(plen, af, ifa->ifa_netmask)) != 0)
return (ipadm_errno2status(err));
(void) memcpy(ifa->ifa_addr, &data, sizeof (data));
break;
default:
return (IPADM_FAILURE);
}
} else {
if (strcmp(name, "prefixlen") == 0) {
if ((ifa->ifa_netmask = malloc(size)) == NULL)
return (IPADM_NO_MEMORY);
assert(ifa->ifa_addr != NULL);
err = plen2mask(atoi(propstr), ifa->ifa_addr->sa_family,
ifa->ifa_netmask);
if (err != 0)
return (ipadm_errno2status(err));
}
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_all_addr_info(ipadm_handle_t iph, const char *ifname,
ipadm_addr_info_t **addrinfo, uint32_t ipadm_flags, int64_t lifc_flags)
{
nvlist_t *nvladdr = NULL;
nvlist_t *onvl = NULL;
nvpair_t *nvp;
ipadm_status_t status;
ipadm_addr_info_t *ainfo = NULL;
ipadm_addr_info_t *curr;
ipadm_addr_info_t *last = NULL;
char *aobjname;
status = i_ipadm_active_addr_info(iph, ifname, &ainfo, ipadm_flags,
lifc_flags);
if (status != IPADM_SUCCESS)
goto fail;
status = i_ipadm_get_db_addr(iph, ifname, NULL, &onvl);
if (status == IPADM_NOTFOUND) {
if (ainfo == NULL && ifname != NULL)
return (IPADM_ENXIO);
*addrinfo = ainfo;
return (IPADM_SUCCESS);
}
if (status != IPADM_SUCCESS)
goto fail;
if (ainfo != NULL) {
for (curr = ainfo; IA_NEXT(curr) != NULL; curr = IA_NEXT(curr))
;
last = curr;
}
for (nvp = nvlist_next_nvpair(onvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(onvl, nvp)) {
if (nvpair_value_nvlist(nvp, &nvladdr) != 0)
continue;
if (nvlist_lookup_string(nvladdr, IPADM_NVP_AOBJNAME,
&aobjname) != 0)
continue;
for (curr = ainfo; curr != NULL; curr = IA_NEXT(curr)) {
if (strcmp(curr->ia_aobjname, aobjname) == 0)
break;
}
if (curr == NULL) {
curr = calloc(1, sizeof (ipadm_addr_info_t));
if (curr == NULL)
goto fail;
curr->ia_state = IFA_DISABLED;
if (last != NULL)
last->ia_ifa.ifa_next = &curr->ia_ifa;
else
ainfo = curr;
last = curr;
}
if (curr->ia_state == IFA_DISABLED)
status = i_ipadm_nvl2ainfo_persist(nvladdr, curr);
else
status = i_ipadm_nvl2ainfo_active(nvladdr, curr);
if (status != IPADM_SUCCESS)
goto fail;
}
*addrinfo = ainfo;
nvlist_free(onvl);
return (status);
fail:
nvlist_free(onvl);
ipadm_free_addr_info(ainfo);
*addrinfo = NULL;
return (status);
}
static ipadm_status_t
i_ipadm_set_prefixlen(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
{
struct sockaddr_storage netmask;
struct lifreq lifr;
int err, s;
unsigned long prefixlen, abits;
char *end;
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
return (IPADM_NOTSUP);
errno = 0;
prefixlen = strtoul(pval, &end, 10);
if (errno != 0 || *end != '\0')
return (IPADM_INVALID_ARG);
abits = (af == AF_INET ? IP_ABITS : IPV6_ABITS);
if (prefixlen == 0 || prefixlen == (abits - 1))
return (IPADM_INVALID_ARG);
if ((err = plen2mask(prefixlen, af, (struct sockaddr *)&netmask)) != 0)
return (ipadm_errno2status(err));
s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
bzero(&lifr, sizeof (lifr));
i_ipadm_addrobj2lifname(ipaddr, lifr.lifr_name,
sizeof (lifr.lifr_name));
(void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask));
if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
if (af == AF_INET) {
(void) ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr);
(void) ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_set_addr_flag(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
{
char lifname[LIFNAMSIZ];
uint64_t on_flags = 0, off_flags = 0;
boolean_t on;
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP &&
strcmp(pdp->ipd_name, "deprecated") == 0)
return (IPADM_NOTSUP);
if (strcmp(pval, IPADM_ONSTR) == 0)
on = B_TRUE;
else if (strcmp(pval, IPADM_OFFSTR) == 0)
on = B_FALSE;
else
return (IPADM_INVALID_ARG);
if (strcmp(pdp->ipd_name, "private") == 0) {
if (on)
on_flags = IFF_PRIVATE;
else
off_flags = IFF_PRIVATE;
} else if (strcmp(pdp->ipd_name, "transmit") == 0) {
if (on)
off_flags = IFF_NOXMIT;
else
on_flags = IFF_NOXMIT;
} else if (strcmp(pdp->ipd_name, "deprecated") == 0) {
if (on)
on_flags = IFF_DEPRECATED;
else
off_flags = IFF_DEPRECATED;
} else {
return (IPADM_PROP_UNKNOWN);
}
i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
return (i_ipadm_set_flags(iph, lifname, af, on_flags, off_flags));
}
static ipadm_status_t
i_ipadm_set_zone(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
{
struct lifreq lifr;
zoneid_t zoneid;
int s;
if (flags & IPADM_OPT_PERSIST) {
return (IPADM_NOTSUP);
} else if (flags & IPADM_OPT_ACTIVE) {
if (strcmp(pval, "all-zones") == 0) {
zoneid = ALL_ZONES;
} else {
if ((zoneid = getzoneidbyname(pval)) == -1)
return (ipadm_errno2status(errno));
}
} else {
return (IPADM_INVALID_ARG);
}
s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
bzero(&lifr, sizeof (lifr));
i_ipadm_addrobj2lifname((ipadm_addrobj_t)arg, lifr.lifr_name,
sizeof (lifr.lifr_name));
lifr.lifr_zoneid = zoneid;
if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_set_reqhost(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
{
ipadm_status_t status;
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
if (ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
return (IPADM_NOTSUP);
if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE) &&
(flags & IPADM_OPT_ACTIVE) && !(flags & IPADM_OPT_PERSIST)) {
return (IPADM_NOTFOUND);
}
status = ipadm_set_reqhost(ipaddr, pval);
if (status != IPADM_SUCCESS)
return (status);
if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
status = i_ipadm_refresh_dhcp(ipaddr);
if (status != IPADM_SUCCESS && status != IPADM_DHCP_IPC_TIMEOUT)
return (status);
}
status = i_ipadm_set_aobj_addrprop(iph, ipaddr, flags,
IPADM_NVP_REQHOST);
return (status);
}
static ipadm_status_t
i_ipadm_set_aobj_addrprop(ipadm_handle_t iph, ipadm_addrobj_t ipaddr,
uint_t flags, const char *propname)
{
ipadm_status_t status;
uint32_t two_stage_flags;
two_stage_flags = (flags | IPADM_OPT_SET_PROPS)
& ~(IPADM_OPT_ACTIVE | IPADM_OPT_PERSIST);
if (ipaddr->ipadm_flags & IPMGMT_ACTIVE)
two_stage_flags |= IPADM_OPT_ACTIVE;
if (ipaddr->ipadm_flags & IPMGMT_PERSIST)
two_stage_flags |= IPADM_OPT_PERSIST;
if (flags & IPADM_OPT_PERSIST)
two_stage_flags |= IPADM_OPT_PERSIST_PROPS;
status = i_ipadm_addr_persist(iph, ipaddr, B_FALSE, two_stage_flags,
propname);
return (status);
}
static ipadm_status_t
i_ipadm_get_broadcast(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
struct sockaddr_in *sin;
struct lifreq lifr;
char lifname[LIFNAMSIZ];
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
ipadm_status_t status;
size_t nbytes = 0;
uint64_t ifflags = 0;
i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
status = i_ipadm_get_flags(iph, lifname, af, &ifflags);
if (status != IPADM_SUCCESS)
return (status);
if (!(ifflags & IFF_BROADCAST)) {
buf[0] = '\0';
return (IPADM_SUCCESS);
}
}
switch (valtype) {
case MOD_PROP_DEFAULT: {
struct sockaddr_storage mask;
struct in_addr broadaddr;
uint_t plen;
in_addr_t addr, maddr;
char val[MAXPROPVALLEN];
uint_t valsz = MAXPROPVALLEN;
ipadm_status_t status;
int err;
struct sockaddr_in *sin;
if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE)) {
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP ||
ipaddr->ipadm_af == AF_INET6) {
buf[0] = '\0';
return (IPADM_SUCCESS);
}
status = i_ipadm_get_static_addr_db(iph, ipaddr);
if (status != IPADM_SUCCESS)
return (status);
sin = SIN(&ipaddr->ipadm_static_addr);
addr = sin->sin_addr.s_addr;
} else {
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, lifname,
sizeof (lifr.lifr_name));
if (ioctl(iph->iph_sock, SIOCGLIFADDR,
(caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
addr = (SIN(&lifr.lifr_addr))->sin_addr.s_addr;
}
status = i_ipadm_get_prefixlen(iph, arg, NULL, val, &valsz, af,
MOD_PROP_DEFAULT);
if (status != IPADM_SUCCESS)
return (status);
plen = atoi(val);
if ((err = plen2mask(plen, AF_INET,
(struct sockaddr *)&mask)) != 0)
return (ipadm_errno2status(err));
maddr = (SIN(&mask))->sin_addr.s_addr;
broadaddr.s_addr = (addr & maddr) | ~maddr;
nbytes = snprintf(buf, *bufsize, "%s", inet_ntoa(broadaddr));
break;
}
case MOD_PROP_ACTIVE:
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, lifname,
sizeof (lifr.lifr_name));
if (ioctl(iph->iph_sock, SIOCGLIFBRDADDR,
(caddr_t)&lifr) < 0) {
return (ipadm_errno2status(errno));
} else {
sin = SIN(&lifr.lifr_addr);
nbytes = snprintf(buf, *bufsize, "%s",
inet_ntoa(sin->sin_addr));
}
break;
default:
return (IPADM_INVALID_ARG);
}
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_prefixlen(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
struct lifreq lifr;
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
char lifname[LIFNAMSIZ];
int s;
uint32_t prefixlen;
size_t nbytes;
ipadm_status_t status;
uint64_t lifflags;
i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
status = i_ipadm_get_flags(iph, lifname, af, &lifflags);
if (status != IPADM_SUCCESS) {
return (status);
} else if (lifflags & IFF_POINTOPOINT) {
buf[0] = '\0';
return (status);
}
}
s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name));
switch (valtype) {
case MOD_PROP_POSSIBLE:
if (af == AF_INET)
nbytes = snprintf(buf, *bufsize, "1-30,32");
else
nbytes = snprintf(buf, *bufsize, "1-126,128");
break;
case MOD_PROP_DEFAULT:
if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
status = i_ipadm_get_default_prefixlen(
&lifr.lifr_addr, &prefixlen);
if (status != IPADM_SUCCESS)
return (status);
} else if ((ipaddr->ipadm_flags & IPMGMT_PERSIST) &&
ipaddr->ipadm_atype == IPADM_ADDR_DHCP) {
buf[0] = '\0';
return (IPADM_SUCCESS);
} else {
status = i_ipadm_get_static_addr_db(iph, ipaddr);
if (status != IPADM_SUCCESS)
return (status);
status = i_ipadm_get_default_prefixlen(
&ipaddr->ipadm_static_addr, &prefixlen);
if (status != IPADM_SUCCESS)
return (status);
}
nbytes = snprintf(buf, *bufsize, "%u", prefixlen);
break;
case MOD_PROP_ACTIVE:
if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
prefixlen = lifr.lifr_addrlen;
nbytes = snprintf(buf, *bufsize, "%u", prefixlen);
break;
default:
return (IPADM_INVALID_ARG);
}
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_addr_flag(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
boolean_t on = B_FALSE;
char lifname[LIFNAMSIZ];
ipadm_status_t status = IPADM_SUCCESS;
uint64_t ifflags;
size_t nbytes;
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
switch (valtype) {
case MOD_PROP_DEFAULT:
if (strcmp(pdp->ipd_name, "private") == 0 ||
strcmp(pdp->ipd_name, "deprecated") == 0) {
on = B_FALSE;
} else if (strcmp(pdp->ipd_name, "transmit") == 0) {
on = B_TRUE;
} else {
return (IPADM_PROP_UNKNOWN);
}
break;
case MOD_PROP_ACTIVE:
i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
status = i_ipadm_get_flags(iph, lifname, af, &ifflags);
if (status != IPADM_SUCCESS)
return (status);
if (strcmp(pdp->ipd_name, "private") == 0)
on = (ifflags & IFF_PRIVATE);
else if (strcmp(pdp->ipd_name, "transmit") == 0)
on = !(ifflags & IFF_NOXMIT);
else if (strcmp(pdp->ipd_name, "deprecated") == 0)
on = (ifflags & IFF_DEPRECATED);
break;
default:
return (IPADM_INVALID_ARG);
}
nbytes = snprintf(buf, *bufsize, "%s",
(on ? IPADM_ONSTR : IPADM_OFFSTR));
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
status = IPADM_NO_BUFS;
}
return (status);
}
static ipadm_status_t
i_ipadm_get_zone(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
struct lifreq lifr;
char zone_name[ZONENAME_MAX];
int s;
size_t nbytes = 0;
if (iph->iph_zoneid != GLOBAL_ZONEID) {
buf[0] = '\0';
return (IPADM_SUCCESS);
}
switch (valtype) {
case MOD_PROP_DEFAULT:
if (getzonenamebyid(GLOBAL_ZONEID, zone_name,
sizeof (zone_name)) > 0)
nbytes = snprintf(buf, *bufsize, "%s", zone_name);
else
return (ipadm_errno2status(errno));
break;
case MOD_PROP_ACTIVE:
bzero(&lifr, sizeof (lifr));
i_ipadm_addrobj2lifname((ipadm_addrobj_t)arg, lifr.lifr_name,
sizeof (lifr.lifr_name));
s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) == -1)
return (ipadm_errno2status(errno));
if (lifr.lifr_zoneid == ALL_ZONES) {
nbytes = snprintf(buf, *bufsize, "%s", "all-zones");
} else if (getzonenamebyid(lifr.lifr_zoneid, zone_name,
sizeof (zone_name)) < 0) {
return (ipadm_errno2status(errno));
} else {
nbytes = snprintf(buf, *bufsize, "%s", zone_name);
}
break;
default:
return (IPADM_INVALID_ARG);
}
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_primary(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
const char *onoff = "";
size_t nbytes;
switch (valtype) {
case MOD_PROP_DEFAULT:
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
onoff = IPADM_OFFSTR;
break;
case MOD_PROP_ACTIVE:
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP) {
dhcp_status_t dhcp_status;
ipadm_status_t ipc_status;
int error;
ipc_status = i_ipadm_dhcp_status(ipaddr, &dhcp_status,
&error);
if (ipc_status != IPADM_SUCCESS &&
ipc_status != IPADM_NOTFOUND)
return (ipc_status);
onoff = dhcp_status.if_dflags & DHCP_IF_PRIMARY ?
IPADM_ONSTR : IPADM_OFFSTR;
}
break;
default:
return (IPADM_INVALID_ARG);
}
nbytes = strlcpy(buf, onoff, *bufsize);
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_reqhost(ipadm_handle_t iph, const void *arg,
ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
uint_t valtype)
{
ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
const char *reqhost = "";
size_t nbytes;
switch (valtype) {
case MOD_PROP_DEFAULT:
break;
case MOD_PROP_ACTIVE:
if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
reqhost = ipaddr->ipadm_reqhost;
break;
default:
return (IPADM_INVALID_ARG);
}
nbytes = strlcpy(buf, reqhost, *bufsize);
if (nbytes >= *bufsize) {
*bufsize = nbytes + 1;
return (IPADM_NO_BUFS);
}
return (IPADM_SUCCESS);
}
static ipadm_prop_desc_t *
i_ipadm_get_addrprop_desc(const char *pname)
{
int i;
for (i = 0; ipadm_addrprop_table[i].ipd_name != NULL; i++) {
if (strcmp(pname, ipadm_addrprop_table[i].ipd_name) == 0 ||
(ipadm_addrprop_table[i].ipd_old_name != NULL &&
strcmp(pname, ipadm_addrprop_table[i].ipd_old_name) == 0))
return (&ipadm_addrprop_table[i]);
}
return (NULL);
}
ipadm_status_t
ipadm_get_addrprop(ipadm_handle_t iph, const char *pname, char *buf,
uint_t *bufsize, const char *aobjname, uint_t valtype)
{
struct ipadm_addrobj_s ipaddr;
ipadm_status_t status = IPADM_SUCCESS;
sa_family_t af;
ipadm_prop_desc_t *pdp = NULL;
if (iph == NULL || pname == NULL || buf == NULL ||
bufsize == NULL || *bufsize == 0 || aobjname == NULL) {
return (IPADM_INVALID_ARG);
}
if ((pdp = i_ipadm_get_addrprop_desc(pname)) == NULL)
return (IPADM_PROP_UNKNOWN);
i_ipadm_init_addr(&ipaddr, "", aobjname, IPADM_ADDR_NONE);
if ((status = i_ipadm_get_addrobj(iph, &ipaddr)) != IPADM_SUCCESS)
return (status);
if (ipaddr.ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF)
return (IPADM_NOTSUP);
af = ipaddr.ipadm_af;
switch (valtype) {
case IPADM_OPT_PERM:
status = i_ipadm_pd2permstr(pdp, buf, bufsize);
break;
case IPADM_OPT_ACTIVE:
if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE)) {
buf[0] = '\0';
} else {
status = pdp->ipd_get(iph, &ipaddr, pdp, buf, bufsize,
af, MOD_PROP_ACTIVE);
}
break;
case IPADM_OPT_DEFAULT:
status = pdp->ipd_get(iph, &ipaddr, pdp, buf, bufsize,
af, MOD_PROP_DEFAULT);
break;
case IPADM_OPT_POSSIBLE:
if (pdp->ipd_get_range != NULL) {
status = pdp->ipd_get_range(iph, &ipaddr, pdp, buf,
bufsize, af, MOD_PROP_POSSIBLE);
break;
}
buf[0] = '\0';
break;
case IPADM_OPT_PERSIST:
status = i_ipadm_get_persist_propval(iph, pdp, buf, bufsize,
&ipaddr);
break;
default:
status = IPADM_INVALID_ARG;
break;
}
return (status);
}
ipadm_status_t
ipadm_set_addrprop(ipadm_handle_t iph, const char *pname,
const char *pval, const char *aobjname, uint_t pflags)
{
struct ipadm_addrobj_s ipaddr;
sa_family_t af;
ipadm_prop_desc_t *pdp = NULL;
char defbuf[MAXPROPVALLEN];
uint_t defbufsize = MAXPROPVALLEN;
boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
ipadm_status_t status = IPADM_SUCCESS;
if (!ipadm_check_auth())
return (IPADM_EAUTH);
if (iph == NULL || pname == NULL || aobjname == NULL || pflags == 0 ||
pflags == IPADM_OPT_PERSIST ||
(pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT)) ||
(!reset && pval == NULL)) {
return (IPADM_INVALID_ARG);
}
if ((pdp = i_ipadm_get_addrprop_desc(pname)) == NULL)
return (IPADM_PROP_UNKNOWN);
if (pdp->ipd_set == NULL || (reset && pdp->ipd_get == NULL))
return (IPADM_NOTSUP);
if (!(pdp->ipd_flags & IPADMPROP_MULVAL) &&
(pflags & (IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
return (IPADM_INVALID_ARG);
}
i_ipadm_init_addr(&ipaddr, "", aobjname, IPADM_ADDR_NONE);
if ((status = i_ipadm_get_addrobj(iph, &ipaddr)) != IPADM_SUCCESS)
return (status);
if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
return (IPADM_OP_DISABLE_OBJ);
if ((pflags & IPADM_OPT_PERSIST) &&
!(ipaddr.ipadm_flags & IPMGMT_PERSIST))
return (IPADM_TEMPORARY_OBJ);
if (ipaddr.ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF)
return (IPADM_NOTSUP);
if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
return (IPADM_NOTSUP);
af = ipaddr.ipadm_af;
if (reset) {
status = pdp->ipd_get(iph, &ipaddr, pdp, defbuf, &defbufsize,
af, MOD_PROP_DEFAULT);
if (status != IPADM_SUCCESS)
return (status);
pval = defbuf;
}
status = pdp->ipd_set(iph, &ipaddr, pdp, pval, af, pflags);
if (status != IPADM_SUCCESS)
return (status);
if (pflags & IPADM_OPT_PERSIST) {
status = i_ipadm_persist_propval(iph, pdp, pval, &ipaddr,
pflags);
}
return (status);
}
ipadm_status_t
i_ipadm_delete_addr(ipadm_handle_t iph, ipadm_addrobj_t addr)
{
struct lifreq lifr;
int sock;
ipadm_status_t status;
bzero(&lifr, sizeof (lifr));
i_ipadm_addrobj2lifname(addr, lifr.lifr_name, sizeof (lifr.lifr_name));
sock = (addr->ipadm_af == AF_INET ? iph->iph_sock : iph->iph_sock6);
if (addr->ipadm_lifnum == 0) {
status = i_ipadm_set_flags(iph, addr->ipadm_ifname,
addr->ipadm_af, 0, IFF_UP);
if (status != IPADM_SUCCESS)
return (status);
bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
lifr.lifr_addr.ss_family = addr->ipadm_af;
if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
if (ioctl(sock, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
} else if (ioctl(sock, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_nvl2in6_addr(nvlist_t *nvl, char *addr_type, in6_addr_t *in6_addr)
{
uint8_t *addr6;
uint_t n;
if (nvlist_lookup_uint8_array(nvl, addr_type, &addr6, &n) != 0)
return (IPADM_NOTFOUND);
assert(n == 16);
bcopy(addr6, in6_addr->s6_addr, n);
return (IPADM_SUCCESS);
}
static boolean_t
i_ipadm_is_user_aobjname_valid(const char *aobjname)
{
const char *cp;
if (aobjname == NULL || strlen(aobjname) >= IPADM_AOBJ_USTRSIZ ||
!isalpha(*aobjname)) {
return (B_FALSE);
}
for (cp = aobjname + 1; *cp && isalnum(*cp); cp++)
;
return (*cp == '\0');
}
static ipadm_status_t
i_ipadm_get_default_prefixlen(struct sockaddr_storage *addr, uint32_t *plen)
{
sa_family_t af = addr->ss_family;
struct sockaddr_storage mask;
struct sockaddr_in *m = (struct sockaddr_in *)&mask;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
struct in_addr ia;
uint32_t prefixlen = 0;
switch (af) {
case AF_INET:
sin = SIN(addr);
ia.s_addr = ntohl(sin->sin_addr.s_addr);
get_netmask4(&ia, &m->sin_addr);
m->sin_addr.s_addr = htonl(m->sin_addr.s_addr);
m->sin_family = AF_INET;
prefixlen = mask2plen((struct sockaddr *)&mask);
break;
case AF_INET6:
sin6 = SIN6(addr);
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
prefixlen = 10;
else
prefixlen = 64;
break;
default:
return (IPADM_INVALID_ARG);
}
*plen = prefixlen;
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_resolve_addr(const char *name, sa_family_t af,
struct sockaddr_storage *ss)
{
struct addrinfo hints, *ai;
int rc;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
boolean_t is_mapped;
(void) memset(&hints, 0, sizeof (hints));
hints.ai_family = af;
hints.ai_flags = (AI_ALL | AI_V4MAPPED);
rc = getaddrinfo(name, NULL, &hints, &ai);
if (rc != 0) {
if (rc == EAI_NONAME)
return (IPADM_BAD_ADDR);
else
return (IPADM_FAILURE);
}
if (ai->ai_next != NULL) {
freeaddrinfo(ai);
return (IPADM_BAD_HOSTNAME);
}
is_mapped = IN6_IS_ADDR_V4MAPPED(&(SIN6(ai->ai_addr))->sin6_addr);
if (is_mapped) {
sin = SIN(ss);
sin->sin_family = AF_INET;
IN6_V4MAPPED_TO_INADDR(&(SIN6(ai->ai_addr))->sin6_addr,
&sin->sin_addr);
} else {
sin6 = SIN6(ss);
sin6->sin6_family = AF_INET6;
bcopy(ai->ai_addr, sin6, sizeof (*sin6));
}
freeaddrinfo(ai);
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_addr(ipadm_addrobj_t ipaddr, const char *astr, sa_family_t af)
{
char *prefixlenstr;
uint32_t prefixlen = 0;
char *endp;
char addrstr[NI_MAXHOST + 5];
ipadm_status_t status;
(void) snprintf(addrstr, sizeof (addrstr), "%s", astr);
if ((prefixlenstr = strchr(addrstr, '/')) != NULL) {
*prefixlenstr++ = '\0';
errno = 0;
prefixlen = strtoul(prefixlenstr, &endp, 10);
if (errno != 0 || *endp != '\0')
return (IPADM_INVALID_ARG);
if ((af == AF_INET && prefixlen > IP_ABITS) ||
(af == AF_INET6 && prefixlen > IPV6_ABITS))
return (IPADM_INVALID_ARG);
}
status = i_ipadm_resolve_addr(addrstr, af, &ipaddr->ipadm_static_addr);
if (status == IPADM_SUCCESS) {
(void) strlcpy(ipaddr->ipadm_static_aname, addrstr,
sizeof (ipaddr->ipadm_static_aname));
ipaddr->ipadm_af = ipaddr->ipadm_static_addr.ss_family;
ipaddr->ipadm_static_prefixlen = prefixlen;
}
return (status);
}
ipadm_status_t
ipadm_get_addr(const ipadm_addrobj_t ipaddr, struct sockaddr_storage *addr)
{
if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_STATIC ||
addr == NULL) {
return (IPADM_INVALID_ARG);
}
*addr = ipaddr->ipadm_static_addr;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_dst_addr(ipadm_addrobj_t ipaddr, const char *daddrstr, sa_family_t af)
{
ipadm_status_t status;
if (strchr(daddrstr, '/') != NULL)
return (IPADM_BAD_ADDR);
status = i_ipadm_resolve_addr(daddrstr, af,
&ipaddr->ipadm_static_dst_addr);
if (status == IPADM_SUCCESS) {
(void) strlcpy(ipaddr->ipadm_static_dname, daddrstr,
sizeof (ipaddr->ipadm_static_dname));
}
return (status);
}
ipadm_status_t
ipadm_set_interface_id(ipadm_addrobj_t ipaddr, const char *interface_id)
{
struct sockaddr_in6 *sin6;
char *end;
char *cp;
uint32_t prefixlen;
char addrstr[INET6_ADDRSTRLEN + 1];
if (ipaddr == NULL || interface_id == NULL ||
ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
return (IPADM_INVALID_ARG);
(void) strlcpy(addrstr, interface_id, sizeof (addrstr));
if ((cp = strchr(addrstr, '/')) == NULL)
return (IPADM_INVALID_ARG);
*cp++ = '\0';
sin6 = &ipaddr->ipadm_intfid;
if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) == 1) {
errno = 0;
prefixlen = strtoul(cp, &end, 10);
if (errno != 0 || *end != '\0' || prefixlen > IPV6_ABITS)
return (IPADM_INVALID_ARG);
sin6->sin6_family = AF_INET6;
ipaddr->ipadm_intfidlen = prefixlen;
return (IPADM_SUCCESS);
}
return (IPADM_INVALID_ARG);
}
ipadm_status_t
ipadm_set_stateless(ipadm_addrobj_t ipaddr, boolean_t stateless)
{
if (ipaddr == NULL ||
ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
return (IPADM_INVALID_ARG);
ipaddr->ipadm_stateless = stateless;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_stateful(ipadm_addrobj_t ipaddr, boolean_t stateful)
{
if (ipaddr == NULL ||
ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
return (IPADM_INVALID_ARG);
ipaddr->ipadm_stateful = stateful;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_primary(ipadm_addrobj_t ipaddr, boolean_t primary)
{
if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
return (IPADM_INVALID_ARG);
ipaddr->ipadm_primary = primary;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_wait_time(ipadm_addrobj_t ipaddr, int32_t wait)
{
if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
return (IPADM_INVALID_ARG);
ipaddr->ipadm_wait = wait;
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_set_reqhost(ipadm_addrobj_t ipaddr, const char *reqhost)
{
const size_t HNLEN = sizeof (ipaddr->ipadm_reqhost);
if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
return (IPADM_INVALID_ARG);
if (ipadm_is_nil_hostname(reqhost))
*ipaddr->ipadm_reqhost = '\0';
else if (!ipadm_is_valid_hostname(reqhost))
return (IPADM_INVALID_ARG);
else if (strlcpy(ipaddr->ipadm_reqhost, reqhost, HNLEN) >= HNLEN)
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_lookupadd_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
{
ipmgmt_aobjop_arg_t larg;
ipmgmt_aobjop_rval_t rval, *rvalp;
int err;
bzero(&larg, sizeof (larg));
larg.ia_cmd = IPMGMT_CMD_ADDROBJ_LOOKUPADD;
(void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
sizeof (larg.ia_aobjname));
(void) strlcpy(larg.ia_ifname, ipaddr->ipadm_ifname,
sizeof (larg.ia_ifname));
larg.ia_family = ipaddr->ipadm_af;
larg.ia_atype = ipaddr->ipadm_atype;
rvalp = &rval;
err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
sizeof (rval), B_FALSE);
if (err == 0) {
if ((iph->iph_flags & IPH_LEGACY) == 0)
ipaddr->ipadm_lifnum = rval.ir_lnum;
if (ipaddr->ipadm_aobjname[0] == '\0') {
(void) strlcpy(ipaddr->ipadm_aobjname,
rval.ir_aobjname,
sizeof (ipaddr->ipadm_aobjname));
}
}
if (err == EEXIST)
return (IPADM_ADDROBJ_EXISTS);
return (ipadm_errno2status(err));
}
ipadm_status_t
i_ipadm_setlifnum_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
{
ipmgmt_aobjop_arg_t larg;
ipmgmt_retval_t rval, *rvalp;
int err;
if (iph->iph_flags & IPH_IPMGMTD)
return (IPADM_SUCCESS);
bzero(&larg, sizeof (larg));
larg.ia_cmd = IPMGMT_CMD_ADDROBJ_SETLIFNUM;
(void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
sizeof (larg.ia_aobjname));
larg.ia_lnum = ipaddr->ipadm_lifnum;
(void) strlcpy(larg.ia_ifname, ipaddr->ipadm_ifname,
sizeof (larg.ia_ifname));
larg.ia_family = ipaddr->ipadm_af;
rvalp = &rval;
err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
sizeof (rval), B_FALSE);
if (err == EEXIST)
return (IPADM_ADDROBJ_EXISTS);
return (ipadm_errno2status(err));
}
ipadm_status_t
i_ipadm_enable_static(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl,
sa_family_t af)
{
char *prefixlenstr = NULL;
char *upstr = NULL;
char *sname = NULL, *dname = NULL;
struct ipadm_addrobj_s ipaddr;
char *aobjname = NULL;
nvlist_t *nvaddr = NULL;
nvpair_t *nvp;
char *cidraddr;
char *name;
ipadm_status_t status;
int err = 0;
uint32_t flags = IPADM_OPT_ACTIVE;
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0 ||
strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
err = nvpair_value_nvlist(nvp, &nvaddr);
} else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) {
err = nvpair_value_string(nvp, &aobjname);
} else if (strcmp(name, IPADM_NVP_PREFIXLEN) == 0) {
err = nvpair_value_string(nvp, &prefixlenstr);
} else if (strcmp(name, "up") == 0) {
err = nvpair_value_string(nvp, &upstr);
}
if (err != 0)
return (ipadm_errno2status(err));
}
for (nvp = nvlist_next_nvpair(nvaddr, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvaddr, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_IPADDRHNAME) == 0)
err = nvpair_value_string(nvp, &sname);
else if (strcmp(name, IPADM_NVP_IPDADDRHNAME) == 0)
err = nvpair_value_string(nvp, &dname);
if (err != 0)
return (ipadm_errno2status(err));
}
if (strcmp(upstr, "yes") == 0)
flags |= IPADM_OPT_UP;
i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_STATIC);
if (prefixlenstr != NULL && atoi(prefixlenstr) > 0) {
if (asprintf(&cidraddr, "%s/%s", sname, prefixlenstr) == -1)
return (IPADM_NO_MEMORY);
status = ipadm_set_addr(&ipaddr, cidraddr, af);
free(cidraddr);
} else {
status = ipadm_set_addr(&ipaddr, sname, af);
}
if (status != IPADM_SUCCESS)
return (status);
if (dname != NULL) {
status = ipadm_set_dst_addr(&ipaddr, dname, af);
if (status != IPADM_SUCCESS)
return (status);
}
return (i_ipadm_create_addr(iph, &ipaddr, flags));
}
ipadm_status_t
i_ipadm_enable_dhcp(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl)
{
int32_t wait = IPADM_DHCP_WAIT_DEFAULT;
boolean_t primary = B_FALSE;
nvlist_t *nvdhcp = NULL;
nvpair_t *nvp;
char *name;
struct ipadm_addrobj_s ipaddr;
char *aobjname = NULL, *reqhost = NULL;
int err = 0;
ipadm_status_t ipadm_err = IPADM_SUCCESS;
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_DHCP) == 0)
err = nvpair_value_nvlist(nvp, &nvdhcp);
else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0)
err = nvpair_value_string(nvp, &aobjname);
else if (strcmp(name, IPADM_NVP_REQHOST) == 0)
err = nvpair_value_string(nvp, &reqhost);
if (err != 0)
return (ipadm_errno2status(err));
}
for (nvp = nvlist_next_nvpair(nvdhcp, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvdhcp, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_WAIT) == 0)
err = nvpair_value_int32(nvp, &wait);
else if (strcmp(name, IPADM_NVP_PRIMARY) == 0)
err = nvpair_value_boolean_value(nvp, &primary);
if (err != 0)
return (ipadm_errno2status(err));
}
i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_DHCP);
ipaddr.ipadm_primary = primary;
if (iph->iph_flags & IPH_INIT)
ipaddr.ipadm_wait = 0;
else
ipaddr.ipadm_wait = wait;
ipadm_err = ipadm_set_reqhost(&ipaddr, reqhost);
if (ipadm_err != IPADM_SUCCESS)
return (ipadm_err);
ipaddr.ipadm_af = AF_INET;
return (i_ipadm_create_dhcp(iph, &ipaddr, IPADM_OPT_ACTIVE));
}
ipadm_status_t
i_ipadm_enable_addrconf(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl)
{
struct ipadm_addrobj_s ipaddr;
char *stateful = NULL, *stateless = NULL;
uint_t n;
uint8_t *addr6 = NULL;
uint32_t intfidlen = 0;
char *aobjname;
nvlist_t *nvaddr;
nvpair_t *nvp;
char *name;
int err = 0;
for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_INTFID) == 0)
err = nvpair_value_nvlist(nvp, &nvaddr);
else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0)
err = nvpair_value_string(nvp, &aobjname);
if (err != 0)
return (ipadm_errno2status(err));
}
for (nvp = nvlist_next_nvpair(nvaddr, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(nvaddr, nvp)) {
name = nvpair_name(nvp);
if (strcmp(name, IPADM_NVP_IPNUMADDR) == 0)
err = nvpair_value_uint8_array(nvp, &addr6, &n);
if (strcmp(name, IPADM_NVP_PREFIXLEN) == 0)
err = nvpair_value_uint32(nvp, &intfidlen);
else if (strcmp(name, IPADM_NVP_STATELESS) == 0)
err = nvpair_value_string(nvp, &stateless);
else if (strcmp(name, IPADM_NVP_STATEFUL) == 0)
err = nvpair_value_string(nvp, &stateful);
if (err != 0)
return (ipadm_errno2status(err));
}
i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_IPV6_ADDRCONF);
if (intfidlen > 0) {
ipaddr.ipadm_intfidlen = intfidlen;
bcopy(addr6, &ipaddr.ipadm_intfid.sin6_addr.s6_addr, n);
}
ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0);
ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0);
return (i_ipadm_create_ipv6addrs(iph, &ipaddr, IPADM_OPT_ACTIVE));
}
ipadm_status_t
ipadm_create_addrobj(ipadm_addr_type_t type, const char *aobjname,
ipadm_addrobj_t *ipaddr)
{
ipadm_addrobj_t newaddr;
ipadm_status_t status;
char *aname, *cp;
char ifname[IPADM_AOBJSIZ];
ifspec_t ifsp;
if (ipaddr == NULL)
return (IPADM_INVALID_ARG);
*ipaddr = NULL;
if (aobjname == NULL || aobjname[0] == '\0')
return (IPADM_INVALID_ARG);
if (strlcpy(ifname, aobjname, IPADM_AOBJSIZ) >= IPADM_AOBJSIZ)
return (IPADM_INVALID_ARG);
if ((aname = strchr(ifname, '/')) != NULL)
*aname++ = '\0';
if (!ifparse_ifspec(ifname, &ifsp))
return (IPADM_INVALID_ARG);
if (aname != NULL && !i_ipadm_is_user_aobjname_valid(aname))
return (IPADM_INVALID_ARG);
if ((newaddr = calloc(1, sizeof (struct ipadm_addrobj_s))) == NULL)
return (IPADM_NO_MEMORY);
if (ifsp.ifsp_lunvalid) {
newaddr->ipadm_lifnum = ifsp.ifsp_lun;
cp = strchr(ifname, IPADM_LOGICAL_SEP);
*cp = '\0';
}
(void) strlcpy(newaddr->ipadm_ifname, ifname,
sizeof (newaddr->ipadm_ifname));
if (aname != NULL) {
(void) snprintf(newaddr->ipadm_aobjname,
sizeof (newaddr->ipadm_aobjname), "%s/%s", ifname, aname);
}
switch (type) {
case IPADM_ADDR_IPV6_ADDRCONF:
newaddr->ipadm_intfidlen = 0;
newaddr->ipadm_stateful = B_TRUE;
newaddr->ipadm_stateless = B_TRUE;
newaddr->ipadm_af = AF_INET6;
break;
case IPADM_ADDR_DHCP:
newaddr->ipadm_primary = B_FALSE;
newaddr->ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
newaddr->ipadm_af = AF_INET;
break;
case IPADM_ADDR_STATIC:
newaddr->ipadm_af = AF_UNSPEC;
newaddr->ipadm_static_prefixlen = 0;
break;
default:
status = IPADM_INVALID_ARG;
goto fail;
}
newaddr->ipadm_atype = type;
*ipaddr = newaddr;
return (IPADM_SUCCESS);
fail:
free(newaddr);
return (status);
}
ipadm_status_t
ipadm_get_aobjname(const ipadm_addrobj_t ipaddr, char *aobjname, size_t len)
{
if (ipaddr == NULL || aobjname == NULL)
return (IPADM_INVALID_ARG);
if (strlcpy(aobjname, ipaddr->ipadm_aobjname, len) >= len)
return (IPADM_INVALID_ARG);
return (IPADM_SUCCESS);
}
void
ipadm_destroy_addrobj(ipadm_addrobj_t ipaddr)
{
free(ipaddr);
}
void
i_ipadm_addrobj2lifname(ipadm_addrobj_t ipaddr, char *lifname, int lifnamesize)
{
if (ipaddr->ipadm_lifnum != 0) {
(void) snprintf(lifname, lifnamesize, "%s:%d",
ipaddr->ipadm_ifname, ipaddr->ipadm_lifnum);
} else {
(void) snprintf(lifname, lifnamesize, "%s",
ipaddr->ipadm_ifname);
}
}
static ipadm_status_t
i_ipadm_addr_exists_on_if(ipadm_handle_t iph, const char *ifname,
sa_family_t af, boolean_t *exists)
{
struct lifreq lifr;
int sock;
if (iph->iph_flags & IPH_LEGACY) {
*exists = B_FALSE;
return (IPADM_SUCCESS);
}
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
if (af == AF_INET) {
sock = iph->iph_sock;
if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
if (lifr.lifr_flags & IFF_DHCPRUNNING) {
*exists = B_TRUE;
return (IPADM_SUCCESS);
}
} else {
sock = iph->iph_sock6;
}
if (ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
*exists = !sockaddrunspec((struct sockaddr *)&lifr.lifr_addr);
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_do_addif(ipadm_handle_t iph, ipadm_addrobj_t addr, boolean_t *added)
{
ipadm_status_t status;
boolean_t addif;
struct lifreq lifr;
int sock;
status = i_ipadm_addr_exists_on_if(iph, addr->ipadm_ifname,
addr->ipadm_af, &addif);
if (status != IPADM_SUCCESS)
return (status);
if (addif) {
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, addr->ipadm_ifname,
sizeof (lifr.lifr_name));
sock = (addr->ipadm_af == AF_INET ? iph->iph_sock :
iph->iph_sock6);
if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
addr->ipadm_lifnum = i_ipadm_get_lnum(lifr.lifr_name);
if (added != NULL)
*added = B_TRUE;
} else {
addr->ipadm_lifnum = 0;
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_get_db_addr(ipadm_handle_t iph, const char *ifname,
const char *aobjname, nvlist_t **onvl)
{
ipmgmt_getaddr_arg_t garg;
bzero(&garg, sizeof (garg));
garg.ia_cmd = IPMGMT_CMD_GETADDR;
if (aobjname != NULL)
(void) strlcpy(garg.ia_aobjname, aobjname,
sizeof (garg.ia_aobjname));
if (ifname != NULL)
(void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
}
ipadm_status_t
ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags)
{
ipadm_status_t status;
sa_family_t af;
sa_family_t daf;
sa_family_t other_af;
boolean_t created_af = B_FALSE;
boolean_t created_other_af = B_FALSE;
ipadm_addr_type_t type;
char *ifname = addr->ipadm_ifname;
boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
boolean_t aobjfound = B_FALSE;
boolean_t is_6to4;
struct lifreq lifr;
uint64_t ifflags;
boolean_t is_boot = (iph->iph_flags & IPH_IPMGMTD);
boolean_t is_ipmp;
char gifname[LIFGRNAMSIZ];
if (!ipadm_check_auth())
return (IPADM_EAUTH);
status = i_ipadm_validate_create_addr(iph, addr, flags);
if (status != IPADM_SUCCESS)
return (status);
if (legacy) {
struct ipadm_addrobj_s ipaddr;
ipaddr = *addr;
status = i_ipadm_get_lif2addrobj(iph, &ipaddr);
if (status == IPADM_SUCCESS) {
aobjfound = B_TRUE;
if (ipaddr.ipadm_atype != IPADM_ADDR_STATIC)
return (IPADM_NOTSUP);
(void) strlcpy(addr->ipadm_aobjname,
ipaddr.ipadm_aobjname, IPADM_AOBJSIZ);
} else if (status == IPADM_NOTFOUND) {
aobjfound = B_FALSE;
} else {
return (status);
}
}
af = addr->ipadm_af;
if (!is_boot && (!legacy || !aobjfound)) {
status = i_ipadm_lookupadd_addrobj(iph, addr);
if (status != IPADM_SUCCESS)
return (status);
}
is_6to4 = i_ipadm_is_6to4(iph, ifname);
status = i_ipadm_create_if(iph, ifname, af, flags);
if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS) {
(void) i_ipadm_delete_addrobj(iph, addr, IPADM_OPT_ACTIVE);
return (status);
}
if (status == IPADM_SUCCESS)
created_af = B_TRUE;
if (!is_6to4 && !legacy && (flags & IPADM_OPT_V46)) {
other_af = (af == AF_INET ? AF_INET6 : AF_INET);
status = i_ipadm_create_if(iph, ifname, other_af, flags);
if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS) {
(void) i_ipadm_delete_if(iph, ifname, af, flags);
return (status);
}
if (status == IPADM_SUCCESS)
created_other_af = B_TRUE;
}
if (addr->ipadm_atype == IPADM_ADDR_STATIC) {
status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
if (status != IPADM_SUCCESS)
goto fail;
if (iph->iph_zoneid != GLOBAL_ZONEID &&
(ifflags & IFF_L3PROTECT) && (flags & IPADM_OPT_PERSIST)) {
status = IPADM_GZ_PERM;
goto fail;
}
daf = addr->ipadm_static_dst_addr.ss_family;
if (ifflags & IFF_POINTOPOINT) {
if (is_6to4) {
if (af != AF_INET6 || daf != AF_UNSPEC) {
status = IPADM_INVALID_ARG;
goto fail;
}
} else {
if (daf != af) {
status = IPADM_INVALID_ARG;
goto fail;
}
if (!legacy && sockaddrunspec(
(struct sockaddr *)
&addr->ipadm_static_dst_addr)) {
status = IPADM_BAD_ADDR;
goto fail;
}
}
} else {
if (daf != AF_UNSPEC)
return (IPADM_INVALID_ARG);
}
}
if (status == IPADM_SUCCESS && is_6to4) {
bzero(&lifr, sizeof (lifr));
(void) strlcpy(lifr.lifr_name, addr->ipadm_ifname,
sizeof (lifr.lifr_name));
if (ioctl(iph->iph_sock6, SIOCGLIFADDR, &lifr) < 0) {
status = ipadm_errno2status(errno);
goto fail;
}
if (sockaddrcmp(&lifr.lifr_addr, &addr->ipadm_static_addr))
return (IPADM_SUCCESS);
}
if ((is_ipmp = i_ipadm_is_under_ipmp(iph, addr->ipadm_ifname))) {
(void) i_ipadm_get_groupname_active(iph, addr->ipadm_ifname,
gifname, sizeof (gifname));
(void) i_ipadm_set_groupname_active(iph, addr->ipadm_ifname,
"");
}
type = addr->ipadm_atype;
switch (type) {
case IPADM_ADDR_STATIC:
status = i_ipadm_create_addr(iph, addr, flags);
break;
case IPADM_ADDR_DHCP:
status = i_ipadm_create_dhcp(iph, addr, flags);
break;
case IPADM_ADDR_IPV6_ADDRCONF:
status = i_ipadm_create_ipv6addrs(iph, addr, flags);
break;
default:
status = IPADM_INVALID_ARG;
break;
}
if (is_ipmp) {
(void) i_ipadm_set_groupname_active(iph, addr->ipadm_ifname,
gifname);
}
fail:
if (status != IPADM_DHCP_IPC_TIMEOUT &&
status != IPADM_SUCCESS) {
if (!legacy) {
if (created_af || created_other_af) {
if (created_af) {
(void) i_ipadm_delete_if(iph, ifname,
af, flags);
}
if (created_other_af) {
(void) i_ipadm_delete_if(iph, ifname,
other_af, flags);
}
} else {
(void) i_ipadm_delete_addrobj(iph, addr, flags);
}
} else if (!aobjfound) {
(void) i_ipadm_delete_addrobj(iph, addr, flags);
}
}
return (status);
}
static ipadm_status_t
i_ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr, uint32_t flags)
{
struct lifreq lifr;
ipadm_status_t status = IPADM_SUCCESS;
int sock;
struct sockaddr_storage m, *mask = &m;
const struct sockaddr_storage *addr = &ipaddr->ipadm_static_addr;
const struct sockaddr_storage *daddr = &ipaddr->ipadm_static_dst_addr;
sa_family_t af;
boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
struct ipadm_addrobj_s legacy_addr;
boolean_t default_prefixlen = B_FALSE;
boolean_t is_boot;
is_boot = ((iph->iph_flags & IPH_IPMGMTD) != 0);
af = ipaddr->ipadm_af;
sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
if (ipaddr->ipadm_static_prefixlen == 0) {
status = i_ipadm_get_default_prefixlen(
&ipaddr->ipadm_static_addr,
&ipaddr->ipadm_static_prefixlen);
if (status != IPADM_SUCCESS)
return (status);
default_prefixlen = B_TRUE;
}
(void) plen2mask(ipaddr->ipadm_static_prefixlen, af,
(struct sockaddr *)mask);
if (!(iph->iph_flags & IPH_LEGACY)) {
status = i_ipadm_do_addif(iph, ipaddr, NULL);
if (status != IPADM_SUCCESS)
return (status);
}
i_ipadm_addrobj2lifname(ipaddr, lifr.lifr_name,
sizeof (lifr.lifr_name));
lifr.lifr_addr = *mask;
if (ioctl(sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) {
status = ipadm_errno2status(errno);
goto ret;
}
lifr.lifr_addr = *addr;
if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
status = ipadm_errno2status(errno);
goto ret;
}
if (daddr->ss_family != AF_UNSPEC) {
lifr.lifr_addr = *daddr;
if (ioctl(sock, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0) {
status = ipadm_errno2status(errno);
goto ret;
}
}
if (flags & IPADM_OPT_UP) {
uint32_t iff_flags = IFF_UP;
if (i_ipadm_is_under_ipmp(iph, lifr.lifr_name) &&
!i_ipadm_is_ipmp(iph, lifr.lifr_name))
iff_flags |= IFF_NOFAILOVER;
status = i_ipadm_set_flags(iph, lifr.lifr_name,
af, iff_flags, 0);
if (status == IPADM_DAD_FOUND)
status = IPADM_SUCCESS;
}
if (status == IPADM_SUCCESS && !is_boot) {
if (legacy) {
bzero(&legacy_addr, sizeof (legacy_addr));
(void) strlcpy(legacy_addr.ipadm_aobjname,
ipaddr->ipadm_aobjname,
sizeof (legacy_addr.ipadm_aobjname));
status = i_ipadm_get_addrobj(iph, &legacy_addr);
if (status == IPADM_SUCCESS &&
legacy_addr.ipadm_lifnum >= 0) {
return (status);
}
}
status = i_ipadm_addr_persist(iph, ipaddr, default_prefixlen,
flags, NULL);
}
ret:
if (status != IPADM_SUCCESS && !legacy)
(void) i_ipadm_delete_addr(iph, ipaddr);
return (status);
}
ipadm_status_t
ipadm_delete_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
{
ipadm_status_t status;
struct ipadm_addrobj_s ipaddr;
boolean_t release = ((flags & IPADM_OPT_RELEASE) != 0);
boolean_t is_ipmp = B_FALSE;
char gifname[LIFGRNAMSIZ];
if (!ipadm_check_auth())
return (IPADM_EAUTH);
if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
!(flags & IPADM_OPT_ACTIVE)) ||
(flags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_RELEASE))) {
return (IPADM_INVALID_ARG);
}
bzero(&ipaddr, sizeof (ipaddr));
if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
status = i_ipadm_get_addrobj(iph, &ipaddr);
if (status != IPADM_SUCCESS)
return (status);
if (release && ipaddr.ipadm_atype != IPADM_ADDR_DHCP)
return (IPADM_NOTSUP);
if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE) &&
(flags & IPADM_OPT_ACTIVE) && !(flags & IPADM_OPT_PERSIST)) {
return (IPADM_NOTFOUND);
}
if (ipaddr.ipadm_flags & IPMGMT_ACTIVE) {
if ((is_ipmp = i_ipadm_is_under_ipmp(iph,
ipaddr.ipadm_ifname))) {
(void) i_ipadm_get_groupname_active(iph,
ipaddr.ipadm_ifname, gifname, sizeof (gifname));
(void) i_ipadm_set_groupname_active(iph,
ipaddr.ipadm_ifname, "");
}
switch (ipaddr.ipadm_atype) {
case IPADM_ADDR_STATIC:
status = i_ipadm_delete_addr(iph, &ipaddr);
break;
case IPADM_ADDR_DHCP:
status = i_ipadm_delete_dhcp(iph, &ipaddr, release);
break;
case IPADM_ADDR_IPV6_ADDRCONF:
status = i_ipadm_delete_ipv6addrs(iph, &ipaddr);
break;
default:
break;
}
if (status == IPADM_ENXIO)
status = IPADM_SUCCESS;
else if (status != IPADM_SUCCESS)
goto out;
}
if (!(ipaddr.ipadm_flags & IPMGMT_PERSIST) &&
(flags & IPADM_OPT_PERSIST)) {
flags &= ~IPADM_OPT_PERSIST;
}
status = i_ipadm_delete_addrobj(iph, &ipaddr, flags);
if (status != IPADM_NOTFOUND)
status = IPADM_SUCCESS;
out:
if (is_ipmp) {
(void) i_ipadm_set_groupname_active(iph,
ipaddr.ipadm_ifname, gifname);
}
return (status);
}
static ipadm_status_t
i_ipadm_create_dhcp(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags)
{
ipadm_status_t status;
ipadm_status_t dh_status;
if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1)
return (IPADM_DHCP_START_ERROR);
retry:
status = i_ipadm_do_addif(iph, addr, NULL);
if (status != IPADM_SUCCESS)
return (status);
if (!(iph->iph_flags & IPH_INIT)) {
status = i_ipadm_setlifnum_addrobj(iph, addr);
if (status == IPADM_ADDROBJ_EXISTS)
goto retry;
if (status != IPADM_SUCCESS)
return (status);
}
status = i_ipadm_op_dhcp(addr, DHCP_START, NULL);
if (status != IPADM_SUCCESS && status != IPADM_DHCP_IPC_TIMEOUT)
goto fail;
dh_status = status;
status = i_ipadm_addr_persist(iph, addr, B_FALSE, flags, NULL);
if (status != IPADM_SUCCESS)
goto fail;
return (dh_status);
fail:
(void) i_ipadm_delete_dhcp(iph, addr, B_TRUE);
return (status);
}
static ipadm_status_t
i_ipadm_delete_dhcp(ipadm_handle_t iph, ipadm_addrobj_t addr, boolean_t release)
{
ipadm_status_t status;
int dherr;
if (release) {
status = i_ipadm_op_dhcp(addr, DHCP_RELEASE, &dherr);
if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE)
status = i_ipadm_op_dhcp(addr, DHCP_DROP, NULL);
} else {
status = i_ipadm_op_dhcp(addr, DHCP_DROP, NULL);
}
if (status != IPADM_SUCCESS)
return (status);
if (addr->ipadm_lifnum != 0) {
struct lifreq lifr;
bzero(&lifr, sizeof (lifr));
i_ipadm_addrobj2lifname(addr, lifr.lifr_name,
sizeof (lifr.lifr_name));
if (ioctl(iph->iph_sock, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0)
return (ipadm_errno2status(errno));
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror)
{
dhcp_ipc_request_t *request;
dhcp_ipc_reply_t *reply = NULL;
dhcp_symbol_t *entry = NULL;
dhcp_data_type_t dtype = DHCP_TYPE_NONE;
void *d4o = NULL;
uint16_t d4olen = 0;
char ifname[LIFNAMSIZ];
int error;
int dhcp_timeout;
bzero(&ifname, sizeof (ifname));
i_ipadm_addrobj2lifname(addr, ifname, sizeof (ifname));
if (addr->ipadm_primary)
type |= DHCP_PRIMARY;
switch (DHCP_IPC_CMD(type)) {
case DHCP_START:
case DHCP_EXTEND:
if (addr->ipadm_af == AF_INET && *addr->ipadm_reqhost != '\0') {
entry = inittab_getbycode(ITAB_CAT_STANDARD,
ITAB_CONS_INFO, CD_HOSTNAME);
if (entry == NULL) {
return (IPADM_FAILURE);
} else {
d4o = inittab_encode(entry, addr->ipadm_reqhost,
&d4olen, B_FALSE);
free(entry);
entry = NULL;
if (d4o == NULL)
return (IPADM_FAILURE);
dtype = DHCP_TYPE_OPTION;
}
}
break;
default:
break;
}
request = dhcp_ipc_alloc_request(type, ifname, d4o, d4olen, dtype);
if (request == NULL) {
free(d4o);
return (IPADM_NO_MEMORY);
}
if (addr->ipadm_wait == IPADM_DHCP_WAIT_FOREVER)
dhcp_timeout = DHCP_IPC_WAIT_FOREVER;
else if (addr->ipadm_wait == IPADM_DHCP_WAIT_DEFAULT)
dhcp_timeout = DHCP_IPC_WAIT_DEFAULT;
else
dhcp_timeout = addr->ipadm_wait;
error = dhcp_ipc_make_request(request, &reply, dhcp_timeout);
free(request);
free(d4o);
if (error == 0) {
error = reply->return_code;
free(reply);
}
if (error != 0) {
if (dhcperror != NULL)
*dhcperror = error;
if (error != DHCP_IPC_E_TIMEOUT)
return (IPADM_DHCP_IPC_ERROR);
else if (dhcp_timeout != 0)
return (IPADM_DHCP_IPC_TIMEOUT);
}
return (IPADM_SUCCESS);
}
static ipadm_status_t
i_ipadm_dhcp_status(ipadm_addrobj_t addr, dhcp_status_t *status,
int *dhcperror)
{
dhcp_ipc_type_t type = DHCP_STATUS;
dhcp_ipc_request_t *request;
dhcp_ipc_reply_t *reply;
dhcp_status_t *private_status;
size_t reply_size;
int error;
if (addr->ipadm_af == AF_INET6)
type |= DHCP_V6;
request = dhcp_ipc_alloc_request(type, addr->ipadm_ifname, NULL, 0,
DHCP_TYPE_NONE);
if (request == NULL)
return (IPADM_NO_MEMORY);
error = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
free(request);
if (error != 0) {
if (dhcperror != NULL)
*dhcperror = error;
return (error != DHCP_IPC_E_TIMEOUT ? IPADM_DHCP_IPC_ERROR
: IPADM_DHCP_IPC_TIMEOUT);
}
error = reply->return_code;
if (error == DHCP_IPC_E_UNKIF) {
free(reply);
bzero(status, sizeof (dhcp_status_t));
return (IPADM_NOTFOUND);
}
private_status = dhcp_ipc_get_data(reply, &reply_size, NULL);
if (reply_size < DHCP_STATUS_VER1_SIZE) {
free(reply);
return (IPADM_DHCP_IPC_ERROR);
}
*status = *private_status;
free(reply);
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_addr_info(ipadm_handle_t iph, const char *ifname,
ipadm_addr_info_t **addrinfo, uint32_t flags, int64_t lifc_flags)
{
ifspec_t ifsp;
if (addrinfo == NULL || iph == NULL)
return (IPADM_INVALID_ARG);
if (ifname != NULL &&
(!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
return (IPADM_INVALID_ARG);
}
return (i_ipadm_get_all_addr_info(iph, ifname, addrinfo,
flags, lifc_flags));
}
void
ipadm_free_addr_info(ipadm_addr_info_t *ainfo)
{
freeifaddrs((struct ifaddrs *)ainfo);
}
ipadm_status_t
i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr,
boolean_t default_prefixlen, uint32_t flags, const char *propname)
{
char *aname = ipaddr->ipadm_aobjname;
nvlist_t *nvl;
int err = 0;
ipadm_status_t status;
uint_t pflags = 0;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
return (IPADM_NO_MEMORY);
if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
ipaddr->ipadm_ifname)) != 0 ||
(err = nvlist_add_string(nvl, IPADM_NVP_AOBJNAME, aname)) != 0 ||
(err = nvlist_add_int32(nvl, IPADM_NVP_LIFNUM,
ipaddr->ipadm_lifnum)) != 0) {
status = ipadm_errno2status(err);
goto ret;
}
switch (ipaddr->ipadm_atype) {
case IPADM_ADDR_STATIC:
status = i_ipadm_add_ipaddr2nvl(nvl, ipaddr);
if (status != IPADM_SUCCESS)
goto ret;
if (flags & IPADM_OPT_UP)
err = nvlist_add_string(nvl, "up", "yes");
else
err = nvlist_add_string(nvl, "up", "no");
status = ipadm_errno2status(err);
break;
case IPADM_ADDR_DHCP:
status = i_ipadm_add_dhcp2nvl(nvl, ipaddr->ipadm_primary,
ipaddr->ipadm_wait);
if (status != IPADM_SUCCESS)
goto ret;
err = nvlist_add_string(nvl, IPADM_NVP_REQHOST,
ipaddr->ipadm_reqhost);
status = ipadm_errno2status(err);
break;
case IPADM_ADDR_IPV6_ADDRCONF:
status = i_ipadm_add_intfid2nvl(nvl, ipaddr);
break;
}
if (status != IPADM_SUCCESS)
goto ret;
if (iph->iph_flags & IPH_INIT) {
pflags |= IPMGMT_INIT;
} else {
if (flags & IPADM_OPT_ACTIVE)
pflags |= IPMGMT_ACTIVE;
if (flags & IPADM_OPT_PERSIST)
pflags |= IPMGMT_PERSIST;
if (flags & IPADM_OPT_SET_PROPS)
pflags |= IPMGMT_PROPS_ONLY;
}
status = i_ipadm_addr_persist_nvl(iph, nvl, pflags);
if (flags & IPADM_OPT_SET_PROPS) {
flags |= IPADM_OPT_ACTIVE;
if (flags & IPADM_OPT_PERSIST_PROPS)
flags |= IPADM_OPT_PERSIST;
else
flags &= ~IPADM_OPT_PERSIST;
flags &= ~(IPADM_OPT_SET_PROPS | IPADM_OPT_PERSIST_PROPS);
}
if (status == IPADM_SUCCESS && (flags & IPADM_OPT_PERSIST)) {
char pbuf[MAXPROPVALLEN], *pval = NULL;
ipadm_prop_desc_t *pdp = NULL;
switch (ipaddr->ipadm_atype) {
case IPADM_ADDR_STATIC:
if (!default_prefixlen && (propname == NULL ||
strcmp(propname, IPADM_NVP_PREFIXLEN) == 0)) {
pdp = i_ipadm_get_addrprop_desc(
IPADM_NVP_PREFIXLEN);
(void) snprintf(pbuf, sizeof (pbuf), "%u",
ipaddr->ipadm_static_prefixlen);
pval = pbuf;
}
break;
case IPADM_ADDR_DHCP:
if (propname == NULL ||
strcmp(propname, IPADM_NVP_REQHOST) == 0) {
pdp = i_ipadm_get_addrprop_desc(
IPADM_NVP_REQHOST);
pval = ipaddr->ipadm_reqhost;
}
break;
default:
break;
}
if (pval != NULL) {
assert(pdp != NULL);
status = i_ipadm_persist_propval(iph, pdp, pval,
ipaddr, flags);
}
}
ret:
nvlist_free(nvl);
return (status);
}
static ipadm_status_t
i_ipadm_addr_persist_nvl(ipadm_handle_t iph, nvlist_t *nvl, uint32_t flags)
{
char *buf = NULL, *nvlbuf = NULL;
size_t nvlsize, bufsize;
ipmgmt_setaddr_arg_t *sargp;
int err;
err = nvlist_pack(nvl, &nvlbuf, &nvlsize, NV_ENCODE_NATIVE, 0);
if (err != 0)
return (ipadm_errno2status(err));
bufsize = sizeof (*sargp) + nvlsize;
buf = calloc(1, bufsize);
sargp = (void *)buf;
sargp->ia_cmd = IPMGMT_CMD_SETADDR;
sargp->ia_flags = flags;
sargp->ia_nvlsize = nvlsize;
(void) bcopy(nvlbuf, buf + sizeof (*sargp), nvlsize);
err = ipadm_door_call(iph, buf, bufsize, NULL, 0, B_FALSE);
free(buf);
free(nvlbuf);
return (ipadm_errno2status(err));
}
ipadm_status_t
i_ipadm_delete_addrobj(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr,
uint32_t flags)
{
ipmgmt_addr_arg_t arg;
int err;
arg.ia_cmd = IPMGMT_CMD_RESETADDR;
arg.ia_flags = 0;
if (flags & IPADM_OPT_ACTIVE)
arg.ia_flags |= IPMGMT_ACTIVE;
if (flags & IPADM_OPT_PERSIST)
arg.ia_flags |= IPMGMT_PERSIST;
(void) strlcpy(arg.ia_aobjname, ipaddr->ipadm_aobjname,
sizeof (arg.ia_aobjname));
arg.ia_lnum = ipaddr->ipadm_lifnum;
err = ipadm_door_call(iph, &arg, sizeof (arg), NULL, 0, B_FALSE);
return (ipadm_errno2status(err));
}
static ipadm_status_t
i_ipadm_updown_common(ipadm_handle_t iph, const char *aobjname,
ipadm_addrobj_t ipaddr, uint32_t ipadm_flags, uint64_t *ifflags)
{
ipadm_status_t status;
char lifname[LIFNAMSIZ];
if (!ipadm_check_auth())
return (IPADM_EAUTH);
if (aobjname == NULL || strlcpy(ipaddr->ipadm_aobjname, aobjname,
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
status = i_ipadm_get_addrobj(iph, ipaddr);
if (status != IPADM_SUCCESS)
return (status);
if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE))
return (IPADM_OP_DISABLE_OBJ);
if ((ipadm_flags & IPADM_OPT_PERSIST) &&
!(ipaddr->ipadm_flags & IPMGMT_PERSIST))
return (IPADM_TEMPORARY_OBJ);
if (ipaddr->ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF ||
(ipaddr->ipadm_atype == IPADM_ADDR_DHCP &&
(ipadm_flags & IPADM_OPT_PERSIST)))
return (IPADM_NOTSUP);
i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
return (i_ipadm_get_flags(iph, lifname, ipaddr->ipadm_af, ifflags));
}
ipadm_status_t
ipadm_up_addr(ipadm_handle_t iph, const char *aobjname, uint32_t ipadm_flags)
{
struct ipadm_addrobj_s ipaddr;
ipadm_status_t status;
uint64_t flags;
char lifname[LIFNAMSIZ];
status = i_ipadm_updown_common(iph, aobjname, &ipaddr, ipadm_flags,
&flags);
if (status != IPADM_SUCCESS)
return (status);
if (flags & IFF_UP)
goto persist;
if (flags & IFF_DUPLICATE)
return (IPADM_DAD_FOUND);
i_ipadm_addrobj2lifname(&ipaddr, lifname, sizeof (lifname));
status = i_ipadm_set_flags(iph, lifname, ipaddr.ipadm_af, IFF_UP, 0);
if (status != IPADM_SUCCESS)
return (status);
persist:
if (ipadm_flags & IPADM_OPT_PERSIST) {
status = i_ipadm_persist_propval(iph, &up_addrprop,
"yes", &ipaddr, 0);
}
return (status);
}
ipadm_status_t
ipadm_down_addr(ipadm_handle_t iph, const char *aobjname, uint32_t ipadm_flags)
{
struct ipadm_addrobj_s ipaddr;
ipadm_status_t status;
struct lifreq lifr;
uint64_t flags;
status = i_ipadm_updown_common(iph, aobjname, &ipaddr, ipadm_flags,
&flags);
if (status != IPADM_SUCCESS)
return (status);
i_ipadm_addrobj2lifname(&ipaddr, lifr.lifr_name,
sizeof (lifr.lifr_name));
if (flags & IFF_UP) {
status = i_ipadm_set_flags(iph, lifr.lifr_name,
ipaddr.ipadm_af, 0, IFF_UP);
if (status != IPADM_SUCCESS)
return (status);
} else if (flags & IFF_DUPLICATE) {
if (ioctl(iph->iph_sock, SIOCGLIFADDR, &lifr) < 0)
return (ipadm_errno2status(errno));
if (ioctl(iph->iph_sock, SIOCSLIFADDR, &lifr) < 0)
return (ipadm_errno2status(errno));
}
if (ipadm_flags & IPADM_OPT_PERSIST) {
status = i_ipadm_persist_propval(iph, &up_addrprop,
"no", &ipaddr, 0);
}
return (status);
}
ipadm_status_t
ipadm_refresh_addr(ipadm_handle_t iph, const char *aobjname,
uint32_t ipadm_flags)
{
ipadm_status_t status = IPADM_SUCCESS;
uint64_t flags;
struct ipadm_addrobj_s ipaddr;
sa_family_t af;
char lifname[LIFNAMSIZ];
boolean_t inform =
((ipadm_flags & IPADM_OPT_INFORM) != 0);
if (!ipadm_check_auth())
return (IPADM_EAUTH);
bzero(&ipaddr, sizeof (ipaddr));
if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
status = i_ipadm_get_addrobj(iph, &ipaddr);
if (status != IPADM_SUCCESS)
return (status);
if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
return (IPADM_OP_DISABLE_OBJ);
if (i_ipadm_is_vni(ipaddr.ipadm_ifname))
return (IPADM_NOTSUP);
if (inform && ipaddr.ipadm_atype != IPADM_ADDR_STATIC)
return (IPADM_INVALID_ARG);
af = ipaddr.ipadm_af;
if (ipaddr.ipadm_atype == IPADM_ADDR_STATIC) {
i_ipadm_addrobj2lifname(&ipaddr, lifname, sizeof (lifname));
status = i_ipadm_get_flags(iph, lifname, af, &flags);
if (status != IPADM_SUCCESS)
return (status);
if (inform) {
if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1)
return (IPADM_DHCP_START_ERROR);
ipaddr.ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
return (i_ipadm_op_dhcp(&ipaddr, DHCP_INFORM, NULL));
}
if (!(flags & IFF_DUPLICATE))
return (IPADM_SUCCESS);
status = i_ipadm_set_flags(iph, lifname, af, IFF_UP, 0);
} else if (ipaddr.ipadm_atype == IPADM_ADDR_DHCP) {
status = i_ipadm_refresh_dhcp(&ipaddr);
} else {
status = IPADM_NOTSUP;
}
return (status);
}
static ipadm_status_t
i_ipadm_refresh_dhcp(ipadm_addrobj_t ipaddr)
{
ipadm_status_t status;
int dherr;
status = i_ipadm_op_dhcp(ipaddr, DHCP_EXTEND, &dherr);
if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE) {
ipaddr->ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
status = i_ipadm_op_dhcp(ipaddr, DHCP_START, NULL);
}
return (status);
}
static ipadm_status_t
i_ipadm_validate_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr,
uint32_t flags)
{
sa_family_t af;
sa_family_t other_af;
char *ifname;
ipadm_status_t status;
boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
boolean_t islo, isvni;
uint64_t ifflags = 0;
boolean_t p_exists;
boolean_t af_exists, other_af_exists, a_exists;
if (ipaddr == NULL || flags == 0 || flags == IPADM_OPT_PERSIST ||
(flags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_UP|IPADM_OPT_V46))) {
return (IPADM_INVALID_ARG);
}
if (ipaddr->ipadm_af == AF_UNSPEC)
return (IPADM_BAD_ADDR);
if (!legacy && ipaddr->ipadm_lifnum != 0)
return (IPADM_INVALID_ARG);
if (legacy && ipaddr->ipadm_atype != IPADM_ADDR_STATIC)
return (IPADM_NOTSUP);
ifname = ipaddr->ipadm_ifname;
if (i_ipadm_is_under_ipmp(iph, ifname))
return (IPADM_SUCCESS);
af = ipaddr->ipadm_af;
af_exists = ipadm_if_enabled(iph, ifname, af);
if (legacy && !af_exists)
return (IPADM_ENXIO);
other_af = (af == AF_INET ? AF_INET6 : AF_INET);
other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
a_exists = (af_exists || other_af_exists);
status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
if (status != IPADM_SUCCESS)
return (status);
if (!a_exists && p_exists)
return (IPADM_OP_DISABLE_OBJ);
if (af_exists) {
status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
if (status != IPADM_SUCCESS)
return (status);
}
islo = i_ipadm_is_loopback(ifname);
isvni = i_ipadm_is_vni(ifname);
switch (ipaddr->ipadm_atype) {
case IPADM_ADDR_STATIC:
if ((islo || isvni) && ipaddr->ipadm_static_dname[0] != '\0')
return (IPADM_INVALID_ARG);
if (!legacy && sockaddrunspec(
(struct sockaddr *)&ipaddr->ipadm_static_addr))
return (IPADM_BAD_ADDR);
break;
case IPADM_ADDR_DHCP:
if (islo || (ifflags & IFF_VRRP))
return (IPADM_NOTSUP);
break;
case IPADM_ADDR_IPV6_ADDRCONF:
if (islo || (ifflags & IFF_VRRP) ||
i_ipadm_is_6to4(iph, ifname)) {
return (IPADM_NOTSUP);
}
break;
default:
return (IPADM_INVALID_ARG);
}
return (IPADM_SUCCESS);
}
ipadm_status_t
i_ipadm_merge_addrprops_from_nvl(nvlist_t *invl, nvlist_t *onvl,
const char *aobjname)
{
const char * const ADDRPROPS[] =
{ IPADM_NVP_PREFIXLEN, IPADM_NVP_REQHOST };
const size_t ADDRPROPSLEN =
sizeof (ADDRPROPS) / sizeof (*ADDRPROPS);
nvpair_t *nvp, *propnvp;
nvlist_t *tnvl;
char *aname;
const char *propname;
size_t i;
int err;
for (i = 0; i < ADDRPROPSLEN; ++i) {
propname = ADDRPROPS[i];
for (nvp = nvlist_next_nvpair(invl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(invl, nvp)) {
if (nvpair_value_nvlist(nvp, &tnvl) == 0 &&
nvlist_exists(tnvl, propname) &&
nvlist_lookup_string(tnvl, IPADM_NVP_AOBJNAME,
&aname) == 0 && strcmp(aname, aobjname) == 0) {
(void) nvlist_lookup_nvpair(tnvl, propname,
&propnvp);
err = nvlist_add_nvpair(onvl, propnvp);
if (err == 0) {
err = nvlist_remove(invl,
nvpair_name(nvp), nvpair_type(nvp));
}
if (err != 0)
return (ipadm_errno2status(err));
break;
}
}
}
return (IPADM_SUCCESS);
}
ipadm_status_t
ipadm_enable_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
{
nvlist_t *addrnvl, *nvl;
nvpair_t *nvp;
ipadm_status_t status;
struct ipadm_addrobj_s ipaddr;
if (!ipadm_check_auth())
return (IPADM_EAUTH);
if (flags & IPADM_OPT_PERSIST)
return (IPADM_NOTSUP);
if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
return (IPADM_INVALID_ARG);
}
status = i_ipadm_get_addrobj(iph, &ipaddr);
if (status != IPADM_SUCCESS)
return (status);
if (ipaddr.ipadm_flags & IPMGMT_ACTIVE)
return (IPADM_ADDROBJ_EXISTS);
status = i_ipadm_get_db_addr(iph, NULL, aobjname, &addrnvl);
if (status != IPADM_SUCCESS)
return (status);
assert(addrnvl != NULL);
for (nvp = nvlist_next_nvpair(addrnvl, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(addrnvl, nvp)) {
boolean_t set_init = B_FALSE;
if (nvpair_value_nvlist(nvp, &nvl) != 0)
continue;
if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) ||
nvlist_exists(nvl, IPADM_NVP_IPV6ADDR) ||
nvlist_exists(nvl, IPADM_NVP_DHCP)) {
status = i_ipadm_merge_addrprops_from_nvl(addrnvl, nvl,
aobjname);
if (status != IPADM_SUCCESS)
continue;
}
if ((iph->iph_flags & IPH_INIT) == 0) {
iph->iph_flags |= IPH_INIT;
set_init = B_TRUE;
}
status = i_ipadm_init_addrobj(iph, nvl);
if (set_init)
iph->iph_flags &= ~IPH_INIT;
if (status != IPADM_SUCCESS)
break;
}
nvlist_free(addrnvl);
return (status);
}
ipadm_status_t
ipadm_disable_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
{
if (flags & IPADM_OPT_PERSIST)
return (IPADM_NOTSUP);
return (ipadm_delete_addr(iph, aobjname, IPADM_OPT_ACTIVE));
}