diff options
Diffstat (limited to 'usr/src/lib/libdladm/common/linkprop.c')
-rw-r--r-- | usr/src/lib/libdladm/common/linkprop.c | 523 |
1 files changed, 513 insertions, 10 deletions
diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index 8e1ef849e9..e3b9ed386f 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,14 +29,66 @@ #include <strings.h> #include <errno.h> #include <ctype.h> +#include <sys/types.h> #include <sys/stat.h> +#include <sys/dld.h> +#include <sys/zone.h> +#include <fcntl.h> +#include <unistd.h> +#include <libdevinfo.h> +#include <zone.h> #include <libwladm.h> #include <libdladm_impl.h> +#include <dlfcn.h> +#include <link.h> + static dladm_status_t i_dladm_set_prop_db(const char *, const char *, char **, uint_t); static dladm_status_t i_dladm_get_prop_db(const char *, const char *, char **, uint_t *); +static dladm_status_t i_dladm_get_prop_temp(const char *, dladm_prop_type_t, + const char *, char **, uint_t *); +static dladm_status_t i_dladm_set_prop_temp(const char *, const char *, + char **, uint_t, uint_t, char **); +static boolean_t i_dladm_is_prop_temponly(const char *prop_name, + char **); + +typedef struct val_desc { + char *vd_name; + void *vd_val; +} val_desc_t; + +struct prop_desc; + +typedef dladm_status_t pd_getf_t(const char *, char **, uint_t *); +typedef dladm_status_t pd_setf_t(const char *, val_desc_t *, uint_t); +typedef dladm_status_t pd_checkf_t(struct prop_desc *, char **, + uint_t, val_desc_t **); + +static pd_getf_t do_get_zone; +static pd_setf_t do_set_zone; +static pd_checkf_t do_check_zone; + +typedef struct prop_desc { + char *pd_name; + val_desc_t pd_defval; + val_desc_t *pd_modval; + uint_t pd_nmodval; + boolean_t pd_temponly; + pd_setf_t *pd_set; + pd_getf_t *pd_getmod; + pd_getf_t *pd_get; + pd_checkf_t *pd_check; +} prop_desc_t; + +static prop_desc_t prop_table[] = { + { "zone", { "", NULL }, NULL, 0, B_TRUE, + do_set_zone, NULL, + do_get_zone, do_check_zone} +}; + +#define MAX_PROPS (sizeof (prop_table) / sizeof (prop_desc_t)) /* * Convert a wladm_status_t to a dladm_status_t. This is used by wrappers @@ -79,7 +131,7 @@ dladm_wladmstatus2status(wladm_status_t wstatus) dladm_status_t dladm_set_prop(const char *link, const char *prop_name, char **prop_val, - uint_t val_cnt, uint_t flags) + uint_t val_cnt, uint_t flags, char **errprop) { dladm_status_t status = DLADM_STATUS_BADARG; @@ -88,15 +140,27 @@ dladm_set_prop(const char *link, const char *prop_name, char **prop_val, return (DLADM_STATUS_BADARG); if ((flags & DLADM_OPT_TEMP) != 0) { - if (wladm_is_valid(link)) { - status = dladm_wladmstatus2status( - wladm_set_prop(link, prop_name, - prop_val, val_cnt)); + status = i_dladm_set_prop_temp(link, prop_name, prop_val, + val_cnt, flags, errprop); + if (status == DLADM_STATUS_TEMPONLY && + (flags & DLADM_OPT_PERSIST) != 0) + return (DLADM_STATUS_TEMPONLY); + + if (status == DLADM_STATUS_NOTFOUND) { + status = DLADM_STATUS_BADARG; + if (wladm_is_valid(link)) { + status = dladm_wladmstatus2status( + wladm_set_prop(link, prop_name, + prop_val, val_cnt, errprop)); + } } if (status != DLADM_STATUS_OK) return (status); } if ((flags & DLADM_OPT_PERSIST) != 0) { + if (i_dladm_is_prop_temponly(prop_name, errprop)) + return (DLADM_STATUS_TEMPONLY); + status = i_dladm_set_prop_db(link, prop_name, prop_val, val_cnt); } @@ -107,20 +171,35 @@ dladm_status_t dladm_walk_prop(const char *link, void *arg, boolean_t (*func)(void *, const char *)) { + int i; + if (link == NULL || func == NULL) return (DLADM_STATUS_BADARG); + /* For wifi links, show wifi properties first */ if (wladm_is_valid(link)) { - return (dladm_wladmstatus2status( - wladm_walk_prop(link, arg, func))); + dladm_status_t status; + + status = dladm_wladmstatus2status( + wladm_walk_prop(link, arg, func)); + if (status != DLADM_STATUS_OK) + return (status); } - return (DLADM_STATUS_BADARG); + + /* Then show data-link properties if there are any */ + for (i = 0; i < MAX_PROPS; i++) { + if (!func(arg, prop_table[i].pd_name)) + break; + } + return (DLADM_STATUS_OK); } dladm_status_t dladm_get_prop(const char *link, dladm_prop_type_t type, const char *prop_name, char **prop_val, uint_t *val_cntp) { + dladm_status_t status; + if (link == NULL || prop_name == NULL || prop_val == NULL || val_cntp == NULL || *val_cntp == 0) return (DLADM_STATUS_BADARG); @@ -130,6 +209,11 @@ dladm_get_prop(const char *link, dladm_prop_type_t type, prop_val, val_cntp)); } + status = i_dladm_get_prop_temp(link, type, prop_name, + prop_val, val_cntp); + if (status != DLADM_STATUS_NOTFOUND) + return (status); + if (wladm_is_valid(link)) { wladm_prop_type_t wtype; @@ -421,7 +505,7 @@ process_linkprop_init(linkprop_db_state_t *lsp, char *buf, propval[i] = (char *)lvp->lv_name; status = dladm_set_prop(lsp->ls_link, lip->li_name, - propval, valcnt, DLADM_OPT_TEMP); + propval, valcnt, DLADM_OPT_TEMP, NULL); /* * We continue with initializing other properties even @@ -698,3 +782,422 @@ dladm_init_linkprop(void) return (LINKPROP_RW_DB(&state, B_FALSE)); } + +static dladm_status_t +i_dladm_get_zoneid(const char *link, zoneid_t *zidp) +{ + int fd; + dld_hold_vlan_t dhv; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + bzero(&dhv, sizeof (dld_hold_vlan_t)); + (void) strlcpy(dhv.dhv_name, link, IFNAMSIZ); + dhv.dhv_zid = -1; + + if (i_dladm_ioctl(fd, DLDIOCZIDGET, &dhv, sizeof (dhv)) < 0 && + errno != ENOENT) { + dladm_status_t status = dladm_errno2status(errno); + + (void) close(fd); + return (status); + } + + if (errno == ENOENT) + *zidp = GLOBAL_ZONEID; + else + *zidp = dhv.dhv_zid; + + (void) close(fd); + return (DLADM_STATUS_OK); +} + +typedef int (*zone_get_devroot_t)(char *, char *, size_t); + +static int +i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen) +{ + char root[MAXPATHLEN]; + zone_get_devroot_t real_zone_get_devroot; + void *dlhandle; + void *sym; + int ret; + + if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL) + return (-1); + + if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) { + (void) dlclose(dlhandle); + return (-1); + } + + real_zone_get_devroot = (zone_get_devroot_t)sym; + + if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0) + (void) snprintf(dev, devlen, "%s%s", root, "/dev"); + (void) dlclose(dlhandle); + return (ret); +} + +static dladm_status_t +i_dladm_add_deventry(zoneid_t zid, const char *link) +{ + char path[MAXPATHLEN]; + di_prof_t prof = NULL; + char zone_name[ZONENAME_MAX]; + dladm_status_t status; + + if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) + return (dladm_errno2status(errno)); + if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0) + return (dladm_errno2status(errno)); + if (di_prof_init(path, &prof) != 0) + return (dladm_errno2status(errno)); + + status = DLADM_STATUS_OK; + if (di_prof_add_dev(prof, link) != 0) { + status = dladm_errno2status(errno); + goto cleanup; + } + if (di_prof_commit(prof) != 0) + status = dladm_errno2status(errno); +cleanup: + if (prof) + di_prof_fini(prof); + + return (status); +} + +static dladm_status_t +i_dladm_remove_deventry(zoneid_t zid, const char *link) +{ + char path[MAXPATHLEN]; + di_prof_t prof = NULL; + char zone_name[ZONENAME_MAX]; + dladm_status_t status; + + if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) + return (dladm_errno2status(errno)); + if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0) + return (dladm_errno2status(errno)); + if (di_prof_init(path, &prof) != 0) + return (dladm_errno2status(errno)); + + status = DLADM_STATUS_OK; + if (di_prof_add_exclude(prof, link) != 0) { + status = dladm_errno2status(errno); + goto cleanup; + } + if (di_prof_commit(prof) != 0) + status = dladm_errno2status(errno); +cleanup: + if (prof) + di_prof_fini(prof); + + return (status); +} + +static dladm_status_t +do_get_zone(const char *link, char **prop_val, uint_t *val_cnt) +{ + char zone_name[ZONENAME_MAX]; + zoneid_t zid; + dladm_status_t status; + + status = i_dladm_get_zoneid(link, &zid); + if (status != DLADM_STATUS_OK) + return (status); + + *val_cnt = 1; + if (zid != GLOBAL_ZONEID) { + if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) + return (dladm_errno2status(errno)); + + (void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX); + } else { + *prop_val[0] = '\0'; + } + + return (DLADM_STATUS_OK); +} + +static dladm_status_t +do_set_zone(const char *link, val_desc_t *vdp, uint_t val_cnt) +{ + dladm_status_t status; + zoneid_t zid_old, zid_new; + char buff[IF_NAMESIZE + 1]; + struct stat st; + + if (val_cnt != 1) + return (DLADM_STATUS_BADVALCNT); + + status = i_dladm_get_zoneid(link, &zid_old); + if (status != DLADM_STATUS_OK) + return (status); + + /* Do nothing if setting to current value */ + zid_new = (zoneid_t)vdp->vd_val; + if (zid_new == zid_old) + return (DLADM_STATUS_OK); + + /* Do a stat to get the vlan created by MAC, if it's not there */ + (void) strcpy(buff, "/dev/"); + (void) strlcat(buff, link, IF_NAMESIZE); + (void) stat(buff, &st); + + if (zid_old != GLOBAL_ZONEID) { + if (dladm_rele_link(link, GLOBAL_ZONEID, B_TRUE) < 0) + return (dladm_errno2status(errno)); + + if (zone_remove_datalink(zid_old, (char *)link) != 0 && + errno != ENXIO) { + status = dladm_errno2status(errno); + goto rollback1; + } + + status = i_dladm_remove_deventry(zid_old, link); + if (status != DLADM_STATUS_OK) + goto rollback2; + } + + if (zid_new != GLOBAL_ZONEID) { + if (zone_add_datalink(zid_new, (char *)link) != 0) { + status = dladm_errno2status(errno); + goto rollback3; + } + + if (dladm_hold_link(link, zid_new, B_TRUE) < 0) { + (void) zone_remove_datalink(zid_new, (char *)link); + status = dladm_errno2status(errno); + goto rollback3; + } + + status = i_dladm_add_deventry(zid_new, link); + if (status != DLADM_STATUS_OK) { + (void) dladm_rele_link(link, GLOBAL_ZONEID, B_FALSE); + (void) zone_remove_datalink(zid_new, (char *)link); + goto rollback3; + } + } + return (DLADM_STATUS_OK); + +rollback3: + if (zid_old != GLOBAL_ZONEID) + (void) i_dladm_add_deventry(zid_old, link); +rollback2: + if (zid_old != GLOBAL_ZONEID) + (void) zone_add_datalink(zid_old, (char *)link); +rollback1: + (void) dladm_hold_link(link, zid_old, B_FALSE); +cleanexit: + return (status); +} + +/* ARGSUSED */ +static dladm_status_t +do_check_zone(prop_desc_t *pdp, char **prop_val, uint_t val_cnt, + val_desc_t **vdpp) +{ + zoneid_t zid; + val_desc_t *vdp = NULL; + + if (val_cnt != 1) + return (DLADM_STATUS_BADVALCNT); + + if ((zid = getzoneidbyname(*prop_val)) == -1) + return (DLADM_STATUS_BADVAL); + + if (zid != GLOBAL_ZONEID) { + ushort_t flags; + + if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, + sizeof (flags)) < 0) { + return (dladm_errno2status(errno)); + } + + if (!(flags & ZF_NET_EXCL)) { + return (DLADM_STATUS_BADVAL); + } + } + + vdp = malloc(sizeof (val_desc_t)); + if (vdp == NULL) + return (DLADM_STATUS_NOMEM); + + vdp->vd_val = (void *)zid; + *vdpp = vdp; + return (DLADM_STATUS_OK); +} + +static dladm_status_t +i_dladm_get_prop_temp(const char *link, dladm_prop_type_t type, + const char *prop_name, char **prop_val, uint_t *val_cntp) +{ + int i; + dladm_status_t status; + uint_t cnt; + prop_desc_t *pdp; + + if (link == NULL || prop_name == NULL || prop_val == NULL || + val_cntp == NULL || *val_cntp == 0) + return (DLADM_STATUS_BADARG); + + for (i = 0; i < MAX_PROPS; i++) + if (strcasecmp(prop_name, prop_table[i].pd_name) == 0) + break; + + if (i == MAX_PROPS) + return (DLADM_STATUS_NOTFOUND); + + pdp = &prop_table[i]; + status = DLADM_STATUS_OK; + + switch (type) { + case DLADM_PROP_VAL_CURRENT: + status = pdp->pd_get(link, prop_val, val_cntp); + break; + case DLADM_PROP_VAL_DEFAULT: + if (pdp->pd_defval.vd_name == NULL) { + status = DLADM_STATUS_NOTSUP; + break; + } + (void) strcpy(*prop_val, pdp->pd_defval.vd_name); + *val_cntp = 1; + break; + + case DLADM_PROP_VAL_MODIFIABLE: + if (pdp->pd_getmod != NULL) { + status = pdp->pd_getmod(link, prop_val, val_cntp); + break; + } + cnt = pdp->pd_nmodval; + if (cnt == 0) { + status = DLADM_STATUS_NOTSUP; + } else if (cnt > *val_cntp) { + status = DLADM_STATUS_TOOSMALL; + } else { + for (i = 0; i < cnt; i++) { + (void) strcpy(prop_val[i], + pdp->pd_modval[i].vd_name); + } + *val_cntp = cnt; + } + break; + default: + status = DLADM_STATUS_BADARG; + break; + } + + return (status); +} + +static dladm_status_t +i_dladm_set_one_prop_temp(const char *link, prop_desc_t *pdp, char **prop_val, + uint_t val_cnt, uint_t flags) +{ + dladm_status_t status; + val_desc_t *vdp = NULL; + uint_t cnt; + + if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0) + return (DLADM_STATUS_TEMPONLY); + + if (pdp->pd_set == NULL) + return (DLADM_STATUS_PROPRDONLY); + + if (prop_val != NULL) { + if (pdp->pd_check != NULL) + status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp); + else + status = DLADM_STATUS_BADARG; + + if (status != DLADM_STATUS_OK) + return (status); + + cnt = val_cnt; + } else { + if (pdp->pd_defval.vd_name == NULL) + return (DLADM_STATUS_NOTSUP); + + if ((vdp = malloc(sizeof (val_desc_t))) == NULL) + return (DLADM_STATUS_NOMEM); + + (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t)); + cnt = 1; + } + + status = pdp->pd_set(link, vdp, cnt); + + free(vdp); + return (status); +} + +static dladm_status_t +i_dladm_set_prop_temp(const char *link, const char *prop_name, char **prop_val, + uint_t val_cnt, uint_t flags, char **errprop) +{ + int i; + dladm_status_t status = DLADM_STATUS_OK; + boolean_t found = B_FALSE; + + for (i = 0; i < MAX_PROPS; i++) { + prop_desc_t *pdp = &prop_table[i]; + dladm_status_t s; + + if (prop_name != NULL && + (strcasecmp(prop_name, pdp->pd_name) != 0)) + continue; + + found = B_TRUE; + s = i_dladm_set_one_prop_temp(link, pdp, prop_val, val_cnt, + flags); + + if (prop_name != NULL) { + status = s; + break; + } else { + if (s != DLADM_STATUS_OK && + s != DLADM_STATUS_NOTSUP) { + if (errprop != NULL) + *errprop = pdp->pd_name; + status = s; + break; + } + } + } + + if (!found) + status = DLADM_STATUS_NOTFOUND; + + return (status); +} + +static boolean_t +i_dladm_is_prop_temponly(const char *prop_name, char **errprop) +{ + int i; + + for (i = 0; i < MAX_PROPS; i++) { + prop_desc_t *pdp = &prop_table[i]; + + if (prop_name != NULL && + (strcasecmp(prop_name, pdp->pd_name) != 0)) + continue; + + if (errprop != NULL) + *errprop = pdp->pd_name; + + if (pdp->pd_temponly) + return (B_TRUE); + } + + return (B_FALSE); +} + +boolean_t +dladm_is_prop_temponly(const char *prop_name, char **errprop) +{ + return (i_dladm_is_prop_temponly(prop_name, errprop)); +} |