xref: /titanic_50/usr/src/lib/libdladm/common/libdllink.c (revision 4983fdb5373239f810c1288128badcb36d079b78)
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. Note that this function takes a link name
256  * argument instead of a linkid, because a data-link (and its linkid) could
257  * be created implicitly as the result of this function. For example, a VLAN
258  * could be created if a VLAN PPA hack name is assigned to an exclusive
259  * non-global zone.
260  */
261 dladm_status_t
262 dladm_setzid(const char *link, zoneid_t zoneid)
263 {
264 	int			fd;
265 	dladm_status_t		status = DLADM_STATUS_OK;
266 	dld_ioc_setzid_t	dis;
267 
268 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
269 		return (dladm_errno2status(errno));
270 
271 	bzero(&dis, sizeof (dld_ioc_setzid_t));
272 	(void) strlcpy(dis.dis_link, link, MAXLINKNAMELEN);
273 	dis.dis_zid = zoneid;
274 
275 	if (i_dladm_ioctl(fd, DLDIOC_SETZID, &dis, sizeof (dis)) < 0)
276 		status = dladm_errno2status(errno);
277 
278 	(void) close(fd);
279 	return (status);
280 }
281 
282 /*
283  * Get zoneid of a given link
284  */
285 dladm_status_t
286 dladm_getzid(datalink_id_t linkid, zoneid_t *zoneidp)
287 {
288 	int			fd;
289 	dladm_status_t		status = DLADM_STATUS_OK;
290 	dld_ioc_getzid_t	dig;
291 
292 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
293 		return (dladm_errno2status(errno));
294 
295 	bzero(&dig, sizeof (dld_ioc_getzid_t));
296 	dig.dig_linkid = linkid;
297 	dig.dig_zid = -1;
298 
299 	if (i_dladm_ioctl(fd, DLDIOC_GETZID, &dig, sizeof (dig)) < 0)
300 		status = dladm_errno2status(errno);
301 
302 	(void) close(fd);
303 
304 	if (status == DLADM_STATUS_OK)
305 		*zoneidp = dig.dig_zid;
306 
307 	return (status);
308 }
309 
310 /*
311  * Case 1: rename an existing link1 to a link2 that does not exist.
312  * Result: <linkid1, link2>
313  */
314 static dladm_status_t
315 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1,
316     const char *link2, uint32_t flags)
317 {
318 	dld_ioc_rename_t	dir;
319 	dladm_conf_t		conf;
320 	dladm_status_t		status = DLADM_STATUS_OK;
321 	int			fd;
322 
323 	/*
324 	 * Link is currently available. Check to see whether anything is
325 	 * holding this link to prevent a rename operation.
326 	 */
327 	if (flags & DLADM_OPT_ACTIVE) {
328 		dir.dir_linkid1 = linkid1;
329 		dir.dir_linkid2 = DATALINK_INVALID_LINKID;
330 		(void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
331 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
332 			return (dladm_errno2status(errno));
333 
334 		if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) {
335 			status = dladm_errno2status(errno);
336 			(void) close(fd);
337 			return (status);
338 		}
339 	}
340 
341 	status = dladm_remap_datalink_id(linkid1, link2);
342 	if (status != DLADM_STATUS_OK)
343 		goto done;
344 
345 	/*
346 	 * Flush the current mapping to persistent configuration.
347 	 */
348 	if ((flags & DLADM_OPT_PERSIST) &&
349 	    (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) ||
350 	    ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) {
351 		(void) dladm_remap_datalink_id(linkid1, link1);
352 	}
353 done:
354 	if (flags & DLADM_OPT_ACTIVE) {
355 		if (status != DLADM_STATUS_OK) {
356 			(void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
357 			(void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir,
358 			    sizeof (dir));
359 		}
360 		(void) close(fd);
361 	}
362 	return (status);
363 }
364 
365 typedef struct link_hold_arg_s {
366 	datalink_id_t	linkid;
367 	datalink_id_t	holder;
368 	uint32_t	flags;
369 } link_hold_arg_t;
370 
371 static int
372 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg)
373 {
374 	link_hold_arg_t		*hold_arg = arg;
375 	dladm_aggr_grp_attr_t	ginfo;
376 	dladm_status_t		status;
377 	int			i;
378 
379 	status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags);
380 	if (status != DLADM_STATUS_OK)
381 		return (DLADM_WALK_CONTINUE);
382 
383 	for (i = 0; i < ginfo.lg_nports; i++) {
384 		if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
385 			hold_arg->holder = aggrid;
386 			return (DLADM_WALK_TERMINATE);
387 		}
388 	}
389 	return (DLADM_WALK_CONTINUE);
390 }
391 
392 static int
393 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg)
394 {
395 	link_hold_arg_t		*hold_arg = arg;
396 	dladm_vlan_attr_t	vinfo;
397 	dladm_status_t		status;
398 
399 	status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags);
400 	if (status != DLADM_STATUS_OK)
401 		return (DLADM_WALK_CONTINUE);
402 
403 	if (vinfo.dv_linkid == hold_arg->linkid) {
404 		hold_arg->holder = vlanid;
405 		return (DLADM_WALK_TERMINATE);
406 	}
407 	return (DLADM_WALK_CONTINUE);
408 }
409 
410 /*
411  * Case 2: rename an available physical link link1 to a REMOVED physical link
412  *     link2.  As a result, link1 directly inherits all datalinks configured
413  *     over link2 (linkid2).
414  * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
415  *     link2_other_attr>
416  */
417 static dladm_status_t
418 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2)
419 {
420 	rcm_handle_t		*rcm_hdl = NULL;
421 	nvlist_t		*nvl = NULL;
422 	link_hold_arg_t		arg;
423 	dld_ioc_rename_t	dir;
424 	int			fd;
425 	dladm_conf_t		conf1, conf2;
426 	char			devname[MAXLINKNAMELEN];
427 	uint64_t		phymaj, phyinst;
428 	dladm_status_t		status = DLADM_STATUS_OK;
429 
430 	/*
431 	 * First check if linkid1 is associated with any persistent
432 	 * aggregations or VLANs. If yes, return BUSY.
433 	 */
434 	arg.linkid = linkid1;
435 	arg.holder = DATALINK_INVALID_LINKID;
436 	arg.flags = DLADM_OPT_PERSIST;
437 	(void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg,
438 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
439 	if (arg.holder != DATALINK_INVALID_LINKID)
440 		return (DLADM_STATUS_LINKBUSY);
441 
442 	arg.flags = DLADM_OPT_PERSIST;
443 	(void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg,
444 	    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
445 	if (arg.holder != DATALINK_INVALID_LINKID)
446 		return (DLADM_STATUS_LINKBUSY);
447 
448 	/*
449 	 * Send DLDIOC_RENAME to request to rename link1's linkid to
450 	 * be linkid2. This will check whether link1 is used by any
451 	 * aggregations or VLANs, or is held by any application. If yes,
452 	 * return failure.
453 	 */
454 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
455 		return (dladm_errno2status(errno));
456 
457 	dir.dir_linkid1 = linkid1;
458 	dir.dir_linkid2 = linkid2;
459 	if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0)
460 		status = dladm_errno2status(errno);
461 
462 	if (status != DLADM_STATUS_OK) {
463 		(void) close(fd);
464 		return (status);
465 	}
466 
467 	/*
468 	 * Now change the phymaj, phyinst and devname associated with linkid1
469 	 * to be associated with linkid2. Before doing that, the old active
470 	 * linkprop of linkid1 should be deleted.
471 	 */
472 	(void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE);
473 
474 	if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) ||
475 	    ((status = dladm_get_conf_field(conf1, FDEVNAME, devname,
476 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
477 	    ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj,
478 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
479 	    ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst,
480 	    sizeof (uint64_t))) != DLADM_STATUS_OK) ||
481 	    ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) {
482 		dir.dir_linkid1 = linkid2;
483 		dir.dir_linkid2 = linkid1;
484 		(void) dladm_init_linkprop(linkid1);
485 		(void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir));
486 		(void) close(fd);
487 		return (status);
488 	}
489 	(void) close(fd);
490 
491 	dladm_destroy_conf(conf1);
492 	(void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname);
493 	(void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj);
494 	(void) dladm_set_conf_field(conf2, FPHYINST,
495 	    DLADM_TYPE_UINT64, &phyinst);
496 	(void) dladm_write_conf(conf2);
497 	dladm_destroy_conf(conf2);
498 
499 	/*
500 	 * Delete link1 and mark link2 up.
501 	 */
502 	(void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE |
503 	    DLADM_OPT_PERSIST);
504 	(void) dladm_remove_conf(linkid1);
505 	(void) dladm_up_datalink_id(linkid2);
506 
507 	/*
508 	 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
509 	 * consumed by the RCM framework to restore all the datalink and
510 	 * IP configuration.
511 	 */
512 	status = DLADM_STATUS_FAILED;
513 	if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
514 	    (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
515 		goto done;
516 	}
517 
518 	if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
519 		goto done;
520 
521 	if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
522 	    RCM_SUCCESS) {
523 		status = DLADM_STATUS_OK;
524 	}
525 
526 done:
527 	if (rcm_hdl != NULL)
528 		(void) rcm_free_handle(rcm_hdl);
529 	if (nvl != NULL)
530 		nvlist_free(nvl);
531 	return (status);
532 }
533 
534 /*
535  * case 3: rename a non-existent link to a REMOVED physical link.
536  * Set the removed physical link's device name to link1, so that
537  * when link1 attaches, it inherits all the link configuration of
538  * the removed physical link.
539  */
540 static dladm_status_t
541 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2)
542 {
543 	dladm_conf_t	conf;
544 	dladm_status_t	status;
545 
546 	if (!dladm_valid_linkname(link1))
547 		return (DLADM_STATUS_LINKINVAL);
548 
549 	status = dladm_read_conf(linkid2, &conf);
550 	if (status != DLADM_STATUS_OK)
551 		goto done;
552 
553 	if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR,
554 	    link1)) == DLADM_STATUS_OK) {
555 		status = dladm_write_conf(conf);
556 	}
557 
558 	dladm_destroy_conf(conf);
559 
560 done:
561 	return (status);
562 }
563 
564 dladm_status_t
565 dladm_rename_link(const char *link1, const char *link2)
566 {
567 	datalink_id_t		linkid1 = DATALINK_INVALID_LINKID;
568 	datalink_id_t		linkid2 = DATALINK_INVALID_LINKID;
569 	uint32_t		flags1, flags2;
570 	datalink_class_t	class1, class2;
571 	uint32_t		media1, media2;
572 	boolean_t		remphy2 = B_FALSE;
573 	dladm_status_t  	status;
574 
575 	(void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1);
576 	if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) ==
577 	    DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
578 	    (flags2 == DLADM_OPT_PERSIST)) {
579 		/*
580 		 * see whether link2 is a removed physical link.
581 		 */
582 		remphy2 = B_TRUE;
583 	}
584 
585 	if (linkid1 != DATALINK_INVALID_LINKID) {
586 		if (linkid2 == DATALINK_INVALID_LINKID) {
587 			/*
588 			 * case 1: rename an existing link to a link that
589 			 * does not exist.
590 			 */
591 			status = i_dladm_rename_link_c1(linkid1, link1, link2,
592 			    flags1);
593 		} else if (remphy2) {
594 			/*
595 			 * case 2: rename an available link to a REMOVED
596 			 * physical link. Return failure if link1 is not
597 			 * an active physical link.
598 			 */
599 			if ((class1 != class2) || (media1 != media2) ||
600 			    !(flags1 & DLADM_OPT_ACTIVE)) {
601 				status = DLADM_STATUS_BADARG;
602 			} else {
603 				status = i_dladm_rename_link_c2(linkid1,
604 				    linkid2);
605 			}
606 		} else {
607 			status = DLADM_STATUS_EXIST;
608 		}
609 	} else if (remphy2) {
610 		status = i_dladm_rename_link_c3(link1, linkid2);
611 	} else {
612 		status = DLADM_STATUS_NOTFOUND;
613 	}
614 	return (status);
615 }
616 
617 typedef struct consumer_del_phys_arg_s {
618 	datalink_id_t	linkid;
619 } consumer_del_phys_arg_t;
620 
621 static int
622 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg)
623 {
624 	consumer_del_phys_arg_t	*del_arg = arg;
625 	dladm_vlan_attr_t	vinfo;
626 	dladm_status_t		status;
627 
628 	status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST);
629 	if (status != DLADM_STATUS_OK)
630 		return (DLADM_WALK_CONTINUE);
631 
632 	if (vinfo.dv_linkid == del_arg->linkid)
633 		(void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST);
634 	return (DLADM_WALK_CONTINUE);
635 }
636 
637 static int
638 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg)
639 {
640 	consumer_del_phys_arg_t		*del_arg = arg;
641 	dladm_aggr_grp_attr_t		ginfo;
642 	dladm_status_t			status;
643 	dladm_aggr_port_attr_db_t	port[1];
644 	int				i;
645 
646 	status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST);
647 	if (status != DLADM_STATUS_OK)
648 		return (DLADM_WALK_CONTINUE);
649 
650 	for (i = 0; i < ginfo.lg_nports; i++)
651 		if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
652 			break;
653 
654 	if (i != ginfo.lg_nports) {
655 		if (ginfo.lg_nports == 1 && i == 0) {
656 			consumer_del_phys_arg_t	aggr_del_arg;
657 
658 			/*
659 			 * First delete all the VLANs on this aggregation, then
660 			 * delete the aggregation itself.
661 			 */
662 			aggr_del_arg.linkid = aggrid;
663 			(void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
664 			    &aggr_del_arg, DATALINK_CLASS_VLAN,
665 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
666 			(void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST);
667 		} else {
668 			port[0].lp_linkid = del_arg->linkid;
669 			(void) dladm_aggr_remove(aggrid, 1, port,
670 			    DLADM_OPT_PERSIST);
671 		}
672 	}
673 	return (DLADM_WALK_CONTINUE);
674 }
675 
676 typedef struct del_phys_arg_s {
677 	dladm_status_t	rval;
678 } del_phys_arg_t;
679 
680 static int
681 i_dladm_phys_delete(datalink_id_t linkid, void *arg)
682 {
683 	uint32_t		flags;
684 	datalink_class_t	class;
685 	uint32_t		media;
686 	dladm_status_t		status = DLADM_STATUS_OK;
687 	del_phys_arg_t		*del_phys_arg = arg;
688 	consumer_del_phys_arg_t	del_arg;
689 
690 	if ((status = dladm_datalink_id2info(linkid, &flags, &class,
691 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
692 		goto done;
693 	}
694 
695 	/*
696 	 * see whether this link is a removed physical link.
697 	 */
698 	if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
699 	    (flags & DLADM_OPT_ACTIVE)) {
700 		status = DLADM_STATUS_BADARG;
701 		goto done;
702 	}
703 
704 	if (media == DL_ETHER) {
705 		del_arg.linkid = linkid;
706 		(void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg,
707 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
708 		    DLADM_OPT_PERSIST);
709 		(void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg,
710 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
711 		    DLADM_OPT_PERSIST);
712 	}
713 
714 	(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
715 	(void) dladm_remove_conf(linkid);
716 
717 done:
718 	del_phys_arg->rval = status;
719 	return (DLADM_WALK_CONTINUE);
720 }
721 
722 dladm_status_t
723 dladm_phys_delete(datalink_id_t linkid)
724 {
725 	del_phys_arg_t	arg = {DLADM_STATUS_OK};
726 
727 	if (linkid == DATALINK_ALL_LINKID) {
728 		(void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg,
729 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
730 		    DLADM_OPT_PERSIST);
731 		return (DLADM_STATUS_OK);
732 	} else {
733 		(void) i_dladm_phys_delete(linkid, &arg);
734 		return (arg.rval);
735 	}
736 }
737 
738 dladm_status_t
739 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags)
740 {
741 	dladm_status_t	status;
742 
743 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
744 
745 	switch (flags) {
746 	case DLADM_OPT_PERSIST: {
747 		dladm_conf_t	conf;
748 
749 		status = dladm_read_conf(linkid, &conf);
750 		if (status != DLADM_STATUS_OK)
751 			return (status);
752 
753 		status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev,
754 		    MAXLINKNAMELEN);
755 		dladm_destroy_conf(conf);
756 		return (status);
757 	}
758 	case DLADM_OPT_ACTIVE: {
759 		dld_ioc_phys_attr_t	dip;
760 		int			fd;
761 
762 		if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
763 			return (dladm_errno2status(errno));
764 
765 		dip.dip_linkid = linkid;
766 		if (i_dladm_ioctl(fd, DLDIOC_PHYS_ATTR, &dip, sizeof (dip))
767 		    < 0) {
768 			status = dladm_errno2status(errno);
769 			(void) close(fd);
770 			return (status);
771 		}
772 		(void) close(fd);
773 		dpap->dp_novanity = dip.dip_novanity;
774 		(void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
775 		return (DLADM_STATUS_OK);
776 	}
777 	default:
778 		return (DLADM_STATUS_BADARG);
779 	}
780 }
781 
782 typedef struct i_walk_dev_state_s {
783 	const char *devname;
784 	datalink_id_t linkid;
785 	boolean_t found;
786 } i_walk_dev_state_t;
787 
788 int
789 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg)
790 {
791 	dladm_phys_attr_t dpa;
792 	dladm_status_t status;
793 	i_walk_dev_state_t *statep = arg;
794 
795 	status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
796 	if ((status == DLADM_STATUS_OK) &&
797 	    (strcmp(statep->devname, dpa.dp_dev) == 0)) {
798 		statep->found = B_TRUE;
799 		statep->linkid = linkid;
800 		return (DLADM_WALK_TERMINATE);
801 	}
802 	return (DLADM_WALK_CONTINUE);
803 }
804 
805 /*
806  * Get the linkid from the physical device name.
807  */
808 dladm_status_t
809 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp)
810 {
811 	i_walk_dev_state_t state;
812 
813 	state.found = B_FALSE;
814 	state.devname = devname;
815 
816 	(void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state,
817 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
818 	if (state.found == B_TRUE) {
819 		*linkidp = state.linkid;
820 		return (DLADM_STATUS_OK);
821 	} else {
822 		return (dladm_errno2status(ENOENT));
823 	}
824 }
825 
826 static int
827 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
828 {
829 	char	*cp, *tp;
830 	int	len;
831 
832 	/*
833 	 * device name length must not be 0, and it must end with digit.
834 	 */
835 	if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
836 		return (EINVAL);
837 
838 	(void) strlcpy(driver, devname, maxlen);
839 	cp = (char *)&driver[len - 1];
840 
841 	for (tp = cp; isdigit(*tp); tp--) {
842 		if (tp <= driver)
843 			return (EINVAL);
844 	}
845 
846 	*ppa = atoi(tp + 1);
847 	*(tp + 1) = '\0';
848 	return (0);
849 }
850 
851 dladm_status_t
852 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len)
853 {
854 	char			devname[MAXLINKNAMELEN];
855 	uint16_t		vid = VLAN_ID_NONE;
856 	datalink_class_t	class;
857 	dladm_status_t		status;
858 
859 	status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0);
860 	if (status != DLADM_STATUS_OK)
861 		goto done;
862 
863 	/*
864 	 * If this is a VLAN, we must first determine the class and linkid of
865 	 * the link the VLAN has been created over.
866 	 */
867 	if (class == DATALINK_CLASS_VLAN) {
868 		dladm_vlan_attr_t	dva;
869 
870 		status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE);
871 		if (status != DLADM_STATUS_OK)
872 			goto done;
873 		linkid = dva.dv_linkid;
874 		vid = dva.dv_vid;
875 
876 		if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL,
877 		    NULL, 0)) != DLADM_STATUS_OK) {
878 			goto done;
879 		}
880 	}
881 
882 	switch (class) {
883 	case DATALINK_CLASS_AGGR: {
884 		dladm_aggr_grp_attr_t	dga;
885 
886 		status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE);
887 		if (status != DLADM_STATUS_OK)
888 			goto done;
889 
890 		if (dga.lg_key == 0) {
891 			/*
892 			 * If the key was not specified when the aggregation
893 			 * is created, we cannot guess its /dev node name.
894 			 */
895 			status = DLADM_STATUS_BADARG;
896 			goto done;
897 		}
898 		(void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
899 		break;
900 	}
901 	case DATALINK_CLASS_PHYS: {
902 		dladm_phys_attr_t	dpa;
903 
904 		status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST);
905 		if (status != DLADM_STATUS_OK)
906 			goto done;
907 
908 		(void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
909 		break;
910 	}
911 	default:
912 		status = DLADM_STATUS_BADARG;
913 		goto done;
914 	}
915 
916 	if (vid != VLAN_ID_NONE) {
917 		char		drv[MAXNAMELEN];
918 		uint_t		ppa;
919 
920 		if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
921 			status = DLADM_STATUS_BADARG;
922 			goto done;
923 		}
924 		if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
925 			status = DLADM_STATUS_TOOSMALL;
926 	} else {
927 		if (strlcpy(dev, devname, len) >= len)
928 			status = DLADM_STATUS_TOOSMALL;
929 	}
930 
931 done:
932 	return (status);
933 }
934 
935 dladm_status_t
936 dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type,
937     void *val)
938 {
939 	char		module[DLPI_LINKNAME_MAX];
940 	uint_t		instance;
941 	char 		link[DLPI_LINKNAME_MAX];
942 	dladm_status_t	status;
943 	uint32_t	flags, media;
944 	kstat_ctl_t	*kcp;
945 	kstat_t		*ksp;
946 	dladm_phys_attr_t dpap;
947 
948 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media,
949 	    link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
950 		return (status);
951 
952 	if (media != DL_ETHER)
953 		return (DLADM_STATUS_LINKINVAL);
954 
955 	status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST);
956 
957 	if (status != DLADM_STATUS_OK)
958 		return (status);
959 
960 	status = dladm_parselink(dpap.dp_dev, module, &instance);
961 
962 	if (status != DLADM_STATUS_OK)
963 		return (status);
964 
965 	if ((kcp = kstat_open()) == NULL)
966 		return (dladm_errno2status(errno));
967 
968 	/*
969 	 * The kstat query could fail if the underlying MAC
970 	 * driver was already detached.
971 	 */
972 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
973 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
974 		goto bail;
975 
976 	if (kstat_read(kcp, ksp, NULL) == -1)
977 		goto bail;
978 
979 	if (dladm_kstat_value(ksp, name, type, val) < 0)
980 		goto bail;
981 
982 	(void) kstat_close(kcp);
983 	return (DLADM_STATUS_OK);
984 bail:
985 	(void) kstat_close(kcp);
986 	return (dladm_errno2status(errno));
987 
988 }
989 
990 int
991 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
992 {
993 	kstat_named_t	*knp;
994 
995 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
996 		return (-1);
997 
998 	if (knp->data_type != type)
999 		return (-1);
1000 
1001 	switch (type) {
1002 	case KSTAT_DATA_UINT64:
1003 		*(uint64_t *)buf = knp->value.ui64;
1004 		break;
1005 	case KSTAT_DATA_UINT32:
1006 		*(uint32_t *)buf = knp->value.ui32;
1007 		break;
1008 	default:
1009 		return (-1);
1010 	}
1011 
1012 	return (0);
1013 }
1014 
1015 dladm_status_t
1016 dladm_parselink(const char *dev, char *provider, uint_t *ppa)
1017 {
1018 	ifspec_t	ifsp;
1019 
1020 	if (dev == NULL || !ifparse_ifspec(dev, &ifsp))
1021 		return (DLADM_STATUS_LINKINVAL);
1022 
1023 	if (provider != NULL)
1024 		(void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
1025 
1026 	if (ppa != NULL)
1027 		*ppa = ifsp.ifsp_ppa;
1028 
1029 	return (DLADM_STATUS_OK);
1030 }
1031