Switch most everything to netip in prep for ipv6 in the overlay (#1173)

This commit is contained in:
Nate Brown 2024-07-31 10:18:56 -05:00 committed by GitHub
parent 00458302ca
commit e264a0ff88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 1900 additions and 2682 deletions

View file

@ -2,16 +2,14 @@ package overlay
import (
"io"
"net"
"github.com/slackhq/nebula/iputil"
"net/netip"
)
type Device interface {
io.ReadWriteCloser
Activate() error
Cidr() *net.IPNet
Cidr() netip.Prefix
Name() string
RouteFor(iputil.VpnIp) iputil.VpnIp
RouteFor(netip.Addr) netip.Addr
NewMultiQueueReader() (io.ReadWriteCloser, error)
}

View file

@ -1,34 +1,30 @@
package overlay
import (
"bytes"
"fmt"
"math"
"net"
"net/netip"
"runtime"
"strconv"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
)
type Route struct {
MTU int
Metric int
Cidr *net.IPNet
Via *iputil.VpnIp
Cidr netip.Prefix
Via netip.Addr
Install bool
}
// Equal determines if a route that could be installed in the system route table is equal to another
// Via is ignored since that is only consumed within nebula itself
func (r Route) Equal(t Route) bool {
if !r.Cidr.IP.Equal(t.Cidr.IP) {
return false
}
if !bytes.Equal(r.Cidr.Mask, t.Cidr.Mask) {
if r.Cidr != t.Cidr {
return false
}
if r.Metric != t.Metric {
@ -51,21 +47,21 @@ func (r Route) String() string {
return s
}
func makeRouteTree(l *logrus.Logger, routes []Route, allowMTU bool) (*cidr.Tree4[iputil.VpnIp], error) {
routeTree := cidr.NewTree4[iputil.VpnIp]()
func makeRouteTree(l *logrus.Logger, routes []Route, allowMTU bool) (*bart.Table[netip.Addr], error) {
routeTree := new(bart.Table[netip.Addr])
for _, r := range routes {
if !allowMTU && r.MTU > 0 {
l.WithField("route", r).Warnf("route MTU is not supported in %s", runtime.GOOS)
}
if r.Via != nil {
routeTree.AddCIDR(r.Cidr, *r.Via)
if r.Via.IsValid() {
routeTree.Insert(r.Cidr, r.Via)
}
}
return routeTree, nil
}
func parseRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
func parseRoutes(c *config.C, network netip.Prefix) ([]Route, error) {
var err error
r := c.Get("tun.routes")
@ -116,12 +112,12 @@ func parseRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
MTU: mtu,
}
_, r.Cidr, err = net.ParseCIDR(fmt.Sprintf("%v", rRoute))
r.Cidr, err = netip.ParsePrefix(fmt.Sprintf("%v", rRoute))
if err != nil {
return nil, fmt.Errorf("entry %v.route in tun.routes failed to parse: %v", i+1, err)
}
if !ipWithin(network, r.Cidr) {
if !network.Contains(r.Cidr.Addr()) || r.Cidr.Bits() < network.Bits() {
return nil, fmt.Errorf(
"entry %v.route in tun.routes is not contained within the network attached to the certificate; route: %v, network: %v",
i+1,
@ -136,7 +132,7 @@ func parseRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
return routes, nil
}
func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
func parseUnsafeRoutes(c *config.C, network netip.Prefix) ([]Route, error) {
var err error
r := c.Get("tun.unsafe_routes")
@ -202,9 +198,9 @@ func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not a string: found %T", i+1, rVia)
}
nVia := net.ParseIP(via)
if nVia == nil {
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes failed to parse address: %v", i+1, via)
viaVpnIp, err := netip.ParseAddr(via)
if err != nil {
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes failed to parse address: %v", i+1, err)
}
rRoute, ok := m["route"]
@ -212,8 +208,6 @@ func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes is not present", i+1)
}
viaVpnIp := iputil.Ip2VpnIp(nVia)
install := true
rInstall, ok := m["install"]
if ok {
@ -224,18 +218,18 @@ func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]Route, error) {
}
r := Route{
Via: &viaVpnIp,
Via: viaVpnIp,
MTU: mtu,
Metric: metric,
Install: install,
}
_, r.Cidr, err = net.ParseCIDR(fmt.Sprintf("%v", rRoute))
r.Cidr, err = netip.ParsePrefix(fmt.Sprintf("%v", rRoute))
if err != nil {
return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes failed to parse: %v", i+1, err)
}
if ipWithin(network, r.Cidr) {
if network.Contains(r.Cidr.Addr()) {
return nil, fmt.Errorf(
"entry %v.route in tun.unsafe_routes is contained within the network attached to the certificate; route: %v, network: %v",
i+1,

View file

@ -2,11 +2,10 @@ package overlay
import (
"fmt"
"net"
"net/netip"
"testing"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/test"
"github.com/stretchr/testify/assert"
)
@ -14,7 +13,8 @@ import (
func Test_parseRoutes(t *testing.T) {
l := test.NewLogger()
c := config.NewC(l)
_, n, _ := net.ParseCIDR("10.0.0.0/24")
n, err := netip.ParsePrefix("10.0.0.0/24")
assert.NoError(t, err)
// test no routes config
routes, err := parseRoutes(c, n)
@ -67,7 +67,7 @@ func Test_parseRoutes(t *testing.T) {
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "nope"}}}
routes, err = parseRoutes(c, n)
assert.Nil(t, routes)
assert.EqualError(t, err, "entry 1.route in tun.routes failed to parse: invalid CIDR address: nope")
assert.EqualError(t, err, "entry 1.route in tun.routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
// below network range
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "1.0.0.0/8"}}}
@ -112,7 +112,8 @@ func Test_parseRoutes(t *testing.T) {
func Test_parseUnsafeRoutes(t *testing.T) {
l := test.NewLogger()
c := config.NewC(l)
_, n, _ := net.ParseCIDR("10.0.0.0/24")
n, err := netip.ParsePrefix("10.0.0.0/24")
assert.NoError(t, err)
// test no routes config
routes, err := parseUnsafeRoutes(c, n)
@ -157,7 +158,7 @@ func Test_parseUnsafeRoutes(t *testing.T) {
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "via": "nope"}}}
routes, err = parseUnsafeRoutes(c, n)
assert.Nil(t, routes)
assert.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: nope")
assert.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: ParseAddr(\"nope\"): unable to parse IP")
// missing route
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "500"}}}
@ -169,7 +170,7 @@ func Test_parseUnsafeRoutes(t *testing.T) {
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "500", "route": "nope"}}}
routes, err = parseUnsafeRoutes(c, n)
assert.Nil(t, routes)
assert.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: invalid CIDR address: nope")
assert.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
// within network range
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "route": "10.0.0.0/24"}}}
@ -252,7 +253,8 @@ func Test_parseUnsafeRoutes(t *testing.T) {
func Test_makeRouteTree(t *testing.T) {
l := test.NewLogger()
c := config.NewC(l)
_, n, _ := net.ParseCIDR("10.0.0.0/24")
n, err := netip.ParsePrefix("10.0.0.0/24")
assert.NoError(t, err)
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{
map[interface{}]interface{}{"via": "192.168.0.1", "route": "1.0.0.0/28"},
@ -264,17 +266,26 @@ func Test_makeRouteTree(t *testing.T) {
routeTree, err := makeRouteTree(l, routes, true)
assert.NoError(t, err)
ip := iputil.Ip2VpnIp(net.ParseIP("1.0.0.2"))
ok, r := routeTree.MostSpecificContains(ip)
ip, err := netip.ParseAddr("1.0.0.2")
assert.NoError(t, err)
r, ok := routeTree.Lookup(ip)
assert.True(t, ok)
assert.Equal(t, iputil.Ip2VpnIp(net.ParseIP("192.168.0.1")), r)
ip = iputil.Ip2VpnIp(net.ParseIP("1.0.0.1"))
ok, r = routeTree.MostSpecificContains(ip)
nip, err := netip.ParseAddr("192.168.0.1")
assert.NoError(t, err)
assert.Equal(t, nip, r)
ip, err = netip.ParseAddr("1.0.0.1")
assert.NoError(t, err)
r, ok = routeTree.Lookup(ip)
assert.True(t, ok)
assert.Equal(t, iputil.Ip2VpnIp(net.ParseIP("192.168.0.2")), r)
ip = iputil.Ip2VpnIp(net.ParseIP("1.1.0.1"))
ok, r = routeTree.MostSpecificContains(ip)
nip, err = netip.ParseAddr("192.168.0.2")
assert.NoError(t, err)
assert.Equal(t, nip, r)
ip, err = netip.ParseAddr("1.1.0.1")
assert.NoError(t, err)
r, ok = routeTree.Lookup(ip)
assert.False(t, ok)
}

View file

@ -1,7 +1,7 @@
package overlay
import (
"net"
"net/netip"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/config"
@ -11,9 +11,9 @@ import (
const DefaultMTU = 1300
// TODO: We may be able to remove routines
type DeviceFactory func(c *config.C, l *logrus.Logger, tunCidr *net.IPNet, routines int) (Device, error)
type DeviceFactory func(c *config.C, l *logrus.Logger, tunCidr netip.Prefix, routines int) (Device, error)
func NewDeviceFromConfig(c *config.C, l *logrus.Logger, tunCidr *net.IPNet, routines int) (Device, error) {
func NewDeviceFromConfig(c *config.C, l *logrus.Logger, tunCidr netip.Prefix, routines int) (Device, error) {
switch {
case c.GetBool("tun.disabled", false):
tun := newDisabledTun(tunCidr, c.GetInt("tun.tx_queue", 500), c.GetBool("stats.message_metrics", false), l)
@ -25,12 +25,12 @@ func NewDeviceFromConfig(c *config.C, l *logrus.Logger, tunCidr *net.IPNet, rout
}
func NewFdDeviceFromConfig(fd *int) DeviceFactory {
return func(c *config.C, l *logrus.Logger, tunCidr *net.IPNet, routines int) (Device, error) {
return func(c *config.C, l *logrus.Logger, tunCidr netip.Prefix, routines int) (Device, error) {
return newTunFromFd(c, l, *fd, tunCidr)
}
}
func getAllRoutesFromConfig(c *config.C, cidr *net.IPNet, initial bool) (bool, []Route, error) {
func getAllRoutesFromConfig(c *config.C, cidr netip.Prefix, initial bool) (bool, []Route, error) {
if !initial && !c.HasChanged("tun.routes") && !c.HasChanged("tun.unsafe_routes") {
return false, nil, nil
}

View file

@ -6,27 +6,26 @@ package overlay
import (
"fmt"
"io"
"net"
"net/netip"
"os"
"sync/atomic"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
)
type tun struct {
io.ReadWriteCloser
fd int
cidr *net.IPNet
cidr netip.Prefix
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
}
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet) (*tun, error) {
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr netip.Prefix) (*tun, error) {
// XXX Android returns an fd in non-blocking mode which is necessary for shutdown to work properly.
// Be sure not to call file.Fd() as it will set the fd to blocking mode.
file := os.NewFile(uintptr(deviceFd), "/dev/net/tun")
@ -53,12 +52,12 @@ func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet)
return t, nil
}
func newTun(_ *config.C, _ *logrus.Logger, _ *net.IPNet, _ bool) (*tun, error) {
func newTun(_ *config.C, _ *logrus.Logger, _ netip.Prefix, _ bool) (*tun, error) {
return nil, fmt.Errorf("newTun not supported in Android")
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
@ -87,7 +86,7 @@ func (t *tun) reload(c *config.C, initial bool) error {
return nil
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -8,15 +8,15 @@ import (
"fmt"
"io"
"net"
"net/netip"
"os"
"sync/atomic"
"syscall"
"unsafe"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
netroute "golang.org/x/net/route"
"golang.org/x/sys/unix"
@ -25,10 +25,10 @@ import (
type tun struct {
io.ReadWriteCloser
Device string
cidr *net.IPNet
cidr netip.Prefix
DefaultMTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
linkAddr *netroute.LinkAddr
l *logrus.Logger
@ -73,7 +73,7 @@ type ifreqMTU struct {
pad [8]byte
}
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*tun, error) {
name := c.GetString("tun.dev", "")
ifIndex := -1
if name != "" && name != "utun" {
@ -172,7 +172,7 @@ func (t *tun) deviceBytes() (o [16]byte) {
return
}
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*tun, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (*tun, error) {
return nil, fmt.Errorf("newTunFromFd not supported in Darwin")
}
@ -188,8 +188,13 @@ func (t *tun) Activate() error {
var addr, mask [4]byte
copy(addr[:], t.cidr.IP.To4())
copy(mask[:], t.cidr.Mask)
if !t.cidr.Addr().Is4() {
//TODO: IPV6-WORK
panic("need ipv6")
}
addr = t.cidr.Addr().As4()
copy(mask[:], prefixToMask(t.cidr))
s, err := unix.Socket(
unix.AF_INET,
@ -329,13 +334,12 @@ func (t *tun) reload(c *config.C, initial bool) error {
return nil
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
ok, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, ok := t.routeTree.Load().Lookup(ip)
if ok {
return r
}
return 0
return netip.Addr{}
}
// Get the LinkAddr for the interface of the given name
@ -384,13 +388,19 @@ func (t *tun) addRoutes(logErrors bool) error {
maskAddr := &netroute.Inet4Addr{}
routes := *t.Routes.Load()
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}
copy(routeAddr.IP[:], r.Cidr.IP.To4())
copy(maskAddr.IP[:], net.IP(r.Cidr.Mask).To4())
if !r.Cidr.Addr().Is4() {
//TODO: implement ipv6
panic("Cant handle ipv6 routes yet")
}
routeAddr.IP = r.Cidr.Addr().As4()
//TODO: we could avoid the copy
copy(maskAddr.IP[:], prefixToMask(r.Cidr))
err := addRoute(routeSock, routeAddr, maskAddr, t.linkAddr)
if err != nil {
@ -435,8 +445,13 @@ func (t *tun) removeRoutes(routes []Route) error {
continue
}
copy(routeAddr.IP[:], r.Cidr.IP.To4())
copy(maskAddr.IP[:], net.IP(r.Cidr.Mask).To4())
if r.Cidr.Addr().Is6() {
//TODO: implement ipv6
panic("Cant handle ipv6 routes yet")
}
routeAddr.IP = r.Cidr.Addr().As4()
copy(maskAddr.IP[:], prefixToMask(r.Cidr))
err := delRoute(routeSock, routeAddr, maskAddr, t.linkAddr)
if err != nil {
@ -536,7 +551,7 @@ func (t *tun) Write(from []byte) (int, error) {
return n - 4, err
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}
@ -547,3 +562,11 @@ func (t *tun) Name() string {
func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
return nil, fmt.Errorf("TODO: multiqueue not implemented for darwin")
}
func prefixToMask(prefix netip.Prefix) []byte {
pLen := 128
if prefix.Addr().Is4() {
pLen = 32
}
return net.CIDRMask(prefix.Bits(), pLen)
}

View file

@ -3,7 +3,7 @@ package overlay
import (
"fmt"
"io"
"net"
"net/netip"
"strings"
"github.com/rcrowley/go-metrics"
@ -13,7 +13,7 @@ import (
type disabledTun struct {
read chan []byte
cidr *net.IPNet
cidr netip.Prefix
// Track these metrics since we don't have the tun device to do it for us
tx metrics.Counter
@ -21,7 +21,7 @@ type disabledTun struct {
l *logrus.Logger
}
func newDisabledTun(cidr *net.IPNet, queueLen int, metricsEnabled bool, l *logrus.Logger) *disabledTun {
func newDisabledTun(cidr netip.Prefix, queueLen int, metricsEnabled bool, l *logrus.Logger) *disabledTun {
tun := &disabledTun{
cidr: cidr,
read: make(chan []byte, queueLen),
@ -43,11 +43,11 @@ func (*disabledTun) Activate() error {
return nil
}
func (*disabledTun) RouteFor(iputil.VpnIp) iputil.VpnIp {
return 0
func (*disabledTun) RouteFor(addr netip.Addr) netip.Addr {
return netip.Addr{}
}
func (t *disabledTun) Cidr() *net.IPNet {
func (t *disabledTun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -9,7 +9,7 @@ import (
"fmt"
"io"
"io/fs"
"net"
"net/netip"
"os"
"os/exec"
"strconv"
@ -17,10 +17,9 @@ import (
"syscall"
"unsafe"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
)
@ -48,10 +47,10 @@ type ifreqDestroy struct {
type tun struct {
Device string
cidr *net.IPNet
cidr netip.Prefix
MTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
io.ReadWriteCloser
@ -79,11 +78,11 @@ func (t *tun) Close() error {
return nil
}
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*tun, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (*tun, error) {
return nil, fmt.Errorf("newTunFromFd not supported in FreeBSD")
}
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*tun, error) {
// Try to open existing tun device
var file *os.File
var err error
@ -174,7 +173,7 @@ func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error
func (t *tun) Activate() error {
var err error
// TODO use syscalls instead of exec.Command
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to run 'ifconfig': %s", err)
@ -233,12 +232,12 @@ func (t *tun) reload(c *config.C, initial bool) error {
return nil
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}
@ -253,7 +252,7 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
func (t *tun) addRoutes(logErrors bool) error {
routes := *t.Routes.Load()
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}

View file

@ -7,32 +7,31 @@ import (
"errors"
"fmt"
"io"
"net"
"net/netip"
"os"
"sync"
"sync/atomic"
"syscall"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
)
type tun struct {
io.ReadWriteCloser
cidr *net.IPNet
cidr netip.Prefix
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
}
func newTun(_ *config.C, _ *logrus.Logger, _ *net.IPNet, _ bool) (*tun, error) {
func newTun(_ *config.C, _ *logrus.Logger, _ netip.Prefix, _ bool) (*tun, error) {
return nil, fmt.Errorf("newTun not supported in iOS")
}
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet) (*tun, error) {
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr netip.Prefix) (*tun, error) {
file := os.NewFile(uintptr(deviceFd), "/dev/tun")
t := &tun{
cidr: cidr,
@ -80,8 +79,8 @@ func (t *tun) reload(c *config.C, initial bool) error {
return nil
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
@ -143,7 +142,7 @@ func (tr *tunReadCloser) Close() error {
return tr.f.Close()
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -4,19 +4,18 @@
package overlay
import (
"bytes"
"fmt"
"io"
"net"
"net/netip"
"os"
"strings"
"sync/atomic"
"unsafe"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
@ -26,7 +25,7 @@ type tun struct {
io.ReadWriteCloser
fd int
Device string
cidr *net.IPNet
cidr netip.Prefix
MaxMTU int
DefaultMTU int
TXQueueLen int
@ -34,7 +33,7 @@ type tun struct {
ioctlFd uintptr
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
routeChan chan struct{}
useSystemRoutes bool
@ -65,7 +64,7 @@ type ifreqQLEN struct {
pad [8]byte
}
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet) (*tun, error) {
func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr netip.Prefix) (*tun, error) {
file := os.NewFile(uintptr(deviceFd), "/dev/net/tun")
t, err := newTunGeneric(c, l, file, cidr)
@ -78,7 +77,7 @@ func newTunFromFd(c *config.C, l *logrus.Logger, deviceFd int, cidr *net.IPNet)
return t, nil
}
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, multiqueue bool) (*tun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, multiqueue bool) (*tun, error) {
fd, err := unix.Open("/dev/net/tun", os.O_RDWR, 0)
if err != nil {
// If /dev/net/tun doesn't exist, try to create it (will happen in docker)
@ -123,7 +122,7 @@ func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, multiqueue bool) (*t
return t, nil
}
func newTunGeneric(c *config.C, l *logrus.Logger, file *os.File, cidr *net.IPNet) (*tun, error) {
func newTunGeneric(c *config.C, l *logrus.Logger, file *os.File, cidr netip.Prefix) (*tun, error) {
t := &tun{
ReadWriteCloser: file,
fd: int(file.Fd()),
@ -231,8 +230,8 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
return file, nil
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
@ -275,8 +274,10 @@ func (t *tun) Activate() error {
var addr, mask [4]byte
copy(addr[:], t.cidr.IP.To4())
copy(mask[:], t.cidr.Mask)
//TODO: IPV6-WORK
addr = t.cidr.Addr().As4()
tmask := net.CIDRMask(t.cidr.Bits(), 32)
copy(mask[:], tmask)
s, err := unix.Socket(
unix.AF_INET,
@ -364,14 +365,19 @@ func (t *tun) setMTU() {
func (t *tun) setDefaultRoute() error {
// Default route
dr := &net.IPNet{IP: t.cidr.IP.Mask(t.cidr.Mask), Mask: t.cidr.Mask}
dr := &net.IPNet{
IP: t.cidr.Masked().Addr().AsSlice(),
Mask: net.CIDRMask(t.cidr.Bits(), t.cidr.Addr().BitLen()),
}
nr := netlink.Route{
LinkIndex: t.deviceIndex,
Dst: dr,
MTU: t.DefaultMTU,
AdvMSS: t.advMSS(Route{}),
Scope: unix.RT_SCOPE_LINK,
Src: t.cidr.IP,
Src: net.IP(t.cidr.Addr().AsSlice()),
Protocol: unix.RTPROT_KERNEL,
Table: unix.RT_TABLE_MAIN,
Type: unix.RTN_UNICAST,
@ -392,9 +398,14 @@ func (t *tun) addRoutes(logErrors bool) error {
continue
}
dr := &net.IPNet{
IP: r.Cidr.Masked().Addr().AsSlice(),
Mask: net.CIDRMask(r.Cidr.Bits(), r.Cidr.Addr().BitLen()),
}
nr := netlink.Route{
LinkIndex: t.deviceIndex,
Dst: r.Cidr,
Dst: dr,
MTU: r.MTU,
AdvMSS: t.advMSS(r),
Scope: unix.RT_SCOPE_LINK,
@ -426,9 +437,14 @@ func (t *tun) removeRoutes(routes []Route) {
continue
}
dr := &net.IPNet{
IP: r.Cidr.Masked().Addr().AsSlice(),
Mask: net.CIDRMask(r.Cidr.Bits(), r.Cidr.Addr().BitLen()),
}
nr := netlink.Route{
LinkIndex: t.deviceIndex,
Dst: r.Cidr,
Dst: dr,
MTU: r.MTU,
AdvMSS: t.advMSS(r),
Scope: unix.RT_SCOPE_LINK,
@ -447,7 +463,7 @@ func (t *tun) removeRoutes(routes []Route) {
}
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}
@ -499,7 +515,15 @@ func (t *tun) updateRoutes(r netlink.RouteUpdate) {
return
}
if !t.cidr.Contains(r.Gw) {
//TODO: IPV6-WORK what if not ok?
gwAddr, ok := netip.AddrFromSlice(r.Gw)
if !ok {
t.l.WithField("route", r).Debug("Ignoring route update, invalid gateway address")
return
}
gwAddr = gwAddr.Unmap()
if !t.cidr.Contains(gwAddr) {
// Gateway isn't in our overlay network, ignore
t.l.WithField("route", r).Debug("Ignoring route update, not in our network")
return
@ -511,28 +535,25 @@ func (t *tun) updateRoutes(r netlink.RouteUpdate) {
return
}
newTree := cidr.NewTree4[iputil.VpnIp]()
if r.Type == unix.RTM_NEWROUTE {
for _, oldR := range t.routeTree.Load().List() {
newTree.AddCIDR(oldR.CIDR, oldR.Value)
}
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Adding route")
newTree.AddCIDR(r.Dst, iputil.Ip2VpnIp(r.Gw))
} else {
gw := iputil.Ip2VpnIp(r.Gw)
for _, oldR := range t.routeTree.Load().List() {
if bytes.Equal(oldR.CIDR.IP, r.Dst.IP) && bytes.Equal(oldR.CIDR.Mask, r.Dst.Mask) && oldR.Value == gw {
// This is the record to delete
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Removing route")
continue
}
newTree.AddCIDR(oldR.CIDR, oldR.Value)
}
dstAddr, ok := netip.AddrFromSlice(r.Dst.IP)
if !ok {
t.l.WithField("route", r).Debug("Ignoring route update, invalid destination address")
return
}
ones, _ := r.Dst.Mask.Size()
dst := netip.PrefixFrom(dstAddr, ones)
newTree := t.routeTree.Load().Clone()
if r.Type == unix.RTM_NEWROUTE {
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Adding route")
newTree.Insert(dst, gwAddr)
} else {
newTree.Delete(dst)
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Removing route")
}
t.routeTree.Store(newTree)
}

View file

@ -6,7 +6,7 @@ package overlay
import (
"fmt"
"io"
"net"
"net/netip"
"os"
"os/exec"
"regexp"
@ -15,10 +15,9 @@ import (
"syscall"
"unsafe"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
)
@ -29,10 +28,10 @@ type ifreqDestroy struct {
type tun struct {
Device string
cidr *net.IPNet
cidr netip.Prefix
MTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
io.ReadWriteCloser
@ -59,13 +58,13 @@ func (t *tun) Close() error {
return nil
}
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*tun, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (*tun, error) {
return nil, fmt.Errorf("newTunFromFd not supported in NetBSD")
}
var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`)
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*tun, error) {
// Try to open tun device
var file *os.File
var err error
@ -109,13 +108,13 @@ func (t *tun) Activate() error {
var err error
// TODO use syscalls instead of exec.Command
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to run 'ifconfig': %s", err)
}
cmd = exec.Command("/sbin/route", "-n", "add", "-net", t.cidr.String(), t.cidr.IP.String())
cmd = exec.Command("/sbin/route", "-n", "add", "-net", t.cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to run 'route add': %s", err)
@ -168,12 +167,12 @@ func (t *tun) reload(c *config.C, initial bool) error {
return nil
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}
@ -188,12 +187,12 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
func (t *tun) addRoutes(logErrors bool) error {
routes := *t.Routes.Load()
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err := cmd.Run(); err != nil {
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err)
@ -214,7 +213,7 @@ func (t *tun) removeRoutes(routes []Route) error {
continue
}
cmd := exec.Command("/sbin/route", "-n", "delete", "-net", r.Cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/route", "-n", "delete", "-net", r.Cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err := cmd.Run(); err != nil {
t.l.WithError(err).WithField("route", r).Error("Failed to remove route")

View file

@ -6,7 +6,7 @@ package overlay
import (
"fmt"
"io"
"net"
"net/netip"
"os"
"os/exec"
"regexp"
@ -14,19 +14,18 @@ import (
"sync/atomic"
"syscall"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
)
type tun struct {
Device string
cidr *net.IPNet
cidr netip.Prefix
MTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
io.ReadWriteCloser
@ -43,13 +42,13 @@ func (t *tun) Close() error {
return nil
}
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*tun, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (*tun, error) {
return nil, fmt.Errorf("newTunFromFd not supported in OpenBSD")
}
var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`)
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*tun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*tun, error) {
deviceName := c.GetString("tun.dev", "")
if deviceName == "" {
return nil, fmt.Errorf("a device name in the format of tunN must be specified")
@ -127,7 +126,7 @@ func (t *tun) reload(c *config.C, initial bool) error {
func (t *tun) Activate() error {
var err error
// TODO use syscalls instead of exec.Command
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/ifconfig", t.Device, t.cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to run 'ifconfig': %s", err)
@ -139,7 +138,7 @@ func (t *tun) Activate() error {
return fmt.Errorf("failed to run 'ifconfig': %s", err)
}
cmd = exec.Command("/sbin/route", "-n", "add", "-inet", t.cidr.String(), t.cidr.IP.String())
cmd = exec.Command("/sbin/route", "-n", "add", "-inet", t.cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err = cmd.Run(); err != nil {
return fmt.Errorf("failed to run 'route add': %s", err)
@ -149,20 +148,20 @@ func (t *tun) Activate() error {
return t.addRoutes(false)
}
func (t *tun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
func (t *tun) addRoutes(logErrors bool) error {
routes := *t.Routes.Load()
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}
cmd := exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err := cmd.Run(); err != nil {
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err)
@ -183,7 +182,7 @@ func (t *tun) removeRoutes(routes []Route) error {
continue
}
cmd := exec.Command("/sbin/route", "-n", "delete", "-inet", r.Cidr.String(), t.cidr.IP.String())
cmd := exec.Command("/sbin/route", "-n", "delete", "-inet", r.Cidr.String(), t.cidr.Addr().String())
t.l.Debug("command: ", cmd.String())
if err := cmd.Run(); err != nil {
t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
@ -194,7 +193,7 @@ func (t *tun) removeRoutes(routes []Route) error {
return nil
}
func (t *tun) Cidr() *net.IPNet {
func (t *tun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -6,21 +6,20 @@ package overlay
import (
"fmt"
"io"
"net"
"net/netip"
"os"
"sync/atomic"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
)
type TestTun struct {
Device string
cidr *net.IPNet
cidr netip.Prefix
Routes []Route
routeTree *cidr.Tree4[iputil.VpnIp]
routeTree *bart.Table[netip.Addr]
l *logrus.Logger
closed atomic.Bool
@ -28,7 +27,7 @@ type TestTun struct {
TxPackets chan []byte // Packets transmitted outside by nebula
}
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*TestTun, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*TestTun, error) {
_, routes, err := getAllRoutesFromConfig(c, cidr, true)
if err != nil {
return nil, err
@ -49,7 +48,7 @@ func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*TestTun, e
}, nil
}
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (*TestTun, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (*TestTun, error) {
return nil, fmt.Errorf("newTunFromFd not supported")
}
@ -87,8 +86,8 @@ func (t *TestTun) Get(block bool) []byte {
// Below this is boilerplate implementation to make nebula actually work
//********************************************************************************************************************//
func (t *TestTun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.MostSpecificContains(ip)
func (t *TestTun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Lookup(ip)
return r
}
@ -96,7 +95,7 @@ func (t *TestTun) Activate() error {
return nil
}
func (t *TestTun) Cidr() *net.IPNet {
func (t *TestTun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -4,30 +4,30 @@ import (
"fmt"
"io"
"net"
"net/netip"
"os/exec"
"strconv"
"sync/atomic"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
"github.com/songgao/water"
)
type waterTun struct {
Device string
cidr *net.IPNet
cidr netip.Prefix
MTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
f *net.Interface
*water.Interface
}
func newWaterTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*waterTun, error) {
func newWaterTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*waterTun, error) {
// NOTE: You cannot set the deviceName under Windows, so you must check tun.Device after calling .Activate()
t := &waterTun{
cidr: cidr,
@ -70,8 +70,8 @@ func (t *waterTun) Activate() error {
`C:\Windows\System32\netsh.exe`, "interface", "ipv4", "set", "address",
fmt.Sprintf("name=%s", t.Device),
"source=static",
fmt.Sprintf("addr=%s", t.cidr.IP),
fmt.Sprintf("mask=%s", net.IP(t.cidr.Mask)),
fmt.Sprintf("addr=%s", t.cidr.Addr()),
fmt.Sprintf("mask=%s", net.CIDRMask(t.cidr.Bits(), t.cidr.Addr().BitLen())),
"gateway=none",
).Run()
if err != nil {
@ -141,7 +141,7 @@ func (t *waterTun) addRoutes(logErrors bool) error {
// Path routes
routes := *t.Routes.Load()
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}
@ -182,12 +182,12 @@ func (t *waterTun) removeRoutes(routes []Route) {
}
}
func (t *waterTun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *waterTun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
func (t *waterTun) Cidr() *net.IPNet {
func (t *waterTun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -5,7 +5,7 @@ package overlay
import (
"fmt"
"net"
"net/netip"
"os"
"path/filepath"
"runtime"
@ -15,11 +15,11 @@ import (
"github.com/slackhq/nebula/config"
)
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ *net.IPNet) (Device, error) {
func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ netip.Prefix) (Device, error) {
return nil, fmt.Errorf("newTunFromFd not supported in Windows")
}
func newTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, multiqueue bool) (Device, error) {
func newTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, multiqueue bool) (Device, error) {
useWintun := true
if err := checkWinTunExists(); err != nil {
l.WithError(err).Warn("Check Wintun driver failed, fallback to wintap driver")

View file

@ -4,15 +4,13 @@ import (
"crypto"
"fmt"
"io"
"net"
"net/netip"
"sync/atomic"
"unsafe"
"github.com/gaissmai/bart"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cidr"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
"github.com/slackhq/nebula/util"
"github.com/slackhq/nebula/wintun"
"golang.org/x/sys/windows"
@ -23,11 +21,10 @@ const tunGUIDLabel = "Fixed Nebula Windows GUID v1"
type winTun struct {
Device string
cidr *net.IPNet
prefix netip.Prefix
cidr netip.Prefix
MTU int
Routes atomic.Pointer[[]Route]
routeTree atomic.Pointer[cidr.Tree4[iputil.VpnIp]]
routeTree atomic.Pointer[bart.Table[netip.Addr]]
l *logrus.Logger
tun *wintun.NativeTun
@ -52,22 +49,16 @@ func generateGUIDByDeviceName(name string) (*windows.GUID, error) {
return (*windows.GUID)(unsafe.Pointer(&sum[0])), nil
}
func newWinTun(c *config.C, l *logrus.Logger, cidr *net.IPNet, _ bool) (*winTun, error) {
func newWinTun(c *config.C, l *logrus.Logger, cidr netip.Prefix, _ bool) (*winTun, error) {
deviceName := c.GetString("tun.dev", "")
guid, err := generateGUIDByDeviceName(deviceName)
if err != nil {
return nil, fmt.Errorf("generate GUID failed: %w", err)
}
prefix, err := iputil.ToNetIpPrefix(*cidr)
if err != nil {
return nil, err
}
t := &winTun{
Device: deviceName,
cidr: cidr,
prefix: prefix,
MTU: c.GetInt("tun.mtu", DefaultMTU),
l: l,
}
@ -140,7 +131,7 @@ func (t *winTun) reload(c *config.C, initial bool) error {
func (t *winTun) Activate() error {
luid := winipcfg.LUID(t.tun.LUID())
err := luid.SetIPAddresses([]netip.Prefix{t.prefix})
err := luid.SetIPAddresses([]netip.Prefix{t.cidr})
if err != nil {
return fmt.Errorf("failed to set address: %w", err)
}
@ -159,24 +150,13 @@ func (t *winTun) addRoutes(logErrors bool) error {
foundDefault4 := false
for _, r := range routes {
if r.Via == nil || !r.Install {
if !r.Via.IsValid() || !r.Install {
// We don't allow route MTUs so only install routes with a via
continue
}
prefix, err := iputil.ToNetIpPrefix(*r.Cidr)
if err != nil {
retErr := util.NewContextualError("Failed to parse cidr to netip prefix, ignoring route", map[string]interface{}{"route": r}, err)
if logErrors {
retErr.Log(t.l)
continue
} else {
return retErr
}
}
// Add our unsafe route
err = luid.AddRoute(prefix, r.Via.ToNetIpAddr(), uint32(r.Metric))
err := luid.AddRoute(r.Cidr, r.Via, uint32(r.Metric))
if err != nil {
retErr := util.NewContextualError("Failed to add route", map[string]interface{}{"route": r}, err)
if logErrors {
@ -190,7 +170,7 @@ func (t *winTun) addRoutes(logErrors bool) error {
}
if !foundDefault4 {
if ones, bits := r.Cidr.Mask.Size(); ones == 0 && bits != 0 {
if r.Cidr.Bits() == 0 && r.Cidr.Addr().BitLen() == 32 {
foundDefault4 = true
}
}
@ -221,13 +201,7 @@ func (t *winTun) removeRoutes(routes []Route) error {
continue
}
prefix, err := iputil.ToNetIpPrefix(*r.Cidr)
if err != nil {
t.l.WithError(err).WithField("route", r).Info("Failed to convert cidr to netip prefix")
continue
}
err = luid.DeleteRoute(prefix, r.Via.ToNetIpAddr())
err := luid.DeleteRoute(r.Cidr, r.Via)
if err != nil {
t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
} else {
@ -237,12 +211,12 @@ func (t *winTun) removeRoutes(routes []Route) error {
return nil
}
func (t *winTun) RouteFor(ip iputil.VpnIp) iputil.VpnIp {
_, r := t.routeTree.Load().MostSpecificContains(ip)
func (t *winTun) RouteFor(ip netip.Addr) netip.Addr {
r, _ := t.routeTree.Load().Lookup(ip)
return r
}
func (t *winTun) Cidr() *net.IPNet {
func (t *winTun) Cidr() netip.Prefix {
return t.cidr
}

View file

@ -2,18 +2,17 @@ package overlay
import (
"io"
"net"
"net/netip"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/iputil"
)
func NewUserDeviceFromConfig(c *config.C, l *logrus.Logger, tunCidr *net.IPNet, routines int) (Device, error) {
func NewUserDeviceFromConfig(c *config.C, l *logrus.Logger, tunCidr netip.Prefix, routines int) (Device, error) {
return NewUserDevice(tunCidr)
}
func NewUserDevice(tunCidr *net.IPNet) (Device, error) {
func NewUserDevice(tunCidr netip.Prefix) (Device, error) {
// these pipes guarantee each write/read will match 1:1
or, ow := io.Pipe()
ir, iw := io.Pipe()
@ -27,7 +26,7 @@ func NewUserDevice(tunCidr *net.IPNet) (Device, error) {
}
type UserDevice struct {
tunCidr *net.IPNet
tunCidr netip.Prefix
outboundReader *io.PipeReader
outboundWriter *io.PipeWriter
@ -39,9 +38,9 @@ type UserDevice struct {
func (d *UserDevice) Activate() error {
return nil
}
func (d *UserDevice) Cidr() *net.IPNet { return d.tunCidr }
func (d *UserDevice) Name() string { return "faketun0" }
func (d *UserDevice) RouteFor(ip iputil.VpnIp) iputil.VpnIp { return ip }
func (d *UserDevice) Cidr() netip.Prefix { return d.tunCidr }
func (d *UserDevice) Name() string { return "faketun0" }
func (d *UserDevice) RouteFor(ip netip.Addr) netip.Addr { return ip }
func (d *UserDevice) NewMultiQueueReader() (io.ReadWriteCloser, error) {
return d, nil
}