github.com/labulakalia/water@v0.0.5-0.20231118024244-f351ca6784b6/syscalls_darwin.go (about)

     1  package water
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  const appleUTUNCtl = "com.apple.net.utun_control"
    17  
    18  /*
    19   * From ioctl.h:
    20   * #define	IOCPARM_MASK	0x1fff		// parameter length, at most 13 bits
    21   * ...
    22   * #define	IOC_OUT		0x40000000	// copy out parameters
    23   * #define	IOC_IN		0x80000000	// copy in parameters
    24   * #define	IOC_INOUT	(IOC_IN|IOC_OUT)
    25   * ...
    26   * #define _IOC(inout,group,num,len) \
    27   * 	(inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
    28   * ...
    29   * #define	_IOWR(g,n,t)	_IOC(IOC_INOUT,	(g), (n), sizeof(t))
    30   *
    31   * From kern_control.h:
    32   * #define CTLIOCGINFO     _IOWR('N', 3, struct ctl_info)	// get id from name
    33   *
    34   */
    35  
    36  const appleCTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
    37  
    38  /*
    39   * #define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t))
    40   * #define TUNSIFMODE _IOW('t', 94, int)
    41   */
    42  const appleTUNSIFMODE = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 94
    43  
    44  /*
    45   * struct sockaddr_ctl {
    46   *     u_char sc_len; // depends on size of bundle ID string
    47   *     u_char sc_family; // AF_SYSTEM
    48   *     u_int16_t ss_sysaddr; // AF_SYS_KERNCONTROL
    49   *     u_int32_t sc_id; // Controller unique identifier
    50   *     u_int32_t sc_unit; // Developer private unit number
    51   *     u_int32_t sc_reserved[5];
    52   * };
    53   */
    54  type sockaddrCtl struct {
    55  	scLen      uint8
    56  	scFamily   uint8
    57  	ssSysaddr  uint16
    58  	scID       uint32
    59  	scUnit     uint32
    60  	scReserved [5]uint32
    61  }
    62  
    63  var sockaddrCtlSize uintptr = 32
    64  
    65  func openDev(config Config) (ifce *Interface, err error) {
    66  	if config.Driver == MacOSDriverTunTapOSX {
    67  		return openDevTunTapOSX(config)
    68  	}
    69  	if config.Driver == MacOSDriverSystem {
    70  		return openDevSystem(config)
    71  	}
    72  	return nil, errors.New("unrecognized driver")
    73  }
    74  
    75  // openDevSystem opens tun device on system
    76  func openDevSystem(config Config) (ifce *Interface, err error) {
    77  	if config.DeviceType != TUN {
    78  		return nil, errors.New("only tun is implemented for SystemDriver, use TunTapOSXDriver for tap")
    79  	}
    80  
    81  	ifIndex := -1
    82  	if config.Name != "" {
    83  		const utunPrefix = "utun"
    84  		if !strings.HasPrefix(config.Name, utunPrefix) {
    85  			return nil, fmt.Errorf("Interface name must be utun[0-9]+")
    86  		}
    87  		ifIndex, err = strconv.Atoi(config.Name[len(utunPrefix):])
    88  		if err != nil || ifIndex < 0 || ifIndex > math.MaxUint32-1 {
    89  			return nil, fmt.Errorf("Interface name must be utun[0-9]+")
    90  		}
    91  	}
    92  
    93  	var fd int
    94  	// Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ...
    95  	//
    96  	// In sys/socket.h:
    97  	// #define PF_SYSTEM	AF_SYSTEM
    98  	//
    99  	// In sys/sys_domain.h:
   100  	// #define SYSPROTO_CONTROL       	2	/* kernel control protocol */
   101  	if fd, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil {
   102  		return nil, fmt.Errorf("error in syscall.Socket: %v", err)
   103  	}
   104  
   105  	var ctlInfo = &struct {
   106  		ctlID   uint32
   107  		ctlName [96]byte
   108  	}{}
   109  	copy(ctlInfo.ctlName[:], []byte(appleUTUNCtl))
   110  
   111  	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(appleCTLIOCGINFO), uintptr(unsafe.Pointer(ctlInfo))); errno != 0 {
   112  		err = errno
   113  		return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err)
   114  	}
   115  
   116  	addrP := unsafe.Pointer(&sockaddrCtl{
   117  		scLen:    uint8(sockaddrCtlSize),
   118  		scFamily: syscall.AF_SYSTEM,
   119  
   120  		/* #define AF_SYS_CONTROL 2 */
   121  		ssSysaddr: 2,
   122  
   123  		scID:   ctlInfo.ctlID,
   124  		scUnit: uint32(ifIndex) + 1,
   125  	})
   126  	if _, _, errno := syscall.RawSyscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(addrP), uintptr(sockaddrCtlSize)); errno != 0 {
   127  		err = errno
   128  		return nil, fmt.Errorf("error in syscall.RawSyscall(syscall.SYS_CONNECT, ...): %v", err)
   129  	}
   130  
   131  	var ifName struct {
   132  		name [16]byte
   133  	}
   134  	ifNameSize := uintptr(16)
   135  	if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd),
   136  		2, /* #define SYSPROTO_CONTROL 2 */
   137  		2, /* #define UTUN_OPT_IFNAME 2 */
   138  		uintptr(unsafe.Pointer(&ifName)),
   139  		uintptr(unsafe.Pointer(&ifNameSize)), 0); errno != 0 {
   140  		err = errno
   141  		return nil, fmt.Errorf("error in syscall.Syscall6(syscall.SYS_GETSOCKOPT, ...): %v", err)
   142  	}
   143  
   144  	if err = setNonBlock(fd); err != nil {
   145  		return nil, fmt.Errorf("setting non-blocking error")
   146  	}
   147  
   148  	return &Interface{
   149  		isTAP: false,
   150  		name:  string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]),
   151  		ReadWriteCloser: &tunReadCloser{
   152  			f: os.NewFile(uintptr(fd), string(ifName.name[:])),
   153  		},
   154  	}, nil
   155  }
   156  
   157  // openDevTunTapOSX opens tun / tap device, assuming tuntaposx is installed
   158  func openDevTunTapOSX(config Config) (ifce *Interface, err error) {
   159  	var fd int
   160  	var socketFD int
   161  
   162  	if config.DeviceType == TAP && !strings.HasPrefix(config.Name, "tap") {
   163  		return nil, errors.New("device name does not start with tap when creating a tap device")
   164  	}
   165  	if config.DeviceType == TUN && !strings.HasPrefix(config.Name, "tun") {
   166  		return nil, errors.New("device name does not start with tun when creating a tun device")
   167  	}
   168  	if config.DeviceType != TAP && config.DeviceType != TUN {
   169  		return nil, errors.New("unsupported DeviceType")
   170  	}
   171  	if len(config.Name) >= 15 {
   172  		return nil, errors.New("device name is too long")
   173  	}
   174  
   175  	if fd, err = syscall.Open(
   176  		"/dev/"+config.Name, os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil {
   177  		return nil, err
   178  	}
   179  	// Note that we are not setting NONBLOCK on the fd itself since it breaks tuntaposx
   180  	// see https://sourceforge.net/p/tuntaposx/bugs/6/
   181  
   182  	// create socket so we can do SIO ioctls, we are not using it afterwards
   183  	if socketFD, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil {
   184  		return nil, fmt.Errorf("error in syscall.Socket: %v", err)
   185  	}
   186  	var ifReq = &struct {
   187  		ifName    [16]byte
   188  		ifruFlags int16
   189  		pad       [16]byte
   190  	}{}
   191  	copy(ifReq.ifName[:], []byte(config.Name))
   192  	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCGIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 {
   193  		err = errno
   194  		return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err)
   195  	}
   196  	ifReq.ifruFlags |= syscall.IFF_RUNNING | syscall.IFF_UP
   197  	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCSIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 {
   198  		err = errno
   199  		return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err)
   200  	}
   201  	syscall.Close(socketFD)
   202  
   203  	return &Interface{
   204  		isTAP:           config.DeviceType == TAP,
   205  		ReadWriteCloser: os.NewFile(uintptr(fd), "tun"),
   206  		name:            config.Name,
   207  	}, nil
   208  }
   209  
   210  // tunReadCloser is a hack to work around the first 4 bytes "packet
   211  // information" because there doesn't seem to be an IFF_NO_PI for darwin.
   212  type tunReadCloser struct {
   213  	f io.ReadWriteCloser
   214  
   215  	rMu  sync.Mutex
   216  	rBuf []byte
   217  
   218  	wMu  sync.Mutex
   219  	wBuf []byte
   220  }
   221  
   222  var _ io.ReadWriteCloser = (*tunReadCloser)(nil)
   223  
   224  func (t *tunReadCloser) Read(to []byte) (int, error) {
   225  	t.rMu.Lock()
   226  	defer t.rMu.Unlock()
   227  
   228  	if cap(t.rBuf) < len(to)+4 {
   229  		t.rBuf = make([]byte, len(to)+4)
   230  	}
   231  	t.rBuf = t.rBuf[:len(to)+4]
   232  
   233  	n, err := t.f.Read(t.rBuf)
   234  	copy(to, t.rBuf[4:])
   235  	return n - 4, err
   236  }
   237  
   238  func (t *tunReadCloser) Write(from []byte) (int, error) {
   239  
   240  	if len(from) == 0 {
   241  		return 0, syscall.EIO
   242  	}
   243  
   244  	t.wMu.Lock()
   245  	defer t.wMu.Unlock()
   246  
   247  	if cap(t.wBuf) < len(from)+4 {
   248  		t.wBuf = make([]byte, len(from)+4)
   249  	}
   250  	t.wBuf = t.wBuf[:len(from)+4]
   251  
   252  	// Determine the IP Family for the NULL L2 Header
   253  	ipVer := from[0] >> 4
   254  	if ipVer == 4 {
   255  		t.wBuf[3] = syscall.AF_INET
   256  	} else if ipVer == 6 {
   257  		t.wBuf[3] = syscall.AF_INET6
   258  	} else {
   259  		return 0, errors.New("Unable to determine IP version from packet")
   260  	}
   261  
   262  	copy(t.wBuf[4:], from)
   263  
   264  	n, err := t.f.Write(t.wBuf)
   265  	return n - 4, err
   266  }
   267  
   268  func (t *tunReadCloser) Close() error {
   269  	return t.f.Close()
   270  }