Skip to content

bgpd: migrate timers during peer_xfer_conn to fix stale route cleanup - #21558

Merged
ton31337 merged 1 commit into
FRRouting:masterfrom
shashanka-ks:bgpd-gr-stale-route-cleanup
Apr 20, 2026
Merged

bgpd: migrate timers during peer_xfer_conn to fix stale route cleanup#21558
ton31337 merged 1 commit into
FRRouting:masterfrom
shashanka-ks:bgpd-gr-stale-route-cleanup

Conversation

@shashanka-ks

Copy link
Copy Markdown
Contributor

When a BGP session goes down on a GR-helper, bgp_stop() arms the stalepath timer (t_gr_stale) and restart timer (t_gr_restart) on the config peer's connection. If the restarting peer reconnects and a connection collision occurs (common in large-scale topologies like Fairwater with 256 sessions per leaf), peer_xfer_conn() swaps the connection pointers: the doppelganger's winning connection (keeper) is given to the config peer, and the config peer's old connection (going_away) is handed to the doppelganger for deletion.

The GR timers armed on going_away were not migrated during this swap. When the doppelganger was subsequently deleted, going_away was freed and the timers were silently lost. Without the stalepath timer safety net, stale routes were never removed even after the configured stalepath-time had long elapsed.

Fix:
Cancel the GR timers on going_away after the swap and re-arm them on keeper with the remaining time. A simple pointer copy is insufficient because EVENT_ARG (the callback context) must reference keeper, not the going_away connection that will be freed.

Custom debug logs:

Before fix - timer lost during xfer, stale routes never removed

bgp_stop: start stalepath timer 120 sec
peer_xfer_conn: going_away t_gr_stale=armed rem_sec=98
peer_xfer_conn: after xfer config_conn t_gr_stale=NULL    <-- LOST
bgp_establish: t_gr_stale=NULL                            <-- never fires

After fix - timer migrated, stale routes removed on expiry

bgp_stop: start stalepath timer 120 sec
peer_xfer_conn: going_away t_gr_stale=armed rem_sec=98
peer_xfer_conn: migrated stalepath timer (98 sec remain)  <-- MIGRATED
peer_xfer_conn: after xfer config_conn t_gr_stale=armed rem_sec=97
bgp_establish: t_gr_stale=armed gr_stale_rem_sec=97
stalepath timer expired                                   <-- FIRES
bgp_clear_stale_route: entered (stale routes removed)

@greptile-apps

greptile-apps Bot commented Apr 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a stale-route cleanup bug in BGP Graceful Restart helper mode: when a connection collision occurs during peer reconnection, peer_xfer_conn() swaps the connection pointers but the GR timers (t_gr_stale, t_gr_restart) armed on going_away were silently lost with the doppelganger, preventing stale route removal. The fix cancels those timers on going_away and re-arms them on keeper with the remaining time, correctly passing keeper as the event arg to match what the timer callbacks (bgp_graceful_stale_timer_expire/bgp_graceful_restart_timer_expire) expect via EVENT_ARG.

Confidence Score: 5/5

Safe to merge β€” the fix is logically correct, well-scoped, and addresses a real stale-route cleanup regression in GR-helper mode.

No P0 or P1 issues found. The timer migration is placed correctly (after the connection-pointer swap so keeper->peer already points to the config peer), the forward declarations are necessary and correct, and the EVENT_ARG passed to each re-armed timer matches what both callbacks cast it to (struct peer_connection *). All remaining observations are P2 or below.

No files require special attention.

Important Files Changed

Filename Overview
bgpd/bgp_fsm.c Adds GR timer migration in peer_xfer_conn() β€” cancels t_gr_stale/t_gr_restart on the old connection (going_away) and re-arms them on keeper with the remaining time; forward declarations added correctly; EVENT_ARG matches what both callbacks expect.

Sequence Diagram

sequenceDiagram
    participant BP as BGP Session
    participant CFG as Config Peer (peer)
    participant DOP as Doppelganger (from_peer)
    participant KP as keeper (doppelganger conn)
    participant GA as going_away (old config conn)

    BP->>CFG: Session drops
    CFG->>GA: bgp_stop() arms t_gr_stale + t_gr_restart on going_away

    Note over DOP: Peer reconnects β†’ collision

    DOP->>KP: New connection (doppelganger wins)
    Note over CFG,GA: peer_xfer_conn() called

    CFG->>KP: peer->connection = keeper
    GA->>DOP: from_peer->connection = going_away

    alt Before fix
        Note over GA: t_gr_stale/t_gr_restart stay on going_away
        DOP->>GA: Doppelganger freed β†’ timers LOST
        Note over CFG: Stale routes never removed
    else After fix (this PR)
        GA->>KP: Migrate t_gr_stale with remaining time
        GA->>KP: Migrate t_gr_restart with remaining time
        Note over GA: going_away timers cancelled
        DOP->>GA: Doppelganger freed (no timers)
        KP->>CFG: t_gr_stale fires β†’ bgp_clear_stale_route()
    end
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: bgpd/bgp_fsm.c
Line: 228-234

Comment:
**Sub-second truncation may fire timer immediately**

