Pārlūkot izejas kodu

Add a way to trigger punch backs via lighthouse (#394)

Nathan Brown 4 gadi atpakaļ
vecāks
revīzija
b6234abfb3
6 mainītis faili ar 62 papildinājumiem un 43 dzēšanām
  1. 6 0
      control.go
  2. 5 0
      hostmap.go
  3. 12 0
      inside.go
  4. 4 1
      interface.go
  5. 34 41
      lighthouse.go
  6. 1 1
      main.go

+ 6 - 0
control.go

@@ -65,6 +65,12 @@ func (c *Control) ShutdownBlock() {
 // RebindUDPServer asks the UDP listener to rebind it's listener. Mainly used on mobile clients when interfaces change
 func (c *Control) RebindUDPServer() {
 	_ = c.f.outside.Rebind()
+
+	// Trigger a lighthouse update, useful for mobile clients that should have an update interval of 0
+	c.f.lightHouse.SendUpdate(c.f)
+
+	// Let the main interface know that we rebound so that underlying tunnels know to trigger punches from their remotes
+	c.f.rebindCount++
 }
 
 // ListHostmap returns details about the actual or pending (handshaking) hostmap

+ 5 - 0
hostmap.go

@@ -51,6 +51,11 @@ type HostInfo struct {
 	recvError         int
 	remoteCidr        *CIDRTree
 
+	// lastRebindCount is the other side of Interface.rebindCount, if these values don't match then we need to ask LH
+	// for a punch from the remote end of this tunnel. The goal being to prime their conntrack for our traffic just like
+	// with a handshake
+	lastRebindCount int8
+
 	lastRoam       time.Time
 	lastRoamRemote *udpAddr
 }

+ 12 - 0
inside.go

@@ -229,6 +229,18 @@ func (f *Interface) sendNoMetrics(t NebulaMessageType, st NebulaMessageSubType,
 	out = HeaderEncode(out, Version, uint8(t), uint8(st), hostinfo.remoteIndexId, c)
 	f.connectionManager.Out(hostinfo.hostId)
 
+	// Query our LH if we haven't since the last time we've been rebound, this will cause the remote to punch against
+	// all our IPs and enable a faster roaming.
+	if hostinfo.lastRebindCount != f.rebindCount {
+		//NOTE: there is an update hole if a tunnel isn't used and exactly 256 rebinds occur before the tunnel is
+		// finally used again. This tunnel would eventually be torn down and recreated if this action didn't help.
+		f.lightHouse.Query(hostinfo.hostId, f)
+		hostinfo.lastRebindCount = f.rebindCount
+		if l.Level >= logrus.DebugLevel {
+			l.WithField("vpnIp", hostinfo.hostId).Debug("Lighthouse update triggered for punch due to rebind counter")
+		}
+	}
+
 	out, err = ci.eKey.EncryptDanger(out, out, p, c, nb)
 	//TODO: see above note on lock
 	//ci.writeLock.Unlock()

+ 4 - 1
interface.go

@@ -61,7 +61,10 @@ type Interface struct {
 	dropMulticast      bool
 	udpBatchSize       int
 	routines           int
-	version            string
+
+	// rebindCount is used to decide if an active tunnel should trigger a punch notification through a lighthouse
+	rebindCount int8
+	version     string
 
 	conntrackCacheTimeout time.Duration
 

+ 34 - 41
lighthouse.go

@@ -41,7 +41,7 @@ type LightHouse struct {
 	staticList  map[uint32]struct{}
 	lighthouses map[uint32]struct{}
 	interval    int
-	nebulaPort  int
+	nebulaPort  uint32
 	punchBack   bool
 	punchDelay  time.Duration
 
@@ -54,7 +54,7 @@ type EncWriter interface {
 	SendMessageToAll(t NebulaMessageType, st NebulaMessageSubType, vpnIp uint32, p, nb, out []byte)
 }
 
-func NewLightHouse(amLighthouse bool, myIp uint32, ips []uint32, interval int, nebulaPort int, pc *udpConn, punchBack bool, punchDelay time.Duration, metricsEnabled bool) *LightHouse {
+func NewLightHouse(amLighthouse bool, myIp uint32, ips []uint32, interval int, nebulaPort uint32, pc *udpConn, punchBack bool, punchDelay time.Duration, metricsEnabled bool) *LightHouse {
 	h := LightHouse{
 		amLighthouse: amLighthouse,
 		myIp:         myIp,
@@ -208,12 +208,6 @@ func (lh *LightHouse) IsLighthouseIP(vpnIP uint32) bool {
 	return false
 }
 
-// Quick generators for protobuf
-
-func NewLhQueryByIpString(VpnIp string) *NebulaMeta {
-	return NewLhQueryByInt(ip2int(net.ParseIP(VpnIp)))
-}
-
 func NewLhQueryByInt(VpnIp uint32) *NebulaMeta {
 	return &NebulaMeta{
 		Type: NebulaMeta_HostQuery,
@@ -223,15 +217,10 @@ func NewLhQueryByInt(VpnIp uint32) *NebulaMeta {
 	}
 }
 
-func NewLhWhoami() *NebulaMeta {
-	return &NebulaMeta{
-		Type:    NebulaMeta_HostWhoami,
-		Details: &NebulaMetaDetails{},
-	}
+func NewIpAndPort(ip net.IP, port uint32) IpAndPort {
+	return IpAndPort{Ip: ip2int(ip), Port: port}
 }
 
-// End Quick generators for protobuf
-
 func NewIpAndPortFromUDPAddr(addr udpAddr) IpAndPort {
 	return IpAndPort{Ip: udp2ipInt(&addr), Port: uint32(addr.Port)}
 }
@@ -242,36 +231,40 @@ func (lh *LightHouse) LhUpdateWorker(f EncWriter) {
 	}
 
 	for {
-		ipp := []*IpAndPort{}
+		lh.SendUpdate(f)
+		time.Sleep(time.Second * time.Duration(lh.interval))
+	}
+}
 
-		for _, e := range *localIps(lh.localAllowList) {
-			// Only add IPs that aren't my VPN/tun IP
-			if ip2int(e) != lh.myIp {
-				ipp = append(ipp, &IpAndPort{Ip: ip2int(e), Port: uint32(lh.nebulaPort)})
-				//fmt.Println(e)
-			}
-		}
-		m := &NebulaMeta{
-			Type: NebulaMeta_HostUpdateNotification,
-			Details: &NebulaMetaDetails{
-				VpnIp:      lh.myIp,
-				IpAndPorts: ipp,
-			},
-		}
+func (lh *LightHouse) SendUpdate(f EncWriter) {
+	var ipps []*IpAndPort
 
-		lh.metricTx(NebulaMeta_HostUpdateNotification, int64(len(lh.lighthouses)))
-		nb := make([]byte, 12, 12)
-		out := make([]byte, mtu)
-		for vpnIp := range lh.lighthouses {
-			mm, err := proto.Marshal(m)
-			if err != nil {
-				l.Debugf("Invalid marshal to update")
-			}
-			//l.Error("LIGHTHOUSE PACKET SEND", mm)
-			f.SendMessageToVpnIp(lightHouse, 0, vpnIp, mm, nb, out)
+	for _, e := range *localIps(lh.localAllowList) {
+		// Only add IPs that aren't my VPN/tun IP
+		if ip2int(e) != lh.myIp {
+			ipp := NewIpAndPort(e, lh.nebulaPort)
+			ipps = append(ipps, &ipp)
+		}
+	}
+	m := &NebulaMeta{
+		Type: NebulaMeta_HostUpdateNotification,
+		Details: &NebulaMetaDetails{
+			VpnIp:      lh.myIp,
+			IpAndPorts: ipps,
+		},
+	}
 
+	lh.metricTx(NebulaMeta_HostUpdateNotification, int64(len(lh.lighthouses)))
+	nb := make([]byte, 12, 12)
+	out := make([]byte, mtu)
+	for vpnIp := range lh.lighthouses {
+		mm, err := proto.Marshal(m)
+		if err != nil {
+			l.Debugf("Invalid marshal to update")
 		}
-		time.Sleep(time.Second * time.Duration(lh.interval))
+		//l.Error("LIGHTHOUSE PACKET SEND", mm)
+		f.SendMessageToVpnIp(lightHouse, 0, vpnIp, mm, nb, out)
+
 	}
 }
 

+ 1 - 1
main.go

@@ -268,7 +268,7 @@ func Main(config *Config, configTest bool, buildVersion string, logger *logrus.L
 		lighthouseHosts,
 		//TODO: change to a duration
 		config.GetInt("lighthouse.interval", 10),
-		port,
+		uint32(port),
 		udpConns[0],
 		punchy.Respond,
 		punchy.Delay,