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 }