[PR #7011] [client] Fix expression order in legacy nftables route rules #30097

Closed
opened 2026-08-05 08:10:05 -04:00 by saavagebueno · 0 comments
Owner

Original Pull Request: https://github.com/netbirdio/netbird/pull/7011

State: closed
Merged: Yes


Describe your changes

On the retained compatibility path used with management servers older than v0.30.0, routing peers using the legacy nftables route implementation installed a forwarding rule whose expressions were assembled in the wrong order. In addLegacyRouteRule the Counter and Verdict{Kind: VerdictAccept} expressions were appended to the rule before the source and destination match expressions produced by applyNetwork.

nftables evaluates the expressions of a rule sequentially and stops at the first terminal verdict. With the accept verdict placed first, a packet reaching the generated rule was accepted immediately, and the source and destination matches that followed were never evaluated. This PR is scoped only to correcting that legacy compatibility path.

Fix

The expression slice is now built as source matches -> destination matches -> counter -> accept verdict, so the verdict is only reached once all match expressions have passed. The assembly was extracted into a small helper, buildLegacyRouteRuleExpressions, which keeps addLegacyRouteRule unchanged in behavior other than the ordering and makes the ordering directly testable.

Test

TestBuildLegacyRouteRuleExpressions is a table-driven unit test covering non-empty source and destination expressions as well as the empty source, empty destination, and both-empty cases. It asserts that the caller supplied match expressions come first and in their original order (compared by identity), and that they are followed by exactly a Counter and an accept Verdict. The test fails against the previous ordering.

The targeted regression test and the complete client/firewall/nftables package test suite pass on Linux amd64.

N/A - found during static review. The affected code is the compatibility path for management servers older than v0.30.0.

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature - OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why): the change corrects the internal ordering of nftables rule expressions so the existing behavior is actually applied. No user-facing configuration, CLI or service flags, gRPC protocols, or public APIs change.

Docs PR URL (required if "docs added" is checked)

Paste the link from https://github.com/netbirdio/docs here:

n/a

Summary by CodeRabbit

  • Bug Fixes
    • Improved legacy firewall route rule construction to consistently apply source and destination matches, counters, and accept actions.
    • Added coverage for routes with missing or optional source and destination criteria.
**Original Pull Request:** https://github.com/netbirdio/netbird/pull/7011 **State:** closed **Merged:** Yes --- ## Describe your changes On the retained compatibility path used with management servers older than v0.30.0, routing peers using the legacy nftables route implementation installed a forwarding rule whose expressions were assembled in the wrong order. In `addLegacyRouteRule` the `Counter` and `Verdict{Kind: VerdictAccept}` expressions were appended to the rule **before** the source and destination match expressions produced by `applyNetwork`. nftables evaluates the expressions of a rule sequentially and stops at the first terminal verdict. With the accept verdict placed first, a packet reaching the generated rule was accepted immediately, and the source and destination matches that followed were never evaluated. This PR is scoped only to correcting that legacy compatibility path. **Fix** The expression slice is now built as `source matches -> destination matches -> counter -> accept verdict`, so the verdict is only reached once all match expressions have passed. The assembly was extracted into a small helper, `buildLegacyRouteRuleExpressions`, which keeps `addLegacyRouteRule` unchanged in behavior other than the ordering and makes the ordering directly testable. **Test** `TestBuildLegacyRouteRuleExpressions` is a table-driven unit test covering non-empty source and destination expressions as well as the empty source, empty destination, and both-empty cases. It asserts that the caller supplied match expressions come first and in their original order (compared by identity), and that they are followed by exactly a `Counter` and an accept `Verdict`. The test fails against the previous ordering. The targeted regression test and the complete `client/firewall/nftables` package test suite pass on Linux amd64. ## Issue ticket number and link N/A - found during static review. The affected code is the compatibility path for management servers older than v0.30.0. ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) - [x] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature - OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why): the change corrects the internal ordering of nftables rule expressions so the existing behavior is actually applied. No user-facing configuration, CLI or service flags, gRPC protocols, or public APIs change. ### Docs PR URL (required if "docs added" is checked) Paste the link from https://github.com/netbirdio/docs here: n/a <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved legacy firewall route rule construction to consistently apply source and destination matches, counters, and accept actions. * Added coverage for routes with missing or optional source and destination criteria. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
saavagebueno added the pull-request label 2026-08-05 08:10:05 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#30097