WIP: rebase fork onto upstream/main (v1.103.0) #15
@@ -14,6 +14,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"slices"
|
||||
@@ -267,11 +268,17 @@ func (e *extension) installHooks(dph *datapathHandler) error {
|
||||
})
|
||||
|
||||
// Tell WireGuard what Transit IPs belong to which connector peers.
|
||||
e.host.Hooks().ExtraWireGuardAllowedIPs.Set(func(k key.NodePublic) views.Slice[netip.Prefix] {
|
||||
e.host.Hooks().ExtraWireGuardAllowedIPs.Set(func(peers iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix {
|
||||
if !e.conn25.isConfigured() {
|
||||
return views.Slice[netip.Prefix]{}
|
||||
return nil
|
||||
}
|
||||
return e.extraWireGuardAllowedIPs(k)
|
||||
var extras map[tailcfg.NodeID][]netip.Prefix
|
||||
for id, k := range peers {
|
||||
if pfxs := e.extraWireGuardAllowedIPs(k); pfxs.Len() > 0 {
|
||||
mak.Set(&extras, id, pfxs.AsSlice())
|
||||
}
|
||||
}
|
||||
return extras
|
||||
})
|
||||
|
||||
return nil
|
||||
|
||||
+25
-13
@@ -430,29 +430,41 @@ type Hooks struct {
|
||||
// See [filter.Filter] for details on how these hooks are invoked.
|
||||
Filter FilterHooks
|
||||
|
||||
// ExtraWireGuardAllowedIPs is called with each peer's public key
|
||||
// from the initial [wgcfg.Config], and returns a view of prefixes to
|
||||
// append to each peer's AllowedIPs.
|
||||
// ExtraWireGuardAllowedIPs is called with a sequence of peers whose
|
||||
// extra AllowedIPs the caller wants (re)computed, and returns
|
||||
// prefixes to append to those peers' AllowedIPs, keyed by node ID.
|
||||
//
|
||||
// The extra AllowedIPs are added after the [router.Config] is generated, but
|
||||
// before the WireGuard config is sent to the engine, so the extra IPs are
|
||||
// given to WireGuard, but not the OS routing table.
|
||||
// The sequence is not necessarily all peers: callers may pass any
|
||||
// subset (such as only the peers changed by a netmap delta), and
|
||||
// the returned map's meaning is scoped to the peers presented. A
|
||||
// peer absent from the returned map has no extra AllowedIPs. As of
|
||||
// 2026-07-15 the only caller passes all current peers on each
|
||||
// reconfig, but extensions must not rely on that.
|
||||
//
|
||||
// The prefixes returned from the hook should not contain duplicates, either
|
||||
// internally, or with netmap peer prefixes. Returned prefixes should only
|
||||
// An extension with nothing to add should return nil without
|
||||
// iterating peers; that keeps steady-state netmap deltas free of
|
||||
// per-peer work when the extension is idle. The peers sequence is
|
||||
// only valid during the call.
|
||||
//
|
||||
// The extra AllowedIPs are given to WireGuard, but not the OS
|
||||
// routing table.
|
||||
//
|
||||
// The returned prefixes should not contain duplicates, either
|
||||
// internally, or with netmap peer prefixes. They should only
|
||||
// contain host routes, and not contain default or subnet routes.
|
||||
// Subsequent calls that return an unchanged set of prefixes for a given peer,
|
||||
// should return the prefixes in the same order for that peer,
|
||||
// to prevent configuration churn.
|
||||
// Subsequent calls that return an unchanged set of prefixes for a
|
||||
// given peer should return the prefixes in the same order for that
|
||||
// peer, to prevent configuration churn.
|
||||
//
|
||||
// The returned slice should not be mutated by the extension after it is returned.
|
||||
// The returned map and slices should not be mutated by the
|
||||
// extension after they are returned.
|
||||
//
|
||||
// The hook is called with LocalBackend's mutex locked.
|
||||
//
|
||||
// TODO(#17858): This hook may not be needed and can possibly be replaced by
|
||||
// new hooks that fit into the new architecture that make use of new
|
||||
// WireGuard APIs.
|
||||
ExtraWireGuardAllowedIPs feature.Hook[func(key.NodePublic) views.Slice[netip.Prefix]]
|
||||
ExtraWireGuardAllowedIPs feature.Hook[func(peers iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix]
|
||||
|
||||
// ExtraRouterConfigRoutes returns a view of prefixes to append to [router.Config.Routes].
|
||||
//
|
||||
|
||||
@@ -6,6 +6,7 @@ package ipnlocal
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"iter"
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
@@ -943,22 +944,23 @@ func (nb *nodeBackend) updateRouteManagerPrefs(p routePrefs) routemanager.PeersW
|
||||
return res.AllowedIPs
|
||||
}
|
||||
|
||||
// updateRouteManagerExtras pushes each peer's extra WireGuard-only
|
||||
// allowed IPs into the route manager, obtained by calling fn (the
|
||||
// [ipnext.Hooks.ExtraWireGuardAllowedIPs] hook) with each peer's
|
||||
// public key.
|
||||
// updateRouteManagerExtras pushes extra WireGuard-only allowed IPs
|
||||
// into the route manager, obtained by calling fn (the
|
||||
// [ipnext.Hooks.ExtraWireGuardAllowedIPs] hook) with a sequence of
|
||||
// the current peers.
|
||||
//
|
||||
// It returns the peers whose allowed source prefixes changed as a
|
||||
// result.
|
||||
func (nb *nodeBackend) updateRouteManagerExtras(fn func(key.NodePublic) views.Slice[netip.Prefix]) routemanager.PeersWithRouteChanges {
|
||||
func (nb *nodeBackend) updateRouteManagerExtras(fn func(peers iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix) routemanager.PeersWithRouteChanges {
|
||||
nb.mu.Lock()
|
||||
defer nb.mu.Unlock()
|
||||
var extras map[tailcfg.NodeID][]netip.Prefix
|
||||
for id, p := range nb.peers {
|
||||
if pfxs := fn(p.Key()); pfxs.Len() > 0 {
|
||||
mak.Set(&extras, id, pfxs.AsSlice())
|
||||
extras := fn(func(yield func(tailcfg.NodeID, key.NodePublic) bool) {
|
||||
for id, p := range nb.peers {
|
||||
if !yield(id, p.Key()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
rt := nb.routeMgr.Begin()
|
||||
rt.SetExtraAllowedIPs(extras)
|
||||
res := rt.Commit()
|
||||
|
||||
@@ -6,6 +6,7 @@ package ipnlocal
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"iter"
|
||||
"maps"
|
||||
"net/netip"
|
||||
"slices"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/util/dnsname"
|
||||
"tailscale.com/util/eventbus"
|
||||
"tailscale.com/util/mak"
|
||||
@@ -524,11 +524,14 @@ func TestNodeBackendRouteManagerExtras(t *testing.T) {
|
||||
nb.SetNetMap(&netmap.NetworkMap{Peers: []tailcfg.NodeView{p1}})
|
||||
|
||||
transit := netip.MustParsePrefix("fe80::1234/128")
|
||||
extrasFor := func(k key.NodePublic) views.Slice[netip.Prefix] {
|
||||
if k == p1.Key() {
|
||||
return views.SliceOf([]netip.Prefix{transit})
|
||||
extrasFor := func(peers iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix {
|
||||
var extras map[tailcfg.NodeID][]netip.Prefix
|
||||
for id, k := range peers {
|
||||
if k == p1.Key() {
|
||||
mak.Set(&extras, id, []netip.Prefix{transit})
|
||||
}
|
||||
}
|
||||
return views.Slice[netip.Prefix]{}
|
||||
return extras
|
||||
}
|
||||
|
||||
// Installing extras reports the peer's allowed prefixes as
|
||||
@@ -550,8 +553,8 @@ func TestNodeBackendRouteManagerExtras(t *testing.T) {
|
||||
}
|
||||
|
||||
// A hook that no longer returns extras removes them.
|
||||
changed = nb.updateRouteManagerExtras(func(key.NodePublic) views.Slice[netip.Prefix] {
|
||||
return views.Slice[netip.Prefix]{}
|
||||
changed = nb.updateRouteManagerExtras(func(iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix {
|
||||
return nil
|
||||
})
|
||||
if len(changed) != 1 {
|
||||
t.Errorf("clearing extras changed = %v; want just %v", changed, p1.Key())
|
||||
|
||||
Reference in New Issue
Block a user