[macsec]: Disable LAG member forwarding on MACsec session down - #4744
[macsec]: Disable LAG member forwarding on MACsec session down#4744karthik-nexthop wants to merge 4 commits into
Conversation
Signed-off-by: karthik-nexthop <karthik@nexthop.ai>
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Hi, there are workflow run(s) waiting for approval, you may be first-time contributor. I will notify maintainers to help approve once PR is approved. Thanks! ---Powered by SONiC BuildBot
|
Keep m_lag_forced_admin_down and m_macsec_sa_active Port members added for MACsec LAG member forwarding control. Signed-off-by: Karthik Siruvalam <karthik@nexthop.ai>
34399b4 to
e799b7d
Compare
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
@karthik-nexthop : we need to add one test case that can verify continuous flaps of macsec sessionn and it's impact on teamd to make sure their is no is no race condition for this for APP_DB and SAI programming. |
Add MacsecContinuousFlapNoAppDbSaiRace to cover APP_DB/SAI races under repeated MACsec session flaps. Remove unused m_lag_forced_admin_down left over from a cherry-pick conflict (not present on upstream master). Signed-off-by: Karthik Siruvalam <karthik@nexthop.ai>
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
/azp run |
|
Commenter does not have sufficient privileges for PR 4744 in repo sonic-net/sonic-swss |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
@prsunny , would we please review/merge? thanks |
| setLagMemberState(p, !enabled); | ||
| } | ||
|
|
||
| void PortsOrch::setLagMemberState(Port &port, bool enabled) |
There was a problem hiding this comment.
Though this method is invoked in MacSec path, this name is very generic. Could you rename it?
| it->second.m_macsec_sa_active = enabled; | ||
| } | ||
|
|
||
| const bool is_lag_member = (port.m_lag_member_id != SAI_NULL_OBJECT_ID); |
There was a problem hiding this comment.
We should move this check to the start of the method and bypass if this port is not a member of a lag.
There was a problem hiding this comment.
I'll move the LAG-member check up and return early for non-members so we skip hostif/SAI work immediately.
| /* MACsec data plane is up again. Clear the suppression flag and let | ||
| * teamsyncd's status=enabled refresh drive SAI re-enable once LACP | ||
| * completes, avoiding hashing to a member before teamd selects it. */ | ||
| SWSS_LOG_NOTICE("MACsec SA active on %s; awaiting teamsyncd to re-enable LAG member", |
There was a problem hiding this comment.
Which module sets setDistributionOnLagMember and setCollectionOnLagMember to true? Will be good to update in this section.
There was a problem hiding this comment.
setLagMemberState(true) itself does not re-enable SAI collection/distribution — it only clears m_macsec_sa_active.
Re-enable is driven by teamsyncd. Will update the comment.
| ASSERT_FALSE(ingressDisable) << "cycle " << i; | ||
| } | ||
|
|
||
| // Rapid down/up without an intervening teamsyncd enable: final intent wins. |
There was a problem hiding this comment.
Could you also check with LAG+min_links of LAG configuration?
There was a problem hiding this comment.
That part is covered in the companion sonic-mgmt PR (#26062) — test_macsec_down_disables_lag_member reads CONFIG_DB min_links and gates the PortChannel/BGP checks on it. This mock UT doesn’t run teamd, so I didn’t try to cover min_links here
|
|
||
| // Rapid down/up without an intervening teamsyncd enable: final intent wins. | ||
| Port rapid; | ||
| ASSERT_TRUE(gPortsOrch->getPort(memberAlias, rapid)); |
There was a problem hiding this comment.
Check if LAG goes down(with min-links as total members) when one member's MAC sec fails
There was a problem hiding this comment.
Yes — that’s covered in sonic-mgmt.
| * orchagent/swss restarts (e.g. warm reboot) while MACsec is down, the | ||
| * member comes back enabled before its SAs are re-established. It is not | ||
| * reconciled from STATE_DB MACsec SA presence on init. */ | ||
| bool m_macsec_sa_active = true; |
There was a problem hiding this comment.
This changes the behavior after orchagent restart and not specific to warmboot. This can impact security, please check.
There was a problem hiding this comment.
this is not specific to warmboot. m_macsec_sa_active is in-memory only and defaults to true, so any orchagent restart loses the prior intent. I’ll correct the comment.
|
|
||
| if (!collection_ok || !distribution_ok) | ||
| { | ||
| SWSS_LOG_ERROR("Failed to disable collection/distribution on LAG member %s", |
There was a problem hiding this comment.
Should we reset port.m_macsec_sa_active for any SAI call failures?
There was a problem hiding this comment.
We set m_macsec_sa_active = false before the SAI calls specifically so doLagMemberTask will suppress a later teamsyncd status=enabled refresh.
If the SAI disable fails, the hardware may still be forwarding on that member — we log that as an error, and I agree that risk remains. But reverting the flag on failure would let orchagent re-enable collection/distribution from APP_DB while MACsec is still down, which makes the failure mode worse. Leaving the suppress flag set is the more conservative choice; a retry of the SAI disable would be a separate improvement if we want one.
Rename setLagMemberState to setLagMemberMacsecSaActive, early-return for non-LAG members after persisting m_macsec_sa_active, and document the teamsyncd/doLagMemberTask re-enable path plus any-orchagent-restart limitation for the in-memory flag. Signed-off-by: Karthik Siruvalam <karthik@nexthop.ai>
5fd8093
|
/azp run |
1 similar comment
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
1 similar comment
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
| { | ||
| SWSS_LOG_NOTICE("Flapping host interface %s to force teamd LACP reset due to MACsec down", | ||
| port.m_alias.c_str()); | ||
| setHostIntfsOperStatus(port, false); |
There was a problem hiding this comment.
setHostIntfsOperStatus has a single production caller today — updatePortOperStatus in the SAI port-oper-status notification path — where it mirrors a real hardware link event to the kernel. Using it here to synthesize a fake link flap has a few side effects:
- Spurious telemetry: each call publishes an if-state event via event_publish, so monitoring systems will see two false link-flap events.
- State divergence: updatePortOperStatus also updates m_oper_status, DB oper status, flap counters, nexthop reachability, gearbox state, and VoQ sync. Calling setHostIntfsOperStatus directly skips all of that, so orchagent's internal state won't reflect the kernel-visible flap.
- Timing: back-to-back down/up with no delay risks the kernel coalescing the two netlink carrier events, in which case teamd never sees the down and never deselects the member — breaking the recovery path.
It would be probably cleaner to drive teamd deselection through a teamd-native mechanism (e.g. teamdctl runner port disable, or writing to the teamd control socket) rather than faking a link event through the SAI hostif layer?
prabhataravind
left a comment
There was a problem hiding this comment.
This PR introduces a second control plane for LAG member selection — macsecorch directly manipulates SAI LAG member state and mutes teamd via a suppression guard — which conflicts with SONiC's established architecture where teamd/LACP exclusively owns member selection through APP_LAG_MEMBER_TABLE.
The setHostIntfsOperStatus down/up flap here compounds the issue: this function's sole production caller is updatePortOperStatus (line 10064), where it mirrors real hardware link events. Using it synthetically to trick teamd into deselecting the member (a) publishes two false if-state telemetry events, (b) skips flap-count/nexthop/gearbox/VoQ state updates that updatePortOperStatus performs, and (c) depends on the kernel not coalescing the back-to-back netlink carrier events — an undocumented timing assumption.
Consider having macsecorch signal teamd directly (e.g. teamdctl port config update or a D-Bus/control-socket message) to deselect the member. Teamd would then write status=disabled to APP_LAG_MEMBER_TABLE, doLagMemberTask would disable collection/distribution through the normal path, and the suppression guard, hostif flap, and m_macsec_sa_active flag all become unnecessary.
What I did
m_macsec_sa_activeonPort(orchagent/port.h) to track MACsec data-plane intent in-memory.PortsOrch::setLagMemberState()(orchagent/portsorch.cpp/.h) that:APP_LAG_MEMBER_TABLE status=enableddrive SAI re-enable after LACP completes.doLagMemberTask: suppresses teamsyncd-driven re-enable whenm_macsec_sa_activeis false.setMACsecEnabledState: callssetLagMemberState(p, !enabled)when MACsec is enabled/disabled on a port.macsecorch: hookscreateMACsecSA/deleteMACsecSAto callsetLagMemberStateonly when both ingress and egress directions are up/down (bothDirectionsUp/bothDirectionsDown), so asymmetric rekey teardown does not drop the member prematurely.tests/mock_tests/portsorch_ut.cpp:MacsecDownDisablesLagMemberAndSuppressesTeamdReEnableMacsecDownDoesNotFlapNonLagPortWhy I did it
When an MKA/MACsec session expires on a PortChannel member (e.g. EAPOL blocked, keepalive timeout), the MACsec data plane goes down but the LAG member can remain selected by teamd. Traffic may continue to hash to that member, causing black-holed or ICV-failed frames instead of failing over to healthy members.
After orchagent disables the member's collection/distribution, a routine teamsyncd refresh of
APP_LAG_MEMBER_TABLEwithstatus=enabledcan silently re-enable forwarding while MACsec is still down.How I verified it
Unit tests:
make -C tests/mock_tests portsorch_ut ./tests/mock_tests/portsorch_ut --gtest_filter='*MacsecDown*'Related sonic-mgmt coverage:
tests/macsec/test_fault_handling.py::test_eapol_block_brings_lag_member_down.
Details if related
macsec_changes_explained.md