ipnext,ipnlocal,wgengine/filter: add extension hooks for custom filter matchers
Add PacketMatch hooks to the packet filter, allowing extensions to customize filtering decisions: - IngressAllowHooks: checked in RunIn after pre() but before the standard runIn4/runIn6 match rules. Hooks can accept packets to destinations outside the local IP set. First match wins; the returned why string is used for logging. - LinkLocalAllowHooks: checked inside pre() for both ingress and egress, providing exceptions to the default policy of dropping link-local unicast packets. First match wins. The GCP DNS address (169.254.169.254) is always allowed regardless of hooks. PacketMatch returns (match bool, why string) to provide a log reason consistent with the existing filter functions. Hooks are registered via the new FilterHooks struct in ipnext.Hooks and wired through to filter.Filter in LocalBackend.updateFilterLocked. Fixes tailscale/corp#35989 Fixes tailscale/corp#37207 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
committed by
mzbenami
parent
dc80fd6324
commit
811fe7d18e
@@ -21,6 +21,7 @@ import (
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/mapx"
|
||||
"tailscale.com/wgengine/filter"
|
||||
)
|
||||
|
||||
// Extension augments LocalBackend with additional functionality.
|
||||
@@ -377,6 +378,39 @@ 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -2884,7 +2884,11 @@ func (b *LocalBackend) updateFilterLocked(prefs ipn.PrefsView) {
|
||||
b.setFilter(filter.NewShieldsUpFilter(localNets, logNets, oldFilter, b.logf))
|
||||
} else {
|
||||
b.logf("[v1] netmap packet filter: %v filters", len(packetFilter))
|
||||
b.setFilter(filter.New(packetFilter, b.srcIPHasCapForFilter, localNets, logNets, oldFilter, b.logf))
|
||||
filt := filter.New(packetFilter, b.srcIPHasCapForFilter, localNets, logNets, oldFilter, b.logf)
|
||||
|
||||
filt.IngressAllowHooks = b.extHost.Hooks().Filter.IngressAllowHooks
|
||||
filt.LinkLocalAllowHooks = b.extHost.Hooks().Filter.LinkLocalAllowHooks
|
||||
b.setFilter(filt)
|
||||
}
|
||||
// The filter for a jailed node is the exact same as a ShieldsUp filter.
|
||||
oldJailedFilter := b.e.GetJailedFilter()
|
||||
|
||||
Reference in New Issue
Block a user