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.
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.
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]
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/types.h>
37 #include <net/if_types.h>
38 #include <net/if_dl.h>
40 #include <libdllink.h>
41 #include <libdlvlan.h>
42 #include <libdlaggr.h>
43 #include <libdladm_impl.h>
46 * Link Aggregation Administration Library.
48 * This library is used by administration tools such as dladm(8) to
49 * configure link aggregations.
52 /* Limits on buffer size for LAIOC_INFO request */
53 #define MIN_INFO_SIZE (4*1024)
54 #define MAX_INFO_SIZE (128*1024)
56 static uchar_t zero_mac
[] = {0, 0, 0, 0, 0, 0};
57 #define VALID_PORT_MAC(mac) \
58 (((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) && \
61 #define PORT_DELIMITER ":"
63 typedef struct dladm_aggr_modify_attr
{
65 boolean_t ld_mac_fixed
;
66 uchar_t ld_mac
[ETHERADDRL
];
67 aggr_lacp_mode_t ld_lacp_mode
;
68 aggr_lacp_timer_t ld_lacp_timer
;
69 } dladm_aggr_modify_attr_t
;
71 typedef struct policy_s
{
76 static policy_t policies
[] = {
77 {"L2", AGGR_POLICY_L2
},
78 {"L3", AGGR_POLICY_L3
},
79 {"L4", AGGR_POLICY_L4
}};
81 #define NPOLICIES (sizeof (policies) / sizeof (policy_t))
83 typedef struct dladm_aggr_lacpmode_s
{
85 aggr_lacp_mode_t mode_id
;
86 } dladm_aggr_lacpmode_t
;
88 static dladm_aggr_lacpmode_t lacp_modes
[] = {
89 {"off", AGGR_LACP_OFF
},
90 {"active", AGGR_LACP_ACTIVE
},
91 {"passive", AGGR_LACP_PASSIVE
}};
93 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
95 typedef struct dladm_aggr_lacptimer_s
{
97 aggr_lacp_timer_t lt_id
;
98 } dladm_aggr_lacptimer_t
;
100 static dladm_aggr_lacptimer_t lacp_timers
[] = {
101 {"short", AGGR_LACP_TIMER_SHORT
},
102 {"long", AGGR_LACP_TIMER_LONG
}};
104 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
106 typedef struct dladm_aggr_port_state
{
108 aggr_port_state_t state_id
;
109 } dladm_aggr_port_state_t
;
111 static dladm_aggr_port_state_t port_states
[] = {
112 {"standby", AGGR_PORT_STATE_STANDBY
},
113 {"attached", AGGR_PORT_STATE_ATTACHED
}
116 #define NPORT_STATES \
117 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
119 static dladm_status_t
120 write_port(dladm_handle_t handle
, char *portstr
, datalink_id_t portid
,
123 char pname
[MAXLINKNAMELEN
+ 1];
124 dladm_status_t status
;
126 if ((status
= dladm_datalink_id2info(handle
, portid
, NULL
, NULL
, NULL
,
127 pname
, sizeof (pname
))) != DLADM_STATUS_OK
)
129 (void) strlcat(pname
, PORT_DELIMITER
, sizeof (pname
));
130 if (strlcat(portstr
, pname
, portstrsize
) >= portstrsize
)
131 status
= DLADM_STATUS_TOOSMALL
;
135 static dladm_status_t
136 read_port(dladm_handle_t handle
, char **portstr
, datalink_id_t
*portid
)
138 dladm_status_t status
;
141 if ((pname
= strtok(*portstr
, PORT_DELIMITER
)) == NULL
)
142 return (DLADM_STATUS_REPOSITORYINVAL
);
143 *portstr
+= (strlen(pname
) + 1);
144 status
= dladm_name2info(handle
, pname
, portid
, NULL
, NULL
, NULL
);
149 i_dladm_aggr_ioctl(dladm_handle_t handle
, int cmd
, void *ptr
)
151 return (ioctl(dladm_dld_fd(handle
), cmd
, ptr
));
155 * Caller must free attr.lg_ports. The ptr pointer is advanced while convert
156 * the laioc_info_t to the dladm_aggr_grp_attr_t structure.
159 i_dladm_aggr_iocp2grpattr(void **ptr
, dladm_aggr_grp_attr_t
*attrp
)
161 laioc_info_group_t
*grp
;
162 laioc_info_port_t
*port
;
164 void *where
= (*ptr
);
166 grp
= (laioc_info_group_t
*)where
;
168 attrp
->lg_linkid
= grp
->lg_linkid
;
169 attrp
->lg_key
= grp
->lg_key
;
170 attrp
->lg_nports
= grp
->lg_nports
;
171 attrp
->lg_policy
= grp
->lg_policy
;
172 attrp
->lg_lacp_mode
= grp
->lg_lacp_mode
;
173 attrp
->lg_lacp_timer
= grp
->lg_lacp_timer
;
174 attrp
->lg_force
= grp
->lg_force
;
176 bcopy(grp
->lg_mac
, attrp
->lg_mac
, ETHERADDRL
);
177 attrp
->lg_mac_fixed
= grp
->lg_mac_fixed
;
179 if ((attrp
->lg_ports
= malloc(grp
->lg_nports
*
180 sizeof (dladm_aggr_port_attr_t
))) == NULL
) {
188 * Go through each port that is part of the group.
190 for (i
= 0; i
< grp
->lg_nports
; i
++) {
191 port
= (laioc_info_port_t
*)where
;
193 attrp
->lg_ports
[i
].lp_linkid
= port
->lp_linkid
;
194 bcopy(port
->lp_mac
, attrp
->lg_ports
[i
].lp_mac
, ETHERADDRL
);
195 attrp
->lg_ports
[i
].lp_state
= port
->lp_state
;
196 attrp
->lg_ports
[i
].lp_lacp_state
= port
->lp_lacp_state
;
207 * Get active configuration of a specific aggregation.
208 * Caller must free attrp->la_ports.
210 static dladm_status_t
211 i_dladm_aggr_info_active(dladm_handle_t handle
, datalink_id_t linkid
,
212 dladm_aggr_grp_attr_t
*attrp
)
217 dladm_status_t status
= DLADM_STATUS_OK
;
219 bufsize
= MIN_INFO_SIZE
;
220 ioc
= (laioc_info_t
*)calloc(1, bufsize
);
222 return (DLADM_STATUS_NOMEM
);
224 ioc
->li_group_linkid
= linkid
;
227 ioc
->li_bufsize
= bufsize
;
228 if (i_dladm_aggr_ioctl(handle
, LAIOC_INFO
, ioc
) != 0) {
229 if (errno
== ENOSPC
) {
231 * The LAIOC_INFO call failed due to a short
232 * buffer. Reallocate the buffer and try again.
235 if (bufsize
<= MAX_INFO_SIZE
) {
236 ioc
= (laioc_info_t
*)realloc(ioc
, bufsize
);
238 bzero(ioc
, sizeof (bufsize
));
243 status
= dladm_errno2status(errno
);
248 * Go through each group returned by the aggregation driver.
250 where
= (char *)(ioc
+ 1);
251 if (i_dladm_aggr_iocp2grpattr(&where
, attrp
) != 0) {
252 status
= dladm_errno2status(errno
);
262 * Get configuration information of a specific aggregation.
263 * Caller must free attrp->la_ports.
265 static dladm_status_t
266 i_dladm_aggr_info_persist(dladm_handle_t handle
, datalink_id_t linkid
,
267 dladm_aggr_grp_attr_t
*attrp
)
271 char *portstr
= NULL
, *next
;
272 dladm_status_t status
;
275 char macstr
[ETHERADDRL
* 3];
277 attrp
->lg_linkid
= linkid
;
278 if ((status
= dladm_getsnap_conf(handle
, linkid
, &conf
)) !=
282 status
= dladm_get_conf_field(handle
, conf
, FKEY
, &u64
, sizeof (u64
));
283 if (status
!= DLADM_STATUS_OK
)
285 attrp
->lg_key
= (uint16_t)u64
;
287 status
= dladm_get_conf_field(handle
, conf
, FPOLICY
, &u64
,
289 if (status
!= DLADM_STATUS_OK
)
291 attrp
->lg_policy
= (uint32_t)u64
;
293 status
= dladm_get_conf_field(handle
, conf
, FFIXMACADDR
,
294 &attrp
->lg_mac_fixed
, sizeof (boolean_t
));
295 if (status
!= DLADM_STATUS_OK
)
298 if (attrp
->lg_mac_fixed
) {
301 if ((status
= dladm_get_conf_field(handle
, conf
, FMACADDR
,
302 macstr
, sizeof (macstr
))) != DLADM_STATUS_OK
) {
305 if (!dladm_aggr_str2macaddr(macstr
, &fixed
, attrp
->lg_mac
)) {
306 status
= DLADM_STATUS_REPOSITORYINVAL
;
311 status
= dladm_get_conf_field(handle
, conf
, FFORCE
, &attrp
->lg_force
,
313 if (status
!= DLADM_STATUS_OK
)
316 status
= dladm_get_conf_field(handle
, conf
, FLACPMODE
, &u64
,
318 if (status
!= DLADM_STATUS_OK
)
320 attrp
->lg_lacp_mode
= (aggr_lacp_mode_t
)u64
;
322 status
= dladm_get_conf_field(handle
, conf
, FLACPTIMER
, &u64
,
324 if (status
!= DLADM_STATUS_OK
)
326 attrp
->lg_lacp_timer
= (aggr_lacp_timer_t
)u64
;
328 status
= dladm_get_conf_field(handle
, conf
, FNPORTS
, &u64
,
330 if (status
!= DLADM_STATUS_OK
)
332 nports
= (uint32_t)u64
;
333 attrp
->lg_nports
= nports
;
335 size
= nports
* (MAXLINKNAMELEN
+ 1) + 1;
336 if ((portstr
= calloc(1, size
)) == NULL
) {
337 status
= DLADM_STATUS_NOMEM
;
341 status
= dladm_get_conf_field(handle
, conf
, FPORTS
, portstr
, size
);
342 if (status
!= DLADM_STATUS_OK
)
345 if ((attrp
->lg_ports
= malloc(nports
*
346 sizeof (dladm_aggr_port_attr_t
))) == NULL
) {
347 status
= DLADM_STATUS_NOMEM
;
351 for (next
= portstr
, i
= 0; i
< nports
; i
++) {
352 if ((status
= read_port(handle
, &next
,
353 &attrp
->lg_ports
[i
].lp_linkid
)) != DLADM_STATUS_OK
)
354 free(attrp
->lg_ports
);
359 dladm_destroy_conf(handle
, conf
);
364 dladm_aggr_info(dladm_handle_t handle
, datalink_id_t linkid
,
365 dladm_aggr_grp_attr_t
*attrp
, uint32_t flags
)
367 assert(flags
== DLADM_OPT_ACTIVE
|| flags
== DLADM_OPT_PERSIST
);
368 if (flags
== DLADM_OPT_ACTIVE
)
369 return (i_dladm_aggr_info_active(handle
, linkid
, attrp
));
371 return (i_dladm_aggr_info_persist(handle
, linkid
, attrp
));
375 * Add or remove one or more ports to/from an existing link aggregation.
377 static dladm_status_t
378 i_dladm_aggr_add_rmv(dladm_handle_t handle
, datalink_id_t linkid
,
379 uint32_t nports
, dladm_aggr_port_attr_db_t
*ports
, uint32_t flags
, int cmd
)
381 char *orig_portstr
= NULL
, *portstr
= NULL
;
382 laioc_add_rem_t
*iocp
= NULL
;
383 laioc_port_t
*ioc_ports
;
384 uint32_t orig_nports
, result_nports
, len
, i
, j
;
386 datalink_class_t
class;
387 dladm_status_t status
= DLADM_STATUS_OK
;
393 return (DLADM_STATUS_BADARG
);
396 * Sanity check - aggregations can only be created over Ethernet
397 * physical links and simnets.
399 for (i
= 0; i
< nports
; i
++) {
400 if ((dladm_datalink_id2info(handle
, ports
[i
].lp_linkid
, NULL
,
401 &class, &media
, NULL
, 0) != DLADM_STATUS_OK
) ||
402 !((class == DATALINK_CLASS_PHYS
) ||
403 (class == DATALINK_CLASS_SIMNET
)) || (media
!= DL_ETHER
)) {
404 return (DLADM_STATUS_BADARG
);
409 * First, update the persistent configuration if requested. We only
410 * need to update the FPORTS and FNPORTS fields of this aggregation.
411 * Note that FPORTS is a list of port linkids separated by
412 * PORT_DELIMITER (':').
414 if (flags
& DLADM_OPT_PERSIST
) {
415 status
= dladm_open_conf(handle
, linkid
, &conf
);
416 if (status
!= DLADM_STATUS_OK
)
420 * Get the original configuration of FNPORTS and FPORTS.
422 status
= dladm_get_conf_field(handle
, conf
, FNPORTS
, &u64
,
424 if (status
!= DLADM_STATUS_OK
)
426 orig_nports
= (uint32_t)u64
;
429 * At least one port needs to be in the aggregation.
431 if ((cmd
== LAIOC_REMOVE
) && (orig_nports
<= nports
)) {
432 status
= DLADM_STATUS_BADARG
;
436 size
= orig_nports
* (MAXLINKNAMELEN
+ 1) + 1;
437 if ((orig_portstr
= calloc(1, size
)) == NULL
) {
438 status
= dladm_errno2status(errno
);
442 status
= dladm_get_conf_field(handle
, conf
, FPORTS
,
444 if (status
!= DLADM_STATUS_OK
)
447 result_nports
= (cmd
== LAIOC_ADD
) ? orig_nports
+ nports
:
450 size
= result_nports
* (MAXLINKNAMELEN
+ 1) + 1;
451 if ((portstr
= calloc(1, size
)) == NULL
) {
452 status
= dladm_errno2status(errno
);
457 * get the new configuration and set to result_nports and
460 if (cmd
== LAIOC_ADD
) {
461 (void) strlcpy(portstr
, orig_portstr
, size
);
462 for (i
= 0; i
< nports
; i
++) {
463 status
= write_port(handle
, portstr
,
464 ports
[i
].lp_linkid
, size
);
465 if (status
!= DLADM_STATUS_OK
) {
472 datalink_id_t portid
;
475 for (next
= orig_portstr
, j
= 0; j
< orig_nports
; j
++) {
477 * Read the portids from the old configuration
480 status
= read_port(handle
, &next
, &portid
);
481 if (status
!= DLADM_STATUS_OK
) {
487 * See whether this port is in the removal
488 * list. If not, copy to the new config.
490 for (i
= 0; i
< nports
; i
++) {
491 if (ports
[i
].lp_linkid
== portid
)
495 status
= write_port(handle
, portstr
,
497 if (status
!= DLADM_STATUS_OK
) {
505 if (remove
!= nports
) {
506 status
= DLADM_STATUS_LINKINVAL
;
510 result_nports
-= nports
;
514 if ((status
= dladm_set_conf_field(handle
, conf
, FNPORTS
,
515 DLADM_TYPE_UINT64
, &u64
)) != DLADM_STATUS_OK
) {
520 status
= dladm_set_conf_field(handle
, conf
, FPORTS
,
521 DLADM_TYPE_STR
, portstr
);
523 if (status
!= DLADM_STATUS_OK
)
527 * Write the new configuration to the persistent repository.
529 status
= dladm_write_conf(handle
, conf
);
532 dladm_destroy_conf(handle
, conf
);
533 if (status
!= DLADM_STATUS_OK
) {
540 * If the caller only requested to update the persistent
541 * configuration, we are done.
543 if (!(flags
& DLADM_OPT_ACTIVE
))
547 * Update the active configuration.
549 len
= sizeof (*iocp
) + nports
* sizeof (laioc_port_t
);
550 if ((iocp
= malloc(len
)) == NULL
) {
551 status
= DLADM_STATUS_NOMEM
;
555 iocp
->la_linkid
= linkid
;
556 iocp
->la_nports
= nports
;
557 if (cmd
== LAIOC_ADD
)
558 iocp
->la_force
= (flags
& DLADM_OPT_FORCE
);
560 ioc_ports
= (laioc_port_t
*)(iocp
+ 1);
561 for (i
= 0; i
< nports
; i
++)
562 ioc_ports
[i
].lp_linkid
= ports
[i
].lp_linkid
;
564 if (i_dladm_aggr_ioctl(handle
, cmd
, iocp
) < 0)
565 status
= dladm_errno2status(errno
);
571 * If the active configuration update fails, restore the old
572 * persistent configuration if we've changed that.
574 if ((status
!= DLADM_STATUS_OK
) && (flags
& DLADM_OPT_PERSIST
)) {
575 if (dladm_open_conf(handle
, linkid
, &conf
) == DLADM_STATUS_OK
) {
577 if ((dladm_set_conf_field(handle
, conf
, FNPORTS
,
578 DLADM_TYPE_UINT64
, &u64
) == DLADM_STATUS_OK
) &&
579 (dladm_set_conf_field(handle
, conf
, FPORTS
,
580 DLADM_TYPE_STR
, orig_portstr
) == DLADM_STATUS_OK
)) {
581 (void) dladm_write_conf(handle
, conf
);
583 (void) dladm_destroy_conf(handle
, conf
);
591 * Send a modify command to the link aggregation driver.
593 static dladm_status_t
594 i_dladm_aggr_modify_sys(dladm_handle_t handle
, datalink_id_t linkid
,
595 uint32_t mask
, dladm_aggr_modify_attr_t
*attr
)
599 ioc
.lu_linkid
= linkid
;
601 ioc
.lu_modify_mask
= 0;
602 if (mask
& DLADM_AGGR_MODIFY_POLICY
)
603 ioc
.lu_modify_mask
|= LAIOC_MODIFY_POLICY
;
604 if (mask
& DLADM_AGGR_MODIFY_MAC
)
605 ioc
.lu_modify_mask
|= LAIOC_MODIFY_MAC
;
606 if (mask
& DLADM_AGGR_MODIFY_LACP_MODE
)
607 ioc
.lu_modify_mask
|= LAIOC_MODIFY_LACP_MODE
;
608 if (mask
& DLADM_AGGR_MODIFY_LACP_TIMER
)
609 ioc
.lu_modify_mask
|= LAIOC_MODIFY_LACP_TIMER
;
611 ioc
.lu_policy
= attr
->ld_policy
;
612 ioc
.lu_mac_fixed
= attr
->ld_mac_fixed
;
613 bcopy(attr
->ld_mac
, ioc
.lu_mac
, ETHERADDRL
);
614 ioc
.lu_lacp_mode
= attr
->ld_lacp_mode
;
615 ioc
.lu_lacp_timer
= attr
->ld_lacp_timer
;
617 if (i_dladm_aggr_ioctl(handle
, LAIOC_MODIFY
, &ioc
) < 0) {
619 return (DLADM_STATUS_MACADDRINVAL
);
621 return (dladm_errno2status(errno
));
623 return (DLADM_STATUS_OK
);
628 * Send a create command to the link aggregation driver.
630 static dladm_status_t
631 i_dladm_aggr_create_sys(dladm_handle_t handle
, datalink_id_t linkid
,
632 uint16_t key
, uint32_t nports
, dladm_aggr_port_attr_db_t
*ports
,
633 uint32_t policy
, boolean_t mac_addr_fixed
, const uchar_t
*mac_addr
,
634 aggr_lacp_mode_t lacp_mode
, aggr_lacp_timer_t lacp_timer
, boolean_t force
)
637 laioc_create_t
*iocp
= NULL
;
638 laioc_port_t
*ioc_ports
;
639 dladm_status_t status
= DLADM_STATUS_OK
;
641 len
= sizeof (*iocp
) + nports
* sizeof (laioc_port_t
);
644 return (DLADM_STATUS_NOMEM
);
647 iocp
->lc_linkid
= linkid
;
648 iocp
->lc_nports
= nports
;
649 iocp
->lc_policy
= policy
;
650 iocp
->lc_lacp_mode
= lacp_mode
;
651 iocp
->lc_lacp_timer
= lacp_timer
;
652 ioc_ports
= (laioc_port_t
*)(iocp
+ 1);
653 iocp
->lc_force
= force
;
655 for (i
= 0; i
< nports
; i
++)
656 ioc_ports
[i
].lp_linkid
= ports
[i
].lp_linkid
;
658 if (mac_addr_fixed
&& !VALID_PORT_MAC(mac_addr
)) {
659 status
= DLADM_STATUS_MACADDRINVAL
;
663 bcopy(mac_addr
, iocp
->lc_mac
, ETHERADDRL
);
664 iocp
->lc_mac_fixed
= mac_addr_fixed
;
666 if (i_dladm_aggr_ioctl(handle
, LAIOC_CREATE
, iocp
) < 0)
667 status
= dladm_errno2status(errno
);
675 * Invoked to bring up a link aggregation group.
678 i_dladm_aggr_up(dladm_handle_t handle
, datalink_id_t linkid
, void *arg
)
680 dladm_status_t
*statusp
= (dladm_status_t
*)arg
;
681 dladm_aggr_grp_attr_t attr
;
682 dladm_aggr_port_attr_db_t
*ports
= NULL
;
685 dladm_status_t status
;
687 status
= dladm_aggr_info(handle
, linkid
, &attr
, DLADM_OPT_PERSIST
);
688 if (status
!= DLADM_STATUS_OK
) {
690 return (DLADM_WALK_CONTINUE
);
693 if (attr
.lg_key
<= AGGR_MAX_KEY
)
696 ports
= malloc(attr
.lg_nports
* sizeof (dladm_aggr_port_attr_db_t
));
698 status
= DLADM_STATUS_NOMEM
;
703 * Validate (and purge) each physical link associated with this
704 * aggregation, if the specific hardware has been removed during
705 * the system shutdown.
707 for (i
= 0, j
= 0; i
< attr
.lg_nports
; i
++) {
708 datalink_id_t portid
= attr
.lg_ports
[i
].lp_linkid
;
712 s
= dladm_datalink_id2info(handle
, portid
, &flags
, NULL
, NULL
,
714 if (s
!= DLADM_STATUS_OK
|| !(flags
& DLADM_OPT_ACTIVE
))
717 ports
[j
++].lp_linkid
= portid
;
722 * All of the physical links are removed.
724 status
= DLADM_STATUS_BADARG
;
729 * Create active aggregation.
731 if ((status
= i_dladm_aggr_create_sys(handle
, linkid
,
732 key
, j
, ports
, attr
.lg_policy
, attr
.lg_mac_fixed
,
733 (const uchar_t
*)attr
.lg_mac
, attr
.lg_lacp_mode
,
734 attr
.lg_lacp_timer
, attr
.lg_force
)) != DLADM_STATUS_OK
) {
738 if ((status
= dladm_up_datalink_id(handle
, linkid
)) !=
742 ioc
.ld_linkid
= linkid
;
743 (void) i_dladm_aggr_ioctl(handle
, LAIOC_DELETE
, &ioc
);
750 return (DLADM_WALK_CONTINUE
);
754 * Bring up one aggregation, or all persistent aggregations. In the latter
755 * case, the walk may terminate early if bringup of an aggregation fails.
758 dladm_aggr_up(dladm_handle_t handle
, datalink_id_t linkid
)
760 dladm_status_t status
;
762 if (linkid
== DATALINK_ALL_LINKID
) {
763 (void) dladm_walk_datalink_id(i_dladm_aggr_up
, handle
, &status
,
764 DATALINK_CLASS_AGGR
, DATALINK_ANY_MEDIATYPE
,
766 return (DLADM_STATUS_OK
);
768 (void) i_dladm_aggr_up(handle
, linkid
, &status
);
774 * Given a policy string, return a policy mask. Returns B_TRUE on
775 * success, or B_FALSE if an error occurred during parsing.
778 dladm_aggr_str2policy(const char *str
, uint32_t *policy
)
787 while ((token
= strtok_r((token
== NULL
) ? (char *)str
: NULL
, ",",
789 for (i
= 0; i
< NPOLICIES
; i
++) {
791 if (strcasecmp(token
, pol
->pol_name
) == 0) {
792 *policy
|= pol
->policy
;
804 * Given a policy mask, returns a printable string, or NULL if the
805 * policy mask is invalid. It is the responsibility of the caller to
806 * free the returned string after use.
809 dladm_aggr_policy2str(uint32_t policy
, char *str
)
811 int i
, npolicies
= 0;
819 for (i
= 0; i
< NPOLICIES
; i
++) {
821 if ((policy
& pol
->policy
) != 0) {
824 (void) strlcat(str
, ",", DLADM_STRSIZE
);
825 (void) strlcat(str
, pol
->pol_name
, DLADM_STRSIZE
);
833 * Given a MAC address string, return the MAC address in the mac_addr
834 * array. If the MAC address was not explicitly specified, i.e. is
835 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
836 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
839 dladm_aggr_str2macaddr(const char *str
, boolean_t
*mac_fixed
, uchar_t
*mac_addr
)
844 *mac_fixed
= (strcmp(str
, "auto") != 0);
846 bzero(mac_addr
, ETHERADDRL
);
850 conv_str
= _link_aton(str
, &mac_len
);
851 if (conv_str
== NULL
)
854 if (mac_len
!= ETHERADDRL
) {
859 if ((bcmp(zero_mac
, conv_str
, ETHERADDRL
) == 0) ||
860 (conv_str
[0] & 0x01)) {
865 bcopy(conv_str
, mac_addr
, ETHERADDRL
);
872 * Returns a string containing a printable representation of a MAC address.
875 dladm_aggr_macaddr2str(const unsigned char *mac
, char *buf
)
877 static char unknown_mac
[] = {0, 0, 0, 0, 0, 0};
882 if (bcmp(unknown_mac
, mac
, ETHERADDRL
) == 0)
883 (void) strlcpy(buf
, "unknown", DLADM_STRSIZE
);
885 return (_link_ntoa(mac
, buf
, ETHERADDRL
, IFT_OTHER
));
891 * Given a LACP mode string, find the corresponding LACP mode number. Returns
892 * B_TRUE if a match was found, B_FALSE otherwise.
895 dladm_aggr_str2lacpmode(const char *str
, aggr_lacp_mode_t
*lacp_mode
)
898 dladm_aggr_lacpmode_t
*mode
;
900 for (i
= 0; i
< NLACP_MODES
; i
++) {
901 mode
= &lacp_modes
[i
];
902 if (strncasecmp(str
, mode
->mode_str
,
903 strlen(mode
->mode_str
)) == 0) {
904 *lacp_mode
= mode
->mode_id
;
913 * Given a LACP mode number, returns a printable string, or NULL if the
914 * LACP mode number is invalid.
917 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id
, char *buf
)
920 dladm_aggr_lacpmode_t
*mode
;
925 for (i
= 0; i
< NLACP_MODES
; i
++) {
926 mode
= &lacp_modes
[i
];
927 if (mode
->mode_id
== mode_id
) {
928 (void) snprintf(buf
, DLADM_STRSIZE
, "%s",
934 (void) strlcpy(buf
, "unknown", DLADM_STRSIZE
);
939 * Given a LACP timer string, find the corresponding LACP timer number. Returns
940 * B_TRUE if a match was found, B_FALSE otherwise.
943 dladm_aggr_str2lacptimer(const char *str
, aggr_lacp_timer_t
*lacp_timer
)
946 dladm_aggr_lacptimer_t
*timer
;
948 for (i
= 0; i
< NLACP_TIMERS
; i
++) {
949 timer
= &lacp_timers
[i
];
950 if (strncasecmp(str
, timer
->lt_str
,
951 strlen(timer
->lt_str
)) == 0) {
952 *lacp_timer
= timer
->lt_id
;
961 * Given a LACP timer, returns a printable string, or NULL if the
962 * LACP timer number is invalid.
965 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id
, char *buf
)
968 dladm_aggr_lacptimer_t
*timer
;
973 for (i
= 0; i
< NLACP_TIMERS
; i
++) {
974 timer
= &lacp_timers
[i
];
975 if (timer
->lt_id
== timer_id
) {
976 (void) snprintf(buf
, DLADM_STRSIZE
, "%s",
982 (void) strlcpy(buf
, "unknown", DLADM_STRSIZE
);
987 dladm_aggr_portstate2str(aggr_port_state_t state_id
, char *buf
)
990 dladm_aggr_port_state_t
*state
;
995 for (i
= 0; i
< NPORT_STATES
; i
++) {
996 state
= &port_states
[i
];
997 if (state
->state_id
== state_id
) {
998 (void) snprintf(buf
, DLADM_STRSIZE
, "%s",
1004 (void) strlcpy(buf
, "unknown", DLADM_STRSIZE
);
1008 static dladm_status_t
1009 dladm_aggr_persist_aggr_conf(dladm_handle_t handle
, const char *link
,
1010 datalink_id_t linkid
, uint16_t key
, uint32_t nports
,
1011 dladm_aggr_port_attr_db_t
*ports
, uint32_t policy
, boolean_t mac_addr_fixed
,
1012 const uchar_t
*mac_addr
, aggr_lacp_mode_t lacp_mode
,
1013 aggr_lacp_timer_t lacp_timer
, boolean_t force
)
1016 char *portstr
= NULL
;
1017 char macstr
[ETHERADDRL
* 3];
1018 dladm_status_t status
;
1022 if ((status
= dladm_create_conf(handle
, link
, linkid
,
1023 DATALINK_CLASS_AGGR
, DL_ETHER
, &conf
)) != DLADM_STATUS_OK
) {
1028 status
= dladm_set_conf_field(handle
, conf
, FKEY
, DLADM_TYPE_UINT64
,
1030 if (status
!= DLADM_STATUS_OK
)
1034 status
= dladm_set_conf_field(handle
, conf
, FNPORTS
, DLADM_TYPE_UINT64
,
1036 if (status
!= DLADM_STATUS_OK
)
1039 size
= nports
* MAXLINKNAMELEN
+ 1;
1040 if ((portstr
= calloc(1, size
)) == NULL
) {
1041 status
= DLADM_STATUS_NOMEM
;
1045 for (i
= 0; i
< nports
; i
++) {
1046 status
= write_port(handle
, portstr
, ports
[i
].lp_linkid
, size
);
1047 if (status
!= DLADM_STATUS_OK
) {
1052 status
= dladm_set_conf_field(handle
, conf
, FPORTS
, DLADM_TYPE_STR
,
1056 if (status
!= DLADM_STATUS_OK
)
1060 status
= dladm_set_conf_field(handle
, conf
, FPOLICY
, DLADM_TYPE_UINT64
,
1062 if (status
!= DLADM_STATUS_OK
)
1065 status
= dladm_set_conf_field(handle
, conf
, FFIXMACADDR
,
1066 DLADM_TYPE_BOOLEAN
, &mac_addr_fixed
);
1067 if (status
!= DLADM_STATUS_OK
)
1070 if (mac_addr_fixed
) {
1071 if (!VALID_PORT_MAC(mac_addr
)) {
1072 status
= DLADM_STATUS_MACADDRINVAL
;
1076 (void) dladm_aggr_macaddr2str(mac_addr
, macstr
);
1077 status
= dladm_set_conf_field(handle
, conf
, FMACADDR
,
1078 DLADM_TYPE_STR
, macstr
);
1079 if (status
!= DLADM_STATUS_OK
)
1083 status
= dladm_set_conf_field(handle
, conf
, FFORCE
, DLADM_TYPE_BOOLEAN
,
1085 if (status
!= DLADM_STATUS_OK
)
1089 status
= dladm_set_conf_field(handle
, conf
, FLACPMODE
,
1090 DLADM_TYPE_UINT64
, &u64
);
1091 if (status
!= DLADM_STATUS_OK
)
1095 status
= dladm_set_conf_field(handle
, conf
, FLACPTIMER
,
1096 DLADM_TYPE_UINT64
, &u64
);
1097 if (status
!= DLADM_STATUS_OK
)
1101 * Commit the link aggregation configuration.
1103 status
= dladm_write_conf(handle
, conf
);
1106 dladm_destroy_conf(handle
, conf
);
1111 * Create a new link aggregation group. Update the configuration
1112 * file and bring it up.
1115 dladm_aggr_create(dladm_handle_t handle
, const char *name
, uint16_t key
,
1116 uint32_t nports
, dladm_aggr_port_attr_db_t
*ports
, uint32_t policy
,
1117 boolean_t mac_addr_fixed
, const uchar_t
*mac_addr
,
1118 aggr_lacp_mode_t lacp_mode
, aggr_lacp_timer_t lacp_timer
, uint32_t flags
)
1120 datalink_id_t linkid
= DATALINK_INVALID_LINKID
;
1123 datalink_class_t
class;
1124 dladm_status_t status
;
1125 boolean_t force
= (flags
& DLADM_OPT_FORCE
) ? B_TRUE
: B_FALSE
;
1127 if (key
!= 0 && key
> AGGR_MAX_KEY
)
1128 return (DLADM_STATUS_KEYINVAL
);
1131 return (DLADM_STATUS_BADARG
);
1133 for (i
= 0; i
< nports
; i
++) {
1134 if ((dladm_datalink_id2info(handle
, ports
[i
].lp_linkid
, NULL
,
1135 &class, &media
, NULL
, 0) != DLADM_STATUS_OK
) ||
1136 !((class == DATALINK_CLASS_PHYS
|| class ==
1137 DATALINK_CLASS_SIMNET
) && (media
== DL_ETHER
))) {
1138 return (DLADM_STATUS_BADARG
);
1142 flags
&= (DLADM_OPT_ACTIVE
| DLADM_OPT_PERSIST
);
1143 if ((status
= dladm_create_datalink_id(handle
, name
,
1144 DATALINK_CLASS_AGGR
, DL_ETHER
, flags
, &linkid
)) !=
1149 if ((flags
& DLADM_OPT_PERSIST
) &&
1150 (status
= dladm_aggr_persist_aggr_conf(handle
, name
, linkid
, key
,
1151 nports
, ports
, policy
, mac_addr_fixed
, mac_addr
, lacp_mode
,
1152 lacp_timer
, force
)) != DLADM_STATUS_OK
) {
1156 if (!(flags
& DLADM_OPT_ACTIVE
))
1157 return (DLADM_STATUS_OK
);
1159 status
= i_dladm_aggr_create_sys(handle
, linkid
, key
, nports
, ports
,
1160 policy
, mac_addr_fixed
, mac_addr
, lacp_mode
, lacp_timer
, force
);
1162 if (status
!= DLADM_STATUS_OK
) {
1163 if (flags
& DLADM_OPT_PERSIST
)
1164 (void) dladm_remove_conf(handle
, linkid
);
1168 return (DLADM_STATUS_OK
);
1171 if (linkid
!= DATALINK_INVALID_LINKID
)
1172 (void) dladm_destroy_datalink_id(handle
, linkid
, flags
);
1177 static dladm_status_t
1178 i_dladm_aggr_get_aggr_attr(dladm_handle_t handle
, dladm_conf_t conf
,
1179 uint32_t mask
, dladm_aggr_modify_attr_t
*attrp
)
1181 dladm_status_t status
= DLADM_STATUS_OK
;
1182 char macstr
[ETHERADDRL
* 3];
1185 if (mask
& DLADM_AGGR_MODIFY_POLICY
) {
1186 status
= dladm_get_conf_field(handle
, conf
, FPOLICY
, &u64
,
1188 if (status
!= DLADM_STATUS_OK
)
1190 attrp
->ld_policy
= (uint32_t)u64
;
1193 if (mask
& DLADM_AGGR_MODIFY_MAC
) {
1194 status
= dladm_get_conf_field(handle
, conf
, FFIXMACADDR
,
1195 &attrp
->ld_mac_fixed
, sizeof (boolean_t
));
1196 if (status
!= DLADM_STATUS_OK
)
1199 if (attrp
->ld_mac_fixed
) {
1202 status
= dladm_get_conf_field(handle
, conf
, FMACADDR
,
1203 macstr
, sizeof (macstr
));
1204 if (status
!= DLADM_STATUS_OK
)
1207 if (!dladm_aggr_str2macaddr(macstr
, &fixed
,
1209 return (DLADM_STATUS_REPOSITORYINVAL
);
1214 if (mask
& DLADM_AGGR_MODIFY_LACP_MODE
) {
1215 status
= dladm_get_conf_field(handle
, conf
, FLACPMODE
, &u64
,
1217 if (status
!= DLADM_STATUS_OK
)
1219 attrp
->ld_lacp_mode
= (aggr_lacp_mode_t
)u64
;
1222 if (mask
& DLADM_AGGR_MODIFY_LACP_TIMER
) {
1223 status
= dladm_get_conf_field(handle
, conf
, FLACPTIMER
, &u64
,
1225 if (status
!= DLADM_STATUS_OK
)
1227 attrp
->ld_lacp_timer
= (aggr_lacp_timer_t
)u64
;
1233 static dladm_status_t
1234 i_dladm_aggr_set_aggr_attr(dladm_handle_t handle
, dladm_conf_t conf
,
1235 uint32_t mask
, dladm_aggr_modify_attr_t
*attrp
)
1237 dladm_status_t status
= DLADM_STATUS_OK
;
1238 char macstr
[ETHERADDRL
* 3];
1241 if (mask
& DLADM_AGGR_MODIFY_POLICY
) {
1242 u64
= attrp
->ld_policy
;
1243 status
= dladm_set_conf_field(handle
, conf
, FPOLICY
,
1244 DLADM_TYPE_UINT64
, &u64
);
1245 if (status
!= DLADM_STATUS_OK
)
1249 if (mask
& DLADM_AGGR_MODIFY_MAC
) {
1250 status
= dladm_set_conf_field(handle
, conf
, FFIXMACADDR
,
1251 DLADM_TYPE_BOOLEAN
, &attrp
->ld_mac_fixed
);
1252 if (status
!= DLADM_STATUS_OK
)
1255 if (attrp
->ld_mac_fixed
) {
1256 (void) dladm_aggr_macaddr2str(attrp
->ld_mac
, macstr
);
1257 status
= dladm_set_conf_field(handle
, conf
, FMACADDR
,
1258 DLADM_TYPE_STR
, macstr
);
1259 if (status
!= DLADM_STATUS_OK
)
1264 if (mask
& DLADM_AGGR_MODIFY_LACP_MODE
) {
1265 u64
= attrp
->ld_lacp_mode
;
1266 status
= dladm_set_conf_field(handle
, conf
, FLACPMODE
,
1267 DLADM_TYPE_UINT64
, &u64
);
1268 if (status
!= DLADM_STATUS_OK
)
1272 if (mask
& DLADM_AGGR_MODIFY_LACP_TIMER
) {
1273 u64
= attrp
->ld_lacp_timer
;
1274 status
= dladm_set_conf_field(handle
, conf
, FLACPTIMER
,
1275 DLADM_TYPE_UINT64
, &u64
);
1276 if (status
!= DLADM_STATUS_OK
)
1284 * Modify the parameters of an existing link aggregation group. Update
1285 * the configuration file and pass the changes to the kernel.
1288 dladm_aggr_modify(dladm_handle_t handle
, datalink_id_t linkid
,
1289 uint32_t modify_mask
, uint32_t policy
, boolean_t mac_fixed
,
1290 const uchar_t
*mac_addr
, aggr_lacp_mode_t lacp_mode
,
1291 aggr_lacp_timer_t lacp_timer
, uint32_t flags
)
1293 dladm_aggr_modify_attr_t new_attr
, old_attr
;
1295 dladm_status_t status
;
1297 new_attr
.ld_policy
= policy
;
1298 new_attr
.ld_mac_fixed
= mac_fixed
;
1299 new_attr
.ld_lacp_mode
= lacp_mode
;
1300 new_attr
.ld_lacp_timer
= lacp_timer
;
1301 bcopy(mac_addr
, new_attr
.ld_mac
, ETHERADDRL
);
1303 if (flags
& DLADM_OPT_PERSIST
) {
1304 status
= dladm_open_conf(handle
, linkid
, &conf
);
1305 if (status
!= DLADM_STATUS_OK
)
1308 if ((status
= i_dladm_aggr_get_aggr_attr(handle
, conf
,
1309 modify_mask
, &old_attr
)) != DLADM_STATUS_OK
) {
1313 if ((status
= i_dladm_aggr_set_aggr_attr(handle
, conf
,
1314 modify_mask
, &new_attr
)) != DLADM_STATUS_OK
) {
1318 status
= dladm_write_conf(handle
, conf
);
1321 dladm_destroy_conf(handle
, conf
);
1322 if (status
!= DLADM_STATUS_OK
)
1326 if (!(flags
& DLADM_OPT_ACTIVE
))
1327 return (DLADM_STATUS_OK
);
1329 status
= i_dladm_aggr_modify_sys(handle
, linkid
, modify_mask
,
1331 if ((status
!= DLADM_STATUS_OK
) && (flags
& DLADM_OPT_PERSIST
)) {
1332 if (dladm_open_conf(handle
, linkid
, &conf
) == DLADM_STATUS_OK
) {
1333 if (i_dladm_aggr_set_aggr_attr(handle
, conf
,
1334 modify_mask
, &old_attr
) == DLADM_STATUS_OK
) {
1335 (void) dladm_write_conf(handle
, conf
);
1337 dladm_destroy_conf(handle
, conf
);
1344 typedef struct aggr_held_arg_s
{
1345 datalink_id_t aggrid
;
1350 i_dladm_aggr_is_held(dladm_handle_t handle
, datalink_id_t linkid
, void *arg
)
1352 aggr_held_arg_t
*aggr_held_arg
= arg
;
1353 dladm_vlan_attr_t dva
;
1355 if (dladm_vlan_info(handle
, linkid
, &dva
, DLADM_OPT_PERSIST
) !=
1357 return (DLADM_WALK_CONTINUE
);
1359 if (dva
.dv_linkid
== aggr_held_arg
->aggrid
) {
1361 * This VLAN is created over the given aggregation.
1363 aggr_held_arg
->isheld
= B_TRUE
;
1364 return (DLADM_WALK_TERMINATE
);
1366 return (DLADM_WALK_CONTINUE
);
1370 * Delete a previously created link aggregation group. Either the name "aggr"
1371 * or the "key" is specified.
1374 dladm_aggr_delete(dladm_handle_t handle
, datalink_id_t linkid
, uint32_t flags
)
1377 datalink_class_t
class;
1378 dladm_status_t status
;
1380 if ((dladm_datalink_id2info(handle
, linkid
, NULL
, &class, NULL
, NULL
,
1381 0) != DLADM_STATUS_OK
) || (class != DATALINK_CLASS_AGGR
)) {
1382 return (DLADM_STATUS_BADARG
);
1385 if (flags
& DLADM_OPT_ACTIVE
) {
1386 ioc
.ld_linkid
= linkid
;
1387 if ((i_dladm_aggr_ioctl(handle
, LAIOC_DELETE
, &ioc
) < 0) &&
1388 ((errno
!= ENOENT
) || !(flags
& DLADM_OPT_PERSIST
))) {
1389 status
= dladm_errno2status(errno
);
1394 * Delete ACTIVE linkprop first.
1396 (void) dladm_set_linkprop(handle
, linkid
, NULL
, NULL
, 0,
1398 (void) dladm_destroy_datalink_id(handle
, linkid
,
1403 * If we reach here, it means that the active aggregation has already
1404 * been deleted, and there is no active VLANs holding this aggregation.
1405 * Now we see whether there is any persistent VLANs holding this
1406 * aggregation. If so, we fail the operation.
1408 if (flags
& DLADM_OPT_PERSIST
) {
1409 aggr_held_arg_t arg
;
1411 arg
.aggrid
= linkid
;
1412 arg
.isheld
= B_FALSE
;
1414 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held
, handle
,
1415 &arg
, DATALINK_CLASS_VLAN
, DATALINK_ANY_MEDIATYPE
,
1418 return (DLADM_STATUS_LINKBUSY
);
1420 (void) dladm_remove_conf(handle
, linkid
);
1421 (void) dladm_destroy_datalink_id(handle
, linkid
,
1425 return (DLADM_STATUS_OK
);
1429 * Add one or more ports to an existing link aggregation.
1432 dladm_aggr_add(dladm_handle_t handle
, datalink_id_t linkid
, uint32_t nports
,
1433 dladm_aggr_port_attr_db_t
*ports
, uint32_t flags
)
1435 return (i_dladm_aggr_add_rmv(handle
, linkid
, nports
, ports
, flags
,
1440 * Remove one or more ports from an existing link aggregation.
1443 dladm_aggr_remove(dladm_handle_t handle
, datalink_id_t linkid
, uint32_t nports
,
1444 dladm_aggr_port_attr_db_t
*ports
, uint32_t flags
)
1446 return (i_dladm_aggr_add_rmv(handle
, linkid
, nports
, ports
, flags
,
1450 typedef struct i_walk_key_state_s
{
1452 datalink_id_t linkid
;
1454 } i_walk_key_state_t
;
1457 i_dladm_walk_key2linkid(dladm_handle_t handle
, datalink_id_t linkid
, void *arg
)
1461 dladm_status_t status
;
1462 i_walk_key_state_t
*statep
= (i_walk_key_state_t
*)arg
;
1465 if (dladm_getsnap_conf(handle
, linkid
, &conf
) != DLADM_STATUS_OK
)
1466 return (DLADM_WALK_CONTINUE
);
1468 status
= dladm_get_conf_field(handle
, conf
, FKEY
, &u64
, sizeof (u64
));
1469 key
= (uint16_t)u64
;
1470 dladm_destroy_conf(handle
, conf
);
1472 if ((status
== DLADM_STATUS_OK
) && (key
== statep
->key
)) {
1473 statep
->found
= B_TRUE
;
1474 statep
->linkid
= linkid
;
1475 return (DLADM_WALK_TERMINATE
);
1478 return (DLADM_WALK_CONTINUE
);
1482 dladm_key2linkid(dladm_handle_t handle
, uint16_t key
, datalink_id_t
*linkidp
,
1485 i_walk_key_state_t state
;
1487 if (key
> AGGR_MAX_KEY
)
1488 return (DLADM_STATUS_NOTFOUND
);
1490 state
.found
= B_FALSE
;
1493 (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid
, handle
, &state
,
1494 DATALINK_CLASS_AGGR
, DATALINK_ANY_MEDIATYPE
, flags
);
1495 if (state
.found
== B_TRUE
) {
1496 *linkidp
= state
.linkid
;
1497 return (DLADM_STATUS_OK
);
1499 return (DLADM_STATUS_NOTFOUND
);