Skip to content

ospfd: quick neighbor feature with BFD - #21784

Merged
riw777 merged 4 commits into
FRRouting:masterfrom
nabahr:ospf_qnbr
May 20, 2026
Merged

ospfd: quick neighbor feature with BFD#21784
riw777 merged 4 commits into
FRRouting:masterfrom
nabahr:ospf_qnbr

Conversation

@nabahr

@nabahr nabahr commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

This PR adds a quick neighbor feature with a simple interface to add neighbors without a hello packet being received.
This PR integrates it with BFD but it could be used in other ways to trigger neighbors via external signals.
The existing BFD implementation only uses the Down state to delete inactive neighbors and does not make use of BFD to quickly re-add neighbors when connectivity is re-established.
This PR adds the ability to enable quick neighbors for the BFD session such that the session will stay active after the neighbor goes down, and when the neighbor comes back up, it is quickly re-added and Hellos are exchanged to form an adjacency.

This feature is only used when BFD is enabled with the "quick" option, otherwise it has no effect.

Update 4/28/26:
I needed to refactor BFD sessions in ospf to be stored per-interface instead of per-neighbor so that the BFD session can remain active after a neighbor is removed. This is now the first commit in the series.

@nabahr

nabahr commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Apr 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces an OSPF "quick neighbor" feature that integrates with BFD to enable sub-hello-interval adjacency re-establishment. The main architectural change is refactoring per-neighbor BFD sessions into per-interface ospf_bfd_session_entry structs so that BFD sessions survive neighbor teardown in quick mode. Previously flagged issues (null-guard on bfd_config->quick, quick flag not updated on reconfigure, inverted condition in the BFD UP handler, num_q_nbrs leak on early deletion, missing fallback wait timer, ospf_header zero-init) appear to have been resolved in this revision.

Confidence Score: 5/5

Safe to merge; all previously flagged P1 issues appear resolved and remaining findings are style-level P2s.

No new P0 or P1 issues found. The BFD session lifetime, num_q_nbrs accounting, null-guard, and wait-timer backstop concerns raised in earlier rounds are addressed. Remaining comments are code-clarity and test-style observations.

ospfd/ospf_bfd.c (complex lock-accounting in entry_del/flush), ospfd/ospf_packet.c (rekey logic for PtP/VLink)

Important Files Changed

Filename Overview
ospfd/ospf_bfd.c Core refactor: BFD sessions moved from per-neighbor to per-interface ospf_bfd_session_entry structs; new functions for flush, prune, clear, and session-change callback rewritten around entry pointer. Previously flagged issues addressed.
ospfd/ospf_quicknbr.c New file implementing quick-neighbor add logic: creates a neighbor without a Hello, drives it through NSM to ExStart, and manages t_qn_wait to defer ISM_WaitTimer until all quick-neighbor router-IDs are learned.
ospfd/ospf_packet.c Adds one-shot router-ID learning for quick neighbors in ospf_verify_header; adds ospf_qnbr_rekey_ptp_vlink to re-key PtP/VLink neighbor table entries from source-address to router-ID once learned.
ospfd/ospf_neighbor.c Adds ospf_qnbr_get, ospf_nbr_bring_down wrapper, and num_q_nbrs decrement guard in ospf_nbr_delete for quick-neighbor placeholders.
ospfd/ospf_nsm.c Adds IS_QUICKNBR branch in nsm_adj_ok to advance quick neighbors from TwoWay to ExStart, bypassing normal router-ID-dependent DR/BDR check.
ospfd/ospf_ism.c Adds t_qn_wait cancellation to all ISM timer-reset paths; adds quick-mode extra hello on DR/BDR change with required null-guard on bfd_config.
ospfd/ospf_interface.c Initialises oi->bfd_sessions in ospf_if_new and flushes + frees it in ospf_if_free, correctly ordered before route_table_finish on nbrs.
tests/topotests/bfd_ospf_quicknbr_topo1/test_bfd_ospf_quicknbr_topo1.py New topotest covering startup, BFD-down session retention, quick re-establishment, and orphan-session pruning on quick→non-quick transition; minor sw.cmd_raises vs sw_net.cmd_raises inconsistency.

Sequence Diagram

