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