#include <libdladm_impl.h>
#include <libdllink.h>
#include <libdloverlay.h>
#include <sys/dld.h>
#include <sys/overlay.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <libscf.h>
#include <libvarpd_client.h>
#define VARPD_PROPERTY_NAME "varpd/id"
#define VARPD_SERVICE "network/varpd:default"
static const char *dladm_overlay_doorpath = "/var/run/varpd/varpd.door";
static boolean_t
varpd_svc_isonline(void)
{
boolean_t isonline = B_FALSE;
char *s;
if ((s = smf_get_state(VARPD_SERVICE)) != NULL) {
if (strcmp(s, SCF_STATE_STRING_ONLINE) == 0)
isonline = B_TRUE;
free(s);
}
return (isonline);
}
#define MAX_WAIT_TIME 15
static dladm_status_t
varpd_enable_service(void)
{
uint_t i;
if (varpd_svc_isonline())
return (DLADM_STATUS_OK);
if (smf_enable_instance(VARPD_SERVICE, 0) == -1) {
if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
return (DLADM_STATUS_DENIED);
return (DLADM_STATUS_NOTFOUND);
}
for (i = 0; i < MAX_WAIT_TIME; i++) {
if (varpd_svc_isonline())
return (DLADM_STATUS_OK);
(void) sleep(1);
}
return (DLADM_STATUS_FAILED);
}
static int
dladm_overlay_count_cb(dladm_handle_t handle __unused,
datalink_id_t linkid __unused, void *arg)
{
(*(uint32_t *)arg)++;
return (DLADM_WALK_CONTINUE);
}
static void
varpd_disable_service_when_no_overlays(dladm_handle_t handle)
{
uint32_t cnt = 0;
(void) dladm_walk_datalink_id(dladm_overlay_count_cb, handle,
&cnt, DATALINK_CLASS_OVERLAY, DATALINK_ANY_MEDIATYPE,
DLADM_OPT_ACTIVE);
if (cnt == 0)
(void) smf_disable_instance(VARPD_SERVICE, 0);
}
typedef struct dladm_overlay_propinfo {
boolean_t dop_isvarpd;
union {
overlay_ioc_propinfo_t *dop_overlay;
varpd_client_prop_handle_t *dop_varpd;
} dop_un;
} dladm_overlay_propinfo_t;
dladm_status_t
dladm_overlay_prop_info(dladm_overlay_propinfo_handle_t phdl,
const char **namep, uint_t *typep, uint_t *protp, const void **defp,
uint32_t *sizep, const mac_propval_range_t **possp)
{
dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)phdl;
overlay_ioc_propinfo_t *oinfop = infop->dop_un.dop_overlay;
if (infop->dop_isvarpd == B_FALSE) {
if (namep != NULL)
*namep = oinfop->oipi_name;
if (typep != NULL)
*typep = oinfop->oipi_type;
if (protp != NULL)
*protp = oinfop->oipi_prot;
if (defp != NULL)
*defp = oinfop->oipi_default;
if (sizep != NULL)
*sizep = oinfop->oipi_defsize;
if (possp != NULL) {
*possp = (const mac_propval_range_t *)oinfop->oipi_poss;
}
} else {
int ret;
ret = libvarpd_c_prop_info(infop->dop_un.dop_varpd, namep,
typep, protp, defp, sizep, possp);
if (ret != 0)
return (dladm_errno2status(ret));
}
return (DLADM_STATUS_OK);
}
static dladm_status_t
dladm_overlay_parse_prop(overlay_prop_type_t type, void *buf, uint32_t *sizep,
const char *val)
{
int ret;
int64_t ival;
uint64_t uval;
char *eptr;
struct in6_addr ipv6;
struct in_addr ip;
switch (type) {
case OVERLAY_PROP_T_INT:
errno = 0;
ival = strtol(val, &eptr, 10);
if ((ival == 0 && errno == EINVAL) ||
((ival == LONG_MAX || ival == LONG_MIN) &&
errno == ERANGE))
return (DLADM_STATUS_BADARG);
bcopy(&ival, buf, sizeof (int64_t));
*sizep = sizeof (int64_t);
break;
case OVERLAY_PROP_T_UINT:
errno = 0;
uval = strtol(val, &eptr, 10);
if ((uval == 0 && errno == EINVAL) ||
(uval == ULONG_MAX && errno == ERANGE))
return (DLADM_STATUS_BADARG);
bcopy(&uval, buf, sizeof (uint64_t));
*sizep = sizeof (uint64_t);
break;
case OVERLAY_PROP_T_STRING:
ret = strlcpy((char *)buf, val, OVERLAY_PROP_SIZEMAX);
if (ret >= OVERLAY_PROP_SIZEMAX)
return (DLADM_STATUS_BADARG);
*sizep = ret + 1;
break;
case OVERLAY_PROP_T_IP:
if (inet_pton(AF_INET6, val, &ipv6) != 1) {
if (inet_pton(AF_INET, val, &ip) != 1)
return (DLADM_STATUS_BADARG);
IN6_INADDR_TO_V4MAPPED(&ip, &ipv6);
}
bcopy(&ipv6, buf, sizeof (struct in6_addr));
*sizep = sizeof (struct in6_addr);
break;
default:
abort();
}
return (DLADM_STATUS_OK);
}
static dladm_status_t
i_dladm_overlay_setprop_db(dladm_handle_t handle, datalink_id_t linkid,
const char *name, char *const *valp, uint_t cnt)
{
dladm_conf_t conf;
dladm_status_t status;
if (linkid == DATALINK_INVALID_LINKID ||
name == NULL || valp == NULL || cnt != 1) {
return (DLADM_STATUS_BADARG);
}
status = dladm_open_conf(handle, linkid, &conf);
if (status != DLADM_STATUS_OK)
return (status);
status = dladm_set_conf_field(handle, conf, name, DLADM_TYPE_STR,
valp[0]);
if (status == DLADM_STATUS_OK)
status = dladm_write_conf(handle, conf);
dladm_destroy_conf(handle, conf);
return (status);
}
static dladm_status_t
dladm_overlay_varpd_setprop(dladm_handle_t handle __unused,
varpd_client_handle_t *chdl, uint64_t inst, datalink_id_t linkid __unused,
const char *name, char *const *valp, uint_t cnt __unused)
{
int ret;
uint32_t size;
uint8_t buf[LIBVARPD_PROP_SIZEMAX];
varpd_client_prop_handle_t *phdl;
uint_t type;
dladm_status_t status;
if ((ret = libvarpd_c_prop_handle_alloc(chdl, inst, &phdl)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_prop_info_fill_by_name(phdl, name)) != 0) {
libvarpd_c_prop_handle_free(phdl);
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_prop_info(phdl, NULL, &type, NULL, NULL, NULL,
NULL)) != 0) {
libvarpd_c_prop_handle_free(phdl);
return (dladm_errno2status(ret));
}
if ((status = dladm_overlay_parse_prop(type, buf, &size, valp[0])) !=
DLADM_STATUS_OK) {
libvarpd_c_prop_handle_free(phdl);
return (status);
}
status = DLADM_STATUS_OK;
ret = libvarpd_c_prop_set(phdl, buf, size);
libvarpd_c_prop_handle_free(phdl);
if (ret != 0)
status = dladm_errno2status(ret);
if (status != DLADM_STATUS_OK)
return (status);
return (status);
}
static dladm_status_t
dladm_overlay_setprop(dladm_handle_t handle, datalink_id_t linkid,
const char *name, char *const *valp, uint_t cnt)
{
int ret;
dladm_status_t status;
overlay_ioc_propinfo_t info;
overlay_ioc_prop_t prop;
if (linkid == DATALINK_INVALID_LINKID ||
name == NULL || valp == NULL || cnt != 1)
return (DLADM_STATUS_BADARG);
bzero(&info, sizeof (overlay_ioc_propinfo_t));
info.oipi_linkid = linkid;
info.oipi_id = -1;
if (strlcpy(info.oipi_name, name, OVERLAY_PROP_NAMELEN) >=
OVERLAY_PROP_NAMELEN)
return (DLADM_STATUS_BADARG);
status = DLADM_STATUS_OK;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &info);
if (ret != 0)
status = dladm_errno2status(errno);
if (status != DLADM_STATUS_OK)
return (status);
prop.oip_linkid = linkid;
prop.oip_id = info.oipi_id;
prop.oip_name[0] = '\0';
if ((status = dladm_overlay_parse_prop(info.oipi_type, prop.oip_value,
&prop.oip_size, valp[0])) != DLADM_STATUS_OK)
return (status);
status = DLADM_STATUS_OK;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_SETPROP, &prop);
if (ret != 0)
status = dladm_errno2status(errno);
if (status != DLADM_STATUS_OK)
return (status);
return (status);
}
static int
dladm_overlay_activate_cb(dladm_handle_t handle, datalink_id_t linkid,
dladm_overlay_propinfo_handle_t phdl, void *arg)
{
dladm_status_t status;
uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
uint_t prot;
size_t size = sizeof (buf);
const char *name;
dladm_errlist_t *errs = arg;
if ((status = dladm_overlay_prop_info(phdl, &name, NULL, &prot, NULL,
NULL, NULL)) != DLADM_STATUS_OK)
return (status);
if ((prot & OVERLAY_PROP_PERM_REQ) == 0)
return (DLADM_WALK_CONTINUE);
if (dladm_overlay_get_prop(handle, linkid, phdl, buf, &size) !=
DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
if (size == 0)
(void) dladm_errlist_append(errs, "unset required property: %s",
name);
return (DLADM_WALK_CONTINUE);
}
dladm_status_t
dladm_overlay_delete(dladm_handle_t handle, datalink_id_t linkid,
uint32_t flags)
{
datalink_class_t class;
overlay_ioc_delete_t oid;
varpd_client_handle_t *chdl;
int ret;
uint64_t varpdid;
if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
NULL, 0) != DLADM_STATUS_OK) {
return (DLADM_STATUS_BADARG);
}
if (class != DATALINK_CLASS_OVERLAY)
return (DLADM_STATUS_BADARG);
oid.oid_linkid = linkid;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_DELETE, &oid);
if (ret != 0 && errno != ENOENT) {
return (dladm_errno2status(errno));
}
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) {
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
if (ret == ENOENT) {
goto finish;
}
(void) libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
ret = libvarpd_c_instance_destroy(chdl, varpdid);
finish:
(void) libvarpd_c_destroy(chdl);
if ((flags & DLADM_OPT_PERSIST) != 0) {
(void) dladm_remove_conf(handle, linkid);
(void) dladm_destroy_datalink_id(handle, linkid, flags);
}
(void) varpd_disable_service_when_no_overlays(handle);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_get_prop(dladm_handle_t handle, datalink_id_t linkid,
dladm_overlay_propinfo_handle_t infohdl, void *buf, size_t *sizep)
{
int ret;
overlay_ioc_prop_t oip;
dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)infohdl;
if (*sizep < DLADM_OVERLAY_PROP_SIZEMAX)
return (dladm_errno2status(ERANGE));
if (infop->dop_isvarpd == B_FALSE) {
bzero(&oip, sizeof (overlay_ioc_prop_t));
oip.oip_linkid = linkid;
oip.oip_id = infop->dop_un.dop_overlay->oipi_id;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_GETPROP, &oip);
if (ret != 0)
return (dladm_errno2status(errno));
bcopy(oip.oip_value, buf, DLADM_OVERLAY_PROP_SIZEMAX);
*sizep = oip.oip_size;
} else {
uint32_t size = *sizep;
ret = libvarpd_c_prop_get(infop->dop_un.dop_varpd, buf, &size);
if (ret != 0)
return (dladm_errno2status(errno));
*sizep = size;
}
return (DLADM_STATUS_OK);
}
static dladm_status_t
dladm_overlay_walk_varpd_prop(dladm_handle_t handle, datalink_id_t linkid,
uint64_t varpdid, dladm_overlay_prop_f func, void *arg)
{
int ret;
varpd_client_handle_t *chdl;
varpd_client_prop_handle_t *phdl;
uint_t i, nprops;
dladm_status_t status;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_prop_handle_alloc(chdl, varpdid, &phdl)) != 0) {
(void) libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_prop_nprops(chdl, varpdid, &nprops)) != 0) {
libvarpd_c_prop_handle_free(phdl);
(void) libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
status = DLADM_STATUS_OK;
for (i = 0; i < nprops; i++) {
dladm_overlay_propinfo_t dop;
bzero(&dop, sizeof (dop));
dop.dop_isvarpd = B_TRUE;
dop.dop_un.dop_varpd = phdl;
if ((ret = libvarpd_c_prop_info_fill(phdl, i)) != 0) {
status = dladm_errno2status(ret);
break;
}
ret = func(handle, linkid,
(dladm_overlay_propinfo_handle_t)&dop, arg);
if (ret == DLADM_WALK_TERMINATE)
break;
}
libvarpd_c_prop_handle_free(phdl);
libvarpd_c_destroy(chdl);
return (status);
}
dladm_status_t
dladm_overlay_walk_prop(dladm_handle_t handle, datalink_id_t linkid,
dladm_overlay_prop_f func, void *arg, dladm_errlist_t *errs)
{
int i, ret;
char buf[MAXLINKNAMELEN];
char errmsg[DLADM_STRSIZE];
datalink_class_t class;
dladm_status_t info_status;
overlay_ioc_nprops_t oin;
overlay_ioc_propinfo_t oipi;
dladm_overlay_propinfo_t dop;
uint64_t varpdid = UINT64_MAX;
if ((info_status = dladm_datalink_id2info(handle, linkid, NULL, &class,
NULL, buf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to get info for "
"datalink id %u: %s",
linkid, dladm_status2str(info_status, errmsg));
return (DLADM_STATUS_BADARG);
}
if (class != DATALINK_CLASS_OVERLAY) {
(void) dladm_errlist_append(errs, "%s is not an overlay", buf);
return (DLADM_STATUS_BADARG);
}
bzero(&oin, sizeof (overlay_ioc_nprops_t));
oin.oipn_linkid = linkid;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_NPROPS, &oin);
if (ret != 0) {
(void) dladm_errlist_append(errs, "failed to get "
"overlay properties for overlay %s: %s",
buf, strerror(errno));
return (dladm_errno2status(errno));
}
for (i = 0; i < oin.oipn_nprops; i++) {
bzero(&dop, sizeof (dladm_overlay_propinfo_t));
bzero(&oipi, sizeof (overlay_ioc_propinfo_t));
oipi.oipi_linkid = linkid;
oipi.oipi_id = i;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &oipi);
if (ret != 0) {
(void) dladm_errlist_append(errs, "failed to get "
"propinfo for overlay %s, property %d: %s",
buf, i, strerror(errno));
return (dladm_errno2status(errno));
}
dop.dop_isvarpd = B_FALSE;
dop.dop_un.dop_overlay = &oipi;
ret = func(handle, linkid,
(dladm_overlay_propinfo_handle_t)&dop, arg);
if (ret == DLADM_WALK_TERMINATE)
break;
if (strcmp(oipi.oipi_name, VARPD_PROPERTY_NAME) == 0) {
uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX];
size_t bufsize = sizeof (buf);
uint64_t *vp;
if (dladm_overlay_get_prop(handle, linkid,
(dladm_overlay_propinfo_handle_t)&dop, buf,
&bufsize) != DLADM_STATUS_OK)
continue;
vp = (uint64_t *)buf;
varpdid = *vp;
}
}
if (varpdid == UINT64_MAX)
return (DLADM_STATUS_OK);
ret = dladm_overlay_walk_varpd_prop(handle, linkid, varpdid, func,
arg);
if (ret != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to get varpd props for "
"overlay %s, varpd id %llu: %s",
buf, varpdid, dladm_status2str(info_status, errmsg));
}
return (ret);
}
static dladm_status_t
dladm_overlay_persist_config(dladm_handle_t handle, dladm_overlay_attr_t *attr)
{
dladm_conf_t conf;
dladm_status_t status;
if ((status = dladm_create_conf(handle, attr->oa_name, attr->oa_linkid,
DATALINK_CLASS_OVERLAY, DL_ETHER, &conf)) != DLADM_STATUS_OK) {
return (status);
}
status = dladm_set_conf_field(handle, conf, FVNETID,
DLADM_TYPE_UINT64, &attr->oa_vid);
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_set_conf_field(handle, conf, FENCAP,
DLADM_TYPE_STR, attr->oa_encap);
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_set_conf_field(handle, conf, FSEARCH,
DLADM_TYPE_STR, attr->oa_search);
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_write_conf(handle, conf);
done:
dladm_destroy_conf(handle, conf);
return (status);
}
static dladm_status_t
i_dladm_overlay_create_sys(dladm_handle_t handle, dladm_overlay_attr_t *attr)
{
overlay_ioc_create_t oic;
dladm_status_t status;
int ret;
bzero(&oic, sizeof (oic));
oic.oic_linkid = attr->oa_linkid;
oic.oic_vnetid = attr->oa_vid;
(void) strlcpy(oic.oic_encap, attr->oa_encap, MAXLINKNAMELEN);
status = DLADM_STATUS_OK;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_CREATE, &oic);
if (ret != 0) {
status = dladm_errno2status(errno);
}
return (status);
}
static dladm_status_t
i_dladm_overlay_commit_sys(dladm_handle_t handle, dladm_overlay_attr_t *attr,
dladm_arg_list_t *props, dladm_errlist_t *errs)
{
overlay_ioc_activate_t oia;
varpd_client_handle_t *vch;
dladm_status_t status;
size_t slen;
uint64_t id;
int ret;
uint_t i;
slen = strlen(attr->oa_search);
for (i = 0; props != NULL && i < props->al_count; i++) {
dladm_arg_info_t *aip = &props->al_info[i];
if (strncmp(aip->ai_name, attr->oa_search, slen) == 0 &&
aip->ai_name[slen] == '/')
continue;
status = dladm_overlay_setprop(handle, attr->oa_linkid,
aip->ai_name, aip->ai_val, aip->ai_count);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to set property %s", aip->ai_name);
return (status);
}
if (attr->oa_flags & DLADM_OPT_PERSIST) {
status = i_dladm_overlay_setprop_db(handle,
attr->oa_linkid, aip->ai_name, aip->ai_val,
aip->ai_count);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to persistently set property %s",
aip->ai_name);
return (status);
}
}
}
if ((ret = libvarpd_c_create(&vch, dladm_overlay_doorpath)) != 0) {
(void) dladm_errlist_append(errs,
"failed to create libvarpd handle: %s", strerror(ret));
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_instance_create(vch, attr->oa_linkid,
attr->oa_search, &id)) != 0) {
(void) dladm_errlist_append(errs,
"failed to create varpd instance: %s", strerror(ret));
libvarpd_c_destroy(vch);
return (dladm_errno2status(ret));
}
for (i = 0; props != NULL && i < props->al_count; i++) {
dladm_arg_info_t *aip = &props->al_info[i];
if (strncmp(aip->ai_name, attr->oa_search, slen) != 0 ||
aip->ai_name[slen] != '/')
continue;
ret = dladm_overlay_varpd_setprop(handle, vch, id,
attr->oa_linkid, aip->ai_name, aip->ai_val, aip->ai_count);
if (ret != 0) {
(void) dladm_errlist_append(errs,
"failed to set varpd prop: %s\n", aip->ai_name);
(void) libvarpd_c_instance_destroy(vch, id);
libvarpd_c_destroy(vch);
return (dladm_errno2status(ret));
}
if (attr->oa_flags & DLADM_OPT_PERSIST) {
status = i_dladm_overlay_setprop_db(handle,
attr->oa_linkid, aip->ai_name, aip->ai_val,
aip->ai_count);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to "
"persistently set varpd prop: %s\n",
aip->ai_name);
(void) libvarpd_c_instance_destroy(vch, id);
libvarpd_c_destroy(vch);
return (status);
}
}
}
if ((ret = libvarpd_c_instance_activate(vch, id)) != 0) {
(void) dladm_errlist_append(errs,
"failed to activate varpd instance: %s", strerror(ret));
(void) dladm_overlay_walk_varpd_prop(handle, attr->oa_linkid,
id, dladm_overlay_activate_cb, errs);
(void) libvarpd_c_instance_destroy(vch, id);
libvarpd_c_destroy(vch);
return (dladm_errno2status(ret));
}
bzero(&oia, sizeof (oia));
oia.oia_linkid = attr->oa_linkid;
status = DLADM_STATUS_OK;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_ACTIVATE, &oia);
if (ret != 0) {
ret = errno;
(void) dladm_errlist_append(errs, "failed to activate "
"device: %s", strerror(ret));
(void) libvarpd_c_instance_destroy(vch, id);
(void) dladm_overlay_walk_prop(handle, attr->oa_linkid,
dladm_overlay_activate_cb, errs, errs);
status = dladm_errno2status(ret);
}
libvarpd_c_destroy(vch);
return (status);
}
dladm_status_t
dladm_overlay_create(dladm_handle_t handle, const char *name,
const char *encap, const char *search, uint64_t vid,
dladm_arg_list_t *props, dladm_errlist_t *errs, uint32_t flags)
{
dladm_status_t status;
datalink_id_t linkid;
dladm_overlay_attr_t attr;
char errmsg[DLADM_STRSIZE];
if (strlcpy(attr.oa_name, name, sizeof (attr.oa_name)) >=
sizeof (attr.oa_name)) {
return (DLADM_STATUS_BADARG);
}
if (strlcpy(attr.oa_encap, encap, sizeof (attr.oa_encap)) >=
sizeof (attr.oa_encap)) {
return (DLADM_STATUS_BADARG);
}
if (strlcpy(attr.oa_search, search, sizeof (attr.oa_search)) >=
sizeof (attr.oa_search)) {
return (DLADM_STATUS_BADARG);
}
status = varpd_enable_service();
if (status != DLADM_STATUS_OK)
return (status);
status = dladm_create_datalink_id(handle, name, DATALINK_CLASS_OVERLAY,
DL_ETHER, flags, &linkid);
if (status != DLADM_STATUS_OK)
return (status);
attr.oa_linkid = linkid;
attr.oa_vid = vid;
attr.oa_flags = flags;
status = i_dladm_overlay_create_sys(handle, &attr);
if (status != DLADM_STATUS_OK) {
(void) dladm_destroy_datalink_id(handle, linkid, flags);
return (status);
}
if ((flags & DLADM_OPT_PERSIST) != 0) {
status = dladm_overlay_persist_config(handle, &attr);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to create "
"persistent configuration for %s: %s",
attr.oa_name, dladm_status2str(status, errmsg));
}
}
if (status == DLADM_STATUS_OK)
status = i_dladm_overlay_commit_sys(handle, &attr, props, errs);
if (status != DLADM_STATUS_OK) {
(void) dladm_overlay_delete(handle, linkid, flags);
(void) dladm_destroy_datalink_id(handle, linkid, flags);
}
return (status);
}
typedef struct overlay_walk_cb {
dladm_handle_t owc_handle;
datalink_id_t owc_linkid;
void *owc_arg;
dladm_overlay_cache_f owc_func;
uint_t owc_mode;
uint_t owc_dest;
} overlay_walk_cb_t;
static int
dladm_overlay_walk_cache_cb(varpd_client_handle_t *chdl __unused,
uint64_t varpdid __unused, const struct ether_addr *key,
const varpd_client_cache_entry_t *entry, void *arg)
{
overlay_walk_cb_t *owc = arg;
dladm_overlay_point_t point;
bzero(&point, sizeof (dladm_overlay_point_t));
point.dop_dest = owc->owc_dest;
point.dop_mac = entry->vcp_mac;
point.dop_flags = entry->vcp_flags;
point.dop_ip = entry->vcp_ip;
point.dop_port = entry->vcp_port;
if (owc->owc_mode == OVERLAY_TARGET_POINT)
point.dop_flags |= DLADM_OVERLAY_F_DEFAULT;
if (owc->owc_func(owc->owc_handle, owc->owc_linkid, key, &point,
owc->owc_arg) == DLADM_WALK_TERMINATE)
return (1);
return (0);
}
dladm_status_t
dladm_overlay_walk_cache(dladm_handle_t handle, datalink_id_t linkid,
dladm_overlay_cache_f func, void *arg)
{
int ret;
uint_t mode, dest;
uint64_t varpdid;
varpd_client_handle_t *chdl;
overlay_walk_cb_t cbarg;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
&dest, &mode)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
cbarg.owc_handle = handle;
cbarg.owc_linkid = linkid;
cbarg.owc_arg = arg;
cbarg.owc_func = func;
cbarg.owc_dest = dest;
cbarg.owc_mode = mode;
ret = libvarpd_c_instance_cache_walk(chdl, varpdid,
dladm_overlay_walk_cache_cb, &cbarg);
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_cache_flush(dladm_handle_t handle __unused, datalink_id_t linkid)
{
int ret;
uint64_t varpdid;
varpd_client_handle_t *chdl;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
ret = libvarpd_c_instance_cache_flush(chdl, varpdid);
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_cache_delete(dladm_handle_t handle __unused, datalink_id_t linkid,
const struct ether_addr *key)
{
int ret;
uint64_t varpdid;
varpd_client_handle_t *chdl;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
ret = libvarpd_c_instance_cache_delete(chdl, varpdid, key);
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_cache_set(dladm_handle_t handle __unused, datalink_id_t linkid,
const struct ether_addr *key, char *val)
{
int ret;
uint_t dest;
uint64_t varpdid;
char *ip, *port = NULL;
varpd_client_handle_t *chdl;
varpd_client_cache_entry_t vcp;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
&dest, NULL)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
bzero(&vcp, sizeof (varpd_client_cache_entry_t));
if (strcasecmp(val, "drop") == 0) {
vcp.vcp_flags = OVERLAY_TARGET_CACHE_DROP;
goto send;
}
if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
if (ether_aton_r(val, &vcp.vcp_mac) == NULL) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(EINVAL));
}
}
if (dest & OVERLAY_PLUGIN_D_IP) {
if (dest & OVERLAY_PLUGIN_D_ETHERNET) {
if ((ip = strchr(val, ',')) == NULL) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
ip++;
} else {
ip = val;
}
if (dest & OVERLAY_PLUGIN_D_PORT) {
if ((port = strchr(val, ':')) == NULL) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
*port = '\0';
port++;
}
ret = inet_pton(AF_INET6, ip, &vcp.vcp_ip);
if (ret == -1)
abort();
if (ret == 0) {
struct in_addr v4;
ret = inet_pton(AF_INET, ip, &v4);
if (ret == -1)
abort();
if (ret == 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
IN6_INADDR_TO_V4MAPPED(&v4, &vcp.vcp_ip);
}
}
if (dest & OVERLAY_PLUGIN_D_PORT) {
char *eptr;
unsigned long l;
if (port == NULL && (dest & OVERLAY_PLUGIN_D_ETHERNET)) {
if ((port = strchr(val, ',')) == NULL) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(EINVAL));
}
} else if (port == NULL)
port = val;
errno = 0;
l = strtoul(port, &eptr, 10);
if (errno != 0 || *eptr != '\0') {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(EINVAL));
}
if (l == 0 || l > UINT16_MAX) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(EINVAL));
}
vcp.vcp_port = l;
}
send:
ret = libvarpd_c_instance_cache_set(chdl, varpdid, key, &vcp);
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_cache_get(dladm_handle_t handle __unused, datalink_id_t linkid,
const struct ether_addr *key, dladm_overlay_point_t *point)
{
int ret;
uint_t dest, mode;
uint64_t varpdid;
varpd_client_handle_t *chdl;
varpd_client_cache_entry_t entry;
if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0)
return (dladm_errno2status(ret));
if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid,
&dest, &mode)) != 0) {
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
ret = libvarpd_c_instance_cache_get(chdl, varpdid, key, &entry);
if (ret == 0) {
point->dop_dest = dest;
point->dop_mac = entry.vcp_mac;
point->dop_flags = entry.vcp_flags;
point->dop_ip = entry.vcp_ip;
point->dop_port = entry.vcp_port;
if (mode == OVERLAY_TARGET_POINT)
point->dop_flags |= DLADM_OVERLAY_F_DEFAULT;
}
libvarpd_c_destroy(chdl);
return (dladm_errno2status(ret));
}
dladm_status_t
dladm_overlay_status(dladm_handle_t handle, datalink_id_t linkid,
dladm_overlay_status_f func, void *arg)
{
int ret;
dladm_status_t status;
overlay_ioc_status_t ois;
dladm_overlay_status_t dos;
ois.ois_linkid = linkid;
status = DLADM_STATUS_OK;
ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_STATUS, &ois);
if (ret != 0)
status = dladm_errno2status(errno);
if (status != DLADM_STATUS_OK)
return (status);
dos.dos_degraded = ois.ois_status == OVERLAY_I_DEGRADED ? B_TRUE :
B_FALSE;
(void) strlcpy(dos.dos_fmamsg, ois.ois_message,
sizeof (dos.dos_fmamsg));
func(handle, linkid, &dos, arg);
return (DLADM_STATUS_OK);
}
static void
i_dladm_overlay_props_free(dladm_handle_t handle __unused,
dladm_arg_list_t *props)
{
uint_t i, j;
for (i = 0; props != NULL && i < props->al_count; i++) {
dladm_arg_info_t *aip = &props->al_info[i];
free((char *)aip->ai_name);
for (j = 0; j < aip->ai_count; j++)
free(aip->ai_val[j]);
}
free(props);
}
static dladm_status_t
i_dladm_overlay_fetch_persistent_config(dladm_handle_t handle,
datalink_id_t linkid, dladm_overlay_attr_t *attrp,
dladm_arg_list_t **props)
{
dladm_conf_t conf;
dladm_status_t status;
char attr[MAXLINKATTRLEN], last_attr[MAXLINKATTRLEN];
char attrval[OVERLAY_PROP_SIZEMAX];
size_t attrsz;
dladm_arg_list_t *list = NULL;
*props = NULL;
if ((status = dladm_getsnap_conf(handle, linkid, &conf)) !=
DLADM_STATUS_OK) {
return (status);
}
attrp->oa_linkid = linkid;
status = dladm_get_conf_field(handle, conf, FVNETID, &attrp->oa_vid,
sizeof (attrp->oa_vid));
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_get_conf_field(handle, conf, FENCAP,
attrp->oa_encap, sizeof (attrp->oa_encap));
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_get_conf_field(handle, conf, FSEARCH,
attrp->oa_search, sizeof (attrp->oa_search));
if (status != DLADM_STATUS_OK)
goto done;
list = calloc(1, sizeof (dladm_arg_list_t));
*last_attr = '\0';
while (dladm_getnext_conf_linkprop(handle, conf, last_attr,
attr, attrval, sizeof (attrval), &attrsz) == DLADM_STATUS_OK) {
dladm_arg_info_t *aip;
(void) strlcpy(last_attr, attr, sizeof (last_attr));
if (strchr(attr, '/') == NULL)
continue;
aip = &list->al_info[list->al_count];
bzero(aip, sizeof (dladm_arg_info_t));
if ((aip->ai_name = strdup(attr)) == NULL) {
status = dladm_errno2status(errno);
break;
}
if ((aip->ai_val[0] = strdup(attrval)) == NULL) {
status = dladm_errno2status(errno);
break;
}
aip->ai_count = 1;
list->al_count++;
if (list->al_count >= DLADM_MAX_ARG_CNT) {
status = DLADM_STATUS_TOOMANYELEMENTS;
break;
}
}
done:
dladm_destroy_conf(handle, conf);
if (status != DLADM_STATUS_OK) {
if (list != NULL)
i_dladm_overlay_props_free(handle, list);
return (status);
}
*props = list;
return (DLADM_STATUS_OK);
}
typedef struct dladm_overlay_up_arg_s {
dladm_errlist_t *errlist;
} dladm_overlay_up_arg_t;
static int
i_dladm_overlay_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
{
dladm_overlay_up_arg_t *argp = arg;
dladm_errlist_t *errs = argp->errlist;
datalink_class_t class;
dladm_status_t status;
dladm_overlay_attr_t attr;
dladm_arg_list_t *props;
char errmsg[DLADM_STRSIZE];
bzero(&attr, sizeof (attr));
status = dladm_datalink_id2info(handle, linkid, NULL, &class,
NULL, attr.oa_name, sizeof (attr.oa_name));
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to get info for "
"datalink id %u: %s",
linkid, dladm_status2str(status, errmsg));
return (DLADM_STATUS_BADARG);
}
if (class != DATALINK_CLASS_OVERLAY) {
(void) dladm_errlist_append(errs, "%s is not an overlay",
attr.oa_name);
return (DLADM_STATUS_BADARG);
}
status = varpd_enable_service();
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to enable svc:/%s",
VARPD_SERVICE);
return (DLADM_WALK_TERMINATE);
}
status = i_dladm_overlay_fetch_persistent_config(handle, linkid,
&attr, &props);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs, "failed to retrieve "
"persistent configuration for %s: %s",
attr.oa_name, dladm_status2str(status, errmsg));
return (DLADM_WALK_CONTINUE);
}
status = i_dladm_overlay_create_sys(handle, &attr);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to create overlay device %s: %s",
attr.oa_name, dladm_status2str(status, errmsg));
goto out;
}
status = i_dladm_overlay_commit_sys(handle, &attr, props, errs);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to set properties for overlay device %s: %s",
attr.oa_name, dladm_status2str(status, errmsg));
dladm_overlay_delete(handle, linkid, 0);
goto out;
}
status = dladm_up_datalink_id(handle, linkid);
if (status != DLADM_STATUS_OK) {
(void) dladm_errlist_append(errs,
"failed to bring datalink up for overlay device %s: %s",
attr.oa_name, dladm_status2str(status, errmsg));
dladm_overlay_delete(handle, linkid, 0);
goto out;
}
out:
i_dladm_overlay_props_free(handle, props);
return (DLADM_WALK_CONTINUE);
}
dladm_status_t
dladm_overlay_up(dladm_handle_t handle, datalink_id_t linkid,
dladm_errlist_t *errs)
{
dladm_overlay_up_arg_t overlay_arg = {
.errlist = errs
};
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(i_dladm_overlay_up, handle,
&overlay_arg, DATALINK_CLASS_OVERLAY,
DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
} else {
(void) i_dladm_overlay_up(handle, linkid, &overlay_arg);
}
if (dladm_errlist_count(errs) == 0)
return (DLADM_STATUS_OK);
return (DLADM_STATUS_FAILED);
}