sequenceDiagram
    participant BFD as bfdd
    participant CB as ospf_bfd_session_change
    participant QN as ospf_qn_add
    participant NBR as ospf_qnbr_get / ospf_nbr_add
    participant NSM as NSM state machine
    participant PKT as ospf_verify_header

    note over BFD,PKT: Initial adjacency (quick mode)
    BFD->>CB: BSS_UP (first time)
    CB->>QN: ospf_qn_add(oi, endpoint)
    QN->>NBR: ospf_qnbr_get β†’ ospf_nbr_add (router_id=0)
    NBR-->>QN: nbr (IS_QUICKNBR=true, num_q_nbrs++)
    QN->>NSM: NSM_HelloReceived β†’ NSM_TwoWayReceived β†’ NSM_AdjOK
    NSM-->>QN: neighbor at ExStart (quick path in nsm_adj_ok)
    QN->>QN: arm t_qn_wait (100 ms poll)

    note over BFD,PKT: First OSPF packet arrives (e.g. DD)
    PKT->>PKT: ospf_verify_header: qnbr.router_id==0 β†’ learn router_id
    PKT->>PKT: num_q_nbrs--
    PKT->>PKT: ospf_qnbr_rekey_ptp_vlink (PtP/VLink only)
    PKT->>NSM: ISM_NeighborChange + ospf_hello_send

    note over BFD,PKT: Link loss and recovery
    BFD->>CB: BSS_DOWN
    CB->>NSM: ospf_nbr_bring_down β†’ NSM_InactivityTimer
    NSM->>NBR: ospf_nbr_free β†’ ospf_neighbor_bfd_clear (quick: keep bsp)
    BFD->>CB: BSS_UP (reconnect)
    CB->>QN: ospf_qn_add (new quick neighbor cycle)
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
ospfd/ospf_bfd.c:317-325
**`legacy_bsp` aliasing confusion in non-NULL-entry path**

When the entry exists but `bfd_config` is NULL (BFD disabled while a quick neighbor was live), `ospf_bfd_entry_del` correctly frees `entry->bsp`. However, `legacy_bsp` (a local copy of `nbr->bfd_session`, which aliases `entry->bsp`) is non-NULL and never zeroed in this path β€” the `bfd_sess_free(&legacy_bsp)` fallback on line 329 is only reached when `entry == NULL`. No double-free occurs (the double-pointer idiom in `bfd_sess_free` nulls `entry->bsp`, not `legacy_bsp`), but the stale local pointer makes the intent confusing and the code fragile if rearranged.

### Issue 2 of 3
ospfd/ospf_quicknbr.c:158-164
**`ospf_qn_add` re-fires NSM events on a stale `NSM_Init` quick neighbor without a comment**

When BFD fires UP while a prior quick neighbor is still at `NSM_Init` (rapid BFD bounce mid-transition), `NSM_HelloReceived` fires a second time. The NSM handles `NSM_Init + NSM_HelloReceived` as a no-op (stays at Init), so this is functionally safe, but `t_qn_wait` is re-armed. A brief comment clarifying this is intentional would help future readers.

### Issue 3 of 3
tests/topotests/bfd_ospf_quicknbr_topo1/test_bfd_ospf_quicknbr_topo1.py:1380-1387
**Inconsistent `sw.cmd_raises` vs `sw_net.cmd_raises` in `set_switch_blackhole`**

The `enable=True` branch uses `sw.cmd_raises(...)` (the Topogen gear abstraction) while the `enable=False` branch uses `sw_net.cmd_raises(...)` (the underlying Mininet network object directly). Both work in practice since `sw_net = sw.net`, but the inconsistency makes the enable/disable pair harder to maintain.

```suggestion
    if enable:
        for ifname in switch_ports:
            sw.cmd_raises("tc qdisc replace dev {} root netem loss 100%".format(ifname))
    else:
        for ifname in switch_ports:
            sw.cmd_raises("tc qdisc del dev {} root".format(ifname))
```

Reviews (7): Last reviewed commit: "tests: Add OSPF BFD quick topotest" | Re-trigger Greptile

Comment thread ospfd/ospf_ism.c Outdated
Comment thread ospfd/ospf_bfd.c
Comment thread ospfd/ospf_bfd.c
Comment thread ospfd/ospf_dump.c
Comment thread ospfd/ospf_neighbor.c
@nabahr

nabahr commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread ospfd/ospf_bfd.c Outdated
Comment thread ospfd/ospf_neighbor.c
@nabahr

nabahr commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread ospfd/ospf_bfd.c
Comment thread ospfd/ospf_quicknbr.c
@frrbot frrbot Bot added the tests Topotests, make check, etc label Apr 29, 2026
@github-actions github-actions Bot added size/XXL and removed size/L labels Apr 29, 2026
@nabahr

nabahr commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread ospfd/ospf_neighbor.c
Comment thread ospfd/ospf_bfd.c
@nabahr
nabahr force-pushed the ospf_qnbr branch 2 times, most recently from da123a0 to 2885628 Compare April 29, 2026 19:45
@nabahr

nabahr commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread ospfd/ospf_packet.c
@nabahr

