github.com/amnezia-vpn/amneziawg-go@v0.2.8/tun/tun_darwin.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2017-2023 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  	"time"
    17  	"unsafe"
    18  
    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, unix.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.(unix.Errno); ok && errno == unix.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 := socketCloexec(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)
   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) (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 = socketCloexec(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(bufs [][]byte, sizes []int, offset int) (int, error) {
   221  	// TODO: the BSDs look very similar in Read() and Write(). They should be
   222  	// collapsed, with platform-specific files containing the varying parts of
   223  	// their implementations.
   224  	select {
   225  	case err := <-tun.errors:
   226  		return 0, err
   227  	default:
   228  		buf := bufs[0][offset-4:]
   229  		n, err := tun.tunFile.Read(buf[:])
   230  		if n < 4 {
   231  			return 0, err
   232  		}
   233  		sizes[0] = n - 4
   234  		return 1, err
   235  	}
   236  }
   237  
   238  func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) {
   239  	if offset < 4 {
   240  		return 0, io.ErrShortBuffer
   241  	}
   242  	for i, buf := range bufs {
   243  		buf = buf[offset-4:]
   244  		buf[0] = 0x00
   245  		buf[1] = 0x00
   246  		buf[2] = 0x00
   247  		switch buf[4] >> 4 {
   248  		case 4:
   249  			buf[3] = unix.AF_INET
   250  		case 6:
   251  			buf[3] = unix.AF_INET6
   252  		default:
   253  			return i, unix.EAFNOSUPPORT
   254  		}
   255  		if _, err := tun.tunFile.Write(buf); err != nil {
   256  			return i, err
   257  		}
   258  	}
   259  	return len(bufs), nil
   260  }
   261  
   262  func (tun *NativeTun) Close() error {
   263  	var err1, err2 error
   264  	tun.closeOnce.Do(func() {
   265  		err1 = tun.tunFile.Close()
   266  		if tun.routeSocket != -1 {
   267  			unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
   268  			err2 = unix.Close(tun.routeSocket)
   269  		} else if tun.events != nil {
   270  			close(tun.events)
   271  		}
   272  	})
   273  	if err1 != nil {
   274  		return err1
   275  	}
   276  	return err2
   277  }
   278  
   279  func (tun *NativeTun) setMTU(n int) error {
   280  	fd, err := socketCloexec(
   281  		unix.AF_INET,
   282  		unix.SOCK_DGRAM,
   283  		0,
   284  	)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	defer unix.Close(fd)
   290  
   291  	var ifr unix.IfreqMTU
   292  	copy(ifr.Name[:], tun.name)
   293  	ifr.MTU = int32(n)
   294  	err = unix.IoctlSetIfreqMTU(fd, &ifr)
   295  	if err != nil {
   296  		return fmt.Errorf("failed to set MTU on %s: %w", tun.name, err)
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  func (tun *NativeTun) MTU() (int, error) {
   303  	fd, err := socketCloexec(
   304  		unix.AF_INET,
   305  		unix.SOCK_DGRAM,
   306  		0,
   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  }
   321  
   322  func (tun *NativeTun) BatchSize() int {
   323  	return 1
   324  }
   325  
   326  func socketCloexec(family, sotype, proto int) (fd int, err error) {
   327  	// See go/src/net/sys_cloexec.go for background.
   328  	syscall.ForkLock.RLock()
   329  	defer syscall.ForkLock.RUnlock()
   330  
   331  	fd, err = unix.Socket(family, sotype, proto)
   332  	if err == nil {
   333  		unix.CloseOnExec(fd)
   334  	}
   335  	return
   336  }