Skip to content

tests: add topotest for L3VNI in default VRF#22343

Open
pbrisset wants to merge 1 commit into
FRRouting:masterfrom
pbrisset:bgp-evpn-l3vni-grt
Open

tests: add topotest for L3VNI in default VRF#22343
pbrisset wants to merge 1 commit into
FRRouting:masterfrom
pbrisset:bgp-evpn-l3vni-grt

Conversation

@pbrisset

@pbrisset pbrisset commented Jun 12, 2026

Copy link
Copy Markdown

Add comprehensive functional test suite validating L3VNI operation in the
default (global) VRF using zebra's existing 'vni ' command instead
of BGP configuration. This demonstrates that L3VNI functionality already
works in default VRF without requiring new BGP commands.

Default VRF Approach:

  • Uses zebra's CONFIG_NODE level 'vni ' command
  • Requires kernel VXLAN interface and bridge/SVI infrastructure
  • L3VNI becomes operational when zebra detects proper kernel setup
  • No BGP configuration needed (zebra sends ZEBRA_L3VNI_ADD to BGP)

Test Topology:

  • R1, R2: EVPN VTEPs with L3VNI 5000 in default VRF (zebra approach)
  • R3: Traditional BGP router (no EVPN) for isolation testing
  • Spine: eBGP underlay + EVPN route reflector
  • Client networks: 192.168.1.0/24 (R1), 192.168.3.0/24 (R3)

Key Test Coverage (30 tests):

  1. EVPN Isolation - Validates client networks advertised only via EVPN RT-5

    • R1/R2 exchange routes via EVPN overlay
    • R3 cannot reach R1's clients (prefix-list filters IPv4 unicast)
    • Proper separation of EVPN overlay and traditional BGP underlay
  2. Zebra L3VNI Detection - Validates zebra's VNI management

    • Bridge/SVI infrastructure (VLAN-unaware bridge)
    • VXLAN interface with nolearning flag
    • L3VNI becomes operational when both vxlan_if and svi_if exist
  3. RFC-Compliant RD - Ensures proper multi-router deployments

    • RD format: router-id:vrf_id (e.g., 10.0.1.1:1)
    • NOT "0:0" which would break route propagation
  4. Data Plane Validation

    • RT-5 route advertisement and reception
    • Routes imported from EVPN to IPv4 unicast table (distance 200)
    • Kernel routes with VXLAN nexthop and onlink attribute
    • Complete control + data plane validation
  5. Configuration Management

    • L3VNI removal/restoration with kernel interface cleanup
    • Multiple VNI configurations (zebra allows, operational via kernel)
    • VNI range validation

Test Results:

  • 30 tests: 30 passing
  • Validates complete EVPN L3VNI functionality in default VRF

Signed-off-by: Patrice Brissette pbrisset@cisco.com

@frrbot frrbot Bot added bgp documentation tests Topotests, make check, etc labels Jun 12, 2026
@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a new topotest suite (bgp_evpn_l3vni_default_vrf) that validates L3VNI operation in the default (global) VRF using zebra's top-level vni <vni-id> command, covering 30 tests across five phases from basic configuration through end-to-end ICMP isolation.

  • 5-phase coverage: BGP convergence → EVPN RT-5 advertisement (with RFC-compliant RD check, guarding against the previously-fixed 0:0 regression) → data-plane verification (zebra RIB + kernel routes via vxlan5000 onlink) → connectivity isolation → lifecycle tests (VNI removal/re-add, invalid range, duplicate config).
  • Config files place vni 5000 at zebra's CONFIG_NODE (top-level in zebra.conf) rather than inside the BGP stanza, which is consistent with the zebra-only approach described in the PR.
  • Three tests contain unconditional json.dumps(..., indent=2) debug dumps, one of which fires inside a 30-iteration run_and_expect polling loop, producing significant log noise in CI; these should be removed or demoted to logger.debug() before merge.

Confidence Score: 5/5

This is an additive test-only change with no modifications to production FRR daemon code; it is safe to merge.

All changed files are new test infrastructure (configs, Python test module, documentation). The test logic correctly uses run_and_expect retry loops for all convergence-dependent checks and properly guards against the RD 0:0 regression. The findings are cosmetic: verbose debug logging left in from development, one assertion weaker than the pattern used elsewhere in the same file, and a misleading docstring.

