@ -82,11 +82,11 @@ type extension struct {
logf logger . Logf
logf logger . Logf
bus * eventbus . Bus
bus * eventbus . Bus
mu sync . Mutex // guards the following fields
mu sync . Mutex // guards the following fields
shutdown bool
shutdown bool
port * int // ipn.Prefs.RelayServerPort, nil if disabled
port * int // ipn.Prefs.RelayServerPort, nil if disabled
disconnectFromBusCh chan struct { } // non-nil if consumeEventbusTopics is running, closed to signal it to return
eventSubs * eventbus . Monitor // nil if not connected to eventbus
busDoneCh chan struct { } // non-nil if consumeEventbusTopics is running, closed when it returns
debugSessionsCh chan chan [ ] status . ServerSession // non-nil if consumeEventbusTopics is running
debugSessionsCh chan chan [ ] status . ServerSession // non-nil if consumeEventbusTopics is running
hasNodeAttrDisableRelayServer bool // tailcfg.NodeAttrDisableRelayServer
hasNodeAttrDisableRelayServer bool // tailcfg.NodeAttrDisableRelayServer
}
}
@ -119,15 +119,13 @@ func (e *extension) handleBusLifetimeLocked() {
if ! busShouldBeRunning {
if ! busShouldBeRunning {
e . disconnectFromBusLocked ( )
e . disconnectFromBusLocked ( )
return
return
}
} else if e . eventSubs != nil {
if e . busDoneCh != nil {
return // already running
return // already running
}
}
port := * e . port
e . disconnectFromBusCh = make ( chan struct { } )
ec := e . bus . Client ( "relayserver.extension" )
e . busDoneCh = make ( chan struct { } )
e . debugSessionsCh = make ( chan chan [ ] status . ServerSession )
e . debugSessionsCh = make ( chan chan [ ] status . ServerSession )
go e . consumeEventbusTopics ( port)
e . eventSubs = ptr . To ( ec . Monitor ( e . consumeEventbusTopics ( ec, * e . port) ) )
}
}
func ( e * extension ) selfNodeViewChanged ( nodeView tailcfg . NodeView ) {
func ( e * extension ) selfNodeViewChanged ( nodeView tailcfg . NodeView ) {
@ -175,77 +173,72 @@ var overrideAddrs = sync.OnceValue(func() (ret []netip.Addr) {
// consumeEventbusTopics serves endpoint allocation requests over the eventbus.
// consumeEventbusTopics serves endpoint allocation requests over the eventbus.
// It also serves [relayServer] debug information on a channel.
// It also serves [relayServer] debug information on a channel.
// consumeEventbusTopics must never acquire [extension.mu], which can be held by
// consumeEventbusTopics must never acquire [extension.mu], which can be held
// other goroutines while waiting to receive on [extension.busDoneCh ] or the
// by other goroutines while waiting to receive on [extension.eventSubs ] or the
// inner [extension.debugSessionsCh] channel.
// inner [extension.debugSessionsCh] channel.
func ( e * extension ) consumeEventbusTopics ( port int ) {
func ( e * extension ) consumeEventbusTopics ( ec * eventbus . Client , port int ) func ( * eventbus . Client ) {
defer close ( e . busDoneCh )
reqSub := eventbus . Subscribe [ magicsock . UDPRelayAllocReq ] ( ec )
respPub := eventbus . Publish [ magicsock . UDPRelayAllocResp ] ( ec )
debugSessionsCh := e . debugSessionsCh
eventClient := e . bus . Client ( "relayserver.extension" )
return func ( ec * eventbus . Client ) {
reqSub := eventbus . Subscribe [ magicsock . UDPRelayAllocReq ] ( eventClient )
var rs relayServer // lazily initialized
respPub := eventbus . Publish [ magicsock . UDPRelayAllocResp ] ( eventClient )
defer func ( ) {
defer eventClient . Close ( )
if rs != nil {
rs . Close ( )
var rs relayServer // lazily initialized
defer func ( ) {
if rs != nil {
rs . Close ( )
}
} ( )
for {
select {
case <- e . disconnectFromBusCh :
return
case <- eventClient . Done ( ) :
return
case respCh := <- e . debugSessionsCh :
if rs == nil {
// Don't initialize the server simply for a debug request.
respCh <- nil
continue
}
}
sessions := rs . GetSessions ( )
} ( )
respCh <- sessions
for {
case req := <- reqSub . Events ( ) :
select {
if rs == nil {
case <- ec . Done ( ) :
var err error
return
rs , err = udprelay . NewServer ( e . logf , port , overrideAddrs ( ) )
case respCh := <- debugSessionsCh :
if rs == nil {
// Don't initialize the server simply for a debug request.
respCh <- nil
continue
}
sessions := rs . GetSessions ( )
respCh <- sessions
case req := <- reqSub . Events ( ) :
if rs == nil {
var err error
rs , err = udprelay . NewServer ( e . logf , port , overrideAddrs ( ) )
if err != nil {
e . logf ( "error initializing server: %v" , err )
continue
}
}
se , err := rs . AllocateEndpoint ( req . Message . ClientDisco [ 0 ] , req . Message . ClientDisco [ 1 ] )
if err != nil {
if err != nil {
e . logf ( "error initializing server: %v" , err )
e . logf ( "error allocating endpoint : %v", err )
continue
continue
}
}
}
respPub . Publish ( magicsock . UDPRelayAllocResp {
se , err := rs . AllocateEndpoint ( req . Message . ClientDisco [ 0 ] , req . Message . ClientDisco [ 1 ] )
ReqRxFromNodeKey : req . RxFromNodeKey ,
if err != nil {
ReqRxFromDiscoKey : req . RxFromDiscoKey ,
e . logf ( "error allocating endpoint: %v" , err )
Message : & disco . AllocateUDPRelayEndpointResponse {
continue
Generation : req . Message . Generation ,
}
UDPRelayEndpoint : disco . UDPRelayEndpoint {
respPub . Publish ( magicsock . UDPRelayAllocResp {
ServerDisco : se . ServerDisco ,
ReqRxFromNodeKey : req . RxFromNodeKey ,
ClientDisco : se . ClientDisco ,
ReqRxFromDiscoKey : req . RxFromDiscoKey ,
LamportID : se . LamportID ,
Message : & disco . AllocateUDPRelayEndpointResponse {
VNI : se . VNI ,
Generation : req . Message . Generation ,
BindLifetime : se . BindLifetime . Duration ,
UDPRelayEndpoint : disco . UDPRelayEndpoint {
SteadyStateLifetime : se . SteadyStateLifetime . Duration ,
ServerDisco : se . ServerDisco ,
AddrPorts : se . AddrPorts ,
ClientDisco : se . ClientDisco ,
} ,
LamportID : se . LamportID ,
VNI : se . VNI ,
BindLifetime : se . BindLifetime . Duration ,
SteadyStateLifetime : se . SteadyStateLifetime . Duration ,
AddrPorts : se . AddrPorts ,
} ,
} ,
} ,
} )
} )
}
}
}
}
}
}
}
func ( e * extension ) disconnectFromBusLocked ( ) {
func ( e * extension ) disconnectFromBusLocked ( ) {
if e . busDoneCh != nil {
if e . eventSubs != nil {
close ( e . disconnectFromBusCh )
e . eventSubs . Close ( )
<- e . busDoneCh
e . eventSubs = nil
e . busDoneCh = nil
e . disconnectFromBusCh = nil
e . debugSessionsCh = nil
e . debugSessionsCh = nil
}
}
}
}
@ -270,7 +263,7 @@ func (e *extension) serverStatus() status.ServerStatus {
UDPPort : nil ,
UDPPort : nil ,
Sessions : nil ,
Sessions : nil ,
}
}
if e . port == nil || e . busDoneCh == nil {
if e . port == nil || e . eventSubs == nil {
return st
return st
}
}
st . UDPPort = ptr . To ( * e . port )
st . UDPPort = ptr . To ( * e . port )
@ -281,7 +274,7 @@ func (e *extension) serverStatus() status.ServerStatus {
resp := <- ch
resp := <- ch
st . Sessions = resp
st . Sessions = resp
return st
return st
case <- e . busDoneCh :
case <- e . eventSubs. Done ( ) :
return st
return st
}
}
}
}