qmi, imsd-pcscf: read the ims PDN's P-CSCF list from the modem's runtime settings

The network hands the P-CSCF addresses to the modem in the PDN's Protocol
Configuration Options; ModemManager's bearer code does not ask the modem
for them, qmicli (libqmi 1.39) prints none of them, and libqmi has no TLV
for the IPv6 list (0x2E), which is why PCSCF= was a required setting. A
second QMI WDS client reads them once it is bound to the bearer's mux
data port (the binding ModemManager gives its own clients): Bind Mux
Data Port, Set IP Family 6, Get Current Settings, over one AF_QIPCRTR
socket. Imsd:Qmi holds the bytes and the readers (0x22 the flag, 0x23
IPv4, 0x24 FQDN, 0x2E IPv6 in Qualcomm's layout: a count, then 16 bytes
each); imsd-pcscf is the socket, the service lookup and the sequence, and
prints the list, one address per line; nothing runs it yet. It sends
no Start Network and no Stop Network; the bearer stayed connected and the
registration stood after its runs (the fp6 journal's probe capture 04,
benches 38 and 39). The
build, the CI cross-compile, the tarball and the apk gain the product;
linux-headers is named in the sysroot for <linux/qrtr.h>.

Tested: crafter-build test, 8 passed; the aarch64 build of this commit's
helper on the dev phone (KPN) against the live ims bearer.
This commit is contained in:
Jorijn van der Graaf 2026-10-11 16:07:28 +02:00
commit a89ca5a60c
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
10 changed files with 701 additions and 25 deletions

View file

@ -77,7 +77,7 @@ static std::unique_ptr<Configuration> BusLibrary(std::span<const std::string_vie
}
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
// Four executables come out of this repo; which one a build produces is
// Five executables come out of this repo; which one a build produces is
// chosen by `--product=`:
// daemon (default) — imsd, the control-plane daemon (GDBus + engine),
// and the unit tests, which link imsd-core.
@ -91,13 +91,18 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
// (org.ofono.*) bridging to imsd, for Phosh and
// GNOME Mobile (GDBus translation plus the imsd-bus
// polkit gate on its callers, no core).
// pcscf — imsd-pcscf, the P-CSCF reader for the bring-up: one
// QMI WDS client over a raw QRTR socket (pure std +
// POSIX, links the core for Imsd:Qmi, no GLib).
bool wantMedia = false;
bool wantDialerd = false;
bool wantOfonod = false;
bool wantPcscf = false;
for (std::string_view a : args) {
if (a == "--product=media") wantMedia = true;
if (a == "--product=dialerd") wantDialerd = true;
if (a == "--product=ofonod") wantOfonod = true;
if (a == "--product=pcscf") wantPcscf = true;
}
if (wantOfonod) {
Configuration ofonod;
@ -161,11 +166,12 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
ApplyStandardArgs(*Core, args);
Core->type = ConfigurationType::LibraryStatic;
{
std::array<fs::path, 8> ifaces = {
std::array<fs::path, 9> ifaces = {
"interfaces/Imsd",
"interfaces/Imsd-Util",
"interfaces/Imsd-Aka",
"interfaces/Imsd-Ipsec",
"interfaces/Imsd-Qmi",
"interfaces/Imsd-Sip",
"interfaces/Imsd-Sdp",
"interfaces/Imsd-Messages",
@ -175,6 +181,23 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
Core->GetInterfacesAndImplementations(ifaces, impls);
}
if (wantPcscf) {
Configuration pcscf;
pcscf.path = "./";
pcscf.name = "imsd-pcscf";
pcscf.outputName = "imsd-pcscf";
ApplyStandardArgs(pcscf, args);
pcscf.type = ConfigurationType::Executable;
pcscf.dependencies = { Core.get() };
{
std::array<fs::path, 0> ifaces = {};
std::array<fs::path, 1> impls = { "implementations/pcscf" };
pcscf.GetInterfacesAndImplementations(ifaces, impls);
}
LintRules::AddRules(pcscf);
return pcscf;
}
// imsd — the daemon executable: GLib main loop + GDBus service
// (net.catcrafts.IMS1, same ABI as the Python imsd.py) around the core.
Configuration cfg;
@ -197,6 +220,7 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
cfg.AddTest("Util").Dependencies({ Core.get() });
cfg.AddTest("Aka").Dependencies({ Core.get() });
cfg.AddTest("Ipsec").Dependencies({ Core.get() });
cfg.AddTest("Qmi").Dependencies({ Core.get() });
cfg.AddTest("Messages").Dependencies({ Core.get() });
cfg.AddTest("Engine").Dependencies({ Core.get() });
cfg.AddTest("Sip").Dependencies({ Core.get() });