tests/topotests/bgp_evpn_l3vni_default_vrf/test_bgp_evpn_l3vni_default_vrf.py — the debug JSON dumps and weak coexistence assertion are worth cleaning up before merge.

Important Files Changed

Filename Overview
tests/topotests/bgp_evpn_l3vni_default_vrf/test_bgp_evpn_l3vni_default_vrf.py 1069-line test suite for L3VNI in the default VRF; logic is sound and retry loops are correctly used, but several tests contain unconditional verbose JSON dumps (including one inside a 30-iteration polling loop) and one coexistence assertion is weaker than the pattern used elsewhere in the file.
tests/topotests/bgp_evpn_l3vni_default_vrf/r1/bgpd.conf R1 BGP config: prefix-list UNDERLAY_ONLY correctly restricts IPv4 unicast to the VTEP loopback; EVPN AF with advertise ipv4 unicast and explicit RT 65001:5000 import/export looks correct.
tests/topotests/bgp_evpn_l3vni_default_vrf/r1/zebra.conf R1 zebra config: top-level vni 5000 at CONFIG_NODE matches the zebra command registration approach; vxlan5000 interface stub and ip forwarding are present.
tests/topotests/bgp_evpn_l3vni_default_vrf/r2/bgpd.conf R2 BGP config mirrors R1 with correct RT 65001:5000 and UNDERLAY_ONLY prefix-list; no issues.
tests/topotests/bgp_evpn_l3vni_default_vrf/r3/bgpd.conf R3 BGP config: network statements for 10.0.3.3/32 and 192.168.3.0/24 are correctly on separate lines (previous concatenation issue is resolved).
tests/topotests/bgp_evpn_l3vni_default_vrf/spine/bgpd.conf Spine config: R3 deliberately excluded from the l2vpn evpn AF, which enforces EVPN isolation; looks correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C1 as Client c1 (192.168.1.x)
    participant R1 as R1 (AS 65001) L3VNI 5000
    participant Spine as Spine (AS 65000) EVPN RR
    participant R2 as R2 (AS 65002) L3VNI 5000
    participant R3 as R3 (AS 65003) No EVPN

    Note over R1,R2: zebra vni 5000 triggers ZEBRA_L3VNI_ADD to bgpd
    R1->>Spine: IPv4 unicast: 10.0.1.1/32 only (UNDERLAY_ONLY filter)
    R1->>Spine: L2VPN EVPN RT-5 [5]:[192.168.1.0/24] RD 10.0.1.1:1
    Spine->>R2: L2VPN EVPN RT-5 [5]:[192.168.1.0/24]
    R2->>R2: Import to IPv4 unicast (distance 200, nexthop via vxlan5000)
    R3->>Spine: IPv4 unicast: 10.0.3.3/32, 192.168.3.0/24
    Spine->>R1: IPv4 unicast: 10.0.3.3/32, 192.168.3.0/24
    Note over R1,R3: R3 NOT in L2VPN EVPN AF - no RT-5 routes - isolation enforced
    C1->>R1: ping 192.168.3.10 - NO ROUTE (EVPN/BGP domain isolation)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C1 as Client c1 (192.168.1.x)
    participant R1 as R1 (AS 65001) L3VNI 5000
    participant Spine as Spine (AS 65000) EVPN RR
    participant R2 as R2 (AS 65002) L3VNI 5000
    participant R3 as R3 (AS 65003) No EVPN

    Note over R1,R2: zebra vni 5000 triggers ZEBRA_L3VNI_ADD to bgpd
    R1->>Spine: IPv4 unicast: 10.0.1.1/32 only (UNDERLAY_ONLY filter)
    R1->>Spine: L2VPN EVPN RT-5 [5]:[192.168.1.0/24] RD 10.0.1.1:1
    Spine->>R2: L2VPN EVPN RT-5 [5]:[192.168.1.0/24]
    R2->>R2: Import to IPv4 unicast (distance 200, nexthop via vxlan5000)
    R3->>Spine: IPv4 unicast: 10.0.3.3/32, 192.168.3.0/24
    Spine->>R1: IPv4 unicast: 10.0.3.3/32, 192.168.3.0/24
    Note over R1,R3: R3 NOT in L2VPN EVPN AF - no RT-5 routes - isolation enforced
    C1->>R1: ping 192.168.3.10 - NO ROUTE (EVPN/BGP domain isolation)
Loading

Reviews (8): Last reviewed commit: "tests: add comprehensive topotest for L3..." | Re-trigger Greptile

