ipsec, engine, imsd: the state file records the P-CSCF and a resume follows the kernel's SA peer
A resume compared PCSCF= against the kernel's SAs and, when they did not match, registered fresh without a word — a second initial REGISTER from an address whose registration is live; on KPN a fresh pair to the same P-CSCF meets the 21-minute refusal that costs the live registration (§13), what another P-CSCF does with it is unmeasured. The registration was made to whatever P-CSCF the SAs name (the SA setup takes imsd's previous pair out first), so that is where a resume belongs: SaPeer reads it from the dir-out transport policies — the one address a reqid-1 and a reqid-2 policy both name, each with imsd's fixed protected port as its selector's sport (reqid and mode alone are anyone's): the state text cannot tell the UE from the P-CSCF, a lone policy is not ours and a VPN's beside the pair changes nothing — TryResume resumes there, routes it, and says so when PCSCF= differs; the next fresh registration moves to PCSCF= and moves back when it fails before the SA setup and the old registration still stands (its address still on the link), as it already did between addresses, and its fallback Route is the P-CSCF it registers at, set before the 200's Service-Route is looked for — an empty Service-Route value counts as none — so a move never keeps the previous node's route. A file left by an attempt that got through the SA setup and did not live to record — to this or another P-CSCF — is recognised by its SA pair: the file's spi_uc/spi_us (a 0.3.x file carries them too) against the kernel's (FileNamesAnotherPair, Imsd:Ipsec, pinned in tests/Ipsec), instead of resumed with the wrong dialog; the state file gains pcscf, named in that line. Both fresh paths — the bring-up's and recovery's — now set their target themselves: with no PCSCF=, a bring-up that did not resume fails with its own message, and since no fresh one follows: a resumable pair whose attempt failed stays for the restart to resume, a pair nothing can resume — one the engine's rule wrote off during the attempt among them, a refresh refused with a 403 that carries a Retry-After or a binding a NOTIFY terminated under the re-REGISTER wait, decided once after both catch arms by its verdict (Reconnectable(), and with no fresh target Fresh(): a 401, a refusal with a Retry-After, the second refusal — with no fresh target every refusal reaches the reducer and its count travels in the holds, so the restart's refusal is the second, while with a target only a Retry-After does, as before — a refusal without one is not counted and the fresh registration follows at once; without the count a refused no-target resume would re-REGISTER on the same Call-ID at every RestartSec, the F16 loop) — is removed so the next bring-up finds nothing — and a recovery step that needs one restarts the daemon; the "PCSCF not set" check at the top of BringUp is gone, so a registration can be resumed with no setting at all. Three journal lines of the fresh-target path change with it: `resume connect failed; falling back to fresh` and `state file transport differs from SIP_TRANSPORT; fresh` end in `the next registration is a fresh one`, and a resume the engine writes off (a 403 with a Retry-After, a binding a NOTIFY terminated under the wait) logs `resume failed: … — written off by the network: the next registration is a fresh one` (§7.8). `no reg state file; resume falls back to fresh` keeps its text with a fresh target and ends in `nothing can resume it: the pair is removed` without one; the new `the state file names another SA pair (0x…/0x…[ to X]) than the kernel's (0x…/0x… to Y): another attempt's; …` line takes the same two tails. Tested: crafter-build test, 7 passed (tests/Engine: the refusal count's travel through the holds); the resume on the SA peer with the fresh target absent in builds of this series on the dev phone (KPN), and the no-target bring-up there with a resume that failed on a transport mismatch (the pair kept, the restart resumed) and with the state file removed (the pair removed, the next bring-up found nothing); the two dead-resume lines' no-target tail — a missing file, a file with another pair's SPIs — and the pair's removal on the tip's host build (bench 23f: a fake modem on PATH, a real xfrm in a namespace; a file with the pair's SPIs but another P-CSCF's address passes the check and the resume runs — `RESUME via existing SA`, the connect then failing in the namespace); on the dev phone (bench 41, the tip's build) a resume on a file without its pcscf key, the live pair's SPIs.
This commit is contained in:
parent
21cb0f870d
commit
40a960f749
7 changed files with 448 additions and 84 deletions
|
|
@ -103,8 +103,10 @@ export namespace imsd::engine {
|
|||
// due it also says the old registration is gone (KPN drops it the
|
||||
// moment an initial REGISTER arrives, throttled or not), as does a
|
||||
// fresh attempt that got past the SA setup, a binding the network
|
||||
// terminated, and a resume that failed at bring-up (OldGone) — no
|
||||
// reconnect over the old SA from then on. Every wait
|
||||
// terminated, and a bring-up resume that failed while a fresh
|
||||
// registration follows (OldGone; with no fresh target a resumable pair
|
||||
// is kept for the restart, §7.2 step 1) — no reconnect over the old SA
|
||||
// from then on. Every wait
|
||||
// is either the self-imposed backoff, which a user's Dial cuts short,
|
||||
// or the network's own word (a Retry-After, the schedule after a
|
||||
// refused or throttled fresh attempt, a terminated binding's probation
|
||||
|
|
@ -164,16 +166,18 @@ export namespace imsd::engine {
|
|||
double freshHold = 0;
|
||||
int throttleMinutes = ThrottleMinutesMin;
|
||||
bool oldGone = false; // the old registration is gone: the next process has nothing to reconnect over
|
||||
// a schedule that has advanced is kept too, until a success resets it, and so is the gone verdict
|
||||
bool Any() const { return hold > 0 || freshHold > 0 || throttleMinutes > ThrottleMinutesMin || oldGone; }
|
||||
int refusals = 0; // refused reconnects counted so far (RefusalsBeforeFresh): a restart's refusal is the next one, not the first
|
||||
// a schedule that has advanced is kept too, until a success resets it, and so are the gone verdict and the count
|
||||
bool Any() const { return hold > 0 || freshHold > 0 || throttleMinutes > ThrottleMinutesMin || oldGone || refusals > 0; }
|
||||
};
|
||||
Held Holds(double now) const { return {std::max(0.0, holdUntil - now), std::max(0.0, freshHoldUntil - now), throttleMinutes, oldGone}; }
|
||||
Held Holds(double now) const { return {std::max(0.0, holdUntil - now), std::max(0.0, freshHoldUntil - now), throttleMinutes, oldGone, refusals}; }
|
||||
// A restarted process takes them over; nothing here shortens a
|
||||
// hold it already has, or lengthens one past RetryAfterMax.
|
||||
void Restore(double now, const Held& h) {
|
||||
if (h.hold > 0) holdUntil = std::max(holdUntil, now + std::min(h.hold, static_cast<double>(RetryAfterMax)));
|
||||
if (h.freshHold > 0) freshHoldUntil = std::max(freshHoldUntil, now + std::min(h.freshHold, static_cast<double>(RetryAfterMax)));
|
||||
throttleMinutes = std::clamp(std::max(throttleMinutes, h.throttleMinutes), ThrottleMinutesMin, ThrottleMinutesMax);
|
||||
refusals = std::max(refusals, h.refusals);
|
||||
if (h.oldGone) OldGone(); // no reconnect over SAs the network has written off
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue