mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tstest/natlab: provide inbound interface to HandlePacket.
Requires a bunch of refactoring so that Networks only ever refer to Interfaces that have been attached to them, and Interfaces know about both their Network and Machine. Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
977381f9cc
commit
0ed9f62ed0
@ -70,29 +70,29 @@ type Network struct {
|
|||||||
Prefix6 netaddr.IPPrefix
|
Prefix6 netaddr.IPPrefix
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
machine map[netaddr.IP]*Machine
|
machine map[netaddr.IP]*Interface
|
||||||
defaultGW *Machine // optional
|
defaultGW *Interface // optional
|
||||||
lastV4 netaddr.IP
|
lastV4 netaddr.IP
|
||||||
lastV6 netaddr.IP
|
lastV6 netaddr.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) SetDefaultGateway(gw *Machine) {
|
func (n *Network) SetDefaultGateway(gwIf *Interface) {
|
||||||
n.mu.Lock()
|
n.mu.Lock()
|
||||||
defer n.mu.Unlock()
|
defer n.mu.Unlock()
|
||||||
n.defaultGW = gw
|
n.defaultGW = gwIf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) addMachineLocked(ip netaddr.IP, m *Machine) {
|
func (n *Network) addMachineLocked(ip netaddr.IP, iface *Interface) {
|
||||||
if m == nil {
|
if iface == nil {
|
||||||
return // for tests
|
return // for tests
|
||||||
}
|
}
|
||||||
if n.machine == nil {
|
if n.machine == nil {
|
||||||
n.machine = map[netaddr.IP]*Machine{}
|
n.machine = map[netaddr.IP]*Interface{}
|
||||||
}
|
}
|
||||||
n.machine[ip] = m
|
n.machine[ip] = iface
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) allocIPv4(m *Machine) netaddr.IP {
|
func (n *Network) allocIPv4(iface *Interface) netaddr.IP {
|
||||||
n.mu.Lock()
|
n.mu.Lock()
|
||||||
defer n.mu.Unlock()
|
defer n.mu.Unlock()
|
||||||
if n.Prefix4.IsZero() {
|
if n.Prefix4.IsZero() {
|
||||||
@ -107,11 +107,11 @@ func (n *Network) allocIPv4(m *Machine) netaddr.IP {
|
|||||||
if !n.Prefix4.Contains(n.lastV4) {
|
if !n.Prefix4.Contains(n.lastV4) {
|
||||||
panic("pool exhausted")
|
panic("pool exhausted")
|
||||||
}
|
}
|
||||||
n.addMachineLocked(n.lastV4, m)
|
n.addMachineLocked(n.lastV4, iface)
|
||||||
return n.lastV4
|
return n.lastV4
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) allocIPv6(m *Machine) netaddr.IP {
|
func (n *Network) allocIPv6(iface *Interface) netaddr.IP {
|
||||||
n.mu.Lock()
|
n.mu.Lock()
|
||||||
defer n.mu.Unlock()
|
defer n.mu.Unlock()
|
||||||
if n.Prefix6.IsZero() {
|
if n.Prefix6.IsZero() {
|
||||||
@ -126,7 +126,7 @@ func (n *Network) allocIPv6(m *Machine) netaddr.IP {
|
|||||||
if !n.Prefix6.Contains(n.lastV6) {
|
if !n.Prefix6.Contains(n.lastV6) {
|
||||||
panic("pool exhausted")
|
panic("pool exhausted")
|
||||||
}
|
}
|
||||||
n.addMachineLocked(n.lastV6, m)
|
n.addMachineLocked(n.lastV6, iface)
|
||||||
return n.lastV6
|
return n.lastV6
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,27 +142,28 @@ func addOne(a *[16]byte, index int) {
|
|||||||
func (n *Network) write(p []byte, dst, src netaddr.IPPort) (num int, err error) {
|
func (n *Network) write(p []byte, dst, src netaddr.IPPort) (num int, err error) {
|
||||||
n.mu.Lock()
|
n.mu.Lock()
|
||||||
defer n.mu.Unlock()
|
defer n.mu.Unlock()
|
||||||
m, ok := n.machine[dst.IP]
|
iface, ok := n.machine[dst.IP]
|
||||||
if !ok {
|
if !ok {
|
||||||
if n.defaultGW == nil {
|
if n.defaultGW == nil {
|
||||||
trace(p, "net=%s dropped, no route to %v", n.Name, dst.IP)
|
trace(p, "net=%s dropped, no route to %v", n.Name, dst.IP)
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
m = n.defaultGW
|
iface = n.defaultGW
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretend it went across the network. Make a copy so nobody
|
// Pretend it went across the network. Make a copy so nobody
|
||||||
// can later mess with caller's memory.
|
// can later mess with caller's memory.
|
||||||
trace(p, "net=%s src=%v dst=%v -> mach=%s", n.Name, src, dst, m.Name)
|
trace(p, "net=%s src=%v dst=%v -> mach=%s iface=%s", n.Name, src, dst, iface.machine.Name, iface.name)
|
||||||
pcopy := append([]byte(nil), p...)
|
pcopy := append([]byte(nil), p...)
|
||||||
go m.deliverIncomingPacket(pcopy, dst, src)
|
go iface.machine.deliverIncomingPacket(pcopy, iface, dst, src)
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
net *Network
|
machine *Machine
|
||||||
name string // optional
|
net *Network
|
||||||
ips []netaddr.IP // static; not mutated once created
|
name string // optional
|
||||||
|
ips []netaddr.IP // static; not mutated once created
|
||||||
}
|
}
|
||||||
|
|
||||||
// V4 returns the machine's first IPv4 address, or the zero value if none.
|
// V4 returns the machine's first IPv4 address, or the zero value if none.
|
||||||
@ -226,7 +227,7 @@ func (v PacketVerdict) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A PacketHandler is a function that can process packets.
|
// A PacketHandler is a function that can process packets.
|
||||||
type PacketHandler func(p []byte, dst, src netaddr.IPPort) PacketVerdict
|
type PacketHandler func(p []byte, inIf *Interface, dst, src netaddr.IPPort) PacketVerdict
|
||||||
|
|
||||||
// A Machine is a representation of an operating system's network
|
// A Machine is a representation of an operating system's network
|
||||||
// stack. It has a network routing table and can have multiple
|
// stack. It has a network routing table and can have multiple
|
||||||
@ -261,11 +262,11 @@ func (m *Machine) Inject(p []byte, dst, src netaddr.IPPort) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Machine) deliverIncomingPacket(p []byte, dst, src netaddr.IPPort) {
|
func (m *Machine) deliverIncomingPacket(p []byte, iface *Interface, dst, src netaddr.IPPort) {
|
||||||
// TODO: can't hold lock while handling packet. This is safe as
|
// TODO: can't hold lock while handling packet. This is safe as
|
||||||
// long as you set HandlePacket before traffic starts flowing.
|
// long as you set HandlePacket before traffic starts flowing.
|
||||||
if m.HandlePacket != nil {
|
if m.HandlePacket != nil {
|
||||||
verdict := m.HandlePacket(p, dst, src)
|
verdict := m.HandlePacket(p, iface, dst, src)
|
||||||
trace(p, "mach=%s src=%v dst=%v packethandler verdict=%s", m.Name, src, dst, verdict)
|
trace(p, "mach=%s src=%v dst=%v packethandler verdict=%s", m.Name, src, dst, verdict)
|
||||||
if verdict == Drop {
|
if verdict == Drop {
|
||||||
// Custom packet handler ate the packet, we're done.
|
// Custom packet handler ate the packet, we're done.
|
||||||
@ -318,13 +319,14 @@ func unspecOf(ip netaddr.IP) netaddr.IP {
|
|||||||
// default route.
|
// default route.
|
||||||
func (m *Machine) Attach(interfaceName string, n *Network) *Interface {
|
func (m *Machine) Attach(interfaceName string, n *Network) *Interface {
|
||||||
f := &Interface{
|
f := &Interface{
|
||||||
net: n,
|
machine: m,
|
||||||
name: interfaceName,
|
net: n,
|
||||||
|
name: interfaceName,
|
||||||
}
|
}
|
||||||
if ip := n.allocIPv4(m); !ip.IsZero() {
|
if ip := n.allocIPv4(f); !ip.IsZero() {
|
||||||
f.ips = append(f.ips, ip)
|
f.ips = append(f.ips, ip)
|
||||||
}
|
}
|
||||||
if ip := n.allocIPv6(m); !ip.IsZero() {
|
if ip := n.allocIPv6(f); !ip.IsZero() {
|
||||||
f.ips = append(f.ips, ip)
|
f.ips = append(f.ips, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ func TestAllocIPs(t *testing.T) {
|
|||||||
n := NewInternet()
|
n := NewInternet()
|
||||||
saw := map[netaddr.IP]bool{}
|
saw := map[netaddr.IP]bool{}
|
||||||
for i := 0; i < 255; i++ {
|
for i := 0; i < 255; i++ {
|
||||||
for _, f := range []func(*Machine) netaddr.IP{n.allocIPv4, n.allocIPv6} {
|
for _, f := range []func(*Interface) netaddr.IP{n.allocIPv4, n.allocIPv6} {
|
||||||
ip := f(nil)
|
ip := f(nil)
|
||||||
if saw[ip] {
|
if saw[ip] {
|
||||||
t.Fatalf("got duplicate %v", ip)
|
t.Fatalf("got duplicate %v", ip)
|
||||||
@ -156,14 +156,15 @@ func TestPacketHandler(t *testing.T) {
|
|||||||
|
|
||||||
client := &Machine{Name: "client"}
|
client := &Machine{Name: "client"}
|
||||||
nat := &Machine{Name: "nat"}
|
nat := &Machine{Name: "nat"}
|
||||||
lan.SetDefaultGateway(nat)
|
|
||||||
server := &Machine{Name: "server"}
|
server := &Machine{Name: "server"}
|
||||||
|
|
||||||
ifClient := client.Attach("eth0", lan)
|
ifClient := client.Attach("eth0", lan)
|
||||||
ifNATWAN := nat.Attach("wan", internet)
|
ifNATWAN := nat.Attach("wan", internet)
|
||||||
_ = nat.Attach("lan", lan)
|
ifNATLAN := nat.Attach("lan", lan)
|
||||||
ifServer := server.Attach("server", internet)
|
ifServer := server.Attach("server", internet)
|
||||||
|
|
||||||
|
lan.SetDefaultGateway(ifNATLAN)
|
||||||
|
|
||||||
// This HandlePacket implements a basic (some might say "broken")
|
// This HandlePacket implements a basic (some might say "broken")
|
||||||
// 1:1 NAT, where client's IP gets replaced with the NAT's WAN IP,
|
// 1:1 NAT, where client's IP gets replaced with the NAT's WAN IP,
|
||||||
// and vice versa.
|
// and vice versa.
|
||||||
@ -172,14 +173,14 @@ func TestPacketHandler(t *testing.T) {
|
|||||||
// port remappings or any other things that NATs usually to. But
|
// port remappings or any other things that NATs usually to. But
|
||||||
// it works as a demonstrator for a single client behind the NAT,
|
// it works as a demonstrator for a single client behind the NAT,
|
||||||
// where the NAT box itself doesn't also make PacketConns.
|
// where the NAT box itself doesn't also make PacketConns.
|
||||||
nat.HandlePacket = func(p []byte, dst, src netaddr.IPPort) PacketVerdict {
|
nat.HandlePacket = func(p []byte, iface *Interface, dst, src netaddr.IPPort) PacketVerdict {
|
||||||
switch {
|
switch {
|
||||||
case dst.IP.Is6():
|
case dst.IP.Is6():
|
||||||
return Continue // no NAT for ipv6
|
return Continue // no NAT for ipv6
|
||||||
case src.IP == ifClient.V4():
|
case iface == ifNATLAN && src.IP == ifClient.V4():
|
||||||
nat.Inject(p, dst, netaddr.IPPort{IP: ifNATWAN.V4(), Port: src.Port})
|
nat.Inject(p, dst, netaddr.IPPort{IP: ifNATWAN.V4(), Port: src.Port})
|
||||||
return Drop
|
return Drop
|
||||||
case dst.IP == ifNATWAN.V4():
|
case iface == ifNATWAN && dst.IP == ifNATWAN.V4():
|
||||||
nat.Inject(p, netaddr.IPPort{IP: ifClient.V4(), Port: dst.Port}, src)
|
nat.Inject(p, netaddr.IPPort{IP: ifClient.V4(), Port: dst.Port}, src)
|
||||||
return Drop
|
return Drop
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user