git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/sysinfo/network.go (about)

     1  // Copyright © 2016 Zlatko Čalušić
     2  //
     3  // Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
     4  
     5  package sysinfo
     6  
     7  import (
     8  	"io/ioutil"
     9  	"os"
    10  	"path"
    11  	"strings"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  // NetworkDevice information.
    17  type NetworkDevice struct {
    18  	Name       string `json:"name,omitempty"`
    19  	Driver     string `json:"driver,omitempty"`
    20  	MACAddress string `json:"macaddress,omitempty"`
    21  	Port       string `json:"port,omitempty"`
    22  	Speed      uint   `json:"speed,omitempty"` // device max supported speed in Mbps
    23  }
    24  
    25  func getPortType(supp uint32) (port string) {
    26  	for i, p := range [...]string{"tp", "aui", "mii", "fibre", "bnc"} {
    27  		if supp&(1<<uint(i+7)) > 0 {
    28  			port += p + "/"
    29  		}
    30  	}
    31  
    32  	port = strings.TrimRight(port, "/")
    33  	return
    34  }
    35  
    36  func getMaxSpeed(supp uint32) (speed uint) {
    37  	// Fancy, right?
    38  	switch {
    39  	case supp&0x78000000 > 0:
    40  		speed = 56000
    41  	case supp&0x07800000 > 0:
    42  		speed = 40000
    43  	case supp&0x00600000 > 0:
    44  		speed = 20000
    45  	case supp&0x001c1000 > 0:
    46  		speed = 10000
    47  	case supp&0x00008000 > 0:
    48  		speed = 2500
    49  	case supp&0x00020030 > 0:
    50  		speed = 1000
    51  	case supp&0x0000000c > 0:
    52  		speed = 100
    53  	case supp&0x00000003 > 0:
    54  		speed = 10
    55  	}
    56  
    57  	return
    58  }
    59  
    60  func getSupported(name string) uint32 {
    61  	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
    62  	if err != nil {
    63  		return 0
    64  	}
    65  	defer syscall.Close(fd)
    66  
    67  	// struct ethtool_cmd from /usr/include/linux/ethtool.h
    68  	var ethtool struct {
    69  		Cmd           uint32
    70  		Supported     uint32
    71  		Advertising   uint32
    72  		Speed         uint16
    73  		Duplex        uint8
    74  		Port          uint8
    75  		PhyAddress    uint8
    76  		Transceiver   uint8
    77  		Autoneg       uint8
    78  		MdioSupport   uint8
    79  		Maxtxpkt      uint32
    80  		Maxrxpkt      uint32
    81  		SpeedHi       uint16
    82  		EthTpMdix     uint8
    83  		Reserved2     uint8
    84  		LpAdvertising uint32
    85  		Reserved      [2]uint32
    86  	}
    87  
    88  	// ETHTOOL_GSET from /usr/include/linux/ethtool.h
    89  	const GSET = 0x1
    90  
    91  	ethtool.Cmd = GSET
    92  
    93  	// struct ifreq from /usr/include/linux/if.h
    94  	var ifr struct {
    95  		Name [16]byte
    96  		Data uintptr
    97  	}
    98  
    99  	copy(ifr.Name[:], name+"\000")
   100  	ifr.Data = uintptr(unsafe.Pointer(&ethtool))
   101  
   102  	// SIOCETHTOOL from /usr/include/linux/sockios.h
   103  	const SIOCETHTOOL = 0x8946
   104  
   105  	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), uintptr(unsafe.Pointer(&ifr)))
   106  	if errno == 0 {
   107  		return ethtool.Supported
   108  	}
   109  
   110  	return 0
   111  }
   112  
   113  func (si *SysInfo) getNetworkInfo() {
   114  	sysClassNet := "/sys/class/net"
   115  	devices, err := ioutil.ReadDir(sysClassNet)
   116  	if err != nil {
   117  		return
   118  	}
   119  
   120  	si.Network = make([]NetworkDevice, 0)
   121  	for _, link := range devices {
   122  		fullpath := path.Join(sysClassNet, link.Name())
   123  		dev, err := os.Readlink(fullpath)
   124  		if err != nil {
   125  			continue
   126  		}
   127  
   128  		if strings.HasPrefix(dev, "../../devices/virtual/") {
   129  			continue
   130  		}
   131  
   132  		supp := getSupported(link.Name())
   133  
   134  		device := NetworkDevice{
   135  			Name:       link.Name(),
   136  			MACAddress: slurpFile(path.Join(fullpath, "address")),
   137  			Port:       getPortType(supp),
   138  			Speed:      getMaxSpeed(supp),
   139  		}
   140  
   141  		if driver, err := os.Readlink(path.Join(fullpath, "device", "driver")); err == nil {
   142  			device.Driver = path.Base(driver)
   143  		}
   144  
   145  		si.Network = append(si.Network, device)
   146  	}
   147  }