policy/v2, servertest: add via port captures

A port-limited ACL entry beside a via grant, and a via tag with no
advertiser. Via HA replays now compare router filters by address.
This commit is contained in:
Kristoffer Dalby committed 2026-10-27 21:30:36 +00:00
1 parent 0a2689ae3f
commit 4c354f0c61
4 files changed
+16948 -6

No files matched your search

@@ -174,6 +174,21 @@ var grantSkipReasons = map[string]string{
// fails, so a fix must delete its entry.
"via-grant-v63/blastoise/filter": viaDstAddressForm,
"via-grant-v63/squirtle/filter": viaDstAddressForm,
// Contiguous pinned addresses; TestViaGrantHACompat compares these
// filters by address.
"via-grant-v64/client-a/filter": srcAddressForm,
"via-grant-v64/client-b/filter": srcAddressForm,
"via-grant-v64/plain/filter": srcAddressForm,
"via-grant-v64/srv/filter": srcAddressForm,
"via-grant-v64/via-empty/filter": srcAddressForm,
"via-grant-v64/via-r/filter": srcAddressForm,
"via-grant-v65/client-a/filter": srcAddressForm,
"via-grant-v65/client-b/filter": srcAddressForm,
"via-grant-v65/plain/filter": srcAddressForm,
"via-grant-v65/srv/filter": srcAddressForm,
"via-grant-v65/via-empty/filter": srcAddressForm,
"via-grant-v65/via-r/filter": srcAddressForm,
}
const viaDstAddressForm = "FILTER_ADDRESS_FORM: SaaS writes a single-address via destination as a bare address, headscale as a /32 prefix"
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+14 -6
View File
@@ -1,10 +1,10 @@
// This file implements data-driven via+HA compatibility tests using
// golden data captured from Tailscale SaaS (v37-v46). These scenarios
// golden data captured from Tailscale SaaS (v37-v46, v64, v65). These scenarios
// exercise the interaction between via grant steering and HA primary
// route election with varying combinations of shared/unique via tags,
// regular grants, and multiple HA pairs.
//
// Test data source: ../policy/v2/testdata/grant_results/via-grant-v{37..46}.hujson
// Test data source: ../policy/v2/testdata/grant_results/via-grant-v{37..46,64,65}.hujson
// Source format: github.com/juanfont/headscale/hscontrol/types/testcapture
package servertest_test
@@ -42,6 +42,8 @@ var viaHACompatTests = []struct {
{"via-grant-v44", "four-way HA, mixed via steering"},
{"via-grant-v45", "via+regular overlap, 4-way HA"},
{"via-grant-v46", "kitchen sink: mixed via+regular+HA"},
{"via-grant-v64", "port-limited ACL to a via prefix, non-via global primary"},
{"via-grant-v65", "via tag whose only node advertises nothing"},
}
// TestViaGrantHACompat loads golden captures from Tailscale SaaS that
@@ -185,6 +187,8 @@ func runViaHACompat(t *testing.T, c *testcapture.Capture) {
})
}
saasAddrs := saasAddrsByNode(c, clients)
// Compare each viewer's [tailcfg.MapResponse] against golden [netmap.NetworkMap].
// Route approvals reach clients asynchronously, so the peer-count wait
// above says nothing about routes, primaries or filters: retry the
@@ -197,7 +201,7 @@ func runViaHACompat(t *testing.T, c *testcapture.Capture) {
t.Run(viewerName, func(t *testing.T) {
requireNetmapHolds(t, func(tt require.TestingT) {
compareCaptureNetmap(tt, cl, capture, clients)
compareCaptureNetmap(tt, cl, capture, clients, saasAddrs, c.TestID+"/"+viewerName)
})
})
}
@@ -212,6 +216,8 @@ func compareCaptureNetmap(
viewer *servertest.TestClient,
want testcapture.Node,
clients map[string]*servertest.TestClient,
saasAddrs map[netip.Addr]string,
key string,
) {
if h, ok := t.(interface{ Helper() }); ok {
h.Helper()
@@ -327,9 +333,7 @@ func compareCaptureNetmap(
}
}
// Baseline PacketFilter sanity: count rules. Full per-rule dst-prefix
// comparison is done by the tailscale_routes_data compat test; here
// we only catch gross drift.
// The triples below drop multiplicity; the count keeps it.
if len(want.PacketFilterRules) > 0 {
gotLen := nm.PacketFilterRules.Len()
assert.Equalf(t, len(want.PacketFilterRules), gotLen,
@@ -338,6 +342,10 @@ func compareCaptureNetmap(
)
}
// By address: the policy layer compares wire form, which differs when
// SaaS lists contiguous addresses one by one.
comparePacketFilter(t, nm, want, clients, saasAddrs, key+"/packet-filter", viaCompatSkipReasons)
compareUserProfiles(t, nm, want.Netmap, clients)
}