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