github.com/slackhq/nebula@v1.9.0/overlay/tun_ios.go (about)

     1  //go:build ios && !e2e_testing
     2  // +build ios,!e2e_testing
     3  
     4  package overlay
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net"
    11  	"os"
    12  	"sync"
    13  	"sync/atomic"
    14  	"syscall"
    15  
    16  	"github.com/sirupsen/logrus"
    17  	"github.com/slackhq/nebula/cidr"
    18  	"github.com/slackhq/nebula/config"
    19  	"github.com/slackhq/nebula/iputil"
    20  	"github.com/slackhq/nebula/util"
    21  )
    22  
    23  type tun struct {
    24  	io.ReadWriteCloser
    25  	cidr      *net.IPNet
    26  	Routes    atomic.Pointer[[]Route]
    27  	routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
    28  	l         *logrus.Logger
    29  }
    30  
    31  func newTun(_ *config.C, _ *logrus.Logger, _ *net.IPNet, _ bool) (*tun, error) {
    32  	return nil, fmt.Errorf("newTun not supported in iOS")
    33  }
    34  
    35  func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet) (*tun, error) {
    36  	file := os.NewFile(uintptr(deviceFd), "/dev/tun")
    37  	t := &tun{
    38  		cidr:            cidr,
    39  		ReadWriteCloser: &tunReadCloser{f: file},
    40  		l:               l,
    41  	}
    42  
    43  	err := t.reload(c, true)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	c.RegisterReloadCallback(func(c *config.C) {
    49  		err := t.reload(c, false)
    50  		if err != nil {
    51  			util.LogWithContextIfNeeded("failed to reload tun device", err, t.l)
    52  		}
    53  	})
    54  
    55  	return t, nil
    56  }
    57  
    58  func (t *tun) Activate() error {
    59  	return nil
    60  }
    61  
    62  func (t *tun) reload(c *config.C, initial bool) error {
    63  	change, routes, err := getAllRoutesFromConfig(c, t.cidr, initial)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	if !initial && !change {
    69  		return nil
    70  	}
    71  
    72  	routeTree, err := makeRouteTree(t.l, routes, false)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	// Teach nebula how to handle the routes
    78  	t.Routes.Store(&routes)
    79  	t.routeTree.Store(routeTree)
    80  	return nil
    81  }
    82  
    83  func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
    84  	_, r := t.routeTree.Load().MostSpecificContains(ip)
    85  	return r
    86  }
    87  
    88  // The following is hoisted up from water, we do this so we can inject our own fd on iOS
    89  type tunReadCloser struct {
    90  	f io.ReadWriteCloser
    91  
    92  	rMu  sync.Mutex
    93  	rBuf []byte
    94  
    95  	wMu  sync.Mutex
    96  	wBuf []byte
    97  }
    98  
    99  func (tr *tunReadCloser) Read(to []byte) (int, error) {
   100  	tr.rMu.Lock()
   101  	defer tr.rMu.Unlock()
   102  
   103  	if cap(tr.rBuf) < len(to)+4 {
   104  		tr.rBuf = make([]byte, len(to)+4)
   105  	}
   106  	tr.rBuf = tr.rBuf[:len(to)+4]
   107  
   108  	n, err := tr.f.Read(tr.rBuf)
   109  	copy(to, tr.rBuf[4:])
   110  	return n - 4, err
   111  }
   112  
   113  func (tr *tunReadCloser) Write(from []byte) (int, error) {
   114  	if len(from) == 0 {
   115  		return 0, syscall.EIO
   116  	}
   117  
   118  	tr.wMu.Lock()
   119  	defer tr.wMu.Unlock()
   120  
   121  	if cap(tr.wBuf) < len(from)+4 {
   122  		tr.wBuf = make([]byte, len(from)+4)
   123  	}
   124  	tr.wBuf = tr.wBuf[:len(from)+4]
   125  
   126  	// Determine the IP Family for the NULL L2 Header
   127  	ipVer := from[0] >> 4
   128  	if ipVer == 4 {
   129  		tr.wBuf[3] = syscall.AF_INET
   130  	} else if ipVer == 6 {
   131  		tr.wBuf[3] = syscall.AF_INET6
   132  	} else {
   133  		return 0, errors.New("unable to determine IP version from packet")
   134  	}
   135  
   136  	copy(tr.wBuf[4:], from)
   137  
   138  	n, err := tr.f.Write(tr.wBuf)
   139  	return n - 4, err
   140  }
   141  
   142  func (tr *tunReadCloser) Close() error {
   143  	return tr.f.Close()
   144  }
   145  
   146  func (t *tun) Cidr() *net.IPNet {
   147  	return t.cidr
   148  }
   149  
   150  func (t *tun) Name() string {
   151  	return "iOS"
   152  }
   153  
   154  func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
   155  	return nil, fmt.Errorf("TODO: multiqueue not implemented for ios")
   156  }