all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
This commit is contained in:
parent
17236fd284
commit
4cca63155d
124 changed files with 1037 additions and 1011 deletions
|
|
@ -18,7 +18,7 @@ type grantCategory int
|
|||
|
||||
const (
|
||||
// grantCategoryRegular requires no per-node work. The pre-compiled
|
||||
// rules are complete and only need ReduceFilterRules.
|
||||
// rules are complete and only need [policyutil.ReduceFilterRules].
|
||||
grantCategoryRegular grantCategory = iota
|
||||
|
||||
// grantCategorySelf has autogroup:self destinations that must be
|
||||
|
|
@ -80,7 +80,7 @@ type viaGrantData struct {
|
|||
|
||||
// resolveViaDestinations splits a via grant's destinations into the
|
||||
// flat list of IP prefixes they resolve to plus a flag for
|
||||
// autogroup:internet. Every alias kind goes through Alias.Resolve so
|
||||
// autogroup:internet. Every alias kind goes through [Alias.Resolve] so
|
||||
// adding a new alias type to the policy parser does not silently
|
||||
// disappear from the via path. Non-IP alias kinds (tag, user, group,
|
||||
// wildcard) resolve to /32 host IPs that never overlap with subnet
|
||||
|
|
@ -116,7 +116,7 @@ func resolveViaDestinations(
|
|||
|
||||
// userNodeIndex maps user IDs to their untagged nodes. Built once per
|
||||
// policy or node-set change and read from many goroutines under
|
||||
// PolicyManager.mu; readers must hold the lock (or the snapshot
|
||||
// [PolicyManager.mu]; readers must hold the lock (or the snapshot
|
||||
// returned to them).
|
||||
type userNodeIndex map[uint][]types.NodeView
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ func buildUserNodeIndex(
|
|||
}
|
||||
|
||||
// compileNodeAttrs returns the per-node CapMap derived from policy
|
||||
// nodeAttrs plus the tailnet-wide RandomizeClientPort flag.
|
||||
// nodeAttrs plus the tailnet-wide [Policy.RandomizeClientPort] flag.
|
||||
//
|
||||
// Returns an error when a target alias fails to resolve so the caller
|
||||
// surfaces a corrupt policy instead of silently granting a partial set
|
||||
|
|
@ -163,18 +163,18 @@ func (pol *Policy) compileNodeAttrs(
|
|||
result[id] = capMap
|
||||
}
|
||||
|
||||
// nil RawMessage matches the wire format from a Tailscale-hosted
|
||||
// control plane: capabilities without companion data marshal as
|
||||
// `null` rather than `[]`. Storing nil keeps the merge stable
|
||||
// and lets the compat test diff cleanly against captured
|
||||
// netmaps.
|
||||
// nil [tailcfg.RawMessage] matches the wire format from a
|
||||
// Tailscale-hosted control plane: capabilities without companion
|
||||
// data marshal as null rather than []. Storing nil keeps the
|
||||
// merge stable and lets the compat test diff cleanly against
|
||||
// captured netmaps.
|
||||
if _, exists := capMap[attr]; !exists {
|
||||
capMap[attr] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Cache each node's IPs once per call. Without the cache, the
|
||||
// node-attr inner loop would call NodeView.IPs() once per attr
|
||||
// node-attr inner loop would call [types.NodeView.IPs] once per attr
|
||||
// per node — O(grants × nodes) allocations of a 2-element slice
|
||||
// for what is invariant per node within a single policy compile.
|
||||
type nodeIPs struct {
|
||||
|
|
@ -221,10 +221,11 @@ func (pol *Policy) compileNodeAttrs(
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// compileGrants resolves all policy grants into compiledGrant structs.
|
||||
// compileGrants resolves all policy grants into [compiledGrant] structs.
|
||||
// Source resolution and non-self destination resolution happens once
|
||||
// here. This is the single resolution path that replaces the
|
||||
// duplicated work in compileFilterRules and compileGrantWithAutogroupSelf.
|
||||
// duplicated work in [Policy.compileFilterRules] and the autogroup:self
|
||||
// expansion.
|
||||
func (pol *Policy) compileGrants(
|
||||
users types.Users,
|
||||
nodes views.Slice[types.NodeView],
|
||||
|
|
@ -256,7 +257,7 @@ func (pol *Policy) compileGrants(
|
|||
return compiled
|
||||
}
|
||||
|
||||
// compileOneGrant resolves a single grant into a compiledGrant.
|
||||
// compileOneGrant resolves a single grant into a [compiledGrant].
|
||||
// All source resolution happens here. Non-self, non-via destination
|
||||
// resolution also happens here. Per-node data (self dests, via
|
||||
// matching) is stored for deferred compilation.
|
||||
|
|
@ -341,7 +342,7 @@ func (pol *Policy) compileOneGrant(
|
|||
|
||||
// compileOneViaGrant resolves sources for a via grant and stores the
|
||||
// deferred per-node data. The actual via-node matching and route
|
||||
// intersection happens in compileViaForNode.
|
||||
// intersection happens in [compileViaForNode].
|
||||
func (pol *Policy) compileOneViaGrant(
|
||||
grant Grant,
|
||||
users types.Users,
|
||||
|
|
@ -404,8 +405,8 @@ func (pol *Policy) compileOneViaGrant(
|
|||
// resolveSources resolves grant sources per-alias, returning the
|
||||
// resolved addresses and a separate slice of non-wildcard sources.
|
||||
// This is the canonical source-resolution path. Its output lands in
|
||||
// compiledGrant.srcIPStrings (among other places) and callers on the
|
||||
// hot path should prefer reading that over calling Resolve again.
|
||||
// [compiledGrant.srcIPStrings] (among other places) and callers on the
|
||||
// hot path should prefer reading that over calling [Alias.Resolve] again.
|
||||
func resolveSources(
|
||||
pol *Policy,
|
||||
sources Aliases,
|
||||
|
|
@ -490,8 +491,9 @@ func buildSrcIPStrings(
|
|||
}
|
||||
|
||||
// compileOtherDests compiles filter rules for non-self, non-via
|
||||
// destinations. This produces both DstPorts rules (from
|
||||
// InternetProtocols) and CapGrant rules (from App).
|
||||
// destinations. This produces both [tailcfg.FilterRule.DstPorts] rules
|
||||
// (from [Grant.InternetProtocols]) and [tailcfg.CapGrant] rules (from
|
||||
// [Grant.App]).
|
||||
func (pol *Policy) compileOtherDests(
|
||||
users types.Users,
|
||||
nodes views.Slice[types.NodeView],
|
||||
|
|
@ -580,7 +582,7 @@ func (pol *Policy) compileOtherDests(
|
|||
return rules
|
||||
}
|
||||
|
||||
// hasPerNodeGrants reports whether any compiled grant requires
|
||||
// hasPerNodeGrants reports whether any [compiledGrant] requires
|
||||
// per-node filter compilation (via grants or autogroup:self).
|
||||
func hasPerNodeGrants(grants []compiledGrant) bool {
|
||||
for i := range grants {
|
||||
|
|
@ -592,10 +594,10 @@ func hasPerNodeGrants(grants []compiledGrant) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// globalFilterRules extracts global filter rules from compiled
|
||||
// grants. Via grants produce no global rules (they are per-node
|
||||
// only); regular grants contribute their full pre-compiled ruleset;
|
||||
// self grants contribute their non-self portion.
|
||||
// globalFilterRules extracts global filter rules from [compiledGrant]s.
|
||||
// Via grants produce no global rules (they are per-node only); regular
|
||||
// grants contribute their full pre-compiled ruleset; self grants
|
||||
// contribute their non-self portion.
|
||||
func globalFilterRules(grants []compiledGrant) []tailcfg.FilterRule {
|
||||
var rules []tailcfg.FilterRule
|
||||
|
||||
|
|
@ -804,10 +806,11 @@ func compileViaForNode(
|
|||
return nil
|
||||
}
|
||||
|
||||
// SubnetRoutes excludes exit routes, so the overlap gate below sees
|
||||
// only subnet advertisements. autogroup:internet on a via-tagged
|
||||
// exit advertiser is handled separately because its eligibility is
|
||||
// per-node (IsExitNode) rather than per-prefix overlap.
|
||||
// [types.NodeView.SubnetRoutes] excludes exit routes, so the overlap
|
||||
// gate below sees only subnet advertisements. autogroup:internet on
|
||||
// a via-tagged exit advertiser is handled separately because its
|
||||
// eligibility is per-node ([types.NodeView.IsExitNode]) rather than
|
||||
// per-prefix overlap.
|
||||
nodeSubnetRoutes := node.SubnetRoutes()
|
||||
|
||||
var viaDstPrefixes []netip.Prefix
|
||||
|
|
@ -826,11 +829,11 @@ func compileViaForNode(
|
|||
}
|
||||
|
||||
// autogroup:internet on a via-tagged exit advertiser becomes a rule
|
||||
// whose DstPorts enumerate util.TheInternet(). The matchers derived
|
||||
// from this rule let Node.CanAccess surface the exit node to the
|
||||
// grant source via DestsIsTheInternet. ReduceFilterRules strips the
|
||||
// rule from the wire format on non-exit advertisers, preserving
|
||||
// SaaS PacketFilter encoding.
|
||||
// whose DstPorts enumerate [util.TheInternet]. The matchers derived
|
||||
// from this rule let [types.NodeView.CanAccess] surface the exit node
|
||||
// to the grant source via [matcher.Match.DestsIsTheInternet].
|
||||
// [policyutil.ReduceFilterRules] strips the rule from the wire format
|
||||
// on non-exit advertisers, preserving SaaS PacketFilter encoding.
|
||||
if cg.via.hasAutoGroupInternet && node.IsExitNode() {
|
||||
viaDstPrefixes = append(
|
||||
viaDstPrefixes,
|
||||
|
|
|
|||
|
|
@ -735,10 +735,11 @@ func TestTagPropagationToPeerMap(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotEmpty(t, matchersForUser2, "MatchersForNode should return non-empty matchers (at least self-access rule)")
|
||||
|
||||
// Test ReduceNodes logic with the updated nodes and matchers
|
||||
// This is what buildTailPeers does - it takes peers from ListPeers (which might include user1)
|
||||
// and filters them using ReduceNodes with the updated matchers
|
||||
// Inline the ReduceNodes logic to avoid import cycle
|
||||
// Test [policy.ReduceNodes] logic with the updated nodes and matchers
|
||||
// This is what [mapper.MapResponseBuilder.buildTailPeers] does - it takes peers from
|
||||
// [state.State.ListPeers] (which might include user1) and filters them using
|
||||
// [policy.ReduceNodes] with the updated matchers
|
||||
// Inline the [policy.ReduceNodes] logic to avoid import cycle
|
||||
user2View := user2Node.View()
|
||||
user1UpdatedView := user1NodeUpdated.View()
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
// - check: every listed user reaches every dst via a check-action
|
||||
// rule specifically (accept-only matches fail the assertion).
|
||||
|
||||
// SSHPolicyTestResult is the outcome of a single SSHPolicyTest.
|
||||
// SSHPolicyTestResult is the outcome of a single [SSHPolicyTest].
|
||||
type SSHPolicyTestResult struct {
|
||||
Src string `json:"src"`
|
||||
Passed bool `json:"passed"`
|
||||
|
|
@ -122,7 +122,7 @@ func checkFailReason(res SSHPolicyTestResult, user, dst string) string {
|
|||
}
|
||||
|
||||
// RunSSHTests evaluates the live policy's sshTests block and wraps any
|
||||
// failure in errSSHPolicyTestsFailed.
|
||||
// failure in [errSSHPolicyTestsFailed].
|
||||
func (pm *PolicyManager) RunSSHTests() error {
|
||||
if pm == nil || pm.pol == nil || len(pm.pol.SSHTests) == 0 {
|
||||
return nil
|
||||
|
|
@ -162,7 +162,7 @@ func evaluateSSHTests(
|
|||
}
|
||||
|
||||
// runSSHPolicyTests evaluates every sshTests entry. The cache is keyed
|
||||
// by dst NodeID so repeat destinations only compile once per pass.
|
||||
// by dst [types.NodeID] so repeat destinations only compile once per pass.
|
||||
func runSSHPolicyTests(
|
||||
pol *Policy,
|
||||
users []types.User,
|
||||
|
|
@ -389,7 +389,7 @@ func appendUserDst(m map[string][]string, user, dst string) map[string][]string
|
|||
|
||||
// resolveSSHTestSource returns the src's principal addresses and, for
|
||||
// user-shaped sources, the user ID (so autogroup:self can scope to it).
|
||||
// Tag, host, and IP sources return userID 0.
|
||||
// [Tag], [Host], and IP sources return userID 0.
|
||||
func resolveSSHTestSource(
|
||||
src Alias,
|
||||
pol *Policy,
|
||||
|
|
@ -428,9 +428,10 @@ func resolveSSHTestSource(
|
|||
}
|
||||
|
||||
// resolveSSHTestDestNodes maps each dst alias to its destination
|
||||
// NodeViews. autogroup:self needs special handling: it cannot resolve
|
||||
// without per-node context, so it walks the node set keyed on src's
|
||||
// owning user. Other aliases resolve to an IPSet and match via InIPSet.
|
||||
// [types.NodeView]s. autogroup:self needs special handling: it cannot
|
||||
// resolve without per-node context, so it walks the node set keyed on
|
||||
// src's owning user. Other aliases resolve to an [netipx.IPSet] and match
|
||||
// via [types.NodeView.InIPSet].
|
||||
func resolveSSHTestDestNodes(
|
||||
dsts SSHTestDestinations,
|
||||
pol *Policy,
|
||||
|
|
@ -527,8 +528,8 @@ func resolveSSHTestDestNodes(
|
|||
return out, emptyDsts, nil
|
||||
}
|
||||
|
||||
// prefixesToIPSet builds the IPSet that InIPSet expects on the node
|
||||
// side.
|
||||
// prefixesToIPSet builds the [netipx.IPSet] that [types.NodeView.InIPSet]
|
||||
// expects on the node side.
|
||||
func prefixesToIPSet(prefixes []netip.Prefix) (*netipx.IPSet, error) {
|
||||
var b netipx.IPSetBuilder
|
||||
|
||||
|
|
@ -539,9 +540,9 @@ func prefixesToIPSet(prefixes []netip.Prefix) (*netipx.IPSet, error) {
|
|||
return b.IPSet()
|
||||
}
|
||||
|
||||
// compiledSSHPolicy returns the per-node compiled SSH policy, caching
|
||||
// compiledSSHPolicy returns the per-node compiled [tailcfg.SSHPolicy], caching
|
||||
// on miss. baseURL is empty because reachability only checks for the
|
||||
// presence of HoldAndDelegate, not its value.
|
||||
// presence of [tailcfg.SSHAction.HoldAndDelegate], not its value.
|
||||
func compiledSSHPolicy(
|
||||
pol *Policy,
|
||||
users []types.User,
|
||||
|
|
@ -607,8 +608,8 @@ func reachability(
|
|||
return acceptHit, checkHit
|
||||
}
|
||||
|
||||
// principalContainsAddr reports whether any principal's NodeIP matches
|
||||
// srcAddr exactly (the SSH compiler emits one principal per source IP).
|
||||
// principalContainsAddr reports whether any principal's [tailcfg.SSHPrincipal.NodeIP]
|
||||
// matches srcAddr exactly (the SSH compiler emits one principal per source IP).
|
||||
func principalContainsAddr(
|
||||
principals []*tailcfg.SSHPrincipal,
|
||||
srcAddr netip.Addr,
|
||||
|
|
@ -635,7 +636,7 @@ func principalContainsAddr(
|
|||
return false
|
||||
}
|
||||
|
||||
// sshUserMapAllows reports whether SSHUsers permits user. The SSHUsers
|
||||
// sshUserMapAllows reports whether [SSHUsers] permits user. The [SSHUsers]
|
||||
// wire shape (see filter.go compileSSHPolicy):
|
||||
//
|
||||
// - SSHUsers["root"] == "root" allows root; == "" disallows it.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package v2
|
|||
// Tailscale-hosted control plane emits where headscale has no
|
||||
// equivalent concept yet. The compat test in
|
||||
// tailscale_nodeattrs_compat_test.go builds the self-view CapMap via
|
||||
// [types.NodeView.TailNode] -- the same call the mapper makes -- and
|
||||
// [types.Node.TailNode] -- the same call the mapper makes -- and
|
||||
// strips these from BOTH sides before [cmp.Diff]; every other cap is
|
||||
// compared in full as it lands on the wire.
|
||||
//
|
||||
|
|
@ -30,8 +30,9 @@ import (
|
|||
// (suggest-exit-node, dns-subdomain-resolve — see
|
||||
// ipn/ipnlocal/local.go:7534 and node_backend.go:745) are emitted only
|
||||
// when the peer satisfies the cap's emission condition. This function
|
||||
// encodes those conditions; the mapper calls it from buildTailPeers and
|
||||
// the compat test calls it to compute the expected per-peer wire shape.
|
||||
// encodes those conditions; the mapper calls it from
|
||||
// [mapper.MapResponseBuilder.buildTailPeers] and the compat test calls
|
||||
// it to compute the expected per-peer wire shape.
|
||||
func PeerCapMap(peer types.NodeView, peerSelfCaps tailcfg.NodeCapMap) tailcfg.NodeCapMap {
|
||||
if len(peerSelfCaps) == 0 {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
// errPolicyTestsFailed and errSSHPolicyTestsFailed share the
|
||||
// "test(s) failed" prefix but stay distinct so callers can use
|
||||
// errors.Is to tell ACL-test and SSH-test failures apart.
|
||||
// [errors.Is] to tell ACL-test and SSH-test failures apart.
|
||||
var (
|
||||
errPolicyTestsFailed = errors.New("test(s) failed")
|
||||
errSSHPolicyTestsFailed = errors.New("test(s) failed")
|
||||
|
|
@ -55,7 +55,7 @@ type PolicyTest struct {
|
|||
|
||||
// SSHPolicyTest is one entry in the policy's `sshTests` block. The
|
||||
// accept/deny/check arrays carry usernames, not destinations — every
|
||||
// listed user is asserted against every entry in Dst.
|
||||
// listed user is asserted against every entry in [SSHPolicyTest.Dst].
|
||||
type SSHPolicyTest struct {
|
||||
// Src is a single source alias (user, group, tag, host, or IP).
|
||||
Src Alias `json:"src"`
|
||||
|
|
@ -78,7 +78,7 @@ type SSHPolicyTest struct {
|
|||
}
|
||||
|
||||
// SSHTestDestinations is the typed list of destination aliases an
|
||||
// sshTests entry targets. validateSSHTestDestination enforces the
|
||||
// sshTests entry targets. [validateSSHTestDestination] enforces the
|
||||
// SSH-specific shape rules (no :port, no CIDR, no autogroup:internet,
|
||||
// known tag).
|
||||
type SSHTestDestinations []Alias
|
||||
|
|
@ -100,7 +100,7 @@ func (d *SSHTestDestinations) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
// UnmarshalJSON parses each typed field. An empty src lands as a nil
|
||||
// Alias so validation surfaces ErrSSHTestEmptySrc rather than a parser
|
||||
// [Alias] so validation surfaces [ErrSSHTestEmptySrc] rather than a parser
|
||||
// failure.
|
||||
func (t *SSHPolicyTest) UnmarshalJSON(b []byte) error {
|
||||
var raw struct {
|
||||
|
|
@ -134,7 +134,7 @@ func (t *SSHPolicyTest) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// PolicyTestResult is the outcome of a single PolicyTest.
|
||||
// PolicyTestResult is the outcome of a single [PolicyTest].
|
||||
type PolicyTestResult struct {
|
||||
Src string `json:"src"`
|
||||
Proto Protocol `json:"proto,omitempty"`
|
||||
|
|
@ -216,7 +216,7 @@ func (pm *PolicyManager) RunTests() error {
|
|||
}
|
||||
|
||||
// evaluateTests runs the `tests` block against a fresh compilation of pol.
|
||||
// It is the user-write sandbox: the live PolicyManager state is left
|
||||
// It is the user-write sandbox: the live [PolicyManager] state is left
|
||||
// untouched, so a failing test rejects the write without side effects.
|
||||
func evaluateTests(pol *Policy, users []types.User, nodes views.Slice[types.NodeView]) error {
|
||||
if pol == nil || len(pol.Tests) == 0 {
|
||||
|
|
@ -262,7 +262,7 @@ func runPolicyTests(pol *Policy, filter []tailcfg.FilterRule, users []types.User
|
|||
return results
|
||||
}
|
||||
|
||||
// runPolicyTest evaluates one PolicyTest.
|
||||
// runPolicyTest evaluates one [PolicyTest].
|
||||
func runPolicyTest(test PolicyTest, pol *Policy, filter []tailcfg.FilterRule, users []types.User, nodes views.Slice[types.NodeView]) PolicyTestResult {
|
||||
res := PolicyTestResult{
|
||||
Src: test.Src,
|
||||
|
|
@ -322,8 +322,8 @@ func runPolicyTest(test PolicyTest, pol *Policy, filter []tailcfg.FilterRule, us
|
|||
return res
|
||||
}
|
||||
|
||||
// resolveTestSource resolves the Src alias of a PolicyTest into a slice of
|
||||
// netip.Prefix. parseAlias + Alias.Resolve cover every alias type the rest
|
||||
// resolveTestSource resolves the Src alias of a [PolicyTest] into a slice of
|
||||
// [netip.Prefix]. [parseAlias] + [Alias.Resolve] cover every alias type the rest
|
||||
// of the policy engine supports, so tests inherit alias semantics for free.
|
||||
func resolveTestSource(src string, pol *Policy, users []types.User, nodes views.Slice[types.NodeView]) ([]netip.Prefix, error) {
|
||||
alias, err := parseAlias(src)
|
||||
|
|
@ -377,13 +377,13 @@ func evalReachability(srcPrefixes []netip.Prefix, dst string, proto Protocol, po
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// parseDestinationAlias is a thin wrapper over AliasWithPorts.UnmarshalJSON
|
||||
// parseDestinationAlias is a thin wrapper over [AliasWithPorts.UnmarshalJSON]
|
||||
// so callers can hand it a bare `"host:port"` string without re-implementing
|
||||
// the parse logic.
|
||||
func parseDestinationAlias(dst string) (*AliasWithPorts, error) {
|
||||
var awp AliasWithPorts
|
||||
|
||||
// AliasWithPorts.UnmarshalJSON expects a quoted JSON string, so wrap.
|
||||
// [AliasWithPorts.UnmarshalJSON] expects a quoted JSON string, so wrap.
|
||||
err := awp.UnmarshalJSON([]byte(`"` + dst + `"`))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -425,9 +425,9 @@ func srcReachesDst(src netip.Prefix, dstPrefixes []netip.Prefix, ports []tailcfg
|
|||
}
|
||||
|
||||
// ruleMatchesSource reports whether the rule's source list contains src.
|
||||
// SrcIPs may be CIDR, single addresses, IP ranges (`a-b`), or `*`; we use
|
||||
// util.ParseIPSet to cover all of those uniformly. Unparseable entries
|
||||
// are skipped (the rule compiler emits well-formed strings, so this is
|
||||
// [tailcfg.FilterRule.SrcIPs] may be CIDR, single addresses, IP ranges (`a-b`),
|
||||
// or `*`; we use [util.ParseIPSet] to cover all of those uniformly. Unparseable
|
||||
// entries are skipped (the rule compiler emits well-formed strings, so this is
|
||||
// defence-in-depth, not error handling).
|
||||
func ruleMatchesSource(rule tailcfg.FilterRule, src netip.Prefix) bool {
|
||||
for _, raw := range rule.SrcIPs {
|
||||
|
|
@ -445,9 +445,10 @@ func ruleMatchesSource(rule tailcfg.FilterRule, src netip.Prefix) bool {
|
|||
}
|
||||
|
||||
// ruleMatchesProto reports whether the rule permits any of requestedProtos.
|
||||
// An unset rule.IPProto means "any protocol" and matches everything.
|
||||
// requestedProtos is the per-test protocol set: a single proto for an
|
||||
// explicit test.Proto, or the default set when test.Proto is empty.
|
||||
// An unset [tailcfg.FilterRule.IPProto] means "any protocol" and matches
|
||||
// everything. requestedProtos is the per-test protocol set: a single proto
|
||||
// for an explicit [PolicyTest.Proto], or the default set when
|
||||
// [PolicyTest.Proto] is empty.
|
||||
func ruleMatchesProto(rule tailcfg.FilterRule, requestedProtos []int) bool {
|
||||
if len(rule.IPProto) == 0 {
|
||||
return true
|
||||
|
|
@ -479,7 +480,7 @@ func ruleAllowsAnyDest(rule tailcfg.FilterRule, dstPrefixes []netip.Prefix, port
|
|||
return false
|
||||
}
|
||||
|
||||
// destEntryMatchesPrefixes reports whether the rule's NetPortRange.IP
|
||||
// destEntryMatchesPrefixes reports whether the rule's [tailcfg.NetPortRange.IP]
|
||||
// (CIDR, single IP, IP range, or "*") covers any prefix in dstPrefixes.
|
||||
func destEntryMatchesPrefixes(dp tailcfg.NetPortRange, dstPrefixes []netip.Prefix) bool {
|
||||
set, err := util.ParseIPSet(dp.IP, nil)
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ var (
|
|||
//
|
||||
// Brackets are only accepted around IPv6 addresses, not IPv4, hostnames, or other alias types.
|
||||
// Bracket stripping reduces both forms to bare "addr:port" or "addr/prefix:port",
|
||||
// which the normal LastIndex(":") split handles correctly because port strings
|
||||
// never contain colons.
|
||||
// which the normal [strings.LastIndex] of ":" split handles correctly because
|
||||
// port strings never contain colons.
|
||||
func splitDestinationAndPort(input string) (string, string, error) {
|
||||
// Handle RFC 3986 bracketed IPv6 (e.g. "[::1]:80" or "[fd7a::1]/128:80,443").
|
||||
// Strip brackets after validation and fall through to normal parsing.
|
||||
|
|
@ -82,7 +82,7 @@ func splitDestinationAndPort(input string) (string, string, error) {
|
|||
return destination, port, nil
|
||||
}
|
||||
|
||||
// parsePortRange parses a port definition string and returns a slice of PortRange structs.
|
||||
// parsePortRange parses a port definition string and returns a slice of [tailcfg.PortRange] structs.
|
||||
func parsePortRange(portDef string) ([]tailcfg.PortRange, error) {
|
||||
if portDef == "*" {
|
||||
return []tailcfg.PortRange{tailcfg.PortRangeAny}, nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue