13871Syz147064 /*
23871Syz147064  * CDDL HEADER START
33871Syz147064  *
43871Syz147064  * The contents of this file are subject to the terms of the
53871Syz147064  * Common Development and Distribution License (the "License").
63871Syz147064  * You may not use this file except in compliance with the License.
73871Syz147064  *
83871Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93871Syz147064  * or http://www.opensolaris.org/os/licensing.
103871Syz147064  * See the License for the specific language governing permissions
113871Syz147064  * and limitations under the License.
123871Syz147064  *
133871Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
143871Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153871Syz147064  * If applicable, add the following below this CDDL HEADER, with the
163871Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
173871Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
183871Syz147064  *
193871Syz147064  * CDDL HEADER END
203871Syz147064  */
213871Syz147064 /*
225895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
233871Syz147064  * Use is subject to license terms.
243871Syz147064  */
253871Syz147064 
263871Syz147064 #include <sys/types.h>
273871Syz147064 #include <unistd.h>
283871Syz147064 #include <errno.h>
293871Syz147064 #include <fcntl.h>
305895Syz147064 #include <assert.h>
315895Syz147064 #include <ctype.h>
323871Syz147064 #include <strings.h>
333871Syz147064 #include <sys/stat.h>
343871Syz147064 #include <sys/dld.h>
355895Syz147064 #include <sys/vlan.h>
367342SAruna.Ramakrishna@Sun.COM #include <zone.h>
375895Syz147064 #include <librcm.h>
383871Syz147064 #include <libdlpi.h>
393871Syz147064 #include <libdevinfo.h>
405895Syz147064 #include <libdlaggr.h>
415895Syz147064 #include <libdlvlan.h>
423871Syz147064 #include <libdllink.h>
435895Syz147064 #include <libdlmgmt.h>
443871Syz147064 #include <libdladm_impl.h>
455903Ssowmini #include <libinetutil.h>
463871Syz147064 
473871Syz147064 /*
483871Syz147064  * Return the attributes of the specified datalink from the DLD driver.
493871Syz147064  */
505895Syz147064 static dladm_status_t
515895Syz147064 i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap)
523871Syz147064 {
533871Syz147064 	dld_ioc_attr_t	dia;
543871Syz147064 
555895Syz147064 	dia.dia_linkid = linkid;
563871Syz147064 
57*7408SSebastien.Roy@Sun.COM 	if (ioctl(fd, DLDIOC_ATTR, &dia) < 0)
585895Syz147064 		return (dladm_errno2status(errno));
593871Syz147064 
603871Syz147064 	dap->da_max_sdu = dia.dia_max_sdu;
613871Syz147064 
625895Syz147064 	return (DLADM_STATUS_OK);
633871Syz147064 }
643871Syz147064 
655895Syz147064 struct i_dladm_walk_arg {
665895Syz147064 	dladm_walkcb_t *fn;
675895Syz147064 	void *arg;
685895Syz147064 };
693871Syz147064 
705895Syz147064 static int
715895Syz147064 i_dladm_walk(datalink_id_t linkid, void *arg)
725895Syz147064 {
735895Syz147064 	struct i_dladm_walk_arg *walk_arg = arg;
745895Syz147064 	char link[MAXLINKNAMELEN];
753871Syz147064 
765895Syz147064 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
775895Syz147064 	    sizeof (link)) == DLADM_STATUS_OK) {
785895Syz147064 		return (walk_arg->fn(link, walk_arg->arg));
793871Syz147064 	}
803871Syz147064 
815895Syz147064 	return (DLADM_WALK_CONTINUE);
823871Syz147064 }
833871Syz147064 
843871Syz147064 /*
855895Syz147064  * Walk all datalinks.
863871Syz147064  */
875895Syz147064 dladm_status_t
885895Syz147064 dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class,
895895Syz147064     datalink_media_t dmedia, uint32_t flags)
903871Syz147064 {
915895Syz147064 	struct i_dladm_walk_arg walk_arg;
923871Syz147064 
935895Syz147064 	walk_arg.fn = fn;
945895Syz147064 	walk_arg.arg = arg;
955895Syz147064 	return (dladm_walk_datalink_id(i_dladm_walk, &walk_arg,
965895Syz147064 	    class, dmedia, flags));
973871Syz147064 }
983871Syz147064 
993871Syz147064 /*
1005895Syz147064  * These routines are used by administration tools such as dladm(1M) to
1013871Syz147064  * iterate through the list of MAC interfaces
1023871Syz147064  */
1033871Syz147064 
1043871Syz147064 typedef struct dladm_mac_dev {
1053871Syz147064 	char			dm_name[MAXNAMELEN];
1065895Syz147064 	struct dladm_mac_dev    *dm_next;
1073871Syz147064 } dladm_mac_dev_t;
1083871Syz147064 
1093871Syz147064 typedef struct macadm_walk {
1105895Syz147064 	dladm_mac_dev_t	 *dmd_dev_list;
1113871Syz147064 } dladm_mac_walk_t;
1123871Syz147064 
1133871Syz147064 /*
1143871Syz147064  * Local callback invoked for each DDI_NT_NET node.
1153871Syz147064  */
1163871Syz147064 /* ARGSUSED */
1173871Syz147064 static int
1183871Syz147064 i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg)
1193871Syz147064 {
1203871Syz147064 	dladm_mac_walk_t	*dmwp = arg;
1213871Syz147064 	dladm_mac_dev_t		*dmdp = dmwp->dmd_dev_list;
1223871Syz147064 	dladm_mac_dev_t		**last_dmdp = &dmwp->dmd_dev_list;
1233871Syz147064 	char			mac[MAXNAMELEN];
1243871Syz147064 
1253871Syz147064 	(void) snprintf(mac, MAXNAMELEN, "%s%d",
1263871Syz147064 	    di_driver_name(node), di_instance(node));
1273871Syz147064 
1283871Syz147064 	/*
1293871Syz147064 	 * Skip aggregations.
1303871Syz147064 	 */
1313871Syz147064 	if (strcmp("aggr", di_driver_name(node)) == 0)
1323871Syz147064 		return (DI_WALK_CONTINUE);
1333871Syz147064 
1345895Syz147064 	/*
1355895Syz147064 	 * Skip softmacs.
1365895Syz147064 	 */
1375895Syz147064 	if (strcmp("softmac", di_driver_name(node)) == 0)
1385895Syz147064 		return (DI_WALK_CONTINUE);
1395895Syz147064 
1403871Syz147064 	while (dmdp) {
1413871Syz147064 		/*
1423871Syz147064 		 * Skip duplicates.
1433871Syz147064 		 */
1443871Syz147064 		if (strcmp(dmdp->dm_name, mac) == 0)
1453871Syz147064 			return (DI_WALK_CONTINUE);
1463871Syz147064 
1473871Syz147064 		last_dmdp = &dmdp->dm_next;
1483871Syz147064 		dmdp = dmdp->dm_next;
1493871Syz147064 	}
1503871Syz147064 
1513871Syz147064 	if ((dmdp = malloc(sizeof (*dmdp))) == NULL)
1523871Syz147064 		return (DI_WALK_CONTINUE);
1533871Syz147064 
1543871Syz147064 	(void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN);
1553871Syz147064 	dmdp->dm_next = NULL;
1563871Syz147064 	*last_dmdp = dmdp;
1573871Syz147064 
1583871Syz147064 	return (DI_WALK_CONTINUE);
1593871Syz147064 }
1603871Syz147064 
1613871Syz147064 /*
1625895Syz147064  * Invoke the specified callback for each DDI_NT_NET node.
1633871Syz147064  */
1645895Syz147064 dladm_status_t
1655895Syz147064 dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg)
1663871Syz147064 {
1673871Syz147064 	di_node_t		root;
1683871Syz147064 	dladm_mac_walk_t	dmw;
1693871Syz147064 	dladm_mac_dev_t		*dmdp, *next;
1705895Syz147064 	boolean_t		done = B_FALSE;
1713871Syz147064 
1723871Syz147064 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
1735895Syz147064 		return (dladm_errno2status(errno));
1743871Syz147064 
1753871Syz147064 	dmw.dmd_dev_list = NULL;
1763871Syz147064 
1773871Syz147064 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw,
1783871Syz147064 	    i_dladm_mac_walk);
1793871Syz147064 
1803871Syz147064 	di_fini(root);
1813871Syz147064 
1823871Syz147064 	dmdp = dmw.dmd_dev_list;
1833871Syz147064 	for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) {
1843871Syz147064 		next = dmdp->dm_next;
1855895Syz147064 		if (!done &&
1865895Syz147064 		    ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) {
1875895Syz147064 			done = B_TRUE;
1885895Syz147064 		}
1893871Syz147064 		free(dmdp);
1903871Syz147064 	}
1913871Syz147064 
1925895Syz147064 	return (DLADM_STATUS_OK);
1933871Syz147064 }
1943871Syz147064 
1953871Syz147064 /*
1965895Syz147064  * Get the current attributes of the specified datalink.
1973871Syz147064  */
1985895Syz147064 dladm_status_t
1995895Syz147064 dladm_info(datalink_id_t linkid, dladm_attr_t *dap)
2003871Syz147064 {
2013871Syz147064 	int		fd;
2025895Syz147064 	dladm_status_t	status;
2033871Syz147064 
2043871Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
2055895Syz147064 		return (dladm_errno2status(errno));
2063871Syz147064 
2075895Syz147064 	status = i_dladm_info(fd, linkid, dap);
2083871Syz147064 
2093871Syz147064 	(void) close(fd);
2105895Syz147064 	return (status);
2113871Syz147064 }
2123871Syz147064 
2133871Syz147064 const char *
2143871Syz147064 dladm_linkstate2str(link_state_t state, char *buf)
2153871Syz147064 {
2163871Syz147064 	const char	*s;
2173871Syz147064 
2183871Syz147064 	switch (state) {
2193871Syz147064 	case LINK_STATE_UP:
2203871Syz147064 		s = "up";
2213871Syz147064 		break;
2223871Syz147064 	case LINK_STATE_DOWN:
2233871Syz147064 		s = "down";
2243871Syz147064 		break;
2253871Syz147064 	default:
2263871Syz147064 		s = "unknown";
2273871Syz147064 		break;
2283871Syz147064 	}
2293871Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
2303871Syz147064 	return (buf);
2313871Syz147064 }
2323871Syz147064 
2333871Syz147064 const char *
2343871Syz147064 dladm_linkduplex2str(link_duplex_t duplex, char *buf)
2353871Syz147064 {
2363871Syz147064 	const char	*s;
2373871Syz147064 
2383871Syz147064 	switch (duplex) {
2393871Syz147064 	case LINK_DUPLEX_FULL:
2403871Syz147064 		s = "full";
2413871Syz147064 		break;
2423871Syz147064 	case LINK_DUPLEX_HALF:
2433871Syz147064 		s = "half";
2443871Syz147064 		break;
2453871Syz147064 	default:
2463871Syz147064 		s = "unknown";
2473871Syz147064 		break;
2483871Syz147064 	}
2493871Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
2503871Syz147064 	return (buf);
2513871Syz147064 }
2523871Syz147064 
2533871Syz147064 /*
2546233Syz147064  * Set zoneid of a given link. Note that this function takes a link name
2556233Syz147064  * argument instead of a linkid, because a data-link (and its linkid) could
2566233Syz147064  * be created implicitly as the result of this function. For example, a VLAN
2576233Syz147064  * could be created if a VLAN PPA hack name is assigned to an exclusive
2586233Syz147064  * non-global zone.
2595895Syz147064  */
2605895Syz147064 dladm_status_t
2617342SAruna.Ramakrishna@Sun.COM dladm_setzid(const char *dlname, char *zone_name)
2625895Syz147064 {
2637342SAruna.Ramakrishna@Sun.COM 	datalink_id_t	linkid;
2647342SAruna.Ramakrishna@Sun.COM 	char		*val;
2657342SAruna.Ramakrishna@Sun.COM 	char		**prop_val;
2667342SAruna.Ramakrishna@Sun.COM 	char		link[MAXLINKNAMELEN];
2677342SAruna.Ramakrishna@Sun.COM 	uint_t		ppa;
2687342SAruna.Ramakrishna@Sun.COM 	char		dev[DLPI_LINKNAME_MAX];
2697342SAruna.Ramakrishna@Sun.COM 	int		valsize;
2707342SAruna.Ramakrishna@Sun.COM 	dladm_status_t	status = DLADM_STATUS_OK;
2717342SAruna.Ramakrishna@Sun.COM 	char		*prop_name = "zone";
2727342SAruna.Ramakrishna@Sun.COM 	boolean_t	needfree = B_FALSE;
2737342SAruna.Ramakrishna@Sun.COM 	char		delim = ':';
2745895Syz147064 
2757342SAruna.Ramakrishna@Sun.COM 	/* If the link does not exist, it is a ppa-hacked vlan. */
2767342SAruna.Ramakrishna@Sun.COM 	status = dladm_name2info(dlname, &linkid, NULL, NULL, NULL);
2777342SAruna.Ramakrishna@Sun.COM 	switch (status) {
2787342SAruna.Ramakrishna@Sun.COM 	case DLADM_STATUS_NOTFOUND:
2797342SAruna.Ramakrishna@Sun.COM 		if (strlen(dlname) > MAXLINKNAMELEN)
2807342SAruna.Ramakrishna@Sun.COM 			return (DLADM_STATUS_BADVAL);
2817342SAruna.Ramakrishna@Sun.COM 
2827342SAruna.Ramakrishna@Sun.COM 		if (strlen(zone_name) > ZONENAME_MAX)
2837342SAruna.Ramakrishna@Sun.COM 			return (DLADM_STATUS_BADVAL);
2845895Syz147064 
2857342SAruna.Ramakrishna@Sun.COM 		status = dladm_parselink(dlname, dev, &ppa);
2867342SAruna.Ramakrishna@Sun.COM 		if (status != DLADM_STATUS_OK)
2877342SAruna.Ramakrishna@Sun.COM 			return (status);
2885895Syz147064 
2897342SAruna.Ramakrishna@Sun.COM 		ppa = (uint_t)DLS_PPA2INST(ppa);
2907342SAruna.Ramakrishna@Sun.COM 		(void) snprintf(link, sizeof (link), "%s%d", dev, ppa);
2917342SAruna.Ramakrishna@Sun.COM 
2927342SAruna.Ramakrishna@Sun.COM 		status = dladm_name2info(link, &linkid, NULL,  NULL, NULL);
2937342SAruna.Ramakrishna@Sun.COM 		if (status != DLADM_STATUS_OK)
2947342SAruna.Ramakrishna@Sun.COM 			return (status);
2955895Syz147064 
2967342SAruna.Ramakrishna@Sun.COM 		/*
2977342SAruna.Ramakrishna@Sun.COM 		 * Since the link does not exist as yet, we've to pass the
2987342SAruna.Ramakrishna@Sun.COM 		 * link name too as part of data, so that the kernel can
2997342SAruna.Ramakrishna@Sun.COM 		 * create the link. Hence, we're packing the zone_name and
3007342SAruna.Ramakrishna@Sun.COM 		 * the link name into val.
3017342SAruna.Ramakrishna@Sun.COM 		 */
3027342SAruna.Ramakrishna@Sun.COM 		valsize = ZONENAME_MAX + MAXLINKNAMELEN + 1;
3037342SAruna.Ramakrishna@Sun.COM 		val = malloc(valsize);
3047342SAruna.Ramakrishna@Sun.COM 		if (val == NULL)
3057342SAruna.Ramakrishna@Sun.COM 			return (DLADM_STATUS_NOMEM);
3067342SAruna.Ramakrishna@Sun.COM 		needfree = B_TRUE;
3075895Syz147064 
3087342SAruna.Ramakrishna@Sun.COM 		(void) snprintf(val, valsize, "%s%c%s", zone_name,
3097342SAruna.Ramakrishna@Sun.COM 		    delim, dlname);
3105895Syz147064 
3117342SAruna.Ramakrishna@Sun.COM 		break;
3127342SAruna.Ramakrishna@Sun.COM 	case DLADM_STATUS_OK:
3137342SAruna.Ramakrishna@Sun.COM 		/*
3147342SAruna.Ramakrishna@Sun.COM 		 * The link exists, so only the zone_name is being passed as
3157342SAruna.Ramakrishna@Sun.COM 		 * val. We could also pass zone_name + linkname like in the
3167342SAruna.Ramakrishna@Sun.COM 		 * previous case just to maintain consistency, but other calls
3177342SAruna.Ramakrishna@Sun.COM 		 * like set_linkprop() in dladm.c [which is called when we run
3187342SAruna.Ramakrishna@Sun.COM 		 * 'dladm set-linkprop -p zone <linkname>' at the command line]
3197342SAruna.Ramakrishna@Sun.COM 		 * pass in the value entered at the command line [which is zone
3207342SAruna.Ramakrishna@Sun.COM 		 * name] as val.
3217342SAruna.Ramakrishna@Sun.COM 		 */
3227342SAruna.Ramakrishna@Sun.COM 		val = zone_name;
3237342SAruna.Ramakrishna@Sun.COM 		break;
3247342SAruna.Ramakrishna@Sun.COM 	default:
3257342SAruna.Ramakrishna@Sun.COM 		return (DLADM_STATUS_FAILED);
3267342SAruna.Ramakrishna@Sun.COM 	}
3275895Syz147064 
3287342SAruna.Ramakrishna@Sun.COM 	prop_val = &val;
3297342SAruna.Ramakrishna@Sun.COM 	status = dladm_set_linkprop(linkid, prop_name, prop_val, 1,
3307342SAruna.Ramakrishna@Sun.COM 	    DLADM_OPT_ACTIVE);
3315895Syz147064 
3327342SAruna.Ramakrishna@Sun.COM 	if (needfree)
3337342SAruna.Ramakrishna@Sun.COM 		free(val);
3345895Syz147064 	return (status);
3355895Syz147064 }
3365895Syz147064 
3375895Syz147064 /*
3385895Syz147064  * Case 1: rename an existing link1 to a link2 that does not exist.
3395895Syz147064  * Result: <linkid1, link2>
3403871Syz147064  */
3415895Syz147064 static dladm_status_t
3425895Syz147064 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1,
3435895Syz147064     const char *link2, uint32_t flags)
3445895Syz147064 {
3455895Syz147064 	dld_ioc_rename_t	dir;
3465895Syz147064 	dladm_conf_t		conf;
3475895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
3485895Syz147064 	int			fd;
3495895Syz147064 
3505895Syz147064 	/*
3515895Syz147064 	 * Link is currently available. Check to see whether anything is
3525895Syz147064 	 * holding this link to prevent a rename operation.
3535895Syz147064 	 */
3545895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
3555895Syz147064 		dir.dir_linkid1 = linkid1;
3565895Syz147064 		dir.dir_linkid2 = DATALINK_INVALID_LINKID;
3575895Syz147064 		(void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
3585895Syz147064 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
3595895Syz147064 			return (dladm_errno2status(errno));
3605895Syz147064 
361*7408SSebastien.Roy@Sun.COM 		if (ioctl(fd, DLDIOC_RENAME, &dir) < 0) {
3625895Syz147064 			status = dladm_errno2status(errno);
3635895Syz147064 			(void) close(fd);
3645895Syz147064 			return (status);
3655895Syz147064 		}
3665895Syz147064 	}
3675895Syz147064 
3685895Syz147064 	status = dladm_remap_datalink_id(linkid1, link2);
3695895Syz147064 	if (status != DLADM_STATUS_OK)
3705895Syz147064 		goto done;
3715895Syz147064 
3725895Syz147064 	/*
3735895Syz147064 	 * Flush the current mapping to persistent configuration.
3745895Syz147064 	 */
3755895Syz147064 	if ((flags & DLADM_OPT_PERSIST) &&
3765895Syz147064 	    (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) ||
3775895Syz147064 	    ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) {
3785895Syz147064 		(void) dladm_remap_datalink_id(linkid1, link1);
3795895Syz147064 	}
3805895Syz147064 done:
3815895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
3825895Syz147064 		if (status != DLADM_STATUS_OK) {
3835895Syz147064 			(void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
384*7408SSebastien.Roy@Sun.COM 			(void) ioctl(fd, DLDIOC_RENAME, &dir);
3855895Syz147064 		}
3865895Syz147064 		(void) close(fd);
3875895Syz147064 	}
3885895Syz147064 	return (status);
3895895Syz147064 }
3905895Syz147064 
3915895Syz147064 typedef struct link_hold_arg_s {
3925895Syz147064 	datalink_id_t	linkid;
3935895Syz147064 	datalink_id_t	holder;
3945895Syz147064 	uint32_t	flags;
3955895Syz147064 } link_hold_arg_t;
3965895Syz147064 
3975895Syz147064 static int
3985895Syz147064 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg)
3995895Syz147064 {
4005895Syz147064 	link_hold_arg_t		*hold_arg = arg;
4015895Syz147064 	dladm_aggr_grp_attr_t	ginfo;
4025895Syz147064 	dladm_status_t		status;
4035895Syz147064 	int			i;
4045895Syz147064 
4055895Syz147064 	status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags);
4065895Syz147064 	if (status != DLADM_STATUS_OK)
4075895Syz147064 		return (DLADM_WALK_CONTINUE);
4085895Syz147064 
4095895Syz147064 	for (i = 0; i < ginfo.lg_nports; i++) {
4105895Syz147064 		if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
4115895Syz147064 			hold_arg->holder = aggrid;
4125895Syz147064 			return (DLADM_WALK_TERMINATE);
4135895Syz147064 		}
4145895Syz147064 	}
4155895Syz147064 	return (DLADM_WALK_CONTINUE);
4165895Syz147064 }
4175895Syz147064 
4185895Syz147064 static int
4195895Syz147064 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg)
4205895Syz147064 {
4215895Syz147064 	link_hold_arg_t		*hold_arg = arg;
4225895Syz147064 	dladm_vlan_attr_t	vinfo;
4235895Syz147064 	dladm_status_t		status;
4245895Syz147064 
4255895Syz147064 	status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags);
4265895Syz147064 	if (status != DLADM_STATUS_OK)
4275895Syz147064 		return (DLADM_WALK_CONTINUE);
4285895Syz147064 
4295895Syz147064 	if (vinfo.dv_linkid == hold_arg->linkid) {
4305895Syz147064 		hold_arg->holder = vlanid;
4315895Syz147064 		return (DLADM_WALK_TERMINATE);
4325895Syz147064 	}
4335895Syz147064 	return (DLADM_WALK_CONTINUE);
4345895Syz147064 }
4355895Syz147064 
4365895Syz147064 /*
4375895Syz147064  * Case 2: rename an available physical link link1 to a REMOVED physical link
4385895Syz147064  *     link2.  As a result, link1 directly inherits all datalinks configured
4395895Syz147064  *     over link2 (linkid2).
4405895Syz147064  * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
4415895Syz147064  *     link2_other_attr>
4425895Syz147064  */
4435895Syz147064 static dladm_status_t
4445895Syz147064 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2)
4453871Syz147064 {
4465895Syz147064 	rcm_handle_t		*rcm_hdl = NULL;
4475895Syz147064 	nvlist_t		*nvl = NULL;
4485895Syz147064 	link_hold_arg_t		arg;
4495895Syz147064 	dld_ioc_rename_t	dir;
4505895Syz147064 	int			fd;
4515895Syz147064 	dladm_conf_t		conf1, conf2;
4525895Syz147064 	char			devname[MAXLINKNAMELEN];
4535895Syz147064 	uint64_t		phymaj, phyinst;
4545895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
4555895Syz147064 
4565895Syz147064 	/*
4575895Syz147064 	 * First check if linkid1 is associated with any persistent
4585895Syz147064 	 * aggregations or VLANs. If yes, return BUSY.
4595895Syz147064 	 */
4605895Syz147064 	arg.linkid = linkid1;
4615895Syz147064 	arg.holder = DATALINK_INVALID_LINKID;
4625895Syz147064 	arg.flags = DLADM_OPT_PERSIST;
4635895Syz147064 	(void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg,
4645895Syz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
4655895Syz147064 	if (arg.holder != DATALINK_INVALID_LINKID)
4665895Syz147064 		return (DLADM_STATUS_LINKBUSY);
4675895Syz147064 
4685895Syz147064 	arg.flags = DLADM_OPT_PERSIST;
4695895Syz147064 	(void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg,
4705895Syz147064 	    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
4715895Syz147064 	if (arg.holder != DATALINK_INVALID_LINKID)
4725895Syz147064 		return (DLADM_STATUS_LINKBUSY);
4735895Syz147064 
4745895Syz147064 	/*
4755895Syz147064 	 * Send DLDIOC_RENAME to request to rename link1's linkid to
4765895Syz147064 	 * be linkid2. This will check whether link1 is used by any
4775895Syz147064 	 * aggregations or VLANs, or is held by any application. If yes,
4785895Syz147064 	 * return failure.
4795895Syz147064 	 */
4805895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
4815895Syz147064 		return (dladm_errno2status(errno));
4825895Syz147064 
4835895Syz147064 	dir.dir_linkid1 = linkid1;
4845895Syz147064 	dir.dir_linkid2 = linkid2;
485*7408SSebastien.Roy@Sun.COM 	if (ioctl(fd, DLDIOC_RENAME, &dir) < 0)
4865895Syz147064 		status = dladm_errno2status(errno);
4875895Syz147064 
4885895Syz147064 	if (status != DLADM_STATUS_OK) {
4895895Syz147064 		(void) close(fd);
4905895Syz147064 		return (status);
4915895Syz147064 	}
4925895Syz147064 
4935895Syz147064 	/*
4945895Syz147064 	 * Now change the phymaj, phyinst and devname associated with linkid1
4955895Syz147064 	 * to be associated with linkid2. Before doing that, the old active
4965895Syz147064 	 * linkprop of linkid1 should be deleted.
4975895Syz147064 	 */
4985895Syz147064 	(void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE);
4995895Syz147064 
5005895Syz147064 	if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) ||
5015895Syz147064 	    ((status = dladm_get_conf_field(conf1, FDEVNAME, devname,
5025895Syz147064 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
5035895Syz147064 	    ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj,
5045895Syz147064 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
5055895Syz147064 	    ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst,
5065895Syz147064 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
5075895Syz147064 	    ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) {
5085895Syz147064 		dir.dir_linkid1 = linkid2;
5095895Syz147064 		dir.dir_linkid2 = linkid1;
5106916Sartem 		(void) dladm_init_linkprop(linkid1, B_FALSE);
511*7408SSebastien.Roy@Sun.COM 		(void) ioctl(fd, DLDIOC_RENAME, &dir);
5125895Syz147064 		(void) close(fd);
5135895Syz147064 		return (status);
5145895Syz147064 	}
5155895Syz147064 	(void) close(fd);
5165895Syz147064 
5175895Syz147064 	dladm_destroy_conf(conf1);
5185895Syz147064 	(void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname);
5195895Syz147064 	(void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj);
5205895Syz147064 	(void) dladm_set_conf_field(conf2, FPHYINST,
5215895Syz147064 	    DLADM_TYPE_UINT64, &phyinst);
5225895Syz147064 	(void) dladm_write_conf(conf2);
5235895Syz147064 	dladm_destroy_conf(conf2);
5245895Syz147064 
5255895Syz147064 	/*
5265895Syz147064 	 * Delete link1 and mark link2 up.
5275895Syz147064 	 */
5285895Syz147064 	(void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE |
5295895Syz147064 	    DLADM_OPT_PERSIST);
5305895Syz147064 	(void) dladm_remove_conf(linkid1);
5315895Syz147064 	(void) dladm_up_datalink_id(linkid2);
5325895Syz147064 
5335895Syz147064 	/*
5345895Syz147064 	 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
5355895Syz147064 	 * consumed by the RCM framework to restore all the datalink and
5365895Syz147064 	 * IP configuration.
5375895Syz147064 	 */
5385895Syz147064 	status = DLADM_STATUS_FAILED;
5395895Syz147064 	if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
5405895Syz147064 	    (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
5415895Syz147064 		goto done;
5425895Syz147064 	}
5435895Syz147064 
5445895Syz147064 	if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
5455895Syz147064 		goto done;
5465895Syz147064 
5475895Syz147064 	if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
5485895Syz147064 	    RCM_SUCCESS) {
5495895Syz147064 		status = DLADM_STATUS_OK;
5505895Syz147064 	}
5515895Syz147064 
5525895Syz147064 done:
5535895Syz147064 	if (rcm_hdl != NULL)
5545895Syz147064 		(void) rcm_free_handle(rcm_hdl);
5555895Syz147064 	if (nvl != NULL)
5565895Syz147064 		nvlist_free(nvl);
5575895Syz147064 	return (status);
5583871Syz147064 }
5593871Syz147064 
5603871Syz147064 /*
5615895Syz147064  * case 3: rename a non-existent link to a REMOVED physical link.
5625895Syz147064  * Set the removed physical link's device name to link1, so that
5635895Syz147064  * when link1 attaches, it inherits all the link configuration of
5645895Syz147064  * the removed physical link.
5653871Syz147064  */
5665895Syz147064 static dladm_status_t
5675895Syz147064 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2)
5685895Syz147064 {
5695895Syz147064 	dladm_conf_t	conf;
5705895Syz147064 	dladm_status_t	status;
5715895Syz147064 
5725895Syz147064 	if (!dladm_valid_linkname(link1))
5735895Syz147064 		return (DLADM_STATUS_LINKINVAL);
5745895Syz147064 
5755895Syz147064 	status = dladm_read_conf(linkid2, &conf);
5765895Syz147064 	if (status != DLADM_STATUS_OK)
5775895Syz147064 		goto done;
5785895Syz147064 
5795895Syz147064 	if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR,
5805895Syz147064 	    link1)) == DLADM_STATUS_OK) {
5815895Syz147064 		status = dladm_write_conf(conf);
5825895Syz147064 	}
5835895Syz147064 
5845895Syz147064 	dladm_destroy_conf(conf);
5855895Syz147064 
5865895Syz147064 done:
5875895Syz147064 	return (status);
5885895Syz147064 }
5895895Syz147064 
5905895Syz147064 dladm_status_t
5915895Syz147064 dladm_rename_link(const char *link1, const char *link2)
5925895Syz147064 {
5935895Syz147064 	datalink_id_t		linkid1 = DATALINK_INVALID_LINKID;
5945895Syz147064 	datalink_id_t		linkid2 = DATALINK_INVALID_LINKID;
5955895Syz147064 	uint32_t		flags1, flags2;
5965895Syz147064 	datalink_class_t	class1, class2;
5975895Syz147064 	uint32_t		media1, media2;
5985895Syz147064 	boolean_t		remphy2 = B_FALSE;
5995895Syz147064 	dladm_status_t  	status;
6005895Syz147064 
6015895Syz147064 	(void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1);
6025895Syz147064 	if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) ==
6035895Syz147064 	    DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
6045895Syz147064 	    (flags2 == DLADM_OPT_PERSIST)) {
6055895Syz147064 		/*
6065895Syz147064 		 * see whether link2 is a removed physical link.
6075895Syz147064 		 */
6085895Syz147064 		remphy2 = B_TRUE;
6095895Syz147064 	}
6105895Syz147064 
6115895Syz147064 	if (linkid1 != DATALINK_INVALID_LINKID) {
6125895Syz147064 		if (linkid2 == DATALINK_INVALID_LINKID) {
6135895Syz147064 			/*
6145895Syz147064 			 * case 1: rename an existing link to a link that
6155895Syz147064 			 * does not exist.
6165895Syz147064 			 */
6175895Syz147064 			status = i_dladm_rename_link_c1(linkid1, link1, link2,
6185895Syz147064 			    flags1);
6195895Syz147064 		} else if (remphy2) {
6205895Syz147064 			/*
6215895Syz147064 			 * case 2: rename an available link to a REMOVED
6225895Syz147064 			 * physical link. Return failure if link1 is not
6235895Syz147064 			 * an active physical link.
6245895Syz147064 			 */
6255895Syz147064 			if ((class1 != class2) || (media1 != media2) ||
6265895Syz147064 			    !(flags1 & DLADM_OPT_ACTIVE)) {
6275895Syz147064 				status = DLADM_STATUS_BADARG;
6285895Syz147064 			} else {
6295895Syz147064 				status = i_dladm_rename_link_c2(linkid1,
6305895Syz147064 				    linkid2);
6315895Syz147064 			}
6325895Syz147064 		} else {
6335895Syz147064 			status = DLADM_STATUS_EXIST;
6345895Syz147064 		}
6355895Syz147064 	} else if (remphy2) {
6365895Syz147064 		status = i_dladm_rename_link_c3(link1, linkid2);
6375895Syz147064 	} else {
6385895Syz147064 		status = DLADM_STATUS_NOTFOUND;
6395895Syz147064 	}
6405895Syz147064 	return (status);
6415895Syz147064 }
6425895Syz147064 
6435895Syz147064 typedef struct consumer_del_phys_arg_s {
6445895Syz147064 	datalink_id_t	linkid;
6455895Syz147064 } consumer_del_phys_arg_t;
6465895Syz147064 
6475895Syz147064 static int
6485895Syz147064 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg)
6495895Syz147064 {
6505895Syz147064 	consumer_del_phys_arg_t	*del_arg = arg;
6515895Syz147064 	dladm_vlan_attr_t	vinfo;
6525895Syz147064 	dladm_status_t		status;
6535895Syz147064 
6545895Syz147064 	status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST);
6555895Syz147064 	if (status != DLADM_STATUS_OK)
6565895Syz147064 		return (DLADM_WALK_CONTINUE);
6575895Syz147064 
6585895Syz147064 	if (vinfo.dv_linkid == del_arg->linkid)
6595895Syz147064 		(void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST);
6605895Syz147064 	return (DLADM_WALK_CONTINUE);
6615895Syz147064 }
6625895Syz147064 
6635895Syz147064 static int
6645895Syz147064 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg)
6655895Syz147064 {
6665895Syz147064 	consumer_del_phys_arg_t		*del_arg = arg;
6675895Syz147064 	dladm_aggr_grp_attr_t		ginfo;
6685895Syz147064 	dladm_status_t			status;
6695895Syz147064 	dladm_aggr_port_attr_db_t	port[1];
6705895Syz147064 	int				i;
6715895Syz147064 
6725895Syz147064 	status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST);
6735895Syz147064 	if (status != DLADM_STATUS_OK)
6745895Syz147064 		return (DLADM_WALK_CONTINUE);
6755895Syz147064 
6765895Syz147064 	for (i = 0; i < ginfo.lg_nports; i++)
6775895Syz147064 		if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
6785895Syz147064 			break;
6795895Syz147064 
6805895Syz147064 	if (i != ginfo.lg_nports) {
6815895Syz147064 		if (ginfo.lg_nports == 1 && i == 0) {
6825895Syz147064 			consumer_del_phys_arg_t	aggr_del_arg;
6835895Syz147064 
6845895Syz147064 			/*
6855895Syz147064 			 * First delete all the VLANs on this aggregation, then
6865895Syz147064 			 * delete the aggregation itself.
6875895Syz147064 			 */
6885895Syz147064 			aggr_del_arg.linkid = aggrid;
6895895Syz147064 			(void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
6905895Syz147064 			    &aggr_del_arg, DATALINK_CLASS_VLAN,
6915895Syz147064 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
6925895Syz147064 			(void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST);
6935895Syz147064 		} else {
6945895Syz147064 			port[0].lp_linkid = del_arg->linkid;
6955895Syz147064 			(void) dladm_aggr_remove(aggrid, 1, port,
6965895Syz147064 			    DLADM_OPT_PERSIST);
6975895Syz147064 		}
6985895Syz147064 	}
6995895Syz147064 	return (DLADM_WALK_CONTINUE);
7005895Syz147064 }
7015895Syz147064 
7025895Syz147064 typedef struct del_phys_arg_s {
7035895Syz147064 	dladm_status_t	rval;
7045895Syz147064 } del_phys_arg_t;
7055895Syz147064 
7065895Syz147064 static int
7075895Syz147064 i_dladm_phys_delete(datalink_id_t linkid, void *arg)
7085895Syz147064 {
7095895Syz147064 	uint32_t		flags;
7105895Syz147064 	datalink_class_t	class;
7115895Syz147064 	uint32_t		media;
7125895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
7135895Syz147064 	del_phys_arg_t		*del_phys_arg = arg;
7145895Syz147064 	consumer_del_phys_arg_t	del_arg;
7155895Syz147064 
7165895Syz147064 	if ((status = dladm_datalink_id2info(linkid, &flags, &class,
7175895Syz147064 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
7185895Syz147064 		goto done;
7195895Syz147064 	}
7205895Syz147064 
7215895Syz147064 	/*
7225895Syz147064 	 * see whether this link is a removed physical link.
7235895Syz147064 	 */
7245895Syz147064 	if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
7255895Syz147064 	    (flags & DLADM_OPT_ACTIVE)) {
7265895Syz147064 		status = DLADM_STATUS_BADARG;
7275895Syz147064 		goto done;
7285895Syz147064 	}
7295895Syz147064 
7305895Syz147064 	if (media == DL_ETHER) {
7315895Syz147064 		del_arg.linkid = linkid;
7325895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg,
7335895Syz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
7345895Syz147064 		    DLADM_OPT_PERSIST);
7355895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg,
7365895Syz147064 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
7375895Syz147064 		    DLADM_OPT_PERSIST);
7385895Syz147064 	}
7395895Syz147064 
7405895Syz147064 	(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
7415895Syz147064 	(void) dladm_remove_conf(linkid);
7425895Syz147064 
7435895Syz147064 done:
7445895Syz147064 	del_phys_arg->rval = status;
7455895Syz147064 	return (DLADM_WALK_CONTINUE);
7465895Syz147064 }
7475895Syz147064 
7485895Syz147064 dladm_status_t
7495895Syz147064 dladm_phys_delete(datalink_id_t linkid)
7505895Syz147064 {
7515895Syz147064 	del_phys_arg_t	arg = {DLADM_STATUS_OK};
7525895Syz147064 
7535895Syz147064 	if (linkid == DATALINK_ALL_LINKID) {
7545895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg,
7555895Syz147064 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
7565895Syz147064 		    DLADM_OPT_PERSIST);
7575895Syz147064 		return (DLADM_STATUS_OK);
7585895Syz147064 	} else {
7595895Syz147064 		(void) i_dladm_phys_delete(linkid, &arg);
7605895Syz147064 		return (arg.rval);
7615895Syz147064 	}
7625895Syz147064 }
7635895Syz147064 
7645895Syz147064 dladm_status_t
7655895Syz147064 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags)
7665895Syz147064 {
7675895Syz147064 	dladm_status_t	status;
7685895Syz147064 
7695895Syz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
7705895Syz147064 
7715895Syz147064 	switch (flags) {
7725895Syz147064 	case DLADM_OPT_PERSIST: {
7735895Syz147064 		dladm_conf_t	conf;
7745895Syz147064 
7755895Syz147064 		status = dladm_read_conf(linkid, &conf);
7765895Syz147064 		if (status != DLADM_STATUS_OK)
7775895Syz147064 			return (status);
7785895Syz147064 
7795895Syz147064 		status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev,
7805895Syz147064 		    MAXLINKNAMELEN);
7815895Syz147064 		dladm_destroy_conf(conf);
7825895Syz147064 		return (status);
7835895Syz147064 	}
7845895Syz147064 	case DLADM_OPT_ACTIVE: {
7855895Syz147064 		dld_ioc_phys_attr_t	dip;
7865895Syz147064 		int			fd;
7875895Syz147064 
7885895Syz147064 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
7895895Syz147064 			return (dladm_errno2status(errno));
7905895Syz147064 
7915895Syz147064 		dip.dip_linkid = linkid;
792*7408SSebastien.Roy@Sun.COM 		if (ioctl(fd, DLDIOC_PHYS_ATTR, &dip) < 0) {
7935895Syz147064 			status = dladm_errno2status(errno);
7945895Syz147064 			(void) close(fd);
7955895Syz147064 			return (status);
7965895Syz147064 		}
7975895Syz147064 		(void) close(fd);
7985895Syz147064 		dpap->dp_novanity = dip.dip_novanity;
7995895Syz147064 		(void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
8005895Syz147064 		return (DLADM_STATUS_OK);
8015895Syz147064 	}
8025895Syz147064 	default:
8035895Syz147064 		return (DLADM_STATUS_BADARG);
8045895Syz147064 	}
8055895Syz147064 }
8065895Syz147064 
8075895Syz147064 typedef struct i_walk_dev_state_s {
8085895Syz147064 	const char *devname;
8095895Syz147064 	datalink_id_t linkid;
8105895Syz147064 	boolean_t found;
8115895Syz147064 } i_walk_dev_state_t;
8125895Syz147064 
8133871Syz147064 int
8145895Syz147064 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg)
8155895Syz147064 {
8165895Syz147064 	dladm_phys_attr_t dpa;
8175895Syz147064 	dladm_status_t status;
8185895Syz147064 	i_walk_dev_state_t *statep = arg;
8195895Syz147064 
8205895Syz147064 	status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
8215895Syz147064 	if ((status == DLADM_STATUS_OK) &&
8225895Syz147064 	    (strcmp(statep->devname, dpa.dp_dev) == 0)) {
8235895Syz147064 		statep->found = B_TRUE;
8245895Syz147064 		statep->linkid = linkid;
8255895Syz147064 		return (DLADM_WALK_TERMINATE);
8265895Syz147064 	}
8275895Syz147064 	return (DLADM_WALK_CONTINUE);
8285895Syz147064 }
8295895Syz147064 
8305895Syz147064 /*
8315895Syz147064  * Get the linkid from the physical device name.
8325895Syz147064  */
8335895Syz147064 dladm_status_t
8345895Syz147064 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp)
8355895Syz147064 {
8365895Syz147064 	i_walk_dev_state_t state;
8375895Syz147064 
8385895Syz147064 	state.found = B_FALSE;
8395895Syz147064 	state.devname = devname;
8405895Syz147064 
8415895Syz147064 	(void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state,
8425895Syz147064 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
8435895Syz147064 	if (state.found == B_TRUE) {
8445895Syz147064 		*linkidp = state.linkid;
8455895Syz147064 		return (DLADM_STATUS_OK);
8465895Syz147064 	} else {
8475895Syz147064 		return (dladm_errno2status(ENOENT));
8485895Syz147064 	}
8495895Syz147064 }
8505895Syz147064 
8515895Syz147064 static int
8525895Syz147064 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
8535895Syz147064 {
8545895Syz147064 	char	*cp, *tp;
8555895Syz147064 	int	len;
8565895Syz147064 
8575895Syz147064 	/*
8585895Syz147064 	 * device name length must not be 0, and it must end with digit.
8595895Syz147064 	 */
8605895Syz147064 	if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
8615895Syz147064 		return (EINVAL);
8625895Syz147064 
8635895Syz147064 	(void) strlcpy(driver, devname, maxlen);
8645895Syz147064 	cp = (char *)&driver[len - 1];
8655895Syz147064 
8665895Syz147064 	for (tp = cp; isdigit(*tp); tp--) {
8675895Syz147064 		if (tp <= driver)
8685895Syz147064 			return (EINVAL);
8695895Syz147064 	}
8705895Syz147064 
8715895Syz147064 	*ppa = atoi(tp + 1);
8725895Syz147064 	*(tp + 1) = '\0';
8735895Syz147064 	return (0);
8745895Syz147064 }
8755895Syz147064 
8765895Syz147064 dladm_status_t
8775895Syz147064 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len)
8783871Syz147064 {
8795895Syz147064 	char			devname[MAXLINKNAMELEN];
8805895Syz147064 	uint16_t		vid = VLAN_ID_NONE;
8815895Syz147064 	datalink_class_t	class;
8825895Syz147064 	dladm_status_t		status;
8835895Syz147064 
8845895Syz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0);
8855895Syz147064 	if (status != DLADM_STATUS_OK)
8865895Syz147064 		goto done;
8875895Syz147064 
8885895Syz147064 	/*
8895895Syz147064 	 * If this is a VLAN, we must first determine the class and linkid of
8905895Syz147064 	 * the link the VLAN has been created over.
8915895Syz147064 	 */
8925895Syz147064 	if (class == DATALINK_CLASS_VLAN) {
8935895Syz147064 		dladm_vlan_attr_t	dva;
8945895Syz147064 
8955895Syz147064 		status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE);
8965895Syz147064 		if (status != DLADM_STATUS_OK)
8975895Syz147064 			goto done;
8985895Syz147064 		linkid = dva.dv_linkid;
8995895Syz147064 		vid = dva.dv_vid;
9005895Syz147064 
9015895Syz147064 		if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL,
9025895Syz147064 		    NULL, 0)) != DLADM_STATUS_OK) {
9035895Syz147064 			goto done;
9045895Syz147064 		}
9055895Syz147064 	}
9065895Syz147064 
9075895Syz147064 	switch (class) {
9085895Syz147064 	case DATALINK_CLASS_AGGR: {
9095895Syz147064 		dladm_aggr_grp_attr_t	dga;
9105895Syz147064 
9115895Syz147064 		status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE);
9125895Syz147064 		if (status != DLADM_STATUS_OK)
9135895Syz147064 			goto done;
9145895Syz147064 
9155895Syz147064 		if (dga.lg_key == 0) {
9165895Syz147064 			/*
9175895Syz147064 			 * If the key was not specified when the aggregation
9185895Syz147064 			 * is created, we cannot guess its /dev node name.
9195895Syz147064 			 */
9205895Syz147064 			status = DLADM_STATUS_BADARG;
9215895Syz147064 			goto done;
9225895Syz147064 		}
9235895Syz147064 		(void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
9245895Syz147064 		break;
9255895Syz147064 	}
9265895Syz147064 	case DATALINK_CLASS_PHYS: {
9275895Syz147064 		dladm_phys_attr_t	dpa;
9285895Syz147064 
9295895Syz147064 		status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
9305895Syz147064 		if (status != DLADM_STATUS_OK)
9315895Syz147064 			goto done;
9325895Syz147064 
9335895Syz147064 		(void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
9345895Syz147064 		break;
9355895Syz147064 	}
9365895Syz147064 	default:
9375895Syz147064 		status = DLADM_STATUS_BADARG;
9385895Syz147064 		goto done;
9395895Syz147064 	}
9405895Syz147064 
9415895Syz147064 	if (vid != VLAN_ID_NONE) {
9425895Syz147064 		char		drv[MAXNAMELEN];
9435895Syz147064 		uint_t		ppa;
9445895Syz147064 
9455895Syz147064 		if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
9465895Syz147064 			status = DLADM_STATUS_BADARG;
9475895Syz147064 			goto done;
9485895Syz147064 		}
9495895Syz147064 		if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
9505895Syz147064 			status = DLADM_STATUS_TOOSMALL;
9515895Syz147064 	} else {
9525895Syz147064 		if (strlcpy(dev, devname, len) >= len)
9535895Syz147064 			status = DLADM_STATUS_TOOSMALL;
9545895Syz147064 	}
9555895Syz147064 
9565895Syz147064 done:
9575895Syz147064 	return (status);
9583871Syz147064 }
9595903Ssowmini 
9605903Ssowmini dladm_status_t
9615903Ssowmini dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type,
9625903Ssowmini     void *val)
9635903Ssowmini {
9645903Ssowmini 	char		module[DLPI_LINKNAME_MAX];
9655903Ssowmini 	uint_t		instance;
9665903Ssowmini 	char 		link[DLPI_LINKNAME_MAX];
9675903Ssowmini 	dladm_status_t	status;
9685903Ssowmini 	uint32_t	flags, media;
9695903Ssowmini 	kstat_ctl_t	*kcp;
9705903Ssowmini 	kstat_t		*ksp;
9715903Ssowmini 	dladm_phys_attr_t dpap;
9725903Ssowmini 
9735903Ssowmini 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media,
9745903Ssowmini 	    link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
9755903Ssowmini 		return (status);
9765903Ssowmini 
9775903Ssowmini 	if (media != DL_ETHER)
9785903Ssowmini 		return (DLADM_STATUS_LINKINVAL);
9795903Ssowmini 
9805903Ssowmini 	status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST);
9815903Ssowmini 
9825903Ssowmini 	if (status != DLADM_STATUS_OK)
9835903Ssowmini 		return (status);
9845903Ssowmini 
9855903Ssowmini 	status = dladm_parselink(dpap.dp_dev, module, &instance);
9865903Ssowmini 
9875903Ssowmini 	if (status != DLADM_STATUS_OK)
9885903Ssowmini 		return (status);
9895903Ssowmini 
9905903Ssowmini 	if ((kcp = kstat_open()) == NULL)
9915903Ssowmini 		return (dladm_errno2status(errno));
9925903Ssowmini 
9935903Ssowmini 	/*
9945903Ssowmini 	 * The kstat query could fail if the underlying MAC
9955903Ssowmini 	 * driver was already detached.
9965903Ssowmini 	 */
9975903Ssowmini 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
9985903Ssowmini 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
9995903Ssowmini 		goto bail;
10005903Ssowmini 
10015903Ssowmini 	if (kstat_read(kcp, ksp, NULL) == -1)
10025903Ssowmini 		goto bail;
10035903Ssowmini 
10045903Ssowmini 	if (dladm_kstat_value(ksp, name, type, val) < 0)
10055903Ssowmini 		goto bail;
10065903Ssowmini 
10075903Ssowmini 	(void) kstat_close(kcp);
10085903Ssowmini 	return (DLADM_STATUS_OK);
10095903Ssowmini bail:
10105903Ssowmini 	(void) kstat_close(kcp);
10115903Ssowmini 	return (dladm_errno2status(errno));
10125903Ssowmini 
10135903Ssowmini }
10145903Ssowmini 
10155903Ssowmini int
10165903Ssowmini dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
10175903Ssowmini {
10185903Ssowmini 	kstat_named_t	*knp;
10195903Ssowmini 
10205903Ssowmini 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
10215903Ssowmini 		return (-1);
10225903Ssowmini 
10235903Ssowmini 	if (knp->data_type != type)
10245903Ssowmini 		return (-1);
10255903Ssowmini 
10265903Ssowmini 	switch (type) {
10275903Ssowmini 	case KSTAT_DATA_UINT64:
10285903Ssowmini 		*(uint64_t *)buf = knp->value.ui64;
10295903Ssowmini 		break;
10305903Ssowmini 	case KSTAT_DATA_UINT32:
10315903Ssowmini 		*(uint32_t *)buf = knp->value.ui32;
10325903Ssowmini 		break;
10335903Ssowmini 	default:
10345903Ssowmini 		return (-1);
10355903Ssowmini 	}
10365903Ssowmini 
10375903Ssowmini 	return (0);
10385903Ssowmini }
10395903Ssowmini 
10405903Ssowmini dladm_status_t
10415903Ssowmini dladm_parselink(const char *dev, char *provider, uint_t *ppa)
10425903Ssowmini {
10435903Ssowmini 	ifspec_t	ifsp;
10445903Ssowmini 
10455903Ssowmini 	if (dev == NULL || !ifparse_ifspec(dev, &ifsp))
10465903Ssowmini 		return (DLADM_STATUS_LINKINVAL);
10475903Ssowmini 
10485903Ssowmini 	if (provider != NULL)
10495903Ssowmini 		(void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
10505903Ssowmini 
10515903Ssowmini 	if (ppa != NULL)
10525903Ssowmini 		*ppa = ifsp.ifsp_ppa;
10535903Ssowmini 
10545903Ssowmini 	return (DLADM_STATUS_OK);
10555903Ssowmini }
1056