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