From 3a93ec35ee80fa11c14b2f890480af797800cd04 Mon Sep 17 00:00:00 2001 From: Daniel Redetzke Date: Sun, 4 Oct 2026 00:39:13 +0300 Subject: [PATCH] Health: show IPv6 uplink and public IPv4/IPv6 addresses The uplink check only reported the IPv4 default route. Split it into IPv4 and IPv6 uplink rows, and add a public address row for each, read from the uplink interface (private addresses are shown as behind NAT). --- kernel_linux.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- kernel_other.go | 3 ++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/kernel_linux.go b/kernel_linux.go index dfc669b..815328d 100644 --- a/kernel_linux.go +++ b/kernel_linux.go @@ -287,6 +287,37 @@ func lanNetworks(uplink string) []netip.Prefix { return out } +// publicAddr reports the uplink's address for the health check: the first +// public one, or else the first private one, marked as behind NAT. It reads +// the interface rather than asking an outside service. +func publicAddr(uplink string, v6 bool) (bool, string) { + l, err := netlink.LinkByName(uplink) + if err != nil { + return false, uplink + " not found" + } + family := netlink.FAMILY_V4 + if v6 { + family = netlink.FAMILY_V6 + } + addrs, _ := netlink.AddrList(l, family) + var private string + for _, a := range addrs { + if !a.IP.IsGlobalUnicast() { + continue + } + if !a.IP.IsPrivate() { + return true, a.IP.String() + } + if private == "" { + private = a.IP.String() + } + } + if private != "" { + return true, private + " (private, behind NAT)" + } + return false, "no address on " + uplink +} + func readSysctl(path string) string { b, err := os.ReadFile(path) if err != nil { @@ -313,7 +344,19 @@ func (k *linuxKernel) Checks(c *Config) []Check { ok, detail := firewallPresent() out = append(out, Check{"nftables rules", ok, detail}) up4 := k.Uplink(c, false) - out = append(out, Check{"Uplink", up4 != "", map[bool]string{true: "IPv4 via " + up4, false: "no default route found"}[up4 != ""]}) + out = append(out, Check{"IPv4 uplink", up4 != "", map[bool]string{true: "via " + up4, false: "no default route found"}[up4 != ""]}) + if up4 != "" { + ok, detail := publicAddr(up4, false) + out = append(out, Check{"Public IPv4", ok, detail}) + } + if c.Server.IPv6Enabled { + up6 := k.Uplink(c, true) + out = append(out, Check{"IPv6 uplink", up6 != "", map[bool]string{true: "via " + up6, false: "no default route found"}[up6 != ""]}) + if up6 != "" { + ok, detail := publicAddr(up6, true) + out = append(out, Check{"Public IPv6", ok, detail}) + } + } return out } diff --git a/kernel_other.go b/kernel_other.go index 76b4c55..01946f1 100644 --- a/kernel_other.go +++ b/kernel_other.go @@ -73,7 +73,8 @@ func (k *simKernel) Checks(c *Config) []Check { {"WireGuard interface", true, c.Server.Interface + " is up"}, {"IPv4 forwarding", true, "net.ipv4.ip_forward=1"}, {"nftables rules", true, "table inet " + appName + " present"}, - {"Uplink", true, "IPv4 via eth0"}, + {"IPv4 uplink", true, "via eth0"}, + {"Public IPv4", true, "203.0.113.10"}, } }