2024-06-14 21:26:46 +00:00
|
|
|
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")
|
2024-06-24 19:58:15 +00:00
|
|
|
ErrParseMac = errors.New("Failed to parse MAC address")
|
2024-06-14 21:26:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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 {
|
2024-06-24 19:58:15 +00:00
|
|
|
class int
|
|
|
|
orig string
|
|
|
|
pci, usb, mac string
|
|
|
|
netpath string
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 19:58:15 +00:00
|
|
|
func (i iface) String() (ret string) {
|
2024-06-14 21:26:46 +00:00
|
|
|
var pfx string
|
2024-06-24 19:58:15 +00:00
|
|
|
ret = i.orig
|
|
|
|
|
2024-06-14 21:26:46 +00:00
|
|
|
switch i.class {
|
|
|
|
case ClassEthernet:
|
|
|
|
pfx = "en"
|
|
|
|
case ClassWireless:
|
|
|
|
pfx = "wl"
|
|
|
|
}
|
2024-06-24 19:58:15 +00:00
|
|
|
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
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func New(ifname string) (i iface, err error) {
|
|
|
|
i.orig = ifname
|
|
|
|
|
2024-06-24 19:58:15 +00:00
|
|
|
i.netpath = strings.Join([]string{SYSFS, "class/net", i.orig}, "/")
|
2024-06-14 21:26:46 +00:00
|
|
|
|
|
|
|
switch ifname[0] {
|
|
|
|
case 'e':
|
|
|
|
i.class = ClassEthernet
|
|
|
|
case 'w':
|
|
|
|
i.class = ClassWireless
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i iface) shouldRun() (should bool, err error) {
|
2024-06-24 19:58:15 +00:00
|
|
|
ifname_assign_type := strings.Join([]string{i.netpath, "name_assign_type"}, "/")
|
2024-06-14 21:26:46 +00:00
|
|
|
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)
|
2024-06-24 19:58:15 +00:00
|
|
|
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
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-06-24 19:58:15 +00:00
|
|
|
func (i iface) Process(phys bool) (nif string, err error) {
|
2024-06-14 21:26:46 +00:00
|
|
|
var (
|
|
|
|
procmode int
|
|
|
|
)
|
2024-06-24 19:58:15 +00:00
|
|
|
nif = i.orig
|
2024-06-14 21:26:46 +00:00
|
|
|
|
2024-06-24 19:58:15 +00:00
|
|
|
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
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 19:58:15 +00:00
|
|
|
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
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
2024-06-24 19:58:15 +00:00
|
|
|
|
|
|
|
switch procmode {
|
|
|
|
case PathProcPCI:
|
|
|
|
{
|
|
|
|
if err = i.ProcPCI(v); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case PathProcUSB:
|
|
|
|
{
|
|
|
|
if err = i.ProcUSB(v); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 19:58:15 +00:00
|
|
|
err = i.ProcMAC()
|
|
|
|
|
2024-06-14 21:26:46 +00:00
|
|
|
// It can error but as long as we get a identifier along the path it's okay
|
2024-06-24 19:58:15 +00:00
|
|
|
if i.pci == "" && i.mac == "" {
|
|
|
|
return
|
2024-06-14 21:26:46 +00:00
|
|
|
}
|
2024-06-24 19:58:15 +00:00
|
|
|
nif = i.String()
|
2024-06-14 21:26:46 +00:00
|
|
|
return
|
|
|
|
}
|