ip: preserve connected routes during protocol replay - #703
hcaldicott wants to merge 1 commit into
Conversation
rjarry
left a comment
There was a problem hiding this comment.
Hey Harrison,
Your check is a bit loose. See how to make it more robust below.
Also could you remove any mention that you were assisted by AI? It does not bring much value and we prefer to keep the git history leaner.
Thanks!
3b4929b to
b973e2e
Compare
Good pickups - I will address these and re-run my lab tests! |
b973e2e to
25cac2c
Compare
|
I have updated the PR. I am now rebuilding our physical lab on this to re-test the corrected behaviour. Converting back to a draft until my testing is completed. |
|
Hey @hcaldicott, did you make progress with your testing? |
Sorry for the slow turnaround - I've been quite busy and had kind of forgotten to finish this off - thanks for the bump to remind me. I have now re-tested the reworked guards on the physical cluster. Converting back from draft. |
|
Testing is done — the reworked guards are now validated on our physical lab (4x Lenovo SE350, X722 fabric, iBGP EVPN full mesh, FRR 10.6.1 with the grout dplane plugin). Setup: the PR head cherry-picked onto our production branch, packaged through our staging pipeline (unit suite runs in the package build), and installed on one fabric node carrying live EVPN services. The node's address-owned routes are two fabric /31s (origin link), the /32 router-id (origin internal) and the per-interface IPv6 link-local /64s. Results: Ownership guards, exercised directly against the real prefixes: deleting any of the address-owned routes is refused with EBUSY; a protocol-style re-add of a connected /31 via the fabric peer is accepted as a no-op and the route keeps its link origin. |
| if (is_addr_owned_route(req->vrf_id, GR_IFACE_ID_UNDEF, &req->dest.ip, req->dest.prefixlen)) | ||
| return api_out(0, 0, NULL); |
There was a problem hiding this comment.
1. Link-local ownership lookup misses 🐞 Bug ≡ Correctness
Both IPv6 ownership checks use GR_IFACE_ID_UNDEF, so link-local destinations are scoped to interface zero and cannot match connected routes stored under their owning interface. An FRR replay using a nexthop ID can therefore replace the connected route, while the subsequent withdrawal also looks under the wrong scope.
Agent Prompt
## Issue description
IPv6 connected-route ownership checks use `GR_IFACE_ID_UNDEF`. For link-local prefixes this produces a different RIB key from the interface-scoped key used when the connected route was installed, allowing protocol replay to replace it.
## Issue Context
`addr6_linklocal_scope()` embeds the interface ID into link-local RIB keys. Resolve the add request's nexthop before checking ownership and use its interface ID; ensure deletion detects address-owned link-local routes despite lacking an interface field in its request.
## Fix Focus Areas
- modules/ip6/control/route.c[403-485]
- modules/ip6/control/ip6.h[23-35]
- modules/ip6/control/address.c[201-230]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
@hcaldicott do you think this is a legit concern? I don't think FRR would try to replace a link local route. That does not make much sense to me. And we don't have any interface ID to use here anyway.
rjarry
left a comment
There was a problem hiding this comment.
Thanks for testing again!
During control-plane replay, a routing daemon can briefly advertise a connected prefix before it has learned the interface address, then withdraw it again once it reconciles. route4_add() replaced the address-owned connected route with the protocol route, so the later withdrawal removed the only route to the prefix while the address stayed configured, cutting off the connected network until the address was re-added. Add is_addr_owned_route() which checks that the route's RIB origin is internal and that its nexthop carries the local address flags. Accept protocol adds for such prefixes as a silent no-op and refuse deleting them with EBUSY. Deletions resolve their nexthop with an exact-prefix lookup first so the nexthop type of a covering route is never used to delete a more specific prefix; the previous longest-prefix lookup remains as the fallback when no exact entry exists. Add a smoke test replaying the add/withdraw cycle over configured IPv4 and IPv6 addresses, checking the withdrawal is refused and the connected routes survive. Signed-off-by: Harrison Caldicott <harrison@itsfubar.com.au> Reviewed-by: Robin Jarry <rjarry@redhat.com>
25cac2c to
0b6e7cf
Compare
During control-plane replay, a routing daemon can briefly advertise a
connected prefix before it has learned the interface address, then
withdraw it again once it reconciles.
route4_add()replaces theaddress-owned connected route with the protocol route, so the later
withdrawal removes the only route to the prefix while the address stays
configured — the connected network is unreachable until the address is
removed and re-added.
This change treats address-owned connected routes as authoritative:
protocol adds for such prefixes are accepted as a silent no-op, and API
deletes of internal-origin routes are refused the same way. Deletions now
resolve their nexthop with an exact-prefix lookup first, so the nexthop
type of a covering route is never used to delete a more specific prefix;
the previous longest-prefix lookup remains as the fallback when no exact
entry exists. Same handling for both address families.
Found while integrating FRR restart handling for EVPN multihoming (#698),
but the race only needs a routing daemon replaying routes over a
configured address.
Testing
connected_route_ownership_test.shreplays theadd/withdraw cycle over configured IPv4 and IPv6 addresses and asserts
the connected routes survive with their
linkorigin.main): the test fails at thefirst assertion — after add/withdraw,
172.16.0.0/24is gone from theRIB entirely.
ip_loadbalance_test.shconfirms ordinary route add/del and ECMPbehaviour is unchanged (AlmaLinux 9 container, arm64).
Related: #698