github.com/tailscale/wireguard-go@v0.0.20201119-0.20210522003738-46b531feb08a/tun/tun_darwin.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  	"net"
    12  	"os"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  	"unsafe"
    17  
    18  	"golang.org/x/net/ipv6"
    19  	"golang.org/x/sys/unix"
    20  )
    21  
    22  const utunControlName = "com.apple.net.utun_control"
    23  
    24  type NativeTun struct {
    25  	name        string
    26  	tunFile     *os.File
    27  	events      chan Event
    28  	errors      chan error
    29  	routeSocket int
    30  	closeOnce   sync.Once
    31  }
    32  
    33  func retryInterfaceByIndex(index int) (iface *net.Interface, err error) {
    34  	for i := 0; i < 20; i++ {
    35  		iface, err = net.InterfaceByIndex(index)
    36  		if err != nil && errors.Is(err, syscall.ENOMEM) {
    37  			time.Sleep(time.Duration(i) * time.Second / 3)
    38  			continue
    39  		}
    40  		return iface, err
    41  	}
    42  	return nil, err
    43  }
    44  
    45  func (tun *NativeTun) routineRouteListener(tunIfindex int) {
    46  	var (
    47  		statusUp  bool
    48  		statusMTU int
    49  	)
    50  
    51  	defer close(tun.events)
    52  
    53  	data := make([]byte, os.Getpagesize())
    54  	for {
    55  	retry:
    56  		n, err := unix.Read(tun.routeSocket, data)
    57  		if err != nil {
    58  			if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
    59  				goto retry
    60  			}
    61  			tun.errors <- err
    62  			return
    63  		}
    64  
    65  		if n < 14 {
    66  			continue
    67  		}
    68  
    69  		if data[3 /* type */] != unix.RTM_IFINFO {
    70  			continue
    71  		}
    72  		ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
    73  		if ifindex != tunIfindex {
    74  			continue
    75  		}
    76  
    77  		iface, err := retryInterfaceByIndex(ifindex)
    78  		if err != nil {
    79  			tun.errors <- err
    80  			return
    81  		}
    82  
    83  		// Up / Down event
    84  		up := (iface.Flags & net.FlagUp) != 0
    85  		if up != statusUp && up {
    86  			tun.events <- EventUp
    87  		}
    88  		if up != statusUp && !up {
    89  			tun.events <- EventDown
    90  		}
    91  		statusUp = up
    92  
    93  		// MTU changes
    94  		if iface.MTU != statusMTU {
    95  			tun.events <- EventMTUUpdate
    96  		}
    97  		statusMTU = iface.MTU
    98  	}
    99  }
   100  
   101  func CreateTUN(name string, mtu int) (Device, error) {
   102  	ifIndex := -1
   103  	if name != "utun" {
   104  		_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
   105  		if err != nil || ifIndex < 0 {
   106  			return nil, fmt.Errorf("Interface name must be utun[0-9]*")
   107  		}
   108  	}
   109  
   110  	fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
   111  
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	ctlInfo := &unix.CtlInfo{}
   117  	copy(ctlInfo.Name[:], []byte(utunControlName))
   118  	err = unix.IoctlCtlInfo(fd, ctlInfo)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("IoctlGetCtlInfo: %w", err)
   121  	}
   122  
   123  	sc := &unix.SockaddrCtl{
   124  		ID:   ctlInfo.Id,
   125  		Unit: uint32(ifIndex) + 1,
   126  	}
   127  
   128  	err = unix.Connect(fd, sc)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	err = syscall.SetNonblock(fd, true)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
   138  
   139  	if err == nil && name == "utun" {
   140  		fname := os.Getenv("WG_TUN_NAME_FILE")
   141  		if fname != "" {
   142  			os.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
   143  		}
   144  	}
   145  
   146  	return tun, err
   147  }
   148  
   149  func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
   150  	tun := &NativeTun{
   151  		tunFile: file,
   152  		events:  make(chan Event, 10),
   153  		errors:  make(chan error, 5),
   154  	}
   155  
   156  	name, err := tun.Name()
   157  	if err != nil {
   158  		tun.tunFile.Close()
   159  		return nil, err
   160  	}
   161  
   162  	tunIfindex, err := func() (int, error) {
   163  		iface, err := net.InterfaceByName(name)
   164  		if err != nil {
   165  			return -1, err
   166  		}
   167  		return iface.Index, nil
   168  	}()
   169  	if err != nil {
   170  		tun.tunFile.Close()
   171  		return nil, err
   172  	}
   173  
   174  	tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
   175  	if err != nil {
   176  		tun.tunFile.Close()
   177  		return nil, err
   178  	}
   179  
   180  	go tun.routineRouteListener(tunIfindex)
   181  
   182  	if mtu > 0 {
   183  		err = tun.setMTU(mtu)
   184  		if err != nil {
   185  			tun.Close()
   186  			return nil, err
   187  		}
   188  	}
   189  
   190  	return tun, nil
   191  }
   192  
   193  func (tun *NativeTun) Name() (string, error) {
   194  	var err error
   195  	tun.operateOnFd(func(fd uintptr) {
   196  		tun.name, err = unix.GetsockoptString(
   197  			int(fd),
   198  			2, /* #define SYSPROTO_CONTROL 2 */
   199  			2, /* #define UTUN_OPT_IFNAME 2 */
   200  		)
   201  	})
   202  
   203  	if err != nil {
   204  		return "", fmt.Errorf("GetSockoptString: %w", err)
   205  	}
   206  
   207  	return tun.name, nil
   208  }
   209  
   210  func (tun *NativeTun) File() *os.File {
   211  	return tun.tunFile
   212  }
   213  
   214  func (tun *NativeTun) Events() chan Event {
   215  	return tun.events
   216  }
   217  
   218  func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
   219  	select {
   220  	case err := <-tun.errors:
   221  		return 0, err
   222  	default:
   223  		buff := buff[offset-4:]
   224  		n, err := tun.tunFile.Read(buff[:])
   225  		if n < 4 {
   226  			return 0, err
   227  		}
   228  		return n - 4, err
   229  	}
   230  }
   231  
   232  func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
   233  
   234  	// reserve space for header
   235  
   236  	buff = buff[offset-4:]
   237  
   238  	// add packet information header
   239  
   240  	buff[0] = 0x00
   241  	buff[1] = 0x00
   242  	buff[2] = 0x00
   243  
   244  	if buff[4]>>4 == ipv6.Version {
   245  		buff[3] = unix.AF_INET6
   246  	} else {
   247  		buff[3] = unix.AF_INET
   248  	}
   249  
   250  	// write
   251  
   252  	return tun.tunFile.Write(buff)
   253  }
   254  
   255  func (tun *NativeTun) Flush() error {
   256  	// TODO: can flushing be implemented by buffering and using sendmmsg?
   257  	return nil
   258  }
   259  
   260  func (tun *NativeTun) Close() error {
   261  	var err1, err2 error
   262  	tun.closeOnce.Do(func() {
   263  		err1 = tun.tunFile.Close()
   264  		if tun.routeSocket != -1 {
   265  			unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
   266  			err2 = unix.Close(tun.routeSocket)
   267  		} else if tun.events != nil {
   268  			close(tun.events)
   269  		}
   270  	})
   271  	if err1 != nil {
   272  		return err1
   273  	}
   274  	return err2
   275  }
   276  
   277  func (tun *NativeTun) setMTU(n int) error {
   278  	fd, err := unix.Socket(
   279  		unix.AF_INET,
   280  		unix.SOCK_DGRAM,
   281  		0,
   282  	)
   283  
   284  	if err != nil {
   285  		return err
   286  	}
   287  
   288  	defer unix.Close(fd)
   289  
   290  	var ifr unix.IfreqMTU
   291  	copy(ifr.Name[:], tun.name)
   292  	ifr.MTU = int32(n)
   293  	err = unix.IoctlSetIfreqMTU(fd, &ifr)
   294  	if err != nil {
   295  		return fmt.Errorf("failed to set MTU on %s: %w", tun.name, err)
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func (tun *NativeTun) MTU() (int, error) {
   302  	fd, err := unix.Socket(
   303  		unix.AF_INET,
   304  		unix.SOCK_DGRAM,
   305  		0,
   306  	)
   307  
   308  	if err != nil {
   309  		return 0, err
   310  	}
   311  
   312  	defer unix.Close(fd)
   313  
   314  	ifr, err := unix.IoctlGetIfreqMTU(fd, tun.name)
   315  	if err != nil {
   316  		return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, err)
   317  	}
   318  
   319  	return int(ifr.MTU), nil
   320  }