engine: register fresh when the refresh is refused or nothing answers
After bring-up the daemon recovered a dead protected flow by reconnecting through the existing SA and refreshing, on a 5 s → 5 min backoff, forever. That was the whole recovery: a refresh the network refused, or a P-CSCF that stopped answering, kept it in that loop on the same Call-ID and SA until someone restarted the service. O2 UK, 2026-09-24: the 30-minute refresh answered 503, 21 refusals over 70 min; after a manual restart the new registration's flow died 8 s in and no connect over its SA succeeded for 2 h 39 min; a second restart registered within two seconds (13:27:55 → 13:27:57 in the journal; ims ledger F16). Do in place the rest of what a restart does: when the refresh keeps being refused, or reconnect cycles keep ending without an answer unless the modem reports no packet service, register fresh. The fresh registration is the expensive step, so a failed one waits before the next: on a schedule (21 min, growing to 60) after a refusal or a throttle, on a backoff after silence, and a Retry-After from the network is honoured up to an hour. What the network set — a Retry-After, the schedule after a refused or throttled attempt — is kept across a restart in /run/imsd.holds, for the address it was set on. A refused initial registration at bring-up — a final from 300 up to the unprotected REGISTER (its 401 is the challenge), any final but 200 to the protected one, a second 401 (rejected credentials) included — now waits on the same schedule instead of exiting for a systemd restart every two minutes. The policy is a pure reducer in Imsd:Engine (RegRecovery), shaped like the call machine: the daemon supplies the clock and the network, the reducer holds the deadlines and names the next step, and tests/Engine pin it. tests/Engine now exits 0 or 1 instead of its failure count: with more than 255 checks a count of 256 would exit 0, and crafter-build reads 77 as a skipped suite. The bring-up's deferral is logged in the policy's form: `bring-up deferred: <why> — attempt N, next fresh registration in M min` becomes `bring-up deferred: <why>; next: fresh registration in M min`. A bring-up writes the verdict that the old registration is gone as well: a start that goes on to register fresh — its resume failed, or there was nothing to resume over — records it before the attempt, and when the attempt then fails without an answer the daemon exits with the file in place. The next start on the same address and boot does not resume, where 0.3.9 retried the resume: it registers fresh at once. On KPN, with the old registration in fact still live — a start during an outage, staged on the dev phone — that attempt was throttled, and the 21-min schedule began. A bring-up does not retry its resume through an outage the way recovery reconnects. Tested on the dev phone (KPN) in builds of this series; not every path has run there.
This commit is contained in:
parent
46bf363915
commit
142adf057c
4 changed files with 806 additions and 87 deletions
30
README.md
30
README.md
|
|
@ -273,6 +273,7 @@ PCSCF=2001:db8::105
|
|||
| `LOCAL` | *(auto)* | UE address; default = the global IPv6 on `DEV` |
|
||||
| `USER_AGENT` | `imsd/<version>` | REGISTER User-Agent. Some networks fingerprint UAs; setting your device's stock build string reproduces the stock modem's registration exactly. Empty omits the header |
|
||||
| `STATE_FILE` | `/var/lib/imsd/imsreg.state` (root) / `$XDG_STATE_HOME/imsd/imsreg.state` | persisted registration context for warm resume |
|
||||
| `HOLDS_FILE` | `/run/imsd.holds` (root) / `$XDG_RUNTIME_DIR/imsd.holds` (`/tmp/imsd.holds` without a runtime dir) | the waits the network imposed (a `Retry-After`, the refusal schedule) and where that schedule stands, kept across a restart of imsd for the address they were set on — not across a reboot; `/dev/null` switches the file off (only a regular file is ever removed; anything else is emptied) |
|
||||
| `RESUME` | `auto` | `0` forces a fresh registration (ignores a warm SA) |
|
||||
| `REFRESH_INTERVAL` | *(auto)* | keepalive re-REGISTER period in s; default = half the granted expiry, clamped to [120, 1800] |
|
||||
| `EALG` | `aes-cbc` | offered ESP cipher (`aes-cbc`, `des-ede3-cbc`, `null`). From the 401's `Security-Server` list imsd selects the highest-preference `ipsec-3gpp` mechanism that matches this offer (RFC 3329 §2.3.1) and takes the SPIs/ports from the entry carrying them (some cores list every mechanism they support and put the SPIs on the one they applied); when no entry matches, the P-CSCF's SPI-carrying entry is installed as listed (`hmac-md5-96`, an absent `ealg` = null) and the journal says so. The line, the selection and the SA parameters are logged at every fresh registration |
|
||||
|
|
@ -289,6 +290,35 @@ The protected client/server ports are fixed at 45061/45062
|
|||
(`imsd::util::kPortUc`/`kPortUs`) — changing them in source also requires
|
||||
updating the firewall rules `ims-pdn-up.sh` installs.
|
||||
|
||||
**When the registration stops working** (the refresh is refused, or the
|
||||
protected flow dies and nothing answers the reconnect) imsd does in place
|
||||
what a restart does: it reconnects through the existing SA and refreshes;
|
||||
after two refused refreshes or two unanswered reconnect cycles (such a
|
||||
cycle is not counted while the modem reports no packet service) it
|
||||
registers fresh (new Call-ID, new challenge, new SA pair). Reconnects back
|
||||
off 5 s → 5 min; a refused or throttled fresh attempt waits on the throttle
|
||||
schedule (21 min, growing to 60), a silent one 30 s → 10 min, and a
|
||||
network `Retry-After` is honoured, up to an hour (the schedule's longest wait): a longer
|
||||
one is cut to it and the refusal heard again then. While a fresh registration is deferred, the existing SA is
|
||||
still retried on the reconnect backoff, unless the old registration is known gone: a `403` once a
|
||||
fresh registration is due, or an attempt that got past the SA setup. A `Dial`
|
||||
that reaches imsd while it is unregistered brings the next recovery step
|
||||
forward (the reconnect, or the fresh attempt once it is due), never a wait
|
||||
the network imposed (a `Retry-After`, or the schedule after a
|
||||
refused or throttled fresh attempt); imsd-ofonod refuses a `Dial` then, and
|
||||
imsd-dialerd lists no modem. A bring-up the network deferred waits the same
|
||||
schedule out with nothing to reconnect over (the resume just found the
|
||||
stored SAs unusable), then tries again. The network's holds outlive a
|
||||
restart of imsd: they are kept in
|
||||
`/run/imsd.holds` for the address they were set on, with the schedule's place
|
||||
(a restart does not start it over at 21 min), and waited out at the next
|
||||
start, before the resume (a refresh's `Retry-After`) or before the fresh
|
||||
registration (a fresh attempt's schedule or `Retry-After`); a reboot or
|
||||
another address starts clean.
|
||||
The journal says which (`…; next: fresh registration in N s`, `registering
|
||||
fresh (recovery attempt N)`, `fresh registration deferred/refused/failed:
|
||||
…`, `bring-up deferred: …`).
|
||||
|
||||
### imsd-media (per-call, set through imsd's environment)
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|
|
|
|||
Loading…
Reference in a new issue