github.com/slackhq/nebula@v1.9.0/overlay/tun_openbsd.go (about) 1 //go:build !e2e_testing 2 // +build !e2e_testing 3 4 package overlay 5 6 import ( 7 "fmt" 8 "io" 9 "net" 10 "os" 11 "os/exec" 12 "regexp" 13 "strconv" 14 "sync/atomic" 15 "syscall" 16 17 "github.com/sirupsen/logrus" 18 "github.com/slackhq/nebula/cidr" 19 "github.com/slackhq/nebula/config" 20 "github.com/slackhq/nebula/iputil" 21 "github.com/slackhq/nebula/util" 22 ) 23 24 type tun struct { 25 Device string 26 cidr *net.IPNet 27 MTU int 28 Routes atomic.Pointer[[]Route] 29 routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]] 30 l *logrus.Logger 31 32 io.ReadWriteCloser 33 34 // cache out buffer since we need to prepend 4 bytes for tun metadata 35 out []byte 36 } 37 38 func (t *tun) Close() error { 39 if t.ReadWriteCloser != nil { 40 return t.ReadWriteCloser.Close() 41 } 42 43 return nil 44 } 45 46 func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*tun, error) { 47 return nil, fmt.Errorf("newTunFromFd not supported in OpenBSD") 48 } 49 50 var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`) 51 52 func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error) { 53 deviceName := c.GetString("tun.dev", "") 54 if deviceName == "" { 55 return nil, fmt.Errorf("a device name in the format of tunN must be specified") 56 } 57 58 if !deviceNameRE.MatchString(deviceName) { 59 return nil, fmt.Errorf("a device name in the format of tunN must be specified") 60 } 61 62 file, err := os.OpenFile("/dev/"+deviceName, os.O_RDWR, 0) 63 if err != nil { 64 return nil, err 65 } 66 67 t := &tun{ 68 ReadWriteCloser: file, 69 Device: deviceName, 70 cidr: cidr, 71 MTU: c.GetInt("tun.mtu", DefaultMTU), 72 l: l, 73 } 74 75 err = t.reload(c, true) 76 if err != nil { 77 return nil, err 78 } 79 80 c.RegisterReloadCallback(func(c *config.C) { 81 err := t.reload(c, false) 82 if err != nil { 83 util.LogWithContextIfNeeded("failed to reload tun device", err, t.l) 84 } 85 }) 86 87 return t, nil 88 } 89 90 func (t *tun) reload(c *config.C, initial bool) error { 91 change, routes, err := getAllRoutesFromConfig(c, t.cidr, initial) 92 if err != nil { 93 return err 94 } 95 96 if !initial && !change { 97 return nil 98 } 99 100 routeTree, err := makeRouteTree(t.l, routes, false) 101 if err != nil { 102 return err 103 } 104 105 // Teach nebula how to handle the routes before establishing them in the system table 106 oldRoutes := t.Routes.Swap(&routes) 107 t.routeTree.Store(routeTree) 108 109 if !initial { 110 // Remove first, if the system removes a wanted route hopefully it will be re-added next 111 err := t.removeRoutes(findRemovedRoutes(routes, *oldRoutes)) 112 if err != nil { 113 util.LogWithContextIfNeeded("Failed to remove routes", err, t.l) 114 } 115 116 // Ensure any routes we actually want are installed 117 err = t.addRoutes(true) 118 if err != nil { 119 // Catch any stray logs 120 util.LogWithContextIfNeeded("Failed to add routes", err, t.l) 121 } 122 } 123 124 return nil 125 } 126 127 func (t *tun) Activate() error { 128 var err error 129 // TODO use syscalls instead of exec.Command 130 cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String()) 131 t.l.Debug("command: ", cmd.String()) 132 if err = cmd.Run(); err != nil { 133 return fmt.Errorf("failed to run 'ifconfig': %s", err) 134 } 135 136 cmd = exec.Command("/sbin/ifconfig", t.Device, "mtu", strconv.Itoa(t.MTU)) 137 t.l.Debug("command: ", cmd.String()) 138 if err = cmd.Run(); err != nil { 139 return fmt.Errorf("failed to run 'ifconfig': %s", err) 140 } 141 142 cmd = exec.Command("/sbin/route", "-n", "add", "-inet", t.cidr.String(), t.cidr.IP.String()) 143 t.l.Debug("command: ", cmd.String()) 144 if err = cmd.Run(); err != nil { 145 return fmt.Errorf("failed to run 'route add': %s", err) 146 } 147 148 // Unsafe path routes 149 return t.addRoutes(false) 150 } 151 152 func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp { 153 _, r := t.routeTree.Load().MostSpecificContains(ip) 154 return r 155 } 156 157 func (t *tun) addRoutes(logErrors bool) error { 158 routes := *t.Routes.Load() 159 for _, r := range routes { 160 if r.Via == nil || !r.Install { 161 // We don't allow route MTUs so only install routes with a via 162 continue 163 } 164 165 cmd := exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.cidr.IP.String()) 166 t.l.Debug("command: ", cmd.String()) 167 if err := cmd.Run(); err != nil { 168 retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err) 169 if logErrors { 170 retErr.Log(t.l) 171 } else { 172 return retErr 173 } 174 } 175 } 176 177 return nil 178 } 179 180 func (t *tun) removeRoutes(routes []Route) error { 181 for _, r := range routes { 182 if !r.Install { 183 continue 184 } 185 186 cmd := exec.Command("/sbin/route", "-n", "delete", "-inet", r.Cidr.String(), t.cidr.IP.String()) 187 t.l.Debug("command: ", cmd.String()) 188 if err := cmd.Run(); err != nil { 189 t.l.WithError(err).WithField("route", r).Error("Failed to remove route") 190 } else { 191 t.l.WithField("route", r).Info("Removed route") 192 } 193 } 194 return nil 195 } 196 197 func (t *tun) Cidr() *net.IPNet { 198 return t.cidr 199 } 200 201 func (t *tun) Name() string { 202 return t.Device 203 } 204 205 func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) { 206 return nil, fmt.Errorf("TODO: multiqueue not implemented for freebsd") 207 } 208 209 func (t *tun) Read(to []byte) (int, error) { 210 buf := make([]byte, len(to)+4) 211 212 n, err := t.ReadWriteCloser.Read(buf) 213 214 copy(to, buf[4:]) 215 return n - 4, err 216 } 217 218 // Write is only valid for single threaded use 219 func (t *tun) Write(from []byte) (int, error) { 220 buf := t.out 221 if cap(buf) < len(from)+4 { 222 buf = make([]byte, len(from)+4) 223 t.out = buf 224 } 225 buf = buf[:len(from)+4] 226 227 if len(from) == 0 { 228 return 0, syscall.EIO 229 } 230 231 // Determine the IP Family for the NULL L2 Header 232 ipVer := from[0] >> 4 233 if ipVer == 4 { 234 buf[3] = syscall.AF_INET 235 } else if ipVer == 6 { 236 buf[3] = syscall.AF_INET6 237 } else { 238 return 0, fmt.Errorf("unable to determine IP version from packet") 239 } 240 241 copy(buf[4:], from) 242 243 n, err := t.ReadWriteCloser.Write(buf) 244 return n - 4, err 245 }