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