github.com/liloew/wireguard-go@v0.0.0-20220224014633-9cd745e6f114/tun/tun_freebsd.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package tun 7 8 import ( 9 "errors" 10 "fmt" 11 "io" 12 "net" 13 "os" 14 "sync" 15 "syscall" 16 "unsafe" 17 18 "golang.org/x/sys/unix" 19 ) 20 21 const ( 22 _TUNSIFHEAD = 0x80047460 23 _TUNSIFMODE = 0x8004745e 24 _TUNGIFNAME = 0x4020745d 25 _TUNSIFPID = 0x2000745f 26 27 _SIOCGIFINFO_IN6 = 0xc048696c 28 _SIOCSIFINFO_IN6 = 0xc048696d 29 _ND6_IFF_AUTO_LINKLOCAL = 0x20 30 _ND6_IFF_NO_DAD = 0x100 31 ) 32 33 // Iface requests with just the name 34 type ifreqName struct { 35 Name [unix.IFNAMSIZ]byte 36 _ [16]byte 37 } 38 39 // Iface requests with a pointer 40 type ifreqPtr struct { 41 Name [unix.IFNAMSIZ]byte 42 Data uintptr 43 _ [16 - unsafe.Sizeof(uintptr(0))]byte 44 } 45 46 // Iface requests with MTU 47 type ifreqMtu struct { 48 Name [unix.IFNAMSIZ]byte 49 MTU uint32 50 _ [12]byte 51 } 52 53 // ND6 flag manipulation 54 type nd6Req struct { 55 Name [unix.IFNAMSIZ]byte 56 Linkmtu uint32 57 Maxmtu uint32 58 Basereachable uint32 59 Reachable uint32 60 Retrans uint32 61 Flags uint32 62 Recalctm int 63 Chlim uint8 64 Initialized uint8 65 Randomseed0 [8]byte 66 Randomseed1 [8]byte 67 Randomid [8]byte 68 } 69 70 type NativeTun struct { 71 name string 72 tunFile *os.File 73 events chan Event 74 errors chan error 75 routeSocket int 76 closeOnce sync.Once 77 } 78 79 func (tun *NativeTun) routineRouteListener(tunIfindex int) { 80 var ( 81 statusUp bool 82 statusMTU int 83 ) 84 85 defer close(tun.events) 86 87 data := make([]byte, os.Getpagesize()) 88 for { 89 retry: 90 n, err := unix.Read(tun.routeSocket, data) 91 if err != nil { 92 if errors.Is(err, syscall.EINTR) { 93 goto retry 94 } 95 tun.errors <- err 96 return 97 } 98 99 if n < 14 { 100 continue 101 } 102 103 if data[3 /* type */] != unix.RTM_IFINFO { 104 continue 105 } 106 ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) 107 if ifindex != tunIfindex { 108 continue 109 } 110 111 iface, err := net.InterfaceByIndex(ifindex) 112 if err != nil { 113 tun.errors <- err 114 return 115 } 116 117 // Up / Down event 118 up := (iface.Flags & net.FlagUp) != 0 119 if up != statusUp && up { 120 tun.events <- EventUp 121 } 122 if up != statusUp && !up { 123 tun.events <- EventDown 124 } 125 statusUp = up 126 127 // MTU changes 128 if iface.MTU != statusMTU { 129 tun.events <- EventMTUUpdate 130 } 131 statusMTU = iface.MTU 132 } 133 } 134 135 func tunName(fd uintptr) (string, error) { 136 var ifreq ifreqName 137 _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, _TUNGIFNAME, uintptr(unsafe.Pointer(&ifreq))) 138 if err != 0 { 139 return "", err 140 } 141 return unix.ByteSliceToString(ifreq.Name[:]), nil 142 } 143 144 // Destroy a named system interface 145 func tunDestroy(name string) error { 146 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) 147 if err != nil { 148 return err 149 } 150 defer unix.Close(fd) 151 152 var ifr [32]byte 153 copy(ifr[:], name) 154 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCIFDESTROY), uintptr(unsafe.Pointer(&ifr[0]))) 155 if errno != 0 { 156 return fmt.Errorf("failed to destroy interface %s: %w", name, errno) 157 } 158 159 return nil 160 } 161 162 func CreateTUN(name string, mtu int, nopi bool) (Device, error) { 163 if len(name) > unix.IFNAMSIZ-1 { 164 return nil, errors.New("interface name too long") 165 } 166 167 // See if interface already exists 168 iface, _ := net.InterfaceByName(name) 169 if iface != nil { 170 return nil, fmt.Errorf("interface %s already exists", name) 171 } 172 173 tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0) 174 if err != nil { 175 return nil, err 176 } 177 178 tun := NativeTun{tunFile: tunFile} 179 var assignedName string 180 tun.operateOnFd(func(fd uintptr) { 181 assignedName, err = tunName(fd) 182 }) 183 if err != nil { 184 tunFile.Close() 185 return nil, err 186 } 187 188 // Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet 189 ifheadmode := 1 190 var errno syscall.Errno 191 tun.operateOnFd(func(fd uintptr) { 192 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFHEAD, uintptr(unsafe.Pointer(&ifheadmode))) 193 }) 194 195 if errno != 0 { 196 tunFile.Close() 197 tunDestroy(assignedName) 198 return nil, fmt.Errorf("unable to put into IFHEAD mode: %w", errno) 199 } 200 201 // Get out of PTP mode. 202 ifflags := syscall.IFF_BROADCAST | syscall.IFF_MULTICAST 203 tun.operateOnFd(func(fd uintptr) { 204 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(_TUNSIFMODE), uintptr(unsafe.Pointer(&ifflags))) 205 }) 206 207 if errno != 0 { 208 tunFile.Close() 209 tunDestroy(assignedName) 210 return nil, fmt.Errorf("unable to put into IFF_BROADCAST mode: %w", errno) 211 } 212 213 // Disable link-local v6, not just because WireGuard doesn't do that anyway, but 214 // also because there are serious races with attaching and detaching LLv6 addresses 215 // in relation to interface lifetime within the FreeBSD kernel. 216 confd6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0) 217 if err != nil { 218 tunFile.Close() 219 tunDestroy(assignedName) 220 return nil, err 221 } 222 defer unix.Close(confd6) 223 var ndireq nd6Req 224 copy(ndireq.Name[:], assignedName) 225 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCGIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq))) 226 if errno != 0 { 227 tunFile.Close() 228 tunDestroy(assignedName) 229 return nil, fmt.Errorf("unable to get nd6 flags for %s: %w", assignedName, errno) 230 } 231 ndireq.Flags = ndireq.Flags &^ _ND6_IFF_AUTO_LINKLOCAL 232 ndireq.Flags = ndireq.Flags | _ND6_IFF_NO_DAD 233 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCSIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq))) 234 if errno != 0 { 235 tunFile.Close() 236 tunDestroy(assignedName) 237 return nil, fmt.Errorf("unable to set nd6 flags for %s: %w", assignedName, errno) 238 } 239 240 if name != "" { 241 confd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) 242 if err != nil { 243 tunFile.Close() 244 tunDestroy(assignedName) 245 return nil, err 246 } 247 defer unix.Close(confd) 248 var newnp [unix.IFNAMSIZ]byte 249 copy(newnp[:], name) 250 var ifr ifreqPtr 251 copy(ifr.Name[:], assignedName) 252 ifr.Data = uintptr(unsafe.Pointer(&newnp[0])) 253 _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr))) 254 if errno != 0 { 255 tunFile.Close() 256 tunDestroy(assignedName) 257 return nil, fmt.Errorf("Failed to rename %s to %s: %w", assignedName, name, errno) 258 } 259 } 260 261 return CreateTUNFromFile(tunFile, mtu, nopi) 262 } 263 264 func CreateTUNFromFile(file *os.File, mtu int, nopi bool) (Device, error) { 265 tun := &NativeTun{ 266 tunFile: file, 267 events: make(chan Event, 10), 268 errors: make(chan error, 1), 269 } 270 271 var errno syscall.Errno 272 tun.operateOnFd(func(fd uintptr) { 273 _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFPID, uintptr(0)) 274 }) 275 if errno != 0 { 276 tun.tunFile.Close() 277 return nil, fmt.Errorf("unable to become controlling TUN process: %w", errno) 278 } 279 280 name, err := tun.Name() 281 if err != nil { 282 tun.tunFile.Close() 283 return nil, err 284 } 285 286 tunIfindex, err := func() (int, error) { 287 iface, err := net.InterfaceByName(name) 288 if err != nil { 289 return -1, err 290 } 291 return iface.Index, nil 292 }() 293 if err != nil { 294 tun.tunFile.Close() 295 return nil, err 296 } 297 298 tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) 299 if err != nil { 300 tun.tunFile.Close() 301 return nil, err 302 } 303 304 go tun.routineRouteListener(tunIfindex) 305 306 err = tun.setMTU(mtu) 307 if err != nil { 308 tun.Close() 309 return nil, err 310 } 311 312 return tun, nil 313 } 314 315 func (tun *NativeTun) Name() (string, error) { 316 var name string 317 var err error 318 tun.operateOnFd(func(fd uintptr) { 319 name, err = tunName(fd) 320 }) 321 if err != nil { 322 return "", err 323 } 324 tun.name = name 325 return name, nil 326 } 327 328 func (tun *NativeTun) File() *os.File { 329 return tun.tunFile 330 } 331 332 func (tun *NativeTun) Events() chan Event { 333 return tun.events 334 } 335 336 func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 337 select { 338 case err := <-tun.errors: 339 return 0, err 340 default: 341 buff := buff[offset-4:] 342 n, err := tun.tunFile.Read(buff[:]) 343 if n < 4 { 344 return 0, err 345 } 346 return n - 4, err 347 } 348 } 349 350 func (tun *NativeTun) Write(buf []byte, offset int) (int, error) { 351 if offset < 4 { 352 return 0, io.ErrShortBuffer 353 } 354 buf = buf[offset-4:] 355 if len(buf) < 5 { 356 return 0, io.ErrShortBuffer 357 } 358 buf[0] = 0x00 359 buf[1] = 0x00 360 buf[2] = 0x00 361 switch buf[4] >> 4 { 362 case 4: 363 buf[3] = unix.AF_INET 364 case 6: 365 buf[3] = unix.AF_INET6 366 default: 367 return 0, unix.EAFNOSUPPORT 368 } 369 return tun.tunFile.Write(buf) 370 } 371 372 func (tun *NativeTun) Flush() error { 373 // TODO: can flushing be implemented by buffering and using sendmmsg? 374 return nil 375 } 376 377 func (tun *NativeTun) Close() error { 378 var err1, err2, err3 error 379 tun.closeOnce.Do(func() { 380 err1 = tun.tunFile.Close() 381 err2 = tunDestroy(tun.name) 382 if tun.routeSocket != -1 { 383 unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) 384 err3 = unix.Close(tun.routeSocket) 385 tun.routeSocket = -1 386 } else if tun.events != nil { 387 close(tun.events) 388 } 389 }) 390 if err1 != nil { 391 return err1 392 } 393 if err2 != nil { 394 return err2 395 } 396 return err3 397 } 398 399 func (tun *NativeTun) setMTU(n int) error { 400 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) 401 if err != nil { 402 return err 403 } 404 defer unix.Close(fd) 405 406 var ifr ifreqMtu 407 copy(ifr.Name[:], tun.name) 408 ifr.MTU = uint32(n) 409 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr))) 410 if errno != 0 { 411 return fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno) 412 } 413 return nil 414 } 415 416 func (tun *NativeTun) MTU() (int, error) { 417 fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0) 418 if err != nil { 419 return 0, err 420 } 421 defer unix.Close(fd) 422 423 var ifr ifreqMtu 424 copy(ifr.Name[:], tun.name) 425 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr))) 426 if errno != 0 { 427 return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno) 428 } 429 return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil 430 }