ipsec, imsd: the SA setup deletes by exact key instead of flushing the host's xfrm
A fresh registration's SA setup began with `ip xfrm state flush` and `ip xfrm policy flush`: every SA userspace had installed and every SPD policy went, a policy-based VPN's (strongSwan's, libreswan's) SAs and policies included. The setup now takes out what two rules select and leaves the rest; and the SA reader, which took the P-CSCF ports for a resume from any reqid-1/2 `dir out` policy — correct only while the flush left imsd's pair alone in the kernel — takes them from the policies between our two addresses only. Whether imsd runs beside a live strongSwan or libreswan connection is unmeasured: the dev phone shows a tunnel policy and a tunnel state left in place (Tested below), no more. OwnXfrmDeleteCommands (Imsd:Ipsec) builds the deletes from the kernel text the shell reads: every transport-mode policy with reqid 1 or 2 whose selector carries imsd's protected port on the UE side, by the selector ip xfrm policy prints (src, dst, proto, sport, dport, dev) and its direction, and every transport-mode reqid-1/2 ESP state running from such a policy's source to its destination or anchored on the UE's ims-PDN address, by src, dst, proto esp and SPI — policy or not: a setup whose policy add failed leaves such states (the setup logs a failed add and goes on), and an ESP state another transport-mode user put on that address with reqid 1 or 2 goes with them (an AH or IPComp state is not selected: the rule asks for ESP). Every reader of the policy text — the deletes and the SA reader's port pass here — goes through one predicate (detail::OwnPolicy), written for whatever reads that text later. BuildSetupCommands loses its two flush lines and the shell runs the deletes right before it (RunOwnXfrmDeletes); BuildTeardownCommands, the same two flushes with no caller, goes. A policy-based VPN's tunnel entries and another transport-mode user's pair on other ports and addresses stay. Residual: a transport-mode policy with reqid 1 and the UE-side port 45061, or reqid 2 and 45062 — alone or paired, between any addresses — carries imsd's fingerprint and goes, with the reqid-1/2 transport-mode ESP states in its direction, and a reqid-1/2 transport-mode ESP state on the UE's ims-PDN address goes whoever made it (tests/Ipsec pins both, and that an AH state stays). Tested: crafter-build test, 7 passed (the eight deletes byte-exact on the fixture in the registered FP6's shape (synthetic addresses and keys); a VPN's tunnel SA and policies beside the pair untouched; another transport-mode user's pair on other ports and addresses untouched, its reqid-1 ESP state on the UE's address deleted whoever made it, its AH state left; a policy-less state on the UE's address deleted, one between other addresses left; the reader's ports from our pair beside a foreign transport policy with ports); on the dev phone (KPN), in builds of this series, a fresh registration with a VPN's tunnel policy and state in the kernel: both still there afterwards, imsd's previous pair gone.
This commit is contained in:
parent
b60a3633c1
commit
21cb0f870d
4 changed files with 198 additions and 25 deletions
|
|
@ -114,6 +114,18 @@ std::string RedactKeys(const std::vector<std::string>& argv) {
|
|||
return s;
|
||||
}
|
||||
|
||||
// imsd's own xfrm entries out of the kernel, each by its key (the
|
||||
// OwnXfrmDeleteCommands list — never `ip xfrm … flush`: another IPsec user's
|
||||
// entries, a policy-based VPN's, stay, §12); a delete that fails (an entry
|
||||
// gone between the read and the delete) is logged and the rest go on. At
|
||||
// file scope, as LoadHolds and PersistHolds are.
|
||||
void RunOwnXfrmDeletes(const std::vector<std::vector<std::string>>& cmds) {
|
||||
for (auto& cmd : cmds) {
|
||||
int rc = RunCmd(cmd);
|
||||
if (rc != 0) Log(std::format("{} -> rc {}", RedactKeys(cmd), rc));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- small text extractors (401 header params, qmicli "completed:") -------
|
||||
|
||||
std::optional<std::string> QuotedAfter(std::string_view text, std::string_view key) {
|
||||
|
|
@ -1471,6 +1483,13 @@ private:
|
|||
sp.portUc = PortUc; sp.portUs = PortUs; sp.portPs = portPs; sp.portPc = portPc;
|
||||
sp.alg = alg;
|
||||
sp.ealg = ealg;
|
||||
// imsd's previous pair out first, each entry by its key — not `ip xfrm …
|
||||
// flush`: another IPsec user's entries (a policy-based VPN's) stay (§12)
|
||||
{
|
||||
auto state = RunCapture({"ip", "xfrm", "state"});
|
||||
auto policy = RunCapture({"ip", "xfrm", "policy"});
|
||||
RunOwnXfrmDeletes(imsd::ipsec::OwnXfrmDeleteCommands(state.out, policy.out, local_));
|
||||
}
|
||||
saReplaced_ = true; // from here on the old registration's SAs are gone
|
||||
for (auto& cmd : imsd::ipsec::BuildSetupCommands(sp)) {
|
||||
int rc = RunCmd(cmd);
|
||||
|
|
@ -1501,7 +1520,7 @@ private:
|
|||
if (autoTcp) {
|
||||
// Two unanswered TCP connects: treat the P-CSCF as UDP-only
|
||||
// and register again over UDP — a new challenge and a new SA
|
||||
// pair (the SA setup flushes this one), same ports.
|
||||
// pair (the SA setup takes this one out), same ports.
|
||||
Log("protected TCP connect unanswered; registering fresh over UDP");
|
||||
SetTransport(Transport::Udp);
|
||||
FreshRegisterAttempt();
|
||||
|
|
@ -1873,7 +1892,7 @@ private:
|
|||
}
|
||||
|
||||
// The rest of what a restart does, in place: a new Call-ID, a new
|
||||
// challenge, a new SA pair (FreshRegister flushes the old ones), then
|
||||
// challenge, a new SA pair (FreshRegister takes the old ones out), then
|
||||
// the post-registration steps. A refused or throttled attempt waits on
|
||||
// the throttle schedule (a wait no Dial cuts short — on KPN a refused
|
||||
// attempt costs the old registration) and a silent one on the
|
||||
|
|
|
|||
Loading…
Reference in a new issue