github.com/liloew/wireguard-go@v0.0.0-20220224014633-9cd745e6f114/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, nopi bool) (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  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	ctlInfo := &unix.CtlInfo{}
   116  	copy(ctlInfo.Name[:], []byte(utunControlName))
   117  	err = unix.IoctlCtlInfo(fd, ctlInfo)
   118  	if err != nil {
   119  		unix.Close(fd)
   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  		unix.Close(fd)
   131  		return nil, err
   132  	}
   133  
   134  	err = unix.SetNonblock(fd, true)
   135  	if err != nil {
   136  		unix.Close(fd)
   137  		return nil, err
   138  	}
   139  	tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, nopi)
   140  
   141  	if err == nil && name == "utun" {
   142  		fname := os.Getenv("WG_TUN_NAME_FILE")
   143  		if fname != "" {
   144  			os.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0o400)
   145  		}
   146  	}
   147  
   148  	return tun, err
   149  }
   150  
   151  func CreateTUNFromFile(file *os.File, mtu int, nopi bool) (Device, error) {
   152  	tun := &NativeTun{
   153  		tunFile: file,
   154  		events:  make(chan Event, 10),
   155  		errors:  make(chan error, 5),
   156  	}
   157  
   158  	name, err := tun.Name()
   159  	if err != nil {
   160  		tun.tunFile.Close()
   161  		return nil, err
   162  	}
   163  
   164  	tunIfindex, err := func() (int, error) {
   165  		iface, err := net.InterfaceByName(name)
   166  		if err != nil {
   167  			return -1, err
   168  		}
   169  		return iface.Index, nil
   170  	}()
   171  	if err != nil {
   172  		tun.tunFile.Close()
   173  		return nil, err
   174  	}
   175  
   176  	tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
   177  	if err != nil {
   178  		tun.tunFile.Close()
   179  		return nil, err
   180  	}
   181  
   182  	go tun.routineRouteListener(tunIfindex)
   183  
   184  	if mtu > 0 {
   185  		err = tun.setMTU(mtu)
   186  		if err != nil {
   187  			tun.Close()
   188  			return nil, err
   189  		}
   190  	}
   191  
   192  	return tun, nil
   193  }
   194  
   195  func (tun *NativeTun) Name() (string, error) {
   196  	var err error
   197  	tun.operateOnFd(func(fd uintptr) {
   198  		tun.name, err = unix.GetsockoptString(
   199  			int(fd),
   200  			2, /* #define SYSPROTO_CONTROL 2 */
   201  			2, /* #define UTUN_OPT_IFNAME 2 */
   202  		)
   203  	})
   204  
   205  	if err != nil {
   206  		return "", fmt.Errorf("GetSockoptString: %w", err)
   207  	}
   208  
   209  	return tun.name, nil
   210  }
   211  
   212  func (tun *NativeTun) File() *os.File {
   213  	return tun.tunFile
   214  }
   215  
   216  func (tun *NativeTun) Events() chan Event {
   217  	return tun.events
   218  }
   219  
   220  func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
   221  	select {
   222  	case err := <-tun.errors:
   223  		return 0, err
   224  	default:
   225  		mu := sync.Mutex{}
   226  		mu.Lock()
   227  		defer mu.Unlock()
   228  		bf := make([]byte, len(buff))
   229  		n, err := tun.tunFile.Read(bf[:])
   230  		if n < offset {
   231  			return 0, err
   232  		}
   233  		copy(buff, bf[offset:])
   234  		return n - offset, err
   235  	}
   236  }
   237  
   238  func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
   239  	// reserve space for header
   240  
   241  	mu := sync.Mutex{}
   242  	mu.Lock()
   243  	defer mu.Unlock()
   244  	bf := make([]byte, len(buff)+4)
   245  
   246  	// add packet information header
   247  	bf[0] = 0x00
   248  	bf[1] = 0x00
   249  	bf[2] = 0x00
   250  	if buff[4]>>4 == ipv6.Version {
   251  		bf[3] = unix.AF_INET6
   252  	} else {
   253  		bf[3] = unix.AF_INET
   254  	}
   255  
   256  	copy(bf[4:], buff)
   257  	// write
   258  	return tun.tunFile.Write(bf)
   259  }
   260  
   261  func (tun *NativeTun) Flush() error {
   262  	// TODO: can flushing be implemented by buffering and using sendmmsg?
   263  	return nil
   264  }
   265  
   266  func (tun *NativeTun) Close() error {
   267  	var err1, err2 error
   268  	tun.closeOnce.Do(func() {
   269  		err1 = tun.tunFile.Close()
   270  		if tun.routeSocket != -1 {
   271  			unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
   272  			err2 = unix.Close(tun.routeSocket)
   273  		} else if tun.events != nil {
   274  			close(tun.events)
   275  		}
   276  	})
   277  	if err1 != nil {
   278  		return err1
   279  	}
   280  	return err2
   281  }
   282  
   283  func (tun *NativeTun) setMTU(n int) error {
   284  	fd, err := unix.Socket(
   285  		unix.AF_INET,
   286  		unix.SOCK_DGRAM,
   287  		0,
   288  	)
   289  	if err != nil {
   290  		return err
   291  	}
   292  
   293  	defer unix.Close(fd)
   294  
   295  	var ifr unix.IfreqMTU
   296  	copy(ifr.Name[:], tun.name)
   297  	ifr.MTU = int32(n)
   298  	err = unix.IoctlSetIfreqMTU(fd, &ifr)
   299  	if err != nil {
   300  		return fmt.Errorf("failed to set MTU on %s: %w", tun.name, err)
   301  	}
   302  
   303  	return nil
   304  }
   305  
   306  func (tun *NativeTun) MTU() (int, error) {
   307  	fd, err := unix.Socket(
   308  		unix.AF_INET,
   309  		unix.SOCK_DGRAM,
   310  		0,
   311  	)
   312  	if err != nil {
   313  		return 0, err
   314  	}
   315  
   316  	defer unix.Close(fd)
   317  
   318  	ifr, err := unix.IoctlGetIfreqMTU(fd, tun.name)
   319  	if err != nil {
   320  		return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, err)
   321  	}
   322  
   323  	return int(ifr.MTU), nil
   324  }