mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-21 12:28:39 +00:00
name NAT types, add constructors
Change-Id: Id558e763773e6efa700cfb7943b64c78bfffc4ed Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
0e9bbbebeb
commit
e971923a92
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand/v2"
|
||||
"net/netip"
|
||||
"time"
|
||||
@ -8,6 +9,38 @@ import (
|
||||
"tailscale.com/util/mak"
|
||||
)
|
||||
|
||||
// IPPool is the interface that a NAT implementation uses to get information
|
||||
// about a network.
|
||||
//
|
||||
// Outside of tests, this is typically a *network.
|
||||
type IPPool interface {
|
||||
// WANIP returns the primary WAN IP address.
|
||||
//
|
||||
// TODO: add another method for networks with multiple WAN IP addresses.
|
||||
WANIP() netip.Addr
|
||||
|
||||
// SoleLanIP reports whether this network has a sole LAN client
|
||||
// and if so, its IP address.
|
||||
SoleLANIP() (_ netip.Addr, ok bool)
|
||||
|
||||
// TODO: port availability stuff for interacting with portmapping
|
||||
}
|
||||
|
||||
// newTableFunc is a constructor for a NAT table.
|
||||
// The provided IPPool is typically (outside of tests) a *network.
|
||||
type newTableFunc func(IPPool) (NATTable, error)
|
||||
|
||||
// natTypes are the known NAT types.
|
||||
var natTypes = map[string]newTableFunc{}
|
||||
|
||||
// registerNATType registers a NAT type.
|
||||
func registerNATType(name string, f newTableFunc) {
|
||||
if _, ok := natTypes[name]; ok {
|
||||
panic("duplicate NAT type: " + name)
|
||||
}
|
||||
natTypes[name] = f
|
||||
}
|
||||
|
||||
// NATTable is what a NAT implementation is expected to do.
|
||||
//
|
||||
// This project tests Tailscale as it faces various combinations various NAT
|
||||
@ -46,7 +79,15 @@ type oneToOneNAT struct {
|
||||
wanIP netip.Addr
|
||||
}
|
||||
|
||||
var _ NATTable = (*oneToOneNAT)(nil)
|
||||
func init() {
|
||||
registerNATType("one2one", func(p IPPool) (NATTable, error) {
|
||||
lanIP, ok := p.SoleLANIP()
|
||||
if !ok {
|
||||
return nil, errors.New("can't use one2one NAT type on networks other than single-node networks")
|
||||
}
|
||||
return &oneToOneNAT{lanIP: lanIP, wanIP: p.WANIP()}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (n *oneToOneNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
||||
return netip.AddrPortFrom(n.wanIP, src.Port())
|
||||
@ -86,7 +127,11 @@ type hardNAT struct {
|
||||
in map[hardKeyIn]lanAddrAndTime
|
||||
}
|
||||
|
||||
var _ NATTable = (*hardNAT)(nil)
|
||||
func init() {
|
||||
registerNATType("hard", func(p IPPool) (NATTable, error) {
|
||||
return &hardNAT{wanIP: p.WANIP()}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (n *hardNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
||||
ko := hardKeyOut{src.Addr(), dst}
|
||||
@ -146,7 +191,11 @@ type easyNAT struct {
|
||||
in map[uint16]lanAddrAndTime
|
||||
}
|
||||
|
||||
var _ NATTable = (*easyNAT)(nil)
|
||||
func init() {
|
||||
registerNATType("easy", func(p IPPool) (NATTable, error) {
|
||||
return &easyNAT{wanIP: p.WANIP()}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (n *easyNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc netip.AddrPort) {
|
||||
if pm, ok := n.out[src]; ok {
|
||||
@ -162,6 +211,7 @@ func (n *easyNAT) PickOutgoingSrc(src, dst netip.AddrPort, at time.Time) (wanSrc
|
||||
port := 32<<10 + (start+off)%(32<<10)
|
||||
if _, ok := n.in[port]; !ok {
|
||||
wanAddr := netip.AddrPortFrom(n.wanIP, port)
|
||||
|
||||
// Found a free port.
|
||||
mak.Set(&n.out, src, portMappingAndTime{port: port, at: at})
|
||||
mak.Set(&n.in, port, lanAddrAndTime{lanAddr: src, at: at})
|
||||
|
@ -45,7 +45,7 @@ import (
|
||||
|
||||
var (
|
||||
listen = flag.String("listen", "/tmp/qemu.sock", "path to listen on")
|
||||
hard = flag.Bool("hard", false, "use hard NAT")
|
||||
natType = flag.String("nat", "easy", "type of NAT to use")
|
||||
portmap = flag.Bool("portmap", false, "enable portmapping")
|
||||
)
|
||||
|
||||
@ -84,7 +84,9 @@ func main() {
|
||||
}
|
||||
|
||||
s.nodes[node1.mac] = node1
|
||||
net1.InitNAT(*hard)
|
||||
if err := net1.InitNAT(*natType); err != nil {
|
||||
log.Fatalf("InitNAT: %v", err)
|
||||
}
|
||||
net1.portmap = *portmap
|
||||
net2 := &network{
|
||||
s: s,
|
||||
@ -98,7 +100,9 @@ func main() {
|
||||
lanIP: netip.MustParseAddr("10.2.0.102"),
|
||||
}
|
||||
s.nodes[node2.mac] = node2
|
||||
net2.InitNAT(*hard)
|
||||
if err := net2.InitNAT(*natType); err != nil {
|
||||
log.Fatalf("InitNAT: %v", err)
|
||||
}
|
||||
|
||||
if err := s.checkWorld(); err != nil {
|
||||
log.Fatalf("checkWorld: %v", err)
|
||||
@ -201,12 +205,17 @@ func (s *Server) checkWorld() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) InitNAT(useHardNAT bool) {
|
||||
if useHardNAT {
|
||||
n.SetNATTable(&hardNAT{wanIP: n.wanIP})
|
||||
} else {
|
||||
n.SetNATTable(&easyNAT{wanIP: n.wanIP})
|
||||
func (n *network) InitNAT(natType string) error {
|
||||
ctor, ok := natTypes[natType]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown NAT type %q", natType)
|
||||
}
|
||||
t, err := ctor(n)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating NAT type %q for network %v: %w", natType, n.wanIP, err)
|
||||
}
|
||||
n.SetNATTable(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) SetNATTable(nt NATTable) {
|
||||
@ -215,6 +224,20 @@ func (n *network) SetNATTable(nt NATTable) {
|
||||
n.natTable = nt
|
||||
}
|
||||
|
||||
// SoleLANIP implements [IPPool].
|
||||
func (n *network) SoleLANIP() (netip.Addr, bool) {
|
||||
if len(n.nodesByIP) != 1 {
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
for ip := range n.nodesByIP {
|
||||
return ip, true
|
||||
}
|
||||
return netip.Addr{}, false
|
||||
}
|
||||
|
||||
// WANIP implements [IPPool].
|
||||
func (n *network) WANIP() netip.Addr { return n.wanIP }
|
||||
|
||||
func (n *network) initStack() error {
|
||||
n.ns = stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||
|
Loading…
x
Reference in New Issue
Block a user