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