nabahr commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai In regards to "the no ip ospf bfd quick command tears down all BFD sessions rather than just removing quick mode"
This was a deliberate choice. Using the no version of the command undoes the config of the original command, which means tearing down all BFD sessions. If the user wishes to switch to quick mode after BFD is enabled, re-running the command with quick (ip ospf bfd quick) will enable quick. That is also true to disable quick, running the command without quick will disable it.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown

That's a reasonable design, and looking at the code now I can see ospf_interface_enable_bfd already handles the quick→non-quick transition gracefully — it prunes orphan sessions via ospf_bfd_if_prune_nonquick and re-applies BFD config so gating takes effect immediately. So the positive form ip ospf bfd (without quick) is the correct path for downgrading modes without flapping neighbors.

The no form fully undoing the command is consistent with standard network CLI conventions. I'll withdraw that concern β€” the behavior is intentional and the graceful transition path exists.

Tip: You can give me more context with [.greptile/rules](https://www.greptile.com/docs/code-review/custom-standards) so I get this right next time.

@nabahr

nabahr commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai review

@nabahr

nabahr commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

That's a reasonable design, and looking at the code now I can see ospf_interface_enable_bfd already handles the quick→non-quick transition gracefully — it prunes orphan sessions via ospf_bfd_if_prune_nonquick and re-applies BFD config so gating takes effect immediately. So the positive form ip ospf bfd (without quick) is the correct path for downgrading modes without flapping neighbors.

The no form fully undoing the command is consistent with standard network CLI conventions. I'll withdraw that concern β€” the behavior is intentional and the graceful transition path exists.

Tip: You can give me more context with [.greptile/rules](https://www.greptile.com/docs/code-review/custom-standards) so I get this right next time.

@greptileai You said you would withdraw this concern but it's still an issue you list in your review.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown

You're right, and I apologize for the inconsistency. Saying "I'll withdraw that concern" in a reply thread doesn't remove it from the original review comment β€” the original comment remains visible on the PR. The two are independent: inline review comments persist regardless of what's said in follow-up replies.

The concern about no ip ospf bfd quick behavior has been addressed β€” you explained the design, I looked at the code (ospf_interface_enable_bfd + ospf_bfd_if_prune_nonquick), and agreed the behavior is intentional and correct. Consider that concern resolved.

@nabahr nabahr changed the title WIP: Ospf quick neighbor feature with BFD ospfd: quick neighbor feature with BFD Apr 30, 2026
@nabahr
nabahr marked this pull request as ready for review April 30, 2026 21:01
@nabahr
nabahr requested a review from aceelindem May 4, 2026 18:42
@github-actions github-actions Bot added the rebase PR needs rebase label May 5, 2026
@riw777
riw777 self-requested a review May 5, 2026 14:42
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

nabahr added 4 commits May 5, 2026 18:34
…ghbor

This allows the possibility for BFD sessions to persist after a neighbor
goes away. Keeping the BFD session open means we could then potentially
bring the neighbor back with a up signal from BFD.

Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
Quick neighbors exposes a simple interface to add/delete a neighbor
based on the provided router-id, link address, and endpoint/neighbor
address.
On Quick neigbor delete, if we find the neighbor, we immediately set
the NSM to Inactivity and the neighbor is removed, this is the same
as the current BFD neighbor delete.
On Quick neighbor add, if the neighbor is not found, then we add it
to the neighbors list without a router id or other hello fields.
These fields are either left empty to be learned later or presumed
based on the local link. We artifically push the neighbor through
the NSM states until it is in the ExStart state. If the interface is
currently in the Waiting state, we stop the normal wait timer and
restart a quick wait timer that will check for quick neighbors (those
without a router id) and once we've learned the router id's of all
the quick neigbors, we push the interface out of the waiting state.
In order to facilitate quick neighbor learning, we detect OSPF
packets from quick neighbors and update the router id as soon as
possible, i.e. during verification of the OSPF header. When a new
router-id is learned, we trigger a neighbor change event in the ISM
and send an immediate hello. We also send another immediate hello
when the DR/BDR election changes.
Finally, in the NSM, we also assume that a quick neighbor should form
an adjacency so that it can transition to a full neighbor.

Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
Updated docs to match how it's implemented.
Added documentation of the quick neighbor feature.

Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
This topotest exercises the quick neighbor feature in OSPF using
BFD sessions that live on after a neighbor goes down. This allows
it to come back quickly when BFD detects the neighbor when there
are long timers.

Signed-off-by: Nathan Bahr <nbahr@atcorp.com>

@riw777 riw777 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks good

@riw777
riw777 merged commit a632da1 into FRRouting:master May 20, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants