github.com/slackhq/nebula@v1.9.0/handshake_manager.go (about) 1 package nebula 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 "encoding/binary" 8 "errors" 9 "net" 10 "sync" 11 "time" 12 13 "github.com/rcrowley/go-metrics" 14 "github.com/sirupsen/logrus" 15 "github.com/slackhq/nebula/header" 16 "github.com/slackhq/nebula/iputil" 17 "github.com/slackhq/nebula/udp" 18 ) 19 20 const ( 21 DefaultHandshakeTryInterval = time.Millisecond * 100 22 DefaultHandshakeRetries = 10 23 DefaultHandshakeTriggerBuffer = 64 24 DefaultUseRelays = true 25 ) 26 27 var ( 28 defaultHandshakeConfig = HandshakeConfig{ 29 tryInterval: DefaultHandshakeTryInterval, 30 retries: DefaultHandshakeRetries, 31 triggerBuffer: DefaultHandshakeTriggerBuffer, 32 useRelays: DefaultUseRelays, 33 } 34 ) 35 36 type HandshakeConfig struct { 37 tryInterval time.Duration 38 retries int 39 triggerBuffer int 40 useRelays bool 41 42 messageMetrics *MessageMetrics 43 } 44 45 type HandshakeManager struct { 46 // Mutex for interacting with the vpnIps and indexes maps 47 sync.RWMutex 48 49 vpnIps map[iputil.VpnIp]*HandshakeHostInfo 50 indexes map[uint32]*HandshakeHostInfo 51 52 mainHostMap *HostMap 53 lightHouse *LightHouse 54 outside udp.Conn 55 config HandshakeConfig 56 OutboundHandshakeTimer *LockingTimerWheel[iputil.VpnIp] 57 messageMetrics *MessageMetrics 58 metricInitiated metrics.Counter 59 metricTimedOut metrics.Counter 60 f *Interface 61 l *logrus.Logger 62 63 // can be used to trigger outbound handshake for the given vpnIp 64 trigger chan iputil.VpnIp 65 } 66 67 type HandshakeHostInfo struct { 68 sync.Mutex 69 70 startTime time.Time // Time that we first started trying with this handshake 71 ready bool // Is the handshake ready 72 counter int // How many attempts have we made so far 73 lastRemotes []*udp.Addr // Remotes that we sent to during the previous attempt 74 packetStore []*cachedPacket // A set of packets to be transmitted once the handshake completes 75 76 hostinfo *HostInfo 77 } 78 79 func (hh *HandshakeHostInfo) cachePacket(l *logrus.Logger, t header.MessageType, st header.MessageSubType, packet []byte, f packetCallback, m *cachedPacketMetrics) { 80 if len(hh.packetStore) < 100 { 81 tempPacket := make([]byte, len(packet)) 82 copy(tempPacket, packet) 83 84 hh.packetStore = append(hh.packetStore, &cachedPacket{t, st, f, tempPacket}) 85 if l.Level >= logrus.DebugLevel { 86 hh.hostinfo.logger(l). 87 WithField("length", len(hh.packetStore)). 88 WithField("stored", true). 89 Debugf("Packet store") 90 } 91 92 } else { 93 m.dropped.Inc(1) 94 95 if l.Level >= logrus.DebugLevel { 96 hh.hostinfo.logger(l). 97 WithField("length", len(hh.packetStore)). 98 WithField("stored", false). 99 Debugf("Packet store") 100 } 101 } 102 } 103 104 func NewHandshakeManager(l *logrus.Logger, mainHostMap *HostMap, lightHouse *LightHouse, outside udp.Conn, config HandshakeConfig) *HandshakeManager { 105 return &HandshakeManager{ 106 vpnIps: map[iputil.VpnIp]*HandshakeHostInfo{}, 107 indexes: map[uint32]*HandshakeHostInfo{}, 108 mainHostMap: mainHostMap, 109 lightHouse: lightHouse, 110 outside: outside, 111 config: config, 112 trigger: make(chan iputil.VpnIp, config.triggerBuffer), 113 OutboundHandshakeTimer: NewLockingTimerWheel[iputil.VpnIp](config.tryInterval, hsTimeout(config.retries, config.tryInterval)), 114 messageMetrics: config.messageMetrics, 115 metricInitiated: metrics.GetOrRegisterCounter("handshake_manager.initiated", nil), 116 metricTimedOut: metrics.GetOrRegisterCounter("handshake_manager.timed_out", nil), 117 l: l, 118 } 119 } 120 121 func (c *HandshakeManager) Run(ctx context.Context) { 122 clockSource := time.NewTicker(c.config.tryInterval) 123 defer clockSource.Stop() 124 125 for { 126 select { 127 case <-ctx.Done(): 128 return 129 case vpnIP := <-c.trigger: 130 c.handleOutbound(vpnIP, true) 131 case now := <-clockSource.C: 132 c.NextOutboundHandshakeTimerTick(now) 133 } 134 } 135 } 136 137 func (hm *HandshakeManager) HandleIncoming(addr *udp.Addr, via *ViaSender, packet []byte, h *header.H) { 138 // First remote allow list check before we know the vpnIp 139 if addr != nil { 140 if !hm.lightHouse.GetRemoteAllowList().AllowUnknownVpnIp(addr.IP) { 141 hm.l.WithField("udpAddr", addr).Debug("lighthouse.remote_allow_list denied incoming handshake") 142 return 143 } 144 } 145 146 switch h.Subtype { 147 case header.HandshakeIXPSK0: 148 switch h.MessageCounter { 149 case 1: 150 ixHandshakeStage1(hm.f, addr, via, packet, h) 151 152 case 2: 153 newHostinfo := hm.queryIndex(h.RemoteIndex) 154 tearDown := ixHandshakeStage2(hm.f, addr, via, newHostinfo, packet, h) 155 if tearDown && newHostinfo != nil { 156 hm.DeleteHostInfo(newHostinfo.hostinfo) 157 } 158 } 159 } 160 } 161 162 func (c *HandshakeManager) NextOutboundHandshakeTimerTick(now time.Time) { 163 c.OutboundHandshakeTimer.Advance(now) 164 for { 165 vpnIp, has := c.OutboundHandshakeTimer.Purge() 166 if !has { 167 break 168 } 169 c.handleOutbound(vpnIp, false) 170 } 171 } 172 173 func (hm *HandshakeManager) handleOutbound(vpnIp iputil.VpnIp, lighthouseTriggered bool) { 174 hh := hm.queryVpnIp(vpnIp) 175 if hh == nil { 176 return 177 } 178 hh.Lock() 179 defer hh.Unlock() 180 181 hostinfo := hh.hostinfo 182 // If we are out of time, clean up 183 if hh.counter >= hm.config.retries { 184 hh.hostinfo.logger(hm.l).WithField("udpAddrs", hh.hostinfo.remotes.CopyAddrs(hm.mainHostMap.GetPreferredRanges())). 185 WithField("initiatorIndex", hh.hostinfo.localIndexId). 186 WithField("remoteIndex", hh.hostinfo.remoteIndexId). 187 WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). 188 WithField("durationNs", time.Since(hh.startTime).Nanoseconds()). 189 Info("Handshake timed out") 190 hm.metricTimedOut.Inc(1) 191 hm.DeleteHostInfo(hostinfo) 192 return 193 } 194 195 // Increment the counter to increase our delay, linear backoff 196 hh.counter++ 197 198 // Check if we have a handshake packet to transmit yet 199 if !hh.ready { 200 if !ixHandshakeStage0(hm.f, hh) { 201 hm.OutboundHandshakeTimer.Add(vpnIp, hm.config.tryInterval*time.Duration(hh.counter)) 202 return 203 } 204 } 205 206 // Get a remotes object if we don't already have one. 207 // This is mainly to protect us as this should never be the case 208 // NB ^ This comment doesn't jive. It's how the thing gets initialized. 209 // It's the common path. Should it update every time, in case a future LH query/queries give us more info? 210 if hostinfo.remotes == nil { 211 hostinfo.remotes = hm.lightHouse.QueryCache(vpnIp) 212 } 213 214 remotes := hostinfo.remotes.CopyAddrs(hm.mainHostMap.GetPreferredRanges()) 215 remotesHaveChanged := !udp.AddrSlice(remotes).Equal(hh.lastRemotes) 216 217 // We only care about a lighthouse trigger if we have new remotes to send to. 218 // This is a very specific optimization for a fast lighthouse reply. 219 if lighthouseTriggered && !remotesHaveChanged { 220 // If we didn't return here a lighthouse could cause us to aggressively send handshakes 221 return 222 } 223 224 hh.lastRemotes = remotes 225 226 // TODO: this will generate a load of queries for hosts with only 1 ip 227 // (such as ones registered to the lighthouse with only a private IP) 228 // So we only do it one time after attempting 5 handshakes already. 229 if len(remotes) <= 1 && hh.counter == 5 { 230 // If we only have 1 remote it is highly likely our query raced with the other host registered within the lighthouse 231 // Our vpnIp here has a tunnel with a lighthouse but has yet to send a host update packet there so we only know about 232 // the learned public ip for them. Query again to short circuit the promotion counter 233 hm.lightHouse.QueryServer(vpnIp) 234 } 235 236 // Send the handshake to all known ips, stage 2 takes care of assigning the hostinfo.remote based on the first to reply 237 var sentTo []*udp.Addr 238 hostinfo.remotes.ForEach(hm.mainHostMap.GetPreferredRanges(), func(addr *udp.Addr, _ bool) { 239 hm.messageMetrics.Tx(header.Handshake, header.MessageSubType(hostinfo.HandshakePacket[0][1]), 1) 240 err := hm.outside.WriteTo(hostinfo.HandshakePacket[0], addr) 241 if err != nil { 242 hostinfo.logger(hm.l).WithField("udpAddr", addr). 243 WithField("initiatorIndex", hostinfo.localIndexId). 244 WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). 245 WithError(err).Error("Failed to send handshake message") 246 247 } else { 248 sentTo = append(sentTo, addr) 249 } 250 }) 251 252 // Don't be too noisy or confusing if we fail to send a handshake - if we don't get through we'll eventually log a timeout, 253 // so only log when the list of remotes has changed 254 if remotesHaveChanged { 255 hostinfo.logger(hm.l).WithField("udpAddrs", sentTo). 256 WithField("initiatorIndex", hostinfo.localIndexId). 257 WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). 258 Info("Handshake message sent") 259 } else if hm.l.IsLevelEnabled(logrus.DebugLevel) { 260 hostinfo.logger(hm.l).WithField("udpAddrs", sentTo). 261 WithField("initiatorIndex", hostinfo.localIndexId). 262 WithField("handshake", m{"stage": 1, "style": "ix_psk0"}). 263 Debug("Handshake message sent") 264 } 265 266 if hm.config.useRelays && len(hostinfo.remotes.relays) > 0 { 267 hostinfo.logger(hm.l).WithField("relays", hostinfo.remotes.relays).Info("Attempt to relay through hosts") 268 // Send a RelayRequest to all known Relay IP's 269 for _, relay := range hostinfo.remotes.relays { 270 // Don't relay to myself, and don't relay through the host I'm trying to connect to 271 if *relay == vpnIp || *relay == hm.lightHouse.myVpnIp { 272 continue 273 } 274 relayHostInfo := hm.mainHostMap.QueryVpnIp(*relay) 275 if relayHostInfo == nil || relayHostInfo.remote == nil { 276 hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Establish tunnel to relay target") 277 hm.f.Handshake(*relay) 278 continue 279 } 280 // Check the relay HostInfo to see if we already established a relay through it 281 if existingRelay, ok := relayHostInfo.relayState.QueryRelayForByIp(vpnIp); ok { 282 switch existingRelay.State { 283 case Established: 284 hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Send handshake via relay") 285 hm.f.SendVia(relayHostInfo, existingRelay, hostinfo.HandshakePacket[0], make([]byte, 12), make([]byte, mtu), false) 286 case Requested: 287 hostinfo.logger(hm.l).WithField("relay", relay.String()).Info("Re-send CreateRelay request") 288 // Re-send the CreateRelay request, in case the previous one was lost. 289 m := NebulaControl{ 290 Type: NebulaControl_CreateRelayRequest, 291 InitiatorRelayIndex: existingRelay.LocalIndex, 292 RelayFromIp: uint32(hm.lightHouse.myVpnIp), 293 RelayToIp: uint32(vpnIp), 294 } 295 msg, err := m.Marshal() 296 if err != nil { 297 hostinfo.logger(hm.l). 298 WithError(err). 299 Error("Failed to marshal Control message to create relay") 300 } else { 301 // This must send over the hostinfo, not over hm.Hosts[ip] 302 hm.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) 303 hm.l.WithFields(logrus.Fields{ 304 "relayFrom": hm.lightHouse.myVpnIp, 305 "relayTo": vpnIp, 306 "initiatorRelayIndex": existingRelay.LocalIndex, 307 "relay": *relay}). 308 Info("send CreateRelayRequest") 309 } 310 default: 311 hostinfo.logger(hm.l). 312 WithField("vpnIp", vpnIp). 313 WithField("state", existingRelay.State). 314 WithField("relay", relayHostInfo.vpnIp). 315 Errorf("Relay unexpected state") 316 } 317 } else { 318 // No relays exist or requested yet. 319 if relayHostInfo.remote != nil { 320 idx, err := AddRelay(hm.l, relayHostInfo, hm.mainHostMap, vpnIp, nil, TerminalType, Requested) 321 if err != nil { 322 hostinfo.logger(hm.l).WithField("relay", relay.String()).WithError(err).Info("Failed to add relay to hostmap") 323 } 324 325 m := NebulaControl{ 326 Type: NebulaControl_CreateRelayRequest, 327 InitiatorRelayIndex: idx, 328 RelayFromIp: uint32(hm.lightHouse.myVpnIp), 329 RelayToIp: uint32(vpnIp), 330 } 331 msg, err := m.Marshal() 332 if err != nil { 333 hostinfo.logger(hm.l). 334 WithError(err). 335 Error("Failed to marshal Control message to create relay") 336 } else { 337 hm.f.SendMessageToHostInfo(header.Control, 0, relayHostInfo, msg, make([]byte, 12), make([]byte, mtu)) 338 hm.l.WithFields(logrus.Fields{ 339 "relayFrom": hm.lightHouse.myVpnIp, 340 "relayTo": vpnIp, 341 "initiatorRelayIndex": idx, 342 "relay": *relay}). 343 Info("send CreateRelayRequest") 344 } 345 } 346 } 347 } 348 } 349 350 // If a lighthouse triggered this attempt then we are still in the timer wheel and do not need to re-add 351 if !lighthouseTriggered { 352 hm.OutboundHandshakeTimer.Add(vpnIp, hm.config.tryInterval*time.Duration(hh.counter)) 353 } 354 } 355 356 // GetOrHandshake will try to find a hostinfo with a fully formed tunnel or start a new handshake if one is not present 357 // The 2nd argument will be true if the hostinfo is ready to transmit traffic 358 func (hm *HandshakeManager) GetOrHandshake(vpnIp iputil.VpnIp, cacheCb func(*HandshakeHostInfo)) (*HostInfo, bool) { 359 // Check the main hostmap and maintain a read lock if our host is not there 360 hm.mainHostMap.RLock() 361 if h, ok := hm.mainHostMap.Hosts[vpnIp]; ok { 362 hm.mainHostMap.RUnlock() 363 // Do not attempt promotion if you are a lighthouse 364 if !hm.lightHouse.amLighthouse { 365 h.TryPromoteBest(hm.mainHostMap.GetPreferredRanges(), hm.f) 366 } 367 return h, true 368 } 369 370 defer hm.mainHostMap.RUnlock() 371 return hm.StartHandshake(vpnIp, cacheCb), false 372 } 373 374 // StartHandshake will ensure a handshake is currently being attempted for the provided vpn ip 375 func (hm *HandshakeManager) StartHandshake(vpnIp iputil.VpnIp, cacheCb func(*HandshakeHostInfo)) *HostInfo { 376 hm.Lock() 377 378 if hh, ok := hm.vpnIps[vpnIp]; ok { 379 // We are already trying to handshake with this vpn ip 380 if cacheCb != nil { 381 cacheCb(hh) 382 } 383 hm.Unlock() 384 return hh.hostinfo 385 } 386 387 hostinfo := &HostInfo{ 388 vpnIp: vpnIp, 389 HandshakePacket: make(map[uint8][]byte, 0), 390 relayState: RelayState{ 391 relays: map[iputil.VpnIp]struct{}{}, 392 relayForByIp: map[iputil.VpnIp]*Relay{}, 393 relayForByIdx: map[uint32]*Relay{}, 394 }, 395 } 396 397 hh := &HandshakeHostInfo{ 398 hostinfo: hostinfo, 399 startTime: time.Now(), 400 } 401 hm.vpnIps[vpnIp] = hh 402 hm.metricInitiated.Inc(1) 403 hm.OutboundHandshakeTimer.Add(vpnIp, hm.config.tryInterval) 404 405 if cacheCb != nil { 406 cacheCb(hh) 407 } 408 409 // If this is a static host, we don't need to wait for the HostQueryReply 410 // We can trigger the handshake right now 411 _, doTrigger := hm.lightHouse.GetStaticHostList()[vpnIp] 412 if !doTrigger { 413 // Add any calculated remotes, and trigger early handshake if one found 414 doTrigger = hm.lightHouse.addCalculatedRemotes(vpnIp) 415 } 416 417 if doTrigger { 418 select { 419 case hm.trigger <- vpnIp: 420 default: 421 } 422 } 423 424 hm.Unlock() 425 hm.lightHouse.QueryServer(vpnIp) 426 return hostinfo 427 } 428 429 var ( 430 ErrExistingHostInfo = errors.New("existing hostinfo") 431 ErrAlreadySeen = errors.New("already seen") 432 ErrLocalIndexCollision = errors.New("local index collision") 433 ) 434 435 // CheckAndComplete checks for any conflicts in the main and pending hostmap 436 // before adding hostinfo to main. If err is nil, it was added. Otherwise err will be: 437 // 438 // ErrAlreadySeen if we already have an entry in the hostmap that has seen the 439 // exact same handshake packet 440 // 441 // ErrExistingHostInfo if we already have an entry in the hostmap for this 442 // VpnIp and the new handshake was older than the one we currently have 443 // 444 // ErrLocalIndexCollision if we already have an entry in the main or pending 445 // hostmap for the hostinfo.localIndexId. 446 func (c *HandshakeManager) CheckAndComplete(hostinfo *HostInfo, handshakePacket uint8, f *Interface) (*HostInfo, error) { 447 c.mainHostMap.Lock() 448 defer c.mainHostMap.Unlock() 449 c.Lock() 450 defer c.Unlock() 451 452 // Check if we already have a tunnel with this vpn ip 453 existingHostInfo, found := c.mainHostMap.Hosts[hostinfo.vpnIp] 454 if found && existingHostInfo != nil { 455 testHostInfo := existingHostInfo 456 for testHostInfo != nil { 457 // Is it just a delayed handshake packet? 458 if bytes.Equal(hostinfo.HandshakePacket[handshakePacket], testHostInfo.HandshakePacket[handshakePacket]) { 459 return testHostInfo, ErrAlreadySeen 460 } 461 462 testHostInfo = testHostInfo.next 463 } 464 465 // Is this a newer handshake? 466 if existingHostInfo.lastHandshakeTime >= hostinfo.lastHandshakeTime && !existingHostInfo.ConnectionState.initiator { 467 return existingHostInfo, ErrExistingHostInfo 468 } 469 470 existingHostInfo.logger(c.l).Info("Taking new handshake") 471 } 472 473 existingIndex, found := c.mainHostMap.Indexes[hostinfo.localIndexId] 474 if found { 475 // We have a collision, but for a different hostinfo 476 return existingIndex, ErrLocalIndexCollision 477 } 478 479 existingPendingIndex, found := c.indexes[hostinfo.localIndexId] 480 if found && existingPendingIndex.hostinfo != hostinfo { 481 // We have a collision, but for a different hostinfo 482 return existingIndex, ErrLocalIndexCollision 483 } 484 485 existingRemoteIndex, found := c.mainHostMap.RemoteIndexes[hostinfo.remoteIndexId] 486 if found && existingRemoteIndex != nil && existingRemoteIndex.vpnIp != hostinfo.vpnIp { 487 // We have a collision, but this can happen since we can't control 488 // the remote ID. Just log about the situation as a note. 489 hostinfo.logger(c.l). 490 WithField("remoteIndex", hostinfo.remoteIndexId).WithField("collision", existingRemoteIndex.vpnIp). 491 Info("New host shadows existing host remoteIndex") 492 } 493 494 c.mainHostMap.unlockedAddHostInfo(hostinfo, f) 495 return existingHostInfo, nil 496 } 497 498 // Complete is a simpler version of CheckAndComplete when we already know we 499 // won't have a localIndexId collision because we already have an entry in the 500 // pendingHostMap. An existing hostinfo is returned if there was one. 501 func (hm *HandshakeManager) Complete(hostinfo *HostInfo, f *Interface) { 502 hm.mainHostMap.Lock() 503 defer hm.mainHostMap.Unlock() 504 hm.Lock() 505 defer hm.Unlock() 506 507 existingRemoteIndex, found := hm.mainHostMap.RemoteIndexes[hostinfo.remoteIndexId] 508 if found && existingRemoteIndex != nil { 509 // We have a collision, but this can happen since we can't control 510 // the remote ID. Just log about the situation as a note. 511 hostinfo.logger(hm.l). 512 WithField("remoteIndex", hostinfo.remoteIndexId).WithField("collision", existingRemoteIndex.vpnIp). 513 Info("New host shadows existing host remoteIndex") 514 } 515 516 // We need to remove from the pending hostmap first to avoid undoing work when after to the main hostmap. 517 hm.unlockedDeleteHostInfo(hostinfo) 518 hm.mainHostMap.unlockedAddHostInfo(hostinfo, f) 519 } 520 521 // allocateIndex generates a unique localIndexId for this HostInfo 522 // and adds it to the pendingHostMap. Will error if we are unable to generate 523 // a unique localIndexId 524 func (hm *HandshakeManager) allocateIndex(hh *HandshakeHostInfo) error { 525 hm.mainHostMap.RLock() 526 defer hm.mainHostMap.RUnlock() 527 hm.Lock() 528 defer hm.Unlock() 529 530 for i := 0; i < 32; i++ { 531 index, err := generateIndex(hm.l) 532 if err != nil { 533 return err 534 } 535 536 _, inPending := hm.indexes[index] 537 _, inMain := hm.mainHostMap.Indexes[index] 538 539 if !inMain && !inPending { 540 hh.hostinfo.localIndexId = index 541 hm.indexes[index] = hh 542 return nil 543 } 544 } 545 546 return errors.New("failed to generate unique localIndexId") 547 } 548 549 func (c *HandshakeManager) DeleteHostInfo(hostinfo *HostInfo) { 550 c.Lock() 551 defer c.Unlock() 552 c.unlockedDeleteHostInfo(hostinfo) 553 } 554 555 func (c *HandshakeManager) unlockedDeleteHostInfo(hostinfo *HostInfo) { 556 delete(c.vpnIps, hostinfo.vpnIp) 557 if len(c.vpnIps) == 0 { 558 c.vpnIps = map[iputil.VpnIp]*HandshakeHostInfo{} 559 } 560 561 delete(c.indexes, hostinfo.localIndexId) 562 if len(c.vpnIps) == 0 { 563 c.indexes = map[uint32]*HandshakeHostInfo{} 564 } 565 566 if c.l.Level >= logrus.DebugLevel { 567 c.l.WithField("hostMap", m{"mapTotalSize": len(c.vpnIps), 568 "vpnIp": hostinfo.vpnIp, "indexNumber": hostinfo.localIndexId, "remoteIndexNumber": hostinfo.remoteIndexId}). 569 Debug("Pending hostmap hostInfo deleted") 570 } 571 } 572 573 func (hm *HandshakeManager) QueryVpnIp(vpnIp iputil.VpnIp) *HostInfo { 574 hh := hm.queryVpnIp(vpnIp) 575 if hh != nil { 576 return hh.hostinfo 577 } 578 return nil 579 580 } 581 582 func (hm *HandshakeManager) queryVpnIp(vpnIp iputil.VpnIp) *HandshakeHostInfo { 583 hm.RLock() 584 defer hm.RUnlock() 585 return hm.vpnIps[vpnIp] 586 } 587 588 func (hm *HandshakeManager) QueryIndex(index uint32) *HostInfo { 589 hh := hm.queryIndex(index) 590 if hh != nil { 591 return hh.hostinfo 592 } 593 return nil 594 } 595 596 func (hm *HandshakeManager) queryIndex(index uint32) *HandshakeHostInfo { 597 hm.RLock() 598 defer hm.RUnlock() 599 return hm.indexes[index] 600 } 601 602 func (c *HandshakeManager) GetPreferredRanges() []*net.IPNet { 603 return c.mainHostMap.GetPreferredRanges() 604 } 605 606 func (c *HandshakeManager) ForEachVpnIp(f controlEach) { 607 c.RLock() 608 defer c.RUnlock() 609 610 for _, v := range c.vpnIps { 611 f(v.hostinfo) 612 } 613 } 614 615 func (c *HandshakeManager) ForEachIndex(f controlEach) { 616 c.RLock() 617 defer c.RUnlock() 618 619 for _, v := range c.indexes { 620 f(v.hostinfo) 621 } 622 } 623 624 func (c *HandshakeManager) EmitStats() { 625 c.RLock() 626 hostLen := len(c.vpnIps) 627 indexLen := len(c.indexes) 628 c.RUnlock() 629 630 metrics.GetOrRegisterGauge("hostmap.pending.hosts", nil).Update(int64(hostLen)) 631 metrics.GetOrRegisterGauge("hostmap.pending.indexes", nil).Update(int64(indexLen)) 632 c.mainHostMap.EmitStats() 633 } 634 635 // Utility functions below 636 637 func generateIndex(l *logrus.Logger) (uint32, error) { 638 b := make([]byte, 4) 639 640 // Let zero mean we don't know the ID, so don't generate zero 641 var index uint32 642 for index == 0 { 643 _, err := rand.Read(b) 644 if err != nil { 645 l.Errorln(err) 646 return 0, err 647 } 648 649 index = binary.BigEndian.Uint32(b) 650 } 651 652 if l.Level >= logrus.DebugLevel { 653 l.WithField("index", index). 654 Debug("Generated index") 655 } 656 return index, nil 657 } 658 659 func hsTimeout(tries int, interval time.Duration) time.Duration { 660 return time.Duration(tries / 2 * ((2 * int(interval)) + (tries-1)*int(interval))) 661 }