diff options
Diffstat (limited to 'usr/src/lib/libdladm/common/libdllink.c')
-rw-r--r-- | usr/src/lib/libdladm/common/libdllink.c | 915 |
1 files changed, 726 insertions, 189 deletions
diff --git a/usr/src/lib/libdladm/common/libdllink.c b/usr/src/lib/libdladm/common/libdllink.c index 45f4641588..bd046acda9 100644 --- a/usr/src/lib/libdladm/common/libdllink.c +++ b/usr/src/lib/libdladm/common/libdllink.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,213 +29,85 @@ #include <unistd.h> #include <errno.h> #include <fcntl.h> +#include <assert.h> +#include <ctype.h> #include <strings.h> #include <sys/stat.h> #include <sys/dld.h> +#include <sys/vlan.h> +#include <librcm.h> #include <libdlpi.h> #include <libdevinfo.h> +#include <libdlaggr.h> +#include <libdlvlan.h> #include <libdllink.h> +#include <libdlmgmt.h> #include <libdladm_impl.h> -typedef struct dladm_dev { - char dd_name[IFNAMSIZ]; - struct dladm_dev *dd_next; -} dladm_dev_t; - -typedef struct dladm_walk { - dladm_dev_t *dw_dev_list; -} dladm_walk_t; - /* * Return the attributes of the specified datalink from the DLD driver. */ -static int -i_dladm_info(int fd, const char *name, dladm_attr_t *dap) +static dladm_status_t +i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap) { dld_ioc_attr_t dia; - if (strlen(name) >= IFNAMSIZ) { - errno = EINVAL; - return (-1); - } - - (void) strlcpy(dia.dia_name, name, IFNAMSIZ); + dia.dia_linkid = linkid; - if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0) - return (-1); + if (i_dladm_ioctl(fd, DLDIOC_ATTR, &dia, sizeof (dia)) < 0) + return (dladm_errno2status(errno)); - (void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN); dap->da_max_sdu = dia.dia_max_sdu; - dap->da_vid = dia.dia_vid; - return (0); + return (DLADM_STATUS_OK); } -/* - * Adds a datalink to the array corresponding to arg. - */ -static void -i_dladm_nt_net_add(void *arg, char *name) -{ - dladm_walk_t *dwp = arg; - dladm_dev_t *ddp = dwp->dw_dev_list; - dladm_dev_t **lastp = &dwp->dw_dev_list; - - while (ddp) { - /* - * Skip duplicates. - */ - if (strcmp(ddp->dd_name, name) == 0) - return; - - lastp = &ddp->dd_next; - ddp = ddp->dd_next; - } - - if ((ddp = malloc(sizeof (*ddp))) == NULL) - return; - - (void) strlcpy(ddp->dd_name, name, IFNAMSIZ); - ddp->dd_next = NULL; - *lastp = ddp; -} - -/* - * Walker callback invoked for each DDI_NT_NET node. - */ -static int -i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg) -{ - char linkname[DLPI_LINKNAME_MAX]; - dlpi_handle_t dh; - - if (dlpi_makelink(linkname, di_minor_name(minor), - di_instance(node)) != DLPI_SUCCESS) - return (DI_WALK_CONTINUE); - - if (dlpi_open(linkname, &dh, 0) == DLPI_SUCCESS) { - i_dladm_nt_net_add(arg, linkname); - dlpi_close(dh); - } - return (DI_WALK_CONTINUE); -} - -/* - * Hold a data-link. - */ -static int -i_dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck) -{ - int fd; - dld_hold_vlan_t dhv; - - if (strlen(name) >= IFNAMSIZ) { - errno = EINVAL; - return (-1); - } +struct i_dladm_walk_arg { + dladm_walkcb_t *fn; + void *arg; +}; - if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) - return (-1); - - bzero(&dhv, sizeof (dld_hold_vlan_t)); - (void) strlcpy(dhv.dhv_name, name, IFNAMSIZ); - dhv.dhv_zid = zoneid; - dhv.dhv_docheck = docheck; - - if (i_dladm_ioctl(fd, DLDIOCHOLDVLAN, &dhv, sizeof (dhv)) < 0) { - int olderrno = errno; - - (void) close(fd); - errno = olderrno; - return (-1); - } - - (void) close(fd); - return (0); -} - -/* - * Release a data-link. - */ static int -i_dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck) +i_dladm_walk(datalink_id_t linkid, void *arg) { - int fd; - dld_hold_vlan_t dhv; - - if (strlen(name) >= IFNAMSIZ) { - errno = EINVAL; - return (-1); - } - - if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) - return (-1); - - bzero(&dhv, sizeof (dld_hold_vlan_t)); - (void) strlcpy(dhv.dhv_name, name, IFNAMSIZ); - dhv.dhv_zid = zoneid; - dhv.dhv_docheck = docheck; + struct i_dladm_walk_arg *walk_arg = arg; + char link[MAXLINKNAMELEN]; - if (i_dladm_ioctl(fd, DLDIOCRELEVLAN, &dhv, sizeof (dhv)) < 0) { - int olderrno = errno; - - (void) close(fd); - errno = olderrno; - return (-1); + if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, + sizeof (link)) == DLADM_STATUS_OK) { + return (walk_arg->fn(link, walk_arg->arg)); } - (void) close(fd); - return (0); + return (DLADM_WALK_CONTINUE); } /* - * Invoke the specified callback function for each active DDI_NT_NET - * node. + * Walk all datalinks. */ -int -dladm_walk(void (*fn)(void *, const char *), void *arg) +dladm_status_t +dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class, + datalink_media_t dmedia, uint32_t flags) { - di_node_t root; - dladm_walk_t dw; - dladm_dev_t *ddp, *last_ddp; - - if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { - errno = EFAULT; - return (-1); - } - dw.dw_dev_list = NULL; - - (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw, - i_dladm_nt_net_walk); - - di_fini(root); + struct i_dladm_walk_arg walk_arg; - ddp = dw.dw_dev_list; - while (ddp) { - fn(arg, ddp->dd_name); - last_ddp = ddp; - ddp = ddp->dd_next; - free(last_ddp); - } - - return (0); + walk_arg.fn = fn; + walk_arg.arg = arg; + return (dladm_walk_datalink_id(i_dladm_walk, &walk_arg, + class, dmedia, flags)); } /* - * MAC Administration Library. - * - * This library is used by administration tools such as dladm(1M) to + * These routines are used by administration tools such as dladm(1M) to * iterate through the list of MAC interfaces - * */ typedef struct dladm_mac_dev { char dm_name[MAXNAMELEN]; - struct dladm_mac_dev *dm_next; + struct dladm_mac_dev *dm_next; } dladm_mac_dev_t; typedef struct macadm_walk { - dladm_mac_dev_t *dmd_dev_list; + dladm_mac_dev_t *dmd_dev_list; } dladm_mac_walk_t; /* @@ -259,6 +131,12 @@ i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg) if (strcmp("aggr", di_driver_name(node)) == 0) return (DI_WALK_CONTINUE); + /* + * Skip softmacs. + */ + if (strcmp("softmac", di_driver_name(node)) == 0) + return (DI_WALK_CONTINUE); + while (dmdp) { /* * Skip duplicates. @@ -281,17 +159,18 @@ i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg) } /* - * Invoke the specified callback for each DDI_NT_MAC node. + * Invoke the specified callback for each DDI_NT_NET node. */ -int -dladm_mac_walk(void (*fn)(void *, const char *), void *arg) +dladm_status_t +dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg) { di_node_t root; dladm_mac_walk_t dmw; dladm_mac_dev_t *dmdp, *next; + boolean_t done = B_FALSE; if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) - return (-1); + return (dladm_errno2status(errno)); dmw.dmd_dev_list = NULL; @@ -303,33 +182,32 @@ dladm_mac_walk(void (*fn)(void *, const char *), void *arg) dmdp = dmw.dmd_dev_list; for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) { next = dmdp->dm_next; - (*fn)(arg, dmdp->dm_name); + if (!done && + ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) { + done = B_TRUE; + } free(dmdp); } - return (0); + return (DLADM_STATUS_OK); } /* - * Returns the current attributes of the specified datalink. + * Get the current attributes of the specified datalink. */ -int -dladm_info(const char *name, dladm_attr_t *dap) +dladm_status_t +dladm_info(datalink_id_t linkid, dladm_attr_t *dap) { int fd; + dladm_status_t status; if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) - return (-1); - - if (i_dladm_info(fd, name, dap) < 0) - goto failed; + return (dladm_errno2status(errno)); - (void) close(fd); - return (0); + status = i_dladm_info(fd, linkid, dap); -failed: (void) close(fd); - return (-1); + return (status); } const char * @@ -373,19 +251,678 @@ dladm_linkduplex2str(link_duplex_t duplex, char *buf) } /* - * Do a "hold" operation to a link. + * Set zoneid of a given link */ -int -dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck) +dladm_status_t +dladm_setzid(const char *link, zoneid_t zoneid) +{ + int fd; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_setzid_t dis; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + bzero(&dis, sizeof (dld_ioc_setzid_t)); + (void) strlcpy(dis.dis_link, link, MAXLINKNAMELEN); + dis.dis_zid = zoneid; + + if (i_dladm_ioctl(fd, DLDIOC_SETZID, &dis, sizeof (dis)) < 0) + status = dladm_errno2status(errno); + + (void) close(fd); + return (status); +} + +/* + * Get zoneid of a given link + */ +dladm_status_t +dladm_getzid(datalink_id_t linkid, zoneid_t *zoneidp) +{ + int fd; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_getzid_t dig; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + bzero(&dig, sizeof (dld_ioc_getzid_t)); + dig.dig_linkid = linkid; + dig.dig_zid = -1; + + if (i_dladm_ioctl(fd, DLDIOC_GETZID, &dig, sizeof (dig)) < 0) + status = dladm_errno2status(errno); + + (void) close(fd); + + if (status == DLADM_STATUS_OK) + *zoneidp = dig.dig_zid; + + return (status); +} + +/* + * Case 1: rename an existing link1 to a link2 that does not exist. + * Result: <linkid1, link2> + */ +static dladm_status_t +i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1, + const char *link2, uint32_t flags) { - return (i_dladm_hold_link(name, zoneid, docheck)); + dld_ioc_rename_t dir; + dladm_conf_t conf; + dladm_status_t status = DLADM_STATUS_OK; + int fd; + + /* + * Link is currently available. Check to see whether anything is + * holding this link to prevent a rename operation. + */ + if (flags & DLADM_OPT_ACTIVE) { + dir.dir_linkid1 = linkid1; + dir.dir_linkid2 = DATALINK_INVALID_LINKID; + (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) { + status = dladm_errno2status(errno); + (void) close(fd); + return (status); + } + } + + status = dladm_remap_datalink_id(linkid1, link2); + if (status != DLADM_STATUS_OK) + goto done; + + /* + * Flush the current mapping to persistent configuration. + */ + if ((flags & DLADM_OPT_PERSIST) && + (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) || + ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) { + (void) dladm_remap_datalink_id(linkid1, link1); + } +done: + if (flags & DLADM_OPT_ACTIVE) { + if (status != DLADM_STATUS_OK) { + (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); + (void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, + sizeof (dir)); + } + (void) close(fd); + } + return (status); +} + +typedef struct link_hold_arg_s { + datalink_id_t linkid; + datalink_id_t holder; + uint32_t flags; +} link_hold_arg_t; + +static int +i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg) +{ + link_hold_arg_t *hold_arg = arg; + dladm_aggr_grp_attr_t ginfo; + dladm_status_t status; + int i; + + status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + for (i = 0; i < ginfo.lg_nports; i++) { + if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) { + hold_arg->holder = aggrid; + return (DLADM_WALK_TERMINATE); + } + } + return (DLADM_WALK_CONTINUE); +} + +static int +i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg) +{ + link_hold_arg_t *hold_arg = arg; + dladm_vlan_attr_t vinfo; + dladm_status_t status; + + status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + if (vinfo.dv_linkid == hold_arg->linkid) { + hold_arg->holder = vlanid; + return (DLADM_WALK_TERMINATE); + } + return (DLADM_WALK_CONTINUE); } /* - * Do a "release" operation to a link. + * Case 2: rename an available physical link link1 to a REMOVED physical link + * link2. As a result, link1 directly inherits all datalinks configured + * over link2 (linkid2). + * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname, + * link2_other_attr> */ +static dladm_status_t +i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2) +{ + rcm_handle_t *rcm_hdl = NULL; + nvlist_t *nvl = NULL; + link_hold_arg_t arg; + dld_ioc_rename_t dir; + int fd; + dladm_conf_t conf1, conf2; + char devname[MAXLINKNAMELEN]; + uint64_t phymaj, phyinst; + dladm_status_t status = DLADM_STATUS_OK; + + /* + * First check if linkid1 is associated with any persistent + * aggregations or VLANs. If yes, return BUSY. + */ + arg.linkid = linkid1; + arg.holder = DATALINK_INVALID_LINKID; + arg.flags = DLADM_OPT_PERSIST; + (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg, + DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + if (arg.holder != DATALINK_INVALID_LINKID) + return (DLADM_STATUS_LINKBUSY); + + arg.flags = DLADM_OPT_PERSIST; + (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg, + DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + if (arg.holder != DATALINK_INVALID_LINKID) + return (DLADM_STATUS_LINKBUSY); + + /* + * Send DLDIOC_RENAME to request to rename link1's linkid to + * be linkid2. This will check whether link1 is used by any + * aggregations or VLANs, or is held by any application. If yes, + * return failure. + */ + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + dir.dir_linkid1 = linkid1; + dir.dir_linkid2 = linkid2; + if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) + status = dladm_errno2status(errno); + + if (status != DLADM_STATUS_OK) { + (void) close(fd); + return (status); + } + + /* + * Now change the phymaj, phyinst and devname associated with linkid1 + * to be associated with linkid2. Before doing that, the old active + * linkprop of linkid1 should be deleted. + */ + (void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE); + + if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) || + ((status = dladm_get_conf_field(conf1, FDEVNAME, devname, + MAXLINKNAMELEN)) != DLADM_STATUS_OK) || + ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj, + sizeof (uint64_t))) != DLADM_STATUS_OK) || + ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst, + sizeof (uint64_t))) != DLADM_STATUS_OK) || + ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) { + dir.dir_linkid1 = linkid2; + dir.dir_linkid2 = linkid1; + (void) dladm_init_linkprop(linkid1); + (void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)); + (void) close(fd); + return (status); + } + (void) close(fd); + + dladm_destroy_conf(conf1); + (void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname); + (void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj); + (void) dladm_set_conf_field(conf2, FPHYINST, + DLADM_TYPE_UINT64, &phyinst); + (void) dladm_write_conf(conf2); + dladm_destroy_conf(conf2); + + /* + * Delete link1 and mark link2 up. + */ + (void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE | + DLADM_OPT_PERSIST); + (void) dladm_remove_conf(linkid1); + (void) dladm_up_datalink_id(linkid2); + + /* + * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be + * consumed by the RCM framework to restore all the datalink and + * IP configuration. + */ + status = DLADM_STATUS_FAILED; + if ((nvlist_alloc(&nvl, 0, 0) != 0) || + (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) { + goto done; + } + + if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS) + goto done; + + if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) == + RCM_SUCCESS) { + status = DLADM_STATUS_OK; + } + +done: + if (rcm_hdl != NULL) + (void) rcm_free_handle(rcm_hdl); + if (nvl != NULL) + nvlist_free(nvl); + return (status); +} + +/* + * case 3: rename a non-existent link to a REMOVED physical link. + * Set the removed physical link's device name to link1, so that + * when link1 attaches, it inherits all the link configuration of + * the removed physical link. + */ +static dladm_status_t +i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2) +{ + dladm_conf_t conf; + dladm_status_t status; + + if (!dladm_valid_linkname(link1)) + return (DLADM_STATUS_LINKINVAL); + + status = dladm_read_conf(linkid2, &conf); + if (status != DLADM_STATUS_OK) + goto done; + + if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR, + link1)) == DLADM_STATUS_OK) { + status = dladm_write_conf(conf); + } + + dladm_destroy_conf(conf); + +done: + return (status); +} + +dladm_status_t +dladm_rename_link(const char *link1, const char *link2) +{ + datalink_id_t linkid1 = DATALINK_INVALID_LINKID; + datalink_id_t linkid2 = DATALINK_INVALID_LINKID; + uint32_t flags1, flags2; + datalink_class_t class1, class2; + uint32_t media1, media2; + boolean_t remphy2 = B_FALSE; + dladm_status_t status; + + (void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1); + if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) == + DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && + (flags2 == DLADM_OPT_PERSIST)) { + /* + * see whether link2 is a removed physical link. + */ + remphy2 = B_TRUE; + } + + if (linkid1 != DATALINK_INVALID_LINKID) { + if (linkid2 == DATALINK_INVALID_LINKID) { + /* + * case 1: rename an existing link to a link that + * does not exist. + */ + status = i_dladm_rename_link_c1(linkid1, link1, link2, + flags1); + } else if (remphy2) { + /* + * case 2: rename an available link to a REMOVED + * physical link. Return failure if link1 is not + * an active physical link. + */ + if ((class1 != class2) || (media1 != media2) || + !(flags1 & DLADM_OPT_ACTIVE)) { + status = DLADM_STATUS_BADARG; + } else { + status = i_dladm_rename_link_c2(linkid1, + linkid2); + } + } else { + status = DLADM_STATUS_EXIST; + } + } else if (remphy2) { + status = i_dladm_rename_link_c3(link1, linkid2); + } else { + status = DLADM_STATUS_NOTFOUND; + } + return (status); +} + +typedef struct consumer_del_phys_arg_s { + datalink_id_t linkid; +} consumer_del_phys_arg_t; + +static int +i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg) +{ + consumer_del_phys_arg_t *del_arg = arg; + dladm_vlan_attr_t vinfo; + dladm_status_t status; + + status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + if (vinfo.dv_linkid == del_arg->linkid) + (void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST); + return (DLADM_WALK_CONTINUE); +} + +static int +i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg) +{ + consumer_del_phys_arg_t *del_arg = arg; + dladm_aggr_grp_attr_t ginfo; + dladm_status_t status; + dladm_aggr_port_attr_db_t port[1]; + int i; + + status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + for (i = 0; i < ginfo.lg_nports; i++) + if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid) + break; + + if (i != ginfo.lg_nports) { + if (ginfo.lg_nports == 1 && i == 0) { + consumer_del_phys_arg_t aggr_del_arg; + + /* + * First delete all the VLANs on this aggregation, then + * delete the aggregation itself. + */ + aggr_del_arg.linkid = aggrid; + (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, + &aggr_del_arg, DATALINK_CLASS_VLAN, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + (void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST); + } else { + port[0].lp_linkid = del_arg->linkid; + (void) dladm_aggr_remove(aggrid, 1, port, + DLADM_OPT_PERSIST); + } + } + return (DLADM_WALK_CONTINUE); +} + +typedef struct del_phys_arg_s { + dladm_status_t rval; +} del_phys_arg_t; + +static int +i_dladm_phys_delete(datalink_id_t linkid, void *arg) +{ + uint32_t flags; + datalink_class_t class; + uint32_t media; + dladm_status_t status = DLADM_STATUS_OK; + del_phys_arg_t *del_phys_arg = arg; + consumer_del_phys_arg_t del_arg; + + if ((status = dladm_datalink_id2info(linkid, &flags, &class, + &media, NULL, 0)) != DLADM_STATUS_OK) { + goto done; + } + + /* + * see whether this link is a removed physical link. + */ + if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) || + (flags & DLADM_OPT_ACTIVE)) { + status = DLADM_STATUS_BADARG; + goto done; + } + + if (media == DL_ETHER) { + del_arg.linkid = linkid; + (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg, + DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_PERSIST); + (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg, + DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_PERSIST); + } + + (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); + (void) dladm_remove_conf(linkid); + +done: + del_phys_arg->rval = status; + return (DLADM_WALK_CONTINUE); +} + +dladm_status_t +dladm_phys_delete(datalink_id_t linkid) +{ + del_phys_arg_t arg = {DLADM_STATUS_OK}; + + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_PERSIST); + return (DLADM_STATUS_OK); + } else { + (void) i_dladm_phys_delete(linkid, &arg); + return (arg.rval); + } +} + +dladm_status_t +dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags) +{ + dladm_status_t status; + + assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); + + switch (flags) { + case DLADM_OPT_PERSIST: { + dladm_conf_t conf; + + status = dladm_read_conf(linkid, &conf); + if (status != DLADM_STATUS_OK) + return (status); + + status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev, + MAXLINKNAMELEN); + dladm_destroy_conf(conf); + return (status); + } + case DLADM_OPT_ACTIVE: { + dld_ioc_phys_attr_t dip; + int fd; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + dip.dip_linkid = linkid; + if (i_dladm_ioctl(fd, DLDIOC_PHYS_ATTR, &dip, sizeof (dip)) + < 0) { + status = dladm_errno2status(errno); + (void) close(fd); + return (status); + } + (void) close(fd); + dpap->dp_novanity = dip.dip_novanity; + (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN); + return (DLADM_STATUS_OK); + } + default: + return (DLADM_STATUS_BADARG); + } +} + +typedef struct i_walk_dev_state_s { + const char *devname; + datalink_id_t linkid; + boolean_t found; +} i_walk_dev_state_t; + int -dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck) +i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg) +{ + dladm_phys_attr_t dpa; + dladm_status_t status; + i_walk_dev_state_t *statep = arg; + + status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); + if ((status == DLADM_STATUS_OK) && + (strcmp(statep->devname, dpa.dp_dev) == 0)) { + statep->found = B_TRUE; + statep->linkid = linkid; + return (DLADM_WALK_TERMINATE); + } + return (DLADM_WALK_CONTINUE); +} + +/* + * Get the linkid from the physical device name. + */ +dladm_status_t +dladm_dev2linkid(const char *devname, datalink_id_t *linkidp) { - return (i_dladm_rele_link(name, zoneid, docheck)); + i_walk_dev_state_t state; + + state.found = B_FALSE; + state.devname = devname; + + (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + if (state.found == B_TRUE) { + *linkidp = state.linkid; + return (DLADM_STATUS_OK); + } else { + return (dladm_errno2status(ENOENT)); + } +} + +static int +parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen) +{ + char *cp, *tp; + int len; + + /* + * device name length must not be 0, and it must end with digit. + */ + if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1])) + return (EINVAL); + + (void) strlcpy(driver, devname, maxlen); + cp = (char *)&driver[len - 1]; + + for (tp = cp; isdigit(*tp); tp--) { + if (tp <= driver) + return (EINVAL); + } + + *ppa = atoi(tp + 1); + *(tp + 1) = '\0'; + return (0); +} + +dladm_status_t +dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len) +{ + char devname[MAXLINKNAMELEN]; + uint16_t vid = VLAN_ID_NONE; + datalink_class_t class; + dladm_status_t status; + + status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0); + if (status != DLADM_STATUS_OK) + goto done; + + /* + * If this is a VLAN, we must first determine the class and linkid of + * the link the VLAN has been created over. + */ + if (class == DATALINK_CLASS_VLAN) { + dladm_vlan_attr_t dva; + + status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) + goto done; + linkid = dva.dv_linkid; + vid = dva.dv_vid; + + if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL, + NULL, 0)) != DLADM_STATUS_OK) { + goto done; + } + } + + switch (class) { + case DATALINK_CLASS_AGGR: { + dladm_aggr_grp_attr_t dga; + + status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) + goto done; + + if (dga.lg_key == 0) { + /* + * If the key was not specified when the aggregation + * is created, we cannot guess its /dev node name. + */ + status = DLADM_STATUS_BADARG; + goto done; + } + (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key); + break; + } + case DATALINK_CLASS_PHYS: { + dladm_phys_attr_t dpa; + + status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) + goto done; + + (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN); + break; + } + default: + status = DLADM_STATUS_BADARG; + goto done; + } + + if (vid != VLAN_ID_NONE) { + char drv[MAXNAMELEN]; + uint_t ppa; + + if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) { + status = DLADM_STATUS_BADARG; + goto done; + } + if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len) + status = DLADM_STATUS_TOOSMALL; + } else { + if (strlcpy(dev, devname, len) >= len) + status = DLADM_STATUS_TOOSMALL; + } + +done: + return (status); } |