change: service watches the network itself (#111) #121
Loading…
Reference in a new issue
No description provided.
Delete branch "change/network-observer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
monitor.NetMonand the network-change policy (aqomui_gui.network_change-- kill tunnels, reconnect, rebuild the bypass) ran as unprivileged gui code. With no gui process around, nobody reacted to a network change at all. Network policy belongs where tunnel state lives. Successor to #89. Closes #111.Fix
aqomui/observer.py: the utils network cluster (default_routes,default_interface,preferred_interface) moves in, plusNetObserver-- the ported NetMon poll as aGuardedThreadthe service owns.reconcile_bypass; down = disconnect both. Then it broadcasts the outcome (new D-Bus signalnet_state_changed, snapshot methodget_net_state).connect_last_server-- the reconnect is the user's autoconnect policy, so it stays client-side (a Qt-free shared class the cli can also use is follow-up work, see the issue filed alongside this PR). Teardown feedback reaches the ui through the existing status signals.log_from_threadlost its last consumer and is gone.D-Bus surface changed: gui and service must upgrade together (same as #110).
Behavior deltas to be aware of:
/sys/class/netread error keeps the prior state instead of resetting to down (NetMon used to replay the full up reaction when the read recovered).Verification
pytest: 305 passed, including newtests/test_observer.py(moved network-fact tests +poll_oncetransition cases) and reaction-policy tests fornetwork_changed(service) andnet_state_changed(gui).build-branch-and-install.sh, then (1) gui closed: unplug/replug the link, watch the service log react and the bypass rebuild; (2) gui open with autoconnect: same flip, expect teardown statuses, pings, reconnect; (3) service restart under a running gui: expect resync + the new auto-reconnect.Live round, first pass (service restart under a running gui, then gui relaunch):
Mechanism verified working. At 23:22:39 the restarted service reconciled, the old gui's owner-watch resynced, and at 23:22:40 the observer fired its first up-transition and ran the full reaction with no gui involvement (
Network: new connection detected→ dns saved, disconnects, bypass rebuilt, broadcast). The relaunched gui picked up state viaget_net_state(Detected new network connectionat 23:23:57).The failed connect that followed was environmental, not this PR. The manual connect at 23:24:55 took 12s to reach Peer Connection Initiated (historically ~100ms to the same server, incl. earlier the same day) and the pre-existing 15s gui watchdog killed it 3s short of completing. Measured during diagnosis: 15% packet loss and ~1.1s RTT to the vpn server over the physical link -- uplink bufferbloat from torrent upload on the ~43Mbps uplink. A reproduced connect over D-Bus completed at 12.8s under the same congestion. The connect path is untouched by this diff.
Observed, accepted for now: a restart with the network up runs three bypass rebuilds in ~1.5s (init
reconcile_bypass→ gui resyncbypasscall → observer up-reaction reconcile). Each carries a recovery case the others don't (no-gui restarts / owner registration when the on-disk cgroup is gone / networks that come up later), all are idempotent, so deduplicating risks the #84 class of regression for log noise. Flagging rather than fixing.Still pending before merge: link flip with gui closed; link flip with gui open and
autoconnect=1(note: current config hasautoconnect: 0, so no reconnect is the expected result of a flip test until it's enabled).824444ea284d9cdfe737Live flip test caught a real one: the link flip produced no reaction at all -- no down, no up, nothing logged. Root cause is inherited, not a port mistake: NetMon's "any interface operstate-up" check can never see a link flip on this host, because ~130 docker veths/bridges are permanently operstate-up -- and so is a connected tunnel's own DCO device. By interface state this machine never goes down, so the reaction code was unreachable. The 2019 design predates both containers-everywhere and DCO devices that outlive a link drop.
Reworked the watcher (amended into the branch commit, force-pushed): NetObserver now polls
default_routes()and reports transitions of the default route -- gone, back, or changed (the changed-gateway case is one interface state could never see). Two identical polls required before reporting, so v4/v6 arriving seconds apart on reconnect collapse into one reaction instead of two teardown-rebuild cycles.default_routes()now skips the tunnels' own devices (tun_aqomui*/wg_aqomui*), so a plainredirect-gatewayroute can't read as a new network and react the tunnel to death -- this also hardens the bypass's use of the same helper. New tests cover all transition shapes + the filter; 308 passing.Needs a rebuild + service restart, then the flip test again. Expected:
Network: connection lost - closing tunnelswithin ~4s of the drop (2s poll x 2-poll stabilization), teardown of both tunnels, then on reconnectNetwork: new connection detected, bypass rebuild, broadcast, gui autoconnect.Flip test round 2: the headline path works end to end. 01:03:39
Network: connection lost - closing tunnels-- the service detected the drop itself within ~2-4s and killed both tunnels; 01:03:49Network: new connection detected-- dns saved, bypass rebuilt for enp5s0, broadcast, and the gui autoconnected main + bypass (NJ up in 1.5s, VA right behind). Detection, teardown, rebuild, broadcast, client reconnect all verified live.One adjacent hole surfaced and is fixed in a second commit (
faa4863): the bypass tunnel's teardown askscgroup_vpnfor a rebuild, which the network-loss reaction now triggers on every outage -- mid-outage the routes are gone, so it built a cgroup against interface "None" (route/rp_filter errors, dnsmasq unable to join). It now gets the same no-network guardapply_bypassalready had; the up reaction reconciles when a network returns. Regression test added (309 passing).Remaining before merge: one more flip to see the guard's
Bypass: no network - rebuild deferredline replace the error spam on the down side.