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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stropts.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <strings.h>
36 #include <libintl.h>
37 #include <net/if_types.h>
38 #include <net/if_dl.h>
39 #include <sys/dld.h>
40 #include <libdllink.h>
41 #include <libdlvlan.h>
42 #include <libdlaggr.h>
43 #include <libdladm_impl.h>
44
45 /*
46 * Link Aggregation Administration Library.
47 *
48 * This library is used by administration tools such as dladm(8) to
49 * configure link aggregations.
50 */
51
52 /* Limits on buffer size for LAIOC_INFO request */
53 #define MIN_INFO_SIZE (4*1024)
54 #define MAX_INFO_SIZE (128*1024)
55
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) && \
59 (!((mac)[0] & 0x01)))
60
61 #define PORT_DELIMITER ":"
62
63 typedef struct dladm_aggr_modify_attr {
64 uint32_t ld_policy;
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;
70
71 typedef struct policy_s {
72 char *pol_name;
73 uint32_t policy;
74 } policy_t;
75
76 static policy_t policies[] = {
77 {"L2", AGGR_POLICY_L2},
78 {"L3", AGGR_POLICY_L3},
79 {"L4", AGGR_POLICY_L4}};
80
81 #define NPOLICIES (sizeof (policies) / sizeof (policy_t))
82
83 typedef struct dladm_aggr_lacpmode_s {
84 char *mode_str;
85 aggr_lacp_mode_t mode_id;
86 } dladm_aggr_lacpmode_t;
87
88 static dladm_aggr_lacpmode_t lacp_modes[] = {
89 {"off", AGGR_LACP_OFF},
90 {"active", AGGR_LACP_ACTIVE},
91 {"passive", AGGR_LACP_PASSIVE}};
92
93 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
94
95 typedef struct dladm_aggr_lacptimer_s {
96 char *lt_str;
97 aggr_lacp_timer_t lt_id;
98 } dladm_aggr_lacptimer_t;
99
100 static dladm_aggr_lacptimer_t lacp_timers[] = {
101 {"short", AGGR_LACP_TIMER_SHORT},
102 {"long", AGGR_LACP_TIMER_LONG}};
103
104 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
105
106 typedef struct dladm_aggr_port_state {
107 char *state_str;
108 aggr_port_state_t state_id;
109 } dladm_aggr_port_state_t;
110
111 static dladm_aggr_port_state_t port_states[] = {
112 {"standby", AGGR_PORT_STATE_STANDBY },
113 {"attached", AGGR_PORT_STATE_ATTACHED }
114 };
115
116 #define NPORT_STATES \
117 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
118
119 static dladm_status_t
write_port(dladm_handle_t handle,char * portstr,datalink_id_t portid,size_t portstrsize)120 write_port(dladm_handle_t handle, char *portstr, datalink_id_t portid,
121 size_t portstrsize)
122 {
123 char pname[MAXLINKNAMELEN + 1];
124 dladm_status_t status;
125
126 if ((status = dladm_datalink_id2info(handle, portid, NULL, NULL, NULL,
127 pname, sizeof (pname))) != DLADM_STATUS_OK)
128 return (status);
129 (void) strlcat(pname, PORT_DELIMITER, sizeof (pname));
130 if (strlcat(portstr, pname, portstrsize) >= portstrsize)
131 status = DLADM_STATUS_TOOSMALL;
132 return (status);
133 }
134
135 static dladm_status_t
read_port(dladm_handle_t handle,char ** portstr,datalink_id_t * portid)136 read_port(dladm_handle_t handle, char **portstr, datalink_id_t *portid)
137 {
138 dladm_status_t status;
139 char *pname;
140
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);
145 return (status);
146 }
147
148 static int
i_dladm_aggr_ioctl(dladm_handle_t handle,int cmd,void * ptr)149 i_dladm_aggr_ioctl(dladm_handle_t handle, int cmd, void *ptr)
150 {
151 return (ioctl(dladm_dld_fd(handle), cmd, ptr));
152 }
153
154 /*
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.
157 */
158 static int
i_dladm_aggr_iocp2grpattr(void ** ptr,dladm_aggr_grp_attr_t * attrp)159 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp)
160 {
161 laioc_info_group_t *grp;
162 laioc_info_port_t *port;
163 uint_t i;
164 void *where = (*ptr);
165
166 grp = (laioc_info_group_t *)where;
167
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;
175
176 bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL);
177 attrp->lg_mac_fixed = grp->lg_mac_fixed;
178
179 if ((attrp->lg_ports = malloc(grp->lg_nports *
180 sizeof (dladm_aggr_port_attr_t))) == NULL) {
181 errno = ENOMEM;
182 goto fail;
183 }
184
185 where = (grp + 1);
186
187 /*
188 * Go through each port that is part of the group.
189 */
190 for (i = 0; i < grp->lg_nports; i++) {
191 port = (laioc_info_port_t *)where;
192
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;
197
198 where = (port + 1);
199 }
200 *ptr = where;
201 return (0);
202 fail:
203 return (-1);
204 }
205
206 /*
207 * Get active configuration of a specific aggregation.
208 * Caller must free attrp->la_ports.
209 */
210 static dladm_status_t
i_dladm_aggr_info_active(dladm_handle_t handle,datalink_id_t linkid,dladm_aggr_grp_attr_t * attrp)211 i_dladm_aggr_info_active(dladm_handle_t handle, datalink_id_t linkid,
212 dladm_aggr_grp_attr_t *attrp)
213 {
214 laioc_info_t *ioc;
215 int bufsize;
216 void *where;
217 dladm_status_t status = DLADM_STATUS_OK;
218
219 bufsize = MIN_INFO_SIZE;
220 ioc = (laioc_info_t *)calloc(1, bufsize);
221 if (ioc == NULL)
222 return (DLADM_STATUS_NOMEM);
223
224 ioc->li_group_linkid = linkid;
225
226 tryagain:
227 ioc->li_bufsize = bufsize;
228 if (i_dladm_aggr_ioctl(handle, LAIOC_INFO, ioc) != 0) {
229 if (errno == ENOSPC) {
230 /*
231 * The LAIOC_INFO call failed due to a short
232 * buffer. Reallocate the buffer and try again.
233 */
234 bufsize *= 2;
235 if (bufsize <= MAX_INFO_SIZE) {
236 ioc = (laioc_info_t *)realloc(ioc, bufsize);
237 if (ioc != NULL) {
238 bzero(ioc, sizeof (bufsize));
239 goto tryagain;
240 }
241 }
242 }
243 status = dladm_errno2status(errno);
244 goto bail;
245 }
246
247 /*
248 * Go through each group returned by the aggregation driver.
249 */
250 where = (char *)(ioc + 1);
251 if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) {
252 status = dladm_errno2status(errno);
253 goto bail;
254 }
255
256 bail:
257 free(ioc);
258 return (status);
259 }
260
261 /*
262 * Get configuration information of a specific aggregation.
263 * Caller must free attrp->la_ports.
264 */
265 static dladm_status_t
i_dladm_aggr_info_persist(dladm_handle_t handle,datalink_id_t linkid,dladm_aggr_grp_attr_t * attrp)266 i_dladm_aggr_info_persist(dladm_handle_t handle, datalink_id_t linkid,
267 dladm_aggr_grp_attr_t *attrp)
268 {
269 dladm_conf_t conf;
270 uint32_t nports, i;
271 char *portstr = NULL, *next;
272 dladm_status_t status;
273 uint64_t u64;
274 int size;
275 char macstr[ETHERADDRL * 3];
276
277 attrp->lg_linkid = linkid;
278 if ((status = dladm_getsnap_conf(handle, linkid, &conf)) !=
279 DLADM_STATUS_OK)
280 return (status);
281
282 status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
283 if (status != DLADM_STATUS_OK)
284 goto done;
285 attrp->lg_key = (uint16_t)u64;
286
287 status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
288 sizeof (u64));
289 if (status != DLADM_STATUS_OK)
290 goto done;
291 attrp->lg_policy = (uint32_t)u64;
292
293 status = dladm_get_conf_field(handle, conf, FFIXMACADDR,
294 &attrp->lg_mac_fixed, sizeof (boolean_t));
295 if (status != DLADM_STATUS_OK)
296 goto done;
297
298 if (attrp->lg_mac_fixed) {
299 boolean_t fixed;
300
301 if ((status = dladm_get_conf_field(handle, conf, FMACADDR,
302 macstr, sizeof (macstr))) != DLADM_STATUS_OK) {
303 goto done;
304 }
305 if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) {
306 status = DLADM_STATUS_REPOSITORYINVAL;
307 goto done;
308 }
309 }
310
311 status = dladm_get_conf_field(handle, conf, FFORCE, &attrp->lg_force,
312 sizeof (boolean_t));
313 if (status != DLADM_STATUS_OK)
314 goto done;
315
316 status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
317 sizeof (u64));
318 if (status != DLADM_STATUS_OK)
319 goto done;
320 attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64;
321
322 status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
323 sizeof (u64));
324 if (status != DLADM_STATUS_OK)
325 goto done;
326 attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64;
327
328 status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
329 sizeof (u64));
330 if (status != DLADM_STATUS_OK)
331 goto done;
332 nports = (uint32_t)u64;
333 attrp->lg_nports = nports;
334
335 size = nports * (MAXLINKNAMELEN + 1) + 1;
336 if ((portstr = calloc(1, size)) == NULL) {
337 status = DLADM_STATUS_NOMEM;
338 goto done;
339 }
340
341 status = dladm_get_conf_field(handle, conf, FPORTS, portstr, size);
342 if (status != DLADM_STATUS_OK)
343 goto done;
344
345 if ((attrp->lg_ports = malloc(nports *
346 sizeof (dladm_aggr_port_attr_t))) == NULL) {
347 status = DLADM_STATUS_NOMEM;
348 goto done;
349 }
350
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);
355 }
356
357 done:
358 free(portstr);
359 dladm_destroy_conf(handle, conf);
360 return (status);
361 }
362
363 dladm_status_t
dladm_aggr_info(dladm_handle_t handle,datalink_id_t linkid,dladm_aggr_grp_attr_t * attrp,uint32_t flags)364 dladm_aggr_info(dladm_handle_t handle, datalink_id_t linkid,
365 dladm_aggr_grp_attr_t *attrp, uint32_t flags)
366 {
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));
370 else
371 return (i_dladm_aggr_info_persist(handle, linkid, attrp));
372 }
373
374 /*
375 * Add or remove one or more ports to/from an existing link aggregation.
376 */
377 static dladm_status_t
i_dladm_aggr_add_rmv(dladm_handle_t handle,datalink_id_t linkid,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t flags,int cmd)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)
380 {
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 = 0, result_nports, len, i, j;
385 dladm_conf_t conf;
386 datalink_class_t class;
387 dladm_status_t status = DLADM_STATUS_OK;
388 int size;
389 uint64_t u64;
390 uint32_t media;
391
392 if (nports == 0)
393 return (DLADM_STATUS_BADARG);
394
395 /*
396 * Sanity check - aggregations can only be created over Ethernet
397 * physical links and simnets.
398 */
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);
405 }
406 }
407
408 /*
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 (':').
413 */
414 if (flags & DLADM_OPT_PERSIST) {
415 status = dladm_open_conf(handle, linkid, &conf);
416 if (status != DLADM_STATUS_OK)
417 return (status);
418
419 /*
420 * Get the original configuration of FNPORTS and FPORTS.
421 */
422 status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
423 sizeof (u64));
424 if (status != DLADM_STATUS_OK)
425 goto destroyconf;
426 orig_nports = (uint32_t)u64;
427
428 /*
429 * At least one port needs to be in the aggregation.
430 */
431 if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) {
432 status = DLADM_STATUS_BADARG;
433 goto destroyconf;
434 }
435
436 size = orig_nports * (MAXLINKNAMELEN + 1) + 1;
437 if ((orig_portstr = calloc(1, size)) == NULL) {
438 status = dladm_errno2status(errno);
439 goto destroyconf;
440 }
441
442 status = dladm_get_conf_field(handle, conf, FPORTS,
443 orig_portstr, size);
444 if (status != DLADM_STATUS_OK)
445 goto destroyconf;
446
447 result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports :
448 orig_nports;
449
450 size = result_nports * (MAXLINKNAMELEN + 1) + 1;
451 if ((portstr = calloc(1, size)) == NULL) {
452 status = dladm_errno2status(errno);
453 goto destroyconf;
454 }
455
456 /*
457 * get the new configuration and set to result_nports and
458 * portstr.
459 */
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) {
466 free(portstr);
467 goto destroyconf;
468 }
469 }
470 } else {
471 char *next;
472 datalink_id_t portid;
473 uint32_t remove = 0;
474
475 for (next = orig_portstr, j = 0; j < orig_nports; j++) {
476 /*
477 * Read the portids from the old configuration
478 * one by one.
479 */
480 status = read_port(handle, &next, &portid);
481 if (status != DLADM_STATUS_OK) {
482 free(portstr);
483 goto destroyconf;
484 }
485
486 /*
487 * See whether this port is in the removal
488 * list. If not, copy to the new config.
489 */
490 for (i = 0; i < nports; i++) {
491 if (ports[i].lp_linkid == portid)
492 break;
493 }
494 if (i == nports) {
495 status = write_port(handle, portstr,
496 portid, size);
497 if (status != DLADM_STATUS_OK) {
498 free(portstr);
499 goto destroyconf;
500 }
501 } else {
502 remove++;
503 }
504 }
505 if (remove != nports) {
506 status = DLADM_STATUS_LINKINVAL;
507 free(portstr);
508 goto destroyconf;
509 }
510 result_nports -= nports;
511 }
512
513 u64 = result_nports;
514 if ((status = dladm_set_conf_field(handle, conf, FNPORTS,
515 DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) {
516 free(portstr);
517 goto destroyconf;
518 }
519
520 status = dladm_set_conf_field(handle, conf, FPORTS,
521 DLADM_TYPE_STR, portstr);
522 free(portstr);
523 if (status != DLADM_STATUS_OK)
524 goto destroyconf;
525
526 /*
527 * Write the new configuration to the persistent repository.
528 */
529 status = dladm_write_conf(handle, conf);
530
531 destroyconf:
532 dladm_destroy_conf(handle, conf);
533 if (status != DLADM_STATUS_OK) {
534 free(orig_portstr);
535 return (status);
536 }
537 }
538
539 /*
540 * If the caller only requested to update the persistent
541 * configuration, we are done.
542 */
543 if (!(flags & DLADM_OPT_ACTIVE))
544 goto done;
545
546 /*
547 * Update the active configuration.
548 */
549 len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
550 if ((iocp = malloc(len)) == NULL) {
551 status = DLADM_STATUS_NOMEM;
552 goto done;
553 }
554
555 iocp->la_linkid = linkid;
556 iocp->la_nports = nports;
557 if (cmd == LAIOC_ADD)
558 iocp->la_force = (flags & DLADM_OPT_FORCE);
559
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;
563
564 if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0)
565 status = dladm_errno2status(errno);
566
567 done:
568 free(iocp);
569
570 /*
571 * If the active configuration update fails, restore the old
572 * persistent configuration if we've changed that.
573 */
574 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
575 if (dladm_open_conf(handle, linkid, &conf) == DLADM_STATUS_OK) {
576 u64 = orig_nports;
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);
582 }
583 (void) dladm_destroy_conf(handle, conf);
584 }
585 }
586 free(orig_portstr);
587 return (status);
588 }
589
590 /*
591 * Send a modify command to the link aggregation driver.
592 */
593 static dladm_status_t
i_dladm_aggr_modify_sys(dladm_handle_t handle,datalink_id_t linkid,uint32_t mask,dladm_aggr_modify_attr_t * attr)594 i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid,
595 uint32_t mask, dladm_aggr_modify_attr_t *attr)
596 {
597 laioc_modify_t ioc;
598
599 ioc.lu_linkid = linkid;
600
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;
610
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;
616
617 if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) {
618 if (errno == EINVAL)
619 return (DLADM_STATUS_MACADDRINVAL);
620 else
621 return (dladm_errno2status(errno));
622 } else {
623 return (DLADM_STATUS_OK);
624 }
625 }
626
627 /*
628 * Send a create command to the link aggregation driver.
629 */
630 static dladm_status_t
i_dladm_aggr_create_sys(dladm_handle_t handle,datalink_id_t linkid,uint16_t key,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t policy,boolean_t mac_addr_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer,boolean_t force)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)
635 {
636 uint_t i, len;
637 laioc_create_t *iocp = NULL;
638 laioc_port_t *ioc_ports;
639 dladm_status_t status = DLADM_STATUS_OK;
640
641 len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
642 iocp = malloc(len);
643 if (iocp == NULL)
644 return (DLADM_STATUS_NOMEM);
645
646 iocp->lc_key = key;
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;
654
655 for (i = 0; i < nports; i++)
656 ioc_ports[i].lp_linkid = ports[i].lp_linkid;
657
658 if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) {
659 status = DLADM_STATUS_MACADDRINVAL;
660 goto done;
661 }
662
663 bcopy(mac_addr, iocp->lc_mac, ETHERADDRL);
664 iocp->lc_mac_fixed = mac_addr_fixed;
665
666 if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0)
667 status = dladm_errno2status(errno);
668
669 done:
670 free(iocp);
671 return (status);
672 }
673
674 /*
675 * Invoked to bring up a link aggregation group.
676 */
677 static int
i_dladm_aggr_up(dladm_handle_t handle,datalink_id_t linkid,void * arg)678 i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
679 {
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;
683 uint16_t key = 0;
684 uint_t i, j;
685 dladm_status_t status;
686
687 status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST);
688 if (status != DLADM_STATUS_OK) {
689 *statusp = status;
690 return (DLADM_WALK_CONTINUE);
691 }
692
693 if (attr.lg_key <= AGGR_MAX_KEY)
694 key = attr.lg_key;
695
696 ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t));
697 if (ports == NULL) {
698 status = DLADM_STATUS_NOMEM;
699 goto done;
700 }
701
702 /*
703 * Validate (and purge) each physical link associated with this
704 * aggregation, if the specific hardware has been removed during
705 * the system shutdown.
706 */
707 for (i = 0, j = 0; i < attr.lg_nports; i++) {
708 datalink_id_t portid = attr.lg_ports[i].lp_linkid;
709 uint32_t flags;
710 dladm_status_t s;
711
712 s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL,
713 NULL, 0);
714 if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE))
715 continue;
716
717 ports[j++].lp_linkid = portid;
718 }
719
720 if (j == 0) {
721 /*
722 * All of the physical links are removed.
723 */
724 status = DLADM_STATUS_BADARG;
725 goto done;
726 }
727
728 /*
729 * Create active aggregation.
730 */
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) {
735 goto done;
736 }
737
738 if ((status = dladm_up_datalink_id(handle, linkid)) !=
739 DLADM_STATUS_OK) {
740 laioc_delete_t ioc;
741
742 ioc.ld_linkid = linkid;
743 (void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc);
744 }
745 done:
746 free(attr.lg_ports);
747 free(ports);
748
749 *statusp = status;
750 return (DLADM_WALK_CONTINUE);
751 }
752
753 /*
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.
756 */
757 dladm_status_t
dladm_aggr_up(dladm_handle_t handle,datalink_id_t linkid)758 dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid)
759 {
760 dladm_status_t status;
761
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,
765 DLADM_OPT_PERSIST);
766 return (DLADM_STATUS_OK);
767 } else {
768 (void) i_dladm_aggr_up(handle, linkid, &status);
769 return (status);
770 }
771 }
772
773 /*
774 * Given a policy string, return a policy mask. Returns B_TRUE on
775 * success, or B_FALSE if an error occurred during parsing.
776 */
777 boolean_t
dladm_aggr_str2policy(const char * str,uint32_t * policy)778 dladm_aggr_str2policy(const char *str, uint32_t *policy)
779 {
780 uint_t i;
781 policy_t *pol;
782 char *token = NULL;
783 char *lasts;
784
785 *policy = 0;
786
787 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
788 &lasts)) != NULL) {
789 for (i = 0; i < NPOLICIES; i++) {
790 pol = &policies[i];
791 if (strcasecmp(token, pol->pol_name) == 0) {
792 *policy |= pol->policy;
793 break;
794 }
795 }
796 if (i == NPOLICIES)
797 return (B_FALSE);
798 }
799
800 return (B_TRUE);
801 }
802
803 /*
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.
807 */
808 char *
dladm_aggr_policy2str(uint32_t policy,char * str)809 dladm_aggr_policy2str(uint32_t policy, char *str)
810 {
811 uint_t i, npolicies = 0;
812 policy_t *pol;
813
814 if (str == NULL)
815 return (NULL);
816
817 str[0] = '\0';
818
819 for (i = 0; i < NPOLICIES; i++) {
820 pol = &policies[i];
821 if ((policy & pol->policy) != 0) {
822 npolicies++;
823 if (npolicies > 1)
824 (void) strlcat(str, ",", DLADM_STRSIZE);
825 (void) strlcat(str, pol->pol_name, DLADM_STRSIZE);
826 }
827 }
828
829 return (str);
830 }
831
832 /*
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.
837 */
838 boolean_t
dladm_aggr_str2macaddr(const char * str,boolean_t * mac_fixed,uchar_t * mac_addr)839 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
840 {
841 uchar_t *conv_str;
842 int mac_len;
843
844 *mac_fixed = (strcmp(str, "auto") != 0);
845 if (!*mac_fixed) {
846 bzero(mac_addr, ETHERADDRL);
847 return (B_TRUE);
848 }
849
850 conv_str = _link_aton(str, &mac_len);
851 if (conv_str == NULL)
852 return (B_FALSE);
853
854 if (mac_len != ETHERADDRL) {
855 free(conv_str);
856 return (B_FALSE);
857 }
858
859 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
860 (conv_str[0] & 0x01)) {
861 free(conv_str);
862 return (B_FALSE);
863 }
864
865 bcopy(conv_str, mac_addr, ETHERADDRL);
866 free(conv_str);
867
868 return (B_TRUE);
869 }
870
871 /*
872 * Returns a string containing a printable representation of a MAC address.
873 */
874 const char *
dladm_aggr_macaddr2str(const unsigned char * mac,char * buf)875 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf)
876 {
877 static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
878
879 if (buf == NULL)
880 return (NULL);
881
882 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
883 (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
884 else
885 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
886
887 return (buf);
888 }
889
890 /*
891 * Given a LACP mode string, find the corresponding LACP mode number. Returns
892 * B_TRUE if a match was found, B_FALSE otherwise.
893 */
894 boolean_t
dladm_aggr_str2lacpmode(const char * str,aggr_lacp_mode_t * lacp_mode)895 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
896 {
897 uint_t i;
898 dladm_aggr_lacpmode_t *mode;
899
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;
905 return (B_TRUE);
906 }
907 }
908
909 return (B_FALSE);
910 }
911
912 /*
913 * Given a LACP mode number, returns a printable string, or NULL if the
914 * LACP mode number is invalid.
915 */
916 const char *
dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id,char * buf)917 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
918 {
919 uint_t i;
920 dladm_aggr_lacpmode_t *mode;
921
922 if (buf == NULL)
923 return (NULL);
924
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",
929 mode->mode_str);
930 return (buf);
931 }
932 }
933
934 (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
935 return (buf);
936 }
937
938 /*
939 * Given a LACP timer string, find the corresponding LACP timer number. Returns
940 * B_TRUE if a match was found, B_FALSE otherwise.
941 */
942 boolean_t
dladm_aggr_str2lacptimer(const char * str,aggr_lacp_timer_t * lacp_timer)943 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
944 {
945 uint_t i;
946 dladm_aggr_lacptimer_t *timer;
947
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;
953 return (B_TRUE);
954 }
955 }
956
957 return (B_FALSE);
958 }
959
960 /*
961 * Given a LACP timer, returns a printable string, or NULL if the
962 * LACP timer number is invalid.
963 */
964 const char *
dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id,char * buf)965 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
966 {
967 uint_t i;
968 dladm_aggr_lacptimer_t *timer;
969
970 if (buf == NULL)
971 return (NULL);
972
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",
977 timer->lt_str);
978 return (buf);
979 }
980 }
981
982 (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
983 return (buf);
984 }
985
986 const char *
dladm_aggr_portstate2str(aggr_port_state_t state_id,char * buf)987 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
988 {
989 uint_t i;
990 dladm_aggr_port_state_t *state;
991
992 if (buf == NULL)
993 return (NULL);
994
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",
999 state->state_str);
1000 return (buf);
1001 }
1002 }
1003
1004 (void) strlcpy(buf, "unknown", DLADM_STRSIZE);
1005 return (buf);
1006 }
1007
1008 static dladm_status_t
dladm_aggr_persist_aggr_conf(dladm_handle_t handle,const char * link,datalink_id_t linkid,uint16_t key,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t policy,boolean_t mac_addr_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer,boolean_t force)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)
1014 {
1015 dladm_conf_t conf;
1016 char *portstr = NULL;
1017 char macstr[ETHERADDRL * 3];
1018 dladm_status_t status;
1019 uint_t i, size;
1020 uint64_t u64;
1021
1022 if ((status = dladm_create_conf(handle, link, linkid,
1023 DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) {
1024 return (status);
1025 }
1026
1027 u64 = key;
1028 status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64,
1029 &u64);
1030 if (status != DLADM_STATUS_OK)
1031 goto done;
1032
1033 u64 = nports;
1034 status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64,
1035 &u64);
1036 if (status != DLADM_STATUS_OK)
1037 goto done;
1038
1039 size = nports * MAXLINKNAMELEN + 1;
1040 if ((portstr = calloc(1, size)) == NULL) {
1041 status = DLADM_STATUS_NOMEM;
1042 goto done;
1043 }
1044
1045 for (i = 0; i < nports; i++) {
1046 status = write_port(handle, portstr, ports[i].lp_linkid, size);
1047 if (status != DLADM_STATUS_OK) {
1048 free(portstr);
1049 goto done;
1050 }
1051 }
1052 status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR,
1053 portstr);
1054 free(portstr);
1055
1056 if (status != DLADM_STATUS_OK)
1057 goto done;
1058
1059 u64 = policy;
1060 status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64,
1061 &u64);
1062 if (status != DLADM_STATUS_OK)
1063 goto done;
1064
1065 status = dladm_set_conf_field(handle, conf, FFIXMACADDR,
1066 DLADM_TYPE_BOOLEAN, &mac_addr_fixed);
1067 if (status != DLADM_STATUS_OK)
1068 goto done;
1069
1070 if (mac_addr_fixed) {
1071 if (!VALID_PORT_MAC(mac_addr)) {
1072 status = DLADM_STATUS_MACADDRINVAL;
1073 goto done;
1074 }
1075
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)
1080 goto done;
1081 }
1082
1083 status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN,
1084 &force);
1085 if (status != DLADM_STATUS_OK)
1086 goto done;
1087
1088 u64 = lacp_mode;
1089 status = dladm_set_conf_field(handle, conf, FLACPMODE,
1090 DLADM_TYPE_UINT64, &u64);
1091 if (status != DLADM_STATUS_OK)
1092 goto done;
1093
1094 u64 = lacp_timer;
1095 status = dladm_set_conf_field(handle, conf, FLACPTIMER,
1096 DLADM_TYPE_UINT64, &u64);
1097 if (status != DLADM_STATUS_OK)
1098 goto done;
1099
1100 /*
1101 * Commit the link aggregation configuration.
1102 */
1103 status = dladm_write_conf(handle, conf);
1104
1105 done:
1106 dladm_destroy_conf(handle, conf);
1107 return (status);
1108 }
1109
1110 /*
1111 * Create a new link aggregation group. Update the configuration
1112 * file and bring it up.
1113 */
1114 dladm_status_t
dladm_aggr_create(dladm_handle_t handle,const char * name,uint16_t key,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t policy,boolean_t mac_addr_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer,uint32_t flags)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)
1119 {
1120 datalink_id_t linkid = DATALINK_INVALID_LINKID;
1121 uint32_t media;
1122 uint32_t i;
1123 datalink_class_t class;
1124 dladm_status_t status;
1125 boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE;
1126
1127 if (key != 0 && key > AGGR_MAX_KEY)
1128 return (DLADM_STATUS_KEYINVAL);
1129
1130 if (nports == 0)
1131 return (DLADM_STATUS_BADARG);
1132
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);
1139 }
1140 }
1141
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)) !=
1145 DLADM_STATUS_OK) {
1146 goto fail;
1147 }
1148
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) {
1153 goto fail;
1154 }
1155
1156 if (!(flags & DLADM_OPT_ACTIVE))
1157 return (DLADM_STATUS_OK);
1158
1159 status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports,
1160 policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force);
1161
1162 if (status != DLADM_STATUS_OK) {
1163 if (flags & DLADM_OPT_PERSIST)
1164 (void) dladm_remove_conf(handle, linkid);
1165 goto fail;
1166 }
1167
1168 return (DLADM_STATUS_OK);
1169
1170 fail:
1171 if (linkid != DATALINK_INVALID_LINKID)
1172 (void) dladm_destroy_datalink_id(handle, linkid, flags);
1173
1174 return (status);
1175 }
1176
1177 static dladm_status_t
i_dladm_aggr_get_aggr_attr(dladm_handle_t handle,dladm_conf_t conf,uint32_t mask,dladm_aggr_modify_attr_t * attrp)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)
1180 {
1181 dladm_status_t status = DLADM_STATUS_OK;
1182 char macstr[ETHERADDRL * 3];
1183 uint64_t u64;
1184
1185 if (mask & DLADM_AGGR_MODIFY_POLICY) {
1186 status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
1187 sizeof (u64));
1188 if (status != DLADM_STATUS_OK)
1189 return (status);
1190 attrp->ld_policy = (uint32_t)u64;
1191 }
1192
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)
1197 return (status);
1198
1199 if (attrp->ld_mac_fixed) {
1200 boolean_t fixed;
1201
1202 status = dladm_get_conf_field(handle, conf, FMACADDR,
1203 macstr, sizeof (macstr));
1204 if (status != DLADM_STATUS_OK)
1205 return (status);
1206
1207 if (!dladm_aggr_str2macaddr(macstr, &fixed,
1208 attrp->ld_mac)) {
1209 return (DLADM_STATUS_REPOSITORYINVAL);
1210 }
1211 }
1212 }
1213
1214 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1215 status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
1216 sizeof (u64));
1217 if (status != DLADM_STATUS_OK)
1218 return (status);
1219 attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64;
1220 }
1221
1222 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1223 status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
1224 sizeof (u64));
1225 if (status != DLADM_STATUS_OK)
1226 return (status);
1227 attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64;
1228 }
1229
1230 return (status);
1231 }
1232
1233 static dladm_status_t
i_dladm_aggr_set_aggr_attr(dladm_handle_t handle,dladm_conf_t conf,uint32_t mask,dladm_aggr_modify_attr_t * attrp)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)
1236 {
1237 dladm_status_t status = DLADM_STATUS_OK;
1238 char macstr[ETHERADDRL * 3];
1239 uint64_t u64;
1240
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)
1246 return (status);
1247 }
1248
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)
1253 return (status);
1254
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)
1260 return (status);
1261 }
1262 }
1263
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)
1269 return (status);
1270 }
1271
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)
1277 return (status);
1278 }
1279
1280 return (status);
1281 }
1282
1283 /*
1284 * Modify the parameters of an existing link aggregation group. Update
1285 * the configuration file and pass the changes to the kernel.
1286 */
1287 dladm_status_t
dladm_aggr_modify(dladm_handle_t handle,datalink_id_t linkid,uint32_t modify_mask,uint32_t policy,boolean_t mac_fixed,const uchar_t * mac_addr,aggr_lacp_mode_t lacp_mode,aggr_lacp_timer_t lacp_timer,uint32_t flags)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)
1292 {
1293 dladm_aggr_modify_attr_t new_attr, old_attr;
1294 dladm_conf_t conf;
1295 dladm_status_t status;
1296
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);
1302
1303 if (flags & DLADM_OPT_PERSIST) {
1304 status = dladm_open_conf(handle, linkid, &conf);
1305 if (status != DLADM_STATUS_OK)
1306 return (status);
1307
1308 if ((status = i_dladm_aggr_get_aggr_attr(handle, conf,
1309 modify_mask, &old_attr)) != DLADM_STATUS_OK) {
1310 goto done;
1311 }
1312
1313 if ((status = i_dladm_aggr_set_aggr_attr(handle, conf,
1314 modify_mask, &new_attr)) != DLADM_STATUS_OK) {
1315 goto done;
1316 }
1317
1318 status = dladm_write_conf(handle, conf);
1319
1320 done:
1321 dladm_destroy_conf(handle, conf);
1322 if (status != DLADM_STATUS_OK)
1323 return (status);
1324 }
1325
1326 if (!(flags & DLADM_OPT_ACTIVE))
1327 return (DLADM_STATUS_OK);
1328
1329 status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask,
1330 &new_attr);
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);
1336 }
1337 dladm_destroy_conf(handle, conf);
1338 }
1339 }
1340
1341 return (status);
1342 }
1343
1344 typedef struct aggr_held_arg_s {
1345 datalink_id_t aggrid;
1346 boolean_t isheld;
1347 } aggr_held_arg_t;
1348
1349 static int
i_dladm_aggr_is_held(dladm_handle_t handle,datalink_id_t linkid,void * arg)1350 i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1351 {
1352 aggr_held_arg_t *aggr_held_arg = arg;
1353 dladm_vlan_attr_t dva;
1354
1355 if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) !=
1356 DLADM_STATUS_OK)
1357 return (DLADM_WALK_CONTINUE);
1358
1359 if (dva.dv_linkid == aggr_held_arg->aggrid) {
1360 /*
1361 * This VLAN is created over the given aggregation.
1362 */
1363 aggr_held_arg->isheld = B_TRUE;
1364 return (DLADM_WALK_TERMINATE);
1365 }
1366 return (DLADM_WALK_CONTINUE);
1367 }
1368
1369 /*
1370 * Delete a previously created link aggregation group. Either the name "aggr"
1371 * or the "key" is specified.
1372 */
1373 dladm_status_t
dladm_aggr_delete(dladm_handle_t handle,datalink_id_t linkid,uint32_t flags)1374 dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags)
1375 {
1376 laioc_delete_t ioc;
1377 datalink_class_t class;
1378 dladm_status_t status;
1379
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);
1383 }
1384
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);
1390 return (status);
1391 }
1392
1393 /*
1394 * Delete ACTIVE linkprop first.
1395 */
1396 (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
1397 DLADM_OPT_ACTIVE);
1398 (void) dladm_destroy_datalink_id(handle, linkid,
1399 DLADM_OPT_ACTIVE);
1400 }
1401
1402 /*
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.
1407 */
1408 if (flags & DLADM_OPT_PERSIST) {
1409 aggr_held_arg_t arg;
1410
1411 arg.aggrid = linkid;
1412 arg.isheld = B_FALSE;
1413
1414 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle,
1415 &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
1416 DLADM_OPT_PERSIST);
1417 if (arg.isheld)
1418 return (DLADM_STATUS_LINKBUSY);
1419
1420 (void) dladm_remove_conf(handle, linkid);
1421 (void) dladm_destroy_datalink_id(handle, linkid,
1422 DLADM_OPT_PERSIST);
1423 }
1424
1425 return (DLADM_STATUS_OK);
1426 }
1427
1428 /*
1429 * Add one or more ports to an existing link aggregation.
1430 */
1431 dladm_status_t
dladm_aggr_add(dladm_handle_t handle,datalink_id_t linkid,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t flags)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)
1434 {
1435 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
1436 LAIOC_ADD));
1437 }
1438
1439 /*
1440 * Remove one or more ports from an existing link aggregation.
1441 */
1442 dladm_status_t
dladm_aggr_remove(dladm_handle_t handle,datalink_id_t linkid,uint32_t nports,dladm_aggr_port_attr_db_t * ports,uint32_t flags)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)
1445 {
1446 return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
1447 LAIOC_REMOVE));
1448 }
1449
1450 typedef struct i_walk_key_state_s {
1451 uint16_t key;
1452 datalink_id_t linkid;
1453 boolean_t found;
1454 } i_walk_key_state_t;
1455
1456 static int
i_dladm_walk_key2linkid(dladm_handle_t handle,datalink_id_t linkid,void * arg)1457 i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1458 {
1459 dladm_conf_t conf;
1460 uint16_t key;
1461 dladm_status_t status;
1462 i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
1463 uint64_t u64;
1464
1465 if (dladm_getsnap_conf(handle, linkid, &conf) != DLADM_STATUS_OK)
1466 return (DLADM_WALK_CONTINUE);
1467
1468 status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
1469 key = (uint16_t)u64;
1470 dladm_destroy_conf(handle, conf);
1471
1472 if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
1473 statep->found = B_TRUE;
1474 statep->linkid = linkid;
1475 return (DLADM_WALK_TERMINATE);
1476 }
1477
1478 return (DLADM_WALK_CONTINUE);
1479 }
1480
1481 dladm_status_t
dladm_key2linkid(dladm_handle_t handle,uint16_t key,datalink_id_t * linkidp,uint32_t flags)1482 dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp,
1483 uint32_t flags)
1484 {
1485 i_walk_key_state_t state;
1486
1487 if (key > AGGR_MAX_KEY)
1488 return (DLADM_STATUS_NOTFOUND);
1489
1490 state.found = B_FALSE;
1491 state.key = key;
1492
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);
1498 } else {
1499 return (DLADM_STATUS_NOTFOUND);
1500 }
1501 }
1502