ospfd: RFC 4222 R4 per-neighbor LSA gap pacing#22374
Conversation
f1761fd to
3447e2f
Compare
Greptile SummaryThis PR implements RFC 4222 Recommendation 4 (per-neighbor LSA gap pacing) in ospfd, alongside R5 adjacency pacing. When enabled on an interface, outbound LSAs are queued per-neighbor and metered at a configurable inter-packet gap that adapts via MIMD based on unACKed LSA watermarks.
Confidence Score: 2/5Not safe to merge as-is: the ls_rxmt_unacked counter permanently accumulates on LSA re-origination, and disabling R4 pacing on a live interface silently drops LSAs not yet recorded in the retransmit list. The ls_rxmt_unacked counter is the sole feedback signal driving both MIMD algorithms. When ospf_ls_retransmit_add supersedes an older LSA version it discards the old node without decrementing the counter, so after each re-origination cycle the counter can only grow. In a production network with frequent topology changes this permanently disables the gap-shrink and adjacency-increase halves of both algorithms. Separately, toggling no ip ospf lsa-pacing while a flood is in progress abandons LSAs queued for initial delivery that have no retransmit record, causing LSDB divergence. ospfd/ospf_flood.c (ospf_ls_retransmit_add around line 1143) and ospfd/ospf_interface.c (ospf_rec4_recompute_effective) need fixes before this is safe to enable in production. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Flood as ospf_flood_through_interface
participant Q as r4_send_queue
participant Timer as ospf_r4_nbr_send_timer
participant Send as ospf_ls_upd_queue_send
participant Write as ospf_write
participant Count as ospf_count_sent_lsa
participant RxmtDB as nbr->ls_rxmt
participant MIMD as pace_maybe_adjust_gap
Flood->>Q: ospf_r4_nbr_enqueue(nbr, lsa)
Note over Q: skips ls_retransmit_add
Timer->>Send: ospf_ls_upd_queue_send(oi, r4_send_queue, nbr)
Send->>RxmtDB: ospf_ls_retransmit_add(dst_nbr, lsa)
Send->>Write: ospf_packet_add(oi, op)
Write->>Count: ospf_count_sent_lsa(oi, dst, lsa)
Count->>RxmtDB: "lookup counted_sent=false, ls_rxmt_unacked++"
Note over MIMD: ACK arrives
MIMD->>RxmtDB: ospf_ls_retransmit_delete, ls_rxmt_unacked--
MIMD->>MIMD: "pace_maybe_adjust_gap U<=L shrink gap"
%%{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 Flood as ospf_flood_through_interface
participant Q as r4_send_queue
participant Timer as ospf_r4_nbr_send_timer
participant Send as ospf_ls_upd_queue_send
participant Write as ospf_write
participant Count as ospf_count_sent_lsa
participant RxmtDB as nbr->ls_rxmt
participant MIMD as pace_maybe_adjust_gap
Flood->>Q: ospf_r4_nbr_enqueue(nbr, lsa)
Note over Q: skips ls_retransmit_add
Timer->>Send: ospf_ls_upd_queue_send(oi, r4_send_queue, nbr)
Send->>RxmtDB: ospf_ls_retransmit_add(dst_nbr, lsa)
Send->>Write: ospf_packet_add(oi, op)
Write->>Count: ospf_count_sent_lsa(oi, dst, lsa)
Count->>RxmtDB: "lookup counted_sent=false, ls_rxmt_unacked++"
Note over MIMD: ACK arrives
MIMD->>RxmtDB: ospf_ls_retransmit_delete, ls_rxmt_unacked--
MIMD->>MIMD: "pace_maybe_adjust_gap U<=L shrink gap"
|
c9f07fc to
d4fabd3
Compare
d4fabd3 to
e830773
Compare
|
Force-pushed an update addressing a defect found in further testing of the paced retransmit path:
|
|
Can we get the linter error fixed? Thanks! |
e830773 to
d9e4ab3
Compare
Implement RFC 4222 recommendation 5: per-interface adjacency pacing to limit the number of simultaneous OSPF adjacency formations and avoid database-exchange bursts on bandwidth-constrained interfaces. Two modes are provided: - Static: a fixed concurrency limit configured per interface. - Dynamic: an AIMD-based limit that adjusts automatically using the total unacknowledged LSA count (U) across the interface as a congestion signal. When U rises above a high-water mark the limit is halved; when U falls below a low-water mark the limit is incremented by one. Neighbors that cannot immediately enter ExStart are queued per interface in FIFO order. When a slot opens, ospf_adj_pacing_kick() restarts the next queued neighbor. The dynamic limit is re-evaluated on LSA acknowledgement and on the retransmit timer. Signed-off-by: Ravi Ravindran <rravindran@labn.net>
Add topotests for RFC 4222 recommendation 5 adjacency pacing covering three test files: - test_ospf_adj_pacing_config.py: verifies static and dynamic pacing configuration persistence, interface flap behavior, FRR daemon restart, no-command cleanup, threshold validation, and defaults. - test_ospf_broadcast_router_dynamic_pacing.py: verifies dynamic pacing on a 7-router broadcast topology with DR election; tests AIMD congestion detection under LSA flood and queue-kick behavior when the dynamic limit increases after congestion clears. - test_ospf_broadcast_router_static_pacing.py: verifies static pacing on the same broadcast topology. Router configurations cover point-to-point (r1-r3) and broadcast (r1_broadcast through r7_broadcast) topologies with hello/dead timers tuned for fast convergence. Signed-off-by: Ravi Ravindran <rravindran@labn.net>
Add developer and user documentation for RFC 4222 recommendation 5 adjacency pacing. doc/developer/ospf-adj-pacing.rst: describes the static and dynamic pacing modes, the AIMD algorithm, code paths in ospf_nsm.c, and how unacknowledged LSA counting works across broadcast and point-to-point segments. doc/developer/ospf.rst: add ospf-adj-pacing to the OSPF developer documentation toctree. doc/user/ospfd.rst: add four clicmd entries for the adjacency pacing interface commands: static limit, dynamic mode, dynamic thresholds, and no-form. Signed-off-by: Ravi Ravindran <rravindran@labn.net>
Add 7 test cases to test_ospf_adj_pacing_config.py covering static pacing configuration that was missing from initial test coverage: - config persistence (write memory / running-config) - persistence across ospfd restart - persistence across interface flap - no-command removes static config - boundary values (limit=1 minimum, limit=65535 maximum) - transition from static to dynamic mode - transition from dynamic to static mode Signed-off-by: Ravi Ravindran <rravindran@labn.net>
Implement RFC 4222 recommendation 4: rate-limit outbound LS Update packets on a per-neighbor basis using a configurable inter-packet gap. When enabled on an interface, outbound LSAs destined for a neighbor are queued in a per-neighbor send list (nbr->r4_send_queue) rather than written immediately. A per-neighbor timer (ospf_r4_nbr_send_timer) drains the queue at the configured rate, sending at most rec4_max_lsas LSAs per LS Update packet. The inter-packet gap is adapted using a multiplicative-increase / multiplicative-decrease (MIMD) algorithm based on the per-neighbor unacknowledged LSA count U: U > H: G = min(G * F, Gmax) -- gap grows, send rate slows U == 0: G = Gmin -- immediate reset to floor U <= L: G = max(G / F, Gmin) -- gap shrinks, send rate rises Defaults: initial/min gap 20 ms, max 1000 ms, factor 2, adjust-interval 1000 ms, high-water 100, low-water 2, max-lsas-per-update 1. New interface commands: ip ospf lsa-pacing ip ospf lsa-pacing initial-gap <ms> ip ospf lsa-pacing min-gap <ms> max-gap <ms> ip ospf lsa-pacing factor <F> ip ospf lsa-pacing adjust-interval <ms> ip ospf lsa-pacing max-lsas-per-update <N> ip ospf lsa-pacing low-watermark <L> high-watermark <H> Signed-off-by: Ravi Ravindran <rravindran@labn.net>
336a899 to
9427408
Compare
Add RFC4222 R4 coverage for LSA pacing behavior under functional, broadcast, opaque, CLI, and congested conditions. Signed-off-by: Ravi Ravindran <rravindran@labn.net>
9427408 to
b62e86d
Compare
Implements RFC 4222 Recommendation 4: per-neighbor LSA gap pacing.
When enabled on an interface, outbound LSAs are queued per-neighbor
and metered at a configurable inter-packet gap rather than sent
back-to-back. The gap adapts using MIMD: grows multiplicatively when
the unacknowledged LSA count exceeds a high-watermark, shrinks
multiplicatively when it falls below a low-watermark.
New commands (all interface-scoped):
ip ospf lsa-pacing
ip ospf lsa-pacing min-gap max-gap
ip ospf lsa-pacing initial-gap
ip ospf lsa-pacing factor
ip ospf lsa-pacing adjust-interval
ip ospf lsa-pacing max-lsas-per-update
ip ospf lsa-pacing low-watermark high-watermark
Defaults: min/initial gap 20 ms, max 1000 ms, factor 2,
adjust-interval 1000 ms, high-water 100, low-water 2, max-lsas 1.
This feature is independent of R5 adjacency pacing (also on this
branch) — neither gates the other.
Topotests added under tests/topotests/ospf_rfc4222_r4/: