| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 |
- package nebula
- import (
- "net/netip"
- "sync"
- "github.com/slackhq/nebula/udp"
- )
- // establishedRemotes tells the udp conns that implement udp.EstablishedPeerConn which addresses are the current
- // direct remote of at least one established tunnel. A hostinfo holds a reference on its remote while it is in the
- // main hostmap, and SetRemote moves the reference; a relayed tunnel has no direct remote and holds none.
- type establishedRemotes struct {
- mu sync.Mutex
- conns []udp.EstablishedPeerConn
- refs map[netip.AddrPort]int
- }
- // newEstablishedRemotes returns nil when no conn wants to know, which every method accepts.
- func newEstablishedRemotes(conns []udp.Conn) *establishedRemotes {
- var pc []udp.EstablishedPeerConn
- for _, c := range conns {
- if p, ok := c.(udp.EstablishedPeerConn); ok {
- pc = append(pc, p)
- }
- }
- if len(pc) == 0 {
- return nil
- }
- return &establishedRemotes{conns: pc, refs: map[netip.AddrPort]int{}}
- }
- // add makes hostinfo hold a reference on its remote, as it joins the main hostmap. A hostinfo holds references in
- // one establishedRemotes at a time, so an add while it is in another is ignored.
- func (e *establishedRemotes) add(hostinfo *HostInfo) {
- if e == nil {
- return
- }
- e.mu.Lock()
- defer e.mu.Unlock()
- if !hostinfo.establishedIn.CompareAndSwap(nil, e) && hostinfo.establishedIn.Load() != e {
- return
- }
- e.syncLocked(hostinfo)
- }
- // remove drops hostinfo's reference, as it leaves the main hostmap.
- func (e *establishedRemotes) remove(hostinfo *HostInfo) {
- if e == nil {
- return
- }
- e.mu.Lock()
- defer e.mu.Unlock()
- if hostinfo.establishedIn.CompareAndSwap(e, nil) {
- e.syncLocked(hostinfo)
- }
- }
- // syncLocked moves hostinfo's reference to the remote it should hold now: its current remote while it is
- // established, none otherwise. Reading both under mu makes the last call win however add, remove and SetRemote
- // interleave. Only the establishedRemotes that hostinfo is in, or has just left, may call it, since
- // hostinfo.establishedRemote is a reference in that one's refs.
- func (e *establishedRemotes) syncLocked(hostinfo *HostInfo) {
- var want netip.AddrPort
- if hostinfo.establishedIn.Load() == e {
- want = hostinfo.GetRemote()
- }
- held := hostinfo.establishedRemote
- if want == held {
- return
- }
- if held.IsValid() {
- e.refs[held]--
- if e.refs[held] == 0 {
- delete(e.refs, held)
- for _, c := range e.conns {
- c.SetEstablishedPeer(held, false)
- }
- }
- }
- if want.IsValid() {
- e.refs[want]++
- if e.refs[want] == 1 {
- for _, c := range e.conns {
- c.SetEstablishedPeer(want, true)
- }
- }
- }
- hostinfo.establishedRemote = want
- }
|