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