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