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