imsd 0.2.7 — initial public snapshot
Userspace VoLTE/IMS daemon for mainline Linux phones (developed on the Fairphone 6): a GLib-free core (SIP, SDP, USIM AKA, IPsec SA setup, RTP media, call engine) behind a GDBus control daemon, a standalone AMR-WB media leg, and a Plasma Dialer backend. C++26 modules built with Crafter Build; the tree is clean under the project's house-style linter (crafter-build lint) across all three build products. Assisted-by: Claude:claude-fable-5 Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
This commit is contained in:
commit
6e77823933
31 changed files with 8761 additions and 0 deletions
124
tests/Ipsec/main.cpp
Normal file
124
tests/Ipsec/main.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
// lint-disable-file fixed-width-types
|
||||
// Imsd:Ipsec unit tests — the `ip xfrm` setup command sequence (byte-exact
|
||||
// argv) and the warm-SA reader, fed `ip xfrm state`/`policy` output in the
|
||||
// exact shape captured from a registered FP6 (addresses/keys synthetic).
|
||||
import std;
|
||||
import Imsd;
|
||||
|
||||
using namespace imsd::ipsec;
|
||||
|
||||
namespace {
|
||||
int Failures = 0;
|
||||
void Check(bool cond, std::string_view msg) {
|
||||
if (!cond) {
|
||||
std::println(std::cerr, "FAIL: {}", msg);
|
||||
++Failures;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Join(const std::vector<std::string>& v) {
|
||||
std::string s;
|
||||
for (const auto& t : v) { s += t; s += ' '; }
|
||||
if (!s.empty()) s.pop_back();
|
||||
return s;
|
||||
}
|
||||
|
||||
// `ip xfrm state` in the shape of a registered FP6's kernel state
|
||||
// (P-CSCF ::105, UE ...db43).
|
||||
constexpr std::string_view State =
|
||||
"src 2001:db8::105 dst 2001:db8:1111:2222:3333:4444:5555:db43\n"
|
||||
"\tproto esp spi 0x0a84ab2f reqid 2 mode transport\n"
|
||||
"\treplay-window 0 \n"
|
||||
"\tauth-trunc hmac(sha1) 0x00112233445566778899aabbccddeeff 96\n"
|
||||
"\tenc cbc(aes) 0xffeeddccbbaa99887766554433221100\n"
|
||||
"src 2001:db8:1111:2222:3333:4444:5555:db43 dst 2001:db8::105\n"
|
||||
"\tproto esp spi 0x0526f2c1 reqid 2 mode transport\n"
|
||||
"\tenc cbc(aes) 0xffeeddccbbaa99887766554433221100\n"
|
||||
"src 2001:db8::105 dst 2001:db8:1111:2222:3333:4444:5555:db43\n"
|
||||
"\tproto esp spi 0x0071acd4 reqid 1 mode transport\n"
|
||||
"\tlastused 2026-07-19 20:42:01\n"
|
||||
"src 2001:db8:1111:2222:3333:4444:5555:db43 dst 2001:db8::105\n"
|
||||
"\tproto esp spi 0x0526f2c0 reqid 1 mode transport\n"
|
||||
"\tlastused 2026-07-19 20:42:01\n";
|
||||
|
||||
constexpr std::string_view Policy =
|
||||
"src 2001:db8::105/128 dst 2001:db8:1111:2222:3333:4444:5555:db43/128 sport 33421 dport 45062 \n"
|
||||
"\tdir in priority 0 \n"
|
||||
"\ttmpl src 2001:db8::105 dst 2001:db8:1111:2222:3333:4444:5555:db43\n"
|
||||
"\t\tproto esp reqid 2 mode transport\n"
|
||||
"src 2001:db8:1111:2222:3333:4444:5555:db43/128 dst 2001:db8::105/128 sport 45062 dport 33421 \n"
|
||||
"\tdir out priority 0 \n"
|
||||
"\ttmpl src 2001:db8:1111:2222:3333:4444:5555:db43 dst 2001:db8::105\n"
|
||||
"\t\tproto esp reqid 2 mode transport\n"
|
||||
"src 2001:db8::105/128 dst 2001:db8:1111:2222:3333:4444:5555:db43/128 sport 6000 dport 45061 \n"
|
||||
"\tdir in priority 0 \n"
|
||||
"\ttmpl src 2001:db8::105 dst 2001:db8:1111:2222:3333:4444:5555:db43\n"
|
||||
"\t\tproto esp reqid 1 mode transport\n"
|
||||
"src 2001:db8:1111:2222:3333:4444:5555:db43/128 dst 2001:db8::105/128 sport 45061 dport 6000 \n"
|
||||
"\tdir out priority 0 \n"
|
||||
"\ttmpl src 2001:db8:1111:2222:3333:4444:5555:db43 dst 2001:db8::105\n"
|
||||
"\t\tproto esp reqid 1 mode transport\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
// ---- warm-SA reader against real device output
|
||||
{
|
||||
auto sa = ParseExistingSa(State, Policy, "2001:db8::105", "2001:db8:1111:2222:3333:4444:5555:db43");
|
||||
Check(sa.has_value(), "existing SA parses");
|
||||
Check(sa && sa->portPs == 6000, "port-s (reqid1 out dport) = 6000");
|
||||
Check(sa && sa->portPc == 33421, "port-c (reqid2 out dport) = 33421");
|
||||
Check(sa && sa->spiUc == 0x0071acd4u, "spi_uc (P->UE reqid1)");
|
||||
Check(sa && sa->spiUs == 0x0a84ab2fu, "spi_us (P->UE reqid2)");
|
||||
Check(sa && sa->spiPs == 0x0526f2c0u, "spi_ps (UE->P reqid1)");
|
||||
Check(sa && sa->spiPc == 0x0526f2c1u, "spi_pc (UE->P reqid2)");
|
||||
Check(sa && sa->securityServer == "ipsec-3gpp; q=0.1; alg=hmac-sha-1-96; ealg=aes-cbc; " "spi-c=86438593; spi-s=86438592; port-c=33421; port-s=6000", "reconstructed Security-Server value");
|
||||
}
|
||||
// incomplete input -> nullopt
|
||||
Check(!ParseExistingSa("src a dst b\n\tspi 0x1 reqid 1 mode transport\n", "", "b", "a").has_value(), "missing pieces -> nullopt");
|
||||
|
||||
// ---- fresh setup command sequence
|
||||
{
|
||||
SaParams p;
|
||||
p.local = "2001:db8::db43";
|
||||
p.pcscf = "2001:db8::105";
|
||||
p.ik = std::vector<std::uint8_t>(16, 0xAB);
|
||||
p.ck = std::vector<std::uint8_t>(16, 0xCD);
|
||||
p.spiUc = 100; p.spiUs = 200; p.spiPc = 300; p.spiPs = 400;
|
||||
p.portUc = 45061; p.portUs = 45062; p.portPs = 6000; p.portPc = 33421;
|
||||
auto cmds = BuildSetupCommands(p);
|
||||
Check(cmds.size() == 10, "flush x2 + 4 states + 4 policies");
|
||||
Check(Join(cmds[0]) == "ip xfrm state flush", "state flush first");
|
||||
Check(Join(cmds[1]) == "ip xfrm policy flush", "policy flush second");
|
||||
// reqid1 outbound state: src LOCAL dst P spi=spiPs, IK auth, CK enc
|
||||
Check(Join(cmds[2]) ==
|
||||
"ip xfrm state add src 2001:db8::db43 dst 2001:db8::105 "
|
||||
"proto esp spi 400 mode transport reqid 1 "
|
||||
"auth-trunc hmac(sha1) 0xabababababababababababababababab 96 "
|
||||
"enc cbc(aes) 0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
|
||||
"state add reqid1 outbound byte-exact");
|
||||
// first out policy: sport PORT_UC dport PORT_PS reqid 1
|
||||
Check(Join(cmds[6]) == "ip xfrm policy add src 2001:db8::db43/128 dst 2001:db8::105/128 " "sport 45061 dport 6000 dir out tmpl src 2001:db8::db43 " "dst 2001:db8::105 proto esp reqid 1 mode transport", "policy add out reqid1 byte-exact");
|
||||
// v4 uses /32
|
||||
SaParams p4 = p;
|
||||
p4.local = "10.0.0.2"; p4.pcscf = "10.0.0.1";
|
||||
auto c4 = BuildSetupCommands(p4);
|
||||
Check(c4[6][5] == "10.0.0.2/32", "v4 prefix length /32");
|
||||
}
|
||||
// null cipher -> cipher_null + empty key arg
|
||||
{
|
||||
SaParams p;
|
||||
p.local = "::2"; p.pcscf = "::1";
|
||||
p.ik = std::vector<std::uint8_t>(16, 0x01);
|
||||
p.ck = {};
|
||||
p.ealg = "null";
|
||||
auto cmds = BuildSetupCommands(p);
|
||||
auto& st = cmds[2];
|
||||
Check(st[st.size() - 3] == "enc" && st[st.size() - 2] == "cipher_null" && st[st.size() - 1].empty(), "null cipher: cipher_null + empty key");
|
||||
}
|
||||
|
||||
if (Failures == 0) std::println("Ipsec: all tests passed");
|
||||
return Failures;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue