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
79 changed files with 1900 additions and 2682 deletions

View File

@@ -1,6 +1,8 @@
package udp
import (
"net/netip"
"github.com/slackhq/nebula/config"
"github.com/slackhq/nebula/firewall"
"github.com/slackhq/nebula/header"
@@ -9,7 +11,7 @@ import (
const MTU = 9001
type EncReader func(
addr *Addr,
addr netip.AddrPort,
out []byte,
packet []byte,
header *header.H,
@@ -22,9 +24,9 @@ type EncReader func(
type Conn interface {
Rebind() error
LocalAddr() (*Addr, error)
LocalAddr() (netip.AddrPort, error)
ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int)
WriteTo(b []byte, addr *Addr) error
WriteTo(b []byte, addr netip.AddrPort) error
ReloadConfig(c *config.C)
Close() error
}
@@ -34,13 +36,13 @@ type NoopConn struct{}
func (NoopConn) Rebind() error {
return nil
}
func (NoopConn) LocalAddr() (*Addr, error) {
return nil, nil
func (NoopConn) LocalAddr() (netip.AddrPort, error) {
return netip.AddrPort{}, nil
}
func (NoopConn) ListenOut(_ EncReader, _ LightHouseHandlerFunc, _ *firewall.ConntrackCacheTicker, _ int) {
return
}
func (NoopConn) WriteTo(_ []byte, _ *Addr) error {
func (NoopConn) WriteTo(_ []byte, _ netip.AddrPort) error {
return nil
}
func (NoopConn) ReloadConfig(_ *config.C) {

View File

@@ -1,9 +1,10 @@
package udp
import (
"github.com/slackhq/nebula/iputil"
"net/netip"
)
//TODO: The items in this file belong in their own packages but doing that in a single PR is a nightmare
type LightHouseHandlerFunc func(rAddr *Addr, vpnIp iputil.VpnIp, p []byte)
// TODO: IPV6-WORK this can likely be removed now
type LightHouseHandlerFunc func(rAddr netip.AddrPort, vpnIp netip.Addr, p []byte)

View File

@@ -1,100 +0,0 @@
package udp
import (
"encoding/json"
"fmt"
"net"
"strconv"
)
type m map[string]interface{}
type Addr struct {
IP net.IP
Port uint16
}
func NewAddr(ip net.IP, port uint16) *Addr {
addr := Addr{IP: make([]byte, net.IPv6len), Port: port}
copy(addr.IP, ip.To16())
return &addr
}
func NewAddrFromString(s string) *Addr {
ip, port, err := ParseIPAndPort(s)
//TODO: handle err
_ = err
return &Addr{IP: ip.To16(), Port: port}
}
func (ua *Addr) Equals(t *Addr) bool {
if t == nil || ua == nil {
return t == nil && ua == nil
}
return ua.IP.Equal(t.IP) && ua.Port == t.Port
}
func (ua *Addr) String() string {
if ua == nil {
return "<nil>"
}
return net.JoinHostPort(ua.IP.String(), fmt.Sprintf("%v", ua.Port))
}
func (ua *Addr) MarshalJSON() ([]byte, error) {
if ua == nil {
return nil, nil
}
return json.Marshal(m{"ip": ua.IP, "port": ua.Port})
}
func (ua *Addr) Copy() *Addr {
if ua == nil {
return nil
}
nu := Addr{
Port: ua.Port,
IP: make(net.IP, len(ua.IP)),
}
copy(nu.IP, ua.IP)
return &nu
}
type AddrSlice []*Addr
func (a AddrSlice) Equal(b AddrSlice) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !a[i].Equals(b[i]) {
return false
}
}
return true
}
func ParseIPAndPort(s string) (net.IP, uint16, error) {
rIp, sPort, err := net.SplitHostPort(s)
if err != nil {
return nil, 0, err
}
addr, err := net.ResolveIPAddr("ip", rIp)
if err != nil {
return nil, 0, err
}
iPort, err := strconv.Atoi(sPort)
if err != nil {
return nil, 0, err
}
return addr.IP, uint16(iPort), nil
}

View File

@@ -6,13 +6,14 @@ package udp
import (
"fmt"
"net"
"net/netip"
"syscall"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
return NewGenericListener(l, ip, port, multi, batch)
}

View File

@@ -9,13 +9,14 @@ package udp
import (
"fmt"
"net"
"net/netip"
"syscall"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
return NewGenericListener(l, ip, port, multi, batch)
}

View File

@@ -8,13 +8,14 @@ package udp
import (
"fmt"
"net"
"net/netip"
"syscall"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
return NewGenericListener(l, ip, port, multi, batch)
}

View File

@@ -11,6 +11,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/config"
@@ -25,7 +26,7 @@ type GenericConn struct {
var _ Conn = &GenericConn{}
func NewGenericListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewGenericListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
lc := NewListenConfig(multi)
pc, err := lc.ListenPacket(context.TODO(), "udp", net.JoinHostPort(ip.String(), fmt.Sprintf("%v", port)))
if err != nil {
@@ -37,23 +38,24 @@ func NewGenericListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch
return nil, fmt.Errorf("Unexpected PacketConn: %T %#v", pc, pc)
}
func (u *GenericConn) WriteTo(b []byte, addr *Addr) error {
_, err := u.UDPConn.WriteToUDP(b, &net.UDPAddr{IP: addr.IP, Port: int(addr.Port)})
func (u *GenericConn) WriteTo(b []byte, addr netip.AddrPort) error {
_, err := u.UDPConn.WriteToUDPAddrPort(b, addr)
return err
}
func (u *GenericConn) LocalAddr() (*Addr, error) {
func (u *GenericConn) LocalAddr() (netip.AddrPort, error) {
a := u.UDPConn.LocalAddr()
switch v := a.(type) {
case *net.UDPAddr:
addr := &Addr{IP: make([]byte, len(v.IP))}
copy(addr.IP, v.IP)
addr.Port = uint16(v.Port)
return addr, nil
addr, ok := netip.AddrFromSlice(v.IP)
if !ok {
return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
}
return netip.AddrPortFrom(addr, uint16(v.Port)), nil
default:
return nil, fmt.Errorf("LocalAddr returned: %#v", a)
return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
}
}
@@ -75,19 +77,26 @@ func (u *GenericConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *f
buffer := make([]byte, MTU)
h := &header.H{}
fwPacket := &firewall.Packet{}
udpAddr := &Addr{IP: make([]byte, 16)}
nb := make([]byte, 12, 12)
for {
// Just read one packet at a time
n, rua, err := u.ReadFromUDP(buffer)
n, rua, err := u.ReadFromUDPAddrPort(buffer)
if err != nil {
u.l.WithError(err).Debug("udp socket is closed, exiting read loop")
return
}
udpAddr.IP = rua.IP
udpAddr.Port = uint16(rua.Port)
r(udpAddr, plaintext[:0], buffer[:n], h, fwPacket, lhf, nb, q, cache.Get(u.l))
r(
netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()),
plaintext[:0],
buffer[:n],
h,
fwPacket,
lhf,
nb,
q,
cache.Get(u.l),
)
}
}

View File

@@ -7,6 +7,7 @@ import (
"encoding/binary"
"fmt"
"net"
"net/netip"
"syscall"
"unsafe"
@@ -35,10 +36,9 @@ func maybeIPV4(ip net.IP) (net.IP, bool) {
return ip, false
}
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
ipV4, isV4 := maybeIPV4(ip)
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
af := unix.AF_INET6
if isV4 {
if ip.Is4() {
af = unix.AF_INET
}
syscall.ForkLock.RLock()
@@ -61,13 +61,13 @@ func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (
//TODO: support multiple listening IPs (for limiting ipv6)
var sa unix.Sockaddr
if isV4 {
if ip.Is4() {
sa4 := &unix.SockaddrInet4{Port: port}
copy(sa4.Addr[:], ipV4)
sa4.Addr = ip.As4()
sa = sa4
} else {
sa6 := &unix.SockaddrInet6{Port: port}
copy(sa6.Addr[:], ip.To16())
sa6.Addr = ip.As16()
sa = sa6
}
if err = unix.Bind(fd, sa); err != nil {
@@ -79,7 +79,7 @@ func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (
//v, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_INCOMING_CPU)
//l.Println(v, err)
return &StdConn{sysFd: fd, isV4: isV4, l: l, batch: batch}, err
return &StdConn{sysFd: fd, isV4: ip.Is4(), l: l, batch: batch}, err
}
func (u *StdConn) Rebind() error {
@@ -102,30 +102,29 @@ func (u *StdConn) GetSendBuffer() (int, error) {
return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_SNDBUF)
}
func (u *StdConn) LocalAddr() (*Addr, error) {
func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
sa, err := unix.Getsockname(u.sysFd)
if err != nil {
return nil, err
return netip.AddrPort{}, err
}
addr := &Addr{}
switch sa := sa.(type) {
case *unix.SockaddrInet4:
addr.IP = net.IP{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]}.To16()
addr.Port = uint16(sa.Port)
case *unix.SockaddrInet6:
addr.IP = sa.Addr[0:]
addr.Port = uint16(sa.Port)
}
return netip.AddrPortFrom(netip.AddrFrom4(sa.Addr), uint16(sa.Port)), nil
return addr, nil
case *unix.SockaddrInet6:
return netip.AddrPortFrom(netip.AddrFrom16(sa.Addr), uint16(sa.Port)), nil
default:
return netip.AddrPort{}, fmt.Errorf("unsupported sock type: %T", sa)
}
}
func (u *StdConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firewall.ConntrackCacheTicker, q int) {
plaintext := make([]byte, MTU)
h := &header.H{}
fwPacket := &firewall.Packet{}
udpAddr := &Addr{}
var ip netip.Addr
nb := make([]byte, 12, 12)
//TODO: should we track this?
@@ -146,12 +145,23 @@ func (u *StdConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firew
//metric.Update(int64(n))
for i := 0; i < n; i++ {
if u.isV4 {
udpAddr.IP = names[i][4:8]
ip, _ = netip.AddrFromSlice(names[i][4:8])
//TODO: IPV6-WORK what is not ok?
} else {
udpAddr.IP = names[i][8:24]
ip, _ = netip.AddrFromSlice(names[i][8:24])
//TODO: IPV6-WORK what is not ok?
}
udpAddr.Port = binary.BigEndian.Uint16(names[i][2:4])
r(udpAddr, plaintext[:0], buffers[i][:msgs[i].Len], h, fwPacket, lhf, nb, q, cache.Get(u.l))
r(
netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(names[i][2:4])),
plaintext[:0],
buffers[i][:msgs[i].Len],
h,
fwPacket,
lhf,
nb,
q,
cache.Get(u.l),
)
}
}
}
@@ -197,19 +207,20 @@ func (u *StdConn) ReadMulti(msgs []rawMessage) (int, error) {
}
}
func (u *StdConn) WriteTo(b []byte, addr *Addr) error {
func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
if u.isV4 {
return u.writeTo4(b, addr)
return u.writeTo4(b, ip)
}
return u.writeTo6(b, addr)
return u.writeTo6(b, ip)
}
func (u *StdConn) writeTo6(b []byte, addr *Addr) error {
func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error {
var rsa unix.RawSockaddrInet6
rsa.Family = unix.AF_INET6
rsa.Addr = ip.Addr().As16()
port := ip.Port()
// Little Endian -> Network Endian
rsa.Port = (addr.Port >> 8) | ((addr.Port & 0xff) << 8)
copy(rsa.Addr[:], addr.IP.To16())
rsa.Port = (port >> 8) | ((port & 0xff) << 8)
for {
_, _, err := unix.Syscall6(
@@ -232,17 +243,17 @@ func (u *StdConn) writeTo6(b []byte, addr *Addr) error {
}
}
func (u *StdConn) writeTo4(b []byte, addr *Addr) error {
addrV4, isAddrV4 := maybeIPV4(addr.IP)
if !isAddrV4 {
func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error {
if !ip.Addr().Is4() {
return fmt.Errorf("Listener is IPv4, but writing to IPv6 remote")
}
var rsa unix.RawSockaddrInet4
rsa.Family = unix.AF_INET
rsa.Addr = ip.Addr().As4()
port := ip.Port()
// Little Endian -> Network Endian
rsa.Port = (addr.Port >> 8) | ((addr.Port & 0xff) << 8)
copy(rsa.Addr[:], addrV4)
rsa.Port = (port >> 8) | ((port & 0xff) << 8)
for {
_, _, err := unix.Syscall6(

View File

@@ -8,13 +8,14 @@ package udp
import (
"fmt"
"net"
"net/netip"
"syscall"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
return NewGenericListener(l, ip, port, multi, batch)
}

View File

@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"net"
"net/netip"
"sync"
"sync/atomic"
"syscall"
@@ -61,16 +62,14 @@ type RIOConn struct {
results [packetsPerRing]winrio.Result
}
func NewRIOListener(l *logrus.Logger, ip net.IP, port int) (*RIOConn, error) {
func NewRIOListener(l *logrus.Logger, addr netip.Addr, port int) (*RIOConn, error) {
if !winrio.Initialize() {
return nil, errors.New("could not initialize winrio")
}
u := &RIOConn{l: l}
addr := [16]byte{}
copy(addr[:], ip.To16())
err := u.bind(&windows.SockaddrInet6{Addr: addr, Port: port})
err := u.bind(&windows.SockaddrInet6{Addr: addr.As16(), Port: port})
if err != nil {
return nil, fmt.Errorf("bind: %w", err)
}
@@ -124,7 +123,6 @@ func (u *RIOConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firew
buffer := make([]byte, MTU)
h := &header.H{}
fwPacket := &firewall.Packet{}
udpAddr := &Addr{IP: make([]byte, 16)}
nb := make([]byte, 12, 12)
for {
@@ -135,11 +133,17 @@ func (u *RIOConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firew
return
}
udpAddr.IP = rua.Addr[:]
p := (*[2]byte)(unsafe.Pointer(&udpAddr.Port))
p[0] = byte(rua.Port >> 8)
p[1] = byte(rua.Port)
r(udpAddr, plaintext[:0], buffer[:n], h, fwPacket, lhf, nb, q, cache.Get(u.l))
r(
netip.AddrPortFrom(netip.AddrFrom16(rua.Addr).Unmap(), (rua.Port>>8)|((rua.Port&0xff)<<8)),
plaintext[:0],
buffer[:n],
h,
fwPacket,
lhf,
nb,
q,
cache.Get(u.l),
)
}
}
@@ -231,7 +235,7 @@ retry:
return n, ep, nil
}
func (u *RIOConn) WriteTo(buf []byte, addr *Addr) error {
func (u *RIOConn) WriteTo(buf []byte, ip netip.AddrPort) error {
if !u.isOpen.Load() {
return net.ErrClosed
}
@@ -274,10 +278,9 @@ func (u *RIOConn) WriteTo(buf []byte, addr *Addr) error {
packet := u.tx.Push()
packet.addr.Family = windows.AF_INET6
p := (*[2]byte)(unsafe.Pointer(&packet.addr.Port))
p[0] = byte(addr.Port >> 8)
p[1] = byte(addr.Port)
copy(packet.addr.Addr[:], addr.IP.To16())
packet.addr.Addr = ip.Addr().As16()
port := ip.Port()
packet.addr.Port = (port >> 8) | ((port & 0xff) << 8)
copy(packet.data[:], buf)
dataBuffer := &winrio.Buffer{
@@ -295,17 +298,15 @@ func (u *RIOConn) WriteTo(buf []byte, addr *Addr) error {
return winrio.SendEx(u.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, 0)
}
func (u *RIOConn) LocalAddr() (*Addr, error) {
func (u *RIOConn) LocalAddr() (netip.AddrPort, error) {
sa, err := windows.Getsockname(u.sock)
if err != nil {
return nil, err
return netip.AddrPort{}, err
}
v6 := sa.(*windows.SockaddrInet6)
return &Addr{
IP: v6.Addr[:],
Port: uint16(v6.Port),
}, nil
return netip.AddrPortFrom(netip.AddrFrom16(v6.Addr).Unmap(), uint16(v6.Port)), nil
}
func (u *RIOConn) Rebind() error {

View File

@@ -4,9 +4,8 @@
package udp
import (
"fmt"
"io"
"net"
"net/netip"
"sync/atomic"
"github.com/sirupsen/logrus"
@@ -16,30 +15,24 @@ import (
)
type Packet struct {
ToIp net.IP
ToPort uint16
FromIp net.IP
FromPort uint16
Data []byte
To netip.AddrPort
From netip.AddrPort
Data []byte
}
func (u *Packet) Copy() *Packet {
n := &Packet{
ToIp: make(net.IP, len(u.ToIp)),
ToPort: u.ToPort,
FromIp: make(net.IP, len(u.FromIp)),
FromPort: u.FromPort,
Data: make([]byte, len(u.Data)),
To: u.To,
From: u.From,
Data: make([]byte, len(u.Data)),
}
copy(n.ToIp, u.ToIp)
copy(n.FromIp, u.FromIp)
copy(n.Data, u.Data)
return n
}
type TesterConn struct {
Addr *Addr
Addr netip.AddrPort
RxPackets chan *Packet // Packets to receive into nebula
TxPackets chan *Packet // Packets transmitted outside by nebula
@@ -48,9 +41,9 @@ type TesterConn struct {
l *logrus.Logger
}
func NewListener(l *logrus.Logger, ip net.IP, port int, _ bool, _ int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, _ bool, _ int) (Conn, error) {
return &TesterConn{
Addr: &Addr{ip, uint16(port)},
Addr: netip.AddrPortFrom(ip, uint16(port)),
RxPackets: make(chan *Packet, 10),
TxPackets: make(chan *Packet, 10),
l: l,
@@ -71,7 +64,7 @@ func (u *TesterConn) Send(packet *Packet) {
}
if u.l.Level >= logrus.DebugLevel {
u.l.WithField("header", h).
WithField("udpAddr", fmt.Sprintf("%v:%v", packet.FromIp, packet.FromPort)).
WithField("udpAddr", packet.From).
WithField("dataLen", len(packet.Data)).
Debug("UDP receiving injected packet")
}
@@ -98,23 +91,18 @@ func (u *TesterConn) Get(block bool) *Packet {
// Below this is boilerplate implementation to make nebula actually work
//********************************************************************************************************************//
func (u *TesterConn) WriteTo(b []byte, addr *Addr) error {
func (u *TesterConn) WriteTo(b []byte, addr netip.AddrPort) error {
if u.closed.Load() {
return io.ErrClosedPipe
}
p := &Packet{
Data: make([]byte, len(b), len(b)),
FromIp: make([]byte, 16),
FromPort: u.Addr.Port,
ToIp: make([]byte, 16),
ToPort: addr.Port,
Data: make([]byte, len(b), len(b)),
From: u.Addr,
To: addr,
}
copy(p.Data, b)
copy(p.ToIp, addr.IP.To16())
copy(p.FromIp, u.Addr.IP.To16())
u.TxPackets <- p
return nil
}
@@ -123,7 +111,6 @@ func (u *TesterConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *fi
plaintext := make([]byte, MTU)
h := &header.H{}
fwPacket := &firewall.Packet{}
ua := &Addr{IP: make([]byte, 16)}
nb := make([]byte, 12, 12)
for {
@@ -131,9 +118,7 @@ func (u *TesterConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *fi
if !ok {
return
}
ua.Port = p.FromPort
copy(ua.IP, p.FromIp.To16())
r(ua, plaintext[:0], p.Data, h, fwPacket, lhf, nb, q, cache.Get(u.l))
r(p.From, plaintext[:0], p.Data, h, fwPacket, lhf, nb, q, cache.Get(u.l))
}
}
@@ -144,7 +129,7 @@ func NewUDPStatsEmitter(_ []Conn) func() {
return func() {}
}
func (u *TesterConn) LocalAddr() (*Addr, error) {
func (u *TesterConn) LocalAddr() (netip.AddrPort, error) {
return u.Addr, nil
}

View File

@@ -6,12 +6,13 @@ package udp
import (
"fmt"
"net"
"net/netip"
"syscall"
"github.com/sirupsen/logrus"
)
func NewListener(l *logrus.Logger, ip net.IP, port int, multi bool, batch int) (Conn, error) {
func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
if multi {
//NOTE: Technically we can support it with RIO but it wouldn't be at the socket level
// The udp stack would need to be reworked to hide away the implementation differences between