mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 04:09:15 -04:00
fix(network): exclude virtual ether links and prune stale ones from networkd (#2505)
The networkd backend treated any link reporting Type=ether as a wired uplink. Podman bridges and veth pairs report Type=ether, so they were classified as ethernet: isWired() short-circuited on Type and never consulted looksVirtual(), which also lacked a podman prefix. The link map was also never pruned. Links discovered at enumeration or via signals were kept forever, so torn-down container interfaces lingered as routable and could win the wired-uplink slot over the real NIC -- leaving the indicator showing WiFi while a wired connection was active and default-routed. - isWired()/isWireless() exclude virtual interfaces before consulting Type, and looksVirtual() now recognises podman. - enumerateLinks() reconciles the cached map against ListLinks via syncLinks(), pruning links that no longer appear so dead interfaces don't accumulate.
This commit is contained in:
@@ -27,16 +27,19 @@ type linkInfo struct {
|
||||
}
|
||||
|
||||
func (l *linkInfo) isWired() bool {
|
||||
if looksVirtual(l.name) {
|
||||
return false
|
||||
}
|
||||
if l.linkType != "" {
|
||||
return l.linkType == "ether"
|
||||
}
|
||||
if looksVirtual(l.name) || strings.HasPrefix(l.name, "wlan") || strings.HasPrefix(l.name, "wlp") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return !strings.HasPrefix(l.name, "wlan") && !strings.HasPrefix(l.name, "wlp")
|
||||
}
|
||||
|
||||
func (l *linkInfo) isWireless() bool {
|
||||
if looksVirtual(l.name) {
|
||||
return false
|
||||
}
|
||||
if l.linkType != "" {
|
||||
return l.linkType == "wlan"
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func (l *linkInfo) isWireless() bool {
|
||||
|
||||
func looksVirtual(name string) bool {
|
||||
virtualPrefixes := []string{
|
||||
"lo", "docker", "veth", "virbr", "br-", "vnet", "tun", "tap",
|
||||
"lo", "docker", "podman", "veth", "virbr", "br-", "vnet", "tun", "tap",
|
||||
"vboxnet", "vmnet", "kube", "cni", "flannel", "cali",
|
||||
}
|
||||
for _, prefix := range virtualPrefixes {
|
||||
@@ -110,6 +113,12 @@ func (b *SystemdNetworkdBackend) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
type enumeratedLink struct {
|
||||
ifindex int32
|
||||
name string
|
||||
path dbus.ObjectPath
|
||||
}
|
||||
|
||||
func (b *SystemdNetworkdBackend) enumerateLinks() error {
|
||||
obj := b.conn.Object(networkdBusName, b.managerPath)
|
||||
|
||||
@@ -123,25 +132,48 @@ func (b *SystemdNetworkdBackend) enumerateLinks() error {
|
||||
return fmt.Errorf("ListLinks: %w", err)
|
||||
}
|
||||
|
||||
fresh := make([]enumeratedLink, len(links))
|
||||
for i, l := range links {
|
||||
fresh[i] = enumeratedLink{ifindex: l.Ifindex, name: l.Name, path: l.Path}
|
||||
}
|
||||
|
||||
b.linksMutex.Lock()
|
||||
defer b.linksMutex.Unlock()
|
||||
b.syncLinks(fresh)
|
||||
|
||||
for _, l := range links {
|
||||
if existing, ok := b.links[l.Name]; ok && existing.path == l.Path {
|
||||
existing.ifindex = l.Ifindex
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncLinks reconciles the cached link map against the freshly enumerated set:
|
||||
// it adds links not seen before (querying their Type once), refreshes the
|
||||
// ifindex of survivors, and prunes links that no longer appear. Pruning is what
|
||||
// keeps torn-down container interfaces (podman bridges, veth pairs) from
|
||||
// lingering as routable and being mistaken for the wired uplink.
|
||||
// Callers must hold linksMutex.
|
||||
func (b *SystemdNetworkdBackend) syncLinks(fresh []enumeratedLink) {
|
||||
present := make(map[string]bool, len(fresh))
|
||||
for _, l := range fresh {
|
||||
present[l.name] = true
|
||||
if existing, ok := b.links[l.name]; ok && existing.path == l.path {
|
||||
existing.ifindex = l.ifindex
|
||||
continue
|
||||
}
|
||||
info := &linkInfo{
|
||||
ifindex: l.Ifindex,
|
||||
name: l.Name,
|
||||
path: l.Path,
|
||||
linkType: b.fetchLinkType(l.Path),
|
||||
ifindex: l.ifindex,
|
||||
name: l.name,
|
||||
path: l.path,
|
||||
linkType: b.fetchLinkType(l.path),
|
||||
}
|
||||
b.links[l.Name] = info
|
||||
log.Debugf("networkd: enumerated link %s (ifindex=%d, path=%s, type=%q)", l.Name, l.Ifindex, l.Path, info.linkType)
|
||||
b.links[l.name] = info
|
||||
log.Debugf("networkd: enumerated link %s (ifindex=%d, path=%s, type=%q)", l.name, l.ifindex, l.path, info.linkType)
|
||||
}
|
||||
|
||||
return nil
|
||||
for name := range b.links {
|
||||
if !present[name] {
|
||||
log.Debugf("networkd: pruned stale link %s", name)
|
||||
delete(b.links, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetchLinkType queries networkd's Describe method and extracts the link Type
|
||||
|
||||
@@ -160,6 +160,12 @@ func TestLinkInfo_Classify(t *testing.T) {
|
||||
{"loopback type", "lo", "loopback", false, false},
|
||||
{"none type (tun overlay)", "nebula.homelab", "none", false, false},
|
||||
{"none type (wireguard)", "wg0", "none", false, false},
|
||||
// Virtual interfaces report Type=ether but must never be mistaken for
|
||||
// the wired uplink — stale podman/veth links would otherwise poison
|
||||
// ethernet detection.
|
||||
{"veth ether excluded", "veth1234", "ether", false, false},
|
||||
{"podman bridge ether excluded", "podman3", "ether", false, false},
|
||||
{"docker bridge ether excluded", "docker0", "ether", false, false},
|
||||
// Fallback path: linkType unavailable, name-prefix heuristic applies.
|
||||
{"fallback enp wired", "enp141s0", "", true, false},
|
||||
{"fallback wlan wireless", "wlan0", "", false, true},
|
||||
@@ -205,8 +211,46 @@ func TestParseDescribeType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncLinks_PrunesRemovedLinks(t *testing.T) {
|
||||
// Stale container interfaces (torn-down podman bridges, veth pairs) must
|
||||
// not linger in the link map after they disappear from ListLinks — kept as
|
||||
// routable, they stole the wired-uplink slot from the real ethernet NIC.
|
||||
backend, _ := NewSystemdNetworkdBackend()
|
||||
backend.links = map[string]*linkInfo{
|
||||
"eno1": {ifindex: 2, name: "eno1", path: "/org/freedesktop/network1/link/_32", linkType: "ether", opState: "routable"},
|
||||
"podman3": {ifindex: 9, name: "podman3", path: "/org/freedesktop/network1/link/_39", linkType: "ether", opState: "routable"},
|
||||
"veth0": {ifindex: 10, name: "veth0", path: "/org/freedesktop/network1/link/_310", linkType: "ether", opState: "routable"},
|
||||
}
|
||||
|
||||
backend.syncLinks([]enumeratedLink{
|
||||
{ifindex: 2, name: "eno1", path: "/org/freedesktop/network1/link/_32"},
|
||||
})
|
||||
|
||||
assert.Len(t, backend.links, 1)
|
||||
assert.Contains(t, backend.links, "eno1")
|
||||
assert.NotContains(t, backend.links, "podman3")
|
||||
assert.NotContains(t, backend.links, "veth0")
|
||||
}
|
||||
|
||||
func TestSyncLinks_RefreshesSurvivingLink(t *testing.T) {
|
||||
// A link that survives keeps its cached Type — Describe is only queried for
|
||||
// newly seen links — while picking up a refreshed ifindex.
|
||||
backend, _ := NewSystemdNetworkdBackend()
|
||||
backend.links = map[string]*linkInfo{
|
||||
"eno1": {ifindex: 2, name: "eno1", path: "/org/freedesktop/network1/link/_32", linkType: "ether"},
|
||||
}
|
||||
|
||||
backend.syncLinks([]enumeratedLink{
|
||||
{ifindex: 7, name: "eno1", path: "/org/freedesktop/network1/link/_32"},
|
||||
})
|
||||
|
||||
assert.Len(t, backend.links, 1)
|
||||
assert.Equal(t, int32(7), backend.links["eno1"].ifindex)
|
||||
assert.Equal(t, "ether", backend.links["eno1"].linkType)
|
||||
}
|
||||
|
||||
func TestLooksVirtual(t *testing.T) {
|
||||
virtual := []string{"lo", "docker0", "veth123", "virbr0", "br-abc", "vnet0", "tun0", "tap0", "vboxnet0", "vmnet1", "kube-ipvs0", "cni0", "flannel.1", "cali-abc"}
|
||||
virtual := []string{"lo", "docker0", "veth123", "virbr0", "br-abc", "vnet0", "tun0", "tap0", "vboxnet0", "vmnet1", "kube-ipvs0", "cni0", "flannel.1", "cali-abc", "podman0", "podman3"}
|
||||
for _, n := range virtual {
|
||||
assert.True(t, looksVirtual(n), "%s should look virtual", n)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user