Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <unistd.h>
     28 #include <errno.h>
     29 #include <fcntl.h>
     30 #include <assert.h>
     31 #include <ctype.h>
     32 #include <strings.h>
     33 #include <sys/stat.h>
     34 #include <sys/dld.h>
     35 #include <sys/vlan.h>
     36 #include <zone.h>
     37 #include <librcm.h>
     38 #include <libdlpi.h>
     39 #include <libdevinfo.h>
     40 #include <libdlaggr.h>
     41 #include <libdlvlan.h>
     42 #include <libdllink.h>
     43 #include <libdlmgmt.h>
     44 #include <libdladm_impl.h>
     45 #include <libinetutil.h>
     46 
     47 /*
     48  * Return the attributes of the specified datalink from the DLD driver.
     49  */
     50 static dladm_status_t
     51 i_dladm_info(dladm_handle_t handle, const datalink_id_t linkid,
     52     dladm_attr_t *dap)
     53 {
     54 	dld_ioc_attr_t	dia;
     55 
     56 	dia.dia_linkid = linkid;
     57 
     58 	if (ioctl(dladm_dld_fd(handle), DLDIOC_ATTR, &dia) < 0)
     59 		return (dladm_errno2status(errno));
     60 
     61 	dap->da_max_sdu = dia.dia_max_sdu;
     62 
     63 	return (DLADM_STATUS_OK);
     64 }
     65 
     66 static dladm_status_t
     67 dladm_usagelog(dladm_handle_t handle, dladm_logtype_t type,
     68     dld_ioc_usagelog_t *log_info)
     69 {
     70 	if (type == DLADM_LOGTYPE_FLOW)
     71 		log_info->ul_type = MAC_LOGTYPE_FLOW;
     72 	else
     73 		log_info->ul_type = MAC_LOGTYPE_LINK;
     74 
     75 	if (ioctl(dladm_dld_fd(handle), DLDIOC_USAGELOG, log_info) < 0)
     76 		return (DLADM_STATUS_IOERR);
     77 
     78 	return (DLADM_STATUS_OK);
     79 }
     80 
     81 dladm_status_t
     82 dladm_start_usagelog(dladm_handle_t handle, dladm_logtype_t type,
     83     uint_t interval)
     84 {
     85 	dld_ioc_usagelog_t	log_info;
     86 
     87 	log_info.ul_onoff = B_TRUE;
     88 	log_info.ul_interval = interval;
     89 
     90 	return (dladm_usagelog(handle, type, &log_info));
     91 }
     92 
     93 dladm_status_t
     94 dladm_stop_usagelog(dladm_handle_t handle, dladm_logtype_t type)
     95 {
     96 	dld_ioc_usagelog_t	log_info;
     97 
     98 	log_info.ul_onoff = B_FALSE;
     99 	log_info.ul_interval = 0;
    100 
    101 	return (dladm_usagelog(handle, type, &log_info));
    102 }
    103 
    104 struct i_dladm_walk_arg {
    105 	dladm_walkcb_t *fn;
    106 	void *arg;
    107 };
    108 
    109 static int
    110 i_dladm_walk(dladm_handle_t handle, datalink_id_t linkid, void *arg)
    111 {
    112 	struct i_dladm_walk_arg *walk_arg = arg;
    113 	char link[MAXLINKNAMELEN];
    114 
    115 	if (dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, link,
    116 	    sizeof (link)) == DLADM_STATUS_OK) {
    117 		return (walk_arg->fn(link, walk_arg->arg));
    118 	}
    119 
    120 	return (DLADM_WALK_CONTINUE);
    121 }
    122 
    123 /*
    124  * Walk all datalinks.
    125  */
    126 dladm_status_t
    127 dladm_walk(dladm_walkcb_t *fn, dladm_handle_t handle, void *arg,
    128     datalink_class_t class, datalink_media_t dmedia, uint32_t flags)
    129 {
    130 	struct i_dladm_walk_arg walk_arg;
    131 
    132 	walk_arg.fn = fn;
    133 	walk_arg.arg = arg;
    134 	return (dladm_walk_datalink_id(i_dladm_walk, handle, &walk_arg,
    135 	    class, dmedia, flags));
    136 }
    137 
    138 #define	MAXGRPPERLINK	64
    139 
    140 int
    141 dladm_walk_hwgrp(dladm_handle_t handle, datalink_id_t linkid, void *arg,
    142     boolean_t (*fn)(void *, dladm_hwgrp_attr_t *))
    143 {
    144 	int		bufsize, ret;
    145 	int		nhwgrp = MAXGRPPERLINK;
    146 	dld_ioc_hwgrpget_t *iomp = NULL;
    147 
    148 	bufsize = sizeof (dld_ioc_hwgrpget_t) +
    149 	    nhwgrp * sizeof (dld_hwgrpinfo_t);
    150 
    151 	if ((iomp = (dld_ioc_hwgrpget_t *)calloc(1, bufsize)) == NULL)
    152 		return (-1);
    153 
    154 	iomp->dih_size = nhwgrp * sizeof (dld_hwgrpinfo_t);
    155 	iomp->dih_linkid = linkid;
    156 
    157 	ret = ioctl(dladm_dld_fd(handle), DLDIOC_GETHWGRP, iomp);
    158 	if (ret == 0) {
    159 		int i;
    160 		dld_hwgrpinfo_t *dhip;
    161 		dladm_hwgrp_attr_t attr;
    162 
    163 		dhip = (dld_hwgrpinfo_t *)(iomp + 1);
    164 		for (i = 0; i < iomp->dih_n_groups; i++) {
    165 			bzero(&attr, sizeof (attr));
    166 
    167 			(void) strlcpy(attr.hg_link_name,
    168 			    dhip->dhi_link_name, sizeof (attr.hg_link_name));
    169 			attr.hg_grp_num = dhip->dhi_grp_num;
    170 			attr.hg_grp_type = dhip->dhi_grp_type;
    171 			attr.hg_n_rings = dhip->dhi_n_rings;
    172 			attr.hg_n_clnts = dhip->dhi_n_clnts;
    173 			(void) strlcpy(attr.hg_client_names,
    174 			    dhip->dhi_clnts, sizeof (attr.hg_client_names));
    175 
    176 			if (!(*fn)(arg, &attr))
    177 				break;
    178 			dhip++;
    179 		}
    180 	}
    181 	free(iomp);
    182 	return (ret);
    183 }
    184 
    185 /*
    186  * Invoke the specified callback for each MAC address entry defined on
    187  * the specified device.
    188  */
    189 int
    190 dladm_walk_macaddr(dladm_handle_t handle, datalink_id_t linkid, void *arg,
    191     boolean_t (*fn)(void *, dladm_macaddr_attr_t *))
    192 {
    193 	int		bufsize, ret;
    194 	int		nmacaddr = 1024;
    195 	dld_ioc_macaddrget_t *iomp = NULL;
    196 
    197 	bufsize = sizeof (dld_ioc_macaddrget_t) +
    198 	    nmacaddr * sizeof (dld_macaddrinfo_t);
    199 
    200 	if ((iomp = (dld_ioc_macaddrget_t *)calloc(1, bufsize)) == NULL)
    201 		return (-1);
    202 
    203 	iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t);
    204 	iomp->dig_linkid = linkid;
    205 
    206 	ret = ioctl(dladm_dld_fd(handle), DLDIOC_MACADDRGET, iomp);
    207 	if (ret == 0) {
    208 		int i;
    209 		dld_macaddrinfo_t *dmip;
    210 		dladm_macaddr_attr_t attr;
    211 
    212 		dmip = (dld_macaddrinfo_t *)(iomp + 1);
    213 		for (i = 0; i < iomp->dig_count; i++) {
    214 			bzero(&attr, sizeof (attr));
    215 
    216 			attr.ma_slot = dmip->dmi_slot;
    217 			attr.ma_flags = 0;
    218 			if (dmip->dmi_flags & DLDIOCMACADDR_USED)
    219 				attr.ma_flags |= DLADM_MACADDR_USED;
    220 			bcopy(dmip->dmi_addr, attr.ma_addr,
    221 			    dmip->dmi_addrlen);
    222 			attr.ma_addrlen = dmip->dmi_addrlen;
    223 			(void) strlcpy(attr.ma_client_name,
    224 			    dmip->dmi_client_name, MAXNAMELEN);
    225 			attr.ma_client_linkid = dmip->dma_client_linkid;
    226 
    227 			if (!(*fn)(arg, &attr))
    228 				break;
    229 			dmip++;
    230 		}
    231 	}
    232 	free(iomp);
    233 	return (ret);
    234 }
    235 
    236 /*
    237  * These routines are used by administration tools such as dladm(1M) to
    238  * iterate through the list of MAC interfaces
    239  */
    240 
    241 typedef struct dladm_mac_dev {
    242 	char			dm_name[MAXNAMELEN];
    243 	struct dladm_mac_dev    *dm_next;
    244 } dladm_mac_dev_t;
    245 
    246 typedef struct macadm_walk {
    247 	dladm_mac_dev_t	 *dmd_dev_list;
    248 } dladm_mac_walk_t;
    249 
    250 /*
    251  * Local callback invoked for each DDI_NT_NET node.
    252  */
    253 /* ARGSUSED */
    254 static int
    255 i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg)
    256 {
    257 	dladm_mac_walk_t	*dmwp = arg;
    258 	dladm_mac_dev_t		*dmdp = dmwp->dmd_dev_list;
    259 	dladm_mac_dev_t		**last_dmdp = &dmwp->dmd_dev_list;
    260 	char			mac[MAXNAMELEN];
    261 
    262 	(void) snprintf(mac, MAXNAMELEN, "%s%d",
    263 	    di_driver_name(node), di_instance(node));
    264 
    265 	/*
    266 	 * Skip aggregations.
    267 	 */
    268 	if (strcmp("aggr", di_driver_name(node)) == 0)
    269 		return (DI_WALK_CONTINUE);
    270 
    271 	/*
    272 	 * Skip softmacs.
    273 	 */
    274 	if (strcmp("softmac", di_driver_name(node)) == 0)
    275 		return (DI_WALK_CONTINUE);
    276 
    277 	while (dmdp) {
    278 		/*
    279 		 * Skip duplicates.
    280 		 */
    281 		if (strcmp(dmdp->dm_name, mac) == 0)
    282 			return (DI_WALK_CONTINUE);
    283 
    284 		last_dmdp = &dmdp->dm_next;
    285 		dmdp = dmdp->dm_next;
    286 	}
    287 
    288 	if ((dmdp = malloc(sizeof (*dmdp))) == NULL)
    289 		return (DI_WALK_CONTINUE);
    290 
    291 	(void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN);
    292 	dmdp->dm_next = NULL;
    293 	*last_dmdp = dmdp;
    294 
    295 	return (DI_WALK_CONTINUE);
    296 }
    297 
    298 /*
    299  * Invoke the specified callback for each DDI_NT_NET node.
    300  */
    301 dladm_status_t
    302 dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg)
    303 {
    304 	di_node_t		root;
    305 	dladm_mac_walk_t	dmw;
    306 	dladm_mac_dev_t		*dmdp, *next;
    307 	boolean_t		done = B_FALSE;
    308 
    309 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
    310 		return (dladm_errno2status(errno));
    311 
    312 	dmw.dmd_dev_list = NULL;
    313 
    314 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw,
    315 	    i_dladm_mac_walk);
    316 
    317 	di_fini(root);
    318 
    319 	dmdp = dmw.dmd_dev_list;
    320 	for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) {
    321 		next = dmdp->dm_next;
    322 		if (!done &&
    323 		    ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) {
    324 			done = B_TRUE;
    325 		}
    326 		free(dmdp);
    327 	}
    328 
    329 	return (DLADM_STATUS_OK);
    330 }
    331 
    332 /*
    333  * Get the current attributes of the specified datalink.
    334  */
    335 dladm_status_t
    336 dladm_info(dladm_handle_t handle, datalink_id_t linkid, dladm_attr_t *dap)
    337 {
    338 	return (i_dladm_info(handle, linkid, dap));
    339 }
    340 
    341 const char *
    342 dladm_linkstate2str(link_state_t state, char *buf)
    343 {
    344 	const char	*s;
    345 
    346 	switch (state) {
    347 	case LINK_STATE_UP:
    348 		s = "up";
    349 		break;
    350 	case LINK_STATE_DOWN:
    351 		s = "down";
    352 		break;
    353 	default:
    354 		s = "unknown";
    355 		break;
    356 	}
    357 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
    358 	return (buf);
    359 }
    360 
    361 const char *
    362 dladm_linkduplex2str(link_duplex_t duplex, char *buf)
    363 {
    364 	const char	*s;
    365 
    366 	switch (duplex) {
    367 	case LINK_DUPLEX_FULL:
    368 		s = "full";
    369 		break;
    370 	case LINK_DUPLEX_HALF:
    371 		s = "half";
    372 		break;
    373 	default:
    374 		s = "unknown";
    375 		break;
    376 	}
    377 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
    378 	return (buf);
    379 }
    380 
    381 /*
    382  * Case 1: rename an existing link1 to a link2 that does not exist.
    383  * Result: <linkid1, link2>
    384  */
    385 static dladm_status_t
    386 i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
    387     const char *link1, const char *link2, uint32_t flags)
    388 {
    389 	dld_ioc_rename_t	dir;
    390 	dladm_status_t		status = DLADM_STATUS_OK;
    391 
    392 	/*
    393 	 * Link is currently available. Check to see whether anything is
    394 	 * holding this link to prevent a rename operation.
    395 	 */
    396 	if (flags & DLADM_OPT_ACTIVE) {
    397 		dir.dir_linkid1 = linkid1;
    398 		dir.dir_linkid2 = DATALINK_INVALID_LINKID;
    399 		(void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
    400 
    401 		if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) {
    402 			status = dladm_errno2status(errno);
    403 			return (status);
    404 		}
    405 	}
    406 
    407 	status = dladm_remap_datalink_id(handle, linkid1, link2);
    408 	if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
    409 		(void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
    410 		(void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
    411 	}
    412 	return (status);
    413 }
    414 
    415 typedef struct link_hold_arg_s {
    416 	datalink_id_t	linkid;
    417 	datalink_id_t	holder;
    418 	uint32_t	flags;
    419 } link_hold_arg_t;
    420 
    421 static int
    422 i_dladm_aggr_link_hold(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
    423 {
    424 	link_hold_arg_t		*hold_arg = arg;
    425 	dladm_aggr_grp_attr_t	ginfo;
    426 	dladm_status_t		status;
    427 	int			i;
    428 
    429 	status = dladm_aggr_info(handle, aggrid, &ginfo, hold_arg->flags);
    430 	if (status != DLADM_STATUS_OK)
    431 		return (DLADM_WALK_CONTINUE);
    432 
    433 	for (i = 0; i < ginfo.lg_nports; i++) {
    434 		if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
    435 			hold_arg->holder = aggrid;
    436 			return (DLADM_WALK_TERMINATE);
    437 		}
    438 	}
    439 	return (DLADM_WALK_CONTINUE);
    440 }
    441 
    442 static int
    443 i_dladm_vlan_link_hold(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
    444 {
    445 	link_hold_arg_t		*hold_arg = arg;
    446 	dladm_vlan_attr_t	vinfo;
    447 	dladm_status_t		status;
    448 
    449 	status = dladm_vlan_info(handle, vlanid, &vinfo, hold_arg->flags);
    450 	if (status != DLADM_STATUS_OK)
    451 		return (DLADM_WALK_CONTINUE);
    452 
    453 	if (vinfo.dv_linkid == hold_arg->linkid) {
    454 		hold_arg->holder = vlanid;
    455 		return (DLADM_WALK_TERMINATE);
    456 	}
    457 	return (DLADM_WALK_CONTINUE);
    458 }
    459 
    460 /*
    461  * Case 2: rename an available physical link link1 to a REMOVED physical link
    462  *     link2.  As a result, link1 directly inherits all datalinks configured
    463  *     over link2 (linkid2).
    464  * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
    465  *     link2_other_attr>
    466  */
    467 static dladm_status_t
    468 i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1,
    469     datalink_id_t linkid2)
    470 {
    471 	rcm_handle_t		*rcm_hdl = NULL;
    472 	nvlist_t		*nvl = NULL;
    473 	link_hold_arg_t		arg;
    474 	dld_ioc_rename_t	dir;
    475 	dladm_conf_t		conf1, conf2;
    476 	char			devname[MAXLINKNAMELEN];
    477 	uint64_t		phymaj, phyinst;
    478 	dladm_status_t		status = DLADM_STATUS_OK;
    479 
    480 	/*
    481 	 * First check if linkid1 is associated with any persistent
    482 	 * aggregations or VLANs. If yes, return BUSY.
    483 	 */
    484 	arg.linkid = linkid1;
    485 	arg.holder = DATALINK_INVALID_LINKID;
    486 	arg.flags = DLADM_OPT_PERSIST;
    487 	(void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, handle, &arg,
    488 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
    489 	if (arg.holder != DATALINK_INVALID_LINKID)
    490 		return (DLADM_STATUS_LINKBUSY);
    491 
    492 	arg.flags = DLADM_OPT_PERSIST;
    493 	(void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, handle, &arg,
    494 	    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
    495 	if (arg.holder != DATALINK_INVALID_LINKID)
    496 		return (DLADM_STATUS_LINKBUSY);
    497 
    498 	/*
    499 	 * Send DLDIOC_RENAME to request to rename link1's linkid to
    500 	 * be linkid2. This will check whether link1 is used by any
    501 	 * aggregations or VLANs, or is held by any application. If yes,
    502 	 * return failure.
    503 	 */
    504 	dir.dir_linkid1 = linkid1;
    505 	dir.dir_linkid2 = linkid2;
    506 	if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0)
    507 		status = dladm_errno2status(errno);
    508 
    509 	if (status != DLADM_STATUS_OK) {
    510 		return (status);
    511 	}
    512 
    513 	/*
    514 	 * Now change the phymaj, phyinst and devname associated with linkid1
    515 	 * to be associated with linkid2. Before doing that, the old active
    516 	 * linkprop of linkid1 should be deleted.
    517 	 */
    518 	(void) dladm_set_linkprop(handle, linkid1, NULL, NULL, 0,
    519 	    DLADM_OPT_ACTIVE);
    520 
    521 	if (((status = dladm_read_conf(handle, linkid1, &conf1)) !=
    522 	    DLADM_STATUS_OK) ||
    523 	    ((status = dladm_get_conf_field(handle, conf1, FDEVNAME, devname,
    524 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
    525 	    ((status = dladm_get_conf_field(handle, conf1, FPHYMAJ, &phymaj,
    526 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
    527 	    ((status = dladm_get_conf_field(handle, conf1, FPHYINST, &phyinst,
    528 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
    529 	    ((status = dladm_read_conf(handle, linkid2, &conf2)) !=
    530 	    DLADM_STATUS_OK)) {
    531 		dir.dir_linkid1 = linkid2;
    532 		dir.dir_linkid2 = linkid1;
    533 		(void) dladm_init_linkprop(handle, linkid1, B_FALSE);
    534 		(void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
    535 		return (status);
    536 	}
    537 
    538 	dladm_destroy_conf(handle, conf1);
    539 	(void) dladm_set_conf_field(handle, conf2, FDEVNAME, DLADM_TYPE_STR,
    540 	    devname);
    541 	(void) dladm_set_conf_field(handle, conf2, FPHYMAJ, DLADM_TYPE_UINT64,
    542 	    &phymaj);
    543 	(void) dladm_set_conf_field(handle, conf2, FPHYINST,
    544 	    DLADM_TYPE_UINT64, &phyinst);
    545 	(void) dladm_write_conf(handle, conf2);
    546 	dladm_destroy_conf(handle, conf2);
    547 
    548 	/*
    549 	 * Delete link1 and mark link2 up.
    550 	 */
    551 	(void) dladm_remove_conf(handle, linkid1);
    552 	(void) dladm_destroy_datalink_id(handle, linkid1, DLADM_OPT_ACTIVE |
    553 	    DLADM_OPT_PERSIST);
    554 	(void) dladm_up_datalink_id(handle, linkid2);
    555 
    556 	/*
    557 	 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
    558 	 * consumed by the RCM framework to restore all the datalink and
    559 	 * IP configuration.
    560 	 */
    561 	status = DLADM_STATUS_FAILED;
    562 	if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
    563 	    (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
    564 		goto done;
    565 	}
    566 
    567 	if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
    568 		goto done;
    569 
    570 	if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
    571 	    RCM_SUCCESS) {
    572 		status = DLADM_STATUS_OK;
    573 	}
    574 
    575 done:
    576 	if (rcm_hdl != NULL)
    577 		(void) rcm_free_handle(rcm_hdl);
    578 	if (nvl != NULL)
    579 		nvlist_free(nvl);
    580 	return (status);
    581 }
    582 
    583 /*
    584  * case 3: rename a non-existent link to a REMOVED physical link.
    585  * Set the removed physical link's device name to link1, so that
    586  * when link1 attaches, it inherits all the link configuration of
    587  * the removed physical link.
    588  */
    589 static dladm_status_t
    590 i_dladm_rename_link_c3(dladm_handle_t handle, const char *link1,
    591     datalink_id_t linkid2)
    592 {
    593 	dladm_conf_t	conf;
    594 	dladm_status_t	status;
    595 
    596 	if (!dladm_valid_linkname(link1))
    597 		return (DLADM_STATUS_LINKINVAL);
    598 
    599 	status = dladm_read_conf(handle, linkid2, &conf);
    600 	if (status != DLADM_STATUS_OK)
    601 		goto done;
    602 
    603 	if ((status = dladm_set_conf_field(handle, conf, FDEVNAME,
    604 	    DLADM_TYPE_STR, link1)) == DLADM_STATUS_OK) {
    605 		status = dladm_write_conf(handle, conf);
    606 	}
    607 
    608 	dladm_destroy_conf(handle, conf);
    609 
    610 done:
    611 	return (status);
    612 }
    613 
    614 dladm_status_t
    615 dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
    616 {
    617 	datalink_id_t		linkid1 = DATALINK_INVALID_LINKID;
    618 	datalink_id_t		linkid2 = DATALINK_INVALID_LINKID;
    619 	uint32_t		flags1, flags2;
    620 	datalink_class_t	class1, class2;
    621 	uint32_t		media1, media2;
    622 	boolean_t		remphy2 = B_FALSE;
    623 	dladm_status_t  	status;
    624 
    625 	(void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1,
    626 	    &media1);
    627 	if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2,
    628 	    &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
    629 	    (flags2 == DLADM_OPT_PERSIST)) {
    630 		/*
    631 		 * see whether link2 is a removed physical link.
    632 		 */
    633 		remphy2 = B_TRUE;
    634 	}
    635 
    636 	if (linkid1 != DATALINK_INVALID_LINKID) {
    637 		if (linkid2 == DATALINK_INVALID_LINKID) {
    638 			/*
    639 			 * case 1: rename an existing link to a link that
    640 			 * does not exist.
    641 			 */
    642 			status = i_dladm_rename_link_c1(handle, linkid1, link1,
    643 			    link2, flags1);
    644 		} else if (remphy2) {
    645 			/*
    646 			 * case 2: rename an available link to a REMOVED
    647 			 * physical link. Return failure if link1 is not
    648 			 * an active physical link.
    649 			 */
    650 			if ((class1 != class2) || (media1 != media2) ||
    651 			    !(flags1 & DLADM_OPT_ACTIVE)) {
    652 				status = DLADM_STATUS_BADARG;
    653 			} else {
    654 				status = i_dladm_rename_link_c2(handle, linkid1,
    655 				    linkid2);
    656 			}
    657 		} else {
    658 			status = DLADM_STATUS_EXIST;
    659 		}
    660 	} else if (remphy2) {
    661 		status = i_dladm_rename_link_c3(handle, link1, linkid2);
    662 	} else {
    663 		status = DLADM_STATUS_NOTFOUND;
    664 	}
    665 	return (status);
    666 }
    667 
    668 typedef struct consumer_del_phys_arg_s {
    669 	datalink_id_t	linkid;
    670 } consumer_del_phys_arg_t;
    671 
    672 static int
    673 i_dladm_vlan_link_del(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
    674 {
    675 	consumer_del_phys_arg_t	*del_arg = arg;
    676 	dladm_vlan_attr_t	vinfo;
    677 	dladm_status_t		status;
    678 
    679 	status = dladm_vlan_info(handle, vlanid, &vinfo, DLADM_OPT_PERSIST);
    680 	if (status != DLADM_STATUS_OK)
    681 		return (DLADM_WALK_CONTINUE);
    682 
    683 	if (vinfo.dv_linkid == del_arg->linkid)
    684 		(void) dladm_vlan_delete(handle, vlanid, DLADM_OPT_PERSIST);
    685 	return (DLADM_WALK_CONTINUE);
    686 }
    687 
    688 static int
    689 i_dladm_aggr_link_del(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
    690 {
    691 	consumer_del_phys_arg_t		*del_arg = arg;
    692 	dladm_aggr_grp_attr_t		ginfo;
    693 	dladm_status_t			status;
    694 	dladm_aggr_port_attr_db_t	port[1];
    695 	int				i;
    696 
    697 	status = dladm_aggr_info(handle, aggrid, &ginfo, DLADM_OPT_PERSIST);
    698 	if (status != DLADM_STATUS_OK)
    699 		return (DLADM_WALK_CONTINUE);
    700 
    701 	for (i = 0; i < ginfo.lg_nports; i++)
    702 		if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
    703 			break;
    704 
    705 	if (i != ginfo.lg_nports) {
    706 		if (ginfo.lg_nports == 1 && i == 0) {
    707 			consumer_del_phys_arg_t	aggr_del_arg;
    708 
    709 			/*
    710 			 * First delete all the VLANs on this aggregation, then
    711 			 * delete the aggregation itself.
    712 			 */
    713 			aggr_del_arg.linkid = aggrid;
    714 			(void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
    715 			    handle, &aggr_del_arg, DATALINK_CLASS_VLAN,
    716 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
    717 			(void) dladm_aggr_delete(handle, aggrid,
    718 			    DLADM_OPT_PERSIST);
    719 		} else {
    720 			port[0].lp_linkid = del_arg->linkid;
    721 			(void) dladm_aggr_remove(handle, aggrid, 1, port,
    722 			    DLADM_OPT_PERSIST);
    723 		}
    724 	}
    725 	return (DLADM_WALK_CONTINUE);
    726 }
    727 
    728 typedef struct del_phys_arg_s {
    729 	dladm_status_t	rval;
    730 } del_phys_arg_t;
    731 
    732 static int
    733 i_dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid, void *arg)
    734 {
    735 	uint32_t		flags;
    736 	datalink_class_t	class;
    737 	uint32_t		media;
    738 	dladm_status_t		status = DLADM_STATUS_OK;
    739 	del_phys_arg_t		*del_phys_arg = arg;
    740 	consumer_del_phys_arg_t	del_arg;
    741 
    742 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
    743 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
    744 		goto done;
    745 	}
    746 
    747 	/*
    748 	 * see whether this link is a removed physical link.
    749 	 */
    750 	if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
    751 	    (flags & DLADM_OPT_ACTIVE)) {
    752 		status = DLADM_STATUS_BADARG;
    753 		goto done;
    754 	}
    755 
    756 	if (media == DL_ETHER) {
    757 		del_arg.linkid = linkid;
    758 		(void) dladm_walk_datalink_id(i_dladm_aggr_link_del, handle,
    759 		    &del_arg, DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
    760 		    DLADM_OPT_PERSIST);
    761 		(void) dladm_walk_datalink_id(i_dladm_vlan_link_del, handle,
    762 		    &del_arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
    763 		    DLADM_OPT_PERSIST);
    764 	}
    765 
    766 	(void) dladm_remove_conf(handle, linkid);
    767 	(void) dladm_destroy_datalink_id(handle, linkid, DLADM_OPT_PERSIST);
    768 done:
    769 	del_phys_arg->rval = status;
    770 	return (DLADM_WALK_CONTINUE);
    771 }
    772 
    773 dladm_status_t
    774 dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid)
    775 {
    776 	del_phys_arg_t	arg = {DLADM_STATUS_OK};
    777 
    778 	if (linkid == DATALINK_ALL_LINKID) {
    779 		(void) dladm_walk_datalink_id(i_dladm_phys_delete, handle, &arg,
    780 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
    781 		    DLADM_OPT_PERSIST);
    782 		return (DLADM_STATUS_OK);
    783 	} else {
    784 		(void) i_dladm_phys_delete(handle, linkid, &arg);
    785 		return (arg.rval);
    786 	}
    787 }
    788 
    789 dladm_status_t
    790 dladm_phys_info(dladm_handle_t handle, datalink_id_t linkid,
    791     dladm_phys_attr_t *dpap, uint32_t flags)
    792 {
    793 	dladm_status_t	status;
    794 
    795 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
    796 
    797 	switch (flags) {
    798 	case DLADM_OPT_PERSIST: {
    799 		dladm_conf_t	conf;
    800 
    801 		status = dladm_read_conf(handle, linkid, &conf);
    802 		if (status != DLADM_STATUS_OK)
    803 			return (status);
    804 
    805 		status = dladm_get_conf_field(handle, conf, FDEVNAME,
    806 		    dpap->dp_dev, MAXLINKNAMELEN);
    807 		dladm_destroy_conf(handle, conf);
    808 		return (status);
    809 	}
    810 	case DLADM_OPT_ACTIVE: {
    811 		dld_ioc_phys_attr_t	dip;
    812 
    813 		dip.dip_linkid = linkid;
    814 		if (ioctl(dladm_dld_fd(handle), DLDIOC_PHYS_ATTR, &dip) < 0) {
    815 			status = dladm_errno2status(errno);
    816 			return (status);
    817 		}
    818 		dpap->dp_novanity = dip.dip_novanity;
    819 		(void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
    820 		return (DLADM_STATUS_OK);
    821 	}
    822 	default:
    823 		return (DLADM_STATUS_BADARG);
    824 	}
    825 }
    826 
    827 typedef struct i_walk_dev_state_s {
    828 	const char *devname;
    829 	datalink_id_t linkid;
    830 	boolean_t found;
    831 } i_walk_dev_state_t;
    832 
    833 int
    834 i_dladm_walk_dev2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
    835 {
    836 	dladm_phys_attr_t dpa;
    837 	dladm_status_t status;
    838 	i_walk_dev_state_t *statep = arg;
    839 
    840 	status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_PERSIST);
    841 	if ((status == DLADM_STATUS_OK) &&
    842 	    (strcmp(statep->devname, dpa.dp_dev) == 0)) {
    843 		statep->found = B_TRUE;
    844 		statep->linkid = linkid;
    845 		return (DLADM_WALK_TERMINATE);
    846 	}
    847 	return (DLADM_WALK_CONTINUE);
    848 }
    849 
    850 /*
    851  * Get the linkid from the physical device name.
    852  */
    853 dladm_status_t
    854 dladm_dev2linkid(dladm_handle_t handle, const char *devname,
    855     datalink_id_t *linkidp)
    856 {
    857 	i_walk_dev_state_t state;
    858 
    859 	state.found = B_FALSE;
    860 	state.devname = devname;
    861 
    862 	(void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, handle, &state,
    863 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
    864 	if (state.found == B_TRUE) {
    865 		*linkidp = state.linkid;
    866 		return (DLADM_STATUS_OK);
    867 	} else {
    868 		return (dladm_errno2status(ENOENT));
    869 	}
    870 }
    871 
    872 static int
    873 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
    874 {
    875 	char	*cp, *tp;
    876 	int	len;
    877 
    878 	/*
    879 	 * device name length must not be 0, and it must end with digit.
    880 	 */
    881 	if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
    882 		return (EINVAL);
    883 
    884 	(void) strlcpy(driver, devname, maxlen);
    885 	cp = (char *)&driver[len - 1];
    886 
    887 	for (tp = cp; isdigit(*tp); tp--) {
    888 		if (tp <= driver)
    889 			return (EINVAL);
    890 	}
    891 
    892 	*ppa = atoi(tp + 1);
    893 	*(tp + 1) = '\0';
    894 	return (0);
    895 }
    896 
    897 dladm_status_t
    898 dladm_linkid2legacyname(dladm_handle_t handle, datalink_id_t linkid, char *dev,
    899     size_t len)
    900 {
    901 	char			devname[MAXLINKNAMELEN];
    902 	uint16_t		vid = VLAN_ID_NONE;
    903 	datalink_class_t	class;
    904 	dladm_status_t		status;
    905 
    906 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
    907 	    NULL, 0);
    908 	if (status != DLADM_STATUS_OK)
    909 		goto done;
    910 
    911 	/*
    912 	 * If this is a VLAN, we must first determine the class and linkid of
    913 	 * the link the VLAN has been created over.
    914 	 */
    915 	if (class == DATALINK_CLASS_VLAN) {
    916 		dladm_vlan_attr_t	dva;
    917 
    918 		status = dladm_vlan_info(handle, linkid, &dva,
    919 		    DLADM_OPT_ACTIVE);
    920 		if (status != DLADM_STATUS_OK)
    921 			goto done;
    922 		linkid = dva.dv_linkid;
    923 		vid = dva.dv_vid;
    924 
    925 		if ((status = dladm_datalink_id2info(handle, linkid, NULL,
    926 		    &class, NULL, NULL, 0)) != DLADM_STATUS_OK) {
    927 			goto done;
    928 		}
    929 	}
    930 
    931 	switch (class) {
    932 	case DATALINK_CLASS_AGGR: {
    933 		dladm_aggr_grp_attr_t	dga;
    934 
    935 		status = dladm_aggr_info(handle, linkid, &dga,
    936 		    DLADM_OPT_ACTIVE);
    937 		if (status != DLADM_STATUS_OK)
    938 			goto done;
    939 
    940 		if (dga.lg_key == 0) {
    941 			/*
    942 			 * If the key was not specified when the aggregation
    943 			 * is created, we cannot guess its /dev node name.
    944 			 */
    945 			status = DLADM_STATUS_BADARG;
    946 			goto done;
    947 		}
    948 		(void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
    949 		break;
    950 	}
    951 	case DATALINK_CLASS_PHYS: {
    952 		dladm_phys_attr_t	dpa;
    953 
    954 		status = dladm_phys_info(handle, linkid, &dpa,
    955 		    DLADM_OPT_PERSIST);
    956 		if (status != DLADM_STATUS_OK)
    957 			goto done;
    958 
    959 		(void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
    960 		break;
    961 	}
    962 	default:
    963 		status = DLADM_STATUS_BADARG;
    964 		goto done;
    965 	}
    966 
    967 	if (vid != VLAN_ID_NONE) {
    968 		char		drv[MAXNAMELEN];
    969 		uint_t		ppa;
    970 
    971 		if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
    972 			status = DLADM_STATUS_BADARG;
    973 			goto done;
    974 		}
    975 		if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
    976 			status = DLADM_STATUS_TOOSMALL;
    977 	} else {
    978 		if (strlcpy(dev, devname, len) >= len)
    979 			status = DLADM_STATUS_TOOSMALL;
    980 	}
    981 
    982 done:
    983 	return (status);
    984 }
    985 
    986 dladm_status_t
    987 dladm_parselink(const char *dev, char *provider, uint_t *ppa)
    988 {
    989 	ifspec_t	ifsp;
    990 
    991 	if (dev == NULL || !ifparse_ifspec(dev, &ifsp))
    992 		return (DLADM_STATUS_LINKINVAL);
    993 
    994 	if (provider != NULL)
    995 		(void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
    996 
    997 	if (ppa != NULL)
    998 		*ppa = ifsp.ifsp_ppa;
    999 
   1000 	return (DLADM_STATUS_OK);
   1001 }
   1002