Hi David, Thanks for logging this, and for the question on the RFC. Section 13.5 covers only Link State Acknowledgement packets. The decision sits in Section 13.4. Step (5f) of Section 13 sends the router there. Section 13.4 treats an LSA as self-originated in two cases. The advertising router equals our Router ID, or the LSA is a network-LSA whose Link State ID is one of our own interface addresses. When we still want to originate that LSA, we advance the sequence and originate a new instance. Section 12.1 identifies an LSA by type, Link State ID, and advertising router together, and the instance we originate carries our Router ID. The received copy still carries the other router, so raising its sequence leaves that other router named on the LSA. When we no longer want to originate the received LSA, Section 13.4 says to flush it: set the age to MaxAge and flood it, instead of updating it. The third example is this LSA. The Link State ID is one of our addresses, and the advertising router is not our Router ID. The text calls that shape rare, and says it indicates that the Router ID has changed. The required action follows the shape of the LSA. An address that has moved onto this router has the same shape. Section 12.4.2 repeats the same flush. The patch is against current master, in ospf_receive_lsupd(). It sits in the block that already logs "Received unexpected self-originated LSA". When the LSA is a network-LSA and the advertising router is not us, the received copy is aged to MaxAge, installed, and flooded. That is the flush ospf_advance_lsa() already uses when no live copy exists. A network-LSA that we advertised ourselves still goes to ospf_advance_lsa(), so a normal refresh is unchanged. If we are the DR, the network-LSA we originate is a different LSA: same Link State ID, our Router ID. That one stays on the normal origination path. --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -595,6 +595,13 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id))))) { OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA"); + if ((lsa_type == LSA_T_NET) && (lsa.rt != p->router_id)) + { + lsa.age = LSA_MAXAGE; + en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body); + ospf_flood_lsa(p, en, NULL); + continue; + } ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body); continue; } Best regards, Shariq Faran ________________________________ From: David Petera via Bird-users <bird-users@network.cz> Sent: Friday, October 2, 2026 7:22 PM To: bird-users@network.cz <bird-users@network.cz> Subject: Re: OSPFv2: a Network LSA from another router is refreshed when its ID is a local address CAUTION: This is an external email. Please be very careful when clicking links or opening attachments. See the URL nok.it/ext for additional information. Hi Shariq, thanks a lot for the detailed report. We have it in our internal issues now. Patch would be appreciated as it would speed things up. Also please could you point us to the exact part of the RFC 2328 that states your suggested behavior is the correct one, or do you think we are dealing with an undefined corner case? From a brief glance at the RFC 2328, Section 13.5 the current behavior seems to be also compliant with the text even though it might lead to weird advertisement loop. However it is completely possible I am overlooking the part were it is mentioned. Thank you again and happy routing, David David Petera (he/him) | BIRD Tech Support | CZ.NIC, z.s.p.o. On 9/29/26 07:45, Shariq Faran (Nokia) via Bird-users wrote: Hello, OSPFv2 is right to treat a Network LSA as self-originated when its Link State ID is one of our interface addresses, even if another router advertised it (RFC 2328 13.4). The next step is wrong. If this router is not originating that LSA, it should be flushed at MaxAge. Instead, when a copy younger than MaxAge is already in the local LSDB, BIRD refreshes it and leaves the other router's ID in the Advertising Router field. The two routers then exchange that same LSA continuously, one at age 0 and the other at age 3600. This is in proto/ospf/lsupd.c, in ospf_receive_lsupd(), at the 13.(5f) / 13.4 check, and in ospf_advance_lsa() in proto/ospf/topology.c. It is present in BIRD 2.0.8. The same check is still in current master as of 29 Sep 2026. This is not a security issue. The receive check is: lsa.rt == router_id OR (OSPFv2 AND type is Network AND ospf_addr_is_local()) ospf_addr_is_local() walks every OSPF interface in the area and compares the Link State ID with the interface address. A match calls ospf_advance_lsa(). That function has two branches. If the local copy exists and its age is still under MaxAge, it does only this: sequence = received sequence + 1 age = 0 It does not change the Advertising Router. It then floods that copy. The log line is printed only in this branch: Advancing LSA: Type: 2002, Id: 10.0.0.1, Rt: 192.0.2.1, Seq: 80000002 If there is no local copy, or the local copy is already MaxAge, the same function installs the received LSA at MaxAge and floods that. That second branch is the flush. The loop needs the first branch, so it needs a young copy to still be present. How the two routers keep it going Call the router named on the LSA R1 (192.0.2.1). Call the router that now has the address R2 (192.0.2.2). The Link State ID is 10.0.0.1. 1. R1 no longer has 10.0.0.1. The LSA still carries R1's router id, so R1 takes the self-originated path. Its copy is MaxAge, so it floods age 3600. 2. R2 still has 10.0.0.1, so it also takes the self-originated path. Its copy is younger than MaxAge, so it sets age 0 and sequence plus 1, leaves the Advertising Router as 192.0.2.1, and floods that. 3. R1 receives a newer copy that still carries its own router id. Its local copy is MaxAge, so it floods age 3600 again. Each pass is limited by MinLSArrival. A normal Network LSA refresh is 1800 seconds. SPF runs on every pass. How to reproduce Two routers, one OSPFv2 area, broadcast interfaces. No customer network is involved. These addresses are only an example. R1 router id 192.0.2.1 R2 router id 192.0.2.2 A broadcast link on which R1 is DR, interface address 10.0.0.1/30 R1 has a full neighbor, so it originates: type 2002, id 10.0.0.1, advertising router 192.0.2.1 While that LSA is still in R2's LSDB and its age is under 3600: 1. Configure 10.0.0.1/30 on R2, on an OSPF interface in the same area. 2. After that, delete 10.0.0.1/30 from R1. R2 must already hold a copy younger than MaxAge at step 2. That is the copy it will advance. When this will not show up The flush sticks if the address is deleted on R1 first, and R2 is given 10.0.0.1 only after that Network LSA is age 3600 or gone from R2. There is then no young copy to advance. Deleting 10.0.0.1/30 on R1 and configuring the same address back on R1 does not start the loop. A point-to-point link does not originate a Network LSA, so it does not hit this either. Suggested fix For a Network LSA whose Advertising Router is not the local router id, use the flush that ospf_advance_lsa() already uses when the local copy is missing or MaxAge. Do that even if a young copy exists. A Network LSA this router really originated (lsa.rt == router_id) should still advance as it does today. Against current master, inside the existing self-originated block: OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA"); if ((lsa_type == LSA_T_NET) && (lsa.rt != p->router_id)) { lsa.age = LSA_MAXAGE; en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body); ospf_flood_lsa(p, en, NULL); continue; } ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body); continue; I can send this as a patch against master if that is useful. Thanks, Shariq Faran