|
|
@@ -29,7 +29,10 @@ import (
|
|
|
"tailscale.com/types/ipproto"
|
|
|
"tailscale.com/types/key"
|
|
|
"tailscale.com/types/logger"
|
|
|
+ "tailscale.com/types/netmap"
|
|
|
+ "tailscale.com/types/views"
|
|
|
"tailscale.com/util/clientmetric"
|
|
|
+ "tailscale.com/util/mak"
|
|
|
"tailscale.com/wgengine/capture"
|
|
|
"tailscale.com/wgengine/filter"
|
|
|
)
|
|
|
@@ -88,6 +91,9 @@ type Wrapper struct {
|
|
|
destMACAtomic syncs.AtomicValue[[6]byte]
|
|
|
discoKey syncs.AtomicValue[key.DiscoPublic]
|
|
|
|
|
|
+ // natV4Config stores the current NAT configuration.
|
|
|
+ natV4Config atomic.Pointer[natV4Config]
|
|
|
+
|
|
|
// vectorBuffer stores the oldest unconsumed packet vector from tdev. It is
|
|
|
// allocated in wrap() and the underlying arrays should never grow.
|
|
|
vectorBuffer [][]byte
|
|
|
@@ -459,6 +465,139 @@ 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()
|
|
|
+ oldSrc := p.Src.Addr()
|
|
|
+ newSrc := nc.selectSrcIP(oldSrc, p.Dst.Addr())
|
|
|
+ if oldSrc != newSrc {
|
|
|
+ p.UpdateSrcAddr(newSrc)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 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()
|
|
|
+ oldDst := p.Dst.Addr()
|
|
|
+ newDst := nc.mapDstIP(oldDst)
|
|
|
+ if newDst != oldDst {
|
|
|
+ p.UpdateDstAddr(newDst)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// findV4 returns the first Tailscale IPv4 address in addrs.
|
|
|
+func findV4(addrs []netip.Prefix) netip.Addr {
|
|
|
+ for _, ap := range addrs {
|
|
|
+ a := ap.Addr()
|
|
|
+ if a.Is4() && tsaddr.IsTailscaleIP(a) {
|
|
|
+ return a
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return netip.Addr{}
|
|
|
+}
|
|
|
+
|
|
|
+// natV4Config is the configuration for IPv4 NAT.
|
|
|
+// 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.
|
|
|
+ nativeAddr netip.Addr
|
|
|
+
|
|
|
+ // listenAddrs is the set of IPv4 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{}
|
|
|
+
|
|
|
+ // dstMasqAddrs is map of dst addresses to their respective MasqueradeAsIP
|
|
|
+ // addresses. The MasqueradeAsIP address is the address that should be used
|
|
|
+ // as the source address for packets to dst.
|
|
|
+ dstMasqAddrs views.Map[netip.Addr, netip.Addr] // dst -> masqAddr
|
|
|
+
|
|
|
+ // TODO(maisem/nyghtowl): add support for subnets and exit nodes and test them.
|
|
|
+ // Determine IP routing table algorithm to use - e.g. ART?
|
|
|
+}
|
|
|
+
|
|
|
+// 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 {
|
|
|
+ if c == nil {
|
|
|
+ return oldDst
|
|
|
+ }
|
|
|
+ if _, ok := c.listenAddrs.GetOk(oldDst); ok {
|
|
|
+ return c.nativeAddr
|
|
|
+ }
|
|
|
+ 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 *natV4Config) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|
|
+ if c == nil {
|
|
|
+ return oldSrc
|
|
|
+ }
|
|
|
+ if oldSrc != c.nativeAddr {
|
|
|
+ return oldSrc
|
|
|
+ }
|
|
|
+ if eip, ok := c.dstMasqAddrs.GetOk(dst); ok {
|
|
|
+ return eip
|
|
|
+ }
|
|
|
+ return oldSrc
|
|
|
+}
|
|
|
+
|
|
|
+// natConfigFromNetMap generates a natV4Config from nm.
|
|
|
+// If v4 NAT is not required, it returns nil.
|
|
|
+func natConfigFromNetMap(nm *netmap.NetworkMap) *natV4Config {
|
|
|
+ if nm == nil || nm.SelfNode == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ nativeAddr := findV4(nm.SelfNode.Addresses)
|
|
|
+ if !nativeAddr.IsValid() {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var (
|
|
|
+ dstMasqAddrs map[netip.Addr]netip.Addr
|
|
|
+ listenAddrs map[netip.Addr]struct{}
|
|
|
+ )
|
|
|
+ for _, p := range nm.Peers {
|
|
|
+ if !p.SelfNodeV4MasqAddrForThisPeer.IsValid() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ peerV4 := findV4(p.Addresses)
|
|
|
+ if !peerV4.IsValid() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ mak.Set(&dstMasqAddrs, peerV4, p.SelfNodeV4MasqAddrForThisPeer)
|
|
|
+ mak.Set(&listenAddrs, p.SelfNodeV4MasqAddrForThisPeer, struct{}{})
|
|
|
+ }
|
|
|
+ if len(listenAddrs) == 0 || len(dstMasqAddrs) == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return &natV4Config{
|
|
|
+ nativeAddr: nativeAddr,
|
|
|
+ listenAddrs: views.MapOf(listenAddrs),
|
|
|
+ dstMasqAddrs: views.MapOf(dstMasqAddrs),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// SetNetMap is called when a new NetworkMap is received.
|
|
|
+// It currently (2023-03-01) only updates the IPv4 NAT configuration.
|
|
|
+func (t *Wrapper) SetNetMap(nm *netmap.NetworkMap) {
|
|
|
+ cfg := natConfigFromNetMap(nm)
|
|
|
+ t.natV4Config.Store(cfg)
|
|
|
+ t.logf("nat config: %+v", cfg)
|
|
|
+}
|
|
|
+
|
|
|
var (
|
|
|
magicDNSIPPort = netip.AddrPortFrom(tsaddr.TailscaleServiceIP(), 0) // 100.100.100.100:0
|
|
|
magicDNSIPPortv6 = netip.AddrPortFrom(tsaddr.TailscaleServiceIPv6(), 0)
|
|
|
@@ -541,8 +680,8 @@ func (t *Wrapper) IdleDuration() time.Duration {
|
|
|
}
|
|
|
|
|
|
func (t *Wrapper) Read(buffs [][]byte, sizes []int, offset int) (int, error) {
|
|
|
+ // packet from OS read and sent to WG
|
|
|
res, ok := <-t.vectorOutbound
|
|
|
-
|
|
|
if !ok {
|
|
|
return 0, io.EOF
|
|
|
}
|
|
|
@@ -566,6 +705,8 @@ func (t *Wrapper) Read(buffs [][]byte, sizes []int, offset int) (int, error) {
|
|
|
defer parsedPacketPool.Put(p)
|
|
|
for _, data := range res.data {
|
|
|
p.Decode(data[res.dataOffset:])
|
|
|
+
|
|
|
+ t.snatV4(p)
|
|
|
if m := t.destIPActivity.Load(); m != nil {
|
|
|
if fn := m[p.Dst.Addr()]; fn != nil {
|
|
|
fn()
|
|
|
@@ -622,6 +763,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)
|
|
|
|
|
|
if m := t.destIPActivity.Load(); m != nil {
|
|
|
if fn := m[p.Dst.Addr()]; fn != nil {
|
|
|
@@ -738,11 +880,12 @@ func (t *Wrapper) filterPacketInboundFromWireGuard(p *packet.Parsed) filter.Resp
|
|
|
func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) {
|
|
|
metricPacketIn.Add(int64(len(buffs)))
|
|
|
i := 0
|
|
|
- if !t.disableFilter {
|
|
|
- p := parsedPacketPool.Get().(*packet.Parsed)
|
|
|
- defer parsedPacketPool.Put(p)
|
|
|
- for _, buff := range buffs {
|
|
|
- p.Decode(buff[offset:])
|
|
|
+ p := parsedPacketPool.Get().(*packet.Parsed)
|
|
|
+ defer parsedPacketPool.Put(p)
|
|
|
+ for _, buff := range buffs {
|
|
|
+ p.Decode(buff[offset:])
|
|
|
+ t.dnatV4(p)
|
|
|
+ if !t.disableFilter {
|
|
|
if t.filterPacketInboundFromWireGuard(p) != filter.Accept {
|
|
|
metricPacketInDrop.Add(1)
|
|
|
} else {
|
|
|
@@ -750,7 +893,8 @@ func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) {
|
|
|
i++
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ if t.disableFilter {
|
|
|
i = len(buffs)
|
|
|
}
|
|
|
buffs = buffs[:i]
|
|
|
@@ -801,6 +945,10 @@ func (t *Wrapper) InjectInboundPacketBuffer(pkt stack.PacketBufferPtr) error {
|
|
|
if capt := t.captureHook.Load(); capt != nil {
|
|
|
capt(capture.SynthesizedToLocal, time.Now(), buf[PacketStartOffset:])
|
|
|
}
|
|
|
+ p := parsedPacketPool.Get().(*packet.Parsed)
|
|
|
+ defer parsedPacketPool.Put(p)
|
|
|
+ p.Decode(buf[PacketStartOffset:])
|
|
|
+ t.dnatV4(p)
|
|
|
|
|
|
return t.InjectInboundDirect(buf, PacketStartOffset)
|
|
|
}
|