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