mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
test/stp: pack host structs like SDCC and cover the edge recovery
Build the harness with -fpack-struct=1 and assert the frame struct sizes, so the host reads wire bytes the way the firmware lays them out. Add scenarios for a port that regains edge after a hold, one that heard a BPDU and stays non-edge, auto-edge switched off, and a captured RouterOS BPDU fed in on every port, tagged and untagged.
This commit is contained in:
1 parent
4d009bc8e8
commit
970e98f4c5
2 files changed
+166
No files matched your search
@@ -2,6 +2,7 @@ CC ?= cc
|
||||
SRCROOT = ../..
|
||||
CFLAGS = -std=c11 -g -O1 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas \
|
||||
-Wno-pointer-sign \
|
||||
-fpack-struct=1 \
|
||||
-DRTLP_HOST_TEST \
|
||||
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
||||
-include ../sdcc_shim.h -I../stub -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "machine.h"
|
||||
@@ -196,6 +197,10 @@ struct sim_pkt_out {
|
||||
uint16_t fwd_delay;
|
||||
uint8_t version1_length;
|
||||
};
|
||||
/* SDCC uses byte alignment; both firmware and fixtures must match wire
|
||||
* offsets rather than sharing the host compiler's padding. */
|
||||
_Static_assert(sizeof(struct sim_pkt_out) == 61, "RST BPDU with CPU tag must be 61 bytes");
|
||||
_Static_assert(offsetof(struct sim_pkt_out, root) == 30, "root identifier wire offset");
|
||||
static struct sim_pkt_out last_tx[NPORTS];
|
||||
static int tc_frames[NPORTS];
|
||||
static int tcn_frames[NPORTS];
|
||||
@@ -310,6 +315,7 @@ struct sim_pkt_in {
|
||||
uint16_t fwd_delay;
|
||||
uint8_t version1_length;
|
||||
};
|
||||
_Static_assert(sizeof(struct sim_pkt_in) == 65, "VLAN-tagged RX BPDU must be 65 bytes");
|
||||
|
||||
static uint32_t swap32(uint32_t v)
|
||||
{
|
||||
@@ -2131,6 +2137,161 @@ static void scen_root_port_needs_link(void)
|
||||
check_state(0, 3, "and forwards");
|
||||
}
|
||||
|
||||
static void follow_root(unsigned seconds)
|
||||
{
|
||||
struct sim_bpdu root = root_on(8);
|
||||
while (seconds--) {
|
||||
bpdu_in(&root);
|
||||
secs(1);
|
||||
}
|
||||
}
|
||||
|
||||
static struct sim_bpdu loop_from_port8(void)
|
||||
{
|
||||
struct sim_bpdu own = {
|
||||
.port = 0, .root_prio = 0x40, .root_cost = 20000,
|
||||
.br_prio = 0x80, .port_id = 8
|
||||
};
|
||||
memcpy(own.root_mac, ROOT_MAC, 6);
|
||||
memcpy(own.br_mac, uip_ethaddr.addr, 6);
|
||||
return own;
|
||||
}
|
||||
|
||||
static void loop_ports_ready(void)
|
||||
{
|
||||
reset_all();
|
||||
links_set((1 << 0) | (1 << 7) | (1 << 8));
|
||||
follow_root(5);
|
||||
}
|
||||
|
||||
static void scen_loop_peer_edge_recovery(void)
|
||||
{
|
||||
struct sim_bpdu own = loop_from_port8();
|
||||
int held = 1;
|
||||
|
||||
printf("55. a silent loop peer regains auto-edge when the loop hold expires\n");
|
||||
loop_ports_ready();
|
||||
check((stp_pflags[0] & STP_PF_OPEREDGE) && (stp_pflags[7] & STP_PF_OPEREDGE),
|
||||
"both host-facing ports start as edge");
|
||||
bpdu_in(&own);
|
||||
check_state(7, 1, "a frame from port 8 returning on port 1 blocks port 8");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE) && stp_cnt[STP_CNT_RX][7] == 0,
|
||||
"the held peer is non-edge even though it received no BPDU itself");
|
||||
for (int i = 0; i < 20; i++) {
|
||||
follow_root(2);
|
||||
held &= port_state(7) == 1 && !(stp_pflags[7] & STP_PF_OPEREDGE);
|
||||
bpdu_in(&own);
|
||||
}
|
||||
check(held, "repeated returned BPDUs keep the peer blocked and non-edge");
|
||||
follow_root(14);
|
||||
check_state(7, 1, "the peer waits its full initial forward delay");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE), "auto-edge cannot bypass the loop hold");
|
||||
follow_root(14);
|
||||
check_state(7, 2, "the peer then spends another forward delay learning");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE), "the learning peer is still non-edge");
|
||||
follow_root(3);
|
||||
check_state(7, 3, "the peer forwards once both forward delays have elapsed");
|
||||
check((stp_pflags[7] & STP_PF_OPEREDGE) && stp_cnt[STP_CNT_RX][7] == 0,
|
||||
"the silent peer regains edge with its RX counter still zero");
|
||||
check(!(stp_pflags[0] & STP_PF_OPEREDGE) && stp_cnt[STP_CNT_RX][0] == 21,
|
||||
"only the port that actually received the returned BPDUs counts and remembers them");
|
||||
bpdu_in(&own);
|
||||
check_state(7, 1, "a new returned BPDU blocks the recovered peer again");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE), "the new loop revokes the recovered edge status");
|
||||
}
|
||||
|
||||
static void scen_loop_peer_heard_bpdu(void)
|
||||
{
|
||||
struct sim_bpdu own = loop_from_port8();
|
||||
struct sim_bpdu peer = {
|
||||
.port = 7, .root_prio = 0x40, .root_cost = 200000,
|
||||
.br_prio = 0x90, .port_id = 1, .flags = 0x0c
|
||||
};
|
||||
|
||||
printf("56. a loop peer that received a real BPDU remains non-edge\n");
|
||||
loop_ports_ready();
|
||||
bpdu_in(&own);
|
||||
memcpy(peer.root_mac, ROOT_MAC, 6);
|
||||
memcpy(peer.br_mac, PEER_MAC, 6);
|
||||
bpdu_in(&peer);
|
||||
follow_root(31);
|
||||
check_state(7, 3, "the loop hold still expires after the full forward delays");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE) && stp_cnt[STP_CNT_RX][7] == 1,
|
||||
"a real received BPDU prevents the peer from becoming edge");
|
||||
}
|
||||
|
||||
static void scen_loop_peer_autoedge_off(void)
|
||||
{
|
||||
struct sim_bpdu own = loop_from_port8();
|
||||
|
||||
printf("57. loop recovery respects administratively disabled auto-edge\n");
|
||||
reset_all();
|
||||
stp_pflags[7] &= ~STP_PF_AUTOEDGE;
|
||||
links_set((1 << 0) | (1 << 7) | (1 << 8));
|
||||
follow_root(31);
|
||||
bpdu_in(&own);
|
||||
follow_root(31);
|
||||
check_state(7, 3, "the peer forwards again after loop recovery");
|
||||
check(!(stp_pflags[7] & STP_PF_OPEREDGE) && stp_cnt[STP_CNT_RX][7] == 0,
|
||||
"zero RX does not override the disabled auto-edge setting");
|
||||
}
|
||||
|
||||
/* Actual captured RouterOS bytes; only the redacted MAC octets are replaced. */
|
||||
static const uint8_t mikrotik_wire[] = {
|
||||
0x01, 0x80, 0xc2, 0x00, 0x00, 0x00, 0xd0, 0xea,
|
||||
0x11, 0x12, 0x34, 0x56, 0x00, 0x27, 0x42, 0x42,
|
||||
0x03, 0x00, 0x00, 0x02, 0x02, 0x3c, 0x10, 0x00,
|
||||
0xd0, 0xea, 0x11, 0x12, 0x34, 0x56, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0xd0, 0xea, 0x11, 0x12,
|
||||
0x34, 0x56, 0x80, 0x0a, 0x00, 0x00, 0x14, 0x00,
|
||||
0x02, 0x00, 0x0f, 0x00, 0x00
|
||||
};
|
||||
|
||||
static void bpdu_wire(uint8_t port, int tagged)
|
||||
{
|
||||
size_t body = 20;
|
||||
|
||||
memset(uip_buf, 0, sizeof(uip_buf));
|
||||
memcpy(uip_buf, mikrotik_wire, 12);
|
||||
uip_buf[12] = 0x88;
|
||||
uip_buf[13] = 0x99;
|
||||
uip_buf[14] = 0x04;
|
||||
uip_buf[19] = port;
|
||||
if (tagged) {
|
||||
uip_buf[20] = 0x81;
|
||||
uip_buf[23] = 1;
|
||||
body += 4;
|
||||
}
|
||||
memcpy(uip_buf + body, mikrotik_wire + 12, sizeof(mikrotik_wire) - 12);
|
||||
uip_len = body + sizeof(mikrotik_wire) - 12;
|
||||
}
|
||||
|
||||
static void scen_mikrotik_wire_capture(void)
|
||||
{
|
||||
int attributed = 1;
|
||||
|
||||
printf("58. the captured RouterOS BPDU is parsed on every physical ingress port\n");
|
||||
for (int tagged = 0; tagged < 2; tagged++) {
|
||||
for (uint8_t port = 0; port < NPORTS; port++) {
|
||||
reset_all();
|
||||
links_set((1 << NPORTS) - 1);
|
||||
secs(5);
|
||||
bpdu_wire(port, tagged);
|
||||
/* The upper source-word bits and trap reason are not port bits. */
|
||||
uip_buf[15] = 0x04;
|
||||
uip_buf[18] = 0xfe;
|
||||
uip_buf[19] |= 0xa0;
|
||||
stp_in();
|
||||
attributed &= stp_root_port == port && root_bridge.prio == 0x10
|
||||
&& stp_dpid[port] == 0x800a;
|
||||
for (uint8_t p = 0; p < NPORTS; p++)
|
||||
attributed &= stp_cnt[STP_CNT_RX][p] == (p == port)
|
||||
&& !!(stp_pflags[p] & STP_PF_OPEREDGE) == (p != port);
|
||||
}
|
||||
}
|
||||
check(attributed, "both VLAN layouts select the captured root and change RX and edge status only on the ingress port");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
verbose = argc > 1 && argv[1][0] == '-' ? (argv[1][1] == 'd' ? 2 : 1) : 0;
|
||||
@@ -2188,6 +2349,10 @@ int main(int argc, char **argv)
|
||||
scen_lag();
|
||||
scen_tc_needs_forwarding();
|
||||
scen_root_port_needs_link();
|
||||
scen_loop_peer_edge_recovery();
|
||||
scen_loop_peer_heard_bpdu();
|
||||
scen_loop_peer_autoedge_off();
|
||||
scen_mikrotik_wire_capture();
|
||||
if (failures) {
|
||||
printf("\n%d check(s) failed\n", failures);
|
||||
return 1;
|
||||
|
||||
Reference in new issue
Block a user