diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 0043c0ecd..b975e9dfb 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -5,7 +5,7 @@ // the node and the coordination server. package tailcfg -//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService --clonefunc +//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPolicy,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService --clonefunc import ( "bytes" diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 3952f5f47..329d60835 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -547,6 +547,32 @@ var _SSHActionCloneNeedsRegeneration = SSHAction(struct { OnRecordingFailure *SSHRecorderFailureAction }{}) +// Clone makes a deep copy of SSHPolicy. +// The result aliases no memory with the original. +func (src *SSHPolicy) Clone() *SSHPolicy { + if src == nil { + return nil + } + dst := new(SSHPolicy) + *dst = *src + if src.Rules != nil { + dst.Rules = make([]*SSHRule, len(src.Rules)) + for i := range dst.Rules { + if src.Rules[i] == nil { + dst.Rules[i] = nil + } else { + dst.Rules[i] = src.Rules[i].Clone() + } + } + } + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHPolicyCloneNeedsRegeneration = SSHPolicy(struct { + Rules []*SSHRule +}{}) + // Clone makes a deep copy of SSHPrincipal. // The result aliases no memory with the original. func (src *SSHPrincipal) Clone() *SSHPrincipal { @@ -647,7 +673,7 @@ var _VIPServiceCloneNeedsRegeneration = VIPService(struct { // Clone duplicates src into dst and reports whether it succeeded. // To succeed, must be of types <*T, *T> or <*T, **T>, -// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService. +// where T is one of User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPolicy,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService. func Clone(dst, src any) bool { switch src := src.(type) { case *User: @@ -785,6 +811,15 @@ func Clone(dst, src any) bool { *dst = src.Clone() return true } + case *SSHPolicy: + switch dst := dst.(type) { + case *SSHPolicy: + *dst = *src.Clone() + return true + case **SSHPolicy: + *dst = src.Clone() + return true + } case *SSHPrincipal: switch dst := dst.(type) { case *SSHPrincipal: diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index f8f9f865c..1702a3f36 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -19,7 +19,7 @@ import ( "tailscale.com/types/views" ) -//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService +//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPolicy,SSHPrincipal,ControlDialPlan,Location,UserProfile,VIPService // View returns a read-only view of User. func (p *User) View() UserView { @@ -1176,6 +1176,60 @@ var _SSHActionViewNeedsRegeneration = SSHAction(struct { OnRecordingFailure *SSHRecorderFailureAction }{}) +// View returns a read-only view of SSHPolicy. +func (p *SSHPolicy) View() SSHPolicyView { + return SSHPolicyView{ж: p} +} + +// SSHPolicyView provides a read-only view over SSHPolicy. +// +// Its methods should only be called if `Valid()` returns true. +type SSHPolicyView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *SSHPolicy +} + +// Valid reports whether v's underlying value is non-nil. +func (v SSHPolicyView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v SSHPolicyView) AsStruct() *SSHPolicy { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v SSHPolicyView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *SSHPolicyView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x SSHPolicy + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v SSHPolicyView) Rules() views.SliceView[*SSHRule, SSHRuleView] { + return views.SliceOfViews[*SSHRule, SSHRuleView](v.ж.Rules) +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _SSHPolicyViewNeedsRegeneration = SSHPolicy(struct { + Rules []*SSHRule +}{}) + // View returns a read-only view of SSHPrincipal. func (p *SSHPrincipal) View() SSHPrincipalView { return SSHPrincipalView{ж: p} diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index b1eecaa8f..42ba30807 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -21,6 +21,8 @@ import ( "tailscale.com/wgengine/filter/filtertype" ) +//go:generate go run tailscale.com/cmd/viewer -type=NetworkMap + // NetworkMap is the current state of the world. // // The fields should all be considered read-only. They might diff --git a/types/netmap/netmap_clone.go b/types/netmap/netmap_clone.go new file mode 100644 index 000000000..ac01cda32 --- /dev/null +++ b/types/netmap/netmap_clone.go @@ -0,0 +1,73 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Code generated by tailscale.com/cmd/cloner; DO NOT EDIT. + +package netmap + +import ( + "maps" + "time" + + "tailscale.com/tailcfg" + "tailscale.com/tka" + "tailscale.com/types/key" + "tailscale.com/types/views" + "tailscale.com/util/set" + "tailscale.com/wgengine/filter/filtertype" +) + +// Clone makes a deep copy of NetworkMap. +// The result aliases no memory with the original. +func (src *NetworkMap) Clone() *NetworkMap { + if src == nil { + return nil + } + dst := new(NetworkMap) + *dst = *src + dst.SelfNode = src.SelfNode + dst.AllCaps = maps.Clone(src.AllCaps) + if src.Peers != nil { + dst.Peers = make([]tailcfg.NodeView, len(src.Peers)) + for i := range dst.Peers { + dst.Peers[i] = src.Peers[i] + } + } + dst.DNS = *src.DNS.Clone() + if src.PacketFilter != nil { + dst.PacketFilter = make([]filtertype.Match, len(src.PacketFilter)) + for i := range dst.PacketFilter { + dst.PacketFilter[i] = *src.PacketFilter[i].Clone() + } + } + dst.PacketFilterRules = src.PacketFilterRules + dst.SSHPolicy = src.SSHPolicy.Clone() + dst.DERPMap = src.DERPMap.Clone() + dst.ControlHealth = append(src.ControlHealth[:0:0], src.ControlHealth...) + dst.UserProfiles = maps.Clone(src.UserProfiles) + return dst +} + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _NetworkMapCloneNeedsRegeneration = NetworkMap(struct { + SelfNode tailcfg.NodeView + AllCaps set.Set[tailcfg.NodeCapability] + NodeKey key.NodePublic + PrivateKey key.NodePrivate + Expiry time.Time + Name string + MachineKey key.MachinePublic + Peers []tailcfg.NodeView + DNS tailcfg.DNSConfig + PacketFilter []filtertype.Match + PacketFilterRules views.Slice[tailcfg.FilterRule] + SSHPolicy *tailcfg.SSHPolicy + CollectServices bool + DERPMap *tailcfg.DERPMap + ControlHealth []string + TKAEnabled bool + TKAHead tka.AUMHash + Domain string + DomainAuditLogID string + UserProfiles map[tailcfg.UserID]tailcfg.UserProfileView +}{}) diff --git a/types/netmap/netmap_view.go b/types/netmap/netmap_view.go new file mode 100644 index 000000000..f0e5ac4b6 --- /dev/null +++ b/types/netmap/netmap_view.go @@ -0,0 +1,122 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Code generated by tailscale/cmd/viewer; DO NOT EDIT. + +package netmap + +import ( + "encoding/json" + "errors" + "time" + + "tailscale.com/tailcfg" + "tailscale.com/tka" + "tailscale.com/types/key" + "tailscale.com/types/views" + "tailscale.com/util/set" + "tailscale.com/wgengine/filter/filtertype" +) + +//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=NetworkMap + +// View returns a read-only view of NetworkMap. +func (p *NetworkMap) View() NetworkMapView { + return NetworkMapView{ж: p} +} + +// NetworkMapView provides a read-only view over NetworkMap. +// +// Its methods should only be called if `Valid()` returns true. +type NetworkMapView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *NetworkMap +} + +// Valid reports whether v's underlying value is non-nil. +func (v NetworkMapView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v NetworkMapView) AsStruct() *NetworkMap { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v NetworkMapView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *NetworkMapView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x NetworkMap + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v NetworkMapView) SelfNode() tailcfg.NodeView { return v.ж.SelfNode } + +func (v NetworkMapView) AllCaps() views.Map[tailcfg.NodeCapability, struct{}] { + return views.MapOf(v.ж.AllCaps) +} +func (v NetworkMapView) NodeKey() key.NodePublic { return v.ж.NodeKey } +func (v NetworkMapView) PrivateKey() key.NodePrivate { return v.ж.PrivateKey } +func (v NetworkMapView) Expiry() time.Time { return v.ж.Expiry } +func (v NetworkMapView) Name() string { return v.ж.Name } +func (v NetworkMapView) MachineKey() key.MachinePublic { return v.ж.MachineKey } +func (v NetworkMapView) Peers() views.Slice[tailcfg.NodeView] { return views.SliceOf(v.ж.Peers) } +func (v NetworkMapView) DNS() tailcfg.DNSConfigView { return v.ж.DNS.View() } +func (v NetworkMapView) PacketFilter() views.ValueSliceView[filtertype.Match, *filtertype.Match, filtertype.MatchView] { + return views.SliceOfValueViews[filtertype.Match, *filtertype.Match](v.ж.PacketFilter) +} +func (v NetworkMapView) PacketFilterRules() views.Slice[tailcfg.FilterRule] { + return v.ж.PacketFilterRules +} +func (v NetworkMapView) SSHPolicy() tailcfg.SSHPolicyView { return v.ж.SSHPolicy.View() } +func (v NetworkMapView) CollectServices() bool { return v.ж.CollectServices } +func (v NetworkMapView) DERPMap() tailcfg.DERPMapView { return v.ж.DERPMap.View() } +func (v NetworkMapView) ControlHealth() views.Slice[string] { return views.SliceOf(v.ж.ControlHealth) } +func (v NetworkMapView) TKAEnabled() bool { return v.ж.TKAEnabled } +func (v NetworkMapView) TKAHead() tka.AUMHash { return v.ж.TKAHead } +func (v NetworkMapView) Domain() string { return v.ж.Domain } +func (v NetworkMapView) DomainAuditLogID() string { return v.ж.DomainAuditLogID } + +func (v NetworkMapView) UserProfiles() views.Map[tailcfg.UserID, tailcfg.UserProfileView] { + return views.MapOf(v.ж.UserProfiles) +} +func (v NetworkMapView) String() string { return v.ж.String() } + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _NetworkMapViewNeedsRegeneration = NetworkMap(struct { + SelfNode tailcfg.NodeView + AllCaps set.Set[tailcfg.NodeCapability] + NodeKey key.NodePublic + PrivateKey key.NodePrivate + Expiry time.Time + Name string + MachineKey key.MachinePublic + Peers []tailcfg.NodeView + DNS tailcfg.DNSConfig + PacketFilter []filtertype.Match + PacketFilterRules views.Slice[tailcfg.FilterRule] + SSHPolicy *tailcfg.SSHPolicy + CollectServices bool + DERPMap *tailcfg.DERPMap + ControlHealth []string + TKAEnabled bool + TKAHead tka.AUMHash + Domain string + DomainAuditLogID string + UserProfiles map[tailcfg.UserID]tailcfg.UserProfileView +}{}) diff --git a/wgengine/filter/filtertype/filtertype.go b/wgengine/filter/filtertype/filtertype.go index 212eda43f..2619a2ce4 100644 --- a/wgengine/filter/filtertype/filtertype.go +++ b/wgengine/filter/filtertype/filtertype.go @@ -14,7 +14,7 @@ import ( "tailscale.com/types/views" ) -//go:generate go run tailscale.com/cmd/cloner --type=Match,CapMatch +//go:generate go run tailscale.com/cmd/viewer --type=Match,CapMatch // PortRange is a range of TCP and UDP ports. type PortRange struct { @@ -78,7 +78,7 @@ type Match struct { Srcs []netip.Prefix // SrcsContains is an optimized function that reports whether Addr is in // Srcs, using the best search method for the size and shape of Srcs. - SrcsContains func(netip.Addr) bool `json:"-"` // report whether Addr is in Srcs + SrcsContains func(netip.Addr) bool `json:"-" codegen:"noclone"` // report whether Addr is in Srcs // SrcCaps is an alternative way to match packets. If the peer's source IP // has one of these capabilities, it's also permitted. The peers are only diff --git a/wgengine/filter/filtertype/filtertype_view.go b/wgengine/filter/filtertype/filtertype_view.go new file mode 100644 index 000000000..de9bf818b --- /dev/null +++ b/wgengine/filter/filtertype/filtertype_view.go @@ -0,0 +1,139 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Code generated by tailscale/cmd/viewer; DO NOT EDIT. + +package filtertype + +import ( + "encoding/json" + "errors" + "net/netip" + + "tailscale.com/tailcfg" + "tailscale.com/types/ipproto" + "tailscale.com/types/views" +) + +//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=Match,CapMatch + +// View returns a read-only view of Match. +func (p *Match) View() MatchView { + return MatchView{ж: p} +} + +// MatchView provides a read-only view over Match. +// +// Its methods should only be called if `Valid()` returns true. +type MatchView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *Match +} + +// Valid reports whether v's underlying value is non-nil. +func (v MatchView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v MatchView) AsStruct() *Match { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v MatchView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *MatchView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x Match + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v MatchView) IPProto() views.Slice[ipproto.Proto] { return v.ж.IPProto } +func (v MatchView) Srcs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Srcs) } +func (v MatchView) SrcsContains() func(netip.Addr) bool { return v.ж.SrcsContains } +func (v MatchView) SrcCaps() views.Slice[tailcfg.NodeCapability] { return views.SliceOf(v.ж.SrcCaps) } +func (v MatchView) Dsts() views.Slice[NetPortRange] { return views.SliceOf(v.ж.Dsts) } +func (v MatchView) Caps() views.ValueSliceView[CapMatch, *CapMatch, CapMatchView] { + return views.SliceOfValueViews[CapMatch, *CapMatch](v.ж.Caps) +} +func (v MatchView) String() string { return v.ж.String() } + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _MatchViewNeedsRegeneration = Match(struct { + IPProto views.Slice[ipproto.Proto] + Srcs []netip.Prefix + SrcsContains func(netip.Addr) bool + SrcCaps []tailcfg.NodeCapability + Dsts []NetPortRange + Caps []CapMatch +}{}) + +// View returns a read-only view of CapMatch. +func (p *CapMatch) View() CapMatchView { + return CapMatchView{ж: p} +} + +// CapMatchView provides a read-only view over CapMatch. +// +// Its methods should only be called if `Valid()` returns true. +type CapMatchView struct { + // ж is the underlying mutable value, named with a hard-to-type + // character that looks pointy like a pointer. + // It is named distinctively to make you think of how dangerous it is to escape + // to callers. You must not let callers be able to mutate it. + ж *CapMatch +} + +// Valid reports whether v's underlying value is non-nil. +func (v CapMatchView) Valid() bool { return v.ж != nil } + +// AsStruct returns a clone of the underlying value which aliases no memory with +// the original. +func (v CapMatchView) AsStruct() *CapMatch { + if v.ж == nil { + return nil + } + return v.ж.Clone() +} + +func (v CapMatchView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } + +func (v *CapMatchView) UnmarshalJSON(b []byte) error { + if v.ж != nil { + return errors.New("already initialized") + } + if len(b) == 0 { + return nil + } + var x CapMatch + if err := json.Unmarshal(b, &x); err != nil { + return err + } + v.ж = &x + return nil +} + +func (v CapMatchView) Dst() netip.Prefix { return v.ж.Dst } +func (v CapMatchView) Cap() tailcfg.PeerCapability { return v.ж.Cap } +func (v CapMatchView) Values() views.Slice[tailcfg.RawMessage] { return views.SliceOf(v.ж.Values) } + +// A compilation failure here means this code must be regenerated, with the command at the top of this file. +var _CapMatchViewNeedsRegeneration = CapMatch(struct { + Dst netip.Prefix + Cap tailcfg.PeerCapability + Values []tailcfg.RawMessage +}{})