cmd/viewer, types/views, all: un-special case slice of netip.Prefix

Make it just a views.Slice[netip.Prefix] instead of its own named type.

Having the special case led to circular dependencies in another WIP PR
of mine.

Updates #8948

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/8958/head
Brad Fitzpatrick 9 months ago committed by Brad Fitzpatrick
parent 261cc498d3
commit 6e57dee7eb

@ -15,6 +15,7 @@ import (
"tailscale.com/net/netutil"
"tailscale.com/net/tsaddr"
"tailscale.com/safesocket"
"tailscale.com/types/views"
)
var setCmd = &ffcli.Command{
@ -171,7 +172,7 @@ func calcAdvertiseRoutesForSet(advertiseExitNodeSet, advertiseRoutesSet bool, cu
if alreadyAdvertisesExitNode == setArgs.advertiseDefaultRoute {
return curPrefs.AdvertiseRoutes, nil
}
routes = tsaddr.FilterPrefixesCopy(curPrefs.AdvertiseRoutes, func(p netip.Prefix) bool {
routes = tsaddr.FilterPrefixesCopy(views.SliceOf(curPrefs.AdvertiseRoutes), func(p netip.Prefix) bool {
return p.Bits() != 0
})
if setArgs.advertiseDefaultRoute {

@ -309,8 +309,8 @@ func (v StructWithSlicesView) StructPointers() views.SliceView[*StructWithPtrs,
func (v StructWithSlicesView) Structs() StructWithPtrs { panic("unsupported") }
func (v StructWithSlicesView) Ints() *int { panic("unsupported") }
func (v StructWithSlicesView) Slice() views.Slice[string] { return views.SliceOf(v.ж.Slice) }
func (v StructWithSlicesView) Prefixes() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.Prefixes)
func (v StructWithSlicesView) Prefixes() views.Slice[netip.Prefix] {
return views.SliceOf(v.ж.Prefixes)
}
func (v StructWithSlicesView) Data() mem.RO { return mem.B(v.ж.Data) }

@ -69,8 +69,6 @@ func (v *{{.ViewName}}) UnmarshalJSON(b []byte) error {
{{end}}
{{define "byteSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() mem.RO { return mem.B(v.ж.{{.FieldName}}) }
{{end}}
{{define "ipPrefixSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.{{.FieldName}}) }
{{end}}
{{define "sliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.Slice[{{.FieldType}}] { return views.SliceOf(v.ж.{{.FieldName}}) }
{{end}}
{{define "viewSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.SliceView[{{.FieldType}},{{.FieldViewName}}] { return views.SliceOfViews[{{.FieldType}},{{.FieldViewName}}](v.ж.{{.FieldName}}) }
@ -176,9 +174,6 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi
case "byte":
it.Import("go4.org/mem")
writeTemplate("byteSliceField")
case "inet.af/netip.Prefix", "net/netip.Prefix":
it.Import("tailscale.com/types/views")
writeTemplate("ipPrefixSliceField")
default:
it.Import("tailscale.com/types/views")
shallow, deep, base := requiresCloning(elem)

@ -79,8 +79,8 @@ func (v PrefsView) Hostname() string { return v.ж.Hostname }
func (v PrefsView) NotepadURLs() bool { return v.ж.NotepadURLs }
func (v PrefsView) ForceDaemon() bool { return v.ж.ForceDaemon }
func (v PrefsView) Egg() bool { return v.ж.Egg }
func (v PrefsView) AdvertiseRoutes() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.AdvertiseRoutes)
func (v PrefsView) AdvertiseRoutes() views.Slice[netip.Prefix] {
return views.SliceOf(v.ж.AdvertiseRoutes)
}
func (v PrefsView) NoSNAT() bool { return v.ж.NoSNAT }
func (v PrefsView) NetfilterMode() preftype.NetfilterMode { return v.ж.NetfilterMode }

@ -776,13 +776,13 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
func peerStatusFromNode(ps *ipnstate.PeerStatus, n *tailcfg.Node) {
ps.ID = n.StableID
ps.Created = n.Created
ps.ExitNodeOption = tsaddr.ContainsExitRoutes(n.AllowedIPs)
ps.ExitNodeOption = tsaddr.ContainsExitRoutes(views.SliceOf(n.AllowedIPs))
if n.Tags != nil {
v := views.SliceOf(n.Tags)
ps.Tags = &v
}
if n.PrimaryRoutes != nil {
v := views.IPPrefixSliceOf(n.PrimaryRoutes)
v := views.SliceOf(n.PrimaryRoutes)
ps.PrimaryRoutes = &v
}
@ -2301,7 +2301,8 @@ func (b *LocalBackend) setAtomicValuesFromPrefsLocked(p ipn.PrefsView) {
b.lastServeConfJSON = mem.B(nil)
b.serveConfig = ipn.ServeConfigView{}
} else {
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(p.AdvertiseRoutes().Filter(tsaddr.IsViaPrefix)))
filtered := tsaddr.FilterPrefixesCopy(p.AdvertiseRoutes(), tsaddr.IsViaPrefix)
b.containsViaIPFuncAtomic.Store(tsaddr.NewContainsIPFunc(filtered))
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked(p)
}
}

@ -209,7 +209,7 @@ type PeerStatus struct {
// PrimaryRoutes are the routes this node is currently the primary
// subnet router for, as determined by the control plane. It does
// not include the IPs in TailscaleIPs.
PrimaryRoutes *views.IPPrefixSlice `json:",omitempty"`
PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitempty"`
// Endpoints:
Addrs []string

@ -23,6 +23,7 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/persist"
"tailscale.com/types/preftype"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
)
@ -506,7 +507,7 @@ func (p *Prefs) AdvertisesExitNode() bool {
if p == nil {
return false
}
return tsaddr.ContainsExitRoutes(p.AdvertiseRoutes)
return tsaddr.ContainsExitRoutes(views.SliceOf(p.AdvertiseRoutes))
}
// SetAdvertiseExitNode mutates p (if non-nil) to add or remove the two

@ -13,6 +13,7 @@ import (
"go4.org/netipx"
"tailscale.com/net/netaddr"
"tailscale.com/types/views"
)
// ChromeOSVMRange returns the subset of the CGNAT IPv4 range used by
@ -225,9 +226,10 @@ func PrefixIs6(p netip.Prefix) bool { return p.Addr().Is6() }
// ContainsExitRoutes reports whether rr contains both the IPv4 and
// IPv6 /0 route.
func ContainsExitRoutes(rr []netip.Prefix) bool {
func ContainsExitRoutes(rr views.Slice[netip.Prefix]) bool {
var v4, v6 bool
for _, r := range rr {
for i := range rr.LenIter() {
r := rr.At(i)
if r == allIPv4 {
v4 = true
} else if r == allIPv6 {
@ -237,6 +239,17 @@ func ContainsExitRoutes(rr []netip.Prefix) bool {
return v4 && v6
}
// ContainsNonExitSubnetRoutes reports whether v contains Subnet
// Routes other than ExitNode Routes.
func ContainsNonExitSubnetRoutes(rr views.Slice[netip.Prefix]) bool {
for i := range rr.LenIter() {
if rr.At(i).Bits() != 0 {
return true
}
}
return false
}
var (
allIPv4 = netip.MustParsePrefix("0.0.0.0/0")
allIPv6 = netip.MustParsePrefix("::/0")
@ -258,10 +271,10 @@ func SortPrefixes(p []netip.Prefix) {
// FilterPrefixes returns a new slice, not aliasing in, containing elements of
// in that match f.
func FilterPrefixesCopy(in []netip.Prefix, f func(netip.Prefix) bool) []netip.Prefix {
func FilterPrefixesCopy(in views.Slice[netip.Prefix], f func(netip.Prefix) bool) []netip.Prefix {
var out []netip.Prefix
for _, v := range in {
if f(v) {
for i := range in.LenIter() {
if v := in.At(i); f(v) {
out = append(out, v)
}
}

@ -131,27 +131,25 @@ func (v *NodeView) UnmarshalJSON(b []byte) error {
return nil
}
func (v NodeView) ID() NodeID { return v.ж.ID }
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
func (v NodeView) Name() string { return v.ж.Name }
func (v NodeView) User() UserID { return v.ж.User }
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
func (v NodeView) Key() key.NodePublic { return v.ж.Key }
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
func (v NodeView) KeySignature() mem.RO { return mem.B(v.ж.KeySignature) }
func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine }
func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey }
func (v NodeView) Addresses() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.Addresses) }
func (v NodeView) AllowedIPs() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.AllowedIPs) }
func (v NodeView) Endpoints() views.Slice[string] { return views.SliceOf(v.ж.Endpoints) }
func (v NodeView) DERP() string { return v.ж.DERP }
func (v NodeView) Hostinfo() HostinfoView { return v.ж.Hostinfo }
func (v NodeView) Created() time.Time { return v.ж.Created }
func (v NodeView) Cap() CapabilityVersion { return v.ж.Cap }
func (v NodeView) Tags() views.Slice[string] { return views.SliceOf(v.ж.Tags) }
func (v NodeView) PrimaryRoutes() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.PrimaryRoutes)
}
func (v NodeView) ID() NodeID { return v.ж.ID }
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
func (v NodeView) Name() string { return v.ж.Name }
func (v NodeView) User() UserID { return v.ж.User }
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
func (v NodeView) Key() key.NodePublic { return v.ж.Key }
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
func (v NodeView) KeySignature() mem.RO { return mem.B(v.ж.KeySignature) }
func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine }
func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey }
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
func (v NodeView) AllowedIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.AllowedIPs) }
func (v NodeView) Endpoints() views.Slice[string] { return views.SliceOf(v.ж.Endpoints) }
func (v NodeView) DERP() string { return v.ж.DERP }
func (v NodeView) Hostinfo() HostinfoView { return v.ж.Hostinfo }
func (v NodeView) Created() time.Time { return v.ж.Created }
func (v NodeView) Cap() CapabilityVersion { return v.ж.Cap }
func (v NodeView) Tags() views.Slice[string] { return views.SliceOf(v.ж.Tags) }
func (v NodeView) PrimaryRoutes() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.PrimaryRoutes) }
func (v NodeView) LastSeen() *time.Time {
if v.ж.LastSeen == nil {
return nil
@ -266,41 +264,39 @@ func (v *HostinfoView) UnmarshalJSON(b []byte) error {
return nil
}
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
func (v HostinfoView) OS() string { return v.ж.OS }
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
func (v HostinfoView) Container() opt.Bool { return v.ж.Container }
func (v HostinfoView) Env() string { return v.ж.Env }
func (v HostinfoView) Distro() string { return v.ж.Distro }
func (v HostinfoView) DistroVersion() string { return v.ж.DistroVersion }
func (v HostinfoView) DistroCodeName() string { return v.ж.DistroCodeName }
func (v HostinfoView) App() string { return v.ж.App }
func (v HostinfoView) Desktop() opt.Bool { return v.ж.Desktop }
func (v HostinfoView) Package() string { return v.ж.Package }
func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel }
func (v HostinfoView) PushDeviceToken() string { return v.ж.PushDeviceToken }
func (v HostinfoView) Hostname() string { return v.ж.Hostname }
func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport }
func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress }
func (v HostinfoView) AllowsUpdate() bool { return v.ж.AllowsUpdate }
func (v HostinfoView) Machine() string { return v.ж.Machine }
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
func (v HostinfoView) GoArchVar() string { return v.ж.GoArchVar }
func (v HostinfoView) GoVersion() string { return v.ж.GoVersion }
func (v HostinfoView) RoutableIPs() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.RoutableIPs)
}
func (v HostinfoView) RequestTags() views.Slice[string] { return views.SliceOf(v.ж.RequestTags) }
func (v HostinfoView) Services() views.Slice[Service] { return views.SliceOf(v.ж.Services) }
func (v HostinfoView) NetInfo() NetInfoView { return v.ж.NetInfo.View() }
func (v HostinfoView) SSH_HostKeys() views.Slice[string] { return views.SliceOf(v.ж.SSH_HostKeys) }
func (v HostinfoView) Cloud() string { return v.ж.Cloud }
func (v HostinfoView) Userspace() opt.Bool { return v.ж.Userspace }
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
func (v HostinfoView) OS() string { return v.ж.OS }
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
func (v HostinfoView) Container() opt.Bool { return v.ж.Container }
func (v HostinfoView) Env() string { return v.ж.Env }
func (v HostinfoView) Distro() string { return v.ж.Distro }
func (v HostinfoView) DistroVersion() string { return v.ж.DistroVersion }
func (v HostinfoView) DistroCodeName() string { return v.ж.DistroCodeName }
func (v HostinfoView) App() string { return v.ж.App }
func (v HostinfoView) Desktop() opt.Bool { return v.ж.Desktop }
func (v HostinfoView) Package() string { return v.ж.Package }
func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel }
func (v HostinfoView) PushDeviceToken() string { return v.ж.PushDeviceToken }
func (v HostinfoView) Hostname() string { return v.ж.Hostname }
func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport }
func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress }
func (v HostinfoView) AllowsUpdate() bool { return v.ж.AllowsUpdate }
func (v HostinfoView) Machine() string { return v.ж.Machine }
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
func (v HostinfoView) GoArchVar() string { return v.ж.GoArchVar }
func (v HostinfoView) GoVersion() string { return v.ж.GoVersion }
func (v HostinfoView) RoutableIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.RoutableIPs) }
func (v HostinfoView) RequestTags() views.Slice[string] { return views.SliceOf(v.ж.RequestTags) }
func (v HostinfoView) Services() views.Slice[Service] { return views.SliceOf(v.ж.Services) }
func (v HostinfoView) NetInfo() NetInfoView { return v.ж.NetInfo.View() }
func (v HostinfoView) SSH_HostKeys() views.Slice[string] { return views.SliceOf(v.ж.SSH_HostKeys) }
func (v HostinfoView) Cloud() string { return v.ж.Cloud }
func (v HostinfoView) Userspace() opt.Bool { return v.ж.Userspace }
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
func (v HostinfoView) Location() *Location {
if v.ж.Location == nil {
return nil

@ -9,10 +9,6 @@ import (
"encoding/json"
"errors"
"maps"
"net/netip"
"slices"
"tailscale.com/net/tsaddr"
)
func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error {
@ -229,84 +225,6 @@ func SliceEqualAnyOrder[T comparable](a, b Slice[T]) bool {
return true
}
// IPPrefixSlice is a read-only accessor for a slice of netip.Prefix.
type IPPrefixSlice struct {
ж Slice[netip.Prefix]
}
// IPPrefixSliceOf returns a IPPrefixSlice for the provided slice.
func IPPrefixSliceOf(x []netip.Prefix) IPPrefixSlice { return IPPrefixSlice{SliceOf(x)} }
// IsNil reports whether the underlying slice is nil.
func (v IPPrefixSlice) IsNil() bool { return v.ж.IsNil() }
// Len returns the length of the slice.
func (v IPPrefixSlice) Len() int { return v.ж.Len() }
// LenIter returns a slice the same length as the v.Len().
// The caller can then range over it to get the valid indexes.
// It does not allocate.
func (v IPPrefixSlice) LenIter() []struct{} { return make([]struct{}, v.ж.Len()) }
// At returns the IPPrefix at index `i` of the slice.
func (v IPPrefixSlice) At(i int) netip.Prefix { return v.ж.At(i) }
// AppendTo appends the underlying slice values to dst.
func (v IPPrefixSlice) AppendTo(dst []netip.Prefix) []netip.Prefix {
return v.ж.AppendTo(dst)
}
// Unwrap returns the underlying Slice[netip.Prefix].
func (v IPPrefixSlice) Unwrap() Slice[netip.Prefix] {
return v.ж
}
// AsSlice returns a copy of underlying slice.
func (v IPPrefixSlice) AsSlice() []netip.Prefix {
return v.ж.AsSlice()
}
// Filter returns a new slice, containing elements of v that match f.
func (v IPPrefixSlice) Filter(f func(netip.Prefix) bool) []netip.Prefix {
return tsaddr.FilterPrefixesCopy(v.ж.ж, f)
}
// PrefixesContainsIP reports whether any IPPrefix contains IP.
func (v IPPrefixSlice) ContainsIP(ip netip.Addr) bool {
return tsaddr.PrefixesContainsIP(v.ж.ж, ip)
}
// PrefixesContainsFunc reports whether f is true for any IPPrefix in the slice.
func (v IPPrefixSlice) ContainsFunc(f func(netip.Prefix) bool) bool {
return slices.ContainsFunc(v.ж.ж, f)
}
// ContainsExitRoutes reports whether v contains ExitNode Routes.
func (v IPPrefixSlice) ContainsExitRoutes() bool {
return tsaddr.ContainsExitRoutes(v.ж.ж)
}
// ContainsNonExitSubnetRoutes reports whether v contains Subnet
// Routes other than ExitNode Routes.
func (v IPPrefixSlice) ContainsNonExitSubnetRoutes() bool {
for i := 0; i < v.Len(); i++ {
if v.At(i).Bits() != 0 {
return true
}
}
return false
}
// MarshalJSON implements json.Marshaler.
func (v IPPrefixSlice) MarshalJSON() ([]byte, error) {
return v.ж.MarshalJSON()
}
// UnmarshalJSON implements json.Unmarshaler.
func (v *IPPrefixSlice) UnmarshalJSON(b []byte) error {
return v.ж.UnmarshalJSON(b)
}
// MapOf returns a view over m. It is the caller's responsibility to make sure K
// and V is immutable, if this is being used to provide a read-only view over m.
func MapOf[K comparable, V comparable](m map[K]V) Map[K, V] {

@ -16,10 +16,10 @@ import (
type viewStruct struct {
Int int
Addrs IPPrefixSlice
Addrs Slice[netip.Prefix]
Strings Slice[string]
AddrsPtr *IPPrefixSlice `json:",omitempty"`
StringsPtr *Slice[string] `json:",omitempty"`
AddrsPtr *Slice[netip.Prefix] `json:",omitempty"`
StringsPtr *Slice[string] `json:",omitempty"`
}
func BenchmarkSliceIteration(b *testing.B) {
@ -66,7 +66,7 @@ func TestViewsJSON(t *testing.T) {
}
return
}
ipp := IPPrefixSliceOf(mustCIDR("192.168.0.0/24"))
ipp := SliceOf(mustCIDR("192.168.0.0/24"))
ss := SliceOf([]string{"bar"})
tests := []struct {
name string

Loading…
Cancel
Save