github.com/cawidtu/notwireguard-go/tun@v0.0.0-20230523131112-68e8e5ce9cdf/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  	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 = 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  		buff := buff[offset-4:]
   226  		n, err := tun.tunFile.Read(buff[:])
   227  		if n < 4 {
   228  			return 0, err
   229  		}
   230  		return n - 4, err
   231  	}
   232  }
   233  
   234  func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
   235  	// reserve space for header
   236  
   237  	buff = buff[offset-4:]
   238  
   239  	// add packet information header
   240  
   241  	buff[0] = 0x00
   242  	buff[1] = 0x00
   243  	buff[2] = 0x00
   244  
   245  	if buff[4]>>4 == ipv6.Version {
   246  		buff[3] = unix.AF_INET6
   247  	} else {
   248  		buff[3] = unix.AF_INET
   249  	}
   250  
   251  	// write
   252  
   253  	return tun.tunFile.Write(buff)
   254  }
   255  
   256  func (tun *NativeTun) Flush() error {
   257  	// TODO: can flushing be implemented by buffering and using sendmmsg?
   258  	return nil
   259  }
   260  
   261  func (tun *NativeTun) Close() error {
   262  	var err1, err2 error
   263  	tun.closeOnce.Do(func() {
   264  		err1 = tun.tunFile.Close()
   265  		if tun.routeSocket != -1 {
   266  			unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
   267  			err2 = unix.Close(tun.routeSocket)
   268  		} else if tun.events != nil {
   269  			close(tun.events)
   270  		}
   271  	})
   272  	if err1 != nil {
   273  		return err1
   274  	}
   275  	return err2
   276  }
   277  
   278  func (tun *NativeTun) setMTU(n int) error {
   279  	fd, err := unix.Socket(
   280  		unix.AF_INET,
   281  		unix.SOCK_DGRAM,
   282  		0,
   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  	if err != nil {
   308  		return 0, err
   309  	}
   310  
   311  	defer unix.Close(fd)
   312  
   313  	ifr, err := unix.IoctlGetIfreqMTU(fd, tun.name)
   314  	if err != nil {
   315  		return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, err)
   316  	}
   317  
   318  	return int(ifr.MTU), nil
   319  }