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