net-predictable/misc.go

176 lines
3.4 KiB
Go

package main
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
)
const (
PathProcPCI = iota
PathProcUSB
)
var (
ErrInvalidPCIFormat = errors.New("Invalid PCI address format")
ErrInvalidUSBFormat = errors.New("Invalid USB address format")
ErrParseMac = errors.New("Failed to parse MAC address")
)
var SYSFS = "/sys"
// Sourced from include/uapi/linux/netdevice.h
// as of v6.8
const (
NET_NAME_UNKNOWN = 0 /* unknown origin (not exposed to userspace) */
NET_NAME_ENUM = 1 /* enumerated by kernel */
NET_NAME_PREDICTABLE = 2 /* predictably named by the kernel */
NET_NAME_USER = 3 /* provided by user-space */
NET_NAME_RENAMED = 4 /* renamed by user-space */
)
const (
ClassEthernet = iota
ClassWireless
)
type iface struct {
class int
orig string
pci, usb, mac string
netpath string
}
func (i iface) String() (ret string) {
var pfx string
ret = i.orig
switch i.class {
case ClassEthernet:
pfx = "en"
case ClassWireless:
pfx = "wl"
}
if i.pci != "" {
ret = fmt.Sprintf("%s%s%s", pfx, i.pci, i.usb)
} else if i.mac != "" {
ret = fmt.Sprintf("%s%s", pfx, i.mac)
}
return
}
func New(ifname string) (i iface, err error) {
i.orig = ifname
i.netpath = strings.Join([]string{SYSFS, "class/net", i.orig}, "/")
switch ifname[0] {
case 'e':
i.class = ClassEthernet
case 'w':
i.class = ClassWireless
}
return
}
func (i iface) shouldRun() (should bool, err error) {
ifname_assign_type := strings.Join([]string{i.netpath, "name_assign_type"}, "/")
should = true
var (
file *os.File
contents []byte
)
if file, err = os.Open(ifname_assign_type); err != nil {
err = fmt.Errorf("Failed to open interface rename property: %w", err)
return
}
defer file.Close()
if contents, err = io.ReadAll(file); err != nil {
err = fmt.Errorf("Failed to read interface rename property: %w", err)
return
}
num, _ := strings.CutSuffix(string(contents), "\n")
var res int
if res, err = strconv.Atoi(num); err != nil {
err = fmt.Errorf("Failed to interpret interface rename property as number: %w", err)
return
}
if res != NET_NAME_ENUM {
should = false
return
}
return
}
func (i iface) Process(phys bool) (nif string, err error) {
var (
procmode int
)
nif = i.orig
if phys {
spath := strings.Join([]string{i.netpath, "device"}, "/")
var real string
if real, err = filepath.EvalSymlinks(spath); err != nil {
if errors.Is(err, os.ErrNotExist) {
err = fmt.Errorf("Interface %s isn't a hardware device", i.orig)
return
}
err = fmt.Errorf("Failed to resolve symlink to device: %w", err)
return
}
var devpath string
var ok bool
sysdev := strings.Join([]string{SYSFS, "devices/"}, "/")
if devpath, ok = strings.CutPrefix(real, sysdev); !ok {
err = fmt.Errorf("Symlink doesn't point to %s\n", sysdev)
return
}
for _, v := range strings.Split(devpath, "/") {
if strings.HasPrefix(v, "pci") {
procmode = PathProcPCI
continue
} else if strings.HasPrefix(v, "usb") {
procmode = PathProcUSB
continue
}
switch procmode {
case PathProcPCI:
{
if err = i.ProcPCI(v); err != nil {
continue
}
}
case PathProcUSB:
{
if err = i.ProcUSB(v); err != nil {
continue
}
}
}
}
}
err = i.ProcMAC()
// It can error but as long as we get a identifier along the path it's okay
if i.pci == "" && i.mac == "" {
return
}
nif = i.String()
return
}