ospfd: fix wrong/inactive next-hop interface for inter-area routes via virtual link#22331
ospfd: fix wrong/inactive next-hop interface for inter-area routes via virtual link#22331mhchann wants to merge 2 commits into
Conversation
Greptile SummaryFixes a bug where inter-area routes whose next-hop is resolved via an OSPF virtual link are installed with an extra inactive path using the wrong outgoing interface — a consequence of
Confidence Score: 4/5The change is safe to merge; it only activates when virtual links are configured, leaves all other code paths untouched, and the new gateway-lookup function follows an established pattern already used elsewhere in the same file. The fix touches the hot path inside ospf_route_copy_nexthops_from_vertex — called on every SPF run — and the extra prefix-match check now fires for every numbered, non-INADDR_ANY nexthop on any OSPF instance that has virtual links. The logic is sound and the fallback (if (roi) oi = roi) is conservative, but a second set of eyes on the routing-table internals is worthwhile before landing. ospfd/ospf_route.c — specifically the new guard block and its interaction with the outer ospf_path_exist de-duplication check. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ospf_route_copy_nexthops_from_vertex] --> B{spf_dry_run?}
B -- yes --> G
B -- no --> C[ospf_if_lookup_by_lsa_pos\narea, nexthop->lsa_pos]
C --> D{oi != NULL AND\nrouter != INADDR_ANY AND\nvlinks non-empty AND\noi not unnumbered?}
D -- no --> G
D -- yes --> E{prefix_match\noi->connected subnet\nvs gateway?}
E -- match correct interface --> G
E -- no match wrong interface --> F[ospf_if_lookup_by_gateway\nospf->oiflist LPM]
F --> F2{roi found?}
F2 -- yes --> F3[oi = roi correct egress interface]
F2 -- no --> G
F3 --> G{oi != NULL AND\npath not already in list?}
G -- yes --> H[ospf_path_new add to paths]
G -- no --> I[skip]
Reviews (1): Last reviewed commit: "ospfd: fix wrong/inactive nexthop interf..." | Re-trigger Greptile |
| if (!area->spf_dry_run) { | ||
| oi = ospf_if_lookup_by_lsa_pos(area, nexthop->lsa_pos); | ||
|
|
||
| /* |
There was a problem hiding this comment.
We call ospf_if_lookup_by_lsa_pos in multiple spots, presumably we can have the same problem in those spots. Why is it not necessary for those?
There was a problem hiding this comment.
Good point - there's no reason to special-case one consumer.
Reworked the fix to address it at the root rather than recovering at the consumer.
The virtual-link next-hop is the only one whose lsa_pos is computed in
a different area (the transit area, in ospf_vl_set_params()) than the
area it is later resolved against (the backbone, in
ospf_route_copy_nexthops_from_vertex()). Instead of detecting that
mismatch downstream, I now resolve the egress interface once where
lsa_pos is valid — in the transit area — and record its ifindex on
the next-hop (struct vertex_nexthop). The consumer uses that ifindex
directly (ospf_if_lookup_by_ifindex()); ifindex is not area-relative,
so it is correct regardless of which area's table is being populated.
The other ospf_if_lookup_by_lsa_pos() callers are unchanged: there the
lsa_pos is always produced for the same area it is resolved against, so
they were never affected. ifindex stays 0 for non virtual-link
next-hops, so they keep using the lsa_pos path — the change is a no-op
for them.
This also removes the gateway/subnet heuristic from the previous version
and disambiguates correctly when multiple interfaces sit on the same
subnet.
Updated; PTAL.
Reproduces the bug where inter-area routes reached through an OSPF virtual link are installed with an extra next-hop that has the correct gateway but a wrong (backbone) outgoing interface, which zebra marks inactive. Three routers r1-r2-r3: r2 is an ABR on the backbone, r3 reaches the backbone only via an area 10 virtual link. The virtual link is configured at runtime, after the physical adjacencies are full, so the backbone interface holds the colliding LSA position - this ordering is required to reproduce the issue (see the module docstring). The test then asserts that on r2 the area 20 prefixes behind r3 are installed with an active next-hop via the transit interface (r2-eth1) and no next-hop via the backbone interface (r2-eth0). Fails before the ospf_route_copy_nexthops_from_vertex() fix with "bogus next-hop via r2-eth0", passes after it. Signed-off-by: mhchann <mhchann082@gmail.com>
Inter-area routes reached through a virtual link could be installed with a wrong, inactive egress interface (one bogus path plus the correct one). The virtual-link nexthop is resolved in the transit area, but the backbone route calculation re-derives the interface with ospf_if_lookup_by_lsa_pos() against the backbone's own interface list. Because lsa_pos is area-relative, this matches an unrelated backbone interface, and the bogus path survives de-duplication because its ifindex differs from the correct one. Fix it at the root instead of recovering at the consumer: resolve the egress interface once, in the transit area where lsa_pos is valid (ospf_vl_set_params), record its ifindex on the nexthop, and have the consumer (ospf_route_copy_nexthops_from_vertex) use that ifindex directly via the new ospf_if_lookup_by_ifindex() helper. ifindex is not area-relative, so no gateway/subnet heuristic is needed and multiple interfaces on the same subnet are not mis-disambiguated. The change is a no-op for non virtual-link nexthops, whose ifindex stays 0. Signed-off-by: mhchann <mhchann082@gmail.com>
8a3941a to
28015f4
Compare
|
Tick the box to add this pull request to the merge queue (same as
|
Problem
In an OSPF virtual-link topology, inter-area routes whose next-hop is the far
end of the virtual link (and the networks behind it) are installed with an
extra next-hop that has the correct gateway but a wrong outgoing interface,
which zebra then marks inactive. Reproduced on the 10.6.1 release:
The OSPF router routing table shows where it comes from — the backbone
router-route to the virtual-link endpoint resolves to the wrong interface:
Topology
r2 is an ABR (backbone + transit area 10). r3 reaches the backbone only via an
area 10 virtual-linkto r2. Default broadcast networks.Root cause
struct vertex_nexthopidentifies the egress interface only bylsa_pos, anindex into one area's router-LSA links — i.e. it is area-relative. The
virtual-link next-hop's
lsa_posis computed in the transit area(
ospf_vl_set_params()), but the backbone router-route to the ABR is built byospf_intra_add_router()→ospf_route_copy_nexthops_from_vertex()witharea == backbone, soospf_if_lookup_by_lsa_pos()interprets thetransit-area position against the backbone router-LSA and selects whatever
interface occupies that position there.
This collides whenever the real backbone interface holds the colliding LSA
position — notably when the virtual link is configured at runtime, after the
interfaces (the usual operational order): the VLINK pseudo interface is then
created last and the backbone interface keeps position 0. The bogus path
survives de-duplication (different ifindex) and is inherited by every
inter-area route through that ABR.
Note this is not limited to setups needing
abr-type standardon the farend: in the canonical virtual-link use case — repairing a partitioned
backbone — both endpoints own backbone interfaces, are ABRs under the default
abr-type, and hit this with no special configuration.
Fix (Update)
Resolve the virtual-link egress interface once, in the transit area where
lsa_posis valid (ospf_vl_set_params()), record itsifindexon thenext-hop (
struct vertex_nexthop), and have the consumer(
ospf_route_copy_nexthops_from_vertex()) use thatifindexdirectly via thenew
ospf_if_lookup_by_ifindex()helper.ifindexis not area-relative, so itis correct regardless of which area's table is being populated, and it
disambiguates correctly when multiple interfaces share a subnet.
ifindexstays 0 for non virtual-link next-hops, so they keep resolving vialsa_pos- the change is a no-op for them, and the otherospf_if_lookup_by_lsa_pos()callers (wherelsa_posand the area alwaysmatch by construction) are left untouched.
Testing
ospf_vlink_nexthop(3 routers; the virtual link is configuredat runtime via vtysh, which is required to reproduce — see the module
docstring). On 10.6.1 and master it fails before this change with
bogus next-hop via r2-eth0 (bug present)and passes after it(
3 passed).ospf abr-type standardin the test because configuring a virtuallink on a non-ABR is rejected, and with the default abr-type a router with
two non-backbone areas is not an ABR until the virtual link itself attaches
it to the backbone.
ospf_topo1suite: 11 passed, unchanged.