diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go index 01e2580a7..2c4dd0f57 100644 --- a/net/tstun/wrap.go +++ b/net/tstun/wrap.go @@ -580,12 +580,12 @@ func natConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config { ) for i := range wcfg.Peers { p := &wcfg.Peers[i] - if !p.V4MasqAddr.IsValid() { + if p.V4MasqAddr == nil || !p.V4MasqAddr.IsValid() { continue } rt.InsertOrReplace(p.PublicKey, p.AllowedIPs...) - mak.Set(&dstMasqAddrs, p.PublicKey, p.V4MasqAddr) - mak.Set(&listenAddrs, p.V4MasqAddr, struct{}{}) + mak.Set(&dstMasqAddrs, p.PublicKey, *p.V4MasqAddr) + mak.Set(&listenAddrs, *p.V4MasqAddr, struct{}{}) } if len(listenAddrs) == 0 || len(dstMasqAddrs) == 0 { return nil diff --git a/net/tstun/wrap_test.go b/net/tstun/wrap_test.go index 15274371a..baf0eff56 100644 --- a/net/tstun/wrap_test.go +++ b/net/tstun/wrap_test.go @@ -31,6 +31,7 @@ import ( "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/netlogtype" + "tailscale.com/types/ptr" "tailscale.com/util/must" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/wgcfg" @@ -602,7 +603,7 @@ func TestNATCfg(t *testing.T) { AllowedIPs: []netip.Prefix{ netip.PrefixFrom(ip, ip.BitLen()), }, - V4MasqAddr: eip, + V4MasqAddr: ptr.To(eip), } p.AllowedIPs = append(p.AllowedIPs, otherAllowedIPs...) return p diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index adcf18c04..620151f11 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -289,7 +289,7 @@ type Node struct { // This only applies to traffic originating from the current node to the // peer or any of its subnets. Traffic originating from subnet routes will // not be masqueraded (e.g. in case of --snat-subnet-routes). - SelfNodeV4MasqAddrForThisPeer netip.Addr `json:",omitempty"` + SelfNodeV4MasqAddrForThisPeer *netip.Addr `json:",omitempty"` // IsWireGuardOnly indicates that this is a non-Tailscale WireGuard peer, it // is not expected to speak Disco or DERP, and it must have Endpoints in @@ -1705,7 +1705,7 @@ func (n *Node) Equal(n2 *Node) bool { bytes.Equal(n.KeySignature, n2.KeySignature) && n.Machine == n2.Machine && n.DiscoKey == n2.DiscoKey && - eqBoolPtr(n.Online, n2.Online) && + eqPtr(n.Online, n2.Online) && eqCIDRs(n.Addresses, n2.Addresses) && eqCIDRs(n.AllowedIPs, n2.AllowedIPs) && eqCIDRs(n.PrimaryRoutes, n2.PrimaryRoutes) && @@ -1722,11 +1722,11 @@ func (n *Node) Equal(n2 *Node) bool { n.ComputedNameWithHost == n2.ComputedNameWithHost && eqStrings(n.Tags, n2.Tags) && n.Expired == n2.Expired && - n.SelfNodeV4MasqAddrForThisPeer == n2.SelfNodeV4MasqAddrForThisPeer && + eqPtr(n.SelfNodeV4MasqAddrForThisPeer, n2.SelfNodeV4MasqAddrForThisPeer) && n.IsWireGuardOnly == n2.IsWireGuardOnly } -func eqBoolPtr(a, b *bool) bool { +func eqPtr[T comparable](a, b *T) bool { if a == b { // covers nil return true } @@ -1734,7 +1734,6 @@ func eqBoolPtr(a, b *bool) bool { return false } return *a == *b - } func eqStrings(a, b []string) bool { diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index ef8dac56e..5326c495c 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -63,6 +63,10 @@ func (src *Node) Clone() *Node { *dst.Online = *src.Online } dst.Capabilities = append(src.Capabilities[:0:0], src.Capabilities...) + if dst.SelfNodeV4MasqAddrForThisPeer != nil { + dst.SelfNodeV4MasqAddrForThisPeer = new(netip.Addr) + *dst.SelfNodeV4MasqAddrForThisPeer = *src.SelfNodeV4MasqAddrForThisPeer + } return dst } @@ -98,7 +102,7 @@ var _NodeCloneNeedsRegeneration = Node(struct { ComputedNameWithHost string DataPlaneAuditLogID string Expired bool - SelfNodeV4MasqAddrForThisPeer netip.Addr + SelfNodeV4MasqAddrForThisPeer *netip.Addr IsWireGuardOnly bool }{}) diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 4beca887f..b0e3f982e 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -18,6 +18,7 @@ import ( . "tailscale.com/tailcfg" "tailscale.com/tstest" "tailscale.com/types/key" + "tailscale.com/types/ptr" "tailscale.com/util/must" "tailscale.com/version" ) @@ -537,12 +538,12 @@ func TestNodeEqual(t *testing.T) { }, { &Node{}, - &Node{SelfNodeV4MasqAddrForThisPeer: netip.MustParseAddr("100.64.0.1")}, + &Node{SelfNodeV4MasqAddrForThisPeer: ptr.To(netip.MustParseAddr("100.64.0.1"))}, false, }, { - &Node{SelfNodeV4MasqAddrForThisPeer: netip.MustParseAddr("100.64.0.1")}, - &Node{SelfNodeV4MasqAddrForThisPeer: netip.MustParseAddr("100.64.0.1")}, + &Node{SelfNodeV4MasqAddrForThisPeer: ptr.To(netip.MustParseAddr("100.64.0.1"))}, + &Node{SelfNodeV4MasqAddrForThisPeer: ptr.To(netip.MustParseAddr("100.64.0.1"))}, true, }, } diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index 5df1ea6a6..b32ccc365 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -176,9 +176,14 @@ func (v NodeView) ComputedName() string { return v.ж.ComputedName func (v NodeView) ComputedNameWithHost() string { return v.ж.ComputedNameWithHost } func (v NodeView) DataPlaneAuditLogID() string { return v.ж.DataPlaneAuditLogID } func (v NodeView) Expired() bool { return v.ж.Expired } -func (v NodeView) SelfNodeV4MasqAddrForThisPeer() netip.Addr { - return v.ж.SelfNodeV4MasqAddrForThisPeer +func (v NodeView) SelfNodeV4MasqAddrForThisPeer() *netip.Addr { + if v.ж.SelfNodeV4MasqAddrForThisPeer == nil { + return nil + } + x := *v.ж.SelfNodeV4MasqAddrForThisPeer + return &x } + func (v NodeView) IsWireGuardOnly() bool { return v.ж.IsWireGuardOnly } func (v NodeView) Equal(v2 NodeView) bool { return v.ж.Equal(v2.ж) } @@ -214,7 +219,7 @@ var _NodeViewNeedsRegeneration = Node(struct { ComputedNameWithHost string DataPlaneAuditLogID string Expired bool - SelfNodeV4MasqAddrForThisPeer netip.Addr + SelfNodeV4MasqAddrForThisPeer *netip.Addr IsWireGuardOnly bool }{}) diff --git a/tstest/integration/testcontrol/testcontrol.go b/tstest/integration/testcontrol/testcontrol.go index 7916782c2..8f5037380 100644 --- a/tstest/integration/testcontrol/testcontrol.go +++ b/tstest/integration/testcontrol/testcontrol.go @@ -33,6 +33,7 @@ import ( "tailscale.com/tailcfg" "tailscale.com/types/key" "tailscale.com/types/logger" + "tailscale.com/types/ptr" ) const msgLimit = 1 << 20 // encrypted message length limit @@ -843,7 +844,7 @@ func (s *Server) MapResponse(req *tailcfg.MapRequest) (res *tailcfg.MapResponse, continue } if masqIP := nodeMasqs[p.Key]; masqIP.IsValid() { - p.SelfNodeV4MasqAddrForThisPeer = masqIP + p.SelfNodeV4MasqAddrForThisPeer = ptr.To(masqIP) } s.mu.Lock() diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index e712767f2..bb04b65df 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -575,8 +575,8 @@ func TestGetTypeHasher(t *testing.T) { { name: "tailcfg.Node", val: &tailcfg.Node{}, - out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - out32: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + out32: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\tn\x88\xf1\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", }, } for _, tt := range tests { diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 20181528b..92b1f13b7 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -50,6 +50,7 @@ import ( "tailscale.com/types/netlogtype" "tailscale.com/types/netmap" "tailscale.com/types/nettype" + "tailscale.com/types/ptr" "tailscale.com/util/cibuild" "tailscale.com/util/racebuild" "tailscale.com/wgengine/filter" @@ -2240,7 +2241,7 @@ func TestIsWireGuardOnlyPeerWithMasquerade(t *testing.T) { IsWireGuardOnly: true, Addresses: []netip.Prefix{wgaip}, AllowedIPs: []netip.Prefix{wgaip}, - SelfNodeV4MasqAddrForThisPeer: masqip.Addr(), + SelfNodeV4MasqAddrForThisPeer: ptr.To(masqip.Addr()), }, }, } diff --git a/wgengine/wgcfg/config.go b/wgengine/wgcfg/config.go index c8285a74f..18f019b53 100644 --- a/wgengine/wgcfg/config.go +++ b/wgengine/wgcfg/config.go @@ -37,7 +37,7 @@ type Peer struct { PublicKey key.NodePublic DiscoKey key.DiscoPublic // present only so we can handle restarts within wgengine, not passed to WireGuard AllowedIPs []netip.Prefix - V4MasqAddr netip.Addr // if non-zero, masquerade IPv4 traffic to this peer using this address + V4MasqAddr *netip.Addr // if non-nil, masquerade IPv4 traffic to this peer using this address PersistentKeepalive uint16 // wireguard-go's endpoint for this peer. It should always equal Peer.PublicKey. // We represent it explicitly so that we can detect if they diverge and recover. diff --git a/wgengine/wgcfg/wgcfg_clone.go b/wgengine/wgcfg/wgcfg_clone.go index d87f4487c..6887dd6cc 100644 --- a/wgengine/wgcfg/wgcfg_clone.go +++ b/wgengine/wgcfg/wgcfg_clone.go @@ -54,6 +54,10 @@ func (src *Peer) Clone() *Peer { dst := new(Peer) *dst = *src dst.AllowedIPs = append(src.AllowedIPs[:0:0], src.AllowedIPs...) + if dst.V4MasqAddr != nil { + dst.V4MasqAddr = new(netip.Addr) + *dst.V4MasqAddr = *src.V4MasqAddr + } return dst } @@ -62,7 +66,7 @@ var _PeerCloneNeedsRegeneration = Peer(struct { PublicKey key.NodePublic DiscoKey key.DiscoPublic AllowedIPs []netip.Prefix - V4MasqAddr netip.Addr + V4MasqAddr *netip.Addr PersistentKeepalive uint16 WGEndpoint key.NodePublic }{})