confd: bridge: Add STP/RSTP support

This commit is contained in:
Tobias Waldekranz
2025-01-08 10:14:46 +01:00
parent b425edffce
commit bfeeade43b
16 changed files with 399 additions and 5 deletions
@@ -0,0 +1,8 @@
# A single mstpd instance can manage multiple bridges, which are
# dynamically added/removed by the kernel via the /sbin/bridge-stp
# usermode helper. We depend on usr/mstpd so that confd can
# enable/disable the service without an initctl barrier, since it
# needs to already be running when a bridge interface with spanning
# tree enabled is created.
service env:-/etc/default/mstpd <!usr/mstpd> \
[S0123456789] mstpd $MSTPD_ARGS -- Spanning Tree daemon
+1
View File
@@ -0,0 +1 @@
../available/mstpd.conf
+1
View File
@@ -18,6 +18,7 @@ All notable changes to the project are documented in this file.
- SSH Server is now configurable, issue #441
SSH Server and NETCONF Server now uses the same SSH hostkey in factory-config
- Add support for GRE/GRETAP tunnels
- Support for STP/RSTP on bridges
### Fixes
+2 -1
View File
@@ -1,3 +1,4 @@
pkglibexec_SCRIPTS = bootstrap error load gen-service gen-hostname \
gen-interfaces gen-motd gen-hardware gen-version
gen-interfaces gen-motd gen-hardware gen-version \
mstpd-wait-online
sbin_SCRIPTS = dagger migrate
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
#
# Block until mstpd has opened its control socket
set -e
timeout=${1:-10}
for _ in $(seq $((timeout * 10))); do
ss -Haxn src '@.mstp_server*' | grep -q mstp_server && exit 0
sleep .1
done
logger -I $$ -p user.error -t mstpd-wait-online \
"Timeout waiting for mstpd to start"
exit 1
+4 -1
View File
@@ -73,8 +73,11 @@ enum netdag_init {
/* Interface specific setup (bridge VLAN PVID) of lowers */
NETDAG_INIT_LOWERS_PROTO = 65,
/* Start daemons running on interface (zcip etc.) */
/* Start/configure daemons on interface (mstpd, zcip etc.) */
NETDAG_INIT_DAEMON = 70,
/* Inject lower-specific configuration to daemon (mstpd) */
NETDAG_INIT_DAEMON_LOWERS = 75,
};
FILE *dagger_fopen_net_init(struct dagger *d, const char *node, enum netdag_init order,
+5
View File
@@ -662,6 +662,11 @@ static sr_error_t ifchange_post(sr_session_ctx_t *session, struct dagger *net,
*/
err = bridge_mcd_gen(cifs);
/* Whenever at least one bridge has spanning tree enabled,
* start mstpd; otherwise, stop it.
*/
err = err ? : bridge_mstpd_gen(cifs);
return err ? SR_ERR_INTERNAL : SR_ERR_OK;
}
+1
View File
@@ -57,6 +57,7 @@ int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
struct lyd_node *cif, struct lyd_node *dif);
/* infix-if-bridge.c */
int bridge_mstpd_gen(struct lyd_node *cifs);
int bridge_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, int add);
/* infix-if-bridge-mcd.c */
int bridge_mcd_gen(struct lyd_node *cifs);
+76
View File
@@ -12,6 +12,78 @@
#include "ietf-interfaces.h"
static const char *mstpd_cost_str(struct lyd_node *cost)
{
const char *val = lyd_get_value(cost);
if (!strcmp(val, "auto"))
return "0";
return val;
}
static int gen_stp_tree(FILE *mstpctl, const char *iface,
const char *brname, struct lyd_node *tree)
{
int mstid = 0;
fprintf(mstpctl, "settreeportcost %s %s %d %s\n", brname, iface, mstid,
mstpd_cost_str(lydx_get_child(tree, "internal-path-cost")));
fprintf(mstpctl, "settreeportprio %s %s %d %s\n", brname, iface, mstid,
lydx_get_cattr(tree, "priority"));
return 0;
}
static int gen_stp(struct lyd_node *cif)
{
const char *brname, *edge, *iface;
struct lyd_node *stp, *cist;
FILE *mstpctl;
int err;
brname = lydx_get_cattr(lydx_get_child(cif, "bridge-port"), "bridge");
if (!brname)
return 0;
if (!lydx_get_xpathf(cif, "../interface[name='%s']/bridge/stp", brname))
return 0;
iface = lydx_get_cattr(cif, "name");
stp = lydx_get_descendant(lyd_child(cif), "bridge-port", "stp", NULL);
mstpctl = dagger_fopen_net_init(&confd.netdag, brname,
NETDAG_INIT_DAEMON_LOWERS, "init-ports.mstpctl");
if (!mstpctl)
return -EIO;
fputs("#!/sbin/mstpctl -b\n", mstpctl);
edge = lydx_get_cattr(stp, "edge");
if (!strcmp(edge, "true")) {
fprintf(mstpctl, "setportautoedge %s %s no\n", brname, iface);
fprintf(mstpctl, "setportadminedge %s %s yes\n", brname, iface);
} else if (!strcmp(edge, "false")) {
fprintf(mstpctl, "setportautoedge %s %s no\n", brname, iface);
fprintf(mstpctl, "setportadminedge %s %s no\n", brname, iface);
} else if (!strcmp(edge, "auto")) {
fprintf(mstpctl, "setportautoedge %s %s yes\n", brname, iface);
}
cist = lydx_get_child(stp, "cist");
err = gen_stp_tree(mstpctl, iface, brname, cist);
if (err)
goto out_close;
fprintf(mstpctl, "setportpathcost %s %s %s\n", brname, iface,
mstpd_cost_str(lydx_get_child(cist, "external-path-cost")));
out_close:
fclose(mstpctl);
return err;
}
static const char *get_port_egress_mode(const char *iface, int vid, const char *brname)
{
static const char *modes[] = { "tagged", "untagged", NULL };
@@ -237,5 +309,9 @@ int bridge_port_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip)
if (err)
return err;
err = gen_stp(cif);
if (err)
return err;
return 0;
}
+89
View File
@@ -308,6 +308,91 @@ static int gen_vlan(struct ixif_br *br)
/* VLAN */
/* STP */
int bridge_mstpd_gen(struct lyd_node *cifs)
{
struct ly_set *stp_brs;
int n, err = 0;
FILE *cond;
if (lyd_find_xpath(cifs, "../interface/bridge/stp", &stp_brs))
return -EINVAL;
n = stp_brs->count;
ly_set_free(stp_brs, NULL);
if (n) {
cond = dagger_fopen_next(&confd.netdag, "init", "@pre", 50, "enable-mstpd.sh");
if (!cond)
return -EIO;
fputs("initctl -bnq cond set mstpd\n"
"/usr/libexec/confd/mstpd-wait-online || exit 1\n", cond);
fclose(cond);
} else if (!dagger_is_bootstrap(&confd.netdag)) {
cond = dagger_fopen_current(&confd.netdag, "exit", "@post", 50, "disable-mstpd.sh");
if (!cond)
return -EIO;
fputs("initctl -bnq cond clear mstpd\n", cond);
fclose(cond);
}
return err;
}
static int gen_stp(struct ixif_br *br)
{
struct lyd_node *stp;
FILE *mstpctl;
stp = lydx_get_descendant(lyd_child(br->cif), "bridge", "stp", NULL);
if (!stp) {
fputs(" stp_state 0", br->bropts.fp);
return 0;
}
fputs(" stp_state 1", br->bropts.fp);
if (lydx_get_descendant(lyd_child(br->cif), "bridge", "vlans", NULL))
/* Use the MSTP compatible mode of managing port
* states, rather then the legacy (and default)
* per-VLAN mode. */
fputs(" mst_enabled 1", br->bropts.fp);
mstpctl = dagger_fopen_net_init(&confd.netdag, br->name,
NETDAG_INIT_DAEMON, "init-br.mstpctl");
if (!mstpctl)
return -EIO;
fputs("#!/sbin/mstpctl -b\n", mstpctl);
fprintf(mstpctl, "setforcevers %s %s\n", br->name,
lydx_get_cattr(stp, "force-protocol"));
fprintf(mstpctl, "setfdelay %s %s\n", br->name,
lydx_get_cattr(stp, "forward-delay"));
fprintf(mstpctl, "setmaxage %s %s\n", br->name,
lydx_get_cattr(stp, "max-age"));
fprintf(mstpctl, "settxholdcount %s %s\n", br->name,
lydx_get_cattr(stp, "transmit-hold-count"));
fprintf(mstpctl, "setmaxhops %s %s\n", br->name,
lydx_get_cattr(stp, "max-hops"));
fprintf(mstpctl, "settreeprio %s 0 %s\n", br->name,
lydx_get_cattr(lydx_get_child(stp, "cist"), "priority"));
fclose(mstpctl);
return 0;
}
/* STP */
/* BR */
static void gen_phys_address(struct ixif_br *br)
@@ -420,6 +505,10 @@ int bridge_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, int add)
fputs(" mcast_flood_always 1", br.bropts.fp);
fputs(" vlan_default_pvid 0", br.bropts.fp);
err = gen_stp(&br);
if (err)
goto out;
err = gen_vlan(&br);
if (err)
goto out;
+1 -1
View File
@@ -37,7 +37,7 @@ MODULES=(
"ieee802-ethernet-interface@2019-06-21.yang"
"infix-ethernet-interface@2024-02-27.yang"
"infix-factory-default@2023-06-28.yang"
"infix-interfaces@2024-11-27.yang -e vlan-filtering"
"infix-interfaces@2025-01-08.yang -e vlan-filtering"
# from rousette
"ietf-restconf@2017-01-26.yang"
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- sh -*-
# REMEMBER TO UPDATE infix-interfaces ALSO IN confd.inc
MODULES=(
"infix-interfaces@2024-11-27.yang -e vlan-filtering -e containers"
"infix-interfaces@2025-01-08.yang -e vlan-filtering -e containers"
"infix-containers@2024-11-15.yang"
)
+189 -1
View File
@@ -26,6 +26,11 @@ submodule infix-if-bridge {
contact "kernelkit@googlegroups.com";
description "Linux bridge extension for ietf-interfaces.";
revision 2025-01-08 {
description "Add Spanning Tree Protocol (STP) support.";
reference "internal";
}
revision 2024-11-28 {
description "Drop must() expressions for IP addres on VLAN bridges.
If a bridge is untagged member of a VLAN it should be
@@ -148,6 +153,26 @@ submodule infix-if-bridge {
reserved link-local multicast groups, in 01:80:C2:00:00:0X.";
}
typedef stp-protocol {
description "Spanning Tree Protocol version.";
type enumeration {
enum stp {
value 0;
description "Spanning Tree Protocol.";
}
enum rstp {
value 2;
description "Rapid Spanning Tree Protocol.";
}
// enum mstp {
// value 3;
// description "Multiple Spanning Tree Protocol";
// }
}
reference "IEEE 802.1Q-2018 13.7.2";
}
typedef stp-state {
description "User-friendly enumeration of different bridge port operational states.";
type enumeration {
@@ -165,7 +190,7 @@ submodule infix-if-bridge {
}
enum forwarding {
value 3;
description "Port is in STP FORWARDING state. This is the default vlan state.";
description "Port is in STP FORWARDING state.";
}
enum blocking {
value 4;
@@ -174,6 +199,36 @@ submodule infix-if-bridge {
}
}
typedef stp-priority {
type uint8 {
range "0..15";
}
default 8;
}
typedef stp-bool-or-auto {
type union {
type enumeration {
enum auto;
}
type boolean;
}
default auto;
}
typedef stp-path-cost {
type union {
type enumeration {
enum auto;
}
type uint32 {
range "0..200000000";
}
}
default auto;
}
typedef querier-mode {
description "Type of IGMP/MLD querier, recommend using 'auto'.";
type enumeration {
@@ -224,6 +279,7 @@ submodule infix-if-bridge {
}
}
/*
* Shared settings
*/
@@ -321,6 +377,34 @@ submodule infix-if-bridge {
}
}
grouping stp-tree {
leaf priority {
description "Priority.";
type stp-priority;
reference "IEEE 802.1Q-2018 13.18";
}
}
grouping stp-tree-port {
leaf internal-path-cost {
description "Internal path cost.";
type stp-path-cost;
reference "IEEE 802.1Q-2018 13.27.33";
}
leaf priority {
description "Priority.";
type stp-priority;
reference "IEEE 802.1Q-2018 13.18";
}
leaf state {
description "Operational state.";
type stp-state;
config false;
}
}
/*
* Data Nodes
*/
@@ -392,6 +476,77 @@ submodule infix-if-bridge {
}
}
}
container stp {
presence stp;
description "Spanning Tree Protocol";
leaf force-protocol {
description "Protocol version.";
type stp-protocol;
default rstp;
reference "IEEE 802.1Q-2018 13.7.2";
}
leaf forward-delay {
description "Forward delay.";
type uint8 {
range "4..30";
}
default 15;
reference "IEEE 802.1Q-2018 13.25";
}
leaf max-age {
description "Max age.";
type uint8 {
range "6..40";
}
default 20;
reference "IEEE 802.1Q-2018 13.25";
}
leaf transmit-hold-count {
description "Transmit hold count.";
type uint8 {
range "1..10";
}
default 6;
reference "IEEE 802.1Q-2018 13.25";
}
leaf max-hops {
description "Max hops.";
type uint8 {
range "6..40";
}
default 20;
reference "IEEE 802.1Q-2018 13.25";
}
container cist {
description "Common and Internal Spanning Tree.";
uses stp-tree;
}
// list mst {
// description "Multiple Spanning Tree Instance.";
// key "mstid";
// leaf mstid {
// description "The MSTI identifier to which this entry applies.";
// type dot1q-types:mstid-type;
// }
// uses stp-tree;
// leaf-list vlan {
// type leafref {
// path "/if:interfaces/interface/bridge/vlans/vlan/vid";
// }
// }
// }
}
}
}
deviation "/if:interfaces/if:interface/infix-if:bridge/type/ieee8021d/multicast-filters/multicast-filter/ports/port" {
@@ -506,6 +661,39 @@ submodule infix-if-bridge {
}
}
container stp {
description "Spanning Tree Protocol";
reference "IEEE 802.1Q-2018 13.27";
leaf edge {
description "Edge.";
type stp-bool-or-auto;
}
container cist {
description "Common and Internal Spanning Tree.";
uses stp-tree-port;
leaf external-path-cost {
description "External path cost.";
type stp-path-cost;
reference "IEEE 802.1Q-2018 13.27.25";
}
}
// list mst {
// description "Multiple Spanning Tree Instance.";
// key "mstid";
// leaf mstid {
// description "The MSTI identifier to which this entry applies.";
// type dot1q-types:mstid-type;
// }
// uses stp-tree-port;
// }
}
leaf stp-state {
type stp-state;
config false;
+5
View File
@@ -27,6 +27,11 @@ module infix-interfaces {
contact "kernelkit@googlegroups.com";
description "Linux bridge and lag extensions for ietf-interfaces.";
revision 2025-01-08 {
description "Add Spanning Tree Protocol (STP) support to bridges.";
reference "internal";
}
revision 2024-11-27 {
description "Allow IP addresses directly on VLAN filtering bridges.";
reference "internal";