|
|
|
@ -98,8 +98,8 @@ type Wrapper struct {
|
|
|
|
|
// timeNow, if non-nil, will be used to obtain the current time.
|
|
|
|
|
timeNow func() time.Time
|
|
|
|
|
|
|
|
|
|
// natV4Config stores the current IPv4 NAT configuration.
|
|
|
|
|
natV4Config atomic.Pointer[natV4Config]
|
|
|
|
|
// natConfig stores the current NAT configuration.
|
|
|
|
|
natConfig atomic.Pointer[natConfig]
|
|
|
|
|
|
|
|
|
|
// vectorBuffer stores the oldest unconsumed packet vector from tdev. It is
|
|
|
|
|
// allocated in wrap() and the underlying arrays should never grow.
|
|
|
|
@ -481,14 +481,9 @@ func (t *Wrapper) sendVectorOutbound(r tunVectorReadResult) {
|
|
|
|
|
t.vectorOutbound <- r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// snatV4 does SNAT on p if it's an IPv4 packet and the destination
|
|
|
|
|
// address requires a different source address.
|
|
|
|
|
func (t *Wrapper) snatV4(p *packet.Parsed) {
|
|
|
|
|
if p.IPVersion != 4 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nc := t.natV4Config.Load()
|
|
|
|
|
// snat does SNAT on p if the destination address requires a different source address.
|
|
|
|
|
func (t *Wrapper) snat(p *packet.Parsed) {
|
|
|
|
|
nc := t.natConfig.Load()
|
|
|
|
|
oldSrc := p.Src.Addr()
|
|
|
|
|
newSrc := nc.selectSrcIP(oldSrc, p.Dst.Addr())
|
|
|
|
|
if oldSrc != newSrc {
|
|
|
|
@ -496,13 +491,9 @@ func (t *Wrapper) snatV4(p *packet.Parsed) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dnatV4 does destination NAT on p if it's an IPv4 packet.
|
|
|
|
|
func (t *Wrapper) dnatV4(p *packet.Parsed) {
|
|
|
|
|
if p.IPVersion != 4 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nc := t.natV4Config.Load()
|
|
|
|
|
// dnat does destination NAT on p.
|
|
|
|
|
func (t *Wrapper) dnat(p *packet.Parsed) {
|
|
|
|
|
nc := t.natConfig.Load()
|
|
|
|
|
oldDst := p.Dst.Addr()
|
|
|
|
|
newDst := nc.mapDstIP(oldDst)
|
|
|
|
|
if newDst != oldDst {
|
|
|
|
@ -521,15 +512,79 @@ func findV4(addrs []netip.Prefix) netip.Addr {
|
|
|
|
|
return netip.Addr{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// natV4Config is the configuration for IPv4 NAT.
|
|
|
|
|
// findV6 returns the first Tailscale IPv6 address in addrs.
|
|
|
|
|
func findV6(addrs []netip.Prefix) netip.Addr {
|
|
|
|
|
for _, ap := range addrs {
|
|
|
|
|
a := ap.Addr()
|
|
|
|
|
if a.Is6() && tsaddr.IsTailscaleIP(a) {
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return netip.Addr{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// natConfig is the configuration for NAT.
|
|
|
|
|
// It should be treated as immutable.
|
|
|
|
|
//
|
|
|
|
|
// The nil value is a valid configuration.
|
|
|
|
|
type natConfig struct {
|
|
|
|
|
v4, v6 *natFamilyConfig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *natConfig) String() string {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return "<nil>"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
b.WriteString("natConfig{")
|
|
|
|
|
fmt.Fprintf(&b, "v4: %v, ", c.v4)
|
|
|
|
|
fmt.Fprintf(&b, "v6: %v", c.v6)
|
|
|
|
|
b.WriteString("}")
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mapDstIP returns the destination IP to use for a packet to dst.
|
|
|
|
|
// If dst is not one of the listen addresses, it is returned as-is,
|
|
|
|
|
// otherwise the native address is returned.
|
|
|
|
|
func (c *natConfig) mapDstIP(oldDst netip.Addr) netip.Addr {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return oldDst
|
|
|
|
|
}
|
|
|
|
|
if oldDst.Is4() {
|
|
|
|
|
return c.v4.mapDstIP(oldDst)
|
|
|
|
|
}
|
|
|
|
|
if oldDst.Is6() {
|
|
|
|
|
return c.v6.mapDstIP(oldDst)
|
|
|
|
|
}
|
|
|
|
|
return oldDst
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// selectSrcIP returns the source IP to use for a packet to dst.
|
|
|
|
|
// If the packet is not from the native address, it is returned as-is.
|
|
|
|
|
func (c *natConfig) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return oldSrc
|
|
|
|
|
}
|
|
|
|
|
if oldSrc.Is4() {
|
|
|
|
|
return c.v4.selectSrcIP(oldSrc, dst)
|
|
|
|
|
}
|
|
|
|
|
if oldSrc.Is6() {
|
|
|
|
|
return c.v6.selectSrcIP(oldSrc, dst)
|
|
|
|
|
}
|
|
|
|
|
return oldSrc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// natFamilyConfig is the NAT configuration for a particular
|
|
|
|
|
// address family.
|
|
|
|
|
// It should be treated as immutable.
|
|
|
|
|
//
|
|
|
|
|
// The nil value is a valid configuration.
|
|
|
|
|
type natV4Config struct {
|
|
|
|
|
// nativeAddr is the IPv4 Tailscale Address of the current node.
|
|
|
|
|
type natFamilyConfig struct {
|
|
|
|
|
// nativeAddr is the Tailscale Address of the current node.
|
|
|
|
|
nativeAddr netip.Addr
|
|
|
|
|
|
|
|
|
|
// listenAddrs is the set of IPv4 addresses that should be
|
|
|
|
|
// listenAddrs is the set of addresses that should be
|
|
|
|
|
// mapped to the native address. These are the addresses that
|
|
|
|
|
// peers will use to connect to this node.
|
|
|
|
|
listenAddrs views.Map[netip.Addr, struct{}] // masqAddr -> struct{}
|
|
|
|
@ -545,12 +600,12 @@ type natV4Config struct {
|
|
|
|
|
dstAddrToPeerKeyMapper *table.RoutingTable
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *natV4Config) String() string {
|
|
|
|
|
func (c *natFamilyConfig) String() string {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return "<nil>"
|
|
|
|
|
return "natFamilyConfig(nil)"
|
|
|
|
|
}
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
b.WriteString("natV4Config{")
|
|
|
|
|
b.WriteString("natFamilyConfig{")
|
|
|
|
|
fmt.Fprintf(&b, "nativeAddr: %v, ", c.nativeAddr)
|
|
|
|
|
fmt.Fprint(&b, "listenAddrs: [")
|
|
|
|
|
|
|
|
|
@ -586,7 +641,7 @@ func (c *natV4Config) String() string {
|
|
|
|
|
// mapDstIP returns the destination IP to use for a packet to dst.
|
|
|
|
|
// If dst is not one of the listen addresses, it is returned as-is,
|
|
|
|
|
// otherwise the native address is returned.
|
|
|
|
|
func (c *natV4Config) mapDstIP(oldDst netip.Addr) netip.Addr {
|
|
|
|
|
func (c *natFamilyConfig) mapDstIP(oldDst netip.Addr) netip.Addr {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return oldDst
|
|
|
|
|
}
|
|
|
|
@ -598,7 +653,7 @@ func (c *natV4Config) mapDstIP(oldDst netip.Addr) netip.Addr {
|
|
|
|
|
|
|
|
|
|
// selectSrcIP returns the source IP to use for a packet to dst.
|
|
|
|
|
// If the packet is not from the native address, it is returned as-is.
|
|
|
|
|
func (c *natV4Config) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|
|
|
|
func (c *natFamilyConfig) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return oldSrc
|
|
|
|
|
}
|
|
|
|
@ -615,16 +670,25 @@ func (c *natV4Config) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|
|
|
|
return oldSrc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// natV4ConfigFromWGConfig generates a natV4Config from nm.
|
|
|
|
|
// If v4 NAT is not required, it returns nil.
|
|
|
|
|
func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|
|
|
|
// natConfigFromWGConfig generates a natFamilyConfig from nm,
|
|
|
|
|
// for the indicated address family.
|
|
|
|
|
// If NAT is not required for that address family, it returns nil.
|
|
|
|
|
func natConfigFromWGConfig(wcfg *wgcfg.Config, addrFam ipproto.IPProtoVersion) *natFamilyConfig {
|
|
|
|
|
if wcfg == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
nativeAddr := findV4(wcfg.Addresses)
|
|
|
|
|
|
|
|
|
|
var nativeAddr netip.Addr
|
|
|
|
|
switch addrFam {
|
|
|
|
|
case ipproto.IPProtoVersion4:
|
|
|
|
|
nativeAddr = findV4(wcfg.Addresses)
|
|
|
|
|
case ipproto.IPProtoVersion6:
|
|
|
|
|
nativeAddr = findV6(wcfg.Addresses)
|
|
|
|
|
}
|
|
|
|
|
if !nativeAddr.IsValid() {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
rt table.RoutingTableBuilder
|
|
|
|
|
dstMasqAddrs map[key.NodePublic]netip.Addr
|
|
|
|
@ -637,17 +701,25 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|
|
|
|
exitNodeRequiresMasq := false // true if using an exit node and it requires masquerading
|
|
|
|
|
for _, p := range wcfg.Peers {
|
|
|
|
|
isExitNode := slices.Contains(p.AllowedIPs, tsaddr.AllIPv4()) || slices.Contains(p.AllowedIPs, tsaddr.AllIPv6())
|
|
|
|
|
if isExitNode && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
|
|
|
|
if isExitNode {
|
|
|
|
|
hasMasqAddrsForFamily := false ||
|
|
|
|
|
(addrFam == ipproto.IPProtoVersion4 && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid()) ||
|
|
|
|
|
(addrFam == ipproto.IPProtoVersion6 && p.V6MasqAddr != nil && p.V6MasqAddr.IsValid())
|
|
|
|
|
if hasMasqAddrsForFamily {
|
|
|
|
|
exitNodeRequiresMasq = true
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := range wcfg.Peers {
|
|
|
|
|
p := &wcfg.Peers[i]
|
|
|
|
|
var addrToUse netip.Addr
|
|
|
|
|
if p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
|
|
|
|
if addrFam == ipproto.IPProtoVersion4 && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
|
|
|
|
addrToUse = *p.V4MasqAddr
|
|
|
|
|
mak.Set(&listenAddrs, addrToUse, struct{}{})
|
|
|
|
|
} else if addrFam == ipproto.IPProtoVersion6 && p.V6MasqAddr != nil && p.V6MasqAddr.IsValid() {
|
|
|
|
|
addrToUse = *p.V6MasqAddr
|
|
|
|
|
mak.Set(&listenAddrs, addrToUse, struct{}{})
|
|
|
|
|
} else if exitNodeRequiresMasq {
|
|
|
|
|
addrToUse = nativeAddr
|
|
|
|
|
} else {
|
|
|
|
@ -659,7 +731,7 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|
|
|
|
if len(listenAddrs) == 0 && len(dstMasqAddrs) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return &natV4Config{
|
|
|
|
|
return &natFamilyConfig{
|
|
|
|
|
nativeAddr: nativeAddr,
|
|
|
|
|
listenAddrs: views.MapOf(listenAddrs),
|
|
|
|
|
dstMasqAddrs: views.MapOf(dstMasqAddrs),
|
|
|
|
@ -668,10 +740,14 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetNetMap is called when a new NetworkMap is received.
|
|
|
|
|
// It currently (2023-03-01) only updates the IPv4 NAT configuration.
|
|
|
|
|
func (t *Wrapper) SetWGConfig(wcfg *wgcfg.Config) {
|
|
|
|
|
cfg := natV4ConfigFromWGConfig(wcfg)
|
|
|
|
|
old := t.natV4Config.Swap(cfg)
|
|
|
|
|
v4, v6 := natConfigFromWGConfig(wcfg, ipproto.IPProtoVersion4), natConfigFromWGConfig(wcfg, ipproto.IPProtoVersion6)
|
|
|
|
|
var cfg *natConfig
|
|
|
|
|
if v4 != nil || v6 != nil {
|
|
|
|
|
cfg = &natConfig{v4: v4, v6: v6}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old := t.natConfig.Swap(cfg)
|
|
|
|
|
if !reflect.DeepEqual(old, cfg) {
|
|
|
|
|
t.logf("nat config: %v", cfg)
|
|
|
|
|
}
|
|
|
|
@ -786,7 +862,7 @@ func (t *Wrapper) Read(buffs [][]byte, sizes []int, offset int) (int, error) {
|
|
|
|
|
for _, data := range res.data {
|
|
|
|
|
p.Decode(data[res.dataOffset:])
|
|
|
|
|
|
|
|
|
|
t.snatV4(p)
|
|
|
|
|
t.snat(p)
|
|
|
|
|
if m := t.destIPActivity.Load(); m != nil {
|
|
|
|
|
if fn := m[p.Dst.Addr()]; fn != nil {
|
|
|
|
|
fn()
|
|
|
|
@ -843,7 +919,7 @@ func (t *Wrapper) injectedRead(res tunInjectedRead, buf []byte, offset int) (int
|
|
|
|
|
p := parsedPacketPool.Get().(*packet.Parsed)
|
|
|
|
|
defer parsedPacketPool.Put(p)
|
|
|
|
|
p.Decode(buf[offset : offset+n])
|
|
|
|
|
t.snatV4(p)
|
|
|
|
|
t.snat(p)
|
|
|
|
|
|
|
|
|
|
if m := t.destIPActivity.Load(); m != nil {
|
|
|
|
|
if fn := m[p.Dst.Addr()]; fn != nil {
|
|
|
|
@ -965,7 +1041,7 @@ func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) {
|
|
|
|
|
captHook := t.captureHook.Load()
|
|
|
|
|
for _, buff := range buffs {
|
|
|
|
|
p.Decode(buff[offset:])
|
|
|
|
|
t.dnatV4(p)
|
|
|
|
|
t.dnat(p)
|
|
|
|
|
if !t.disableFilter {
|
|
|
|
|
if t.filterPacketInboundFromWireGuard(p, captHook) != filter.Accept {
|
|
|
|
|
metricPacketInDrop.Add(1)
|
|
|
|
@ -1030,7 +1106,7 @@ func (t *Wrapper) InjectInboundPacketBuffer(pkt stack.PacketBufferPtr) error {
|
|
|
|
|
if captHook != nil {
|
|
|
|
|
captHook(capture.SynthesizedToLocal, t.now(), p.Buffer(), p.CaptureMeta)
|
|
|
|
|
}
|
|
|
|
|
t.dnatV4(p)
|
|
|
|
|
t.dnat(p)
|
|
|
|
|
|
|
|
|
|
return t.InjectInboundDirect(buf, PacketStartOffset)
|
|
|
|
|
}
|
|
|
|
|