github.com/borderzero/water@v0.0.1/syscalls_darwin.go (about) 1 package water 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "math" 8 "os" 9 "strconv" 10 "strings" 11 "sync" 12 "syscall" 13 "unsafe" 14 ) 15 16 const appleUTUNCtl = "com.apple.net.utun_control" 17 18 /* 19 * From ioctl.h: 20 * #define IOCPARM_MASK 0x1fff // parameter length, at most 13 bits 21 * ... 22 * #define IOC_OUT 0x40000000 // copy out parameters 23 * #define IOC_IN 0x80000000 // copy in parameters 24 * #define IOC_INOUT (IOC_IN|IOC_OUT) 25 * ... 26 * #define _IOC(inout,group,num,len) \ 27 * (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) 28 * ... 29 * #define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) 30 * 31 * From kern_control.h: 32 * #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) // get id from name 33 * 34 */ 35 36 const appleCTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 37 38 /* 39 * #define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) 40 * #define TUNSIFMODE _IOW('t', 94, int) 41 */ 42 const appleTUNSIFMODE = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 94 43 44 /* 45 * struct sockaddr_ctl { 46 * u_char sc_len; // depends on size of bundle ID string 47 * u_char sc_family; // AF_SYSTEM 48 * u_int16_t ss_sysaddr; // AF_SYS_KERNCONTROL 49 * u_int32_t sc_id; // Controller unique identifier 50 * u_int32_t sc_unit; // Developer private unit number 51 * u_int32_t sc_reserved[5]; 52 * }; 53 */ 54 type sockaddrCtl struct { 55 scLen uint8 56 scFamily uint8 57 ssSysaddr uint16 58 scID uint32 59 scUnit uint32 60 scReserved [5]uint32 61 } 62 63 var sockaddrCtlSize uintptr = 32 64 65 func openDev(config Config) (ifce *Interface, err error) { 66 if config.Driver == MacOSDriverTunTapOSX { 67 return openDevTunTapOSX(config) 68 } 69 if config.Driver == MacOSDriverSystem { 70 return openDevSystem(config) 71 } 72 return nil, errors.New("unrecognized driver") 73 } 74 75 // openDevSystem opens tun device on system 76 func openDevSystem(config Config) (ifce *Interface, err error) { 77 if config.DeviceType != TUN { 78 return nil, errors.New("only tun is implemented for SystemDriver, use TunTapOSXDriver for tap") 79 } 80 81 ifIndex := -1 82 if config.Name != "" { 83 const utunPrefix = "utun" 84 if !strings.HasPrefix(config.Name, utunPrefix) { 85 return nil, fmt.Errorf("Interface name must be utun[0-9]+") 86 } 87 ifIndex, err = strconv.Atoi(config.Name[len(utunPrefix):]) 88 if err != nil || ifIndex < 0 || ifIndex > math.MaxUint32-1 { 89 return nil, fmt.Errorf("Interface name must be utun[0-9]+") 90 } 91 } 92 93 var fd int 94 // Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ... 95 // 96 // In sys/socket.h: 97 // #define PF_SYSTEM AF_SYSTEM 98 // 99 // In sys/sys_domain.h: 100 // #define SYSPROTO_CONTROL 2 /* kernel control protocol */ 101 if fd, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil { 102 return nil, fmt.Errorf("error in syscall.Socket: %v", err) 103 } 104 105 var ctlInfo = &struct { 106 ctlID uint32 107 ctlName [96]byte 108 }{} 109 copy(ctlInfo.ctlName[:], []byte(appleUTUNCtl)) 110 111 if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(appleCTLIOCGINFO), uintptr(unsafe.Pointer(ctlInfo))); errno != 0 { 112 err = errno 113 return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 114 } 115 116 addrP := unsafe.Pointer(&sockaddrCtl{ 117 scLen: uint8(sockaddrCtlSize), 118 scFamily: syscall.AF_SYSTEM, 119 120 /* #define AF_SYS_CONTROL 2 */ 121 ssSysaddr: 2, 122 123 scID: ctlInfo.ctlID, 124 scUnit: uint32(ifIndex) + 1, 125 }) 126 if _, _, errno := syscall.RawSyscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(addrP), uintptr(sockaddrCtlSize)); errno != 0 { 127 err = errno 128 return nil, fmt.Errorf("error in syscall.RawSyscall(syscall.SYS_CONNECT, ...): %v", err) 129 } 130 131 var ifName struct { 132 name [16]byte 133 } 134 ifNameSize := uintptr(16) 135 if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), 136 2, /* #define SYSPROTO_CONTROL 2 */ 137 2, /* #define UTUN_OPT_IFNAME 2 */ 138 uintptr(unsafe.Pointer(&ifName)), 139 uintptr(unsafe.Pointer(&ifNameSize)), 0); errno != 0 { 140 err = errno 141 return nil, fmt.Errorf("error in syscall.Syscall6(syscall.SYS_GETSOCKOPT, ...): %v", err) 142 } 143 144 if err = setNonBlock(fd); err != nil { 145 return nil, fmt.Errorf("setting non-blocking error") 146 } 147 148 return &Interface{ 149 isTAP: false, 150 name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), 151 ReadWriteCloser: &tunReadCloser{ 152 f: os.NewFile(uintptr(fd), string(ifName.name[:])), 153 }, 154 }, nil 155 } 156 157 // openDevTunTapOSX opens tun / tap device, assuming tuntaposx is installed 158 func openDevTunTapOSX(config Config) (ifce *Interface, err error) { 159 var fd int 160 var socketFD int 161 162 if config.DeviceType == TAP && !strings.HasPrefix(config.Name, "tap") { 163 return nil, errors.New("device name does not start with tap when creating a tap device") 164 } 165 if config.DeviceType == TUN && !strings.HasPrefix(config.Name, "tun") { 166 return nil, errors.New("device name does not start with tun when creating a tun device") 167 } 168 if config.DeviceType != TAP && config.DeviceType != TUN { 169 return nil, errors.New("unsupported DeviceType") 170 } 171 if len(config.Name) >= 15 { 172 return nil, errors.New("device name is too long") 173 } 174 175 if fd, err = syscall.Open( 176 "/dev/"+config.Name, os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { 177 return nil, err 178 } 179 // Note that we are not setting NONBLOCK on the fd itself since it breaks tuntaposx 180 // see https://sourceforge.net/p/tuntaposx/bugs/6/ 181 182 // create socket so we can do SIO ioctls, we are not using it afterwards 183 if socketFD, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil { 184 return nil, fmt.Errorf("error in syscall.Socket: %v", err) 185 } 186 var ifReq = &struct { 187 ifName [16]byte 188 ifruFlags int16 189 pad [16]byte 190 }{} 191 copy(ifReq.ifName[:], []byte(config.Name)) 192 if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCGIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { 193 err = errno 194 return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 195 } 196 ifReq.ifruFlags |= syscall.IFF_RUNNING | syscall.IFF_UP 197 if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCSIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { 198 err = errno 199 return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 200 } 201 syscall.Close(socketFD) 202 203 return &Interface{ 204 isTAP: config.DeviceType == TAP, 205 ReadWriteCloser: os.NewFile(uintptr(fd), "tun"), 206 name: config.Name, 207 }, nil 208 } 209 210 // tunReadCloser is a hack to work around the first 4 bytes "packet 211 // information" because there doesn't seem to be an IFF_NO_PI for darwin. 212 type tunReadCloser struct { 213 f io.ReadWriteCloser 214 215 rMu sync.Mutex 216 rBuf []byte 217 218 wMu sync.Mutex 219 wBuf []byte 220 } 221 222 var _ io.ReadWriteCloser = (*tunReadCloser)(nil) 223 224 func (t *tunReadCloser) Read(to []byte) (int, error) { 225 t.rMu.Lock() 226 defer t.rMu.Unlock() 227 228 if cap(t.rBuf) < len(to)+4 { 229 t.rBuf = make([]byte, len(to)+4) 230 } 231 t.rBuf = t.rBuf[:len(to)+4] 232 233 n, err := t.f.Read(t.rBuf) 234 copy(to, t.rBuf[4:]) 235 return n - 4, err 236 } 237 238 func (t *tunReadCloser) Write(from []byte) (int, error) { 239 240 if len(from) == 0 { 241 return 0, syscall.EIO 242 } 243 244 t.wMu.Lock() 245 defer t.wMu.Unlock() 246 247 if cap(t.wBuf) < len(from)+4 { 248 t.wBuf = make([]byte, len(from)+4) 249 } 250 t.wBuf = t.wBuf[:len(from)+4] 251 252 // Determine the IP Family for the NULL L2 Header 253 ipVer := from[0] >> 4 254 if ipVer == 4 { 255 t.wBuf[3] = syscall.AF_INET 256 } else if ipVer == 6 { 257 t.wBuf[3] = syscall.AF_INET6 258 } else { 259 return 0, errors.New("Unable to determine IP version from packet") 260 } 261 262 copy(t.wBuf[4:], from) 263 264 n, err := t.f.Write(t.wBuf) 265 return n - 4, err 266 } 267 268 func (t *tunReadCloser) Close() error { 269 return t.f.Close() 270 }