xref: /titanic_50/usr/src/lib/libdladm/common/libdllink.c (revision d62bc4badc1c1f1549c961cfb8b420e650e1272b)
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 /*
22*d62bc4baSyz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23f595a68aSyz147064  * Use is subject to license terms.
24f595a68aSyz147064  */
25f595a68aSyz147064 
26f595a68aSyz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27f595a68aSyz147064 
28f595a68aSyz147064 #include <sys/types.h>
29f595a68aSyz147064 #include <unistd.h>
30f595a68aSyz147064 #include <errno.h>
31f595a68aSyz147064 #include <fcntl.h>
32*d62bc4baSyz147064 #include <assert.h>
33*d62bc4baSyz147064 #include <ctype.h>
34f595a68aSyz147064 #include <strings.h>
35f595a68aSyz147064 #include <sys/stat.h>
36f595a68aSyz147064 #include <sys/dld.h>
37*d62bc4baSyz147064 #include <sys/vlan.h>
38*d62bc4baSyz147064 #include <librcm.h>
39f595a68aSyz147064 #include <libdlpi.h>
40f595a68aSyz147064 #include <libdevinfo.h>
41*d62bc4baSyz147064 #include <libdlaggr.h>
42*d62bc4baSyz147064 #include <libdlvlan.h>
43f595a68aSyz147064 #include <libdllink.h>
44*d62bc4baSyz147064 #include <libdlmgmt.h>
45f595a68aSyz147064 #include <libdladm_impl.h>
46f595a68aSyz147064 
47f595a68aSyz147064 /*
48f595a68aSyz147064  * Return the attributes of the specified datalink from the DLD driver.
49f595a68aSyz147064  */
50*d62bc4baSyz147064 static dladm_status_t
51*d62bc4baSyz147064 i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap)
52f595a68aSyz147064 {
53f595a68aSyz147064 	dld_ioc_attr_t	dia;
54f595a68aSyz147064 
55*d62bc4baSyz147064 	dia.dia_linkid = linkid;
56f595a68aSyz147064 
57*d62bc4baSyz147064 	if (i_dladm_ioctl(fd, DLDIOC_ATTR, &dia, sizeof (dia)) < 0)
58*d62bc4baSyz147064 		return (dladm_errno2status(errno));
59f595a68aSyz147064 
60f595a68aSyz147064 	dap->da_max_sdu = dia.dia_max_sdu;
61f595a68aSyz147064 
62*d62bc4baSyz147064 	return (DLADM_STATUS_OK);
63f595a68aSyz147064 }
64f595a68aSyz147064 
65*d62bc4baSyz147064 struct i_dladm_walk_arg {
66*d62bc4baSyz147064 	dladm_walkcb_t *fn;
67*d62bc4baSyz147064 	void *arg;
68*d62bc4baSyz147064 };
69f595a68aSyz147064 
70f595a68aSyz147064 static int
71*d62bc4baSyz147064 i_dladm_walk(datalink_id_t linkid, void *arg)
72f595a68aSyz147064 {
73*d62bc4baSyz147064 	struct i_dladm_walk_arg *walk_arg = arg;
74*d62bc4baSyz147064 	char link[MAXLINKNAMELEN];
75f595a68aSyz147064 
76*d62bc4baSyz147064 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
77*d62bc4baSyz147064 	    sizeof (link)) == DLADM_STATUS_OK) {
78*d62bc4baSyz147064 		return (walk_arg->fn(link, walk_arg->arg));
79f595a68aSyz147064 	}
80*d62bc4baSyz147064 
81*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
82f595a68aSyz147064 }
83f595a68aSyz147064 
84f595a68aSyz147064 /*
85*d62bc4baSyz147064  * Walk all datalinks.
86f595a68aSyz147064  */
87*d62bc4baSyz147064 dladm_status_t
88*d62bc4baSyz147064 dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class,
89*d62bc4baSyz147064     datalink_media_t dmedia, uint32_t flags)
90f595a68aSyz147064 {
91*d62bc4baSyz147064 	struct i_dladm_walk_arg walk_arg;
92f595a68aSyz147064 
93*d62bc4baSyz147064 	walk_arg.fn = fn;
94*d62bc4baSyz147064 	walk_arg.arg = arg;
95*d62bc4baSyz147064 	return (dladm_walk_datalink_id(i_dladm_walk, &walk_arg,
96*d62bc4baSyz147064 	    class, dmedia, flags));
97f595a68aSyz147064 }
98f595a68aSyz147064 
99f595a68aSyz147064 /*
100*d62bc4baSyz147064  * 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 
134*d62bc4baSyz147064 	/*
135*d62bc4baSyz147064 	 * Skip softmacs.
136*d62bc4baSyz147064 	 */
137*d62bc4baSyz147064 	if (strcmp("softmac", di_driver_name(node)) == 0)
138*d62bc4baSyz147064 		return (DI_WALK_CONTINUE);
139*d62bc4baSyz147064 
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 /*
162*d62bc4baSyz147064  * Invoke the specified callback for each DDI_NT_NET node.
163f595a68aSyz147064  */
164*d62bc4baSyz147064 dladm_status_t
165*d62bc4baSyz147064 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;
170*d62bc4baSyz147064 	boolean_t		done = B_FALSE;
171f595a68aSyz147064 
172f595a68aSyz147064 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
173*d62bc4baSyz147064 		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;
185*d62bc4baSyz147064 		if (!done &&
186*d62bc4baSyz147064 		    ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) {
187*d62bc4baSyz147064 			done = B_TRUE;
188*d62bc4baSyz147064 		}
189f595a68aSyz147064 		free(dmdp);
190f595a68aSyz147064 	}
191f595a68aSyz147064 
192*d62bc4baSyz147064 	return (DLADM_STATUS_OK);
193f595a68aSyz147064 }
194f595a68aSyz147064 
195f595a68aSyz147064 /*
196*d62bc4baSyz147064  * Get the current attributes of the specified datalink.
197f595a68aSyz147064  */
198*d62bc4baSyz147064 dladm_status_t
199*d62bc4baSyz147064 dladm_info(datalink_id_t linkid, dladm_attr_t *dap)
200f595a68aSyz147064 {
201f595a68aSyz147064 	int		fd;
202*d62bc4baSyz147064 	dladm_status_t	status;
203f595a68aSyz147064 
204f595a68aSyz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
205*d62bc4baSyz147064 		return (dladm_errno2status(errno));
206f595a68aSyz147064 
207*d62bc4baSyz147064 	status = i_dladm_info(fd, linkid, dap);
208f595a68aSyz147064 
209f595a68aSyz147064 	(void) close(fd);
210*d62bc4baSyz147064 	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 /*
254*d62bc4baSyz147064  * Set zoneid of a given link
255f595a68aSyz147064  */
256*d62bc4baSyz147064 dladm_status_t
257*d62bc4baSyz147064 dladm_setzid(const char *link, zoneid_t zoneid)
258f595a68aSyz147064 {
259*d62bc4baSyz147064 	int			fd;
260*d62bc4baSyz147064 	dladm_status_t		status = DLADM_STATUS_OK;
261*d62bc4baSyz147064 	dld_ioc_setzid_t	dis;
262*d62bc4baSyz147064 
263*d62bc4baSyz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
264*d62bc4baSyz147064 		return (dladm_errno2status(errno));
265*d62bc4baSyz147064 
266*d62bc4baSyz147064 	bzero(&dis, sizeof (dld_ioc_setzid_t));
267*d62bc4baSyz147064 	(void) strlcpy(dis.dis_link, link, MAXLINKNAMELEN);
268*d62bc4baSyz147064 	dis.dis_zid = zoneid;
269*d62bc4baSyz147064 
270*d62bc4baSyz147064 	if (i_dladm_ioctl(fd, DLDIOC_SETZID, &dis, sizeof (dis)) < 0)
271*d62bc4baSyz147064 		status = dladm_errno2status(errno);
272*d62bc4baSyz147064 
273*d62bc4baSyz147064 	(void) close(fd);
274*d62bc4baSyz147064 	return (status);
275f595a68aSyz147064 }
276f595a68aSyz147064 
277f595a68aSyz147064 /*
278*d62bc4baSyz147064  * Get zoneid of a given link
279f595a68aSyz147064  */
280*d62bc4baSyz147064 dladm_status_t
281*d62bc4baSyz147064 dladm_getzid(datalink_id_t linkid, zoneid_t *zoneidp)
282f595a68aSyz147064 {
283*d62bc4baSyz147064 	int			fd;
284*d62bc4baSyz147064 	dladm_status_t		status = DLADM_STATUS_OK;
285*d62bc4baSyz147064 	dld_ioc_getzid_t	dig;
286*d62bc4baSyz147064 
287*d62bc4baSyz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
288*d62bc4baSyz147064 		return (dladm_errno2status(errno));
289*d62bc4baSyz147064 
290*d62bc4baSyz147064 	bzero(&dig, sizeof (dld_ioc_getzid_t));
291*d62bc4baSyz147064 	dig.dig_linkid = linkid;
292*d62bc4baSyz147064 	dig.dig_zid = -1;
293*d62bc4baSyz147064 
294*d62bc4baSyz147064 	if (i_dladm_ioctl(fd, DLDIOC_GETZID, &dig, sizeof (dig)) < 0)
295*d62bc4baSyz147064 		status = dladm_errno2status(errno);
296*d62bc4baSyz147064 
297*d62bc4baSyz147064 	(void) close(fd);
298*d62bc4baSyz147064 
299*d62bc4baSyz147064 	if (status == DLADM_STATUS_OK)
300*d62bc4baSyz147064 		*zoneidp = dig.dig_zid;
301*d62bc4baSyz147064 
302*d62bc4baSyz147064 	return (status);
303*d62bc4baSyz147064 }
304*d62bc4baSyz147064 
305*d62bc4baSyz147064 /*
306*d62bc4baSyz147064  * Case 1: rename an existing link1 to a link2 that does not exist.
307*d62bc4baSyz147064  * Result: <linkid1, link2>
308*d62bc4baSyz147064  */
309*d62bc4baSyz147064 static dladm_status_t
310*d62bc4baSyz147064 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1,
311*d62bc4baSyz147064     const char *link2, uint32_t flags)
312*d62bc4baSyz147064 {
313*d62bc4baSyz147064 	dld_ioc_rename_t	dir;
314*d62bc4baSyz147064 	dladm_conf_t		conf;
315*d62bc4baSyz147064 	dladm_status_t		status = DLADM_STATUS_OK;
316*d62bc4baSyz147064 	int			fd;
317*d62bc4baSyz147064 
318*d62bc4baSyz147064 	/*
319*d62bc4baSyz147064 	 * Link is currently available. Check to see whether anything is
320*d62bc4baSyz147064 	 * holding this link to prevent a rename operation.
321*d62bc4baSyz147064 	 */
322*d62bc4baSyz147064 	if (flags & DLADM_OPT_ACTIVE) {
323*d62bc4baSyz147064 		dir.dir_linkid1 = linkid1;
324*d62bc4baSyz147064 		dir.dir_linkid2 = DATALINK_INVALID_LINKID;
325*d62bc4baSyz147064 		(void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
326*d62bc4baSyz147064 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
327*d62bc4baSyz147064 			return (dladm_errno2status(errno));
328*d62bc4baSyz147064 
329*d62bc4baSyz147064 		if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) {
330*d62bc4baSyz147064 			status = dladm_errno2status(errno);
331*d62bc4baSyz147064 			(void) close(fd);
332*d62bc4baSyz147064 			return (status);
333*d62bc4baSyz147064 		}
334*d62bc4baSyz147064 	}
335*d62bc4baSyz147064 
336*d62bc4baSyz147064 	status = dladm_remap_datalink_id(linkid1, link2);
337*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
338*d62bc4baSyz147064 		goto done;
339*d62bc4baSyz147064 
340*d62bc4baSyz147064 	/*
341*d62bc4baSyz147064 	 * Flush the current mapping to persistent configuration.
342*d62bc4baSyz147064 	 */
343*d62bc4baSyz147064 	if ((flags & DLADM_OPT_PERSIST) &&
344*d62bc4baSyz147064 	    (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) ||
345*d62bc4baSyz147064 	    ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) {
346*d62bc4baSyz147064 		(void) dladm_remap_datalink_id(linkid1, link1);
347*d62bc4baSyz147064 	}
348*d62bc4baSyz147064 done:
349*d62bc4baSyz147064 	if (flags & DLADM_OPT_ACTIVE) {
350*d62bc4baSyz147064 		if (status != DLADM_STATUS_OK) {
351*d62bc4baSyz147064 			(void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
352*d62bc4baSyz147064 			(void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir,
353*d62bc4baSyz147064 			    sizeof (dir));
354*d62bc4baSyz147064 		}
355*d62bc4baSyz147064 		(void) close(fd);
356*d62bc4baSyz147064 	}
357*d62bc4baSyz147064 	return (status);
358*d62bc4baSyz147064 }
359*d62bc4baSyz147064 
360*d62bc4baSyz147064 typedef struct link_hold_arg_s {
361*d62bc4baSyz147064 	datalink_id_t	linkid;
362*d62bc4baSyz147064 	datalink_id_t	holder;
363*d62bc4baSyz147064 	uint32_t	flags;
364*d62bc4baSyz147064 } link_hold_arg_t;
365*d62bc4baSyz147064 
366*d62bc4baSyz147064 static int
367*d62bc4baSyz147064 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg)
368*d62bc4baSyz147064 {
369*d62bc4baSyz147064 	link_hold_arg_t		*hold_arg = arg;
370*d62bc4baSyz147064 	dladm_aggr_grp_attr_t	ginfo;
371*d62bc4baSyz147064 	dladm_status_t		status;
372*d62bc4baSyz147064 	int			i;
373*d62bc4baSyz147064 
374*d62bc4baSyz147064 	status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags);
375*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
376*d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
377*d62bc4baSyz147064 
378*d62bc4baSyz147064 	for (i = 0; i < ginfo.lg_nports; i++) {
379*d62bc4baSyz147064 		if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
380*d62bc4baSyz147064 			hold_arg->holder = aggrid;
381*d62bc4baSyz147064 			return (DLADM_WALK_TERMINATE);
382*d62bc4baSyz147064 		}
383*d62bc4baSyz147064 	}
384*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
385*d62bc4baSyz147064 }
386*d62bc4baSyz147064 
387*d62bc4baSyz147064 static int
388*d62bc4baSyz147064 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg)
389*d62bc4baSyz147064 {
390*d62bc4baSyz147064 	link_hold_arg_t		*hold_arg = arg;
391*d62bc4baSyz147064 	dladm_vlan_attr_t	vinfo;
392*d62bc4baSyz147064 	dladm_status_t		status;
393*d62bc4baSyz147064 
394*d62bc4baSyz147064 	status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags);
395*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
396*d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
397*d62bc4baSyz147064 
398*d62bc4baSyz147064 	if (vinfo.dv_linkid == hold_arg->linkid) {
399*d62bc4baSyz147064 		hold_arg->holder = vlanid;
400*d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
401*d62bc4baSyz147064 	}
402*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
403*d62bc4baSyz147064 }
404*d62bc4baSyz147064 
405*d62bc4baSyz147064 /*
406*d62bc4baSyz147064  * Case 2: rename an available physical link link1 to a REMOVED physical link
407*d62bc4baSyz147064  *     link2.  As a result, link1 directly inherits all datalinks configured
408*d62bc4baSyz147064  *     over link2 (linkid2).
409*d62bc4baSyz147064  * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
410*d62bc4baSyz147064  *     link2_other_attr>
411*d62bc4baSyz147064  */
412*d62bc4baSyz147064 static dladm_status_t
413*d62bc4baSyz147064 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2)
414*d62bc4baSyz147064 {
415*d62bc4baSyz147064 	rcm_handle_t		*rcm_hdl = NULL;
416*d62bc4baSyz147064 	nvlist_t		*nvl = NULL;
417*d62bc4baSyz147064 	link_hold_arg_t		arg;
418*d62bc4baSyz147064 	dld_ioc_rename_t	dir;
419*d62bc4baSyz147064 	int			fd;
420*d62bc4baSyz147064 	dladm_conf_t		conf1, conf2;
421*d62bc4baSyz147064 	char			devname[MAXLINKNAMELEN];
422*d62bc4baSyz147064 	uint64_t		phymaj, phyinst;
423*d62bc4baSyz147064 	dladm_status_t		status = DLADM_STATUS_OK;
424*d62bc4baSyz147064 
425*d62bc4baSyz147064 	/*
426*d62bc4baSyz147064 	 * First check if linkid1 is associated with any persistent
427*d62bc4baSyz147064 	 * aggregations or VLANs. If yes, return BUSY.
428*d62bc4baSyz147064 	 */
429*d62bc4baSyz147064 	arg.linkid = linkid1;
430*d62bc4baSyz147064 	arg.holder = DATALINK_INVALID_LINKID;
431*d62bc4baSyz147064 	arg.flags = DLADM_OPT_PERSIST;
432*d62bc4baSyz147064 	(void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg,
433*d62bc4baSyz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
434*d62bc4baSyz147064 	if (arg.holder != DATALINK_INVALID_LINKID)
435*d62bc4baSyz147064 		return (DLADM_STATUS_LINKBUSY);
436*d62bc4baSyz147064 
437*d62bc4baSyz147064 	arg.flags = DLADM_OPT_PERSIST;
438*d62bc4baSyz147064 	(void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg,
439*d62bc4baSyz147064 	    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
440*d62bc4baSyz147064 	if (arg.holder != DATALINK_INVALID_LINKID)
441*d62bc4baSyz147064 		return (DLADM_STATUS_LINKBUSY);
442*d62bc4baSyz147064 
443*d62bc4baSyz147064 	/*
444*d62bc4baSyz147064 	 * Send DLDIOC_RENAME to request to rename link1's linkid to
445*d62bc4baSyz147064 	 * be linkid2. This will check whether link1 is used by any
446*d62bc4baSyz147064 	 * aggregations or VLANs, or is held by any application. If yes,
447*d62bc4baSyz147064 	 * return failure.
448*d62bc4baSyz147064 	 */
449*d62bc4baSyz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
450*d62bc4baSyz147064 		return (dladm_errno2status(errno));
451*d62bc4baSyz147064 
452*d62bc4baSyz147064 	dir.dir_linkid1 = linkid1;
453*d62bc4baSyz147064 	dir.dir_linkid2 = linkid2;
454*d62bc4baSyz147064 	if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0)
455*d62bc4baSyz147064 		status = dladm_errno2status(errno);
456*d62bc4baSyz147064 
457*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
458*d62bc4baSyz147064 		(void) close(fd);
459*d62bc4baSyz147064 		return (status);
460*d62bc4baSyz147064 	}
461*d62bc4baSyz147064 
462*d62bc4baSyz147064 	/*
463*d62bc4baSyz147064 	 * Now change the phymaj, phyinst and devname associated with linkid1
464*d62bc4baSyz147064 	 * to be associated with linkid2. Before doing that, the old active
465*d62bc4baSyz147064 	 * linkprop of linkid1 should be deleted.
466*d62bc4baSyz147064 	 */
467*d62bc4baSyz147064 	(void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE);
468*d62bc4baSyz147064 
469*d62bc4baSyz147064 	if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) ||
470*d62bc4baSyz147064 	    ((status = dladm_get_conf_field(conf1, FDEVNAME, devname,
471*d62bc4baSyz147064 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
472*d62bc4baSyz147064 	    ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj,
473*d62bc4baSyz147064 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
474*d62bc4baSyz147064 	    ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst,
475*d62bc4baSyz147064 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
476*d62bc4baSyz147064 	    ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) {
477*d62bc4baSyz147064 		dir.dir_linkid1 = linkid2;
478*d62bc4baSyz147064 		dir.dir_linkid2 = linkid1;
479*d62bc4baSyz147064 		(void) dladm_init_linkprop(linkid1);
480*d62bc4baSyz147064 		(void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir));
481*d62bc4baSyz147064 		(void) close(fd);
482*d62bc4baSyz147064 		return (status);
483*d62bc4baSyz147064 	}
484*d62bc4baSyz147064 	(void) close(fd);
485*d62bc4baSyz147064 
486*d62bc4baSyz147064 	dladm_destroy_conf(conf1);
487*d62bc4baSyz147064 	(void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname);
488*d62bc4baSyz147064 	(void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj);
489*d62bc4baSyz147064 	(void) dladm_set_conf_field(conf2, FPHYINST,
490*d62bc4baSyz147064 	    DLADM_TYPE_UINT64, &phyinst);
491*d62bc4baSyz147064 	(void) dladm_write_conf(conf2);
492*d62bc4baSyz147064 	dladm_destroy_conf(conf2);
493*d62bc4baSyz147064 
494*d62bc4baSyz147064 	/*
495*d62bc4baSyz147064 	 * Delete link1 and mark link2 up.
496*d62bc4baSyz147064 	 */
497*d62bc4baSyz147064 	(void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE |
498*d62bc4baSyz147064 	    DLADM_OPT_PERSIST);
499*d62bc4baSyz147064 	(void) dladm_remove_conf(linkid1);
500*d62bc4baSyz147064 	(void) dladm_up_datalink_id(linkid2);
501*d62bc4baSyz147064 
502*d62bc4baSyz147064 	/*
503*d62bc4baSyz147064 	 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
504*d62bc4baSyz147064 	 * consumed by the RCM framework to restore all the datalink and
505*d62bc4baSyz147064 	 * IP configuration.
506*d62bc4baSyz147064 	 */
507*d62bc4baSyz147064 	status = DLADM_STATUS_FAILED;
508*d62bc4baSyz147064 	if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
509*d62bc4baSyz147064 	    (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
510*d62bc4baSyz147064 		goto done;
511*d62bc4baSyz147064 	}
512*d62bc4baSyz147064 
513*d62bc4baSyz147064 	if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
514*d62bc4baSyz147064 		goto done;
515*d62bc4baSyz147064 
516*d62bc4baSyz147064 	if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
517*d62bc4baSyz147064 	    RCM_SUCCESS) {
518*d62bc4baSyz147064 		status = DLADM_STATUS_OK;
519*d62bc4baSyz147064 	}
520*d62bc4baSyz147064 
521*d62bc4baSyz147064 done:
522*d62bc4baSyz147064 	if (rcm_hdl != NULL)
523*d62bc4baSyz147064 		(void) rcm_free_handle(rcm_hdl);
524*d62bc4baSyz147064 	if (nvl != NULL)
525*d62bc4baSyz147064 		nvlist_free(nvl);
526*d62bc4baSyz147064 	return (status);
527*d62bc4baSyz147064 }
528*d62bc4baSyz147064 
529*d62bc4baSyz147064 /*
530*d62bc4baSyz147064  * case 3: rename a non-existent link to a REMOVED physical link.
531*d62bc4baSyz147064  * Set the removed physical link's device name to link1, so that
532*d62bc4baSyz147064  * when link1 attaches, it inherits all the link configuration of
533*d62bc4baSyz147064  * the removed physical link.
534*d62bc4baSyz147064  */
535*d62bc4baSyz147064 static dladm_status_t
536*d62bc4baSyz147064 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2)
537*d62bc4baSyz147064 {
538*d62bc4baSyz147064 	dladm_conf_t	conf;
539*d62bc4baSyz147064 	dladm_status_t	status;
540*d62bc4baSyz147064 
541*d62bc4baSyz147064 	if (!dladm_valid_linkname(link1))
542*d62bc4baSyz147064 		return (DLADM_STATUS_LINKINVAL);
543*d62bc4baSyz147064 
544*d62bc4baSyz147064 	status = dladm_read_conf(linkid2, &conf);
545*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
546*d62bc4baSyz147064 		goto done;
547*d62bc4baSyz147064 
548*d62bc4baSyz147064 	if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR,
549*d62bc4baSyz147064 	    link1)) == DLADM_STATUS_OK) {
550*d62bc4baSyz147064 		status = dladm_write_conf(conf);
551*d62bc4baSyz147064 	}
552*d62bc4baSyz147064 
553*d62bc4baSyz147064 	dladm_destroy_conf(conf);
554*d62bc4baSyz147064 
555*d62bc4baSyz147064 done:
556*d62bc4baSyz147064 	return (status);
557*d62bc4baSyz147064 }
558*d62bc4baSyz147064 
559*d62bc4baSyz147064 dladm_status_t
560*d62bc4baSyz147064 dladm_rename_link(const char *link1, const char *link2)
561*d62bc4baSyz147064 {
562*d62bc4baSyz147064 	datalink_id_t		linkid1 = DATALINK_INVALID_LINKID;
563*d62bc4baSyz147064 	datalink_id_t		linkid2 = DATALINK_INVALID_LINKID;
564*d62bc4baSyz147064 	uint32_t		flags1, flags2;
565*d62bc4baSyz147064 	datalink_class_t	class1, class2;
566*d62bc4baSyz147064 	uint32_t		media1, media2;
567*d62bc4baSyz147064 	boolean_t		remphy2 = B_FALSE;
568*d62bc4baSyz147064 	dladm_status_t  	status;
569*d62bc4baSyz147064 
570*d62bc4baSyz147064 	(void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1);
571*d62bc4baSyz147064 	if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) ==
572*d62bc4baSyz147064 	    DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
573*d62bc4baSyz147064 	    (flags2 == DLADM_OPT_PERSIST)) {
574*d62bc4baSyz147064 		/*
575*d62bc4baSyz147064 		 * see whether link2 is a removed physical link.
576*d62bc4baSyz147064 		 */
577*d62bc4baSyz147064 		remphy2 = B_TRUE;
578*d62bc4baSyz147064 	}
579*d62bc4baSyz147064 
580*d62bc4baSyz147064 	if (linkid1 != DATALINK_INVALID_LINKID) {
581*d62bc4baSyz147064 		if (linkid2 == DATALINK_INVALID_LINKID) {
582*d62bc4baSyz147064 			/*
583*d62bc4baSyz147064 			 * case 1: rename an existing link to a link that
584*d62bc4baSyz147064 			 * does not exist.
585*d62bc4baSyz147064 			 */
586*d62bc4baSyz147064 			status = i_dladm_rename_link_c1(linkid1, link1, link2,
587*d62bc4baSyz147064 			    flags1);
588*d62bc4baSyz147064 		} else if (remphy2) {
589*d62bc4baSyz147064 			/*
590*d62bc4baSyz147064 			 * case 2: rename an available link to a REMOVED
591*d62bc4baSyz147064 			 * physical link. Return failure if link1 is not
592*d62bc4baSyz147064 			 * an active physical link.
593*d62bc4baSyz147064 			 */
594*d62bc4baSyz147064 			if ((class1 != class2) || (media1 != media2) ||
595*d62bc4baSyz147064 			    !(flags1 & DLADM_OPT_ACTIVE)) {
596*d62bc4baSyz147064 				status = DLADM_STATUS_BADARG;
597*d62bc4baSyz147064 			} else {
598*d62bc4baSyz147064 				status = i_dladm_rename_link_c2(linkid1,
599*d62bc4baSyz147064 				    linkid2);
600*d62bc4baSyz147064 			}
601*d62bc4baSyz147064 		} else {
602*d62bc4baSyz147064 			status = DLADM_STATUS_EXIST;
603*d62bc4baSyz147064 		}
604*d62bc4baSyz147064 	} else if (remphy2) {
605*d62bc4baSyz147064 		status = i_dladm_rename_link_c3(link1, linkid2);
606*d62bc4baSyz147064 	} else {
607*d62bc4baSyz147064 		status = DLADM_STATUS_NOTFOUND;
608*d62bc4baSyz147064 	}
609*d62bc4baSyz147064 	return (status);
610*d62bc4baSyz147064 }
611*d62bc4baSyz147064 
612*d62bc4baSyz147064 typedef struct consumer_del_phys_arg_s {
613*d62bc4baSyz147064 	datalink_id_t	linkid;
614*d62bc4baSyz147064 } consumer_del_phys_arg_t;
615*d62bc4baSyz147064 
616*d62bc4baSyz147064 static int
617*d62bc4baSyz147064 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg)
618*d62bc4baSyz147064 {
619*d62bc4baSyz147064 	consumer_del_phys_arg_t	*del_arg = arg;
620*d62bc4baSyz147064 	dladm_vlan_attr_t	vinfo;
621*d62bc4baSyz147064 	dladm_status_t		status;
622*d62bc4baSyz147064 
623*d62bc4baSyz147064 	status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST);
624*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
625*d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
626*d62bc4baSyz147064 
627*d62bc4baSyz147064 	if (vinfo.dv_linkid == del_arg->linkid)
628*d62bc4baSyz147064 		(void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST);
629*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
630*d62bc4baSyz147064 }
631*d62bc4baSyz147064 
632*d62bc4baSyz147064 static int
633*d62bc4baSyz147064 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg)
634*d62bc4baSyz147064 {
635*d62bc4baSyz147064 	consumer_del_phys_arg_t		*del_arg = arg;
636*d62bc4baSyz147064 	dladm_aggr_grp_attr_t		ginfo;
637*d62bc4baSyz147064 	dladm_status_t			status;
638*d62bc4baSyz147064 	dladm_aggr_port_attr_db_t	port[1];
639*d62bc4baSyz147064 	int				i;
640*d62bc4baSyz147064 
641*d62bc4baSyz147064 	status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST);
642*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
643*d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
644*d62bc4baSyz147064 
645*d62bc4baSyz147064 	for (i = 0; i < ginfo.lg_nports; i++)
646*d62bc4baSyz147064 		if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
647*d62bc4baSyz147064 			break;
648*d62bc4baSyz147064 
649*d62bc4baSyz147064 	if (i != ginfo.lg_nports) {
650*d62bc4baSyz147064 		if (ginfo.lg_nports == 1 && i == 0) {
651*d62bc4baSyz147064 			consumer_del_phys_arg_t	aggr_del_arg;
652*d62bc4baSyz147064 
653*d62bc4baSyz147064 			/*
654*d62bc4baSyz147064 			 * First delete all the VLANs on this aggregation, then
655*d62bc4baSyz147064 			 * delete the aggregation itself.
656*d62bc4baSyz147064 			 */
657*d62bc4baSyz147064 			aggr_del_arg.linkid = aggrid;
658*d62bc4baSyz147064 			(void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
659*d62bc4baSyz147064 			    &aggr_del_arg, DATALINK_CLASS_VLAN,
660*d62bc4baSyz147064 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
661*d62bc4baSyz147064 			(void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST);
662*d62bc4baSyz147064 		} else {
663*d62bc4baSyz147064 			port[0].lp_linkid = del_arg->linkid;
664*d62bc4baSyz147064 			(void) dladm_aggr_remove(aggrid, 1, port,
665*d62bc4baSyz147064 			    DLADM_OPT_PERSIST);
666*d62bc4baSyz147064 		}
667*d62bc4baSyz147064 	}
668*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
669*d62bc4baSyz147064 }
670*d62bc4baSyz147064 
671*d62bc4baSyz147064 typedef struct del_phys_arg_s {
672*d62bc4baSyz147064 	dladm_status_t	rval;
673*d62bc4baSyz147064 } del_phys_arg_t;
674*d62bc4baSyz147064 
675*d62bc4baSyz147064 static int
676*d62bc4baSyz147064 i_dladm_phys_delete(datalink_id_t linkid, void *arg)
677*d62bc4baSyz147064 {
678*d62bc4baSyz147064 	uint32_t		flags;
679*d62bc4baSyz147064 	datalink_class_t	class;
680*d62bc4baSyz147064 	uint32_t		media;
681*d62bc4baSyz147064 	dladm_status_t		status = DLADM_STATUS_OK;
682*d62bc4baSyz147064 	del_phys_arg_t		*del_phys_arg = arg;
683*d62bc4baSyz147064 	consumer_del_phys_arg_t	del_arg;
684*d62bc4baSyz147064 
685*d62bc4baSyz147064 	if ((status = dladm_datalink_id2info(linkid, &flags, &class,
686*d62bc4baSyz147064 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
687*d62bc4baSyz147064 		goto done;
688*d62bc4baSyz147064 	}
689*d62bc4baSyz147064 
690*d62bc4baSyz147064 	/*
691*d62bc4baSyz147064 	 * see whether this link is a removed physical link.
692*d62bc4baSyz147064 	 */
693*d62bc4baSyz147064 	if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
694*d62bc4baSyz147064 	    (flags & DLADM_OPT_ACTIVE)) {
695*d62bc4baSyz147064 		status = DLADM_STATUS_BADARG;
696*d62bc4baSyz147064 		goto done;
697*d62bc4baSyz147064 	}
698*d62bc4baSyz147064 
699*d62bc4baSyz147064 	if (media == DL_ETHER) {
700*d62bc4baSyz147064 		del_arg.linkid = linkid;
701*d62bc4baSyz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg,
702*d62bc4baSyz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
703*d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
704*d62bc4baSyz147064 		(void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg,
705*d62bc4baSyz147064 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
706*d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
707*d62bc4baSyz147064 	}
708*d62bc4baSyz147064 
709*d62bc4baSyz147064 	(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
710*d62bc4baSyz147064 	(void) dladm_remove_conf(linkid);
711*d62bc4baSyz147064 
712*d62bc4baSyz147064 done:
713*d62bc4baSyz147064 	del_phys_arg->rval = status;
714*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
715*d62bc4baSyz147064 }
716*d62bc4baSyz147064 
717*d62bc4baSyz147064 dladm_status_t
718*d62bc4baSyz147064 dladm_phys_delete(datalink_id_t linkid)
719*d62bc4baSyz147064 {
720*d62bc4baSyz147064 	del_phys_arg_t	arg = {DLADM_STATUS_OK};
721*d62bc4baSyz147064 
722*d62bc4baSyz147064 	if (linkid == DATALINK_ALL_LINKID) {
723*d62bc4baSyz147064 		(void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg,
724*d62bc4baSyz147064 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
725*d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
726*d62bc4baSyz147064 		return (DLADM_STATUS_OK);
727*d62bc4baSyz147064 	} else {
728*d62bc4baSyz147064 		(void) i_dladm_phys_delete(linkid, &arg);
729*d62bc4baSyz147064 		return (arg.rval);
730*d62bc4baSyz147064 	}
731*d62bc4baSyz147064 }
732*d62bc4baSyz147064 
733*d62bc4baSyz147064 dladm_status_t
734*d62bc4baSyz147064 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags)
735*d62bc4baSyz147064 {
736*d62bc4baSyz147064 	dladm_status_t	status;
737*d62bc4baSyz147064 
738*d62bc4baSyz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
739*d62bc4baSyz147064 
740*d62bc4baSyz147064 	switch (flags) {
741*d62bc4baSyz147064 	case DLADM_OPT_PERSIST: {
742*d62bc4baSyz147064 		dladm_conf_t	conf;
743*d62bc4baSyz147064 
744*d62bc4baSyz147064 		status = dladm_read_conf(linkid, &conf);
745*d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
746*d62bc4baSyz147064 			return (status);
747*d62bc4baSyz147064 
748*d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev,
749*d62bc4baSyz147064 		    MAXLINKNAMELEN);
750*d62bc4baSyz147064 		dladm_destroy_conf(conf);
751*d62bc4baSyz147064 		return (status);
752*d62bc4baSyz147064 	}
753*d62bc4baSyz147064 	case DLADM_OPT_ACTIVE: {
754*d62bc4baSyz147064 		dld_ioc_phys_attr_t	dip;
755*d62bc4baSyz147064 		int			fd;
756*d62bc4baSyz147064 
757*d62bc4baSyz147064 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
758*d62bc4baSyz147064 			return (dladm_errno2status(errno));
759*d62bc4baSyz147064 
760*d62bc4baSyz147064 		dip.dip_linkid = linkid;
761*d62bc4baSyz147064 		if (i_dladm_ioctl(fd, DLDIOC_PHYS_ATTR, &dip, sizeof (dip))
762*d62bc4baSyz147064 		    < 0) {
763*d62bc4baSyz147064 			status = dladm_errno2status(errno);
764*d62bc4baSyz147064 			(void) close(fd);
765*d62bc4baSyz147064 			return (status);
766*d62bc4baSyz147064 		}
767*d62bc4baSyz147064 		(void) close(fd);
768*d62bc4baSyz147064 		dpap->dp_novanity = dip.dip_novanity;
769*d62bc4baSyz147064 		(void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
770*d62bc4baSyz147064 		return (DLADM_STATUS_OK);
771*d62bc4baSyz147064 	}
772*d62bc4baSyz147064 	default:
773*d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
774*d62bc4baSyz147064 	}
775*d62bc4baSyz147064 }
776*d62bc4baSyz147064 
777*d62bc4baSyz147064 typedef struct i_walk_dev_state_s {
778*d62bc4baSyz147064 	const char *devname;
779*d62bc4baSyz147064 	datalink_id_t linkid;
780*d62bc4baSyz147064 	boolean_t found;
781*d62bc4baSyz147064 } i_walk_dev_state_t;
782*d62bc4baSyz147064 
783*d62bc4baSyz147064 int
784*d62bc4baSyz147064 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg)
785*d62bc4baSyz147064 {
786*d62bc4baSyz147064 	dladm_phys_attr_t dpa;
787*d62bc4baSyz147064 	dladm_status_t status;
788*d62bc4baSyz147064 	i_walk_dev_state_t *statep = arg;
789*d62bc4baSyz147064 
790*d62bc4baSyz147064 	status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
791*d62bc4baSyz147064 	if ((status == DLADM_STATUS_OK) &&
792*d62bc4baSyz147064 	    (strcmp(statep->devname, dpa.dp_dev) == 0)) {
793*d62bc4baSyz147064 		statep->found = B_TRUE;
794*d62bc4baSyz147064 		statep->linkid = linkid;
795*d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
796*d62bc4baSyz147064 	}
797*d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
798*d62bc4baSyz147064 }
799*d62bc4baSyz147064 
800*d62bc4baSyz147064 /*
801*d62bc4baSyz147064  * Get the linkid from the physical device name.
802*d62bc4baSyz147064  */
803*d62bc4baSyz147064 dladm_status_t
804*d62bc4baSyz147064 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp)
805*d62bc4baSyz147064 {
806*d62bc4baSyz147064 	i_walk_dev_state_t state;
807*d62bc4baSyz147064 
808*d62bc4baSyz147064 	state.found = B_FALSE;
809*d62bc4baSyz147064 	state.devname = devname;
810*d62bc4baSyz147064 
811*d62bc4baSyz147064 	(void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state,
812*d62bc4baSyz147064 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
813*d62bc4baSyz147064 	if (state.found == B_TRUE) {
814*d62bc4baSyz147064 		*linkidp = state.linkid;
815*d62bc4baSyz147064 		return (DLADM_STATUS_OK);
816*d62bc4baSyz147064 	} else {
817*d62bc4baSyz147064 		return (dladm_errno2status(ENOENT));
818*d62bc4baSyz147064 	}
819*d62bc4baSyz147064 }
820*d62bc4baSyz147064 
821*d62bc4baSyz147064 static int
822*d62bc4baSyz147064 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
823*d62bc4baSyz147064 {
824*d62bc4baSyz147064 	char	*cp, *tp;
825*d62bc4baSyz147064 	int	len;
826*d62bc4baSyz147064 
827*d62bc4baSyz147064 	/*
828*d62bc4baSyz147064 	 * device name length must not be 0, and it must end with digit.
829*d62bc4baSyz147064 	 */
830*d62bc4baSyz147064 	if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
831*d62bc4baSyz147064 		return (EINVAL);
832*d62bc4baSyz147064 
833*d62bc4baSyz147064 	(void) strlcpy(driver, devname, maxlen);
834*d62bc4baSyz147064 	cp = (char *)&driver[len - 1];
835*d62bc4baSyz147064 
836*d62bc4baSyz147064 	for (tp = cp; isdigit(*tp); tp--) {
837*d62bc4baSyz147064 		if (tp <= driver)
838*d62bc4baSyz147064 			return (EINVAL);
839*d62bc4baSyz147064 	}
840*d62bc4baSyz147064 
841*d62bc4baSyz147064 	*ppa = atoi(tp + 1);
842*d62bc4baSyz147064 	*(tp + 1) = '\0';
843*d62bc4baSyz147064 	return (0);
844*d62bc4baSyz147064 }
845*d62bc4baSyz147064 
846*d62bc4baSyz147064 dladm_status_t
847*d62bc4baSyz147064 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len)
848*d62bc4baSyz147064 {
849*d62bc4baSyz147064 	char			devname[MAXLINKNAMELEN];
850*d62bc4baSyz147064 	uint16_t		vid = VLAN_ID_NONE;
851*d62bc4baSyz147064 	datalink_class_t	class;
852*d62bc4baSyz147064 	dladm_status_t		status;
853*d62bc4baSyz147064 
854*d62bc4baSyz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0);
855*d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
856*d62bc4baSyz147064 		goto done;
857*d62bc4baSyz147064 
858*d62bc4baSyz147064 	/*
859*d62bc4baSyz147064 	 * If this is a VLAN, we must first determine the class and linkid of
860*d62bc4baSyz147064 	 * the link the VLAN has been created over.
861*d62bc4baSyz147064 	 */
862*d62bc4baSyz147064 	if (class == DATALINK_CLASS_VLAN) {
863*d62bc4baSyz147064 		dladm_vlan_attr_t	dva;
864*d62bc4baSyz147064 
865*d62bc4baSyz147064 		status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE);
866*d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
867*d62bc4baSyz147064 			goto done;
868*d62bc4baSyz147064 		linkid = dva.dv_linkid;
869*d62bc4baSyz147064 		vid = dva.dv_vid;
870*d62bc4baSyz147064 
871*d62bc4baSyz147064 		if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL,
872*d62bc4baSyz147064 		    NULL, 0)) != DLADM_STATUS_OK) {
873*d62bc4baSyz147064 			goto done;
874*d62bc4baSyz147064 		}
875*d62bc4baSyz147064 	}
876*d62bc4baSyz147064 
877*d62bc4baSyz147064 	switch (class) {
878*d62bc4baSyz147064 	case DATALINK_CLASS_AGGR: {
879*d62bc4baSyz147064 		dladm_aggr_grp_attr_t	dga;
880*d62bc4baSyz147064 
881*d62bc4baSyz147064 		status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE);
882*d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
883*d62bc4baSyz147064 			goto done;
884*d62bc4baSyz147064 
885*d62bc4baSyz147064 		if (dga.lg_key == 0) {
886*d62bc4baSyz147064 			/*
887*d62bc4baSyz147064 			 * If the key was not specified when the aggregation
888*d62bc4baSyz147064 			 * is created, we cannot guess its /dev node name.
889*d62bc4baSyz147064 			 */
890*d62bc4baSyz147064 			status = DLADM_STATUS_BADARG;
891*d62bc4baSyz147064 			goto done;
892*d62bc4baSyz147064 		}
893*d62bc4baSyz147064 		(void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
894*d62bc4baSyz147064 		break;
895*d62bc4baSyz147064 	}
896*d62bc4baSyz147064 	case DATALINK_CLASS_PHYS: {
897*d62bc4baSyz147064 		dladm_phys_attr_t	dpa;
898*d62bc4baSyz147064 
899*d62bc4baSyz147064 		status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
900*d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
901*d62bc4baSyz147064 			goto done;
902*d62bc4baSyz147064 
903*d62bc4baSyz147064 		(void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
904*d62bc4baSyz147064 		break;
905*d62bc4baSyz147064 	}
906*d62bc4baSyz147064 	default:
907*d62bc4baSyz147064 		status = DLADM_STATUS_BADARG;
908*d62bc4baSyz147064 		goto done;
909*d62bc4baSyz147064 	}
910*d62bc4baSyz147064 
911*d62bc4baSyz147064 	if (vid != VLAN_ID_NONE) {
912*d62bc4baSyz147064 		char		drv[MAXNAMELEN];
913*d62bc4baSyz147064 		uint_t		ppa;
914*d62bc4baSyz147064 
915*d62bc4baSyz147064 		if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
916*d62bc4baSyz147064 			status = DLADM_STATUS_BADARG;
917*d62bc4baSyz147064 			goto done;
918*d62bc4baSyz147064 		}
919*d62bc4baSyz147064 		if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
920*d62bc4baSyz147064 			status = DLADM_STATUS_TOOSMALL;
921*d62bc4baSyz147064 	} else {
922*d62bc4baSyz147064 		if (strlcpy(dev, devname, len) >= len)
923*d62bc4baSyz147064 			status = DLADM_STATUS_TOOSMALL;
924*d62bc4baSyz147064 	}
925*d62bc4baSyz147064 
926*d62bc4baSyz147064 done:
927*d62bc4baSyz147064 	return (status);
928f595a68aSyz147064 }
929