diff --git a/dns_server.go b/dns_server.go index 1b97d7a4f..587feb85d 100644 --- a/dns_server.go +++ b/dns_server.go @@ -67,6 +67,7 @@ func (d *dnsRecords) QueryCert(data string) string { return string(b) } +// todo this needs multi-ip love func (d *dnsRecords) Add(host, data string) { d.Lock() defer d.Unlock() diff --git a/e2e/helpers.go b/e2e/helpers.go index c0893aca2..eadca0021 100644 --- a/e2e/helpers.go +++ b/e2e/helpers.go @@ -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: cert.Version2, //TODO: you changed this to discover standard e2e tests that would fail and you found some. DO NOT COMMIT THIS CHANGE Name: name, Networks: networks, UnsafeNetworks: unsafeNetworks, diff --git a/hostmap.go b/hostmap.go index 63601ee37..9e13ad25c 100644 --- a/hostmap.go +++ b/hostmap.go @@ -321,6 +321,7 @@ func (hm *HostMap) MakePrimary(hostinfo *HostInfo) { } func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) { + //don't need to iterate here -- all vpnaddr entries should be the same oldHostinfo := hm.Hosts[hostinfo.vpnAddrs[0]] if oldHostinfo == hostinfo { return @@ -334,7 +335,10 @@ func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) { hostinfo.next.prev = hostinfo.prev } - hm.Hosts[hostinfo.vpnAddrs[0]] = hostinfo + //do need to iterate here, since we're setting the pointer value + for i := range hostinfo.vpnAddrs { + hm.Hosts[hostinfo.vpnAddrs[i]] = hostinfo + } if oldHostinfo == nil { return @@ -346,17 +350,23 @@ func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) { } func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo, dontRecurse bool) { + //don't need to iterate here -- all vpnaddr entries should be the same 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 - delete(hm.Hosts, hostinfo.vpnAddrs[0]) + for i := range hostinfo.vpnAddrs { + delete(hm.Hosts, hostinfo.vpnAddrs[i]) + } if len(hm.Hosts) == 0 { hm.Hosts = map[netip.Addr]*HostInfo{} } if hostinfo.next != nil { // We had more than 1 hostinfo at this vpnip, promote the next in the list to primary - hm.Hosts[hostinfo.vpnAddrs[0]] = hostinfo.next + for i := range hostinfo.vpnAddrs { + hm.Hosts[hostinfo.vpnAddrs[i]] = hostinfo.next + } + // It is primary, there is no previous hostinfo now hostinfo.next.prev = nil } @@ -487,17 +497,28 @@ 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) { + //todo this must be bad right? + if f.serveDns { remoteCert := hostinfo.ConnectionState.peerCert + //todo multi-IP! dnsR.Add(remoteCert.Certificate.Name()+".", remoteCert.Certificate.Networks()[0].Addr().String()) } - existing := hm.Hosts[hostinfo.vpnAddrs[0]] - hm.Hosts[hostinfo.vpnAddrs[0]] = hostinfo + //pull an existing record if we have one for any vpnip + var existing *HostInfo + for i := range hostinfo.vpnAddrs { + if hm.Hosts[hostinfo.vpnAddrs[i]] != nil { + existing = hm.Hosts[hostinfo.vpnAddrs[i]] - if existing != nil { - hostinfo.next = existing - existing.prev = hostinfo + hostinfo.next = existing + existing.prev = hostinfo + break + } + } + //set all the vpnips to use the new hostinfo + for i := range hostinfo.vpnAddrs { + hm.Hosts[hostinfo.vpnAddrs[i]] = hostinfo } hm.Indexes[hostinfo.localIndexId] = hostinfo @@ -579,7 +600,10 @@ func (i *HostInfo) TryPromoteBest(preferredRanges []netip.Prefix, ifce *Interfac } i.nextLHQuery.Store(now + ifce.reQueryWait.Load()) - ifce.lightHouse.QueryServer(i.vpnAddrs[0]) + //TODO do we need to query all entries? Hosts can gain and lose addresses + for j := range i.vpnAddrs { + ifce.lightHouse.QueryServer(i.vpnAddrs[j]) + } } } diff --git a/lighthouse.go b/lighthouse.go index 5549e8386..df4468882 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -7,7 +7,6 @@ import ( "fmt" "net" "net/netip" - "slices" "strconv" "sync" "sync/atomic" @@ -856,7 +855,7 @@ func (lh *LightHouse) SendUpdate() { relays = append(relays, binary.BigEndian.Uint32(b[:])) } - //TODO: need an ipv4 vpn addr to use + //TODO: need an ipv4 vpn addr to use -- ideally the one in our v1 cert msg.Details.OldRelayVpnAddrs = relays } else if v == 2 { @@ -864,9 +863,7 @@ func (lh *LightHouse) SendUpdate() { for _, r := range lh.GetRelaysForMe() { relays = append(relays, netAddrToProtoAddr(r)) } - - //TODO: need a vpn addr to use - + //in v2, we don't use vpnaddrs in the host update -- we let the lighthouse use our cert. } else { panic("protocol version not supported") } @@ -1167,38 +1164,28 @@ func (lhh *LightHouseHandler) handleHostUpdateNotification(n *NebulaMeta, fromVp lhh.l.Debugln("I am not a lighthouse, do not take host updates: ", fromVpnAddrs) } return + } else if lhh.l.Level >= logrus.DebugLevel { + lhh.l.WithField("vpnAddrs", fromVpnAddrs).WithField("details", n.Details.String()).Debugln("got HostUpdateNotification") } - //Simple check that the host sent this not someone else - var detailsVpnIp netip.Addr var useVersion cert.Version - if n.Details.OldVpnAddr != 0 { - b := [4]byte{} - binary.BigEndian.PutUint32(b[:], n.Details.OldVpnAddr) - detailsVpnIp = netip.AddrFrom4(b) + if n.Details.OldVpnAddr != 0 { //todo this is a bit of a hack useVersion = 1 - } else if n.Details.VpnAddr != nil { - detailsVpnIp = protoAddrToNetAddr(n.Details.VpnAddr) + } else { useVersion = 2 } - //todo hosts with only v2 certs cannot provide their ipv6 addr when contacting the lighthouse via v4? - //todo why do we care about the vpnip in the packet? We know where it came from, right? - - if !slices.Contains(fromVpnAddrs, detailsVpnIp) { - if lhh.l.Level >= logrus.DebugLevel { - lhh.l.WithField("vpnAddrs", fromVpnAddrs).WithField("answer", detailsVpnIp).Debugln("Host sent invalid update") - } - return - } - lhh.lh.Lock() am := lhh.lh.unlockedGetRemoteList(fromVpnAddrs) am.Lock() lhh.lh.Unlock() - am.unlockedSetV4(fromVpnAddrs[0], detailsVpnIp, n.Details.V4AddrPorts, lhh.lh.unlockedShouldAddV4) - am.unlockedSetV6(fromVpnAddrs[0], detailsVpnIp, n.Details.V6AddrPorts, lhh.lh.unlockedShouldAddV6) + //todo why do we care about the vpnip in the packet? We know where it came from, right? + //todo if the zeroth IP changes things will get weird + for i := range fromVpnAddrs { + am.unlockedSetV4(fromVpnAddrs[0], fromVpnAddrs[i], n.Details.V4AddrPorts, lhh.lh.unlockedShouldAddV4) + am.unlockedSetV6(fromVpnAddrs[0], fromVpnAddrs[i], n.Details.V6AddrPorts, lhh.lh.unlockedShouldAddV6) + } var relays []netip.Addr if len(n.Details.OldRelayVpnAddrs) > 0 { @@ -1215,9 +1202,11 @@ func (lhh *LightHouseHandler) handleHostUpdateNotification(n *NebulaMeta, fromVp } } - am.unlockedSetRelay(fromVpnAddrs[0], detailsVpnIp, relays) + //todo does this need to be per-vpnip? + am.unlockedSetRelay(fromVpnAddrs[0], fromVpnAddrs[0], relays) am.Unlock() + //begin sending update notif ack n = lhh.resetMeta() n.Type = NebulaMeta_HostUpdateNotificationAck @@ -1229,11 +1218,8 @@ func (lhh *LightHouseHandler) handleHostUpdateNotification(n *NebulaMeta, fromVp vpnIpB := fromVpnAddrs[0].As4() n.Details.OldVpnAddr = binary.BigEndian.Uint32(vpnIpB[:]) - } else if useVersion == cert.Version2 { - n.Details.VpnAddr = netAddrToProtoAddr(fromVpnAddrs[0]) - } else { - panic("unsupported version") + n.Details.VpnAddr = netAddrToProtoAddr(fromVpnAddrs[0]) } ln, err := n.MarshalTo(lhh.pb) @@ -1243,6 +1229,7 @@ func (lhh *LightHouseHandler) handleHostUpdateNotification(n *NebulaMeta, fromVp } lhh.lh.metricTx(NebulaMeta_HostUpdateNotificationAck, 1) + //all vpnaddrs transit the same tunnel, so we only need to send one w.SendMessageToVpnIp(header.LightHouse, 0, fromVpnAddrs[0], lhh.pb[:ln], lhh.nb, lhh.out[:0]) } diff --git a/overlay/tun_linux.go b/overlay/tun_linux.go index 08c65b4e0..2900881e5 100644 --- a/overlay/tun_linux.go +++ b/overlay/tun_linux.go @@ -367,19 +367,21 @@ func (t *tun) Activate() error { return fmt.Errorf("failed to bring the tun device up: %s", err) } - // Run the interface - ifrf.Flags = ifrf.Flags | unix.IFF_UP | unix.IFF_RUNNING - if err = ioctl(t.ioctlFd, unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil { - return fmt.Errorf("failed to run tun device: %s", err) - } - //set route MTU for i := range t.vpnNetworks { if err = t.setDefaultRoute(t.vpnNetworks[i]); err != nil { - return fmt.Errorf("failed to set default route MTU: %w", err) + //todo why does this only work sometimes? + //avoid crashing for now + t.l.WithError(err).Error("failed to set default route MTU") } } + // Run the interface + ifrf.Flags = ifrf.Flags | unix.IFF_UP | unix.IFF_RUNNING + if err = ioctl(t.ioctlFd, unix.SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil { + return fmt.Errorf("failed to run tun device: %s", err) + } + // Set the routes if err = t.addRoutes(false); err != nil { return err @@ -401,7 +403,6 @@ func (t *tun) setMTU() { func (t *tun) setDefaultRoute(cidr netip.Prefix) error { // Default route - dr := &net.IPNet{ IP: cidr.Masked().Addr().AsSlice(), Mask: net.CIDRMask(cidr.Bits(), cidr.Addr().BitLen()),