From 78059b43e3d00a0f2b75677461692745cce34a63 Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Thu, 4 Jun 2026 23:09:46 -0400 Subject: refactor: remove dependency on ip CLI tool and abstract network logic Eliminate the external dependency on the `ip` (iproute2) command-line tool by centralizing network configuration and diagnostics within a new `internal/network` package using the `netlink` library. Changes: - Introduced `internal/network` package to handle network interface listing and configuration. - Replaced `exec.Command("ip", "link")` in `internal/namespace.VerifyIsolation` with `network.ListInterfaces()`. - Improved `VerifyIsolation` to explicitly ensure only the loopback interface is present in a fresh network namespace. - Moved interface and routing configuration logic from `internal/wireguard` to `internal/network`. - Removed unnecessary `os/exec` imports from network-related files. This change increases the tool's portability by removing the requirement for `iproute2` to be installed in the target environment. --- internal/network/network.go | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 internal/network/network.go (limited to 'internal/network/network.go') diff --git a/internal/network/network.go b/internal/network/network.go new file mode 100644 index 0000000..6afcf5e --- /dev/null +++ b/internal/network/network.go @@ -0,0 +1,82 @@ +//go:build linux + +package network + +import ( + "fmt" + "net" + "strings" + + "github.com/vishvananda/netlink" +) + +// InterfaceInfo contains basic information about a network interface. +type InterfaceInfo struct { + Name string + Index int +} + +// ListInterfaces returns a list of all network interfaces present in the current namespace. +func ListInterfaces() ([]InterfaceInfo, error) { + links, err := netlink.LinkList() + if err != nil { + return nil, fmt.Errorf("failed to list interfaces: %w", err) + } + + var interfaces []InterfaceInfo + for _, link := range links { + interfaces = append(interfaces, InterfaceInfo{ + Name: link.Attrs().Name, + Index: link.Attrs().Index, + }) + } + return interfaces, nil +} + +// ConfigureInterface sets the MTU, brings the interface up, assigns an IP address, +// and configures the default route. +func ConfigureInterface(name, address string, mtu int) error { + link, err := netlink.LinkByName(name) + if err != nil { + return fmt.Errorf("failed to find link %s: %w", name, err) + } + + if err := netlink.LinkSetMTU(link, mtu); err != nil { + return fmt.Errorf("failed to set MTU %d on link %s: %w", mtu, name, err) + } + + if err := netlink.LinkSetUp(link); err != nil { + return fmt.Errorf("failed to bring up link %s: %w", name, err) + } + + addr, err := netlink.ParseAddr(address) + if err != nil { + return fmt.Errorf("invalid IP address %s: %w", address, err) + } + if err := netlink.AddrAdd(link, addr); err != nil { + if !strings.Contains(err.Error(), "file exists") { + return fmt.Errorf("failed to add address %s to link %s: %w", address, name, err) + } + } + + var dst *net.IPNet + if addr.IP.To4() != nil { + _, dst, _ = net.ParseCIDR("0.0.0.0/0") + } else { + _, dst, _ = net.ParseCIDR("::/0") + } + + route := &netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: link.Attrs().Index, + Dst: dst, + } + + if err := netlink.RouteAdd(route); err != nil { + if err := netlink.RouteReplace(route); err != nil { + return fmt.Errorf("failed to configure default route via %s: %w", name, err) + } + } + + return nil +} -- cgit v1.2.3