Skip to content

Commit

Permalink
Mostly working
Browse files Browse the repository at this point in the history
  • Loading branch information
nbrownus committed Sep 23, 2024
1 parent f8b9c80 commit 3ed4dd7
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 116 deletions.
2 changes: 1 addition & 1 deletion control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) {
Mask: net.IPMask{255, 255, 255, 0},
}

remotes := NewRemoteList(nil)
remotes := NewRemoteList([]netip.Addr{netip.IPv4Unspecified()}, nil)
remotes.unlockedPrependV4(netip.IPv4Unspecified(), netAddrToProtoV4AddrPort(remote1.Addr(), remote1.Port()))
remotes.unlockedPrependV6(netip.IPv4Unspecified(), netAddrToProtoV6AddrPort(remote2.Addr(), remote2.Port()))

Expand Down
116 changes: 73 additions & 43 deletions e2e/handshakes_test.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewTestCaCert(before, after time.Time, networks, unsafeNetworks []netip.Pre

// NewTestCert will generate a signed certificate with the provided details.
// Expiry times are defaulted if you do not pass them in
func NewTestCert(ca cert.Certificate, key []byte, name string, before, after time.Time, networks, unsafeNetworks []netip.Prefix, groups []string) (cert.Certificate, []byte, []byte, []byte) {
func NewTestCert(v cert.Version, ca cert.Certificate, key []byte, name string, before, after time.Time, networks, unsafeNetworks []netip.Prefix, groups []string) (cert.Certificate, []byte, []byte, []byte) {
if before.IsZero() {
before = time.Now().Add(time.Second * -60).Round(time.Second)
}
Expand All @@ -59,7 +59,7 @@ func NewTestCert(ca cert.Certificate, key []byte, name string, before, after tim

pub, rawPriv := x25519Keypair()
nc := &cert.TBSCertificate{
Version: cert.Version1,
Version: v,
Name: name,
Networks: networks,
UnsafeNetworks: unsafeNetworks,
Expand Down
42 changes: 37 additions & 5 deletions e2e/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import (
type m map[string]interface{}

// newSimpleServer creates a nebula instance with many assumptions
func newSimpleServer(caCrt cert.Certificate, caKey []byte, name string, sVpnNetworks string, overrides m) (*nebula.Control, []netip.Prefix, netip.AddrPort, *config.C) {
func newSimpleServer(v cert.Version, caCrt cert.Certificate, caKey []byte, name string, sVpnNetworks string, overrides m) (*nebula.Control, []netip.Prefix, netip.AddrPort, *config.C) {
l := NewTestLogger()

var vpnNetworks []netip.Prefix
for _, sn := range strings.Split(sVpnNetworks, ",") {
vpnIpNet, err := netip.ParsePrefix(sn)
vpnIpNet, err := netip.ParsePrefix(strings.TrimSpace(sn))
if err != nil {
panic(err)
}
Expand All @@ -55,7 +55,7 @@ func newSimpleServer(caCrt cert.Certificate, caKey []byte, name string, sVpnNetw
budpIp[3] = 239
udpAddr = netip.AddrPortFrom(netip.AddrFrom16(budpIp), 4242)
}
_, _, myPrivKey, myPEM := NewTestCert(caCrt, caKey, name, time.Now(), time.Now().Add(5*time.Minute), vpnNetworks, nil, []string{})
_, _, myPrivKey, myPEM := NewTestCert(v, caCrt, caKey, name, time.Now(), time.Now().Add(5*time.Minute), vpnNetworks, nil, []string{})

caB, err := caCrt.MarshalPEM()
if err != nil {
Expand Down Expand Up @@ -99,11 +99,16 @@ func newSimpleServer(caCrt cert.Certificate, caKey []byte, name string, sVpnNetw
}

if overrides != nil {
err = mergo.Merge(&overrides, mc, mergo.WithAppendSlice)
final := m{}
err = mergo.Merge(&final, overrides, mergo.WithAppendSlice)
if err != nil {
panic(err)
}
mc = overrides
err = mergo.Merge(&final, mc, mergo.WithAppendSlice)
if err != nil {
panic(err)
}
mc = final
}

cb, err := yaml.Marshal(mc)
Expand Down Expand Up @@ -191,6 +196,33 @@ func assertHostInfoPair(t *testing.T, addrA, addrB netip.AddrPort, vpnNetsA, vpn
}

func assertUdpPacket(t *testing.T, expected, b []byte, fromIp, toIp netip.Addr, fromPort, toPort uint16) {
if toIp.Is6() {
assertUdpPacket6(t, expected, b, fromIp, toIp, fromPort, toPort)
} else {
assertUdpPacket4(t, expected, b, fromIp, toIp, fromPort, toPort)
}
}

func assertUdpPacket6(t *testing.T, expected, b []byte, fromIp, toIp netip.Addr, fromPort, toPort uint16) {
packet := gopacket.NewPacket(b, layers.LayerTypeIPv6, gopacket.Lazy)
v6 := packet.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
assert.NotNil(t, v6, "No ipv6 data found")

assert.Equal(t, fromIp.AsSlice(), []byte(v6.SrcIP), "Source ip was incorrect")
assert.Equal(t, toIp.AsSlice(), []byte(v6.DstIP), "Dest ip was incorrect")

udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
assert.NotNil(t, udp, "No udp data found")

assert.Equal(t, fromPort, uint16(udp.SrcPort), "Source port was incorrect")
assert.Equal(t, toPort, uint16(udp.DstPort), "Dest port was incorrect")

data := packet.ApplicationLayer()
assert.NotNil(t, data)
assert.Equal(t, expected, data.Payload(), "Data was incorrect")
}

func assertUdpPacket4(t *testing.T, expected, b []byte, fromIp, toIp netip.Addr, fromPort, toPort uint16) {
packet := gopacket.NewPacket(b, layers.LayerTypeIPv4, gopacket.Lazy)
v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
assert.NotNil(t, v4, "No ipv4 data found")
Expand Down
47 changes: 32 additions & 15 deletions e2e/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"strings"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -216,7 +216,7 @@ func (r *R) renderFlow() {
continue
}
participants[addr] = struct{}{}
sanAddr := strings.Replace(addr.String(), ":", "-", 1)
sanAddr := normalizeName(addr.String())
participantsVals = append(participantsVals, sanAddr)
fmt.Fprintf(
f, " participant %s as Nebula: %s<br/>UDP: %s\n",
Expand Down Expand Up @@ -253,9 +253,9 @@ func (r *R) renderFlow() {

fmt.Fprintf(f,
" %s%s%s: %s(%s), index %v, counter: %v\n",
strings.Replace(p.from.GetUDPAddr().String(), ":", "-", 1),
normalizeName(p.from.GetUDPAddr().String()),
line,
strings.Replace(p.to.GetUDPAddr().String(), ":", "-", 1),
normalizeName(p.to.GetUDPAddr().String()),
h.TypeName(), h.SubTypeName(), h.RemoteIndex, h.MessageCounter,
)
}
Expand All @@ -270,6 +270,11 @@ func (r *R) renderFlow() {
}
}

func normalizeName(s string) string {
rx := regexp.MustCompile("[\\[\\]\\:]")
return rx.ReplaceAllLiteralString(s, "_")
}

// IgnoreFlow tells the router to stop recording future flows that matches the provided criteria.
// messageType and subType will target nebula underlay packets while tun will target nebula overlay packets
// NOTE: This is a very broad system, if you set tun to true then no more tun traffic will be rendered
Expand Down Expand Up @@ -714,30 +719,42 @@ func (r *R) getControl(fromAddr, toAddr netip.AddrPort, p *udp.Packet) *nebula.C
}

func (r *R) formatUdpPacket(p *packet) string {
packet := gopacket.NewPacket(p.packet.Data, layers.LayerTypeIPv4, gopacket.Lazy)
v4 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
if v4 == nil {
panic("not an ipv4 packet")
var packet gopacket.Packet
var srcAddr netip.Addr

packet = gopacket.NewPacket(p.packet.Data, layers.LayerTypeIPv6, gopacket.Lazy)
if packet.ErrorLayer() == nil {
v6 := packet.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
if v6 == nil {
panic("not an ipv6 packet")
}
srcAddr, _ = netip.AddrFromSlice(v6.SrcIP)
} else {
packet = gopacket.NewPacket(p.packet.Data, layers.LayerTypeIPv4, gopacket.Lazy)
v6 := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
if v6 == nil {
panic("not an ipv6 packet")
}
srcAddr, _ = netip.AddrFromSlice(v6.SrcIP)
}

from := "unknown"
srcAddr, _ := netip.AddrFromSlice(v4.SrcIP)
if c, ok := r.vpnControls[srcAddr]; ok {
from = c.GetUDPAddr().String()
}

udp := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
if udp == nil {
udpLayer := packet.Layer(layers.LayerTypeUDP).(*layers.UDP)
if udpLayer == nil {
panic("not a udp packet")
}

data := packet.ApplicationLayer()
return fmt.Sprintf(
" %s-->>%s: src port: %v<br/>dest port: %v<br/>data: \"%v\"\n",
strings.Replace(from, ":", "-", 1),
strings.Replace(p.to.GetUDPAddr().String(), ":", "-", 1),
udp.SrcPort,
udp.DstPort,
normalizeName(from),
normalizeName(p.to.GetUDPAddr().String()),
udpLayer.SrcPort,
udpLayer.DstPort,
string(data.Payload()),
)
}
8 changes: 5 additions & 3 deletions handshake_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,16 @@ func (hm *HandshakeManager) DeleteHostInfo(hostinfo *HostInfo) {
}

func (hm *HandshakeManager) unlockedDeleteHostInfo(hostinfo *HostInfo) {
//TODO: need to iterate hostinfo.vpnAddrs
delete(hm.vpnIps, hostinfo.vpnAddrs[0])
for _, addr := range hostinfo.vpnAddrs {
delete(hm.vpnIps, addr)
}

if len(hm.vpnIps) == 0 {
hm.vpnIps = map[netip.Addr]*HandshakeHostInfo{}
}

delete(hm.indexes, hostinfo.localIndexId)
if len(hm.vpnIps) == 0 {
if len(hm.indexes) == 0 {
hm.indexes = map[uint32]*HandshakeHostInfo{}
}

Expand Down
2 changes: 1 addition & 1 deletion handshake_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Test_NewHandshakeManagerVpnIp(t *testing.T) {
i2 := blah.StartHandshake(ip, nil)
assert.Same(t, i, i2)

i.remotes = NewRemoteList(nil)
i.remotes = NewRemoteList([]netip.Addr{}, nil)

// Adding something to pending should not affect the main hostmap
assert.Len(t, mainHM.Hosts, 0)
Expand Down
60 changes: 34 additions & 26 deletions hostmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (hm *HostMap) DeleteHostInfo(hostinfo *HostInfo) bool {
hm.Lock()
// If we have a previous or next hostinfo then we are not the last one for this vpn ip
final := (hostinfo.next == nil && hostinfo.prev == nil)
hm.unlockedDeleteHostInfo(hostinfo, false)
hm.unlockedDeleteHostInfo(hostinfo)
hm.Unlock()

return final
Expand All @@ -321,6 +321,8 @@ func (hm *HostMap) MakePrimary(hostinfo *HostInfo) {
}

func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) {
//TODO: we may need to promote follow on hostinfos from these vpnAddrs as well since their oldHostinfo might not be the same as this one
// this really looks like an ideal spot for memory leaks
oldHostinfo := hm.Hosts[hostinfo.vpnAddrs[0]]
if oldHostinfo == hostinfo {
return
Expand All @@ -345,7 +347,19 @@ func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) {
hostinfo.prev = nil
}

func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo, dontRecurse bool) {
func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) {
for _, addr := range hostinfo.vpnAddrs {
h := hm.Hosts[addr]
for h != nil {
if h == hostinfo {
hm.unlockedInnerDeleteHostInfo(h)
}
h = h.next
}
}
}

func (hm *HostMap) unlockedInnerDeleteHostInfo(hostinfo *HostInfo) {
primary, ok := hm.Hosts[hostinfo.vpnAddrs[0]]
if ok && primary == hostinfo {
// The vpnIp pointer points to the same hostinfo as the local index id, we can remove it
Expand Down Expand Up @@ -399,18 +413,6 @@ func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo, dontRecurse bool)
for _, localRelayIdx := range hostinfo.relayState.CopyRelayForIdxs() {
delete(hm.Relays, localRelayIdx)
}

if !dontRecurse {
for _, addr := range hostinfo.vpnAddrs {
h := hm.Hosts[addr]
for h != nil {
if h == hostinfo {
hm.unlockedDeleteHostInfo(h, true)
}
h = h.next
}
}
}
}

func (hm *HostMap) QueryIndex(index uint32) *HostInfo {
Expand Down Expand Up @@ -487,17 +489,8 @@ func (hm *HostMap) queryVpnAddr(vpnIp netip.Addr, promoteIfce *Interface) *HostI
// unlockedAddHostInfo assumes you have a write-lock and will add a hostinfo object to the hostmap Indexes and RemoteIndexes maps.
// If an entry exists for the Hosts table (vpnIp -> hostinfo) then the provided hostinfo will be made primary
func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) {
if f.serveDns {
remoteCert := hostinfo.ConnectionState.peerCert
dnsR.Add(remoteCert.Certificate.Name()+".", remoteCert.Certificate.Networks()[0].Addr().String())
}

existing := hm.Hosts[hostinfo.vpnAddrs[0]]
hm.Hosts[hostinfo.vpnAddrs[0]] = hostinfo

if existing != nil {
hostinfo.next = existing
existing.prev = hostinfo
for _, addr := range hostinfo.vpnAddrs {
hm.unlockedInnerAddHostInfo(addr, hostinfo, f)
}

hm.Indexes[hostinfo.localIndexId] = hostinfo
Expand All @@ -508,12 +501,27 @@ func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) {
"hostinfo": m{"existing": true, "localIndexId": hostinfo.localIndexId, "vpnAddrs": hostinfo.vpnAddrs}}).
Debug("Hostmap vpnIp added")
}
}

func (hm *HostMap) unlockedInnerAddHostInfo(vpnAddr netip.Addr, hostinfo *HostInfo, f *Interface) {
if f.serveDns {
remoteCert := hostinfo.ConnectionState.peerCert
dnsR.Add(remoteCert.Certificate.Name()+".", vpnAddr.String())
}

existing := hm.Hosts[vpnAddr]
hm.Hosts[vpnAddr] = hostinfo

if existing != nil && existing != hostinfo {
hostinfo.next = existing
existing.prev = hostinfo
}

i := 1
check := hostinfo
for check != nil {
if i > MaxHostInfosPerVpnIp {
hm.unlockedDeleteHostInfo(check, false)
hm.unlockedDeleteHostInfo(check)
}
check = check.next
i++
Expand Down
Loading

0 comments on commit 3ed4dd7

Please sign in to comment.