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