mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-27 11:13:02 +02:00
confd: bridge: Add STP/RSTP support
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
../available/mstpd.conf
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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"
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user