github.com/bugfan/wireguard-go@v0.0.0-20230720020150-a7b2fa340c66/tun/tun_linux.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 /* Implementation of the TUN device interface for linux 9 */ 10 11 import ( 12 "bytes" 13 "errors" 14 "fmt" 15 "os" 16 "sync" 17 "syscall" 18 "time" 19 "unsafe" 20 21 "golang.org/x/net/ipv6" 22 "golang.org/x/sys/unix" 23 24 "github.com/bugfan/wireguard-go/rwcancel" 25 ) 26 27 const ( 28 cloneDevicePath = "/dev/net/tun" 29 ifReqSize = unix.IFNAMSIZ + 64 30 ) 31 32 type NativeTun struct { 33 tunFile *os.File 34 index int32 // if index 35 errors chan error // async error handling 36 events chan Event // device related events 37 nopi bool // the device was passed IFF_NO_PI 38 netlinkSock int 39 netlinkCancel *rwcancel.RWCancel 40 hackListenerClosed sync.Mutex 41 statusListenersShutdown chan struct{} 42 43 closeOnce sync.Once 44 45 nameOnce sync.Once // guards calling initNameCache, which sets following fields 46 nameCache string // name of interface 47 nameErr error 48 } 49 50 func (tun *NativeTun) File() *os.File { 51 return tun.tunFile 52 } 53 54 func (tun *NativeTun) routineHackListener() { 55 defer tun.hackListenerClosed.Unlock() 56 /* This is needed for the detection to work across network namespaces 57 * If you are reading this and know a better method, please get in touch. 58 */ 59 last := 0 60 const ( 61 up = 1 62 down = 2 63 ) 64 for { 65 sysconn, err := tun.tunFile.SyscallConn() 66 if err != nil { 67 return 68 } 69 err2 := sysconn.Control(func(fd uintptr) { 70 _, err = unix.Write(int(fd), nil) 71 }) 72 if err2 != nil { 73 return 74 } 75 switch err { 76 case unix.EINVAL: 77 if last != up { 78 // If the tunnel is up, it reports that write() is 79 // allowed but we provided invalid data. 80 tun.events <- EventUp 81 last = up 82 } 83 case unix.EIO: 84 if last != down { 85 // If the tunnel is down, it reports that no I/O 86 // is possible, without checking our provided data. 87 tun.events <- EventDown 88 last = down 89 } 90 default: 91 return 92 } 93 select { 94 case <-time.After(time.Second): 95 // nothing 96 case <-tun.statusListenersShutdown: 97 return 98 } 99 } 100 } 101 102 func createNetlinkSocket() (int, error) { 103 sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE) 104 if err != nil { 105 return -1, err 106 } 107 saddr := &unix.SockaddrNetlink{ 108 Family: unix.AF_NETLINK, 109 Groups: unix.RTMGRP_LINK | unix.RTMGRP_IPV4_IFADDR | unix.RTMGRP_IPV6_IFADDR, 110 } 111 err = unix.Bind(sock, saddr) 112 if err != nil { 113 return -1, err 114 } 115 return sock, nil 116 } 117 118 func (tun *NativeTun) routineNetlinkListener() { 119 defer func() { 120 unix.Close(tun.netlinkSock) 121 tun.hackListenerClosed.Lock() 122 close(tun.events) 123 tun.netlinkCancel.Close() 124 }() 125 126 for msg := make([]byte, 1<<16); ; { 127 var err error 128 var msgn int 129 for { 130 msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0) 131 if err == nil || !rwcancel.RetryAfterError(err) { 132 break 133 } 134 if !tun.netlinkCancel.ReadyRead() { 135 tun.errors <- fmt.Errorf("netlink socket closed: %w", err) 136 return 137 } 138 } 139 if err != nil { 140 tun.errors <- fmt.Errorf("failed to receive netlink message: %w", err) 141 return 142 } 143 144 select { 145 case <-tun.statusListenersShutdown: 146 return 147 default: 148 } 149 150 wasEverUp := false 151 for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { 152 153 hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) 154 155 if int(hdr.Len) > len(remain) { 156 break 157 } 158 159 switch hdr.Type { 160 case unix.NLMSG_DONE: 161 remain = []byte{} 162 163 case unix.RTM_NEWLINK: 164 info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) 165 remain = remain[hdr.Len:] 166 167 if info.Index != tun.index { 168 // not our interface 169 continue 170 } 171 172 if info.Flags&unix.IFF_RUNNING != 0 { 173 tun.events <- EventUp 174 wasEverUp = true 175 } 176 177 if info.Flags&unix.IFF_RUNNING == 0 { 178 // Don't emit EventDown before we've ever emitted EventUp. 179 // This avoids a startup race with HackListener, which 180 // might detect Up before we have finished reporting Down. 181 if wasEverUp { 182 tun.events <- EventDown 183 } 184 } 185 186 tun.events <- EventMTUUpdate 187 188 default: 189 remain = remain[hdr.Len:] 190 } 191 } 192 } 193 } 194 195 func getIFIndex(name string) (int32, error) { 196 fd, err := unix.Socket( 197 unix.AF_INET, 198 unix.SOCK_DGRAM, 199 0, 200 ) 201 if err != nil { 202 return 0, err 203 } 204 205 defer unix.Close(fd) 206 207 var ifr [ifReqSize]byte 208 copy(ifr[:], name) 209 _, _, errno := unix.Syscall( 210 unix.SYS_IOCTL, 211 uintptr(fd), 212 uintptr(unix.SIOCGIFINDEX), 213 uintptr(unsafe.Pointer(&ifr[0])), 214 ) 215 216 if errno != 0 { 217 return 0, errno 218 } 219 220 return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil 221 } 222 223 func (tun *NativeTun) setMTU(n int) error { 224 name, err := tun.Name() 225 if err != nil { 226 return err 227 } 228 229 // open datagram socket 230 fd, err := unix.Socket( 231 unix.AF_INET, 232 unix.SOCK_DGRAM, 233 0, 234 ) 235 236 if err != nil { 237 return err 238 } 239 240 defer unix.Close(fd) 241 242 // do ioctl call 243 var ifr [ifReqSize]byte 244 copy(ifr[:], name) 245 *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) 246 _, _, errno := unix.Syscall( 247 unix.SYS_IOCTL, 248 uintptr(fd), 249 uintptr(unix.SIOCSIFMTU), 250 uintptr(unsafe.Pointer(&ifr[0])), 251 ) 252 253 if errno != 0 { 254 return fmt.Errorf("failed to set MTU of TUN device: %w", errno) 255 } 256 257 return nil 258 } 259 260 func (tun *NativeTun) MTU() (int, error) { 261 name, err := tun.Name() 262 if err != nil { 263 return 0, err 264 } 265 266 // open datagram socket 267 fd, err := unix.Socket( 268 unix.AF_INET, 269 unix.SOCK_DGRAM, 270 0, 271 ) 272 273 if err != nil { 274 return 0, err 275 } 276 277 defer unix.Close(fd) 278 279 // do ioctl call 280 281 var ifr [ifReqSize]byte 282 copy(ifr[:], name) 283 _, _, errno := unix.Syscall( 284 unix.SYS_IOCTL, 285 uintptr(fd), 286 uintptr(unix.SIOCGIFMTU), 287 uintptr(unsafe.Pointer(&ifr[0])), 288 ) 289 if errno != 0 { 290 return 0, fmt.Errorf("failed to get MTU of TUN device: %w", errno) 291 } 292 293 return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil 294 } 295 296 func (tun *NativeTun) Name() (string, error) { 297 tun.nameOnce.Do(tun.initNameCache) 298 return tun.nameCache, tun.nameErr 299 } 300 301 func (tun *NativeTun) initNameCache() { 302 tun.nameCache, tun.nameErr = tun.nameSlow() 303 } 304 305 func (tun *NativeTun) nameSlow() (string, error) { 306 sysconn, err := tun.tunFile.SyscallConn() 307 if err != nil { 308 return "", err 309 } 310 var ifr [ifReqSize]byte 311 var errno syscall.Errno 312 err = sysconn.Control(func(fd uintptr) { 313 _, _, errno = unix.Syscall( 314 unix.SYS_IOCTL, 315 fd, 316 uintptr(unix.TUNGETIFF), 317 uintptr(unsafe.Pointer(&ifr[0])), 318 ) 319 }) 320 if err != nil { 321 return "", fmt.Errorf("failed to get name of TUN device: %w", err) 322 } 323 if errno != 0 { 324 return "", fmt.Errorf("failed to get name of TUN device: %w", errno) 325 } 326 name := ifr[:] 327 if i := bytes.IndexByte(name, 0); i != -1 { 328 name = name[:i] 329 } 330 return string(name), nil 331 } 332 333 func (tun *NativeTun) Write(buf []byte, offset int) (int, error) { 334 if tun.nopi { 335 buf = buf[offset:] 336 } else { 337 // reserve space for header 338 buf = buf[offset-4:] 339 340 // add packet information header 341 buf[0] = 0x00 342 buf[1] = 0x00 343 if buf[4]>>4 == ipv6.Version { 344 buf[2] = 0x86 345 buf[3] = 0xdd 346 } else { 347 buf[2] = 0x08 348 buf[3] = 0x00 349 } 350 } 351 352 n, err := tun.tunFile.Write(buf) 353 if errors.Is(err, syscall.EBADFD) { 354 err = os.ErrClosed 355 } 356 return n, err 357 } 358 359 func (tun *NativeTun) Flush() error { 360 // TODO: can flushing be implemented by buffering and using sendmmsg? 361 return nil 362 } 363 364 func (tun *NativeTun) Read(buf []byte, offset int) (n int, err error) { 365 select { 366 case err = <-tun.errors: 367 default: 368 if tun.nopi { 369 n, err = tun.tunFile.Read(buf[offset:]) 370 } else { 371 buff := buf[offset-4:] 372 n, err = tun.tunFile.Read(buff[:]) 373 if errors.Is(err, syscall.EBADFD) { 374 err = os.ErrClosed 375 } 376 if n < 4 { 377 n = 0 378 } else { 379 n -= 4 380 } 381 } 382 } 383 return 384 } 385 386 func (tun *NativeTun) Events() chan Event { 387 return tun.events 388 } 389 390 func (tun *NativeTun) Close() error { 391 var err1, err2 error 392 tun.closeOnce.Do(func() { 393 if tun.statusListenersShutdown != nil { 394 close(tun.statusListenersShutdown) 395 if tun.netlinkCancel != nil { 396 err1 = tun.netlinkCancel.Cancel() 397 } 398 } else if tun.events != nil { 399 close(tun.events) 400 } 401 err2 = tun.tunFile.Close() 402 }) 403 if err1 != nil { 404 return err1 405 } 406 return err2 407 } 408 409 func CreateTUN(name string, mtu int) (Device, error) { 410 nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) 411 if err != nil { 412 if os.IsNotExist(err) { 413 return nil, fmt.Errorf("CreateTUN(%q) failed; %s does not exist", name, cloneDevicePath) 414 } 415 return nil, err 416 } 417 418 var ifr [ifReqSize]byte 419 var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack) 420 nameBytes := []byte(name) 421 if len(nameBytes) >= unix.IFNAMSIZ { 422 unix.Close(nfd) 423 return nil, fmt.Errorf("interface name too long: %w", unix.ENAMETOOLONG) 424 } 425 copy(ifr[:], nameBytes) 426 *(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags 427 428 _, _, errno := unix.Syscall( 429 unix.SYS_IOCTL, 430 uintptr(nfd), 431 uintptr(unix.TUNSETIFF), 432 uintptr(unsafe.Pointer(&ifr[0])), 433 ) 434 if errno != 0 { 435 unix.Close(nfd) 436 return nil, errno 437 } 438 439 err = unix.SetNonblock(nfd, true) 440 if err != nil { 441 unix.Close(nfd) 442 return nil, err 443 } 444 445 // Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line. 446 447 fd := os.NewFile(uintptr(nfd), cloneDevicePath) 448 return CreateTUNFromFile(fd, mtu) 449 } 450 451 func CreateTUNFromFile(file *os.File, mtu int) (Device, error) { 452 tun := &NativeTun{ 453 tunFile: file, 454 events: make(chan Event, 5), 455 errors: make(chan error, 5), 456 statusListenersShutdown: make(chan struct{}), 457 nopi: false, 458 } 459 460 name, err := tun.Name() 461 if err != nil { 462 return nil, err 463 } 464 465 // start event listener 466 467 tun.index, err = getIFIndex(name) 468 if err != nil { 469 return nil, err 470 } 471 472 tun.netlinkSock, err = createNetlinkSocket() 473 if err != nil { 474 return nil, err 475 } 476 tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) 477 if err != nil { 478 unix.Close(tun.netlinkSock) 479 return nil, err 480 } 481 482 tun.hackListenerClosed.Lock() 483 go tun.routineNetlinkListener() 484 go tun.routineHackListener() // cross namespace 485 486 err = tun.setMTU(mtu) 487 if err != nil { 488 unix.Close(tun.netlinkSock) 489 return nil, err 490 } 491 492 return tun, nil 493 } 494 495 func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) { 496 err := unix.SetNonblock(fd, true) 497 if err != nil { 498 return nil, "", err 499 } 500 file := os.NewFile(uintptr(fd), "/dev/tun") 501 tun := &NativeTun{ 502 tunFile: file, 503 events: make(chan Event, 5), 504 errors: make(chan error, 5), 505 nopi: true, 506 } 507 name, err := tun.Name() 508 if err != nil { 509 return nil, "", err 510 } 511 return tun, name, nil 512 }