Skip to content

Show physical network behind TUN routes - #12071

Open
aisensiy wants to merge 2 commits into
omacom:quattrofrom
aisensiy:fix/network-status-tun
Open

aisensiy wants to merge 2 commits into
omacom:quattrofrom
aisensiy:fix/network-status-tun

Conversation

@aisensiy

@aisensiy aisensiy commented Sep 16, 2026 •

Copy link
Copy Markdown

What

  • Prefer the first live, non-tunnel IPv4 default route from the main table when rendering network status; fall back to the existing probe route when none is available.
  • Use the selected route's source address, or ask the kernel which source it uses to reach the gateway on that interface when the route has no prefsrc. Report the prefix for that same address.
  • Add regression coverage for a policy-routed TUN ahead of a physical connection, a bridge ahead of backup Wi-Fi, a bond behind a TUN, a dead default, multiple interface addresses, and the probe fallback.

Why

With Mihomo/Clash TUN mode, ip route get 1.1.1.1 resolves to the Meta TUN through a policy-routing table. The network panel then reports Meta as Ethernet and displays its internal 198.18.0.1 address instead of the physical interface and LAN address. This also changes the display for full-tunnel VPNs that leave a non-tunnel default route in the main table: the panel reports that underlying link rather than the VPN. If no usable main-table default exists, it retains the probe-route behavior.

Fixes #12069. Related: #8866 addresses the same panel issue with a different route selection policy; maintainers may prefer one approach over the other.

Verification

  • bash test/shell.d/network-status-route-test.sh (8 assertions passed)
  • ./test/cli passed
  • bash -n bin/omarchy-network-status test/shell.d/network-status-route-test.sh passed
  • shellcheck -e SC2086,SC1091 bin/omarchy-network-status test/shell.d/network-status-route-test.sh passed
  • Live read-only check with Mihomo TUN enabled: ./bin/omarchy-network-status --verbose reports physical enp0s13f0u1, 192.168.50.248/24 and gateway 192.168.50.1.
  • ./test/shell did not complete within 240 seconds on this machine. The new network-status test passed during the run; the run also reported failures in unrelated bar/config/migration/plugin tests before the timeout. No full-suite pass is claimed.

@llstrk

llstrk commented Sep 27, 2026

Copy link
Copy Markdown

Automated AI review

Community review: Independent automated community review, unaffiliated with the Omarchy team, intended to help prepare PRs for their review.

Verified: the change fixes the setup reported in #12069. With a Mihomo TUN on a policy-routing table and an Ethernet default in the main table, the status and --verbose output now describe the physical link instead of Meta:

