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  }