Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion include/stp.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
#define g_stp_protect_mask stp_global.protect_mask
#define g_stp_protect_do_disable_mask stp_global.protect_do_disable_mask
#define g_stp_root_protect_mask stp_global.root_protect_mask
#define g_stp_loop_protect_mask stp_global.loop_protect_mask
#define g_stp_protect_disabled_mask stp_global.protect_disabled_mask
#define g_stp_enable_mask stp_global.enable_mask
#define g_stp_enable_config_mask stp_global.enable_admin_mask
Expand All @@ -120,6 +121,7 @@
#define STP_IS_PROTECT_DO_DISABLE_CONFIGURED(_port_) IS_MEMBER(stp_global.protect_do_disable_mask, (_port_))
#define STP_IS_PROTECT_DO_DISABLED(_port_) IS_MEMBER(stp_global.protect_disabled_mask, (_port_))
#define STP_IS_ROOT_PROTECT_CONFIGURED(_p_) IS_MEMBER(stp_global.root_protect_mask, (_p_))
#define STP_IS_LOOP_PROTECT_CONFIGURED(_p_) IS_MEMBER(stp_global.loop_protect_mask, (_p_))
#define STP_IS_PROTOCOL_ENABLED(mode) (stp_global.enable == true && stp_global.proto_mode == mode)

// _port_ is a pointer to either STP_PORT_CLASS or RSTP_PORT_CLASS
Expand Down Expand Up @@ -303,6 +305,8 @@ typedef struct
TIMER forward_delay_timer;
TIMER hold_timer;
TIMER root_protect_timer;
bool loop_guard_active;
bool loop_guard_synced;

UINT32 forward_transitions;
UINT32 rx_config_bpdu;
Expand Down Expand Up @@ -334,7 +338,8 @@ typedef struct
#define STP_PORT_CLASS_PORT_FAST_BIT 14
#define STP_PORT_CLASS_ROOT_PROTECT_BIT 15
#define STP_PORT_CLASS_BPDU_PROTECT_BIT 16
#define STP_PORT_CLASS_CLEAR_STATS_BIT 17
#define STP_PORT_CLASS_LOOP_PROTECT_BIT 17
#define STP_PORT_CLASS_CLEAR_STATS_BIT 18
UINT32 modified_fields;
} __attribute__((aligned(4))) STP_PORT_CLASS;

Expand Down Expand Up @@ -371,6 +376,7 @@ typedef struct
PORT_MASK *protect_do_disable_mask;
PORT_MASK *protect_disabled_mask;
PORT_MASK *root_protect_mask;
PORT_MASK *loop_protect_mask;
uint16_t root_protect_timeout;
L2_PROTO_MODE proto_mode;

