established_remotes.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package nebula
  2. import (
  3. "net/netip"
  4. "sync"
  5. "github.com/slackhq/nebula/udp"
  6. )
  7. // establishedRemotes tells the udp conns that implement udp.EstablishedPeerConn which addresses are the current
  8. // direct remote of at least one established tunnel. A hostinfo holds a reference on its remote while it is in the
  9. // main hostmap, and SetRemote moves the reference; a relayed tunnel has no direct remote and holds none.
  10. type establishedRemotes struct {
  11. mu sync.Mutex
  12. conns []udp.EstablishedPeerConn
  13. refs map[netip.AddrPort]int
  14. }
  15. // newEstablishedRemotes returns nil when no conn wants to know, which every method accepts.
  16. func newEstablishedRemotes(conns []udp.Conn) *establishedRemotes {
  17. var pc []udp.EstablishedPeerConn
  18. for _, c := range conns {
  19. if p, ok := c.(udp.EstablishedPeerConn); ok {
  20. pc = append(pc, p)
  21. }
  22. }
  23. if len(pc) == 0 {
  24. return nil
  25. }
  26. return &establishedRemotes{conns: pc, refs: map[netip.AddrPort]int{}}
  27. }
  28. // add makes hostinfo hold a reference on its remote, as it joins the main hostmap. A hostinfo holds references in
  29. // one establishedRemotes at a time, so an add while it is in another is ignored.
  30. func (e *establishedRemotes) add(hostinfo *HostInfo) {
  31. if e == nil {
  32. return
  33. }
  34. e.mu.Lock()
  35. defer e.mu.Unlock()
  36. if !hostinfo.establishedIn.CompareAndSwap(nil, e) && hostinfo.establishedIn.Load() != e {
  37. return
  38. }
  39. e.syncLocked(hostinfo)
  40. }
  41. // remove drops hostinfo's reference, as it leaves the main hostmap.
  42. func (e *establishedRemotes) remove(hostinfo *HostInfo) {
  43. if e == nil {
  44. return
  45. }
  46. e.mu.Lock()
  47. defer e.mu.Unlock()
  48. if hostinfo.establishedIn.CompareAndSwap(e, nil) {
  49. e.syncLocked(hostinfo)
  50. }
  51. }
  52. // syncLocked moves hostinfo's reference to the remote it should hold now: its current remote while it is
  53. // established, none otherwise. Reading both under mu makes the last call win however add, remove and SetRemote
  54. // interleave. Only the establishedRemotes that hostinfo is in, or has just left, may call it, since
  55. // hostinfo.establishedRemote is a reference in that one's refs.
  56. func (e *establishedRemotes) syncLocked(hostinfo *HostInfo) {
  57. var want netip.AddrPort
  58. if hostinfo.establishedIn.Load() == e {
  59. want = hostinfo.GetRemote()
  60. }
  61. held := hostinfo.establishedRemote
  62. if want == held {
  63. return
  64. }
  65. if held.IsValid() {
  66. e.refs[held]--
  67. if e.refs[held] == 0 {
  68. delete(e.refs, held)
  69. for _, c := range e.conns {
  70. c.SetEstablishedPeer(held, false)
  71. }
  72. }
  73. }
  74. if want.IsValid() {
  75. e.refs[want]++
  76. if e.refs[want] == 1 {
  77. for _, c := range e.conns {
  78. c.SetEstablishedPeer(want, true)
  79. }
  80. }
  81. }
  82. hostinfo.establishedRemote = want
  83. }