...

Source file src/edge-infra.dev/pkg/lib/kernel/udev/sockets/sockets.go

Documentation: edge-infra.dev/pkg/lib/kernel/udev/sockets

     1  //go:build linux
     2  
     3  package sockets
     4  
     5  import (
     6  	"errors"
     7  	"io"
     8  	"net"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  var (
    16  	ErrInvalidIP            = errors.New("invalid ip address")
    17  	ErrInvalidPort          = errors.New("invalid port")
    18  	ErrInvalidSocketAddress = errors.New("address must be in the format of <ip>:<port>")
    19  )
    20  
    21  type Mode uint32
    22  
    23  const (
    24  	KernelEvent Mode = 1
    25  	// Events that are processed by udev - much richer, with more attributes (such as vendor info, serial numbers and more).
    26  	UdevEvent Mode = 2
    27  )
    28  
    29  // NewUEventNetlinkReader returns a reader that connects and listens to
    30  // local netlink within the binaries network namespace
    31  func NewUEventNetlinkReader() (io.ReadCloser, int, error) {
    32  	fd, err := newUEventNetlinkSocket()
    33  	if err != nil {
    34  		return nil, 0, err
    35  	}
    36  
    37  	sockAddr := unix.SockaddrNetlink{
    38  		Family: unix.AF_NETLINK,
    39  		Groups: uint32(UdevEvent),
    40  	}
    41  
    42  	if err = unix.Bind(fd, &sockAddr); err != nil {
    43  		unix.Close(fd)
    44  	}
    45  	return &monitor{fd: fd}, fd, err
    46  }
    47  
    48  // NewUEventRemoteReader returns a reader that connects and listens to a remote socket
    49  func NewUEventRemoteReader(address string) (io.ReadCloser, int, error) {
    50  	fd, err := newUEventRemoteSocket(address)
    51  	if err != nil {
    52  		return nil, 0, err
    53  	}
    54  	return &monitor{fd: fd}, fd, nil
    55  }
    56  
    57  // newUEventNetlinkSocket instantiates a new netlink socket connection
    58  // and returns the file descriptor
    59  func newUEventNetlinkSocket() (int, error) {
    60  	fd, err := unix.Socket(
    61  		unix.AF_NETLINK,
    62  		unix.SOCK_RAW,
    63  		unix.NETLINK_KOBJECT_UEVENT,
    64  	)
    65  	if err != nil {
    66  		return 0, err
    67  	}
    68  
    69  	// avoid leaking the file-descriptor to child processes
    70  	unix.CloseOnExec(fd)
    71  	return fd, err
    72  }
    73  
    74  // newUEventRemoteSocket instantiates a new remote socket connection
    75  // and returns the file descriptor
    76  func newUEventRemoteSocket(address string) (int, error) {
    77  	fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP)
    78  	if err != nil {
    79  		return 0, err
    80  	}
    81  
    82  	port, ip, err := parseAddress(address)
    83  	if err != nil {
    84  		return 0, err
    85  	}
    86  
    87  	addr := unix.SockaddrInet4{
    88  		Port: port,
    89  		Addr: ip,
    90  	}
    91  
    92  	if err := unix.Connect(fd, &addr); err != nil {
    93  		unix.Close(fd)
    94  		return 0, err
    95  	}
    96  	// avoid leaking the file-descriptor to child processes
    97  	unix.CloseOnExec(fd)
    98  	return fd, nil
    99  }
   100  
   101  // parseAddress will validate an IPv4 address in format 0.0.0.0:8080
   102  // and return the port and IP
   103  func parseAddress(address string) (port int, ip [4]byte, err error) {
   104  	addressSplit := strings.Split(address, ":")
   105  	if len(addressSplit) != 2 {
   106  		return 0, ip, ErrInvalidSocketAddress
   107  	}
   108  
   109  	port, err = strconv.Atoi(addressSplit[1])
   110  	if err != nil {
   111  		return 0, ip, ErrInvalidPort
   112  	}
   113  
   114  	ipAddr := net.ParseIP(addressSplit[0]).To4()
   115  	if len(ipAddr) != 4 {
   116  		return 0, ip, ErrInvalidIP
   117  	}
   118  	copy(ip[:], ipAddr)
   119  	return port, ip, nil
   120  }
   121  

View as plain text