Expand All @@ -391,6 +397,7 @@ typedef enum
STP_RAS_MES_AGE_TIMER_EXPIRY,
STP_RAS_ROOT_PROTECT_TIMER_EXPIRY,
STP_RAS_ROOT_PROTECT_VIOLATION,
STP_RAS_LOOP_PROTECT_VIOLATION,
STP_RAS_ROOT_ROLE,
STP_RAS_DESIGNATED_ROLE,
STP_RAS_MP_RX_DELAY_EVENT,
Expand Down
1 change: 1 addition & 0 deletions include/stp_dbsync.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ typedef struct {
uint32_t tx_tcn_bpdu;
uint32_t rx_tcn_bpdu;
uint32_t root_protect_timer;
uint8_t loop_guard_active;

uint8_t clear_stats;
uint32_t modified_fields;
Expand Down
1 change: 1 addition & 0 deletions include/stp_ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ typedef struct STP_PORT_CONFIG_MSG
char intf_name[IFNAMSIZ];
uint8_t enabled;
uint8_t root_guard;
uint8_t loop_guard;
uint8_t bpdu_guard;
uint8_t bpdu_guard_do_disable;
uint8_t portfast;
Expand Down
25 changes: 23 additions & 2 deletions stp/stp.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ void make_forwarding(STP_CLASS *stp_class, PORT_ID port_number)
{
STP_PORT_CLASS *stp_port_class = GET_STP_PORT_CLASS(stp_class, port_number);

if ((stp_port_class->state == BLOCKING) && (!is_timer_active(&stp_port_class->root_protect_timer)))
if ((stp_port_class->state == BLOCKING) && (!is_timer_active(&stp_port_class->root_protect_timer)) && (!stp_port_class->loop_guard_active))
{

stp_port_class->state = LISTENING;
Expand Down Expand Up @@ -726,6 +726,24 @@ void message_age_timer_expiry(STP_CLASS *stp_class, PORT_ID port_number)
stp_port_class = GET_STP_PORT_CLASS(stp_class, port_number);
stp_port_class->self_loop = false;

/* Loop Guard: if configured on a non-designated port, enter loop-inconsistent
* state instead of allowing the port to become designated/forwarding */
if (STP_IS_LOOP_PROTECT_CONFIGURED(port_number) &&
!designated_port(stp_class, port_number))
{
stp_port_class->loop_guard_active = true;
stp_port_class->loop_guard_synced = false;
stp_port_class->state = BLOCKING;
stputil_set_port_state(stp_class, stp_port_class);
SET_BIT(stp_port_class->modified_fields, STP_PORT_CLASS_LOOP_PROTECT_BIT);
SET_BIT(stp_port_class->modified_fields, STP_PORT_CLASS_MEMBER_PORT_STATE_BIT);
STP_SYSLOG("STP: Loop Guard interface %s, VLAN %u loop-inconsistent (BPDUs lost)",
stp_intf_get_port_name(port_number), stp_class->vlan_id);
STP_LOG_INFO("STP_RAS_LOOP_PROTECT_VIOLATION I:%lu P:%lu V:%u",
GET_STP_INDEX(stp_class), port_number, stp_class->vlan_id);
return;
}

root = root_bridge(stp_class);
become_designated_port(stp_class, port_number);
configuration_update(stp_class);
Expand Down Expand Up @@ -876,6 +894,9 @@ void send_tcn_bpdu(STP_CLASS* stp_class, PORT_ID port_number)

if (stptimer_is_active(&stp_port_class->root_protect_timer))
return;


if (stp_port_class->loop_guard_active)
return;

stputil_send_pvst_bpdu(stp_class, port_number, TCN_BPDU_TYPE);
}
1 change: 1 addition & 0 deletions stp/stp_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int8_t stpdata_init_global_port_mask()
ret |= bmp_alloc(&g_stp_protect_mask, g_max_stp_port);
ret |= bmp_alloc(&g_stp_protect_do_disable_mask, g_max_stp_port);
ret |= bmp_alloc(&g_stp_root_protect_mask, g_max_stp_port);
ret |= bmp_alloc(&g_stp_loop_protect_mask, g_max_stp_port);
ret |= bmp_alloc(&g_stp_protect_disabled_mask, g_max_stp_port);

/* By default fast span is enabled on all ports */
Expand Down
6 changes: 6 additions & 0 deletions stp/stp_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,15 @@ void stpdm_global()
UINT8 enable_string[500], enable_admin_string[500];
UINT8 fastspan_string[500], fastspan_admin_string[500], fastuplink_admin_string[500];
UINT8 protect_string[500], protect_do_disable_string[500], protect_disabled_string[500], root_protect_string[500];
UINT8 loop_protect_string[500];

mask_to_string(g_stp_enable_mask, enable_string, 500);
mask_to_string(g_stp_enable_config_mask, enable_admin_string, 500);
mask_to_string(stp_global.protect_mask, protect_string, sizeof(protect_string));
mask_to_string(stp_global.protect_do_disable_mask, protect_do_disable_string, sizeof(protect_do_disable_string));
mask_to_string(stp_global.protect_disabled_mask, protect_disabled_string, sizeof(protect_disabled_string));
mask_to_string(stp_global.root_protect_mask, root_protect_string, sizeof(root_protect_string));
mask_to_string(stp_global.loop_protect_mask, loop_protect_string, sizeof(loop_protect_string));
mask_to_string(g_fastspan_mask, fastspan_string, 500);
mask_to_string(g_fastspan_config_mask, fastspan_admin_string, 500);
mask_to_string(g_fastuplink_mask, fastuplink_admin_string, 500);
Expand All @@ -151,6 +153,7 @@ void stpdm_global()
"protect_do_disable_mask= %s\n\t"
"protect_disabled_mask = %s\n\t"
"root_protect_mask = %s\n\t"
"loop_protect_mask = %s\n\t"
"root_protect_timeout = %u\n\t"
"fastspan_mask = %s\n\t"
"fastspan_admin_mask = %s\n\t"
Expand All @@ -176,6 +179,7 @@ void stpdm_global()
protect_do_disable_string,
protect_disabled_string,
root_protect_string,
loop_protect_string,
stp_global.root_protect_timeout,
fastspan_string,
fastspan_admin_string,
Expand Down Expand Up @@ -289,6 +293,7 @@ void stpdm_port_class(STP_CLASS *stp_class, PORT_ID port_number)
"forward_delay_timer = %s %d\n"
"hold timer = %s %d\n"
"root_protect_timer = %s %u\n"
"loop_guard_active = %s\n"
"forward_transitions = %d\n"
"rx_config_bpdu = %d\n"
"tx_config_bpdu = %d\n"
Expand All @@ -313,6 +318,7 @@ void stpdm_port_class(STP_CLASS *stp_class, PORT_ID port_number)
stp_port->hold_timer.value,
STP_TIMER_STRING(&stp_port->root_protect_timer),
stp_port->root_protect_timer.value,
stp_port->loop_guard_active ? "YES" : "NO",
stp_port->forward_transitions,
stp_port->rx_config_bpdu,
stp_port->tx_config_bpdu,
Expand Down
29 changes: 25 additions & 4 deletions stp/stp_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ void stpmgr_disable_port(STP_CLASS *stp_class, PORT_ID port_number)
stptimer_stop(&stp_port_class->root_protect_timer);
}

if (stp_port_class->loop_guard_active)
{
stp_port_class->loop_guard_active = false;
}

clear_mask_bit(stp_class->enable_mask, port_number);
configuration_update(stp_class);
port_state_selection(stp_class);
Expand Down Expand Up @@ -1249,6 +1254,20 @@ bool stpmgr_config_root_protect(PORT_ID port_id, bool enable)
return true;
}

