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