mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Merge pull request #397 from DrDoof/feat/stp-lag-entity
stp: treat a link aggregation group as one entity
This commit is contained in:
5 files changed
+406
-111
No files matched your search
+26
@@ -58,6 +58,32 @@ port set to admit tagged frames only (`ingress <port>t`) never delivers one
|
|||||||
to the CPU. `stp_setup()` prints a warning for every STP-enabled port in that
|
to the CPU. `stp_setup()` prints a warning for every STP-enabled port in that
|
||||||
state.
|
state.
|
||||||
|
|
||||||
|
## Link aggregation
|
||||||
|
|
||||||
|
A LAG (Link Aggregation Group) is handled like an additional port by the STP
|
||||||
|
protocol, with its own timers and states. The switch devices support up to 4
|
||||||
|
LAGs, which are shown alongside the physical ports in the STP status. A group
|
||||||
|
carries its own path cost, priority, edge and guard settings, and gets its own
|
||||||
|
row on the Spanning Tree page.
|
||||||
|
|
||||||
|
```
|
||||||
|
stp lag 1 cost 10000 # the group decides, not its members
|
||||||
|
stp lag 1 edge off
|
||||||
|
```
|
||||||
|
|
||||||
|
LAGs are configured via the `lag` command. Once a port is a member of a LAG it
|
||||||
|
can no longer be configured individually for STP, so `stp port <n>` on a member
|
||||||
|
tells you which group to configure instead. Membership is re-read from the
|
||||||
|
aggregation registers once a second, so a group changing under LACP is picked
|
||||||
|
up without any coordination between the two.
|
||||||
|
|
||||||
|
The switch hardware does not handle STP state for a LAG as a whole. State
|
||||||
|
changes for the members have to be done by the firmware, updating every member
|
||||||
|
port, which is possible using a single register write. BPDUs go out through the
|
||||||
|
lowest member and carry the group's own port id. Losing one member of a live
|
||||||
|
LAG is not a topology change; the logical port only goes down with its last
|
||||||
|
link.
|
||||||
|
|
||||||
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
|
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
|
||||||
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
|
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
|
||||||
blocking forwards nothing between ports, but it still sends what the CPU
|
blocking forwards nothing between ports, but it still sends what the CPU
|
||||||
|
|||||||
+54
-31
@@ -1020,6 +1020,13 @@ const conf_cmds = [
|
|||||||
/^stp\s+port\s+\d{1,2}\s+guard\s+(none|bpdu|root)$/,
|
/^stp\s+port\s+\d{1,2}\s+guard\s+(none|bpdu|root)$/,
|
||||||
/^stp\s+port\s+\d{1,2}\s+filter\s+(on|off)$/,
|
/^stp\s+port\s+\d{1,2}\s+filter\s+(on|off)$/,
|
||||||
/^stp\s+port\s+\d{1,2}\s+p2p\s+(auto|on|off)$/,
|
/^stp\s+port\s+\d{1,2}\s+p2p\s+(auto|on|off)$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+(on|off)$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+edge\s+(on|off|auto)$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+cost\s+\d{1,9}$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+prio\s+\d{1,3}$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+guard\s+(none|bpdu|root)$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+filter\s+(on|off)$/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+p2p\s+(auto|on|off)$/,
|
||||||
/^igmp\s+(on|off)$/,
|
/^igmp\s+(on|off)$/,
|
||||||
/^mtu\s+\d{1,2}\s+\d+$/,
|
/^mtu\s+\d{1,2}\s+\d+$/,
|
||||||
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
|
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
|
||||||
@@ -1055,6 +1062,7 @@ const conf_overwrite = [
|
|||||||
/^isolate\s+\d{1,2}\b/,
|
/^isolate\s+\d{1,2}\b/,
|
||||||
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
|
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
|
||||||
/^stp\s+port\s+\d{1,2}\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
/^stp\s+port\s+\d{1,2}\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
||||||
|
/^stp\s+lag\s+[1-4]\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
||||||
/^igmp\b/,
|
/^igmp\b/,
|
||||||
/^mtu\s+\d{1,2}\b/,
|
/^mtu\s+\d{1,2}\b/,
|
||||||
/^bw\s+(in|out)\s+\d{1,2}\b/,
|
/^bw\s+(in|out)\s+\d{1,2}\b/,
|
||||||
@@ -2601,45 +2609,58 @@ function num(id, min, max, onch) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function members(mask) {
|
||||||
|
const a = [];
|
||||||
|
for (let i = 0; i < numPorts; i++)
|
||||||
|
if (mask & (1 << i)) a.push(logToPhysPort[i]);
|
||||||
|
return a.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
function buildPortsTable(ports) {
|
function buildPortsTable(ports) {
|
||||||
const tbl = document.getElementById("stpPortsTbl");
|
const tbl = document.getElementById("stpPortsTbl");
|
||||||
const stat = document.getElementById("stpStatTbl");
|
const stat = document.getElementById("stpStatTbl");
|
||||||
for (const p of [...ports].sort((a, b) => a.p - b.p)) {
|
for (const p of [...ports].sort((a, b) => a.p - b.p)) {
|
||||||
|
p.n = p.lag ? "lag " + p.lag : "port " + p.p;
|
||||||
|
p.k = p.lag ? "L" + p.lag : p.p;
|
||||||
const tr = tbl.insertRow();
|
const tr = tbl.insertRow();
|
||||||
tr.insertCell().textContent = p.p;
|
tr.insertCell().textContent = p.lag
|
||||||
tr.insertCell().appendChild(sel("en_" + p.p,
|
? "LAG" + p.lag + " (" + members(p.mbr) + ")"
|
||||||
|
: p.p;
|
||||||
|
tr.insertCell().appendChild(sel("en_" + p.k,
|
||||||
[["on","Enable"],["off","Disable"]],
|
[["on","Enable"],["off","Disable"]],
|
||||||
e => stpCmd("stp port " + p.p + " " + e.target.value)));
|
e => stpCmd("stp " + p.n + " " + e.target.value)));
|
||||||
const pc = num("cost_" + p.p, 0, 200000000,
|
const pc = num("cost_" + p.k, 0, 200000000,
|
||||||
e => stpCmd("stp port " + p.p + " cost " + e.target.value));
|
e => stpCmd("stp " + p.n + " cost " + e.target.value));
|
||||||
pc.style.width = "7em";
|
pc.style.width = "7em";
|
||||||
pc.title = "0 - 200000000 (0 = Auto)";
|
pc.title = "0 - 200000000 (0 = Auto)";
|
||||||
tr.insertCell().appendChild(pc);
|
tr.insertCell().appendChild(pc);
|
||||||
const pr = sel("prio_" + p.p, [],
|
const pr = sel("prio_" + p.k, [],
|
||||||
e => stpCmd("stp port " + p.p + " prio " + e.target.value));
|
e => stpCmd("stp " + p.n + " prio " + e.target.value));
|
||||||
for (let v = 0; v <= 240; v += 16) {
|
for (let v = 0; v <= 240; v += 16) {
|
||||||
const o = document.createElement("option");
|
const o = document.createElement("option");
|
||||||
o.value = v; o.textContent = v + (v === 128 ? " (default)" : "");
|
o.value = v; o.textContent = v + (v === 128 ? " (default)" : "");
|
||||||
pr.appendChild(o);
|
pr.appendChild(o);
|
||||||
}
|
}
|
||||||
tr.insertCell().appendChild(pr);
|
tr.insertCell().appendChild(pr);
|
||||||
tr.insertCell().appendChild(sel("edge_" + p.p,
|
tr.insertCell().appendChild(sel("edge_" + p.k,
|
||||||
[["auto","Auto"],["on","Enable"],["off","Disable"]],
|
[["auto","Auto"],["on","Enable"],["off","Disable"]],
|
||||||
e => stpCmd("stp port " + p.p + " edge " + e.target.value)));
|
e => stpCmd("stp " + p.n + " edge " + e.target.value)));
|
||||||
tr.insertCell().appendChild(sel("filt_" + p.p,
|
tr.insertCell().appendChild(sel("filt_" + p.k,
|
||||||
[["off","Disable"],["on","Enable"]],
|
[["off","Disable"],["on","Enable"]],
|
||||||
e => stpCmd("stp port " + p.p + " filter " + e.target.value)));
|
e => stpCmd("stp " + p.n + " filter " + e.target.value)));
|
||||||
tr.insertCell().appendChild(sel("guard_" + p.p,
|
tr.insertCell().appendChild(sel("guard_" + p.k,
|
||||||
[["none","None"],["bpdu","BPDU"],["root","Root"]],
|
[["none","None"],["bpdu","BPDU"],["root","Root"]],
|
||||||
e => stpCmd("stp port " + p.p + " guard " + e.target.value)));
|
e => stpCmd("stp " + p.n + " guard " + e.target.value)));
|
||||||
tr.insertCell().appendChild(sel("p2p_" + p.p,
|
tr.insertCell().appendChild(sel("p2p_" + p.k,
|
||||||
[["auto","Auto"],["on","Enable"],["off","Disable"]],
|
[["auto","Auto"],["on","Enable"],["off","Disable"]],
|
||||||
e => stpCmd("stp port " + p.p + " p2p " + e.target.value)));
|
e => stpCmd("stp " + p.n + " p2p " + e.target.value)));
|
||||||
|
|
||||||
const sr = stat.insertRow();
|
const sr = stat.insertRow();
|
||||||
sr.insertCell().textContent = p.p;
|
sr.insertCell().textContent = p.lag
|
||||||
|
? "LAG" + p.lag + " (" + members(p.mbr) + ")"
|
||||||
|
: p.p;
|
||||||
for (const id of ["st","role","db","dp","dc","oe","op"])
|
for (const id of ["st","role","db","dp","dc","oe","op"])
|
||||||
sr.insertCell().id = id + "_" + p.p;
|
sr.insertCell().id = id + "_" + p.k;
|
||||||
}
|
}
|
||||||
stpRows = ports.length;
|
stpRows = ports.length;
|
||||||
}
|
}
|
||||||
@@ -2672,18 +2693,19 @@ function fetchStp() {
|
|||||||
+ " \u2014 topology changes: " + parseInt(s.tc, 16))
|
+ " \u2014 topology changes: " + parseInt(s.tc, 16))
|
||||||
: "";
|
: "";
|
||||||
for (const p of s.ports) {
|
for (const p of s.ports) {
|
||||||
|
const k = p.lag ? "L" + p.lag : p.p;
|
||||||
const trip = (p.f & PF_TRIPPED) ? " (guard!)" : "";
|
const trip = (p.f & PF_TRIPPED) ? " (guard!)" : "";
|
||||||
document.getElementById("st_" + p.p).textContent =
|
document.getElementById("st_" + k).textContent =
|
||||||
s.on ? STP_STATES[p.st] + trip : "-";
|
s.on ? STP_STATES[p.st] + trip : "-";
|
||||||
document.getElementById("role_" + p.p).textContent =
|
document.getElementById("role_" + k).textContent =
|
||||||
s.on ? STP_ROLES[p.role] : "-";
|
s.on ? STP_ROLES[p.role] : "-";
|
||||||
document.getElementById("db_" + p.p).textContent = s.on ? fmtBridgeId(p.db) : "-";
|
document.getElementById("db_" + k).textContent = s.on ? fmtBridgeId(p.db) : "-";
|
||||||
document.getElementById("dp_" + p.p).textContent =
|
document.getElementById("dp_" + k).textContent =
|
||||||
s.on ? (parseInt(p.dp.slice(0, 2), 16) + "-" + parseInt(p.dp.slice(2), 16)) : "-";
|
s.on ? (parseInt(p.dp.slice(0, 2), 16) + "-" + parseInt(p.dp.slice(2), 16)) : "-";
|
||||||
document.getElementById("dc_" + p.p).textContent = s.on ? parseInt(p.dc, 16) : "-";
|
document.getElementById("dc_" + k).textContent = s.on ? parseInt(p.dc, 16) : "-";
|
||||||
document.getElementById("oe_" + p.p).textContent =
|
document.getElementById("oe_" + k).textContent =
|
||||||
s.on ? ((p.f & PF_OPEREDGE) ? "True" : "False") : "-";
|
s.on ? ((p.f & PF_OPEREDGE) ? "True" : "False") : "-";
|
||||||
document.getElementById("op_" + p.p).textContent = s.on ? (p.p2 == 2 ? "False" : "True") : "-";
|
document.getElementById("op_" + k).textContent = s.on ? (p.p2 == 2 ? "False" : "True") : "-";
|
||||||
}
|
}
|
||||||
if (stpDirty)
|
if (stpDirty)
|
||||||
return;
|
return;
|
||||||
@@ -2695,15 +2717,16 @@ function fetchStp() {
|
|||||||
document.getElementById("bFwd").value = s.fwd;
|
document.getElementById("bFwd").value = s.fwd;
|
||||||
document.getElementById("bTxhold").value = s.txhold;
|
document.getElementById("bTxhold").value = s.txhold;
|
||||||
for (const p of s.ports) {
|
for (const p of s.ports) {
|
||||||
document.getElementById("en_" + p.p).value = (p.f & PF_ENABLED) ? "on" : "off";
|
const k = p.lag ? "L" + p.lag : p.p;
|
||||||
document.getElementById("edge_" + p.p).value =
|
document.getElementById("en_" + k).value = (p.f & PF_ENABLED) ? "on" : "off";
|
||||||
|
document.getElementById("edge_" + k).value =
|
||||||
(p.f & PF_ADMEDGE) ? "on" : ((p.f & PF_AUTOEDGE) ? "auto" : "off");
|
(p.f & PF_ADMEDGE) ? "on" : ((p.f & PF_AUTOEDGE) ? "auto" : "off");
|
||||||
document.getElementById("cost_" + p.p).value = parseInt(p.pc, 16);
|
document.getElementById("cost_" + k).value = parseInt(p.pc, 16);
|
||||||
document.getElementById("prio_" + p.p).value = p.prio;
|
document.getElementById("prio_" + k).value = p.prio;
|
||||||
document.getElementById("p2p_" + p.p).value = ["auto","on","off"][p.p2];
|
document.getElementById("p2p_" + k).value = ["auto","on","off"][p.p2];
|
||||||
document.getElementById("guard_" + p.p).value =
|
document.getElementById("guard_" + k).value =
|
||||||
(p.f & PF_BPDUGUARD) ? "bpdu" : ((p.f & PF_ROOTGUARD) ? "root" : "none");
|
(p.f & PF_BPDUGUARD) ? "bpdu" : ((p.f & PF_ROOTGUARD) ? "root" : "none");
|
||||||
document.getElementById("filt_" + p.p).value = (p.f & PF_FILTER) ? "on" : "off";
|
document.getElementById("filt_" + k).value = (p.f & PF_FILTER) ? "on" : "off";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+32
-4
@@ -607,17 +607,43 @@ void send_stp(void)
|
|||||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||||
bool_to_html(stp_root_port == 0xff ? 1 : 0);
|
bool_to_html(stp_root_port == 0xff ? 1 : 0);
|
||||||
slen += strtox(outbuf + slen, ",\"rootPort\":");
|
slen += strtox(outbuf + slen, ",\"rootPort\":");
|
||||||
itoa_html(stp_root_port == 0xff ? 0 : machine.log_to_phys_port[stp_root_port]);
|
j = stp_root_port;
|
||||||
|
if (j != 0xff && j >= STP_LAG_BASE) {
|
||||||
|
j = 0;
|
||||||
|
while (j < STP_LAG_BASE && !((stp_lag_mask[stp_root_port - STP_LAG_BASE] >> j) & 1))
|
||||||
|
j++;
|
||||||
|
if (j >= STP_LAG_BASE)
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
itoa_html(j == 0xff ? 0 : machine.log_to_phys_port[j]);
|
||||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||||
byte_to_html(stp_tc_count >> 8);
|
byte_to_html(stp_tc_count >> 8);
|
||||||
byte_to_html(stp_tc_count);
|
byte_to_html(stp_tc_count);
|
||||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||||
reg_read_m(RTL837X_MSTP_STATES);
|
reg_read_m(RTL837X_MSTP_STATES);
|
||||||
for (i = machine.min_port; i <= machine.max_port; i++) {
|
for (i = 0; i < STP_ENTITIES; i++) {
|
||||||
|
if (i < STP_LAG_BASE && (i < machine.min_port || i > machine.max_port))
|
||||||
|
continue;
|
||||||
|
if (i < STP_LAG_BASE && stp_ent_of[i] != i)
|
||||||
|
continue;
|
||||||
|
if (i >= STP_LAG_BASE && !stp_lag_mask[i - STP_LAG_BASE])
|
||||||
|
continue;
|
||||||
slen += strtox(outbuf + slen, "{\"p\":");
|
slen += strtox(outbuf + slen, "{\"p\":");
|
||||||
itoa_html(machine.log_to_phys_port[i]);
|
itoa_html(i < STP_LAG_BASE ? machine.log_to_phys_port[i] : 0);
|
||||||
|
slen += strtox(outbuf + slen, ",\"lag\":");
|
||||||
|
itoa_html(i < STP_LAG_BASE ? 0 : i - STP_LAG_BASE + 1);
|
||||||
|
slen += strtox(outbuf + slen, ",\"mbr\":");
|
||||||
|
itoa16_html(i < STP_LAG_BASE ? 0 : stp_lag_mask[i - STP_LAG_BASE]);
|
||||||
slen += strtox(outbuf + slen, ",\"st\":");
|
slen += strtox(outbuf + slen, ",\"st\":");
|
||||||
st = (sfr_data[3 - (i >> 2)] >> ((i << 1) & 0x7)) & 0x3;
|
j = i;
|
||||||
|
if (i >= STP_LAG_BASE) {
|
||||||
|
j = 0;
|
||||||
|
while (j < STP_LAG_BASE && !((stp_lag_mask[i - STP_LAG_BASE] >> j) & 1))
|
||||||
|
j++;
|
||||||
|
if (j >= STP_LAG_BASE)
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
st = (sfr_data[3 - (j >> 2)] >> ((j << 1) & 0x7)) & 0x3;
|
||||||
itoa_html(st);
|
itoa_html(st);
|
||||||
slen += strtox(outbuf + slen, ",\"role\":");
|
slen += strtox(outbuf + slen, ",\"role\":");
|
||||||
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
|
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
|
||||||
@@ -658,6 +684,8 @@ void send_stp(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void send_eee(void)
|
void send_eee(void)
|
||||||
{
|
{
|
||||||
dbg_string("send_eee called\nsending EEE status\n");
|
dbg_string("send_eee called\nsending EEE status\n");
|
||||||
|
|||||||
+276
-68
@@ -47,31 +47,36 @@ __xdata uint8_t stp_rstp; /* 1 = RST BPDUs, 0 = legacy Config BPDUs */
|
|||||||
__xdata uint8_t stp_txhold; /* BPDUs per port per second */
|
__xdata uint8_t stp_txhold; /* BPDUs per port per second */
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t stp_pflags[10];
|
__xdata uint8_t stp_ent_of[STP_PORTS];
|
||||||
__xdata uint32_t stp_pcost[10]; /* 0 = auto */
|
__xdata uint16_t stp_lag_mask[STP_LAG_COUNT];
|
||||||
__xdata uint8_t stp_pprio[10];
|
__xdata uint8_t stp_ss_i;
|
||||||
__xdata uint8_t stp_pp2p[10]; /* admin point-to-point: 0 auto, 1 on, 2 off */
|
__xdata uint8_t stp_map_dirty;
|
||||||
|
__xdata uint8_t stp_pflags[STP_ENTITIES];
|
||||||
|
__xdata uint32_t stp_pcost[STP_ENTITIES]; /* 0 = auto */
|
||||||
|
__xdata uint8_t stp_pprio[STP_ENTITIES];
|
||||||
|
__xdata uint8_t stp_pp2p[STP_ENTITIES]; /* admin point-to-point: 0 auto, 1 on, 2 off */
|
||||||
|
|
||||||
/* Designated bridge, port and cost last heard on the port; stp_bpdu_age tells
|
/* Designated bridge, port and cost last heard on the port; stp_bpdu_age tells
|
||||||
* whether they are still current.
|
* whether they are still current.
|
||||||
*/
|
*/
|
||||||
__xdata struct bridge stp_dbridge[10];
|
__xdata struct bridge stp_dbridge[STP_ENTITIES];
|
||||||
__xdata uint16_t stp_dpid[10];
|
__xdata uint16_t stp_dpid[STP_ENTITIES];
|
||||||
__xdata uint32_t stp_dcost[10];
|
__xdata uint32_t stp_dcost[STP_ENTITIES];
|
||||||
|
|
||||||
/* ---- Status / runtime ---- */
|
/* ---- Status / runtime ---- */
|
||||||
__xdata struct bridge root_bridge;
|
__xdata struct bridge root_bridge;
|
||||||
__xdata uint32_t root_bridge_cost; /* our cost to the root (rx cost + root port cost) */
|
__xdata uint32_t root_bridge_cost; /* our cost to the root (rx cost + root port cost) */
|
||||||
__xdata uint8_t stp_root_port; /* 0xff = we are the root */
|
__xdata uint8_t stp_root_port; /* 0xff = we are the root */
|
||||||
__xdata uint16_t stp_tc_count;
|
__xdata uint16_t stp_tc_count;
|
||||||
__xdata uint16_t stp_scratch16; /* scratch for status printing only */
|
__xdata uint16_t stp_scratch16; /* scratch for status printing and the lag map */
|
||||||
|
__xdata uint8_t stp_st_of;
|
||||||
|
|
||||||
__xdata uint16_t port_timers[10]; /* listen-period countdown (0 = not listening) */
|
__xdata uint16_t port_timers[STP_ENTITIES]; /* listen-period countdown (0 = not listening) */
|
||||||
__xdata uint16_t port_hello[10]; /* hello TX countdown */
|
__xdata uint16_t port_hello[STP_ENTITIES]; /* hello TX countdown */
|
||||||
__xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */
|
__xdata uint16_t stp_bpdu_age[STP_ENTITIES]; /* ticks since last BPDU seen on port (saturating) */
|
||||||
__xdata uint8_t stp_loop_held[10]; /* port is out of forwarding because a loop was seen on it */
|
__xdata uint8_t stp_loop_held[STP_ENTITIES]; /* port is out of forwarding because a loop was seen on it */
|
||||||
__xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */
|
__xdata uint8_t stp_tx_budget[STP_ENTITIES]; /* tx hold: BPDUs left in the current second */
|
||||||
__xdata uint8_t stp_tx_count[10]; /* BPDUs actually put on the wire, wraps at 256 */
|
__xdata uint8_t stp_tx_count[STP_ENTITIES]; /* BPDUs actually put on the wire, wraps at 256 */
|
||||||
__xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */
|
__xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */
|
||||||
__xdata uint16_t stp_link_prev; /* carrier bitmap as of the last check */
|
__xdata uint16_t stp_link_prev; /* carrier bitmap as of the last check */
|
||||||
__xdata uint16_t stp_link_now;
|
__xdata uint16_t stp_link_now;
|
||||||
@@ -161,10 +166,22 @@ struct stp_pkt_in {
|
|||||||
#define BPDU_ROLE_ROOT (0b10 << 2)
|
#define BPDU_ROLE_ROOT (0b10 << 2)
|
||||||
#define BPDU_ROLE_DESIGNATED (0b11 << 2)
|
#define BPDU_ROLE_DESIGNATED (0b11 << 2)
|
||||||
|
|
||||||
|
/* Port state as the MSTP register encodes it, two bits per port. */
|
||||||
|
#define STP_ST_DISABLED 0b00
|
||||||
|
#define STP_ST_BLOCKING 0b01
|
||||||
|
#define STP_ST_LEARNING 0b10
|
||||||
|
#define STP_ST_FORWARDING 0b11
|
||||||
|
#define STP_ST_MASK 0b11
|
||||||
|
|
||||||
/* Console messages name the port on the front panel, not the internal index. */
|
/* Console messages name the port on the front panel, not the internal index. */
|
||||||
static void print_port_nl(uint8_t port) __reentrant
|
static void print_port_nl(uint8_t port) __reentrant
|
||||||
{
|
{
|
||||||
|
if (port >= STP_LAG_BASE) {
|
||||||
|
write_char('L');
|
||||||
|
write_char('1' + port - STP_LAG_BASE);
|
||||||
|
} else {
|
||||||
print_byte(machine.log_to_phys_port[port]);
|
print_byte(machine.log_to_phys_port[port]);
|
||||||
|
}
|
||||||
write_char('\n');
|
write_char('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +201,8 @@ static __code const char stp_state_txt[] = "off blocklearnfwd ";
|
|||||||
static __code const char stp_role_txt[] = "desgroot";
|
static __code const char stp_role_txt[] = "desgroot";
|
||||||
static __code const char stp_edge_txt[] = "no yes ";
|
static __code const char stp_edge_txt[] = "no yes ";
|
||||||
|
|
||||||
|
static uint8_t stp_ent_active(uint8_t e) __reentrant;
|
||||||
|
|
||||||
static void print_field(__code const char *txt, uint8_t idx, uint8_t width) __reentrant
|
static void print_field(__code const char *txt, uint8_t idx, uint8_t width) __reentrant
|
||||||
{
|
{
|
||||||
txt += idx * width;
|
txt += idx * width;
|
||||||
@@ -207,7 +226,12 @@ static void stp_status(void)
|
|||||||
print_string(" (this switch)\n");
|
print_string(" (this switch)\n");
|
||||||
} else {
|
} else {
|
||||||
print_string(" port ");
|
print_string(" port ");
|
||||||
|
if (stp_root_port >= STP_LAG_BASE) {
|
||||||
|
write_char('L');
|
||||||
|
write_char('1' + stp_root_port - STP_LAG_BASE);
|
||||||
|
} else {
|
||||||
print_byte(machine.log_to_phys_port[stp_root_port]);
|
print_byte(machine.log_to_phys_port[stp_root_port]);
|
||||||
|
}
|
||||||
print_string(" cost ");
|
print_string(" cost ");
|
||||||
print_long(root_bridge_cost);
|
print_long(root_bridge_cost);
|
||||||
write_char('\n');
|
write_char('\n');
|
||||||
@@ -217,11 +241,26 @@ static void stp_status(void)
|
|||||||
write_char('\n');
|
write_char('\n');
|
||||||
print_string("port state role edge tx bpdu\n");
|
print_string("port state role edge tx bpdu\n");
|
||||||
reg_read_m(RTL837X_MSTP_STATES);
|
reg_read_m(RTL837X_MSTP_STATES);
|
||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
|
if (!stp_ent_active(stp_i))
|
||||||
|
continue;
|
||||||
write_char(' ');
|
write_char(' ');
|
||||||
|
if (stp_i >= STP_LAG_BASE) {
|
||||||
|
write_char('L');
|
||||||
|
write_char('1' + stp_i - STP_LAG_BASE);
|
||||||
|
} else {
|
||||||
print_byte(machine.log_to_phys_port[stp_i]);
|
print_byte(machine.log_to_phys_port[stp_i]);
|
||||||
|
}
|
||||||
print_string(" ");
|
print_string(" ");
|
||||||
print_field(stp_state_txt, (sfr_data[3 - (stp_i >> 2)] >> ((stp_i << 1) & 0x7)) & 0x3, 5);
|
stp_st_of = stp_i;
|
||||||
|
if (stp_i >= STP_LAG_BASE) {
|
||||||
|
stp_st_of = 0;
|
||||||
|
while (stp_st_of < STP_PORTS && !((stp_lag_mask[stp_i - STP_LAG_BASE] >> stp_st_of) & 1))
|
||||||
|
stp_st_of++;
|
||||||
|
if (stp_st_of >= STP_PORTS)
|
||||||
|
stp_st_of = 0;
|
||||||
|
}
|
||||||
|
print_field(stp_state_txt, (sfr_data[3 - (stp_st_of >> 2)] >> ((stp_st_of << 1) & 0x7)) & 0x3, 5);
|
||||||
write_char(' ');
|
write_char(' ');
|
||||||
print_field(stp_role_txt, stp_i == stp_root_port ? 1 : 0, 4);
|
print_field(stp_role_txt, stp_i == stp_root_port ? 1 : 0, 4);
|
||||||
write_char(' ');
|
write_char(' ');
|
||||||
@@ -266,18 +305,95 @@ int8_t cmpBytes(__xdata uint8_t *m1, __xdata uint8_t *m2, uint8_t n) __reentrant
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write one port's 2-bit state into the ASIC's MSTP register.
|
static void stp_lag_map(void)
|
||||||
* 00 disable, 01 blocking, 10 learning, 11 forwarding. */
|
{
|
||||||
|
for (stp_ss_i = 0; stp_ss_i < STP_PORTS; stp_ss_i++)
|
||||||
|
stp_ent_of[stp_ss_i] = stp_ss_i;
|
||||||
|
for (stp_scratch = 0; stp_scratch < STP_LAG_COUNT; stp_scratch++) {
|
||||||
|
stp_scratch16 = port_lag_members_get(stp_scratch);
|
||||||
|
if (stp_lag_mask[stp_scratch] != stp_scratch16)
|
||||||
|
stp_map_dirty = 1;
|
||||||
|
stp_lag_mask[stp_scratch] = stp_scratch16;
|
||||||
|
for (stp_ss_i = 0; stp_ss_i < STP_PORTS; stp_ss_i++)
|
||||||
|
if ((stp_lag_mask[stp_scratch] >> stp_ss_i) & 1)
|
||||||
|
stp_ent_of[stp_ss_i] = STP_LAG_BASE + stp_scratch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t stp_ent_active(uint8_t e) __reentrant
|
||||||
|
{
|
||||||
|
if (e >= STP_ENTITIES)
|
||||||
|
return 0;
|
||||||
|
if (e >= STP_LAG_BASE)
|
||||||
|
return stp_lag_mask[e - STP_LAG_BASE] != 0;
|
||||||
|
return e >= machine.min_port && e <= machine.max_port && stp_ent_of[e] == e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t stp_ent_has(uint8_t ent, uint8_t p) __reentrant
|
||||||
|
{
|
||||||
|
if (ent < STP_LAG_BASE)
|
||||||
|
return ent == p;
|
||||||
|
return (stp_lag_mask[ent - STP_LAG_BASE] >> p) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t stp_state_port(uint8_t e) __reentrant
|
||||||
|
{
|
||||||
|
if (e < STP_LAG_BASE)
|
||||||
|
return e;
|
||||||
|
for (stp_ss_i = 0; stp_ss_i < STP_PORTS; stp_ss_i++)
|
||||||
|
if ((stp_lag_mask[e - STP_LAG_BASE] >> stp_ss_i) & 1)
|
||||||
|
return stp_ss_i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void stp_state_bits(uint8_t port, uint8_t state) __reentrant
|
||||||
|
{
|
||||||
|
for (stp_ss_i = 0; stp_ss_i < STP_PORTS; stp_ss_i++)
|
||||||
|
if (stp_ent_has(port, stp_ss_i))
|
||||||
|
sfr_data[3 - (stp_ss_i >> 2)] |= (uint8_t)(state << ((stp_ss_i << 1) & 0x7));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void stp_state_set(uint8_t port, uint8_t state) __reentrant
|
static void stp_state_set(uint8_t port, uint8_t state) __reentrant
|
||||||
{
|
{
|
||||||
reg_read_m(RTL837X_MSTP_STATES);
|
reg_read_m(RTL837X_MSTP_STATES);
|
||||||
stp_scratch = 3 - (port >> 2);
|
for (stp_ss_i = 0; stp_ss_i < STP_PORTS; stp_ss_i++) {
|
||||||
sfr_data[stp_scratch] &= ~(uint8_t)(0b11 << ((port << 1) & 0x7));
|
if (!stp_ent_has(port, stp_ss_i))
|
||||||
sfr_data[stp_scratch] |= (uint8_t)(state << ((port << 1) & 0x7));
|
continue;
|
||||||
|
stp_scratch = 3 - (stp_ss_i >> 2);
|
||||||
|
sfr_data[stp_scratch] &= ~(uint8_t)(STP_ST_MASK << ((stp_ss_i << 1) & 0x7));
|
||||||
|
sfr_data[stp_scratch] |= (uint8_t)(state << ((stp_ss_i << 1) & 0x7));
|
||||||
|
}
|
||||||
reg_write_m(RTL837X_MSTP_STATES);
|
reg_write_m(RTL837X_MSTP_STATES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void stp_ent_apply(uint8_t e) __reentrant
|
||||||
|
{
|
||||||
|
if (!(stp_pflags[e] & STP_PF_ENABLED)) {
|
||||||
|
stp_state_set(e, STP_ST_FORWARDING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stp_pflags[e] & STP_PF_TRIPPED) {
|
||||||
|
stp_state_set(e, STP_ST_DISABLED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stp_pflags[e] & STP_PF_ADMEDGE) {
|
||||||
|
stp_pflags[e] |= STP_PF_OPEREDGE;
|
||||||
|
stp_state_set(e, STP_ST_FORWARDING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (port_timers[e])
|
||||||
|
stp_state_set(e, STP_ST_BLOCKING);
|
||||||
|
else
|
||||||
|
stp_state_set(e, ((stp_link_prev >> e) & 1) ? STP_ST_FORWARDING : STP_ST_BLOCKING);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Signal a topology change. Edge ports are exempt. */
|
/* Signal a topology change. Edge ports are exempt. */
|
||||||
static void stp_topology_change(uint8_t port) __reentrant
|
static void stp_topology_change(uint8_t port) __reentrant
|
||||||
{
|
{
|
||||||
@@ -285,8 +401,14 @@ static void stp_topology_change(uint8_t port) __reentrant
|
|||||||
return;
|
return;
|
||||||
stp_tc_count++;
|
stp_tc_count++;
|
||||||
stp_tc_while = ((uint16_t)stp_maxage_s + stp_fwddelay_s) * STP_HZ;
|
stp_tc_while = ((uint16_t)stp_maxage_s + stp_fwddelay_s) * STP_HZ;
|
||||||
|
if (port >= STP_LAG_BASE) {
|
||||||
|
for (uint8_t i = machine.min_port; i <= machine.max_port; i++)
|
||||||
|
if (stp_ent_has(port, i))
|
||||||
|
port_l2_forget_port(i);
|
||||||
|
} else {
|
||||||
port_l2_forget_port(port);
|
port_l2_forget_port(port);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Hold one port out of forwarding because a loop was seen on it, and keep
|
/* Hold one port out of forwarding because a loop was seen on it, and keep
|
||||||
@@ -296,7 +418,7 @@ static void stp_topology_change(uint8_t port) __reentrant
|
|||||||
*/
|
*/
|
||||||
static void stp_loop_hold_peer(uint8_t port) __reentrant
|
static void stp_loop_hold_peer(uint8_t port) __reentrant
|
||||||
{
|
{
|
||||||
if (port < machine.min_port || port > machine.max_port)
|
if (port >= STP_ENTITIES || !stp_ent_active(port))
|
||||||
return;
|
return;
|
||||||
if (!(stp_pflags[port] & STP_PF_ENABLED))
|
if (!(stp_pflags[port] & STP_PF_ENABLED))
|
||||||
return;
|
return;
|
||||||
@@ -305,7 +427,7 @@ static void stp_loop_hold_peer(uint8_t port) __reentrant
|
|||||||
if (!port_timers[port]) {
|
if (!port_timers[port]) {
|
||||||
print_string("STP: loop detected, blocking port ");
|
print_string("STP: loop detected, blocking port ");
|
||||||
print_port_nl(port);
|
print_port_nl(port);
|
||||||
stp_state_set(port, 0b01);
|
stp_state_set(port, STP_ST_BLOCKING);
|
||||||
stp_pflags[port] &= ~STP_PF_OPEREDGE;
|
stp_pflags[port] &= ~STP_PF_OPEREDGE;
|
||||||
stp_topology_change(port);
|
stp_topology_change(port);
|
||||||
}
|
}
|
||||||
@@ -348,7 +470,18 @@ void stp_cnf_send(uint8_t port) __reentrant
|
|||||||
STP_O->rtl_tag.version = RTL_FRAME_TAG_VERSION;
|
STP_O->rtl_tag.version = RTL_FRAME_TAG_VERSION;
|
||||||
STP_O->rtl_tag.reason = 0x00;
|
STP_O->rtl_tag.reason = 0x00;
|
||||||
STP_O->rtl_tag.flags = HTONS(RTL_TAG_LEARN_DIS);
|
STP_O->rtl_tag.flags = HTONS(RTL_TAG_LEARN_DIS);
|
||||||
|
if (port >= STP_LAG_BASE) {
|
||||||
|
stp_scratch = 0;
|
||||||
|
while (stp_scratch < STP_PORTS && !stp_ent_has(port, stp_scratch))
|
||||||
|
stp_scratch++;
|
||||||
|
if (stp_scratch >= STP_PORTS) {
|
||||||
|
stp_tx_flags_extra = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
STP_O->rtl_tag.pmask = HTONS(((uint16_t)1) << stp_scratch);
|
||||||
|
} else {
|
||||||
STP_O->rtl_tag.pmask = HTONS(((uint16_t)1) << port);
|
STP_O->rtl_tag.pmask = HTONS(((uint16_t)1) << port);
|
||||||
|
}
|
||||||
|
|
||||||
STP_O->dsap = 0x42;
|
STP_O->dsap = 0x42;
|
||||||
STP_O->ssap = 0x42;
|
STP_O->ssap = 0x42;
|
||||||
@@ -359,8 +492,9 @@ void stp_cnf_send(uint8_t port) __reentrant
|
|||||||
STP_O->version = BPDU_VER_RSTP;
|
STP_O->version = BPDU_VER_RSTP;
|
||||||
STP_O->bpdu_type = BPDU_TYPE_RST;
|
STP_O->bpdu_type = BPDU_TYPE_RST;
|
||||||
reg_read_m(RTL837X_MSTP_STATES);
|
reg_read_m(RTL837X_MSTP_STATES);
|
||||||
|
stp_scratch = stp_state_port(port);
|
||||||
STP_O->flags = port == stp_root_port ? BPDU_ROLE_ROOT : BPDU_ROLE_DESIGNATED;
|
STP_O->flags = port == stp_root_port ? BPDU_ROLE_ROOT : BPDU_ROLE_DESIGNATED;
|
||||||
if (((sfr_data[3 - (port >> 2)] >> ((port << 1) & 0x7)) & 0b11) == 0b11)
|
if (((sfr_data[3 - (stp_scratch >> 2)] >> ((stp_scratch << 1) & 0x7)) & STP_ST_MASK) == STP_ST_FORWARDING)
|
||||||
STP_O->flags |= BPDU_FLAG_LEARNING | BPDU_FLAG_FORWARDING;
|
STP_O->flags |= BPDU_FLAG_LEARNING | BPDU_FLAG_FORWARDING;
|
||||||
} else {
|
} else {
|
||||||
STP_O->msg_len = HTONS(BPDU_LEN_CONFIG);
|
STP_O->msg_len = HTONS(BPDU_LEN_CONFIG);
|
||||||
@@ -424,7 +558,7 @@ void stp_in(void) __banked
|
|||||||
stp_scratch = ((uint8_t)HTONS(STP_I->rtl_tag.pmask)) & 0x0f;
|
stp_scratch = ((uint8_t)HTONS(STP_I->rtl_tag.pmask)) & 0x0f;
|
||||||
if (stp_scratch < machine.min_port || stp_scratch > machine.max_port)
|
if (stp_scratch < machine.min_port || stp_scratch > machine.max_port)
|
||||||
return;
|
return;
|
||||||
port = stp_scratch;
|
port = stp_ent_of[stp_scratch];
|
||||||
|
|
||||||
// Make sure this is the type of (R)STP packet we are interested in:
|
// Make sure this is the type of (R)STP packet we are interested in:
|
||||||
if (!(STP_I->dsap == 0x42 && STP_I->ssap == 0x42 && STP_I->ctrl == 0x03))
|
if (!(STP_I->dsap == 0x42 && STP_I->ssap == 0x42 && STP_I->ctrl == 0x03))
|
||||||
@@ -445,7 +579,7 @@ void stp_in(void) __banked
|
|||||||
print_string("STP: BPDU guard tripped, disabling port ");
|
print_string("STP: BPDU guard tripped, disabling port ");
|
||||||
print_port_nl(port);
|
print_port_nl(port);
|
||||||
stp_pflags[port] |= STP_PF_TRIPPED;
|
stp_pflags[port] |= STP_PF_TRIPPED;
|
||||||
stp_state_set(port, 0b00);
|
stp_state_set(port, STP_ST_DISABLED);
|
||||||
stp_tc_count++;
|
stp_tc_count++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -516,8 +650,9 @@ void stp_in(void) __banked
|
|||||||
uint8_t i;
|
uint8_t i;
|
||||||
stp_tc_count++;
|
stp_tc_count++;
|
||||||
for (i = machine.min_port; i <= machine.max_port; i++)
|
for (i = machine.min_port; i <= machine.max_port; i++)
|
||||||
if (i != port && (stp_pflags[i] & STP_PF_ENABLED)
|
if (!stp_ent_has(port, i)
|
||||||
&& !(stp_pflags[i] & STP_PF_OPEREDGE))
|
&& (stp_pflags[stp_ent_of[i]] & STP_PF_ENABLED)
|
||||||
|
&& !(stp_pflags[stp_ent_of[i]] & STP_PF_OPEREDGE))
|
||||||
port_l2_forget_port(i);
|
port_l2_forget_port(i);
|
||||||
}
|
}
|
||||||
if (stp_tc_while < ((uint16_t)stp_hello_s + 1) * STP_HZ)
|
if (stp_tc_while < ((uint16_t)stp_hello_s + 1) * STP_HZ)
|
||||||
@@ -535,7 +670,7 @@ void stp_in(void) __banked
|
|||||||
if (stp_pflags[port] & STP_PF_ROOTGUARD) {
|
if (stp_pflags[port] & STP_PF_ROOTGUARD) {
|
||||||
print_string("STP: root guard blocking port ");
|
print_string("STP: root guard blocking port ");
|
||||||
print_port_nl(port);
|
print_port_nl(port);
|
||||||
stp_state_set(port, 0b01);
|
stp_state_set(port, STP_ST_BLOCKING);
|
||||||
port_timers[port] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
port_timers[port] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
||||||
stp_pflags[port] &= ~STP_PF_OPEREDGE;
|
stp_pflags[port] &= ~STP_PF_OPEREDGE;
|
||||||
return;
|
return;
|
||||||
@@ -563,9 +698,35 @@ void stp_timers(void) __banked
|
|||||||
/* Refill the per-port tx budgets once per second (tx hold count) */
|
/* Refill the per-port tx budgets once per second (tx hold count) */
|
||||||
if (++stp_sec_tick >= STP_HZ) {
|
if (++stp_sec_tick >= STP_HZ) {
|
||||||
stp_sec_tick = 0;
|
stp_sec_tick = 0;
|
||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++)
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++)
|
||||||
stp_tx_budget[stp_i] = stp_txhold;
|
stp_tx_budget[stp_i] = stp_txhold;
|
||||||
|
|
||||||
|
stp_lag_map();
|
||||||
|
if (stp_map_dirty) {
|
||||||
|
stp_map_dirty = 0;
|
||||||
|
for (stp_i = 0; stp_i < STP_LAG_BASE; stp_i++) {
|
||||||
|
if (stp_i < machine.min_port || stp_i > machine.max_port)
|
||||||
|
continue;
|
||||||
|
if (stp_ent_of[stp_i] != stp_i) {
|
||||||
|
stp_loop_held[stp_i] = 0;
|
||||||
|
port_timers[stp_i] = 0;
|
||||||
|
} else {
|
||||||
|
stp_ent_apply(stp_i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (stp_i = STP_LAG_BASE; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
|
if (stp_lag_mask[stp_i - STP_LAG_BASE]) {
|
||||||
|
stp_ent_apply(stp_i);
|
||||||
|
} else {
|
||||||
|
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
|
||||||
|
stp_loop_held[stp_i] = 0;
|
||||||
|
port_timers[stp_i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stp_root_port != 0xff && !stp_ent_active(stp_root_port))
|
||||||
|
stp_claim_root();
|
||||||
|
}
|
||||||
|
|
||||||
/* Link supervision. Without this the state machine never learns
|
/* Link supervision. Without this the state machine never learns
|
||||||
* that a port lost carrier: it keeps the port in forwarding, keeps
|
* that a port lost carrier: it keeps the port in forwarding, keeps
|
||||||
* announcing on it, and never flushes what was learned behind it -
|
* announcing on it, and never flushes what was learned behind it -
|
||||||
@@ -574,14 +735,26 @@ void stp_timers(void) __banked
|
|||||||
* of the 50 Hz tick. */
|
* of the 50 Hz tick. */
|
||||||
reg_read_m(RTL837X_REG_LINKS_STS);
|
reg_read_m(RTL837X_REG_LINKS_STS);
|
||||||
stp_link_now = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
|
stp_link_now = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
|
||||||
|
{
|
||||||
|
__xdata static uint16_t eff;
|
||||||
|
eff = 0;
|
||||||
|
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++)
|
||||||
|
if ((stp_link_now >> stp_i) & 1)
|
||||||
|
eff |= (uint16_t)1 << stp_ent_of[stp_i];
|
||||||
|
stp_link_now = eff;
|
||||||
|
}
|
||||||
if (stp_link_now != stp_link_prev) {
|
if (stp_link_now != stp_link_prev) {
|
||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
|
if (stp_i < STP_PORTS && (stp_i < machine.min_port || stp_i > machine.max_port))
|
||||||
|
continue;
|
||||||
|
if (stp_i < STP_PORTS && stp_ent_of[stp_i] != stp_i)
|
||||||
|
continue;
|
||||||
if (!(stp_pflags[stp_i] & STP_PF_ENABLED))
|
if (!(stp_pflags[stp_i] & STP_PF_ENABLED))
|
||||||
continue;
|
continue;
|
||||||
if (!((stp_link_now ^ stp_link_prev) >> stp_i & 1))
|
if (!((stp_link_now ^ stp_link_prev) >> stp_i & 1))
|
||||||
continue;
|
continue;
|
||||||
/* Either way the port must stop forwarding first. */
|
/* Either way the port must stop forwarding first. */
|
||||||
stp_state_set(stp_i, 0b01);
|
stp_state_set(stp_i, STP_ST_BLOCKING);
|
||||||
if ((stp_link_now >> stp_i) & 1) {
|
if ((stp_link_now >> stp_i) & 1) {
|
||||||
/* Carrier back: re-run the listen period rather than
|
/* Carrier back: re-run the listen period rather than
|
||||||
* forwarding straight away - the segment may have been
|
* forwarding straight away - the segment may have been
|
||||||
@@ -600,8 +773,8 @@ void stp_timers(void) __banked
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
if (!(stp_pflags[stp_i] & STP_PF_ENABLED))
|
if (!stp_ent_active(stp_i) || !(stp_pflags[stp_i] & STP_PF_ENABLED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (stp_bpdu_age[stp_i] < 0xffff)
|
if (stp_bpdu_age[stp_i] < 0xffff)
|
||||||
@@ -626,7 +799,7 @@ void stp_timers(void) __banked
|
|||||||
if (port_timers[stp_i]) {
|
if (port_timers[stp_i]) {
|
||||||
if (!--port_timers[stp_i]) {
|
if (!--port_timers[stp_i]) {
|
||||||
stp_loop_held[stp_i] = 0;
|
stp_loop_held[stp_i] = 0;
|
||||||
stp_state_set(stp_i, 0b11);
|
stp_state_set(stp_i, STP_ST_FORWARDING);
|
||||||
print_string("STP: port forwarding ");
|
print_string("STP: port forwarding ");
|
||||||
print_port_nl(stp_i);
|
print_port_nl(stp_i);
|
||||||
stp_topology_change(stp_i);
|
stp_topology_change(stp_i);
|
||||||
@@ -637,7 +810,7 @@ void stp_timers(void) __banked
|
|||||||
* host-facing, go to forwarding without the full wait. */
|
* host-facing, go to forwarding without the full wait. */
|
||||||
port_timers[stp_i] = 0;
|
port_timers[stp_i] = 0;
|
||||||
stp_pflags[stp_i] |= STP_PF_OPEREDGE;
|
stp_pflags[stp_i] |= STP_PF_OPEREDGE;
|
||||||
stp_state_set(stp_i, 0b11);
|
stp_state_set(stp_i, STP_ST_FORWARDING);
|
||||||
print_string("STP: edge port forwarding ");
|
print_string("STP: edge port forwarding ");
|
||||||
print_port_nl(stp_i);
|
print_port_nl(stp_i);
|
||||||
}
|
}
|
||||||
@@ -667,7 +840,12 @@ void stp_defaults(void) __banked
|
|||||||
stp_fwddelay_s = 15;
|
stp_fwddelay_s = 15;
|
||||||
stp_rstp = 1;
|
stp_rstp = 1;
|
||||||
stp_txhold = 6;
|
stp_txhold = 6;
|
||||||
for (stp_i = 0; stp_i < 10; stp_i++) {
|
for (stp_i = 0; stp_i < STP_PORTS; stp_i++)
|
||||||
|
stp_ent_of[stp_i] = stp_i;
|
||||||
|
for (stp_i = 0; stp_i < STP_LAG_COUNT; stp_i++)
|
||||||
|
stp_lag_mask[stp_i] = 0;
|
||||||
|
stp_map_dirty = 0;
|
||||||
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
/* enabled, auto-edge on: host-facing ports go forwarding after
|
/* enabled, auto-edge on: host-facing ports go forwarding after
|
||||||
* 3 s of BPDU silence instead of the full forward delay */
|
* 3 s of BPDU silence instead of the full forward delay */
|
||||||
stp_pflags[stp_i] = STP_PF_ENABLED | STP_PF_AUTOEDGE;
|
stp_pflags[stp_i] = STP_PF_ENABLED | STP_PF_AUTOEDGE;
|
||||||
@@ -714,25 +892,34 @@ static void stp_fdb_update(__xdata uint16_t pmask)
|
|||||||
void stp_setup(void) __banked
|
void stp_setup(void) __banked
|
||||||
{
|
{
|
||||||
print_string("Enabling STP: ");
|
print_string("Enabling STP: ");
|
||||||
|
stp_lag_map();
|
||||||
sfr_data[0] = sfr_data[1] = sfr_data[2] = sfr_data[3] = 0;
|
sfr_data[0] = sfr_data[1] = sfr_data[2] = sfr_data[3] = 0;
|
||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
|
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
|
||||||
stp_loop_held[stp_i] = 0;
|
stp_loop_held[stp_i] = 0;
|
||||||
stp_bpdu_age[stp_i] = 0;
|
stp_bpdu_age[stp_i] = 0;
|
||||||
stp_tx_budget[stp_i] = stp_txhold;
|
stp_tx_budget[stp_i] = stp_txhold;
|
||||||
stp_tx_count[stp_i] = 0;
|
stp_tx_count[stp_i] = 0;
|
||||||
if (!(stp_pflags[stp_i] & STP_PF_ENABLED) || (stp_pflags[stp_i] & STP_PF_ADMEDGE)) {
|
port_timers[stp_i] = 0;
|
||||||
|
port_hello[stp_i] = (uint16_t)stp_hello_s * STP_HZ;
|
||||||
|
if (stp_i < STP_PORTS && (stp_i < machine.min_port || stp_i > machine.max_port))
|
||||||
|
continue;
|
||||||
|
if (stp_i >= STP_LAG_BASE && !stp_lag_mask[stp_i - STP_LAG_BASE])
|
||||||
|
continue;
|
||||||
|
if (stp_i < STP_PORTS && stp_ent_of[stp_i] != stp_i)
|
||||||
|
continue;
|
||||||
|
if (!(stp_pflags[stp_i] & STP_PF_ENABLED)
|
||||||
|
|| (stp_pflags[stp_i] & STP_PF_ADMEDGE)) {
|
||||||
/* not participating, or admin edge: forwarding immediately */
|
/* not participating, or admin edge: forwarding immediately */
|
||||||
if (stp_pflags[stp_i] & STP_PF_ADMEDGE)
|
if (stp_pflags[stp_i] & STP_PF_ADMEDGE)
|
||||||
stp_pflags[stp_i] |= STP_PF_OPEREDGE;
|
stp_pflags[stp_i] |= STP_PF_OPEREDGE;
|
||||||
sfr_data[3 - (stp_i >> 2)] |= (uint8_t)(0b11 << ((stp_i << 1) & 0x7));
|
stp_state_bits(stp_i, STP_ST_FORWARDING);
|
||||||
port_timers[stp_i] = 0;
|
port_timers[stp_i] = 0;
|
||||||
} else {
|
} else {
|
||||||
/* listen first: blocking until the forward-delay expires */
|
/* listen first: blocking until the forward-delay expires */
|
||||||
sfr_data[3 - (stp_i >> 2)] |= (uint8_t)(0b01 << ((stp_i << 1) & 0x7));
|
stp_state_bits(stp_i, STP_ST_BLOCKING);
|
||||||
port_timers[stp_i] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
port_timers[stp_i] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
||||||
}
|
}
|
||||||
port_hello[stp_i] = (uint16_t)stp_hello_s * STP_HZ;
|
|
||||||
}
|
}
|
||||||
sfr_data[1] |= 0x0c; // Do not block the CPU port (bits 3:2 of byte 1 = port 9)
|
sfr_data[1] |= 0x0c; // Do not block the CPU port (bits 3:2 of byte 1 = port 9)
|
||||||
reg_write_m(RTL837X_MSTP_STATES);
|
reg_write_m(RTL837X_MSTP_STATES);
|
||||||
@@ -752,7 +939,11 @@ void stp_setup(void) __banked
|
|||||||
/* Seed the carrier bitmap, so turning STP on does not report every
|
/* Seed the carrier bitmap, so turning STP on does not report every
|
||||||
* port that was already down as a fresh topology change. */
|
* port that was already down as a fresh topology change. */
|
||||||
reg_read_m(RTL837X_REG_LINKS_STS);
|
reg_read_m(RTL837X_REG_LINKS_STS);
|
||||||
stp_link_prev = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
|
stp_link_now = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
|
||||||
|
stp_link_prev = 0;
|
||||||
|
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++)
|
||||||
|
if ((stp_link_now >> stp_i) & 1)
|
||||||
|
stp_link_prev |= (uint16_t)1 << stp_ent_of[stp_i];
|
||||||
|
|
||||||
stp_claim_root();
|
stp_claim_root();
|
||||||
|
|
||||||
@@ -767,7 +958,9 @@ void stp_off(void) __banked
|
|||||||
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
|
||||||
// Set STP port state to forwarding
|
// Set STP port state to forwarding
|
||||||
// States are: 00 disable, 01 blocking, 10 learning, 11 forwarding
|
// States are: 00 disable, 01 blocking, 10 learning, 11 forwarding
|
||||||
sfr_data[3 - (stp_i >> 2)] |= (uint8_t)(0b11 << ((stp_i << 1) & 0x7));
|
sfr_data[3 - (stp_i >> 2)] |= (uint8_t)(STP_ST_FORWARDING << ((stp_i << 1) & 0x7));
|
||||||
|
}
|
||||||
|
for (stp_i = 0; stp_i < STP_ENTITIES; stp_i++) {
|
||||||
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
|
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
|
||||||
stp_loop_held[stp_i] = 0;
|
stp_loop_held[stp_i] = 0;
|
||||||
port_timers[stp_i] = 0;
|
port_timers[stp_i] = 0;
|
||||||
@@ -782,7 +975,7 @@ void stp_off(void) __banked
|
|||||||
|
|
||||||
void stp_parse(void) __banked __reentrant
|
void stp_parse(void) __banked __reentrant
|
||||||
{
|
{
|
||||||
uint8_t port;
|
uint8_t ent;
|
||||||
|
|
||||||
if (cmd_compare(1, "on")) {
|
if (cmd_compare(1, "on")) {
|
||||||
print_string("STP enabled\n");
|
print_string("STP enabled\n");
|
||||||
@@ -803,35 +996,50 @@ void stp_parse(void) __banked __reentrant
|
|||||||
if (cmd_words_len < 3)
|
if (cmd_words_len < 3)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (cmd_compare(1, "port")) {
|
if (cmd_compare(1, "ent") || cmd_compare(1, "lag")) {
|
||||||
|
stp_lag_map();
|
||||||
if (cmd_words_len < 4)
|
if (cmd_words_len < 4)
|
||||||
goto err;
|
goto err;
|
||||||
|
if (cmd_compare(1, "lag")) {
|
||||||
|
if (!atoi_byte(cmd_words_b[2]))
|
||||||
|
goto err;
|
||||||
|
if (!atoi_results_u8 || atoi_results_u8 > STP_LAG_COUNT)
|
||||||
|
goto err;
|
||||||
|
ent = STP_LAG_BASE + atoi_results_u8 - 1;
|
||||||
|
} else {
|
||||||
if (!cmd_parse_port_separator(cmd_words_b[2]))
|
if (!cmd_parse_port_separator(cmd_words_b[2]))
|
||||||
goto err;
|
goto err;
|
||||||
port = atoi_results_u8;
|
ent = atoi_results_u8;
|
||||||
|
if (stp_ent_of[ent] != ent) {
|
||||||
|
print_string("Port belongs to a LAG, configure it as lag ");
|
||||||
|
print_byte(stp_ent_of[ent] - STP_LAG_BASE + 1);
|
||||||
|
write_char('\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (cmd_words_len < 5 && !cmd_compare(3, "on") && !cmd_compare(3, "off"))
|
if (cmd_words_len < 5 && !cmd_compare(3, "on") && !cmd_compare(3, "off"))
|
||||||
goto err;
|
goto err;
|
||||||
if (cmd_compare(3, "on")) {
|
if (cmd_compare(3, "on")) {
|
||||||
stp_pflags[port] |= STP_PF_ENABLED;
|
stp_pflags[ent] |= STP_PF_ENABLED;
|
||||||
stp_pflags[port] &= ~STP_PF_TRIPPED;
|
stp_pflags[ent] &= ~STP_PF_TRIPPED;
|
||||||
if (stp_enabled) { /* (re)join: listen first */
|
if (stp_enabled) { /* (re)join: listen first */
|
||||||
stp_state_set(port, 0b01);
|
stp_state_set(ent, STP_ST_BLOCKING);
|
||||||
port_timers[port] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
port_timers[ent] = (uint16_t)stp_fwddelay_s * STP_HZ;
|
||||||
}
|
}
|
||||||
} else if (cmd_compare(3, "off")) {
|
} else if (cmd_compare(3, "off")) {
|
||||||
stp_pflags[port] &= ~STP_PF_ENABLED;
|
stp_pflags[ent] &= ~STP_PF_ENABLED;
|
||||||
if (stp_enabled)
|
if (stp_enabled)
|
||||||
stp_state_set(port, 0b11); /* plain forwarding */
|
stp_state_set(ent, STP_ST_FORWARDING); /* plain forwarding */
|
||||||
} else if (cmd_compare(3, "edge")) {
|
} else if (cmd_compare(3, "edge")) {
|
||||||
/* Also drop the *operational* edge flag: it is what exempts the
|
/* Also drop the *operational* edge flag: it is what exempts the
|
||||||
* port from topology changes and lets it skip the listen period,
|
* ent from topology changes and lets it skip the listen period,
|
||||||
* so leaving it set would keep the old behaviour until the next
|
* so leaving it set would keep the old behaviour until the next
|
||||||
* "stp off"/"stp on". An admin edge is operational immediately. */
|
* "stp off"/"stp on". An admin edge is operational immediately. */
|
||||||
stp_pflags[port] &= ~(STP_PF_ADMEDGE | STP_PF_AUTOEDGE | STP_PF_OPEREDGE);
|
stp_pflags[ent] &= ~(STP_PF_ADMEDGE | STP_PF_AUTOEDGE | STP_PF_OPEREDGE);
|
||||||
if (cmd_compare(4, "on"))
|
if (cmd_compare(4, "on"))
|
||||||
stp_pflags[port] |= STP_PF_ADMEDGE | STP_PF_OPEREDGE;
|
stp_pflags[ent] |= STP_PF_ADMEDGE | STP_PF_OPEREDGE;
|
||||||
else if (cmd_compare(4, "auto"))
|
else if (cmd_compare(4, "auto"))
|
||||||
stp_pflags[port] |= STP_PF_AUTOEDGE;
|
stp_pflags[ent] |= STP_PF_AUTOEDGE;
|
||||||
else if (!cmd_compare(4, "off"))
|
else if (!cmd_compare(4, "off"))
|
||||||
goto err;
|
goto err;
|
||||||
} else if (cmd_compare(3, "cost")) {
|
} else if (cmd_compare(3, "cost")) {
|
||||||
@@ -848,14 +1056,14 @@ void stp_parse(void) __banked __reentrant
|
|||||||
}
|
}
|
||||||
if (stp_cost_scratch > 200000000UL)
|
if (stp_cost_scratch > 200000000UL)
|
||||||
goto err;
|
goto err;
|
||||||
stp_pcost[port] = stp_cost_scratch;
|
stp_pcost[ent] = stp_cost_scratch;
|
||||||
} else if (cmd_compare(3, "p2p")) {
|
} else if (cmd_compare(3, "p2p")) {
|
||||||
if (cmd_compare(4, "auto"))
|
if (cmd_compare(4, "auto"))
|
||||||
stp_pp2p[port] = 0;
|
stp_pp2p[ent] = 0;
|
||||||
else if (cmd_compare(4, "on"))
|
else if (cmd_compare(4, "on"))
|
||||||
stp_pp2p[port] = 1;
|
stp_pp2p[ent] = 1;
|
||||||
else if (cmd_compare(4, "off"))
|
else if (cmd_compare(4, "off"))
|
||||||
stp_pp2p[port] = 2;
|
stp_pp2p[ent] = 2;
|
||||||
else
|
else
|
||||||
goto err;
|
goto err;
|
||||||
} else if (cmd_compare(3, "prio")) {
|
} else if (cmd_compare(3, "prio")) {
|
||||||
@@ -863,20 +1071,20 @@ void stp_parse(void) __banked __reentrant
|
|||||||
goto err;
|
goto err;
|
||||||
if (atoi_results_u8 > 240 || (atoi_results_u8 & 0x0f))
|
if (atoi_results_u8 > 240 || (atoi_results_u8 & 0x0f))
|
||||||
goto err;
|
goto err;
|
||||||
stp_pprio[port] = atoi_results_u8;
|
stp_pprio[ent] = atoi_results_u8;
|
||||||
} else if (cmd_compare(3, "guard")) {
|
} else if (cmd_compare(3, "guard")) {
|
||||||
stp_pflags[port] &= ~(STP_PF_BPDUGUARD | STP_PF_ROOTGUARD);
|
stp_pflags[ent] &= ~(STP_PF_BPDUGUARD | STP_PF_ROOTGUARD);
|
||||||
if (cmd_compare(4, "bpdu"))
|
if (cmd_compare(4, "bpdu"))
|
||||||
stp_pflags[port] |= STP_PF_BPDUGUARD;
|
stp_pflags[ent] |= STP_PF_BPDUGUARD;
|
||||||
else if (cmd_compare(4, "root"))
|
else if (cmd_compare(4, "root"))
|
||||||
stp_pflags[port] |= STP_PF_ROOTGUARD;
|
stp_pflags[ent] |= STP_PF_ROOTGUARD;
|
||||||
else if (!cmd_compare(4, "none"))
|
else if (!cmd_compare(4, "none"))
|
||||||
goto err;
|
goto err;
|
||||||
} else if (cmd_compare(3, "filter")) {
|
} else if (cmd_compare(3, "filter")) {
|
||||||
if (cmd_compare(4, "on"))
|
if (cmd_compare(4, "on"))
|
||||||
stp_pflags[port] |= STP_PF_FILTER;
|
stp_pflags[ent] |= STP_PF_FILTER;
|
||||||
else if (cmd_compare(4, "off"))
|
else if (cmd_compare(4, "off"))
|
||||||
stp_pflags[port] &= ~STP_PF_FILTER;
|
stp_pflags[ent] &= ~STP_PF_FILTER;
|
||||||
else
|
else
|
||||||
goto err;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
@@ -926,5 +1134,5 @@ void stp_parse(void) __banked __reentrant
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
print_string("Error: stp on|off|status | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | port <1-9> on|off|edge|cost|prio|guard|filter ...\n");
|
print_string("Error: stp on|off|status | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | ent <1-9>|lag <1-4> on|off|edge|cost|prio|guard|filter ...\n");
|
||||||
}
|
}
|
||||||
+18
-8
@@ -2,6 +2,7 @@
|
|||||||
#define _RTL837X_STP_H_
|
#define _RTL837X_STP_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "rtl837x_common.h"
|
||||||
void stp_in(void) __banked;
|
void stp_in(void) __banked;
|
||||||
void stp_setup(void) __banked;
|
void stp_setup(void) __banked;
|
||||||
void stp_timers(void) __banked;
|
void stp_timers(void) __banked;
|
||||||
@@ -12,6 +13,11 @@ void stp_defaults(void) __banked;
|
|||||||
/* Tick rate of stp_timers(), also used by the web UI. */
|
/* Tick rate of stp_timers(), also used by the web UI. */
|
||||||
#define STP_HZ 50
|
#define STP_HZ 50
|
||||||
|
|
||||||
|
#define STP_PORTS (CPU_PORT + 1)
|
||||||
|
#define STP_LAG_BASE STP_PORTS
|
||||||
|
#define STP_LAG_COUNT 4
|
||||||
|
#define STP_ENTITIES (STP_LAG_BASE + STP_LAG_COUNT)
|
||||||
|
|
||||||
/* Bridge identifier as carried in a BPDU (priority, extension, MAC). */
|
/* Bridge identifier as carried in a BPDU (priority, extension, MAC). */
|
||||||
struct bridge {
|
struct bridge {
|
||||||
uint8_t prio;
|
uint8_t prio;
|
||||||
@@ -36,15 +42,19 @@ extern __xdata uint8_t stp_txhold;
|
|||||||
#define STP_PF_OPEREDGE 0x40 /* runtime: port went forwarding as an edge */
|
#define STP_PF_OPEREDGE 0x40 /* runtime: port went forwarding as an edge */
|
||||||
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
|
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
|
||||||
|
|
||||||
extern __xdata uint8_t stp_pflags[10];
|
extern __xdata uint8_t stp_pflags[STP_ENTITIES];
|
||||||
extern __xdata uint32_t stp_pcost[10];
|
extern __xdata uint32_t stp_pcost[STP_ENTITIES]; /* path cost; 0 = auto (20000) */
|
||||||
extern __xdata uint8_t stp_pprio[10];
|
extern __xdata uint8_t stp_pprio[STP_ENTITIES];
|
||||||
extern __xdata uint8_t stp_pp2p[10];
|
extern __xdata uint8_t stp_pp2p[STP_ENTITIES];
|
||||||
|
extern __xdata uint8_t stp_ent_of[10]; /* the STP entity a port answers to: itself, or its lag */
|
||||||
|
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT]; /* member ports of each lag; 0 = not part of STP */
|
||||||
|
|
||||||
extern __xdata struct bridge stp_dbridge[10];
|
/* Last-heard designated info per port (from received BPDUs); consult
|
||||||
extern __xdata uint16_t stp_dpid[10];
|
* stp_bpdu_age to decide whether it is still current. */
|
||||||
extern __xdata uint32_t stp_dcost[10];
|
extern __xdata struct bridge stp_dbridge[STP_ENTITIES];
|
||||||
extern __xdata uint16_t stp_bpdu_age[10];
|
extern __xdata uint16_t stp_dpid[STP_ENTITIES];
|
||||||
|
extern __xdata uint32_t stp_dcost[STP_ENTITIES];
|
||||||
|
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES]; /* ticks since a BPDU was heard */
|
||||||
|
|
||||||
extern __xdata struct bridge root_bridge;
|
extern __xdata struct bridge root_bridge;
|
||||||
extern __xdata uint32_t root_bridge_cost;
|
extern __xdata uint32_t root_bridge_cost;
|
||||||
|
|||||||
Reference in new issue
Block a user