Stubbed scenario (interface names and addresses from the public issue #12069) Merge base This PR
Plain status ethernet Meta ethernet enp0s13f0u1u4c2
--verbose iface / ip / prefix / gateway Meta / 198.18.0.1 / 30 / 198.18.0.2 enp0s13f0u1u4c2 / 10.0.11.181 / 24 / 10.0.11.1

Output is unchanged for a single Ethernet or Wi-Fi default, and for Ethernet plus Wi-Fi in either metric order. Hosts without NetworkManager, WWAN and IPv6-only hosts fall back to the old probe route as before. No scenario produced a nonzero exit or stderr output. The script and its call sites are unchanged between the merge base and the current quattro tip, and the PR merges cleanly onto it.

Three points are worth addressing: the Ethernet/Wi-Fi allow-list can pick the wrong link, the new address fallback can report the wrong address, and several open PRs rewrite the same code and add the same test file.

A lower-priority Wi-Fi link wins over a bridge, bond or VLAN default

display_route_json walks the main-table defaults in metric order but accepts only devices whose nmcli type is ethernet or wifi. NetworkManager gives bond (300), team (350), VLAN (400), macvlan (410), bridge (425) and PPP (460) defaults a lower metric than Wi-Fi (600), so those preferred routes are skipped in favour of Wi-Fi.

Stubbed scenario: wired NIC in br0 (metric 425, for example for VMs) and Wi-Fi connected as backup (metric 600):

Merge base This PR
Plain status ethernet br0 wifi HomeNet 78 5180.0
--verbose iface / ip br0 / 192.168.1.20 wlan0 / 192.168.1.60

Impact: on such hosts the panel shows the Wi-Fi SSID, address, gateway and byte counters while traffic flows over the bridge. The merge base showed the correct device. The same allow-list also leaves a bond host behind a TUN unfixed (stubbed: it still shows Meta).

Suggested change: take the first main-table default whose device is not a tunnel (skip types such as tun and wireguard) instead of accepting only ethernet/wifi. A variant that accepts any non-empty type other than tun (so an empty type still falls back to the probe route) still passes all three assertions of the new test. Skipping routes whose flags contain dead would additionally keep a link-down Ethernet default from being chosen when ignore_routes_with_linkdown is set (the kernel then routes over Wi-Fi, while this PR keeps reporting the dead link; the merge base follows the kernel).

The address fallback can report an address the kernel does not use

When the selected route has no prefsrc, the new fallback takes the first IPv4 address on the interface. That skips secondary addresses correctly. With real kernel routes in a namespace (synthetic addresses):

Interface addresses, gateway Kernel source This PR's --verbose ip
10.0.0.5/8 and 192.168.2.50/24 (both primary), gateway 192.168.2.1 192.168.2.50 10.0.0.5
link-local 169.254.10.10/16 listed before 192.168.1.50/24 192.168.1.50 169.254.10.10

Impact: on such interfaces the panel and --verbose show an address that is not the one used for traffic. NetworkManager's DHCP default routes normally carry src, so this mostly affects static or manually added routes.

Suggested change: choose the global address whose subnet contains the gateway, and read prefix from the address actually displayed. That also fixes an older mismatch, present before this PR, where prefix comes from the first address even when prefsrc names a different one.

Overlap with open PRs on the same script and test file

Impact: whichever of these PRs merges later needs rework in the script and, for the add/add cases, a renamed or merged test file.

Suggested change: name a distinct test file (for example network-status-route-test.sh; network-status-tun-test.sh, -probe- and -ipv6- are already used by #12114, #7932 and #11302) to avoid the add/add collisions, and link #8866 in the description so maintainers can choose between the two selection rules.

Notes

Behaviour change beyond TUN proxies: full-tunnel VPNs that leave a physical default in the main table now show the physical link instead of the VPN interface. In stubbed scenarios this covers wg-quick (default in table 51820), OpenVPN redirect-gateway def1, and a VPN default in the main table at metric 50. This matches the expected behaviour in #12069, but the title, description and code comment mention only TUN routes. Stating it in the description would make the change easier to evaluate.

Optional test improvement: the test does not cover the promised fallback to the probe route. A mutant that replaces the ip -j route get fallback with an empty result still passes all three assertions. Also, its failure on the merge base comes from the ip stub rejecting the base's plain ip route get call (the base prints disconnected), not from observing Meta. A second case with no Ethernet/Wi-Fi main default that asserts the probe-route output, and a stub that also answers the plain ip route get form, would pin both.


Review information

Test scope: source review of the pinned head against the merge base and current quattro tip; iproute2 7.2.0, NetworkManager 1.58.1 and Linux kernel routing source. Behaviour comes from isolated runs of the merge-base and head scripts with stubbed ip, nmcli and ping (20 scenarios, plain and --verbose), plus namespace runs with real kernel routes for the link-down and address-fallback cases. The new test ran at the head, after a simulated merge onto the tip, and against the merge-base script; a mutation check ran against it. Not tested: live NetworkManager, Mihomo, WireGuard or OpenVPN; nmcli latency; shellcheck; the full ./test/shell suite. Open-PR conflicts come from simulated merges.

AI process: Opus 5.5 Medium coordination and synthesis, Opus 5.5 Xhigh technical review and final fact check, GPT 6 Sol Xhigh search for related issues, Opus 5.5 Medium editorial check.

Opt out: To stop receiving these reviews, reply to this comment saying so.

@aisensiy

Copy link
Copy Markdown
Author

Thanks for the detailed review. I pushed 1fda37d to address the route and address-selection cases: the main-table selection now skips tunnel and dead defaults without excluding bridges/bonds, and when a route has no preferred source the script asks the kernel for the gateway source on the selected interface. The prefix is looked up for the displayed address. I also renamed the test to network-status-route-test.sh and added cases for bridge + backup Wi-Fi, bond + TUN, dead default, multiple addresses, and the probe fallback. The PR description now links #8866 and calls out the wider VPN behavior. Focused test, CLI test, bash syntax and shellcheck passed; the full shell run timed out after 240 seconds, so I am not claiming a full-suite pass. I have not tested these configurations on live bridge/bond/VPN hosts; the added cases use command stubs. Please let me know if you prefer the selection policy in #8866.

@omarchybot omarchybot removed the bug Something isn't working label Oct 1, 2026
@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed at 1fda37d. Nothing was run yet: classification comes before testing, and it did not settle.

Classification is unsettled. Two models classed this pull request separately. Claude Opus 5.5 classed it as a bug fix: on quattro, omarchy-network-status follows ip route get 1.1.1.1, so a policy-routed TUN such as Mihomo's Meta is reported as the Ethernet connection with its internal address (#12069). Codex Medium classed it as an enhancement. A reasonable ground for that reading is in the change itself: the panel now describes the first live non-tunnel default route in the main table rather than the route traffic takes, so a full-tunnel VPN that leaves a physical default in the main table (wg-quick, OpenVPN redirect-gateway def1) now shows the physical link rather than the VPN. That is a deliberate change to what the panel reports, beyond the misreport in #12069. Until the two agree it carries no bug or enhancement label, and the earlier bug label, applied to the previous head, has been removed.

Related: #8866 fixes the same misreport in the same function with a different rule (first connected NetworkManager Wi-Fi or Ethernet device, then the lowest-metric main-table default). #12114 also touches this script but only switches to Wi-Fi when the probe lands on a tunnel, keeping the tunnel's address and gateway in --verbose, so it does not cover the wired case in #12069.

Waiting on: the maintainer, to decide whether the panel should describe the physical link or the route traffic takes, and which of this and #8866 to pursue. Nothing is needed from you for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Network panel reports TUN interface and fake IP as active Ethernet

3 participants