Skip to content

Commit 2104a79

Browse files
authored
make rebind slightly less crufty (#1812)
* tweak it * GUH
1 parent 5f62ccd commit 2104a79

9 files changed

Lines changed: 157 additions & 294 deletions

examples/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ listen:
151151
# office to home) leaves Nebula sending out an interface that no longer has a route. When true, Nebula watches
152152
# the routing socket and rebinds the listener once the change settles.
153153
# iOS does not use this, the host app drives the same rebind itself.
154-
# Default true. This setting is reloadable.
154+
# Default true. Not reloadable.
155155
#rebind_on_network_change: true
156156

157157
# By default, Nebula replies to packets it has no tunnel for with a "recv_error" packet. This packet helps speed up reconnection

main.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,6 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
268268

269269
attachCommands(l, c, ssh, ifce)
270270

271-
networkChanges := newNetworkChangeMonitorFromConfig(ctx, l, c)
272-
273271
control := &Control{
274272
state: StateReady,
275273
f: ifce,
@@ -280,13 +278,13 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
280278
statsStart: stats.Start,
281279
dnsStart: ds.Start,
282280
lighthouseStart: lightHouse.StartUpdateWorker,
283-
networkChangeStart: networkChanges.Start,
284281
connectionManagerStart: connManager.Start,
285282
}
286283

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

291289
return control, nil
292290
}

network_change.go

Lines changed: 0 additions & 145 deletions
This file was deleted.

network_change_test.go

Lines changed: 0 additions & 137 deletions
This file was deleted.

udp/netchange.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package udp
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
7+
"github.com/slackhq/nebula/config"
8+
)
9+
10+
// NetworkChangeMonitor rebinds the udp listener when the local network moves out from under it.
11+
//
12+
// Detection lives here in the udp package, next to the socket it concerns and the platform matrix that already knows
13+
// which sockets go stale. What to do about a change — updating the lighthouse, requerying tunnels — is not the udp
14+
// package's business, so the reaction is injected as a plain function pointer. The monitor calls it once a change
15+
// has settled and stays ignorant of what it does.
16+
//
17+
// On platforms whose sockets do not go stale, watchNetworkChanges hands back a nil channel and Start returns.
18+
type NetworkChangeMonitor struct {
19+
l *slog.Logger
20+
ctx context.Context
21+
enabled bool
22+
// rebind is what we call once a change has settled. It is injected because the udp package has no business
23+
// knowing what a rebind entails, only when one is warranted.
24+
rebind func()
25+
}
26+
27+
// NewNetworkChangeMonitor builds a monitor that calls rebind whenever the local network moves. The returned monitor
28+
// is always usable: Start is safe to call unconditionally, it no-ops when disabled or on a platform that does not
29+
// need it.
30+
func NewNetworkChangeMonitor(ctx context.Context, l *slog.Logger, c *config.C, rebind func()) *NetworkChangeMonitor {
31+
return &NetworkChangeMonitor{
32+
l: l,
33+
ctx: ctx,
34+
enabled: c.GetBool("listen.rebind_on_network_change", true),
35+
rebind: rebind,
36+
}
37+
}
38+
39+
// Start watches for network changes until the context is cancelled, calling rebind once per settled change. It
40+
// blocks, so callers run it in a goroutine, and it no-ops when disabled, unsupported, or with nothing to rebind.
41+
func (m *NetworkChangeMonitor) Start() {
42+
if !m.enabled || m.rebind == nil || m.ctx.Err() != nil {
43+
return
44+
}
45+
46+
changes, err := watchNetworkChanges(m.ctx, m.l)
47+
if err != nil {
48+
// Not fatal. Everything else still works, we just won't notice a network change on our own.
49+
m.l.Error("Failed to watch for network changes, will not rebind the udp listener when the network moves",
50+
"error", err,
51+
)
52+
return
53+
}
54+
55+
if changes == nil {
56+
// This platform's sockets don't go stale, so there is nothing to watch for.
57+
return
58+
}
59+
60+
m.l.Info("Watching for network changes to rebind the udp listener")
61+
62+
for range changes {
63+
m.l.Info("Local network changed, rebinding the udp listener")
64+
m.rebind()
65+
}
66+
}

0 commit comments

Comments
 (0)