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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
27 * bridged - bridging control daemon. This module handles events and general
28 * port-related operations.
36 #include <sys/types.h>
40 #include <libdllink.h>
41 #include <libdlbridge.h>
42 #include <libdlvlan.h>
43 #include <libdlstat.h>
45 #include <stp_vectors.h>
46 #include <net/if_types.h>
47 #include <net/bridge.h>
48 #include <sys/ethernet.h>
52 int refresh_count
= 1; /* never zero */
53 dladm_bridge_prot_t protect
= DLADM_BRIDGE_PROT_STP
;
56 * The 'allports' array is an array of pointers to the struct portdata
57 * structures. We reallocate 'allports' as needed, but the portdata must
58 * remain where it's initially allocated, because libdlpi's notification
59 * mechanism has a copy of a pointer to this structure.
62 struct portdata
**allports
;
64 /* Port allocation increment (arbitrary) */
66 static uint_t numports
;
68 static datalink_id_t main_linkid
;
75 (void) dladm_destroy_datalink_id(dlhandle
, main_linkid
,
80 open_bridge_control(void)
82 bridge_newbridge_t bnb
;
83 dladm_status_t status
;
84 char buf
[DLADM_STRSIZE
];
86 if ((control_fd
= open(BRIDGE_CTLPATH
, O_RDWR
| O_NONBLOCK
)) == -1) {
87 perror(BRIDGE_CTLPATH
);
90 (void) snprintf(bnb
.bnb_name
, sizeof (bnb
.bnb_name
), "%s0",
92 status
= dladm_name2info(dlhandle
, bnb
.bnb_name
, &bnb
.bnb_linkid
, NULL
,
94 if (status
!= DLADM_STATUS_OK
) {
95 (void) fprintf(stderr
, "bridged: %s: %s\n", bnb
.bnb_name
,
96 dladm_status2str(status
, buf
));
99 if (strioctl(control_fd
, BRIOC_NEWBRIDGE
, &bnb
, sizeof (bnb
)) == -1) {
103 main_linkid
= bnb
.bnb_linkid
;
104 if (strioctl(control_fd
, BRIOC_TABLEMAX
, &tablemax
,
105 sizeof (tablemax
)) == -1) {
106 syslog(LOG_ERR
, "cannot set table max %lu on bridge %s: %m",
107 tablemax
, instance_name
);
111 * This covers for any previous incarnation where we might have crashed
112 * or been SIGKILL'd and failed to take down the datalink.
115 (void) atexit(linkdown
);
116 status
= dladm_up_datalink_id(dlhandle
, bnb
.bnb_linkid
);
117 if (status
!= DLADM_STATUS_OK
) {
118 (void) fprintf(stderr
, "bridged: %s link up: %s\n",
119 bnb
.bnb_name
, dladm_status2str(status
, buf
));
125 find_by_linkid(datalink_id_t linkid
)
128 struct portdata
*port
;
130 for (i
= 0; i
< nextport
; i
++) {
132 if (port
->linkid
== linkid
)
140 set_vlan(dladm_handle_t handle
, datalink_id_t linkid
, void *arg
)
142 struct portdata
*port
;
143 dladm_status_t status
;
144 dladm_vlan_attr_t vinfo
;
145 char pointless
[DLADM_STRSIZE
];
146 bridge_vlanenab_t bve
;
148 status
= dladm_vlan_info(handle
, linkid
, &vinfo
, DLADM_OPT_ACTIVE
);
149 if (status
!= DLADM_STATUS_OK
) {
150 syslog(LOG_DEBUG
, "can't get VLAN info on link ID %u: %s",
151 linkid
, dladm_status2str(status
, pointless
));
152 return (DLADM_WALK_CONTINUE
);
155 port
= find_by_linkid(vinfo
.dv_linkid
);
156 if (port
== NULL
|| !port
->kern_added
)
157 return (DLADM_WALK_CONTINUE
);
159 bve
.bve_linkid
= port
->linkid
;
160 bve
.bve_vlan
= vinfo
.dv_vid
;
161 bve
.bve_onoff
= B_TRUE
;
162 if (strioctl(control_fd
, BRIOC_VLANENAB
, &bve
, sizeof (bve
)) == -1) {
163 syslog(LOG_ERR
, "unable to enable VLAN %d on linkid %u: %m",
164 vinfo
.dv_vid
, port
->linkid
);
165 return (DLADM_WALK_TERMINATE
);
167 return (DLADM_WALK_CONTINUE
);
172 * If the named port already exists, then update its configuration. If it
173 * doesn't, then create and enable it.
176 update_port(int vlan_id
, const char *portname
, datalink_id_t linkid
,
177 datalink_class_t
class)
180 struct portdata
*port
;
184 datalink_id_t linkid
;
185 char linkname
[MAXLINKNAMELEN
];
187 bridge_setpvid_t bsv
;
188 uint_t propval
, valcnt
;
189 dladm_status_t status
;
191 for (posn
= 0; posn
< nextport
; posn
++) {
192 if (allports
[posn
]->linkid
== linkid
)
196 /* If we need to allocate more array space, then do so in chunks. */
197 if (posn
>= numports
) {
198 struct portdata
**newarr
;
200 newarr
= reallocarray(allports
, (nextport
+ ALLOCINCR
),
204 fds
= reallocarray(fdarray
, nextport
+ ALLOCINCR
+ FDOFFSET
,
208 if (newarr
== NULL
|| fds
== NULL
) {
209 syslog(LOG_ERR
, "unable to add %s; no memory",
213 numports
= nextport
+ ALLOCINCR
;
216 port_index
= posn
+ 1;
217 fds
= fdarray
+ posn
+ FDOFFSET
;
219 /* If our linkid search ran to the end, then this is a new port. */
220 if (posn
== nextport
) {
221 if ((port
= calloc(1, sizeof (*port
))) == NULL
) {
222 syslog(LOG_ERR
, "unable to add %s; no memory",
226 allports
[posn
] = port
;
227 port
->vlan_id
= vlan_id
;
228 port
->linkid
= linkid
;
229 port
->port_index
= port_index
;
230 port
->phys_status
= B_TRUE
;
231 port
->admin_status
= B_TRUE
;
232 port
->state
= BLS_BLOCKLISTEN
;
235 /* Located port by linkid; we're just updating existing data */
236 port
= allports
[posn
];
239 * If it changed name, then close and reopen so we log under
240 * the most current name for this port.
242 if (port
->name
!= NULL
&& strcmp(portname
, port
->name
) != 0) {
243 if (port
->dlpi
!= NULL
)
244 dlpi_close(port
->dlpi
);
253 * If the port is not yet attached to the bridge in the kernel, then do
256 if (!port
->kern_added
) {
257 adddata
.linkid
= linkid
;
258 (void) strlcpy(adddata
.linkname
, portname
,
259 sizeof (adddata
.linkname
));
260 if (strioctl(control_fd
, BRIOC_ADDLINK
, &adddata
,
261 sizeof (adddata
.linkid
) + strlen(adddata
.linkname
)) == -1) {
262 syslog(LOG_ERR
, "cannot bridge %s: %m", portname
);
265 port
->kern_added
= B_TRUE
;
268 port
->referenced
= B_TRUE
;
271 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
272 DLADM_PROP_VAL_PERSISTENT
, "forward", &propval
, &valcnt
);
273 if (status
== DLADM_STATUS_OK
)
274 port
->admin_status
= propval
;
277 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
278 DLADM_PROP_VAL_PERSISTENT
, "default_tag", &propval
, &valcnt
);
279 if (status
== DLADM_STATUS_OK
)
280 bsv
.bsv_vlan
= propval
;
282 bsv
.bsv_linkid
= linkid
;
283 if (strioctl(control_fd
, BRIOC_SETPVID
, &bsv
, sizeof (bsv
)) == -1) {
284 syslog(LOG_ERR
, "can't set PVID on %s: %m", portname
);
288 if (port
->dlpi
== NULL
) {
289 if (!port_dlpi_open(portname
, port
, class))
291 fds
->fd
= dlpi_fd(port
->dlpi
);
292 fds
->events
= POLLIN
;
295 if (rstp_add_port(port
))
299 if (port
->dlpi
!= NULL
) {
300 dlpi_close(port
->dlpi
);
306 if (port
->kern_added
) {
307 if (strioctl(control_fd
, BRIOC_REMLINK
, &port
->linkid
,
308 sizeof (port
->linkid
)) == -1)
309 syslog(LOG_ERR
, "cannot remove from bridge %s: %m",
312 port
->kern_added
= B_FALSE
;
314 if (posn
+ 1 == nextport
) {
322 update_link(dladm_handle_t handle
, datalink_id_t linkid
, void *arg
)
324 dladm_status_t status
;
325 char bridge
[MAXLINKNAMELEN
], linkname
[MAXLINKNAMELEN
];
326 char pointless
[DLADM_STRSIZE
];
327 datalink_class_t
class;
329 status
= dladm_bridge_getlink(handle
, linkid
, bridge
, sizeof (bridge
));
330 if (status
== DLADM_STATUS_OK
&& strcmp(bridge
, instance_name
) == 0) {
331 status
= dladm_datalink_id2info(handle
, linkid
, NULL
, &class,
332 NULL
, linkname
, sizeof (linkname
));
333 if (status
== DLADM_STATUS_OK
) {
334 update_port(0, linkname
, linkid
, class);
336 syslog(LOG_ERR
, "unable to get link info for ID %u: %s",
337 linkid
, dladm_status2str(status
, pointless
));
339 } else if (debugging
) {
340 if (status
!= DLADM_STATUS_OK
)
342 "unable to get bridge data for ID %u: %s",
343 linkid
, dladm_status2str(status
, pointless
));
345 syslog(LOG_DEBUG
, "link ID %u is on bridge %s, not %s",
346 linkid
, bridge
, instance_name
);
348 return (DLADM_WALK_CONTINUE
);
352 * Refresh action - reread configuration properties.
355 handle_refresh(int sigfd
)
358 struct portdata
*pdp
;
361 dladm_status_t status
;
363 uint32_t new_tablemax
;
365 /* Drain signal events from pipe */
367 (void) read(sigfd
, buf
, sizeof (buf
));
369 status
= dladm_bridge_get_privprop(instance_name
, &new_debug
,
371 if (status
== DLADM_STATUS_OK
) {
372 if (debugging
&& !new_debug
)
373 syslog(LOG_DEBUG
, "disabling debugging");
374 debugging
= new_debug
;
375 if (new_tablemax
!= tablemax
) {
376 syslog(LOG_DEBUG
, "changed tablemax from %lu to %lu",
377 tablemax
, new_tablemax
);
378 if (strioctl(control_fd
, BRIOC_TABLEMAX
, &new_tablemax
,
379 sizeof (tablemax
)) == -1)
380 syslog(LOG_ERR
, "cannot set table max "
381 "%lu on bridge %s: %m", tablemax
,
384 tablemax
= new_tablemax
;
387 syslog(LOG_ERR
, "%s: unable to refresh bridge properties: %s",
388 instance_name
, dladm_status2str(status
, buf
));
393 for (i
= 0; i
< nextport
; i
++)
394 allports
[i
]->referenced
= B_FALSE
;
397 * libdladm doesn't guarantee anything about link ordering in a walk,
398 * so we do this walk twice: once to pick up the ports, and a second
399 * time to get the enabled VLANs on all ports.
401 (void) dladm_walk_datalink_id(update_link
, dlhandle
, NULL
,
402 DATALINK_CLASS_ALL
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_ACTIVE
);
404 (void) dladm_walk_datalink_id(set_vlan
, dlhandle
, NULL
,
405 DATALINK_CLASS_VLAN
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_ACTIVE
);
408 * If any ports now show up as unreferenced, then they've been removed
409 * from the configuration.
411 for (i
= 0; i
< nextport
; i
++) {
413 fdp
= fdarray
+ i
+ FDOFFSET
;
414 if (!pdp
->referenced
) {
415 if (pdp
->stp_added
) {
416 (void) STP_IN_port_remove(pdp
->vlan_id
,
418 pdp
->stp_added
= B_FALSE
;
420 if (pdp
->dlpi
!= NULL
) {
421 dlpi_close(pdp
->dlpi
);
427 if (pdp
->kern_added
) {
428 if (strioctl(control_fd
, BRIOC_REMLINK
,
429 &pdp
->linkid
, sizeof (pdp
->linkid
)) == -1)
430 syslog(LOG_ERR
, "cannot remove linkid "
431 "%u from bridge %s: %m",
432 pdp
->linkid
, instance_name
);
433 pdp
->kern_added
= B_FALSE
;
438 if (++refresh_count
== 0)
443 * Handle messages on the common control stream. This currently just deals
444 * with port SDU mismatches.
451 struct portdata
*port
;
454 retv
= read(control_fd
, &bc
, sizeof (bc
));
455 if (retv
!= sizeof (bc
))
457 if ((port
= find_by_linkid(bc
.bc_linkid
)) == NULL
)
459 if (port
->sdu_failed
== bc
.bc_failed
)
461 port
->sdu_failed
= bc
.bc_failed
;
462 if (!port
->phys_status
|| !port
->admin_status
||
463 protect
!= DLADM_BRIDGE_PROT_STP
)
465 if (port
->admin_non_stp
) {
466 bridge_setstate_t bss
;
468 bss
.bss_linkid
= port
->linkid
;
469 bss
.bss_state
= !port
->sdu_failed
&& !port
->bpdu_protect
?
470 BLS_FORWARDING
: BLS_BLOCKLISTEN
;
471 if (strioctl(control_fd
, BRIOC_SETSTATE
, &bss
,
472 sizeof (bss
)) == -1) {
473 syslog(LOG_ERR
, "cannot set STP state on %s: %m",
477 if ((rc
= STP_IN_enable_port(port
->port_index
, !bc
.bc_failed
)) != 0)
478 syslog(LOG_ERR
, "STP can't %s port %s for SDU failure: %s",
479 port
->name
, bc
.bc_failed
? "disable" : "enable",
480 STP_IN_get_error_explanation(rc
));
484 receive_packet(struct portdata
*port
)
488 uint16_t buffer
[ETHERMAX
/ sizeof (uint16_t)];
489 struct ether_header
*eh
;
490 char sender
[ETHERADDRL
* 3];
492 buflen
= sizeof (buffer
);
493 rc
= dlpi_recv(port
->dlpi
, NULL
, NULL
, buffer
, &buflen
, 1, NULL
);
494 if (rc
!= DLPI_SUCCESS
) {
495 if (rc
!= DLPI_ETIMEDOUT
)
496 syslog(LOG_ERR
, "receive failure on %s: %s", port
->name
,
502 * If we're administratively disabled, then don't deliver packets to
503 * the STP state machine. It will re-enable the port because it uses
504 * the same variable for both link status and administrative state.
506 if (!port
->admin_status
|| protect
!= DLADM_BRIDGE_PROT_STP
) {
509 "discard BPDU on non-forwarding interface %s",
515 * There's a mismatch between the librstp and libdlpi expectations on
516 * receive. librstp wants the packet to start with the 802 length
517 * field, not the destination address.
519 eh
= (struct ether_header
*)buffer
;
520 rc
= STP_IN_check_bpdu_header((BPDU_T
*)&eh
->ether_type
, buflen
);
523 * Note that we attempt to avoid calling the relatively expensive
524 * _link_ntoa function unless we're going to use the result. In normal
525 * usage, we don't need this string.
528 if (port
->admin_non_stp
&& !port
->bpdu_protect
) {
529 bridge_setstate_t bss
;
531 (void) _link_ntoa(eh
->ether_shost
.ether_addr_octet
,
532 sender
, ETHERADDRL
, IFT_OTHER
);
533 syslog(LOG_WARNING
, "unexpected BPDU on %s from %s; "
534 "forwarding disabled", port
->name
, sender
);
535 port
->bpdu_protect
= B_TRUE
;
536 bss
.bss_linkid
= port
->linkid
;
537 bss
.bss_state
= BLS_BLOCKLISTEN
;
538 if (strioctl(control_fd
, BRIOC_SETSTATE
, &bss
,
539 sizeof (bss
)) == -1) {
540 syslog(LOG_ERR
, "cannot set STP state on "
541 "%s: %m", port
->name
);
546 (void) _link_ntoa(eh
->ether_shost
.ether_addr_octet
,
547 sender
, ETHERADDRL
, IFT_OTHER
);
548 syslog(LOG_DEBUG
, "got BPDU from %s on %s; %d bytes",
549 sender
, port
->name
, buflen
);
551 rc
= STP_IN_rx_bpdu(port
->vlan_id
, port
->port_index
,
552 (BPDU_T
*)&eh
->ether_type
, buflen
);
555 (void) _link_ntoa(eh
->ether_shost
.ether_addr_octet
, sender
,
556 ETHERADDRL
, IFT_OTHER
);
558 "discarded malformed packet on %s from %s: %s",
559 port
->name
, sender
, STP_IN_get_error_explanation(rc
));
564 get_dladm_speed(struct portdata
*port
)
566 dladm_status_t status
;
569 status
= dladm_get_single_mac_stat(dlhandle
, port
->linkid
, "ifspeed",
570 KSTAT_DATA_UINT64
, &ifspeed
);
571 if (status
== DLADM_STATUS_OK
&& ifspeed
!= 0)
572 port
->speed
= ifspeed
/ 1000000;
578 enable_forwarding(struct portdata
*port
)
580 bridge_setstate_t bss
;
582 bss
.bss_linkid
= port
->linkid
;
583 bss
.bss_state
= BLS_FORWARDING
;
584 if (strioctl(control_fd
, BRIOC_SETSTATE
, &bss
, sizeof (bss
)) == -1)
585 syslog(LOG_ERR
, "cannot set STP state on %s: %m", port
->name
);
592 hrtime_t last_time
, now
;
595 if (lock_engine() != 0) {
596 syslog(LOG_ERR
, "mutex lock");
600 /* Bootstrap configuration */
603 last_time
= gethrtime();
604 while (!shutting_down
) {
606 if (now
- last_time
>= 1000000000ll) {
607 (void) STP_IN_one_second();
611 tout
= 1000 - (now
- last_time
) / 1000000ll;
614 (void) poll(fdarray
, nextport
+ FDOFFSET
, tout
);
615 if (lock_engine() != 0) {
616 syslog(LOG_ERR
, "mutex lock");
619 if (fdarray
[0].revents
& POLLIN
)
620 handle_refresh(fdarray
[0].fd
);
621 if (fdarray
[1].revents
& POLLIN
)
623 for (i
= 0; i
< nextport
; i
++) {
624 if (fdarray
[i
+ FDOFFSET
].revents
& POLLIN
)
625 receive_packet(allports
[i
]);