`event_timer_remain_second()` does integer division (`remain_msec / 1000`), so if fewer than 1 second remains on the stalepath timer the result is `0`. Passing `0` to `event_add_timer` arms the timer to fire at the very next event-loop iteration β€” semantically correct (the time has nearly elapsed), but worth noting. If sub-second precision matters here, consider using `event_timer_remain_msec()` together with `event_add_timer_msec()` to preserve the fractional remainder.

```suggestion
		unsigned long remain_ms =
			event_timer_remain_msec(going_away->t_gr_stale);
		event_cancel(&going_away->t_gr_stale);
		event_add_timer_msec(bm->master,
				bgp_graceful_stale_timer_expire,
				keeper, remain_ms,
				&keeper->t_gr_stale);
```

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

Reviews (1): Last reviewed commit: "bgpd: migrate timers during peer_xfer_co..." | Re-trigger Greptile

Comment thread bgpd/bgp_fsm.c Outdated
@shashanka-ks
shashanka-ks force-pushed the bgpd-gr-stale-route-cleanup branch from f32b6fd to 6229279 Compare April 16, 2026 08:48
@github-actions github-actions Bot added size/S and removed size/M labels Apr 16, 2026
@shashanka-ks
shashanka-ks force-pushed the bgpd-gr-stale-route-cleanup branch from 6229279 to b2316c2 Compare April 16, 2026 08:56
@github-actions github-actions Bot added size/M and removed size/S labels Apr 16, 2026
Comment thread bgpd/bgp_fsm.c Outdated
@shashanka-ks

Copy link
Copy Markdown
Contributor Author

ci:rerun

1 similar comment
@shashanka-ks

Copy link
Copy Markdown
Contributor Author

ci:rerun

Comment thread bgpd/bgp_fsm.c Outdated
@shashanka-ks
shashanka-ks force-pushed the bgpd-gr-stale-route-cleanup branch 3 times, most recently from f902b42 to c9ac96a Compare April 17, 2026 07:10
When a BGP session goes down on a GR-helper, bgp_stop() arms the
stalepath timer (t_gr_stale) and restart timer (t_gr_restart) on the
config peer's connection. If the restarting peer reconnects and a
connection collision occurs (common in large-scale topologies like
Fairwater with 256 sessions per leaf), peer_xfer_conn() swaps the
connection pointers: the doppelganger's winning connection (keeper)
is given to the config peer, and the config peer's old connection
(going_away) is handed to the doppelganger for deletion.

The GR timers armed on going_away were not migrated during this swap.
When the doppelganger was subsequently deleted, going_away was freed
and the timers were silently lost. Without the stalepath timer safety
net, stale routes were never removed even after the configured
stalepath-time had long elapsed.

Fix:
Cancel the GR timers on going_away after the swap and
re-arm them on keeper with the remaining time. A simple pointer
copy is insufficient because EVENT_ARG (the callback context) must
reference keeper, not the going_away connection that will be freed.

Custom debug logs:

Before fix - timer lost during xfer, stale routes never removed

  bgp_stop: start stalepath timer 120 sec
  peer_xfer_conn: going_away t_gr_stale=armed rem_sec=98
  peer_xfer_conn: after xfer config_conn t_gr_stale=NULL    <-- LOST
  bgp_establish: t_gr_stale=NULL                            <-- never fires

After fix - timer migrated, stale routes removed on expiry

  bgp_stop: start stalepath timer 120 sec
  peer_xfer_conn: going_away t_gr_stale=armed rem_sec=98
  peer_xfer_conn: migrated stalepath timer (98 sec remain)  <-- MIGRATED
  peer_xfer_conn: after xfer config_conn t_gr_stale=armed rem_sec=97
  bgp_establish: t_gr_stale=armed gr_stale_rem_sec=97
  stalepath timer expired                                   <-- FIRES
  bgp_clear_stale_route: entered (stale routes removed)

Signed-off-by: Shashanka K S <shashankak@nvidia.com>
@shashanka-ks
shashanka-ks force-pushed the bgpd-gr-stale-route-cleanup branch from c9ac96a to 82305e0 Compare April 17, 2026 07:11
@shashanka-ks

Copy link
Copy Markdown
Contributor Author

Hi @donaldsharp @ton31337 , could you please rerun the cancelled job: Ubuntu 24.04 amd64 Build? It timed out at 45 minutes due to slow Ubuntu package mirrors during the Docker build β€” not related to the PR changes.

@ton31337 ton31337 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.

LGTM

@ton31337
ton31337 merged commit abfc9c5 into FRRouting:master Apr 20, 2026
31 of 32 checks passed
@ton31337

Copy link
Copy Markdown
Member

@Mergifyio backport stable/10.6 stable/10.5

@mergify

mergify Bot commented Apr 20, 2026

Copy link
Copy Markdown

backport stable/10.6 stable/10.5

βœ… Backports have been created

Details

@shashanka-ks
shashanka-ks deleted the bgpd-gr-stale-route-cleanup branch April 20, 2026 05:24
donaldsharp added a commit that referenced this pull request Apr 20, 2026
bgpd: migrate timers during peer_xfer_conn to fix stale route cleanup (backport #21558)
donaldsharp added a commit that referenced this pull request Apr 20, 2026
bgpd: migrate timers during peer_xfer_conn to fix stale route cleanup (backport #21558)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants