Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ listen:
# office to home) leaves Nebula sending out an interface that no longer has a route. When true, Nebula watches
# the routing socket and rebinds the listener once the change settles.
# iOS does not use this, the host app drives the same rebind itself.
# Default true. This setting is reloadable.
# Default true. Not reloadable.
#rebind_on_network_change: true

# By default, Nebula replies to packets it has no tunnel for with a "recv_error" packet. This packet helps speed up reconnection
Expand Down
8 changes: 3 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,6 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev

attachCommands(l, c, ssh, ifce)

networkChanges := newNetworkChangeMonitorFromConfig(ctx, l, c)

control := &Control{
state: StateReady,
f: ifce,
Expand All @@ -280,13 +278,13 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
statsStart: stats.Start,
dnsStart: ds.Start,
lighthouseStart: lightHouse.StartUpdateWorker,
networkChangeStart: networkChanges.Start,
connectionManagerStart: connManager.Start,
}

// The monitor's whole job is to trigger a rebind, and Control is what owns rebinding and the state gating
// around it, so this can only be wired once Control exists.
networkChanges.setRebind(control.RebindUDPServer)
// around it, so it is injected the rebind func and can only be built once Control exists.
networkChanges := udp.NewNetworkChangeMonitor(ctx, l, c, control.RebindUDPServer)
control.networkChangeStart = networkChanges.Start

return control, nil
}
Expand Down
145 changes: 0 additions & 145 deletions network_change.go

This file was deleted.

137 changes: 0 additions & 137 deletions network_change_test.go

This file was deleted.

66 changes: 66 additions & 0 deletions udp/netchange.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package udp

import (
"context"
"log/slog"

"github.com/slackhq/nebula/config"
)

// NetworkChangeMonitor rebinds the udp listener when the local network moves out from under it.
//
// Detection lives here in the udp package, next to the socket it concerns and the platform matrix that already knows
// which sockets go stale. What to do about a change — updating the lighthouse, requerying tunnels — is not the udp
// package's business, so the reaction is injected as a plain function pointer. The monitor calls it once a change
// has settled and stays ignorant of what it does.
//
// On platforms whose sockets do not go stale, watchNetworkChanges hands back a nil channel and Start returns.
type NetworkChangeMonitor struct {
l *slog.Logger
ctx context.Context
enabled bool
// rebind is what we call once a change has settled. It is injected because the udp package has no business
// knowing what a rebind entails, only when one is warranted.
rebind func()
}

// NewNetworkChangeMonitor builds a monitor that calls rebind whenever the local network moves. The returned monitor
// is always usable: Start is safe to call unconditionally, it no-ops when disabled or on a platform that does not
// need it.
func NewNetworkChangeMonitor(ctx context.Context, l *slog.Logger, c *config.C, rebind func()) *NetworkChangeMonitor {
return &NetworkChangeMonitor{
l: l,
ctx: ctx,
enabled: c.GetBool("listen.rebind_on_network_change", true),
rebind: rebind,
}
}

// Start watches for network changes until the context is cancelled, calling rebind once per settled change. It
// blocks, so callers run it in a goroutine, and it no-ops when disabled, unsupported, or with nothing to rebind.
func (m *NetworkChangeMonitor) Start() {
if !m.enabled || m.rebind == nil || m.ctx.Err() != nil {
return
}

changes, err := watchNetworkChanges(m.ctx, m.l)
if err != nil {
// Not fatal. Everything else still works, we just won't notice a network change on our own.
m.l.Error("Failed to watch for network changes, will not rebind the udp listener when the network moves",
"error", err,
)
return
}

if changes == nil {
// This platform's sockets don't go stale, so there is nothing to watch for.
return
}

m.l.Info("Watching for network changes to rebind the udp listener")

for range changes {
m.l.Info("Local network changed, rebinding the udp listener")
m.rebind()
}
}
Loading