github.com/cawidtu/notwireguard-go/tun@v0.0.0-20230523131112-68e8e5ce9cdf/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/cawidtu/notwireguard-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 if err != nil { 236 return err 237 } 238 239 defer unix.Close(fd) 240 241 // do ioctl call 242 var ifr [ifReqSize]byte 243 copy(ifr[:], name) 244 *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) 245 _, _, errno := unix.Syscall( 246 unix.SYS_IOCTL, 247 uintptr(fd), 248 uintptr(unix.SIOCSIFMTU), 249 uintptr(unsafe.Pointer(&ifr[0])), 250 ) 251 252 if errno != 0 { 253 return fmt.Errorf("failed to set MTU of TUN device: %w", errno) 254 } 255 256 return nil 257 } 258 259 func (tun *NativeTun) MTU() (int, error) { 260 name, err := tun.Name() 261 if err != nil { 262 return 0, err 263 } 264 265 // open datagram socket 266 fd, err := unix.Socket( 267 unix.AF_INET, 268 unix.SOCK_DGRAM, 269 0, 270 ) 271 if err != nil { 272 return 0, err 273 } 274 275 defer unix.Close(fd) 276 277 // do ioctl call 278 279 var ifr [ifReqSize]byte 280 copy(ifr[:], name) 281 _, _, errno := unix.Syscall( 282 unix.SYS_IOCTL, 283 uintptr(fd), 284 uintptr(unix.SIOCGIFMTU), 285 uintptr(unsafe.Pointer(&ifr[0])), 286 ) 287 if errno != 0 { 288 return 0, fmt.Errorf("failed to get MTU of TUN device: %w", errno) 289 } 290 291 return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil 292 } 293 294 func (tun *NativeTun) Name() (string, error) { 295 tun.nameOnce.Do(tun.initNameCache) 296 return tun.nameCache, tun.nameErr 297 } 298 299 func (tun *NativeTun) initNameCache() { 300 tun.nameCache, tun.nameErr = tun.nameSlow() 301 } 302 303 func (tun *NativeTun) nameSlow() (string, error) { 304 sysconn, err := tun.tunFile.SyscallConn() 305 if err != nil { 306 return "", err 307 } 308 var ifr [ifReqSize]byte 309 var errno syscall.Errno 310 err = sysconn.Control(func(fd uintptr) { 311 _, _, errno = unix.Syscall( 312 unix.SYS_IOCTL, 313 fd, 314 uintptr(unix.TUNGETIFF), 315 uintptr(unsafe.Pointer(&ifr[0])), 316 ) 317 }) 318 if err != nil { 319 return "", fmt.Errorf("failed to get name of TUN device: %w", err) 320 } 321 if errno != 0 { 322 return "", fmt.Errorf("failed to get name of TUN device: %w", errno) 323 } 324 name := ifr[:] 325 if i := bytes.IndexByte(name, 0); i != -1 { 326 name = name[:i] 327 } 328 return string(name), nil 329 } 330 331 func (tun *NativeTun) Write(buf []byte, offset int) (int, error) { 332 if tun.nopi { 333 buf = buf[offset:] 334 } else { 335 // reserve space for header 336 buf = buf[offset-4:] 337 338 // add packet information header 339 buf[0] = 0x00 340 buf[1] = 0x00 341 if buf[4]>>4 == ipv6.Version { 342 buf[2] = 0x86 343 buf[3] = 0xdd 344 } else { 345 buf[2] = 0x08 346 buf[3] = 0x00 347 } 348 } 349 350 n, err := tun.tunFile.Write(buf) 351 if errors.Is(err, syscall.EBADFD) { 352 err = os.ErrClosed 353 } 354 return n, err 355 } 356 357 func (tun *NativeTun) Flush() error { 358 // TODO: can flushing be implemented by buffering and using sendmmsg? 359 return nil 360 } 361 362 func (tun *NativeTun) Read(buf []byte, offset int) (n int, err error) { 363 select { 364 case err = <-tun.errors: 365 default: 366 if tun.nopi { 367 n, err = tun.tunFile.Read(buf[offset:]) 368 } else { 369 buff := buf[offset-4:] 370 n, err = tun.tunFile.Read(buff[:]) 371 if errors.Is(err, syscall.EBADFD) { 372 err = os.ErrClosed 373 } 374 if n < 4 { 375 n = 0 376 } else { 377 n -= 4 378 } 379 } 380 } 381 return 382 } 383 384 func (tun *NativeTun) Events() chan Event { 385 return tun.events 386 } 387 388 func (tun *NativeTun) Close() error { 389 var err1, err2 error 390 tun.closeOnce.Do(func() { 391 if tun.statusListenersShutdown != nil { 392 close(tun.statusListenersShutdown) 393 if tun.netlinkCancel != nil { 394 err1 = tun.netlinkCancel.Cancel() 395 } 396 } else if tun.events != nil { 397 close(tun.events) 398 } 399 err2 = tun.tunFile.Close() 400 }) 401 if err1 != nil { 402 return err1 403 } 404 return err2 405 } 406 407 func CreateTUN(name string, mtu int) (Device, error) { 408 nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) 409 if err != nil { 410 if os.IsNotExist(err) { 411 return nil, fmt.Errorf("CreateTUN(%q) failed; %s does not exist", name, cloneDevicePath) 412 } 413 return nil, err 414 } 415 416 var ifr [ifReqSize]byte 417 var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack) 418 nameBytes := []byte(name) 419 if len(nameBytes) >= unix.IFNAMSIZ { 420 unix.Close(nfd) 421 return nil, fmt.Errorf("interface name too long: %w", unix.ENAMETOOLONG) 422 } 423 copy(ifr[:], nameBytes) 424 *(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags 425 426 _, _, errno := unix.Syscall( 427 unix.SYS_IOCTL, 428 uintptr(nfd), 429 uintptr(unix.TUNSETIFF), 430 uintptr(unsafe.Pointer(&ifr[0])), 431 ) 432 if errno != 0 { 433 unix.Close(nfd) 434 return nil, errno 435 } 436 437 err = unix.SetNonblock(nfd, true) 438 if err != nil { 439 unix.Close(nfd) 440 return nil, err 441 } 442 443 // Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line. 444 445 fd := os.NewFile(uintptr(nfd), cloneDevicePath) 446 return CreateTUNFromFile(fd, mtu) 447 } 448 449 func CreateTUNFromFile(file *os.File, mtu int) (Device, error) { 450 tun := &NativeTun{ 451 tunFile: file, 452 events: make(chan Event, 5), 453 errors: make(chan error, 5), 454 statusListenersShutdown: make(chan struct{}), 455 nopi: false, 456 } 457 458 name, err := tun.Name() 459 if err != nil { 460 return nil, err 461 } 462 463 // start event listener 464 465 tun.index, err = getIFIndex(name) 466 if err != nil { 467 return nil, err 468 } 469 470 tun.netlinkSock, err = createNetlinkSocket() 471 if err != nil { 472 return nil, err 473 } 474 tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) 475 if err != nil { 476 unix.Close(tun.netlinkSock) 477 return nil, err 478 } 479 480 tun.hackListenerClosed.Lock() 481 go tun.routineNetlinkListener() 482 go tun.routineHackListener() // cross namespace 483 484 err = tun.setMTU(mtu) 485 if err != nil { 486 unix.Close(tun.netlinkSock) 487 return nil, err 488 } 489 490 return tun, nil 491 } 492 493 func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) { 494 err := unix.SetNonblock(fd, true) 495 if err != nil { 496 return nil, "", err 497 } 498 file := os.NewFile(uintptr(fd), "/dev/tun") 499 tun := &NativeTun{ 500 tunFile: file, 501 events: make(chan Event, 5), 502 errors: make(chan error, 5), 503 nopi: true, 504 } 505 name, err := tun.Name() 506 if err != nil { 507 return nil, "", err 508 } 509 return tun, name, nil 510 }