Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+149
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package ipnext defines types and interfaces used for extending the core LocalBackend
|
||||
@@ -6,6 +6,7 @@
|
||||
package ipnext
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"iter"
|
||||
@@ -17,10 +18,15 @@ import (
|
||||
"tailscale.com/ipn/ipnauth"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tailcfg/peercap"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/mapx"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/types/views"
|
||||
"tailscale.com/wgengine/filter"
|
||||
)
|
||||
|
||||
// Extension augments LocalBackend with additional functionality.
|
||||
@@ -109,7 +115,14 @@ var extensions mapx.OrderedMap[string, *Definition]
|
||||
//
|
||||
// It panics if newExt is nil or if an extension with the same name
|
||||
// has already been registered.
|
||||
//
|
||||
// As a backstop for feature packages that forget to consult
|
||||
// feature.Register, it does nothing if the named feature was disabled
|
||||
// via the TS_DISABLE_FEATURE environment variable.
|
||||
func RegisterExtension(name string, newExt NewExtensionFn) {
|
||||
if feature.Disabled(name) {
|
||||
return
|
||||
}
|
||||
if newExt == nil {
|
||||
panic(fmt.Sprintf("ipnext: newExt is nil: %q", name))
|
||||
}
|
||||
@@ -201,6 +214,16 @@ type Host interface {
|
||||
// NodeBackend returns the [NodeBackend] for the currently active node
|
||||
// (which is approximately the same as the current profile).
|
||||
NodeBackend() NodeBackend
|
||||
|
||||
// AuthReconfigAsync asynchronously pushes a new configuration into wgengine,
|
||||
// if engine updates are not currently blocked, based on the cached netmap and
|
||||
// user prefs. The reconfiguration is applied to [ipnlocal.LocalBackend]'s currently
|
||||
// active node at the time of execution.
|
||||
//
|
||||
// AuthReconfigAsync should not be called at a high rate (i.e., more often
|
||||
// than prefs and netmap changes), except in experimental or proof-of-concept
|
||||
// contexts, since reconfiguration is known to be slow.
|
||||
AuthReconfigAsync()
|
||||
}
|
||||
|
||||
// SafeBackend is a subset of the [ipnlocal.LocalBackend] type's methods that
|
||||
@@ -212,6 +235,33 @@ type SafeBackend interface {
|
||||
TailscaleVarRoot() string
|
||||
}
|
||||
|
||||
// NotifyWatcher is a subset of [tailscale.com/ipn/ipnlocal.LocalBackend]
|
||||
// for extensions that subscribe to the IPN notification bus from within tailscaled.
|
||||
//
|
||||
// Unlike [SafeBackend], its methods acquire LocalBackend’s internal mutex
|
||||
// and must not be called from extension hooks,
|
||||
// instead call them from a goroutine started by [Extension.Init].
|
||||
type NotifyWatcher interface {
|
||||
// WatchNotifications subscribes to the ipn.Notify message bus notification
|
||||
// messages.
|
||||
//
|
||||
// WatchNotifications blocks until ctx is done.
|
||||
//
|
||||
// The provided onWatchAdded, if non-nil, will be called once the watcher
|
||||
// is installed.
|
||||
//
|
||||
// The provided fn will be called for each notification. It will only be
|
||||
// called with non-nil pointers. The caller must not modify roNotify. If
|
||||
// fn returns false, the watch also stops.
|
||||
//
|
||||
// Failure to consume many notifications in a row will result in one final
|
||||
// notification with ErrMessage set, followed by the watch closing, unless mask
|
||||
// includes ipn.NotifyInProcessNoDisconnect. Watchers using
|
||||
// NotifyInProcessNoDisconnect must not call back into LocalBackend from fn or
|
||||
// wait on work that might call back into LocalBackend.
|
||||
WatchNotifications(ctx context.Context, mask ipn.NotifyWatchOpt, onWatchAdded func(), fn func(roNotify *ipn.Notify) (keepGoing bool))
|
||||
}
|
||||
|
||||
// ExtensionServices provides access to the [Host]'s extension management services,
|
||||
// such as fetching active extensions.
|
||||
type ExtensionServices interface {
|
||||
@@ -362,6 +412,12 @@ type Hooks struct {
|
||||
// is created. It is called with the LocalBackend locked.
|
||||
NewControlClient feature.Hooks[NewControlClientCallback]
|
||||
|
||||
// OnNetMapToggle is called (with LocalBackend.mu held) when the network map
|
||||
// is toggled from nil to non-nil, or non-nil to nil. This usually happens
|
||||
// when the client connects to the control plane and receives the initial MapResponse,
|
||||
// or when the client disconnects and the network map is cleared.
|
||||
OnNetMapToggle feature.Hooks[func(*netmap.NetworkMap)]
|
||||
|
||||
// OnSelfChange is called (with LocalBackend.mu held) when the self node
|
||||
// changes, including changing to nothing (an invalid view).
|
||||
OnSelfChange feature.Hooks[func(tailcfg.NodeView)]
|
||||
@@ -377,21 +433,108 @@ type Hooks struct {
|
||||
// ShouldUploadServices reports whether this node should include services
|
||||
// in Hostinfo from the portlist extension.
|
||||
ShouldUploadServices feature.Hook[func() bool]
|
||||
|
||||
// Filter contains hooks for the packet filter.
|
||||
// See [filter.Filter] for details on how these hooks are invoked.
|
||||
Filter FilterHooks
|
||||
|
||||
// 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 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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(peers iter.Seq2[tailcfg.NodeID, key.NodePublic]) map[tailcfg.NodeID][]netip.Prefix]
|
||||
|
||||
// ExtraRouterConfigRoutes returns a view of prefixes to append to [router.Config.Routes].
|
||||
//
|
||||
// Routes goes through the WireGuard engine which makes efforts to avoid
|
||||
// unnecessary reconfiguration by checking that things have actually changed.
|
||||
// So implementors should make sure that the order of the prefixes is stable
|
||||
// and that we don't have duplicate entries.
|
||||
//
|
||||
// The returned slice should not be mutated by the extension after it is returned.
|
||||
//
|
||||
// The hook is called with LocalBackend's mutex locked.
|
||||
ExtraRouterConfigRoutes feature.Hook[func() views.Slice[netip.Prefix]]
|
||||
}
|
||||
|
||||
// FilterHooks contains hooks that extensions can use to customize the packet
|
||||
// filter. Field names match the corresponding fields in filter.Filter.
|
||||
type FilterHooks struct {
|
||||
// IngressAllowHooks are hooks that allow extensions to accept inbound
|
||||
// packets beyond the standard filter rules. Packets that are not dropped
|
||||
// by the direction-agnostic pre-check, but would be not accepted by the
|
||||
// main filter rules, including the check for destinations in the node's
|
||||
// local IP set, will be accepted if they match one of these hooks.
|
||||
// As of 2026-02-24, the ingress filter does not implement explicit drop
|
||||
// rules, but if it does, an explicitly dropped packet will be dropped,
|
||||
// and these hooks will not be evaluated.
|
||||
//
|
||||
// Processing of hooks stop after the first one that returns true.
|
||||
// The returned why string of the first match is used in logging.
|
||||
// Returning false does not drop the packet.
|
||||
// See also [filter.Filter.IngressAllowHooks].
|
||||
IngressAllowHooks feature.Hooks[filter.PacketMatch]
|
||||
|
||||
// LinkLocalAllowHooks are hooks that provide exceptions to the default
|
||||
// policy of dropping link-local unicast packets. They run inside the
|
||||
// direction-agnostic pre-checks for both ingress and egress.
|
||||
//
|
||||
// A hook can allow a link-local packet to pass the link-local check,
|
||||
// but the packet is still subject to all other filter rules, and could be
|
||||
// dropped elsewhere. Matching link-local packets are not logged.
|
||||
// See also [filter.Filter.LinkLocalAllowHooks].
|
||||
LinkLocalAllowHooks feature.Hooks[filter.PacketMatch]
|
||||
}
|
||||
|
||||
// NodeBackend is an interface to query the current node and its peers.
|
||||
//
|
||||
// It is not a snapshot in time but is locked to a particular node.
|
||||
type NodeBackend interface {
|
||||
// Self returns the current node.
|
||||
Self() tailcfg.NodeView
|
||||
|
||||
// AppendMatchingPeers appends all peers that match the predicate
|
||||
// to the base slice and returns it.
|
||||
AppendMatchingPeers(base []tailcfg.NodeView, pred func(tailcfg.NodeView) bool) []tailcfg.NodeView
|
||||
|
||||
// Peers returns all the current peers.
|
||||
Peers() []tailcfg.NodeView
|
||||
|
||||
// PeerCaps returns the capabilities that src has to this node.
|
||||
PeerCaps(src netip.Addr) tailcfg.PeerCapMap
|
||||
|
||||
// PeerHasCap reports whether the peer has the specified peer capability.
|
||||
PeerHasCap(peer tailcfg.NodeView, cap tailcfg.PeerCapability) bool
|
||||
PeerHasCap(peer tailcfg.NodeView, cap peercap.Cap) bool
|
||||
|
||||
// PeerAPIBase returns the "http://ip:port" URL base to reach peer's
|
||||
// PeerAPI, or the empty string if the peer is invalid or doesn't support
|
||||
@@ -408,4 +551,8 @@ type NodeBackend interface {
|
||||
// node that the portlist service collection is desirable, should it
|
||||
// choose to report them.
|
||||
CollectServices() bool
|
||||
|
||||
// DERPMap returns the current DERP map from the current netmap,
|
||||
// or nil if there is no netmap.
|
||||
DERPMap() *tailcfg.DERPMap
|
||||
}
|
||||
Reference in new issue
Block a user