tun_freebsd.go raw

   1  /* SPDX-License-Identifier: MIT
   2   *
   3   * Copyright (C) 2017-2025 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|unix.SOCK_CLOEXEC, 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) (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|unix.O_CLOEXEC, 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|unix.SOCK_CLOEXEC, 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|unix.SOCK_CLOEXEC, 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)
 262  }
 263  
 264  func CreateTUNFromFile(file *os.File, mtu int) (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.SOCK_CLOEXEC, 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(bufs [][]byte, sizes []int, offset int) (int, error) {
 337  	select {
 338  	case err := <-tun.errors:
 339  		return 0, err
 340  	default:
 341  		buf := bufs[0][offset-4:]
 342  		n, err := tun.tunFile.Read(buf[:])
 343  		if n < 4 {
 344  			return 0, err
 345  		}
 346  		sizes[0] = n - 4
 347  		return 1, err
 348  	}
 349  }
 350  
 351  func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) {
 352  	if offset < 4 {
 353  		return 0, io.ErrShortBuffer
 354  	}
 355  	for i, buf := range bufs {
 356  		buf = buf[offset-4:]
 357  		if len(buf) < 5 {
 358  			return i, io.ErrShortBuffer
 359  		}
 360  		buf[0] = 0x00
 361  		buf[1] = 0x00
 362  		buf[2] = 0x00
 363  		switch buf[4] >> 4 {
 364  		case 4:
 365  			buf[3] = unix.AF_INET
 366  		case 6:
 367  			buf[3] = unix.AF_INET6
 368  		default:
 369  			return i, unix.EAFNOSUPPORT
 370  		}
 371  		if _, err := tun.tunFile.Write(buf); err != nil {
 372  			return i, err
 373  		}
 374  	}
 375  	return len(bufs), nil
 376  }
 377  
 378  func (tun *NativeTun) Close() error {
 379  	var err1, err2, err3 error
 380  	tun.closeOnce.Do(func() {
 381  		err1 = tun.tunFile.Close()
 382  		err2 = tunDestroy(tun.name)
 383  		if tun.routeSocket != -1 {
 384  			unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
 385  			err3 = unix.Close(tun.routeSocket)
 386  			tun.routeSocket = -1
 387  		} else if tun.events != nil {
 388  			close(tun.events)
 389  		}
 390  	})
 391  	if err1 != nil {
 392  		return err1
 393  	}
 394  	if err2 != nil {
 395  		return err2
 396  	}
 397  	return err3
 398  }
 399  
 400  func (tun *NativeTun) setMTU(n int) error {
 401  	fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
 402  	if err != nil {
 403  		return err
 404  	}
 405  	defer unix.Close(fd)
 406  
 407  	var ifr ifreqMtu
 408  	copy(ifr.Name[:], tun.name)
 409  	ifr.MTU = uint32(n)
 410  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr)))
 411  	if errno != 0 {
 412  		return fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno)
 413  	}
 414  	return nil
 415  }
 416  
 417  func (tun *NativeTun) MTU() (int, error) {
 418  	fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, 0)
 419  	if err != nil {
 420  		return 0, err
 421  	}
 422  	defer unix.Close(fd)
 423  
 424  	var ifr ifreqMtu
 425  	copy(ifr.Name[:], tun.name)
 426  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr)))
 427  	if errno != 0 {
 428  		return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno)
 429  	}
 430  	return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
 431  }
 432  
 433  func (tun *NativeTun) BatchSize() int {
 434  	return 1
 435  }
 436