mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-25 01:23:22 +01:00
[client] Remove loop after route calculation (#2856)
- ICE do not trigger disconnect callbacks if the stated did not change - Fix route calculation callback loop - Move route state updates into protected scope by mutex - Do not calculate routes in case of peer.Open() and peer.Close()
This commit is contained in:
parent
08b6e9d647
commit
b4d7605147
@ -237,10 +237,6 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
peerState.IP = receivedState.IP
|
peerState.IP = receivedState.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedState.GetRoutes() != nil {
|
|
||||||
peerState.SetRoutes(receivedState.GetRoutes())
|
|
||||||
}
|
|
||||||
|
|
||||||
skipNotification := shouldSkipNotify(receivedState.ConnStatus, peerState)
|
skipNotification := shouldSkipNotify(receivedState.ConnStatus, peerState)
|
||||||
|
|
||||||
if receivedState.ConnStatus != peerState.ConnStatus {
|
if receivedState.ConnStatus != peerState.ConnStatus {
|
||||||
@ -261,12 +257,40 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
d.notifyPeerListChanged()
|
||||||
if found && ch != nil {
|
return nil
|
||||||
close(ch)
|
}
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
|
func (d *Status) AddPeerStateRoute(peer string, route string) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
peerState, ok := d.peers[peer]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("peer doesn't exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peerState.AddRoute(route)
|
||||||
|
d.peers[peer] = peerState
|
||||||
|
|
||||||
|
// todo: consider to make sense of this notification or not
|
||||||
|
d.notifyPeerListChanged()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Status) RemovePeerStateRoute(peer string, route string) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
peerState, ok := d.peers[peer]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("peer doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerState.DeleteRoute(route)
|
||||||
|
d.peers[peer] = peerState
|
||||||
|
|
||||||
|
// todo: consider to make sense of this notification or not
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -301,12 +325,7 @@ func (d *Status) UpdatePeerICEState(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
d.notifyPeerStateChangeListeners(receivedState.PubKey)
|
||||||
if found && ch != nil {
|
|
||||||
close(ch)
|
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -334,12 +353,7 @@ func (d *Status) UpdatePeerRelayedState(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
d.notifyPeerStateChangeListeners(receivedState.PubKey)
|
||||||
if found && ch != nil {
|
|
||||||
close(ch)
|
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -366,12 +380,7 @@ func (d *Status) UpdatePeerRelayedStateToDisconnected(receivedState State) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
d.notifyPeerStateChangeListeners(receivedState.PubKey)
|
||||||
if found && ch != nil {
|
|
||||||
close(ch)
|
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -401,12 +410,7 @@ func (d *Status) UpdatePeerICEStateToDisconnected(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
d.notifyPeerStateChangeListeners(receivedState.PubKey)
|
||||||
if found && ch != nil {
|
|
||||||
close(ch)
|
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -477,11 +481,14 @@ func (d *Status) FinishPeerListModifications() {
|
|||||||
func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} {
|
func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
ch, found := d.changeNotify[peer]
|
ch, found := d.changeNotify[peer]
|
||||||
if !found || ch == nil {
|
if found {
|
||||||
ch = make(chan struct{})
|
return ch
|
||||||
d.changeNotify[peer] = ch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ch = make(chan struct{})
|
||||||
|
d.changeNotify[peer] = ch
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,6 +762,17 @@ func (d *Status) onConnectionChanged() {
|
|||||||
d.notifier.updateServerStates(d.managementState, d.signalState)
|
d.notifier.updateServerStates(d.managementState, d.signalState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notifyPeerStateChangeListeners notifies route manager about the change in peer state
|
||||||
|
func (d *Status) notifyPeerStateChangeListeners(peerID string) {
|
||||||
|
ch, found := d.changeNotify[peerID]
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ch)
|
||||||
|
delete(d.changeNotify, peerID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Status) notifyPeerListChanged() {
|
func (d *Status) notifyPeerListChanged() {
|
||||||
d.notifier.peerListChanged(d.numOfPeers())
|
d.notifier.peerListChanged(d.numOfPeers())
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func TestGetPeerStateChangeNotifierLogic(t *testing.T) {
|
|||||||
|
|
||||||
peerState.IP = ip
|
peerState.IP = ip
|
||||||
|
|
||||||
err := status.UpdatePeerState(peerState)
|
err := status.UpdatePeerRelayedStateToDisconnected(peerState)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
@ -57,6 +57,9 @@ type WorkerICE struct {
|
|||||||
|
|
||||||
localUfrag string
|
localUfrag string
|
||||||
localPwd string
|
localPwd string
|
||||||
|
|
||||||
|
// we record the last known state of the ICE agent to avoid duplicate on disconnected events
|
||||||
|
lastKnownState ice.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWorkerICE(ctx context.Context, log *log.Entry, config ConnConfig, signaler *Signaler, ifaceDiscover stdnet.ExternalIFaceDiscover, statusRecorder *Status, hasRelayOnLocally bool, callBacks WorkerICECallbacks) (*WorkerICE, error) {
|
func NewWorkerICE(ctx context.Context, log *log.Entry, config ConnConfig, signaler *Signaler, ifaceDiscover stdnet.ExternalIFaceDiscover, statusRecorder *Status, hasRelayOnLocally bool, callBacks WorkerICECallbacks) (*WorkerICE, error) {
|
||||||
@ -194,8 +197,7 @@ func (w *WorkerICE) Close() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := w.agent.Close()
|
if err := w.agent.Close(); err != nil {
|
||||||
if err != nil {
|
|
||||||
w.log.Warnf("failed to close ICE agent: %s", err)
|
w.log.Warnf("failed to close ICE agent: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,15 +217,18 @@ func (w *WorkerICE) reCreateAgent(agentCancel context.CancelFunc, candidates []i
|
|||||||
|
|
||||||
err = agent.OnConnectionStateChange(func(state ice.ConnectionState) {
|
err = agent.OnConnectionStateChange(func(state ice.ConnectionState) {
|
||||||
w.log.Debugf("ICE ConnectionState has changed to %s", state.String())
|
w.log.Debugf("ICE ConnectionState has changed to %s", state.String())
|
||||||
if state == ice.ConnectionStateFailed || state == ice.ConnectionStateDisconnected {
|
switch state {
|
||||||
w.conn.OnStatusChanged(StatusDisconnected)
|
case ice.ConnectionStateConnected:
|
||||||
|
w.lastKnownState = ice.ConnectionStateConnected
|
||||||
w.muxAgent.Lock()
|
return
|
||||||
agentCancel()
|
case ice.ConnectionStateFailed, ice.ConnectionStateDisconnected:
|
||||||
_ = agent.Close()
|
if w.lastKnownState != ice.ConnectionStateDisconnected {
|
||||||
w.agent = nil
|
w.lastKnownState = ice.ConnectionStateDisconnected
|
||||||
|
w.conn.OnStatusChanged(StatusDisconnected)
|
||||||
w.muxAgent.Unlock()
|
}
|
||||||
|
w.closeAgent(agentCancel)
|
||||||
|
default:
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,6 +254,17 @@ func (w *WorkerICE) reCreateAgent(agentCancel context.CancelFunc, candidates []i
|
|||||||
return agent, nil
|
return agent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WorkerICE) closeAgent(cancel context.CancelFunc) {
|
||||||
|
w.muxAgent.Lock()
|
||||||
|
defer w.muxAgent.Unlock()
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
if err := w.agent.Close(); err != nil {
|
||||||
|
w.log.Warnf("failed to close ICE agent: %s", err)
|
||||||
|
}
|
||||||
|
w.agent = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WorkerICE) punchRemoteWGPort(pair *ice.CandidatePair, remoteWgPort int) {
|
func (w *WorkerICE) punchRemoteWGPort(pair *ice.CandidatePair, remoteWgPort int) {
|
||||||
// wait local endpoint configuration
|
// wait local endpoint configuration
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
@ -122,13 +122,20 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[route.ID]
|
|||||||
tempScore = float64(metricDiff) * 10
|
tempScore = float64(metricDiff) * 10
|
||||||
}
|
}
|
||||||
|
|
||||||
// in some temporal cases, latency can be 0, so we set it to 1s to not block but try to avoid this route
|
// in some temporal cases, latency can be 0, so we set it to 999ms to not block but try to avoid this route
|
||||||
latency := time.Second
|
latency := 999 * time.Millisecond
|
||||||
if peerStatus.latency != 0 {
|
if peerStatus.latency != 0 {
|
||||||
latency = peerStatus.latency
|
latency = peerStatus.latency
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("peer %s has 0 latency", r.Peer)
|
log.Tracef("peer %s has 0 latency, range %s", r.Peer, c.handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// avoid negative tempScore on the higher latency calculation
|
||||||
|
if latency > 1*time.Second {
|
||||||
|
latency = 999 * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
// higher latency is worse score
|
||||||
tempScore += 1 - latency.Seconds()
|
tempScore += 1 - latency.Seconds()
|
||||||
|
|
||||||
if !peerStatus.relayed {
|
if !peerStatus.relayed {
|
||||||
@ -150,6 +157,8 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[route.ID]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("chosen route: %s, chosen score: %f, current route: %s, current score: %f", chosen, chosenScore, currID, currScore)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case chosen == "":
|
case chosen == "":
|
||||||
var peers []string
|
var peers []string
|
||||||
@ -195,15 +204,20 @@ func (c *clientNetwork) watchPeerStatusChanges(ctx context.Context, peerKey stri
|
|||||||
func (c *clientNetwork) startPeersStatusChangeWatcher() {
|
func (c *clientNetwork) startPeersStatusChangeWatcher() {
|
||||||
for _, r := range c.routes {
|
for _, r := range c.routes {
|
||||||
_, found := c.routePeersNotifiers[r.Peer]
|
_, found := c.routePeersNotifiers[r.Peer]
|
||||||
if !found {
|
if found {
|
||||||
c.routePeersNotifiers[r.Peer] = make(chan struct{})
|
continue
|
||||||
go c.watchPeerStatusChanges(c.ctx, r.Peer, c.peerStateUpdate, c.routePeersNotifiers[r.Peer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closerChan := make(chan struct{})
|
||||||
|
c.routePeersNotifiers[r.Peer] = closerChan
|
||||||
|
go c.watchPeerStatusChanges(c.ctx, r.Peer, c.peerStateUpdate, closerChan)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientNetwork) removeRouteFromWireguardPeer() error {
|
func (c *clientNetwork) removeRouteFromWireGuardPeer() error {
|
||||||
c.removeStateRoute()
|
if err := c.statusRecorder.RemovePeerStateRoute(c.currentChosen.Peer, c.handler.String()); err != nil {
|
||||||
|
log.Warnf("Failed to update peer state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.handler.RemoveAllowedIPs(); err != nil {
|
if err := c.handler.RemoveAllowedIPs(); err != nil {
|
||||||
return fmt.Errorf("remove allowed IPs: %w", err)
|
return fmt.Errorf("remove allowed IPs: %w", err)
|
||||||
@ -218,7 +232,7 @@ func (c *clientNetwork) removeRouteFromPeerAndSystem() error {
|
|||||||
|
|
||||||
var merr *multierror.Error
|
var merr *multierror.Error
|
||||||
|
|
||||||
if err := c.removeRouteFromWireguardPeer(); err != nil {
|
if err := c.removeRouteFromWireGuardPeer(); err != nil {
|
||||||
merr = multierror.Append(merr, fmt.Errorf("remove allowed IPs for peer %s: %w", c.currentChosen.Peer, err))
|
merr = multierror.Append(merr, fmt.Errorf("remove allowed IPs for peer %s: %w", c.currentChosen.Peer, err))
|
||||||
}
|
}
|
||||||
if err := c.handler.RemoveRoute(); err != nil {
|
if err := c.handler.RemoveRoute(); err != nil {
|
||||||
@ -257,7 +271,7 @@ func (c *clientNetwork) recalculateRouteAndUpdatePeerAndSystem() error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, remove the allowed IPs from the previous peer first
|
// Otherwise, remove the allowed IPs from the previous peer first
|
||||||
if err := c.removeRouteFromWireguardPeer(); err != nil {
|
if err := c.removeRouteFromWireGuardPeer(); err != nil {
|
||||||
return fmt.Errorf("remove allowed IPs for peer %s: %w", c.currentChosen.Peer, err)
|
return fmt.Errorf("remove allowed IPs for peer %s: %w", c.currentChosen.Peer, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,37 +282,13 @@ func (c *clientNetwork) recalculateRouteAndUpdatePeerAndSystem() error {
|
|||||||
return fmt.Errorf("add allowed IPs for peer %s: %w", c.currentChosen.Peer, err)
|
return fmt.Errorf("add allowed IPs for peer %s: %w", c.currentChosen.Peer, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addStateRoute()
|
err := c.statusRecorder.AddPeerStateRoute(c.currentChosen.Peer, c.handler.String())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("add peer state route: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientNetwork) addStateRoute() {
|
|
||||||
state, err := c.statusRecorder.GetPeer(c.currentChosen.Peer)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get peer state: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
state.AddRoute(c.handler.String())
|
|
||||||
if err := c.statusRecorder.UpdatePeerState(state); err != nil {
|
|
||||||
log.Warnf("Failed to update peer state: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientNetwork) removeStateRoute() {
|
|
||||||
state, err := c.statusRecorder.GetPeer(c.currentChosen.Peer)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to get peer state: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
state.DeleteRoute(c.handler.String())
|
|
||||||
if err := c.statusRecorder.UpdatePeerState(state); err != nil {
|
|
||||||
log.Warnf("Failed to update peer state: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clientNetwork) sendUpdateToClientNetworkWatcher(update routesUpdate) {
|
func (c *clientNetwork) sendUpdateToClientNetworkWatcher(update routesUpdate) {
|
||||||
go func() {
|
go func() {
|
||||||
c.routeUpdate <- update
|
c.routeUpdate <- update
|
||||||
|
@ -217,6 +217,11 @@ func (rm *Counter[Key, I, O]) Clear() {
|
|||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface for Counter.
|
// MarshalJSON implements the json.Marshaler interface for Counter.
|
||||||
func (rm *Counter[Key, I, O]) MarshalJSON() ([]byte, error) {
|
func (rm *Counter[Key, I, O]) MarshalJSON() ([]byte, error) {
|
||||||
|
rm.refCountMu.Lock()
|
||||||
|
defer rm.refCountMu.Unlock()
|
||||||
|
rm.idMu.Lock()
|
||||||
|
defer rm.idMu.Unlock()
|
||||||
|
|
||||||
return json.Marshal(struct {
|
return json.Marshal(struct {
|
||||||
RefCountMap map[Key]Ref[O] `json:"refCountMap"`
|
RefCountMap map[Key]Ref[O] `json:"refCountMap"`
|
||||||
IDMap map[string][]Key `json:"idMap"`
|
IDMap map[string][]Key `json:"idMap"`
|
||||||
|
Loading…
Reference in New Issue
Block a user