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 }