Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi ip hostmapping #1226

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dns_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm

Name: name,
Networks: networks,
UnsafeNetworks: unsafeNetworks,
Expand Down
42 changes: 33 additions & 9 deletions hostmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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])
}
}
}

Expand Down
47 changes: 17 additions & 30 deletions lighthouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"net"
"net/netip"
"slices"
"strconv"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -856,17 +855,15 @@ 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 {
var relays []*Addr
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")
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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])
}

Expand Down
17 changes: 9 additions & 8 deletions overlay/tun_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()),
Expand Down
Loading