Comment thread bgpd/bgp_evpn_vty.c Outdated
Comment thread bgpd/bgp_evpn_vty.c Outdated
Comment thread bgpd/bgp_evpn_vty.c Outdated
@pbrisset pbrisset changed the title BGP EVPN: Add L3VNI support for default VRF WIP: BGP EVPN: Add L3VNI support for default VRF Jun 12, 2026
@taspelund

Copy link
Copy Markdown

Isn't there already support for setting the L3VNI for the default VRF via vni <L3VNI> run from global config? (e.g. sudo vtysh -c 'vni <L3VNI>'

@pbrisset

Copy link
Copy Markdown
Author

Isn't there already support for setting the L3VNI for the default VRF via vni <L3VNI> run from global config? (e.g. sudo vtysh -c 'vni <L3VNI>'

The existing vrf ... vni command (at global config) creates a Linux VRF and associates it with an L3VNI. This PR is different - it enables L3VNI on the default/global VRF (not a custom VRF) for pure Layer-3 EVPN deployments without creating additional VRFs. The new command is vrf DEFAULT-VRF vni inside [router bgp] context, specifically for L3-only EVPN use cases in the global routing table.

@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 41a7376 to 9a35472 Compare June 12, 2026 18:42
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@taspelund

Copy link
Copy Markdown

The existing vrf ... vni command (at global config) creates a Linux VRF and associates it with an L3VNI.

This is inaccurate. FRR doesn't create Linux netdevs.

This PR is different - it enables L3VNI on the default/global VRF (not a custom VRF) for pure Layer-3 EVPN deployments without creating additional VRFs.

This is exactly what the existing vni command already does.

vni can either be run from global config (conf t) where it associates an L3VNI to the default VRF, or it can be run within VRF config (conf t, vrf <NAME>) where it associates an L3VNI to a named VRF.

Not only that, but there is already a reserved keyword for the default VRF (default), so you can use vni from within vrf default to do the same thing there too.

So it's not really clear to me what the new command or syntax does which can't already be accomplished through existing commands. If there's a bug or issue with the existing commands, I would advocate for reporting and fixing them rather than adding new alternatives that do the same thing.

@pbrisset

Copy link
Copy Markdown
Author

The core routing logic is sound and previous teardown issues have been addressed, but a config-write ordering bug causes bgp_zebra_advertise_all_vni to be called twice on every daemon restart after a write file.

The config-write in bgp_vty.c places vrf DEFAULT-VRF vni before the EVPN address-family section. On every reload of a saved config, evpn_set_advertise_all_vni fires once from the vrf command (while advertise_all_vni is still 0) and a second time when advertise-all-vni is parsed from the EVPN AF block. This sends two VNI-advertisement registrations to zebra on every bgpd restart, which is an observable behavioural regression for any operator who saves their running config.

bgpd/bgp_vty.c — the vrf DEFAULT-VRF vni write position relative to bgp_config_write_family for the EVPN AF; bgpd/bgp_evpn_vty.c — the unconditional advisory %% reminder messages and the bgp_get_default() bypass of VTY context.

Hi,

Do you have a sample config? I will be interested to see that.

@taspelund

taspelund commented Jun 12, 2026

Copy link
Copy Markdown

sure. either of these will apply the L3VNI to the default VRF:

sudo vtysh
conf t
vni <L3VNI>
sudo vtysh
conf t
vrf default
  vni <L3VNI>

@pbrisset

pbrisset commented Jun 12, 2026

Copy link
Copy Markdown
Author

sudo vtysh
conf t
vrf default
vni

hold on.... will come back to you soon

@donaldsharp

Copy link
Copy Markdown
Member

the vni you enter under vrf default is not displayed under any vrf default in show run. It is just mixed in with other default level commands. Not ideal.

@pbrisset

Copy link
Copy Markdown
Author

sure. either of these will apply the L3VNI to the default VRF:

sudo vtysh
conf t
vni <L3VNI>
sudo vtysh
conf t
vrf default
  vni <L3VNI>

@taspelund Thank you for the advice. I'm will close this PR or keep it to commit topotest covering the area.

@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 9a35472 to b414f98 Compare June 13, 2026 13:20
@pbrisset pbrisset changed the title WIP: BGP EVPN: Add L3VNI support for default VRF tests: add topotest for L3VNI in default VRF Jun 13, 2026
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

