wgengine/magicsock: generate relay server set from tailnet policy (#16331)

Updates tailscale/corp#27502

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited
2025-06-20 15:00:28 -07:00
committed by GitHub
parent 12e92b1b08
commit d3bb34c628
4 changed files with 386 additions and 24 deletions
+201 -1
View File
@@ -19,6 +19,7 @@ import (
"net/http/httptest"
"net/netip"
"os"
"reflect"
"runtime"
"strconv"
"strings"
@@ -71,6 +72,7 @@ import (
"tailscale.com/util/slicesx"
"tailscale.com/util/usermetric"
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/filter/filtertype"
"tailscale.com/wgengine/wgcfg"
"tailscale.com/wgengine/wgcfg/nmcfg"
"tailscale.com/wgengine/wglog"
@@ -275,7 +277,10 @@ func (s *magicStack) Status() *ipnstate.Status {
func (s *magicStack) IP() netip.Addr {
for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); time.Sleep(10 * time.Millisecond) {
s.conn.mu.Lock()
addr := s.conn.firstAddrForTest
var addr netip.Addr
if s.conn.self.Valid() && s.conn.self.Addresses().Len() > 0 {
addr = s.conn.self.Addresses().At(0).Addr()
}
s.conn.mu.Unlock()
if addr.IsValid() {
return addr
@@ -3378,3 +3383,198 @@ func Test_virtualNetworkID(t *testing.T) {
})
}
}
func Test_peerAPIIfCandidateRelayServer(t *testing.T) {
selfOnlyIPv4 := &tailcfg.Node{
Cap: math.MinInt32,
Addresses: []netip.Prefix{
netip.MustParsePrefix("1.1.1.1/32"),
},
}
selfOnlyIPv6 := selfOnlyIPv4.Clone()
selfOnlyIPv6.Addresses[0] = netip.MustParsePrefix("::1/128")
peerHostinfo := &tailcfg.Hostinfo{
Services: []tailcfg.Service{
{
Proto: tailcfg.PeerAPI4,
Port: 4,
},
{
Proto: tailcfg.PeerAPI6,
Port: 6,
},
},
}
peerOnlyIPv4 := &tailcfg.Node{
Cap: math.MinInt32,
CapMap: map[tailcfg.NodeCapability][]tailcfg.RawMessage{
tailcfg.NodeAttrRelayServer: nil,
},
Addresses: []netip.Prefix{
netip.MustParsePrefix("2.2.2.2/32"),
},
Hostinfo: peerHostinfo.View(),
}
peerOnlyIPv6 := peerOnlyIPv4.Clone()
peerOnlyIPv6.Addresses[0] = netip.MustParsePrefix("::2/128")
peerOnlyIPv4ZeroCapVer := peerOnlyIPv4.Clone()
peerOnlyIPv4ZeroCapVer.Cap = 0
peerOnlyIPv4NilHostinfo := peerOnlyIPv4.Clone()
peerOnlyIPv4NilHostinfo.Hostinfo = tailcfg.HostinfoView{}
tests := []struct {
name string
filt *filter.Filter
self tailcfg.NodeView
peer tailcfg.NodeView
want netip.AddrPort
}{
{
name: "match v4",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("1.1.1.1/32"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv4.View(),
peer: peerOnlyIPv4.View(),
want: netip.MustParseAddrPort("2.2.2.2:4"),
},
{
name: "match v6",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("::1/128"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv6.View(),
peer: peerOnlyIPv6.View(),
want: netip.MustParseAddrPort("[::2]:6"),
},
{
name: "no match dst",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("::3/128"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv6.View(),
peer: peerOnlyIPv6.View(),
},
{
name: "no match peer cap",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("::2/128")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("::1/128"),
Cap: tailcfg.PeerCapabilityIngress,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv6.View(),
peer: peerOnlyIPv6.View(),
},
{
name: "cap ver not relay capable",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("1.1.1.1/32"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: peerOnlyIPv4.View(),
peer: peerOnlyIPv4ZeroCapVer.View(),
},
{
name: "nil filt",
filt: nil,
self: selfOnlyIPv4.View(),
peer: peerOnlyIPv4.View(),
},
{
name: "nil self",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("1.1.1.1/32"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: tailcfg.NodeView{},
peer: peerOnlyIPv4.View(),
},
{
name: "nil peer",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("1.1.1.1/32"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv4.View(),
peer: tailcfg.NodeView{},
},
{
name: "nil peer hostinfo",
filt: filter.New([]filtertype.Match{
{
Srcs: []netip.Prefix{netip.MustParsePrefix("2.2.2.2/32")},
Caps: []filtertype.CapMatch{
{
Dst: netip.MustParsePrefix("1.1.1.1/32"),
Cap: tailcfg.PeerCapabilityRelayTarget,
},
},
},
}, nil, nil, nil, nil, nil),
self: selfOnlyIPv4.View(),
peer: peerOnlyIPv4NilHostinfo.View(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := peerAPIIfCandidateRelayServer(tt.filt, tt.self, tt.peer); !reflect.DeepEqual(got, tt.want) {
t.Errorf("peerAPIIfCandidateRelayServer() = %v, want %v", got, tt.want)
}
})
}
}