xref: /illumos-gate/usr/src/lib/libdladm/common/libdllink.c (revision 4202e8bf1d88e8c78515b9b8f0386471afe2bf77)
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
i_dladm_info(dladm_handle_t handle,const datalink_id_t linkid,dladm_attr_t * dap)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
dladm_usagelog(dladm_handle_t handle,dladm_logtype_t type,dld_ioc_usagelog_t * log_info)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
dladm_start_usagelog(dladm_handle_t handle,dladm_logtype_t type,uint_t interval)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
dladm_stop_usagelog(dladm_handle_t handle,dladm_logtype_t type)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
i_dladm_walk(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
dladm_walk(dladm_walkcb_t * fn,dladm_handle_t handle,void * arg,datalink_class_t class,datalink_media_t dmedia,uint32_t flags)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
dladm_walk_hwgrp(dladm_handle_t handle,datalink_id_t linkid,void * arg,boolean_t (* fn)(void *,dladm_hwgrp_attr_t *))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
dladm_walk_macaddr(dladm_handle_t handle,datalink_id_t linkid,void * arg,boolean_t (* fn)(void *,dladm_macaddr_attr_t *))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
i_dladm_mac_walk(di_node_t node,di_minor_t minor __unused,void * arg)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
dladm_mac_walk(int (* fn)(const char *,void * arg),void * arg)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
dladm_info(dladm_handle_t handle,datalink_id_t linkid,dladm_attr_t * dap)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 *
dladm_linkstate2str(link_state_t state,char * buf)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 *
dladm_linkduplex2str(link_duplex_t duplex,char * buf)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
i_dladm_rename_link_c1(dladm_handle_t handle,datalink_id_t linkid1,const char * link1,const char * link2,uint32_t flags)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
i_dladm_aggr_link_hold(dladm_handle_t handle,datalink_id_t aggrid,void * arg)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
i_dladm_vlan_link_hold(dladm_handle_t handle,datalink_id_t vlanid,void * arg)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
i_dladm_rename_link_c2(dladm_handle_t handle,datalink_id_t linkid1,datalink_id_t linkid2)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
i_dladm_rename_link_c3(dladm_handle_t handle,const char * link1,datalink_id_t linkid2)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
dladm_rename_link(dladm_handle_t handle,const char * link1,const char * link2)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
i_dladm_vlan_link_del(dladm_handle_t handle,datalink_id_t vlanid,void * arg)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
i_dladm_part_link_del(dladm_handle_t handle,datalink_id_t partid,void * arg)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
i_dladm_aggr_link_del(dladm_handle_t handle,datalink_id_t aggrid,void * arg)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
i_dladm_phys_delete(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
dladm_phys_delete(dladm_handle_t handle,datalink_id_t linkid)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
dladm_phys_info(dladm_handle_t handle,datalink_id_t linkid,dladm_phys_attr_t * dpap,uint32_t flags)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
i_dladm_walk_dev2linkid(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
dladm_dev2linkid(dladm_handle_t handle,const char * devname,datalink_id_t * linkidp)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
parse_devname(const char * devname,char * driver,uint_t * ppa,size_t maxlen)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
dladm_linkid2legacyname(dladm_handle_t handle,datalink_id_t linkid,char * dev,size_t len)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
dladm_parselink(const char * dev,char * provider,uint_t * ppa)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