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