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