github.com/clmul/water@v0.0.3-0.20221017135504-100d910a03ab/syscalls_linux.go (about)

     1  package water
     2  
     3  import (
     4  	"strings"
     5  	"unsafe"
     6  
     7  	"golang.org/x/sys/unix"
     8  )
     9  
    10  const (
    11  	cIFF_TUN         = 0x0001
    12  	cIFF_TAP         = 0x0002
    13  	cIFF_NO_PI       = 0x1000
    14  	cIFF_MULTI_QUEUE = 0x0100
    15  )
    16  
    17  type ifReq struct {
    18  	Name  [0x10]byte
    19  	Flags uint16
    20  	pad   [0x28 - 0x10 - 2]byte
    21  }
    22  
    23  func ioctl(fd int, request uintptr, argp uintptr) error {
    24  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), request, argp)
    25  	if errno != 0 {
    26  		return errno
    27  	}
    28  	return nil
    29  }
    30  
    31  const (
    32  	devfile = "/dev/net/tun"
    33  )
    34  
    35  func createDevNetTun() error {
    36  	err := unix.Mkdir("/dev/net", 0755)
    37  	if err != nil {
    38  		return err
    39  	}
    40  	dev := unix.Mkdev(10, 200)
    41  	err = unix.Mknod(devfile, unix.S_IFCHR|0666, int(dev))
    42  	return err
    43  }
    44  
    45  func openDevNetTun() (int, error) {
    46  	fd, err := unix.Open(devfile, unix.O_RDWR, 0)
    47  	if err == nil {
    48  		return fd, nil
    49  	}
    50  	if err != unix.ENOENT {
    51  		return 0, err
    52  	}
    53  	err = createDevNetTun()
    54  	if err != nil {
    55  		return 0, err
    56  	}
    57  	return unix.Open(devfile, unix.O_RDWR, 0)
    58  }
    59  
    60  func open(config Config) (int, string, error) {
    61  	fd, err := openDevNetTun()
    62  	if err != nil {
    63  		return 0, "", err
    64  	}
    65  
    66  	var flags uint16
    67  	flags = cIFF_TUN | cIFF_NO_PI
    68  	if config.MultiQueue {
    69  		flags |= cIFF_MULTI_QUEUE
    70  	}
    71  	name, err := createInterface(fd, config.Name, flags)
    72  	if err != nil {
    73  		return 0, "", err
    74  	}
    75  
    76  	if err = setDeviceOptions(fd, config); err != nil {
    77  		return 0, "", err
    78  	}
    79  
    80  	return fd, name, err
    81  }
    82  
    83  func createInterface(fd int, ifName string, flags uint16) (createdIFName string, err error) {
    84  	var req ifReq
    85  	req.Flags = flags
    86  	copy(req.Name[:], ifName)
    87  
    88  	err = ioctl(fd, unix.TUNSETIFF, uintptr(unsafe.Pointer(&req)))
    89  	if err != nil {
    90  		return
    91  	}
    92  
    93  	createdIFName = strings.Trim(string(req.Name[:]), "\x00")
    94  	return
    95  }
    96  
    97  func setDeviceOptions(fd int, config Config) (err error) {
    98  	// Device Permissions
    99  	if config.Permissions != nil {
   100  		// Set Owner
   101  		if err = ioctl(fd, unix.TUNSETOWNER, uintptr(config.Permissions.Owner)); err != nil {
   102  			return
   103  		}
   104  
   105  		// Set Group
   106  		if err = ioctl(fd, unix.TUNSETGROUP, uintptr(config.Permissions.Group)); err != nil {
   107  			return
   108  		}
   109  	}
   110  
   111  	// Set/Clear Persist Device Flag
   112  	value := 0
   113  	if config.Persist {
   114  		value = 1
   115  	}
   116  	return ioctl(fd, unix.TUNSETPERSIST, uintptr(value))
   117  }