mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 12:58:37 +00:00
add start of Hard NAT impl
untested so far Change-Id: I682b604d0e90debf9eae3f1814663f336d03f57c Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
bebfa254cf
commit
cda2300682
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand/v2"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -43,6 +44,8 @@ type oneToOneNAT struct {
|
|||||||
wanIP netip.Addr
|
wanIP netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ NATTable = (*oneToOneNAT)(nil)
|
||||||
|
|
||||||
func (n *oneToOneNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
func (n *oneToOneNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
||||||
return netip.AddrPortFrom(n.wanIP, src.Port())
|
return netip.AddrPortFrom(n.wanIP, src.Port())
|
||||||
}
|
}
|
||||||
@ -50,3 +53,75 @@ func (n *oneToOneNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wa
|
|||||||
func (n *oneToOneNAT) PickIncomingDst(src, dst netip.AddrPort, at time.Time) (lanDst netip.AddrPort) {
|
func (n *oneToOneNAT) PickIncomingDst(src, dst netip.AddrPort, at time.Time) (lanDst netip.AddrPort) {
|
||||||
return netip.AddrPortFrom(n.lanIP, dst.Port())
|
return netip.AddrPortFrom(n.lanIP, dst.Port())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hardKeyOut struct {
|
||||||
|
lanIP netip.Addr
|
||||||
|
dst netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
type hardKeyIn struct {
|
||||||
|
wanPort uint16
|
||||||
|
src netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
type portMappingAndTime struct {
|
||||||
|
port uint16
|
||||||
|
at time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type lanAddrAndTime struct {
|
||||||
|
lanAddr netip.AddrPort
|
||||||
|
at time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// hardNAT is an "Endpoint Dependent" NAT, like FreeBSD/pfSense/OPNsense.
|
||||||
|
// This is shown as "MappingVariesByDestIP: true" by netcheck, and what
|
||||||
|
// Tailscale calls "Hard NAT".
|
||||||
|
type hardNAT struct {
|
||||||
|
wanIP netip.Addr
|
||||||
|
|
||||||
|
out map[hardKeyOut]portMappingAndTime
|
||||||
|
in map[hardKeyIn]lanAddrAndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ NATTable = (*hardNAT)(nil)
|
||||||
|
|
||||||
|
func (n *hardNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
||||||
|
ko := hardKeyOut{src.Addr(), dst}
|
||||||
|
if pm, ok := n.out[ko]; ok {
|
||||||
|
// Existing flow.
|
||||||
|
// TODO: bump timestamp
|
||||||
|
return netip.AddrPortFrom(n.wanIP, pm.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No existing mapping exists. Create one.
|
||||||
|
|
||||||
|
// TODO: clean up old expired mappings
|
||||||
|
|
||||||
|
// Instead of proper data structures that would be efficient, we instead
|
||||||
|
// just loop a bunch and look for a free port. This project is only used
|
||||||
|
// by tests and doesn't care about performance, this is good enough.
|
||||||
|
port := src.Port() // start with the port the client used? (TODO: does FreeBSD do this? make knob?)
|
||||||
|
for {
|
||||||
|
ki := hardKeyIn{wanPort: port, src: dst}
|
||||||
|
if _, ok := n.in[ki]; ok {
|
||||||
|
// Collision. Try again.
|
||||||
|
port = rand.N(uint16(32<<10)) + 32<<10 // pick some "ephemeral" port
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n.in[ki] = lanAddrAndTime{lanAddr: src, at: at}
|
||||||
|
n.out[ko] = portMappingAndTime{port: port, at: at}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *hardNAT) PickIncomingDst(src, dst netip.AddrPort, at time.Time) (lanDst netip.AddrPort) {
|
||||||
|
if dst.Addr() != n.wanIP {
|
||||||
|
return netip.AddrPort{} // drop; not for us. shouldn't happen if natlabd routing isn't broken.
|
||||||
|
}
|
||||||
|
ki := hardKeyIn{wanPort: dst.Port(), src: src}
|
||||||
|
if pm, ok := n.in[ki]; ok {
|
||||||
|
// Existing flow.
|
||||||
|
return pm.lanAddr
|
||||||
|
}
|
||||||
|
return netip.AddrPort{} // drop; no mapping
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user