fix(ipsec): учитывать несколько сессий без ложного офлайн

This commit is contained in:
Denozordec committed 2026-10-10 20:42:58 +07:00
1 parent cb6ec09312
commit 60b2e099f0
10 files changed
+272 -24

No files matched your search

+13 -11
View File
@@ -248,17 +248,16 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
const shared = selectSharedIke2Identity(state.identities, ike2PeerNames)
const sharedRosId = shared?.[".id"] ? String(shared[".id"]) : undefined
const activeByRemote = new Map<string, RosIpsecActivePeer>()
const ambiguousRemoteIds = new Set<string>()
const activeByRemote = new Map<string, RosIpsecActivePeer[]>()
for (const a of state.active) {
if (a.state !== "established") continue
const rid = String(a.id ?? a["remote-id"] ?? "").trim()
if (rid) {
if (activeByRemote.has(rid)) ambiguousRemoteIds.add(rid)
else activeByRemote.set(rid, a)
const sessions = activeByRemote.get(rid) ?? []
sessions.push(a)
activeByRemote.set(rid, sessions)
}
}
for (const rid of ambiguousRemoteIds) activeByRemote.delete(rid)
const subjectCounts = new Map<string, number>()
const cnCounts = new Map<string, number>()
@@ -286,8 +285,8 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
}
const out: IpsecClientDto[] = []
const sessionClaims = new Map<RosIpsecActivePeer, IpsecClientDto[]>()
const claimSession = (active: RosIpsecActivePeer | undefined) => {
const sessionClaims = new Map<RosIpsecActivePeer[], IpsecClientDto[]>()
const claimSession = (active: RosIpsecActivePeer[] | undefined) => {
if (!active) return
const claims = sessionClaims.get(active) ?? []
claims.push(out[out.length - 1]!)
@@ -316,6 +315,8 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
const byCommonName = uniqueCn && !subjectCounts.has(cn) ? activeByRemote.get(cn) : undefined
const active = bySubject ?? byCommonName
?? (identity?.["remote-id"] ? findActive(String(identity["remote-id"]).trim()) : undefined)
// Multiple established sessions confirm presence, but no single lease/uptime.
const single = active?.length === 1 ? active[0] : undefined
out.push({
id: `${server.id}:${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
rosId: `${identity ? "identity" : "cert"}:${String(identity?.[".id"] ?? cert[".id"] ?? certName)}`,
@@ -333,8 +334,8 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
modeConfigName: mcName || undefined,
peerName: (identity?.peer ?? "").trim() || undefined,
online: Boolean(active),
activeAddress: active?.["dynamic-address"] || active?.["remote-address"] || active?.address || undefined,
activeSince: active?.uptime || active?.established || undefined,
activeAddress: single?.["dynamic-address"] || single?.["remote-address"] || single?.address || undefined,
activeSince: single?.uptime || single?.established || undefined,
disabled: asBool(identity?.disabled),
comment: comment || undefined,
managed: isUser || isIpsecManagedComment(comment),
@@ -352,6 +353,7 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
const mcName = (i["mode-config"] ?? "").trim()
const remoteId = (i["remote-id"] ?? "").trim()
const active = remoteId ? findActive(remoteId) : undefined
const single = active?.length === 1 ? active[0] : undefined
out.push({
id: `${server.id}:identity:${rosId}`,
rosId: `identity:${rosId}`,
@@ -365,8 +367,8 @@ export function mapClients(state: IpsecServerState): IpsecClientDto[] {
modeConfigName: mcName || undefined,
peerName: (i.peer ?? "").trim() || undefined,
online: Boolean(active),
activeAddress: active?.["dynamic-address"] || active?.["remote-address"] || active?.address || undefined,
activeSince: active?.uptime || active?.established || undefined,
activeAddress: single?.["dynamic-address"] || single?.["remote-address"] || single?.address || undefined,
activeSince: single?.uptime || single?.established || undefined,
disabled: asBool(i.disabled),
comment: i.comment || undefined,
managed,
+29 -1
View File
@@ -122,7 +122,11 @@ state.certs = state.certs.filter(c => c[".id"] !== "*AMBIGUOUS")
Object.assign(certificate, { organization: "Example, Ltd", unit: "QA+Operations" })
state.active[0]!.id = 'C=US, O=Example, Ltd, OU=QA+Operations, CN=QA, Team+VPN="client"\\Lab'
state.active.push({ ...state.active[0]!, ".id": "*DUPSESSION", "dynamic-address": "198.18.79.251" })
assert.equal(mapClients(state).find(c => c.certName === "client")?.online, false); checks++
const multiCertificate = mapClients(state).find(c => c.certName === "client")!
assert.equal(multiCertificate.online, true)
assert.equal(multiCertificate.activeAddress, undefined)
assert.equal(multiCertificate.activeSince, undefined)
assert.equal(multiCertificate.sessionAmbiguous, undefined); checks++
state.active.pop()
Object.assign(certificate, { state: "New York", locality: "Albany" })
state.active[0]!.id = 'C=US, S=New York, L=Albany, O=Example, Ltd, OU=QA+Operations, CN=QA, Team+VPN="client"\\Lab'
@@ -169,4 +173,28 @@ const uniqueClients = mapClients(aliasesState)
assert.equal(uniqueClients.find(c => c.kind === "cert")?.online, true)
assert.equal(uniqueClients.find(c => c.kind === "psk")?.online, false)
assert(uniqueClients.every(c => !c.sessionAmbiguous)); checks++
// Repeated observed IDs are sessions of one principal, not proof of being offline.
aliasesState.active.push({ ...aliasesState.active[0]!, ".id": "*SECOND", "dynamic-address": "198.18.113.251", uptime: "2m" })
for (const reverse of [false, true]) {
if (reverse) aliasesState.active.reverse()
const clients = mapClients(aliasesState)
const cert = clients.find(c => c.kind === "cert")!
assert.equal(cert.online, true)
assert.equal(cert.activeAddress, undefined)
assert.equal(cert.activeSince, undefined)
assert.equal(mapServerSummary(aliasesState, clients).clientsOnline, 1); checks++
}
aliasesState.identities[1]!["remote-id"] = "key-id:shared.example"
assert(mapClients(aliasesState).every(c => c.sessionAmbiguous && !c.online && !c.activeAddress && !c.activeSince)); checks++
aliasesState.certs = []
aliasesState.identities = [aliasesState.identities[1]!]
const multiplePsk = mapClients(aliasesState)[0]!
assert.equal(multiplePsk.online, true)
assert.equal(multiplePsk.activeAddress, undefined)
assert.equal(multiplePsk.activeSince, undefined)
assert.equal(multiplePsk.sessionAmbiguous, undefined); checks++
aliasesState.active[1]!.state = "dying"
assert.equal(mapClients(aliasesState)[0]?.activeAddress, aliasesState.active[0]!["dynamic-address"]); checks++
aliasesState.active[0]!.state = "starting"
assert.equal(mapClients(aliasesState)[0]?.online, false); checks++
console.log(`ipsec-state: ${checks} checks passed`)