net-predictable/misc.go
2024-06-14 21:26:46 +00:00

165 lines
3.2 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")
)
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
path []string
pci, usb string
}
func (i iface) String() string {
var pfx string
switch i.class {
case ClassEthernet:
pfx = "en"
case ClassWireless:
pfx = "wl"
}
return fmt.Sprintf("%s%s%s", pfx, i.pci, i.usb)
}
func New(ifname string) (i iface, err error) {
i.orig = ifname
spath := strings.Join([]string{SYSFS, "class/net", i.orig, "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 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
}
i.path = strings.Split(devpath, "/")
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{SYSFS, "class/net", i.orig, "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)
} else {
if contents, err = io.ReadAll(file); err != nil {
err = fmt.Errorf("Failed to read interface rename property: %w", err)
} else {
file.Close()
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)
} else {
if res != NET_NAME_ENUM {
should = false
return
}
}
}
}
return
}
func (i iface) Process() (nif string, err error) {
var (
procmode int
)
for _, v := range i.path {
if strings.HasPrefix(v, "pci") {
procmode = PathProcPCI
continue
} else if strings.HasPrefix(v, "usb") {
procmode = PathProcUSB
continue
}
switch procmode {
case PathProcPCI:
{
var hold string
if hold, err = ProcPCI(v); err != nil {
continue
}
i.pci = hold
}
case PathProcUSB:
{
var hold string
if hold, err = ProcUSB(v); err != nil {
continue
}
i.usb = hold
}
}
}
nif = i.String()
// It can error but as long as we get a identifier along the path it's okay
if i.pci != "" {
err = nil
}
return
}