/*****************************************************************************/
/* stpmgr_config_loop_protect: enables/disables loop-guard feature on the */
/* input port. */
/*****************************************************************************/
static bool stpmgr_config_loop_protect(PORT_ID port_id, bool enable)
{
if (enable)
set_mask_bit(stp_global.loop_protect_mask, port_id);
else
clear_mask_bit(stp_global.loop_protect_mask, port_id);

return true;
}

/*****************************************************************************/
/* stpmgr_config_root_protect_timeout: configures the timeout in seconds for */
/* which the violated stp port is kept in blocking state. */
Expand Down Expand Up @@ -1717,9 +1736,9 @@ static void stpmgr_process_intf_config_msg(void *msg)
return;
}

STP_LOG_INFO("op:%d, intf:%s, enable:%d, root_grd:%d, bpdu_grd:%d , do_dis:%d, cost:%d, pri:%d, portfast:%d, uplink_fast:%d, count:%d",
pmsg->opcode, pmsg->intf_name, pmsg->enabled, pmsg->root_guard, pmsg->bpdu_guard,
pmsg->bpdu_guard_do_disable, pmsg->path_cost, pmsg->priority,
STP_LOG_INFO("op:%d, intf:%s, enable:%d, root_grd:%d, loop_grd:%d, bpdu_grd:%d , do_dis:%d, cost:%d, pri:%d, portfast:%d, uplink_fast:%d, count:%d",
pmsg->opcode, pmsg->intf_name, pmsg->enabled, pmsg->root_guard, pmsg->loop_guard, pmsg->bpdu_guard,
pmsg->bpdu_guard_do_disable, pmsg->path_cost, pmsg->priority,
pmsg->portfast, pmsg->uplink_fast, pmsg->count);

port_id = stp_intf_get_port_id_by_name(pmsg->intf_name);
Expand Down Expand Up @@ -1771,8 +1790,9 @@ static void stpmgr_process_intf_config_msg(void *msg)
if (pmsg->enabled)
{
stpmgr_config_root_protect(port_id, pmsg->root_guard);
stpmgr_config_loop_protect(port_id, pmsg->loop_guard);
stpmgr_config_protect(port_id, pmsg->bpdu_guard, pmsg->bpdu_guard_do_disable);

stpmgr_config_fastspan(port_id, pmsg->portfast);
stpmgr_config_fastuplink(port_id, pmsg->uplink_fast);
}
Expand All @@ -1788,6 +1808,7 @@ static void stpmgr_process_intf_config_msg(void *msg)
if (pmsg->opcode == STP_DEL_COMMAND || !pmsg->enabled)
{
stpmgr_config_root_protect(port_id, false);
stpmgr_config_loop_protect(port_id, false);
stpmgr_config_protect(port_id, false, false);

stpmgr_config_fastspan(port_id, true);
Expand Down
73 changes: 67 additions & 6 deletions stp/stp_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,27 @@ static bool stputil_root_protect_violation(STP_CLASS *stp_class, PORT_ID port_nu
return true;
}


/*****************************************************************************/
/* stputil_loop_guard_bpdu_recovery: called when a BPDU is received on a */
/* loop-inconsistent port. clears the loop guard active flag, logs recovery, */
/* and marks the port state as modified. */
/*****************************************************************************/
static void stputil_loop_guard_bpdu_recovery(STP_CLASS *stp_class, PORT_ID port_number)
{
STP_PORT_CLASS *stp_port;

stp_port = GET_STP_PORT_CLASS(stp_class, port_number);

STP_SYSLOG("STP: Loop Guard interface %s, VLAN %u consistent (BPDU received)",
stp_intf_get_port_name(port_number), stp_class->vlan_id);

stp_port->loop_guard_active = false;
stp_port->loop_guard_synced = false;
SET_BIT(stp_port->modified_fields, STP_PORT_CLASS_LOOP_PROTECT_BIT);
SET_BIT(stp_port->modified_fields, STP_PORT_CLASS_MEMBER_PORT_STATE_BIT);
}

/*****************************************************************************/
/* stputil_root_protect_validate: routine validates the bpdu to see that it */
/* is conforming to the root protect configuration. */
Expand Down Expand Up @@ -779,6 +800,16 @@ void stputil_process_bpdu(STP_INDEX stp_index, PORT_ID port_number, void *buffer
return;
}

// Loop Guard: if port is in loop-inconsistent state and receives a BPDU, recover
if (STP_IS_LOOP_PROTECT_CONFIGURED(port_number))
{
STP_PORT_CLASS *stp_port_lg = GET_STP_PORT_CLASS(stp_class, port_number);
if (stp_port_lg->loop_guard_active)
{
stputil_loop_guard_bpdu_recovery(stp_class, port_number);
}
}

last_bpdu_rx_time = stp_class->last_bpdu_rx_time;
current_time = sys_get_seconds();
stp_class->last_bpdu_rx_time = current_time;
Expand Down Expand Up @@ -898,7 +929,12 @@ void stptimer_sync_port_class(STP_CLASS *stp_class, STP_PORT_CLASS * stp_port)
if(timer_value != 0 && stp_port->state == BLOCKING)
strcpy(stp_vlan_intf.port_state, "ROOT-INC");
else
strcpy(stp_vlan_intf.port_state, l2_port_state_to_string(stp_port->state, stp_port->port_id.number));
{
if(stp_port->loop_guard_active && stp_port->state == BLOCKING)
strcpy(stp_vlan_intf.port_state, "LOOP-INC");
else
strcpy(stp_vlan_intf.port_state, l2_port_state_to_string(stp_port->state, stp_port->port_id.number));
}

if (stp_port->state == DISABLED)
{
Expand Down Expand Up @@ -965,6 +1001,16 @@ void stptimer_sync_port_class(STP_CLASS *stp_class, STP_PORT_CLASS * stp_port)
stp_vlan_intf.root_protect_timer = -1;
}

if(IS_BIT_SET(stp_port->modified_fields, STP_PORT_CLASS_LOOP_PROTECT_BIT))
{
stp_vlan_intf.loop_guard_active = stp_port->loop_guard_active ? 1 : 0;
stp_port->loop_guard_synced = true;
}
else
{
stp_vlan_intf.loop_guard_active = -1;
}

if(IS_BIT_SET(stp_port->modified_fields, STP_PORT_CLASS_CLEAR_STATS_BIT))
{
stp_vlan_intf.clear_stats = 1;
Expand Down Expand Up @@ -1120,7 +1166,10 @@ void stputil_sync_port_counters(STP_CLASS *stp_class, STP_PORT_CLASS * stp_port)

if (is_timer_active(&stp_port->root_protect_timer))
SET_BIT(stp_port->modified_fields, STP_PORT_CLASS_ROOT_PROTECT_BIT);


if (stp_port->loop_guard_active && !stp_port->loop_guard_synced)
SET_BIT(stp_port->modified_fields, STP_PORT_CLASS_LOOP_PROTECT_BIT);

stptimer_sync_port_class(stp_class, stp_port);
}

Expand Down Expand Up @@ -1302,23 +1351,36 @@ void stptimer_update(STP_CLASS *stp_class)
(stp_port_class->root_protect_timer.active && !STP_IS_ROOT_PROTECT_CONFIGURED(port_number)))
{
stp_port_class->root_protect_timer.active = false;
stputil_root_protect_timer_expired(stp_class, port_number);
stputil_root_protect_timer_expired(stp_class, port_number);

if (debugGlobal.stp.enabled)
{
if (STP_DEBUG_VP(stp_class->vlan_id, port_number))
{
STP_LOG_INFO("I:%lu P:%u V:%u Ev:%d",GET_STP_INDEX(stp_class),port_number,
STP_LOG_INFO("I:%lu P:%u V:%u Ev:%d",GET_STP_INDEX(stp_class),port_number,
stp_class->vlan_id,STP_RAS_ROOT_PROTECT_TIMER_EXPIRY);
}
}
else
{
STP_LOG_INFO("I:%lu P:%u V:%u Ev:%d",GET_STP_INDEX(stp_class),port_number,
STP_LOG_INFO("I:%lu P:%u V:%u Ev:%d",GET_STP_INDEX(stp_class),port_number,
stp_class->vlan_id,STP_RAS_ROOT_PROTECT_TIMER_EXPIRY);
}
}

/* Loop guard config can be removed while the port is already loop-inconsistent.
* In that case, clear loop-guard state and run the same recovery path used on
* message-age expiry so the port can be re-evaluated immediately. */
if (stp_port_class->loop_guard_active && !STP_IS_LOOP_PROTECT_CONFIGURED(port_number))
{
stp_port_class->loop_guard_active = false;
stp_port_class->loop_guard_synced = false;
SET_BIT(stp_port_class->modified_fields, STP_PORT_CLASS_LOOP_PROTECT_BIT);
SET_BIT(stp_port_class->modified_fields, STP_PORT_CLASS_MEMBER_PORT_STATE_BIT);

message_age_timer_expiry(stp_class, port_number);
}

port_number = port_mask_get_next_port(stp_class->enable_mask, port_number);
}

Expand Down Expand Up @@ -1441,4 +1503,3 @@ void sys_assert(int status)
{
assert(status);
}

6 changes: 6 additions & 0 deletions stpsync/stp_sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,12 @@ void StpSync::updateStpVlanInterfaceInfo(STP_VLAN_PORT_TABLE * stp_vlan_intf)
fvVector.push_back(rpt);
}

if(stp_vlan_intf->loop_guard_active != 0xFF)
{
FieldValueTuple lga("loop_guard_active", to_string(stp_vlan_intf->loop_guard_active));
fvVector.push_back(lga);
}

vlan = VLAN_PREFIX + to_string(stp_vlan_intf->vlan_id);
key = vlan + ":" + ifName;

Expand Down