mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:36 +01:00
fix(ssh,ai): keep connections to idle LAN peers fast (#463)
* fix(ssh): retry and multiplex LAN connections * fix(ai): shrink the neighbour-probe fallback to ~1s Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
This commit is contained in:
1 parent
3266aad4a2
commit
a0ff9f7fd7
4 files changed
+77
-15
No files matched your search
@@ -71,6 +71,7 @@ A known limitation with no fix yet.
|
||||
| WA-025 | brcmfmac 4-way-handshake offload authentication — upstream kernel/wpa_supplicant, no code in this repo | Since hostap commit 416386060, wpa_supplicant 2.11+ expects `NL80211_CMD_PORT_AUTHORIZED` from drivers that offload the 4-way handshake; brcmfmac never reports it, so authentication intermittently fails on Apple Silicon (BRCM4378/4387/4388). nixpkgs already carries the Broadcom revert in wpa_supplicant 2.12; the proper fix, Janne Grunau's `wifi: brcmfmac: cfg80211: Report port_authorized for 4-way HS offload` (2026-09), is still under review. Entering the key again after such a failure only creates a duplicate profile, so cancel and retry instead | Fix lands via a nixpkgs kernel update (or by carrying the patch through `nixpkgs-patched`) | 2026-10-07 | on every kernel bump | active |
|
||||
| WA-033 | USB-C/XHCI port `_PLD` ACPI noise — this board's DSDT, no code in this repo | every boot logs 16 `ACPI Error: Aborting method \_SB.PC00.TXHC.RHUB.SS01._PLD ... (AE_NOT_FOUND)` lines, plus `\_SB.PC02.XHCI.RHUB.HS01._PLD`; the ports work, and these messages are unrelated to the GPU's ATPX failure, which names `\_SB.PC00.GFX0.ATPX` instead | Drop if a BIOS update defines the `_PLD` descriptors | 2026-10-14 | on a BIOS update | active |
|
||||
| WA-034 | IPv6-first resolution is dead on the wired link — `hosts/idols-aquamarine/default.nix` (`networking.getaddrinfo.precedence`) | the proxy gateway answers DNS in fake-IP mode and its IPv6 path is dead: an AAAA lookup returns a fake address whose connection fails at once (measured: `curl -6` fails in 8 ms, `curl -4` answers in 0.7 s). glibc clients are covered by the RFC 6724 table with the IPv4-mapped row raised; nix's libcurl and other non-glibc resolvers ignore `/etc/gai.conf`. Disabling IPv6 with `boot.kernel.sysctl` did not work here — networkd re-enables it on the wired link for `DHCP = "ipv6"`/`IPv6AcceptRA` — so that setting was dropped on 2026-10-14 | Drop once the gateway serves proxied names over IPv6 as well (the adjacent fix is `dns.ipv6: false` in mihomo, see WA-030) | 2026-10-14 | on a mihomo or gateway change | active |
|
||||
| WA-035 | SSH retry + multiplexing to LAN peers, ARP fallback tuning — `home/base/tui/ssh.nix`, `hosts/idols-ai/neighbour-probe.nix` | the desktop reaches the LAN over a Mesh WiFi backhaul, which drops unicast to a peer whose MAC it has aged out of its forwarding table while still flooding broadcast: a capture shows seven SYNs from one socket addressed to the cached MAC with no answer, three unicast ARP probes unanswered, then a broadcast ARP answered in 0.4 ms after ~12 s of failed connects. The ssh settings retry a short connect and keep a master warm so the entry stays fresh; the neighbour-probe tunings walk the kernel to that broadcast fallback in ~1 s | Drop once the backhaul is wired or the mesh stops dropping unknown unicast | 2026-10-16 | when the backhaul changes | active |
|
||||
|
||||
## WIP
|
||||
|
||||
|
||||
+38
-15
@@ -3,8 +3,26 @@
|
||||
lib,
|
||||
pkgs,
|
||||
mysecrets,
|
||||
myvars,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# WA-035: settings for the home LAN, which the desktop reaches over a Mesh
|
||||
# WiFi backhaul.
|
||||
lanHost = {
|
||||
# A lost first SYN must not fail the connection; a LAN handshake is ~2 ms,
|
||||
# so the connect is cut short and retried instead of hanging.
|
||||
ConnectTimeout = 1;
|
||||
ConnectionAttempts = 3;
|
||||
# One persistent master per host keeps a session (and with it the mesh's
|
||||
# forwarding entry for that peer) open, so later connections reuse it
|
||||
# instead of resolving and handshaking again. Three missed keepalives reap a
|
||||
# master whose path died.
|
||||
ControlMaster = "auto";
|
||||
ControlPersist = "30m";
|
||||
ServerAliveInterval = 15;
|
||||
};
|
||||
in
|
||||
{
|
||||
home.file.".ssh/romantic.pub".source = "${mysecrets}/public/romantic.pub";
|
||||
|
||||
@@ -12,20 +30,23 @@
|
||||
enable = true;
|
||||
|
||||
enableDefaultConfig = false;
|
||||
settings."*" = {
|
||||
ForwardAgent = false;
|
||||
AddKeysToAgent = "yes";
|
||||
Compression = true;
|
||||
ServerAliveInterval = 0;
|
||||
ServerAliveCountMax = 3;
|
||||
HashKnownHosts = false;
|
||||
UserKnownHostsFile = "~/.ssh/known_hosts";
|
||||
ControlMaster = "no";
|
||||
ControlPath = "~/.ssh/master-%r@%n:%p";
|
||||
ControlPersist = "no";
|
||||
};
|
||||
|
||||
# Keep this one definition: Nix merges a `settings."<pattern>"` attribute
|
||||
# path into `settings = ...` only while that value stays an attrset literal,
|
||||
# so the generated LAN patterns are merged inside it with `//` instead.
|
||||
settings = {
|
||||
"*" = {
|
||||
ForwardAgent = false;
|
||||
AddKeysToAgent = "yes";
|
||||
Compression = true;
|
||||
ServerAliveInterval = 0;
|
||||
ServerAliveCountMax = 3;
|
||||
HashKnownHosts = false;
|
||||
UserKnownHostsFile = "~/.ssh/known_hosts";
|
||||
ControlMaster = "no";
|
||||
ControlPath = "~/.ssh/master-%r@%n:%p";
|
||||
ControlPersist = "no";
|
||||
};
|
||||
|
||||
"github.com" = {
|
||||
# WA-028: pinned to IPv4 until the mihomo v6 fake-ip pool is proven here
|
||||
AddressFamily = "inet";
|
||||
@@ -37,8 +58,10 @@
|
||||
|
||||
"192.168.*" = {
|
||||
ForwardAgent = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
// lanHost;
|
||||
}
|
||||
// lib.genAttrs (builtins.attrNames myvars.networking.hostsAddr) (_: lanHost);
|
||||
};
|
||||
|
||||
# Keep ~/.ssh/config a real, user-owned file rather than a /nix/store symlink.
|
||||
|
||||
@@ -35,6 +35,9 @@ in
|
||||
|
||||
./restic.nix
|
||||
|
||||
# WA-035: neighbour-probe fallback for the Mesh WiFi backhaul to the homelab.
|
||||
./neighbour-probe.nix
|
||||
|
||||
# others
|
||||
./ai
|
||||
];
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
config,
|
||||
myvars,
|
||||
...
|
||||
}:
|
||||
let
|
||||
# The wired link to the router, i.e. the one that leaves through the Mesh WiFi
|
||||
# backhaul to the homelab. Taken from `hostsAddr` so a NIC rename cannot leave
|
||||
# these tunings pointing at a table that no longer exists.
|
||||
iface = myvars.networking.hostsAddr.${config.networking.hostName}.iface;
|
||||
in
|
||||
{
|
||||
# WA-035: the mesh backhaul drops unicast to a peer whose MAC it has aged out
|
||||
# while still flooding broadcast, so a connection to an idle peer waits for the
|
||||
# kernel to give up on unicast ARP and fall back to broadcast. Walk that
|
||||
# neighbour state machine in ~1s instead of ~10s, which fits inside ssh's 5s
|
||||
# retry window (see `home/base/tui/ssh.nix` for the other half).
|
||||
#
|
||||
# Cost: while a peer is genuinely down, probes go out 5x faster for the same
|
||||
# bounded count -- one unicast probe, then the unchanged broadcast attempts. The
|
||||
# failed-neighbour re-probe pace (`interval_probe_time_ms`) is deliberately left
|
||||
# alone, so a peer that stays down is not probed more often than before.
|
||||
#
|
||||
# `default` and the link's own table are both set: a device copies `default`
|
||||
# when it is registered, so a later write to `default` does not reach a table
|
||||
# that already exists.
|
||||
boot.kernel.sysctl = {
|
||||
"net.ipv4.neigh.default.delay_first_probe_time" = 1; # revalidate after 1s, not 5s
|
||||
"net.ipv4.neigh.default.retrans_time_ms" = 200; # probe every 200ms, not 1s
|
||||
"net.ipv4.neigh.default.ucast_solicit" = 1; # one unicast probe, then broadcast
|
||||
"net.ipv4.neigh.${iface}.delay_first_probe_time" = 1;
|
||||
"net.ipv4.neigh.${iface}.retrans_time_ms" = 200;
|
||||
"net.ipv4.neigh.${iface}.ucast_solicit" = 1;
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user