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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * bridged - bridging control daemon. This module provides functions related
29 * to the librstp (Rapid Spanning Tree Protocol) library.
37 #include <sys/types.h>
42 #include <libdllink.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 /* current engine configuration; access protected by engine_lock */
53 static UID_STP_CFG_T uid_cfg
;
56 * Our implementation doesn't have per-VLAN forwarding entries, so we just
57 * flush by the port. If port number is zero, then flush entries.
61 flush_lt(int port_index
, int vlan_id
, LT_FLASH_TYPE_T type
, char *reason
)
65 bridge_flushfwd_t bff
;
67 if (port_index
> nextport
|| port_index
< 0)
70 if (port_index
== 0) {
71 type
= LT_FLASH_ONLY_THE_PORT
;
73 bff
.bff_linkid
= DATALINK_INVALID_LINKID
;
75 pd
= allports
[port_index
- 1];
77 bff
.bff_linkid
= pd
->linkid
;
81 syslog(LOG_DEBUG
, "flush forwarding %s %s: %s",
82 type
== LT_FLASH_ONLY_THE_PORT
? "to" : "except for",
86 bff
.bff_exclude
= (type
== LT_FLASH_ALL_PORTS_EXCLUDE_THIS
);
89 * If flushing fails, we can't return. The only safe thing to do is to
90 * tear down the bridge so that we're not harming the network.
92 if (strioctl(control_fd
, BRIOC_FLUSHFWD
, &bff
, sizeof (bff
)) == -1) {
93 syslog(LOG_ERR
, "cannot flush forwarding entries on %s %s: %m",
94 instance_name
, portname
);
103 get_port_mac(int port_index
, unsigned char *mac
)
107 if (port_index
> nextport
|| port_index
<= 0)
110 pd
= allports
[port_index
- 1];
111 (void) memcpy(mac
, pd
->mac_addr
, ETHERADDRL
);
114 /* Returns speed in megabits per second */
116 get_port_oper_speed(unsigned int port_index
)
118 if (port_index
> nextport
|| port_index
== 0)
121 return (allports
[port_index
- 1]->speed
);
125 get_port_link_status(int port_index
)
129 if (port_index
> nextport
|| port_index
<= 0) {
132 pd
= allports
[port_index
- 1];
133 return (pd
->phys_status
&& pd
->admin_status
&&
134 protect
== DLADM_BRIDGE_PROT_STP
&& !pd
->sdu_failed
?
140 get_duplex(int port_index
)
143 link_duplex_t link_duplex
;
144 dladm_status_t status
;
146 if (port_index
> nextport
|| port_index
<= 0)
149 pd
= allports
[port_index
- 1];
150 status
= dladm_get_single_mac_stat(dlhandle
, pd
->linkid
, "link_duplex",
151 KSTAT_DATA_UINT32
, &link_duplex
);
153 if (status
== DLADM_STATUS_OK
&& link_duplex
== LINK_DUPLEX_FULL
)
160 bls_state(bridge_state_t bstate
)
166 return ("forwarding");
168 return ("block/listen");
174 set_port_state(int port_index
, int vlan_id
, RSTP_PORT_STATE state
)
177 bridge_setstate_t bss
;
179 if (port_index
> nextport
|| port_index
<= 0)
182 pd
= allports
[port_index
- 1];
185 syslog(LOG_DEBUG
, "setting port state on port %d (%s) to %d",
186 port_index
, pd
->name
, state
);
188 case UID_PORT_LEARNING
:
189 bss
.bss_state
= BLS_LEARNING
;
191 case UID_PORT_FORWARDING
:
192 bss
.bss_state
= BLS_FORWARDING
;
195 bss
.bss_state
= BLS_BLOCKLISTEN
;
198 bss
.bss_linkid
= pd
->linkid
;
199 if (strioctl(control_fd
, BRIOC_SETSTATE
, &bss
, sizeof (bss
)) == -1) {
200 syslog(LOG_ERR
, "cannot set STP state on %s from %s to %s: %m",
201 pd
->name
, bls_state(pd
->state
), bls_state(bss
.bss_state
));
203 * If we've been unsuccessful in disabling forwarding, then the
204 * only safe thing to do is to make the daemon exit, so that
205 * the kernel will be forced to destroy the bridge state and
206 * terminate all forwarding.
208 if (pd
->state
== BLS_FORWARDING
&&
209 bss
.bss_state
!= BLS_FORWARDING
) {
214 pd
->state
= bss
.bss_state
;
220 * Our hardware doesn't actually do anything different when STP is enabled or
221 * disabled, so this function does nothing. It would be possible to open and
222 * close the DLPI stream here, if such a thing were necessary.
225 set_hardware_mode(int vlan_id
, UID_STP_MODE_T mode
)
228 syslog(LOG_DEBUG
, "setting hardware mode on vlan %d to %d",
235 tx_bpdu(int port_index
, int vlan_id
, unsigned char *bpdu
, size_t bpdu_len
)
237 struct portdata
*pdp
;
240 if (port_index
> nextport
|| port_index
<= 0)
243 pdp
= allports
[port_index
- 1];
244 rc
= dlpi_send(pdp
->dlpi
, NULL
, 0, bpdu
, bpdu_len
, NULL
);
245 if (rc
== DLPI_SUCCESS
) {
247 syslog(LOG_DEBUG
, "transmitted %d byte BPDU on %s",
248 bpdu_len
, pdp
->name
);
251 syslog(LOG_WARNING
, "failed to send to %s: %s", pdp
->name
,
258 get_port_name(int port_index
)
260 if (port_index
> nextport
|| port_index
<= 0)
263 return (allports
[port_index
- 1]->name
);
268 get_init_stpm_cfg(int vlan_id
, UID_STP_CFG_T
*cfg
)
270 /* under engine_lock because it's a callback from the engine */
277 get_init_port_cfg(int vlan_id
, int port_index
, UID_STP_PORT_CFG_T
*cfg
)
279 struct portdata
*pdp
;
280 uint_t propval
, valcnt
;
281 datalink_id_t linkid
;
282 dladm_status_t status
;
284 if (port_index
> nextport
|| port_index
<= 0)
287 pdp
= allports
[port_index
- 1];
290 cfg
->port_priority
= DEF_PORT_PRIO
;
291 cfg
->admin_non_stp
= DEF_ADMIN_NON_STP
;
292 cfg
->admin_edge
= DEF_ADMIN_EDGE
;
293 cfg
->admin_port_path_cost
= ADMIN_PORT_PATH_COST_AUTO
;
294 cfg
->admin_point2point
= DEF_P2P
;
297 linkid
= pdp
->linkid
;
298 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
299 DLADM_PROP_VAL_PERSISTENT
, "stp_priority", &propval
, &valcnt
);
300 if (status
== DLADM_STATUS_OK
) {
301 cfg
->port_priority
= propval
;
302 cfg
->field_mask
|= PT_CFG_PRIO
;
304 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
305 DLADM_PROP_VAL_PERSISTENT
, "stp", &propval
, &valcnt
);
306 if (status
== DLADM_STATUS_OK
) {
307 cfg
->admin_non_stp
= !propval
;
308 cfg
->field_mask
|= PT_CFG_NON_STP
;
310 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
311 DLADM_PROP_VAL_PERSISTENT
, "stp_edge", &propval
, &valcnt
);
312 if (status
== DLADM_STATUS_OK
) {
313 cfg
->admin_edge
= propval
;
314 cfg
->field_mask
|= PT_CFG_EDGE
;
316 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
317 DLADM_PROP_VAL_PERSISTENT
, "stp_cost", &propval
, &valcnt
);
318 if (status
== DLADM_STATUS_OK
) {
319 cfg
->admin_port_path_cost
= propval
;
320 cfg
->field_mask
|= PT_CFG_COST
;
322 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
323 DLADM_PROP_VAL_PERSISTENT
, "stp_p2p", &propval
, &valcnt
);
324 if (status
== DLADM_STATUS_OK
) {
325 cfg
->admin_point2point
= propval
;
326 cfg
->field_mask
|= PT_CFG_P2P
;
330 * mcheck is special. It is actually a command, but the 802 documents
331 * define it as a variable that spontaneously resets itself. We need
332 * to handle that behavior here.
334 status
= dladm_get_linkprop_values(dlhandle
, linkid
,
335 DLADM_PROP_VAL_PERSISTENT
, "stp_mcheck", &propval
, &valcnt
);
336 if (status
== DLADM_STATUS_OK
&& propval
!= 0) {
339 cfg
->field_mask
|= PT_CFG_MCHECK
;
340 (void) dladm_set_linkprop(dlhandle
, linkid
, "stp_mcheck", &pval
,
341 1, DLADM_OPT_ACTIVE
|DLADM_OPT_PERSIST
|DLADM_OPT_NOREFRESH
);
344 pdp
->admin_non_stp
= cfg
->admin_non_stp
;
345 if (!pdp
->admin_non_stp
)
346 pdp
->bpdu_protect
= B_FALSE
;
352 trace(const char *fmt
, ...)
357 vsyslog(LOG_DEBUG
, fmt
, ap
);
361 static STP_VECTORS_T stp_vectors
= {
365 get_port_link_status
,
379 dladm_status_t status
;
380 char buf
[DLADM_STRSIZE
];
382 STP_IN_init(&stp_vectors
);
383 status
= dladm_bridge_get_properties(instance_name
, &uid_cfg
, &protect
);
384 if (status
!= DLADM_STATUS_OK
) {
385 syslog(LOG_ERR
, "%s: unable to read properties: %s",
386 instance_name
, dladm_status2str(status
, buf
));
392 * This is called by a normal refresh operation. It gets the engine properties
398 dladm_status_t status
;
400 char buf
[DLADM_STRSIZE
];
401 UID_STP_CFG_T new_cfg
;
402 dladm_bridge_prot_t new_prot
;
404 status
= dladm_bridge_get_properties(instance_name
, &new_cfg
,
406 if (status
!= DLADM_STATUS_OK
) {
407 syslog(LOG_ERR
, "%s: unable to refresh bridge properties: %s",
408 instance_name
, dladm_status2str(status
, buf
));
410 if (debugging
&& (protect
!= new_prot
||
411 uid_cfg
.stp_enabled
!= new_cfg
.stp_enabled
)) {
412 syslog(LOG_DEBUG
, "loop protection %s->%s, STP %d->%d",
413 dladm_bridge_prot2str(protect
),
414 dladm_bridge_prot2str(new_prot
),
415 uid_cfg
.stp_enabled
, new_cfg
.stp_enabled
);
419 * The engine doesn't take kindly to parameter changes while
420 * running. Disable first if we must do this.
422 if (uid_cfg
.stp_enabled
&&
423 memcmp(&uid_cfg
, &new_cfg
, sizeof (uid_cfg
)) != 0) {
424 syslog(LOG_DEBUG
, "resetting state machine");
425 uid_cfg
.stp_enabled
= STP_DISABLED
;
426 rc
= STP_IN_stpm_set_cfg(0, &uid_cfg
);
428 syslog(LOG_ERR
, "STP machine reset config: %s",
429 STP_IN_get_error_explanation(rc
));
434 rc
= STP_IN_stpm_set_cfg(0, &uid_cfg
);
436 syslog(LOG_ERR
, "STP machine set config: %s",
437 STP_IN_get_error_explanation(rc
));
442 * This is called when a port changes its MAC address. If it's the main port,
443 * the one that supplies us our bridge ID, then we must choose a new ID, and to
444 * do that we shut the bridge down and bring it back up.
447 rstp_change_mac(struct portdata
*port
, const unsigned char *newaddr
)
450 unsigned char mac
[ETHERADDRL
];
452 char curid
[ETHERADDRL
* 3];
453 char newmac
[ETHERADDRL
* 3];
455 (void) _link_ntoa(port
->mac_addr
, curid
, ETHERADDRL
, IFT_OTHER
);
456 (void) _link_ntoa(newaddr
, newmac
, ETHERADDRL
, IFT_OTHER
);
457 STP_IN_get_bridge_id(port
->vlan_id
, &prio
, mac
);
458 if (memcmp(port
->mac_addr
, mac
, ETHERADDRL
) == 0) {
459 syslog(LOG_NOTICE
, "bridge ID must change: ID %s on %s changed "
460 "to %s", curid
, port
->name
, newmac
);
461 uid_cfg
.stp_enabled
= STP_DISABLED
;
462 if ((rc
= STP_IN_stpm_set_cfg(0, &uid_cfg
)) != 0)
463 syslog(LOG_ERR
, "STP machine set config: %s",
464 STP_IN_get_error_explanation(rc
));
465 (void) memcpy(port
->mac_addr
, newaddr
, ETHERADDRL
);
466 uid_cfg
.stp_enabled
= STP_ENABLED
;
467 if ((rc
= STP_IN_stpm_set_cfg(0, &uid_cfg
)) != 0)
468 syslog(LOG_ERR
, "STP machine set config: %s",
469 STP_IN_get_error_explanation(rc
));
472 "MAC address on %s changed from %s to %s", port
->name
,
474 (void) memcpy(port
->mac_addr
, newaddr
, ETHERADDRL
);
479 rstp_add_port(struct portdata
*port
)
482 UID_STP_PORT_CFG_T portcfg
;
483 bridge_vlanenab_t bve
;
484 bridge_setstate_t bss
;
486 if (!port
->stp_added
&&
487 (rc
= STP_IN_port_add(port
->vlan_id
, port
->port_index
)) != 0) {
488 syslog(LOG_ERR
, "STP add %s %d: %s", port
->name
,
489 port
->port_index
, STP_IN_get_error_explanation(rc
));
492 port
->stp_added
= B_TRUE
;
494 /* guaranteed to succeed at this point */
495 (void) get_init_port_cfg(port
->vlan_id
, port
->port_index
, &portcfg
);
498 * Restore state when reenabling STP engine, set fixed state when
499 * disabling. For TRILL, we don't control forwarding at all, but we
500 * need to turn off our controls for TRILL to do its thing.
502 bss
.bss_linkid
= port
->linkid
;
503 if (protect
!= DLADM_BRIDGE_PROT_STP
) {
504 bss
.bss_state
= port
->state
= BLS_BLOCKLISTEN
;
505 } else if (portcfg
.admin_non_stp
) {
506 bss
.bss_state
= port
->admin_status
&& !port
->sdu_failed
&&
507 !port
->bpdu_protect
? BLS_FORWARDING
: BLS_BLOCKLISTEN
;
509 bss
.bss_state
= port
->state
;
511 if (strioctl(control_fd
, BRIOC_SETSTATE
, &bss
, sizeof (bss
)) == -1) {
512 syslog(LOG_ERR
, "cannot set STP state on %s: %m", port
->name
);
516 rc
= STP_IN_enable_port(port
->port_index
,
517 port
->admin_status
&& port
->phys_status
&& !port
->sdu_failed
&&
518 protect
== DLADM_BRIDGE_PROT_STP
);
520 syslog(LOG_ERR
, "STP enable %s %d: %s", port
->name
,
521 port
->port_index
, STP_IN_get_error_explanation(rc
));
526 rc
= STP_IN_dbg_set_port_trace("all", True
, 0,
529 /* return to default debug state */
530 rc
= STP_IN_dbg_set_port_trace("all", False
, 0,
533 rc
= STP_IN_dbg_set_port_trace("sttrans", True
, 0,
537 syslog(LOG_ERR
, "STP trace %s %d: %s", port
->name
,
538 port
->port_index
, STP_IN_get_error_explanation(rc
));
542 /* Clear out the kernel's allowed VLAN set; second walk will set */
543 bve
.bve_linkid
= port
->linkid
;
545 bve
.bve_onoff
= B_FALSE
;
546 if (strioctl(control_fd
, BRIOC_VLANENAB
, &bve
, sizeof (bve
)) == -1) {
547 syslog(LOG_ERR
, "unable to disable VLANs on %s: %m",
552 if ((rc
= STP_IN_port_set_cfg(0, port
->port_index
, &portcfg
)) != 0) {
553 syslog(LOG_ERR
, "STP port configure %s %d: %s", port
->name
,
554 port
->port_index
, STP_IN_get_error_explanation(rc
));
561 (void) STP_IN_port_remove(port
->vlan_id
, port
->port_index
);
562 port
->stp_added
= B_FALSE
;