Comment thread tests/topotests/bgp_evpn_l3vni_default_vrf/r3/bgpd.conf Outdated
@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch 2 times, most recently from 9bdf7a5 to 46e74ac Compare June 13, 2026 21:37
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 46e74ac to 3e1da40 Compare June 15, 2026 15:58
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 3e1da40 to 38f791c Compare June 15, 2026 16:05
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 38f791c to 29fb544 Compare June 15, 2026 18:16
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

Comment on lines +903 to +905
def _check_routes_restored():
output = r1.vtysh_cmd("show bgp l2vpn evpn route type prefix json", isjson=True)
return len(output) > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Vacuous route-restoration check after L3VNI re-add

len(output) > 0 does not reliably confirm actual routes have returned. FRR can return a metadata-only JSON response such as {"numPrefix": 0, "numPaths": 0} when there are no prefix routes, which evaluates to len == 2 > 0 and makes run_and_expect immediately return True without waiting for real routes to re-propagate. Compare this with _check_r1_rt5_advertisement (line 457) which explicitly guards against this with if not output or len(output) == 0 and then filters out "numPrefix" / "numPaths" metadata keys before looking for a valid RD. The same RD-based check should be applied here.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/topotests/bgp_evpn_l3vni_default_vrf/test_bgp_evpn_l3vni_default_vrf.py
Line: 903-905

Comment:
**Vacuous route-restoration check after L3VNI re-add**

`len(output) > 0` does not reliably confirm actual routes have returned. FRR can return a metadata-only JSON response such as `{"numPrefix": 0, "numPaths": 0}` when there are no prefix routes, which evaluates to `len == 2 > 0` and makes `run_and_expect` immediately return `True` without waiting for real routes to re-propagate. Compare this with `_check_r1_rt5_advertisement` (line 457) which explicitly guards against this with `if not output or len(output) == 0` and then filters out `"numPrefix"` / `"numPaths"` metadata keys before looking for a valid RD. The same RD-based check should be applied here.

How can I resolve this? If you propose a fix, please make it concise.

@mergify

mergify Bot commented Jun 19, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Add extensive functional test suite for BGP EVPN L3VNI configured in
the default (global) VRF instead of custom VRFs. This validates the
'vrf DEFAULT-VRF vni <vni-id>' CLI command implementation.

Test topology:
- R1, R2: BGP EVPN routers with L3VNI 5000 in default VRF
- R3: Traditional BGP router without EVPN
- Spine: eBGP underlay + EVPN route reflector

Test coverage (32 tests across 5 phases):

Phase 1: Basic Configuration (8 tests)
- BGP session establishment
- L3VNI configuration and persistence
- JSON output validation
- Route-target configuration
- EVPN address-family setup

Phase 2: EVPN Route Advertisement (4 tests)
- RT-5 routes with RFC-compliant RD format (router-id:vrf_id)
- Validates RD is NOT "0:0" (which breaks multi-router deployments)
- Route reception and propagation
- EVPN and traditional BGP route coexistence

Phase 3: Data Plane (11 tests)
- BGP route installation
- Zebra RIB validation
- Kernel routing table verification
- Bidirectional route exchange
- Nexthop validation

Phase 4: Connectivity (2 tests)
- End-to-end ICMP connectivity via VXLAN tunnel
- Validates complete data plane functionality

Phase 5: Verification (7 tests)
- VXLAN interface operational status
- VRF isolation (no route leaking)
- L3VNI/L2VNI conflict detection
- L3VNI removal and re-addition
- Invalid VNI range validation
- Duplicate L3VNI configuration detection

Key validations:
- RFC-compliant RD format (10.0.1.1:1, NOT 0:0)
- EVPN RT-5 and traditional BGP coexistence in default VRF
- Proper VXLAN encapsulation with correct nexthops
- Administrative distance 200 for EVPN-imported routes
- Extended Community RT:65001:5000 on EVPN routes

Signed-off-by: Patrice Brissette <pbrisset@cisco.com>
@pbrisset pbrisset force-pushed the bgp-evpn-l3vni-grt branch from 9508fce to 102b3a2 Compare June 23, 2026 18:34
@pbrisset

Copy link
Copy Markdown
Author

@greptile-apps review

@pbrisset

Copy link
Copy Markdown
Author

This PR is ready for final review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bgp documentation master rebase PR needs rebase size/XXL tests Topotests, make check, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants