Emergency calling, stage 1: urn:service:sos with a digits fallback

Classify 112/911 (plus EMERGENCY_NUMBERS) at Dial. A classified call
INVITEs urn:service:sos over the existing registration and, on any
non-2xx final not caused by the user hanging up (including the
setup-timeout CANCEL paths), retries once as a plain INVITE of the
dialled digits — the pre-0.3.0 behavior, so classification can never
place a call worse than the status quo. The reverse edge: a 380
Alternative Service whose body carries the emergency indication
upgrades an unclassified call to the sos URN; a bare 380 stays an
error, since promoting an arbitrary redirect would put a
non-emergency call through to a PSAP. An emergency dial preempts an
in-progress call, and an answer racing a deadline-initiated CANCEL is
taken instead of BYE'd (user-initiated CANCEL races still BYE).

No emergency registration, no emergency PDN, no CS fallback, no
SIM-less calling, no AML — and no carrier has confirmed the sos path
end-to-end. The README warning states exactly that.

Assisted-by: Claude:claude-fable-5
This commit is contained in:
Jorijn van der Graaf 2026-08-01 22:29:12 +02:00
commit f2a404855f
8 changed files with 365 additions and 42 deletions

View file

@ -6,7 +6,7 @@
Userspace IMS/VoLTE daemon for mainline Linux phones.
> [!WARNING]
> imsd currently cannot make emergency calls
> Emergency calling is unverified
Modem firmware on typical mainline-Linux phones will bring up the IMS PDN but
can't be used for actual call audio. imsd implements the whole IMS client in
@ -22,20 +22,6 @@ full-duplex AMR-WB audio, and tear down; incoming calls ring, answer via
`Accept`, and carry media the same way — driven end-to-end from a stock
dialer over the frozen D-Bus ABI. Current state:
| Piece | State |
|---|---|
| SIP message layer + TCP framing (`Imsd:Sip`) | done, tested |
| SDP offer/answer for AMR-WB (`Imsd:Sdp`) | done, tested |
| USIM AKA + AKAv1-MD5 digest, SIM parsers (`Imsd:Aka`) | done, tested |
| IPsec SA commands + warm-SA reader (`Imsd:Ipsec`) | done, tested |
| SIP request/response builders (`Imsd:Messages`) | done, byte-pinned |
| Outgoing + incoming call state machine (`Imsd:Engine`) | done, tested |
| Registration (fresh + warm resume + keepalive refresh, MMTEL feature tags) | done |
| RTP + AMR-WB media plane into PipeWire (`imsd-media`) | done |
| D-Bus service `net.catcrafts.IMS1` (register, Dial, Accept, HangUp, live media) | done |
| Plasma Dialer session backend (`imsd-dialerd`, org.kde.telephony.*) | done |
| DTMF (`SendDtmf`, RFC 4733) | not yet implemented |
The engine core (`imsd-core`) is a GLib-free, pure-`std` static library whose
every decision — SIP framing, SDP selection, the AKAv1-MD5 digest, the
`ip xfrm` command sequence, and the whole call FSM — is unit-tested on a dev
@ -165,6 +151,7 @@ PCSCF=2001:db8::105
| `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`) |
| `EMERGENCY_NUMBERS` | *(empty)* | comma-separated additions to the builtin 112/911 emergency numbers (e.g. a private test core's short code). SIM `EF_ECC` is not read yet |
| `RTP_PORT` | `50004` | local RTP port advertised in SDP |
| `PRECOND` | `0` | `1` offers SDP QoS preconditions |
| `DUMP_SIP` | `0` | `1` writes raw REGISTER-200/SUBSCRIBE-200/INVITE dumps (mode 0600) for debugging — they contain your IMSI/MSISDN and addresses |