appc,feature/conn25: prevent clients from forwarding DNS requests and
modifying DNS responses for domains they are also connectors for For Connectors 2025, determine if a client is configured as a connector and what domains it is a connector for. When acting as a client, don't install Split DNS routes to other connectors for those domains, and don't alter DNS responses for those domains. The responses are forwarded back to the original client, which in turn does the alteration, swapping the real IP for a Magic IP. A client is also a connector for a domain if it has tags that overlap with tags in the configured policy, and --advertise-connector=true in the prefs (not in the self-node Hostinfo from the netmap). We use the prefs as the source of truth because control only gets a copy from the prefs, and may drift. And the AppConnector field is currently zeroed out in the self-node Hostinfo from control. The extension adds a ProfileStateChange hook to process prefs changes, and the config type is split into prefs and nodeview sub-configs. Fixes tailscale/corp#39317 Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
committed by
mzbenami
parent
4f47c3c93d
commit
1dc08f4d41
+205
-51
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"go4.org/mem"
|
||||
"go4.org/netipx"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnext"
|
||||
"tailscale.com/net/dns"
|
||||
"tailscale.com/net/packet"
|
||||
@@ -339,7 +341,9 @@ func TestHandleConnectorTransitIPRequest(t *testing.T) {
|
||||
// Use the same Conn25 for each request in the test and seed it with a test app name.
|
||||
c := newConn25(logger.Discard)
|
||||
c.connector.config = config{
|
||||
appsByName: map[string]appctype.Conn25Attr{appName: {}},
|
||||
nv: nodeViewConfig{
|
||||
appsByName: map[string]appctype.Conn25Attr{appName: {}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, peer := range tt.ctipReqPeers {
|
||||
@@ -398,7 +402,7 @@ func TestReserveIPs(t *testing.T) {
|
||||
domainStr := "example.com."
|
||||
mbd := map[dnsname.FQDN][]string{}
|
||||
mbd["example.com."] = []string{app}
|
||||
c.client.config.appNamesByDomain = mbd
|
||||
c.client.config.nv.appNamesByDomain = mbd
|
||||
domain := must.Get(dnsname.ToFQDN(domainStr))
|
||||
|
||||
for _, tt := range []struct {
|
||||
@@ -453,29 +457,87 @@ func TestReconfig(t *testing.T) {
|
||||
}
|
||||
|
||||
c := newConn25(logger.Discard)
|
||||
if c.isConfigured() {
|
||||
t.Fatal("expected Conn25 to report unconfigured before reconfig")
|
||||
}
|
||||
|
||||
sn := (&tailcfg.Node{
|
||||
CapMap: capMap,
|
||||
}).View()
|
||||
cfg := mustConfig(t, sn, testPrefsIsConnector)
|
||||
c.reconfig(cfg)
|
||||
|
||||
err := c.reconfig(sn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !c.isConfigured() {
|
||||
t.Fatal("expected Conn25 to report configured after reconfig")
|
||||
}
|
||||
|
||||
if len(c.client.config.apps) != 1 || c.client.config.apps[0].Name != "app1" {
|
||||
t.Fatalf("want apps to have one entry 'app1', got %v", c.client.config.apps)
|
||||
if !reflect.DeepEqual(c.client.config, c.connector.config) {
|
||||
t.Fatal("client and connector have different configs")
|
||||
}
|
||||
|
||||
if len(c.client.config.nv.apps) != 1 || c.client.config.nv.apps[0].Name != "app1" {
|
||||
t.Fatalf("want apps to have one entry 'app1', got %v", c.client.config.nv.apps)
|
||||
}
|
||||
|
||||
if c.client.config.prefs.isEligibleConnector != true {
|
||||
t.Fatal("want prefs config to have isEligibleConnector=true")
|
||||
}
|
||||
|
||||
c.reconfig(config{
|
||||
prefs: prefsConfig{isConfigured: true},
|
||||
})
|
||||
if c.isConfigured() {
|
||||
t.Fatal("expected Conn25 to report unconfigured with only prefs config")
|
||||
}
|
||||
|
||||
c.reconfig(config{
|
||||
nv: nodeViewConfig{isConfigured: true},
|
||||
})
|
||||
if c.isConfigured() {
|
||||
t.Fatal("expected Conn25 to report unconfigured with only nodeview config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigReconfig(t *testing.T) {
|
||||
func TestConfigFromPrefs(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
rawCfg string
|
||||
cfg []appctype.Conn25Attr
|
||||
tags []string
|
||||
wantErr bool
|
||||
wantAppsByDomain map[dnsname.FQDN][]string
|
||||
wantSelfRoutedDomains set.Set[dnsname.FQDN]
|
||||
name string
|
||||
prefs ipn.PrefsView
|
||||
expected prefsConfig
|
||||
}{
|
||||
{
|
||||
name: "is-eligible-connector",
|
||||
prefs: testPrefsIsConnector,
|
||||
expected: prefsConfig{
|
||||
isConfigured: true,
|
||||
isEligibleConnector: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not-eligible-connector",
|
||||
prefs: testPrefsNotConnector,
|
||||
expected: prefsConfig{
|
||||
isConfigured: true,
|
||||
isEligibleConnector: false,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if diff := cmp.Diff(tt.expected, configFromPrefs(tt.prefs), cmp.AllowUnexported(prefsConfig{})); diff != "" {
|
||||
t.Errorf("unexpected prefsConfig (-want,+got): %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFromNodeView(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
rawCfg string
|
||||
cfg []appctype.Conn25Attr
|
||||
tags []string
|
||||
wantErr bool
|
||||
wantAppsByDomain map[dnsname.FQDN][]string
|
||||
wantSelfDomains set.Set[dnsname.FQDN]
|
||||
}{
|
||||
{
|
||||
name: "bad-config",
|
||||
@@ -493,10 +555,10 @@ func TestConfigReconfig(t *testing.T) {
|
||||
"a.example.com.": {"one"},
|
||||
"b.example.com.": {"two"},
|
||||
},
|
||||
wantSelfRoutedDomains: set.SetOf([]dnsname.FQDN{"a.example.com."}),
|
||||
wantSelfDomains: set.SetOf([]dnsname.FQDN{"a.example.com."}),
|
||||
},
|
||||
{
|
||||
name: "more-complex",
|
||||
name: "more-complex-with-connector-self-domains",
|
||||
cfg: []appctype.Conn25Attr{
|
||||
{Name: "one", Domains: []string{"1.a.example.com", "1.b.example.com"}, Connectors: []string{"tag:one", "tag:onea"}},
|
||||
{Name: "two", Domains: []string{"2.b.example.com", "2.c.example.com"}, Connectors: []string{"tag:two", "tag:twoa"}},
|
||||
@@ -513,7 +575,19 @@ func TestConfigReconfig(t *testing.T) {
|
||||
"4.b.example.com.": {"four"},
|
||||
"4.d.example.com.": {"four"},
|
||||
},
|
||||
wantSelfRoutedDomains: set.SetOf([]dnsname.FQDN{"1.a.example.com.", "1.b.example.com.", "4.b.example.com.", "4.d.example.com."}),
|
||||
wantSelfDomains: set.SetOf([]dnsname.FQDN{"1.a.example.com.", "1.b.example.com.", "4.b.example.com.", "4.d.example.com."}),
|
||||
},
|
||||
{
|
||||
name: "eligible-connector-no-matching-tag-no-self-domains",
|
||||
cfg: []appctype.Conn25Attr{
|
||||
{Name: "one", Domains: []string{"a.example.com"}, Connectors: []string{"tag:one"}},
|
||||
{Name: "two", Domains: []string{"b.example.com"}, Connectors: []string{"tag:two"}},
|
||||
},
|
||||
tags: []string{"tag:unrelated"},
|
||||
wantAppsByDomain: map[dnsname.FQDN][]string{
|
||||
"a.example.com.": {"one"},
|
||||
"b.example.com.": {"two"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -535,6 +609,7 @@ func TestConfigReconfig(t *testing.T) {
|
||||
CapMap: capMap,
|
||||
Tags: tt.tags,
|
||||
}).View()
|
||||
|
||||
c, err := configFromNodeView(sn)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("wantErr: %t, err: %v", tt.wantErr, err)
|
||||
@@ -542,8 +617,8 @@ func TestConfigReconfig(t *testing.T) {
|
||||
if diff := cmp.Diff(tt.wantAppsByDomain, c.appNamesByDomain); diff != "" {
|
||||
t.Errorf("appsByDomain diff (-want, +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantSelfRoutedDomains, c.selfRoutedDomains); diff != "" {
|
||||
t.Errorf("selfRoutedDomains diff (-want, +got):\n%s", diff)
|
||||
if diff := cmp.Diff(tt.wantSelfDomains, c.selfDomains); diff != "" {
|
||||
t.Errorf("selfDomains diff (-want, +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -562,12 +637,33 @@ func makeSelfNode(t *testing.T, attrs []appctype.Conn25Attr, tags []string) tail
|
||||
capMap := tailcfg.NodeCapMap{
|
||||
tailcfg.NodeCapability(AppConnectorsExperimentalAttrName): cfg,
|
||||
}
|
||||
|
||||
return (&tailcfg.Node{
|
||||
CapMap: capMap,
|
||||
Tags: tags,
|
||||
}).View()
|
||||
}
|
||||
|
||||
var (
|
||||
testPrefsIsConnector = (&ipn.Prefs{AppConnector: ipn.AppConnectorPrefs{Advertise: true}}).View()
|
||||
testPrefsNotConnector = (&ipn.Prefs{AppConnector: ipn.AppConnectorPrefs{Advertise: false}}).View()
|
||||
)
|
||||
|
||||
func mustConfig(t *testing.T, selfNode tailcfg.NodeView, prefs ipn.PrefsView) config {
|
||||
t.Helper()
|
||||
nvCfg, err := configFromNodeView(selfNode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prefsCfg := configFromPrefs(prefs)
|
||||
|
||||
return config{
|
||||
nv: nvCfg,
|
||||
prefs: prefsCfg,
|
||||
}
|
||||
}
|
||||
|
||||
func v4RangeFrom(from, to string) netipx.IPRange {
|
||||
return netipx.IPRangeFrom(
|
||||
netip.MustParseAddr("100.64.0."+from),
|
||||
@@ -706,11 +802,13 @@ func makeDNSResponseForSections(t *testing.T, questions []dnsmessage.Question, a
|
||||
|
||||
func TestMapDNSResponseAssignsAddrs(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
domain string
|
||||
v4Addrs []*dnsmessage.AResource
|
||||
v6Addrs []*dnsmessage.AAAAResource
|
||||
wantByMagicIP map[netip.Addr]addrs
|
||||
name string
|
||||
domain string
|
||||
v4Addrs []*dnsmessage.AResource
|
||||
v6Addrs []*dnsmessage.AAAAResource
|
||||
selfTags []string
|
||||
isEligibleConnector bool
|
||||
wantByMagicIP map[netip.Addr]addrs
|
||||
}{
|
||||
{
|
||||
name: "one-ip-matches",
|
||||
@@ -783,6 +881,48 @@ func TestMapDNSResponseAssignsAddrs(t *testing.T) {
|
||||
{A: [4]byte{2, 0, 0, 0}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-rewrite-self-routed-domain",
|
||||
domain: "example.com.",
|
||||
v4Addrs: []*dnsmessage.AResource{{A: [4]byte{1, 0, 0, 0}}},
|
||||
selfTags: []string{"tag:woo"},
|
||||
isEligibleConnector: true,
|
||||
},
|
||||
{
|
||||
name: "rewrite-tagged-but-not-eligible-connector",
|
||||
domain: "example.com.",
|
||||
v4Addrs: []*dnsmessage.AResource{{A: [4]byte{1, 0, 0, 0}}},
|
||||
selfTags: []string{"tag:woo"},
|
||||
// isEligibleConnector is false: tag matches but prefs not set,
|
||||
// so DNS response should be rewritten normally.
|
||||
wantByMagicIP: map[netip.Addr]addrs{
|
||||
netip.MustParseAddr("100.64.0.0"): {
|
||||
domain: "example.com.",
|
||||
dst: netip.MustParseAddr("1.0.0.0"),
|
||||
magic: netip.MustParseAddr("100.64.0.0"),
|
||||
transit: netip.MustParseAddr("100.64.0.40"),
|
||||
app: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rewrite-eligible-connector-no-matching-tag",
|
||||
domain: "example.com.",
|
||||
v4Addrs: []*dnsmessage.AResource{{A: [4]byte{1, 0, 0, 0}}},
|
||||
selfTags: []string{"tag:unrelated"},
|
||||
isEligibleConnector: true,
|
||||
// isEligibleConnector is true but tag doesn't match the app,
|
||||
// so DNS response should be rewritten normally.
|
||||
wantByMagicIP: map[netip.Addr]addrs{
|
||||
netip.MustParseAddr("100.64.0.0"): {
|
||||
domain: "example.com.",
|
||||
dst: netip.MustParseAddr("1.0.0.0"),
|
||||
magic: netip.MustParseAddr("100.64.0.0"),
|
||||
transit: netip.MustParseAddr("100.64.0.40"),
|
||||
app: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var dnsResp []byte
|
||||
@@ -799,9 +939,15 @@ func TestMapDNSResponseAssignsAddrs(t *testing.T) {
|
||||
V6MagicIPPool: []netipx.IPRange{v6RangeFrom("0", "10"), v6RangeFrom("20", "30")},
|
||||
V4TransitIPPool: []netipx.IPRange{v4RangeFrom("40", "50")},
|
||||
V6TransitIPPool: []netipx.IPRange{v6RangeFrom("40", "50")},
|
||||
}}, []string{})
|
||||
}}, tt.selfTags)
|
||||
prefs := testPrefsNotConnector
|
||||
if tt.isEligibleConnector {
|
||||
prefs = testPrefsIsConnector
|
||||
}
|
||||
|
||||
c := newConn25(logger.Discard)
|
||||
c.reconfig(sn)
|
||||
cfg := mustConfig(t, sn, prefs)
|
||||
c.reconfig(cfg)
|
||||
|
||||
c.mapDNSResponse(dnsResp)
|
||||
if diff := cmp.Diff(tt.wantByMagicIP, c.client.assignments.byMagicIP, cmpopts.EquateComparable(addrs{}, netip.Addr{})); diff != "" {
|
||||
@@ -831,7 +977,7 @@ func TestReserveAddressesDeduplicated(t *testing.T) {
|
||||
c.client.v6MagicIPPool = newIPPool(mustIPSetFromPrefix("fd7a:115c:a1e0:a99c:0100::/80"))
|
||||
c.client.v4TransitIPPool = newIPPool(mustIPSetFromPrefix("169.254.0.0/24"))
|
||||
c.client.v6TransitIPPool = newIPPool(mustIPSetFromPrefix("fd7a:115c:a1e0:a99c:0200::/80"))
|
||||
c.client.config.appNamesByDomain = map[dnsname.FQDN][]string{"example.com.": {"a"}}
|
||||
c.client.config.nv.appNamesByDomain = map[dnsname.FQDN][]string{"example.com.": {"a"}}
|
||||
|
||||
first, err := c.client.reserveAddresses("example.com.", tt.dst)
|
||||
if err != nil {
|
||||
@@ -880,16 +1026,25 @@ func (nb *testNodeBackend) PeerAPIBase(p tailcfg.NodeView) string {
|
||||
return nb.peerAPIURL
|
||||
}
|
||||
|
||||
type testProfileServices struct {
|
||||
ipnext.ProfileServices
|
||||
prefs ipn.PrefsView
|
||||
}
|
||||
|
||||
func (p *testProfileServices) CurrentPrefs() ipn.PrefsView { return p.prefs }
|
||||
|
||||
type testHost struct {
|
||||
ipnext.Host
|
||||
nb ipnext.NodeBackend
|
||||
hooks ipnext.Hooks
|
||||
prefs ipn.PrefsView
|
||||
authReconfigAsync func()
|
||||
}
|
||||
|
||||
func (h *testHost) NodeBackend() ipnext.NodeBackend { return h.nb }
|
||||
func (h *testHost) Hooks() *ipnext.Hooks { return &h.hooks }
|
||||
func (h *testHost) AuthReconfigAsync() { h.authReconfigAsync() }
|
||||
func (h *testHost) NodeBackend() ipnext.NodeBackend { return h.nb }
|
||||
func (h *testHost) Hooks() *ipnext.Hooks { return &h.hooks }
|
||||
func (h *testHost) Profiles() ipnext.ProfileServices { return &testProfileServices{prefs: h.prefs} }
|
||||
func (h *testHost) AuthReconfigAsync() { h.authReconfigAsync() }
|
||||
|
||||
type testSafeBackend struct {
|
||||
ipnext.SafeBackend
|
||||
@@ -950,6 +1105,7 @@ func TestAddressAssignmentIsHandled(t *testing.T) {
|
||||
peers: []tailcfg.NodeView{connectorPeer},
|
||||
peerAPIURL: peersAPI.URL,
|
||||
},
|
||||
prefs: testPrefsNotConnector,
|
||||
authReconfigAsync: func() {
|
||||
authReconfigAsyncCalled <- struct{}{}
|
||||
},
|
||||
@@ -963,10 +1119,9 @@ func TestAddressAssignmentIsHandled(t *testing.T) {
|
||||
Connectors: []string{"tag:woo"},
|
||||
Domains: []string{"example.com"},
|
||||
}}, []string{})
|
||||
err := ext.conn25.reconfig(sn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := mustConfig(t, sn, testPrefsNotConnector)
|
||||
ext.conn25.reconfig(cfg)
|
||||
|
||||
as := addrs{
|
||||
dst: netip.MustParseAddr("1.2.3.4"),
|
||||
@@ -1046,6 +1201,8 @@ func TestMapDNSResponseRewritesResponses(t *testing.T) {
|
||||
V6TransitIPPool: []netipx.IPRange{netipx.IPRangeFrom(netip.MustParseAddr("2606:4700::6813:100"), netip.MustParseAddr("2606:4700::6813:1ff"))},
|
||||
}}, []string{})
|
||||
|
||||
cfg := mustConfig(t, sn, testPrefsNotConnector)
|
||||
|
||||
compareToRecords := func(t *testing.T, resources []dnsmessage.Resource, want []netip.Addr) {
|
||||
t.Helper()
|
||||
var got []netip.Addr
|
||||
@@ -1323,9 +1480,7 @@ func TestMapDNSResponseRewritesResponses(t *testing.T) {
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := newConn25(logger.Discard)
|
||||
if err := c.reconfig(sn); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.reconfig(cfg)
|
||||
bs := c.mapDNSResponse(tt.toMap)
|
||||
tt.assertFx(t, bs)
|
||||
})
|
||||
@@ -1376,6 +1531,7 @@ func TestHandleAddressAssignmentStoresTransitIPs(t *testing.T) {
|
||||
peers: connectorPeers,
|
||||
peerAPIURL: peersAPI.URL,
|
||||
},
|
||||
prefs: testPrefsNotConnector,
|
||||
authReconfigAsync: func() {
|
||||
authReconfigAsyncCalled <- struct{}{}
|
||||
},
|
||||
@@ -1396,10 +1552,9 @@ func TestHandleAddressAssignmentStoresTransitIPs(t *testing.T) {
|
||||
Domains: []string{"hoo.example.com"},
|
||||
},
|
||||
}, []string{})
|
||||
err := ext.conn25.reconfig(sn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := mustConfig(t, sn, testPrefsNotConnector)
|
||||
ext.conn25.reconfig(cfg)
|
||||
|
||||
type lookup struct {
|
||||
connKey key.NodePublic
|
||||
@@ -1576,6 +1731,7 @@ func TestClientTransitIPForMagicIP(t *testing.T) {
|
||||
V4MagicIPPool: []netipx.IPRange{v4RangeFrom("0", "10")}, // 100.64.0.0 - 100.64.0.10
|
||||
V6MagicIPPool: []netipx.IPRange{v6RangeFrom("0", "10")},
|
||||
}}, []string{})
|
||||
cfg := mustConfig(t, sn, testPrefsNotConnector)
|
||||
|
||||
mappedMip := netip.MustParseAddr("100.64.0.0")
|
||||
mappedTip := netip.MustParseAddr("169.0.0.0")
|
||||
@@ -1634,9 +1790,8 @@ func TestClientTransitIPForMagicIP(t *testing.T) {
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := newConn25(t.Logf)
|
||||
if err := c.reconfig(sn); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.reconfig(cfg)
|
||||
|
||||
if err := c.client.assignments.insert(addrs{
|
||||
magic: mappedMip,
|
||||
transit: mappedTip,
|
||||
@@ -1666,6 +1821,8 @@ func TestConnectorRealIPForTransitIPConnection(t *testing.T) {
|
||||
sn := makeSelfNode(t, []appctype.Conn25Attr{{
|
||||
V4TransitIPPool: []netipx.IPRange{v4RangeFrom("40", "50")}, // 100.64.0.40 - 100.64.0.50
|
||||
}}, []string{})
|
||||
cfg := mustConfig(t, sn, testPrefsIsConnector)
|
||||
|
||||
mappedSrc := netip.MustParseAddr("100.0.0.1")
|
||||
unmappedSrc := netip.MustParseAddr("100.0.0.2")
|
||||
mappedTip := netip.MustParseAddr("100.64.0.41")
|
||||
@@ -1717,9 +1874,7 @@ func TestConnectorRealIPForTransitIPConnection(t *testing.T) {
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := newConn25(t.Logf)
|
||||
if err := c.reconfig(sn); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.reconfig(cfg)
|
||||
c.connector.transitIPs = map[netip.Addr]map[netip.Addr]appAddr{}
|
||||
c.connector.transitIPs[mappedSrc] = map[netip.Addr]appAddr{}
|
||||
c.connector.transitIPs[mappedSrc][mappedTip] = appAddr{addr: mappedMip}
|
||||
@@ -1812,10 +1967,9 @@ func TestGetMagicRange(t *testing.T) {
|
||||
V4MagicIPPool: []netipx.IPRange{netipx.IPRangeFrom(netip.MustParseAddr("0.0.0.1"), netip.MustParseAddr("0.0.0.3"))},
|
||||
V6MagicIPPool: []netipx.IPRange{netipx.IPRangeFrom(netip.MustParseAddr("::1"), netip.MustParseAddr("::3"))},
|
||||
}}, []string{})
|
||||
cfg := mustConfig(t, sn, testPrefsNotConnector)
|
||||
c := newConn25(t.Logf)
|
||||
if err := c.reconfig(sn); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.reconfig(cfg)
|
||||
ext := &extension{
|
||||
conn25: c,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user