Files
ctrld/cmd/cli/net_linux_test.go
T

202 lines
7.1 KiB
Go

package cli
import (
"maps"
"os"
"path/filepath"
"testing"
)
// writeSysfsInterface builds one interface entry under a fake sysfs root.
func writeSysfsInterface(t *testing.T, root, name string, files map[string]string, wireless bool) {
t.Helper()
dir := filepath.Join(root, name)
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("create %s: %v", dir, err)
}
for file, content := range files {
if err := os.WriteFile(filepath.Join(dir, file), []byte(content), 0o644); err != nil {
t.Fatalf("write %s: %v", file, err)
}
}
if !wireless {
return
}
if err := os.MkdirAll(filepath.Join(dir, "wireless"), 0o755); err != nil {
t.Fatalf("create the wireless directory: %v", err)
}
}
// writeSysfsDriverLink points the device entry of an interface at a driver.
func writeSysfsDriverLink(t *testing.T, root, name, driver string) {
t.Helper()
device := filepath.Join(root, name, "device")
if err := os.MkdirAll(device, 0o755); err != nil {
t.Fatalf("create %s: %v", device, err)
}
if err := os.Symlink(filepath.Join(root, "drivers", driver), filepath.Join(device, "driver")); err != nil {
t.Fatalf("link the driver of %s: %v", name, err)
}
}
// writeSysfsDriverUevent names the driver in the uevent of the device.
func writeSysfsDriverUevent(t *testing.T, root, name, driver string) {
t.Helper()
device := filepath.Join(root, name, "device")
if err := os.MkdirAll(device, 0o755); err != nil {
t.Fatalf("create %s: %v", device, err)
}
if err := os.WriteFile(filepath.Join(device, "uevent"), []byte("DRIVER="+driver+"\n"), 0o644); err != nil {
t.Fatalf("write the uevent of %s: %v", name, err)
}
}
// stubSysfsNetRoot points the sysfs reader at a temporary directory.
func stubSysfsNetRoot(t *testing.T) string {
t.Helper()
before := sysfsNetRoot
t.Cleanup(func() { sysfsNetRoot = before })
sysfsNetRoot = t.TempDir()
return sysfsNetRoot
}
func Test_sysfsInterfaceInfo(t *testing.T) {
root := stubSysfsNetRoot(t)
writeSysfsInterface(t, root, "wlan0", map[string]string{
"type": "1\n",
"uevent": "INTERFACE=wlan0\nDEVTYPE=wlan\nIFINDEX=3\n",
}, true)
writeSysfsInterface(t, root, "eth0", map[string]string{
"type": "1\n",
"uevent": "INTERFACE=eth0\nIFINDEX=2\n",
}, false)
writeSysfsInterface(t, root, "tun0", map[string]string{
"type": "65534\n",
"uevent": "DEVTYPE=tun\n",
}, false)
tests := []struct {
name string
want sysfsInfo
}{
{"wlan0", sysfsInfo{Type: 1, DevType: "wlan", Wireless: true}},
{"eth0", sysfsInfo{Type: 1}},
{"tun0", sysfsInfo{Type: 65534, DevType: "tun"}},
{"absent0", sysfsInfo{}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := sysfsInterfaceInfo(tc.name); got != tc.want {
t.Errorf("sysfsInterfaceInfo(%q) = %+v, want %+v", tc.name, got, tc.want)
}
})
}
}
func Test_sysfsClass(t *testing.T) {
tests := []struct {
name string
iface string
info sysfsInfo
want string
}{
{"wireless", "wlan0", sysfsInfo{Wireless: true}, "hardware"},
{"ethernet", "eth0", sysfsInfo{Type: 1}, "hardware"},
{"tun devtype", "tun0", sysfsInfo{Type: 65534, DevType: "tun"}, "tunnel"},
{"tap devtype", "tap0", sysfsInfo{Type: 1, DevType: "tap"}, "tunnel"},
{"unknown", "sit0", sysfsInfo{Type: 65534}, ""},
{"docker bridge", "docker0", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
{"container link", "veth1a2b3c", sysfsInfo{Type: 1}, "virtual"},
{"libvirt bridge", "virbr0", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
{"compose bridge", "br-1a2b3c", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
{"uplink bridge", "br0", sysfsInfo{Type: 1, DevType: "bridge"}, "hardware"},
{"bond", "bond0", sysfsInfo{Type: 1, DevType: "bond"}, "hardware"},
{"vlan uplink", "eth0.100", sysfsInfo{Type: 1, DevType: "vlan"}, "hardware"},
{"removed container link", "veth9f8e7d", sysfsInfo{}, "virtual"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := sysfsClass(tc.iface, tc.info); got != tc.want {
t.Errorf("sysfsClass(%q, %+v) = %q, want %q", tc.iface, tc.info, got, tc.want)
}
})
}
}
func Test_platformVirtualInterfaces_namesTheContainerInterfaces(t *testing.T) {
root := t.TempDir()
before := sysfsVirtualNetRoot
t.Cleanup(func() { sysfsVirtualNetRoot = before })
sysfsVirtualNetRoot = root
for _, name := range []string{"lo", "docker0", "veth1a2b3c", "virbr0", "br-1a2b3c", "br0", "bond0"} {
if err := os.MkdirAll(filepath.Join(root, name), 0o755); err != nil {
t.Fatalf("create %s: %v", name, err)
}
}
want := map[string]struct{}{"docker0": {}, "veth1a2b3c": {}, "virbr0": {}, "br-1a2b3c": {}}
if got := platformVirtualInterfaces(); !maps.Equal(got, want) {
t.Errorf("platformVirtualInterfaces = %v, want %v", got, want)
}
if got := sysfsVirtualInterfaces(); len(got) != 7 {
t.Errorf("virtualInterfaces = %v, want every entry of the kernel directory", got)
}
}
func Test_platformInterfaceMeta(t *testing.T) {
root := stubSysfsNetRoot(t)
writeSysfsInterface(t, root, "ctrld-test-eth0", map[string]string{
"type": "1\n",
"uevent": "DEVTYPE=bridge\n",
}, false)
class, hardwarePort, service := platformInterfaceMeta("ctrld-test-eth0")
if class != "hardware" {
t.Errorf("class = %q, want hardware", class)
}
if hardwarePort != "bridge" {
t.Errorf("hardware port = %q, want bridge", hardwarePort)
}
if service != "" {
t.Errorf("service = %q, want empty", service)
}
}
func Test_platformInterfaceMeta_namesTheLink(t *testing.T) {
root := stubSysfsNetRoot(t)
writeSysfsInterface(t, root, "wlan0", map[string]string{"type": "1\n", "uevent": "DEVTYPE=wlan\n"}, true)
writeSysfsInterface(t, root, "eth0", map[string]string{"type": "1\n", "uevent": "INTERFACE=eth0\n"}, false)
writeSysfsInterface(t, root, "br0", map[string]string{"type": "1\n", "uevent": "DEVTYPE=bridge\n"}, false)
writeSysfsInterface(t, root, "tun0", map[string]string{"type": "65534\n", "uevent": "DEVTYPE=tun\n"}, false)
writeSysfsInterface(t, root, "usb0", map[string]string{"type": "1\n"}, false)
writeSysfsDriverLink(t, root, "usb0", "ipheth")
writeSysfsInterface(t, root, "usb1", map[string]string{"type": "1\n"}, false)
writeSysfsDriverUevent(t, root, "usb1", "rndis_host")
writeSysfsInterface(t, root, "eth1", map[string]string{"type": "1\n"}, false)
writeSysfsDriverUevent(t, root, "eth1", "e1000e")
tests := []struct {
iface, class, hardwarePort, linkType string
}{
{"wlan0", "hardware", "Wi-Fi", "wifi"},
{"eth0", "hardware", "Ethernet", "ethernet"},
{"eth1", "hardware", "Ethernet", "ethernet"},
{"br0", "hardware", "bridge", "unknown"},
{"tun0", "tunnel", "tun", "tunnel"},
{"usb0", "hardware", hardwarePortIPhoneUSB, "usb_tether"},
{"usb1", "hardware", hardwarePortIPhoneUSB, "usb_tether"},
}
for _, tc := range tests {
t.Run(tc.iface, func(t *testing.T) {
class, hardwarePort, service := platformInterfaceMeta(tc.iface)
if class != tc.class || hardwarePort != tc.hardwarePort || service != "" {
t.Errorf("platformInterfaceMeta(%q) = %q, %q, %q, want %q, %q, empty",
tc.iface, class, hardwarePort, service, tc.class, tc.hardwarePort)
}
if got := linkTypeFor(hardwarePort, class); got != tc.linkType {
t.Errorf("linkTypeFor(%q, %q) = %q, want %q", hardwarePort, class, got, tc.linkType)
}
})
}
}