fix(ospf): сопоставлять IPv6 адреса и исключать неоднозначный BFD
This commit is contained in:
1 parent
1b63e3ec43
commit
e1617f1472
11 files changed
+201
-19
No files matched your search
@@ -29,3 +29,20 @@ test("topology does not infer Down from missing or transitional evidence", () =>
|
||||
assert.equal(ospfNodeState(["2-Way", "Down"]), "2-Way")
|
||||
assert.equal(ospfNodeState(["Full", "Unknown"]), "Full")
|
||||
})
|
||||
|
||||
test("BFD correlation compares IPv6 addresses canonically within their interface scope", () => {
|
||||
const neighbor = { localRouter: "1", localIface: "gre-own", neighborAddress: "FE80:0000:0000:0000:0000:0000:0000:0002%gre-own", state: "Full" as const }
|
||||
const iface = { serverId: "1", interfaceName: "gre-own", active: true, useBfd: true }
|
||||
const session = { serverId: "1", iface: "gre-own", remoteAddr: "fe80::2", state: "Down" as const, multihop: false }
|
||||
assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [session]), [neighbor])
|
||||
for (const remoteAddr of ["fe80::3", "fe80::2%other", "fe80::2%", "fe80::2%gre-own%extra"]) assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [{ ...session, remoteAddr }]), [])
|
||||
for (const neighborAddress of ["fe80::2%other", "fe80::2%", "fe80::2%gre-own%extra"]) assert.deepEqual(ospfBfdConflicts([{ ...neighbor, neighborAddress }], [iface], [session]), [])
|
||||
})
|
||||
|
||||
test("invalid peer strings and stale duplicate sessions cannot prove an OSPF/BFD conflict", () => {
|
||||
const neighbor = { localRouter: "1", localIface: "gre-own", neighborAddress: "198.18.153.2", state: "Full" as const }
|
||||
const iface = { serverId: "1", interfaceName: "gre-own", active: true, useBfd: true }
|
||||
const session = { serverId: "1", iface: "gre-own", remoteAddr: neighbor.neighborAddress, state: "Down" as const, multihop: false }
|
||||
assert.deepEqual(ospfBfdConflicts([neighbor], [iface], [session, { ...session, dataUnknown: true }]), [])
|
||||
for (const address of ["peer.example", "999.18.153.2", "0198.18.153.2", "fe80::invalid", ":::", "fe80::2/64", ""]) assert.deepEqual(ospfBfdConflicts([{ ...neighbor, neighborAddress: address }], [iface], [{ ...session, remoteAddr: address }]), [])
|
||||
})
|
||||
+14
-3
@@ -19,11 +19,22 @@ export function ospfBfdConflicts<T extends { localRouter: string; localIface: st
|
||||
interfaces: Array<{ serverId: string; interfaceName: string; useBfd?: boolean | null; active?: boolean | null; dataUnknown?: boolean }>,
|
||||
sessions: Array<{ serverId: string; iface: string; remoteAddr: string; state: BfdState; multihop?: boolean | null; dataUnknown?: boolean }>,
|
||||
): T[] {
|
||||
const ip = (value: string) => value.split("%")[0]?.toLowerCase() ?? ""
|
||||
const ip = (value: string, iface: string): string | null => {
|
||||
const [address, zone, ...extra] = value.trim().split("%")
|
||||
if (!address || extra.length || (zone !== undefined && zone !== iface)) return null
|
||||
if (address.includes(":")) {
|
||||
if (!/^[0-9a-f:.]+$/i.test(address)) return null
|
||||
try { return new URL(`http://[${address}]/`).hostname } catch { return null }
|
||||
}
|
||||
const parts = address.split(".")
|
||||
return parts.length === 4 && parts.every(p => /^(0|[1-9]\d{0,2})$/.test(p) && Number(p) <= 255) ? address : null
|
||||
}
|
||||
return neighbors.filter(n => {
|
||||
if (n.dataUnknown || n.state !== "Full" || !n.neighborAddress || !n.localIface) return false
|
||||
if (!interfaces.some(i => !i.dataUnknown && i.active === true && i.useBfd === true && i.serverId === n.localRouter && i.interfaceName === n.localIface)) return false
|
||||
const matching = sessions.filter(s => !s.dataUnknown && s.multihop === false && s.serverId === n.localRouter && s.iface === n.localIface && ip(s.remoteAddr) === ip(n.neighborAddress!))
|
||||
return matching.length === 1 && (matching[0]!.state === "Down" || matching[0]!.state === "AdminDown")
|
||||
const peer = ip(n.neighborAddress, n.localIface)
|
||||
if (!peer) return false
|
||||
const matching = sessions.filter(s => s.multihop === false && s.serverId === n.localRouter && s.iface === n.localIface && ip(s.remoteAddr, s.iface) === peer)
|
||||
return matching.length === 1 && !matching[0]!.dataUnknown && (matching[0]!.state === "Down" || matching[0]!.state === "AdminDown")
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user