tailscale/tstest/natlab/natlab_test.go

511 lines
12 KiB
Go
Raw Normal View History

// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package natlab
import (
"context"
"fmt"
"net"
"net/netip"
"testing"
"time"
"tailscale.com/tstest"
)
func TestAllocIPs(t *testing.T) {
n := NewInternet()
saw := map[netip.Addr]bool{}
for i := 0; i < 255; i++ {
for _, f := range []func(*Interface) netip.Addr{n.allocIPv4, n.allocIPv6} {
ip := f(nil)
if saw[ip] {
t.Fatalf("got duplicate %v", ip)
}
saw[ip] = true
}
}
// This should work:
n.allocIPv6(nil)
// But allocating another IPv4 should panic, exhausting the
// limited /24 range:
defer func() {
if e := recover(); fmt.Sprint(e) != "pool exhausted" {
t.Errorf("unexpected panic: %v", e)
}
}()
n.allocIPv4(nil)
t.Fatalf("expected panic from IPv4")
}
func TestSendPacket(t *testing.T) {
internet := NewInternet()
foo := &Machine{Name: "foo"}
bar := &Machine{Name: "bar"}
ifFoo := foo.Attach("eth0", internet)
ifBar := bar.Attach("enp0s1", internet)
fooAddr := netip.AddrPortFrom(ifFoo.V4(), 123)
barAddr := netip.AddrPortFrom(ifBar.V4(), 456)
ctx := context.Background()
fooPC, err := foo.ListenPacket(ctx, "udp4", fooAddr.String())
if err != nil {
t.Fatal(err)
}
barPC, err := bar.ListenPacket(ctx, "udp4", barAddr.String())
if err != nil {
t.Fatal(err)
}
const msg = "some message"
if _, err := fooPC.WriteTo([]byte(msg), net.UDPAddrFromAddrPort(barAddr)); err != nil {
t.Fatal(err)
}
buf := make([]byte, 1500) // TODO: care about MTUs in the natlab package somewhere
n, addr, err := barPC.ReadFrom(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
if string(buf) != msg {
t.Errorf("read %q; want %q", buf, msg)
}
if addr.String() != fooAddr.String() {
t.Errorf("addr = %q; want %q", addr, fooAddr)
}
}
func TestMultiNetwork(t *testing.T) {
lan := &Network{
Name: "lan",
Prefix4: mustPrefix("192.168.0.0/24"),
}
internet := NewInternet()
client := &Machine{Name: "client"}
nat := &Machine{Name: "nat"}
server := &Machine{Name: "server"}
ifClient := client.Attach("eth0", lan)
ifNATWAN := nat.Attach("ethwan", internet)
ifNATLAN := nat.Attach("ethlan", lan)
ifServer := server.Attach("eth0", internet)
ctx := context.Background()
clientPC, err := client.ListenPacket(ctx, "udp", ":123")
if err != nil {
t.Fatal(err)
}
natPC, err := nat.ListenPacket(ctx, "udp", ":456")
if err != nil {
t.Fatal(err)
}
serverPC, err := server.ListenPacket(ctx, "udp", ":789")
if err != nil {
t.Fatal(err)
}
clientAddr := netip.AddrPortFrom(ifClient.V4(), 123)
natLANAddr := netip.AddrPortFrom(ifNATLAN.V4(), 456)
natWANAddr := netip.AddrPortFrom(ifNATWAN.V4(), 456)
serverAddr := netip.AddrPortFrom(ifServer.V4(), 789)
const msg1, msg2 = "hello", "world"
if _, err := natPC.WriteTo([]byte(msg1), net.UDPAddrFromAddrPort(clientAddr)); err != nil {
t.Fatal(err)
}
if _, err := natPC.WriteTo([]byte(msg2), net.UDPAddrFromAddrPort(serverAddr)); err != nil {
t.Fatal(err)
}
buf := make([]byte, 1500)
n, addr, err := clientPC.ReadFrom(buf)
if err != nil {
t.Fatal(err)
}
if string(buf[:n]) != msg1 {
t.Errorf("read %q; want %q", buf[:n], msg1)
}
if addr.String() != natLANAddr.String() {
t.Errorf("addr = %q; want %q", addr, natLANAddr)
}
n, addr, err = serverPC.ReadFrom(buf)
if err != nil {
t.Fatal(err)
}
if string(buf[:n]) != msg2 {
t.Errorf("read %q; want %q", buf[:n], msg2)
}
if addr.String() != natWANAddr.String() {
t.Errorf("addr = %q; want %q", addr, natLANAddr)
}
}
type trivialNAT struct {
clientIP netip.Addr
lanIf, wanIf *Interface
}
func (n *trivialNAT) HandleIn(p *Packet, iface *Interface) *Packet {
if iface == n.wanIf && p.Dst.Addr() == n.wanIf.V4() {
p.Dst = netip.AddrPortFrom(n.clientIP, p.Dst.Port())
}
return p
}
func (n trivialNAT) HandleOut(p *Packet, iface *Interface) *Packet {
return p
}
func (n *trivialNAT) HandleForward(p *Packet, iif, oif *Interface) *Packet {
// Outbound from LAN -> apply NAT, continue
if iif == n.lanIf && oif == n.wanIf {
if p.Src.Addr() == n.clientIP {
p.Src = netip.AddrPortFrom(n.wanIf.V4(), p.Src.Port())
}
return p
}
// Return traffic to LAN, allow if right dst.
if iif == n.wanIf && oif == n.lanIf && p.Dst.Addr() == n.clientIP {
return p
}
// Else drop.
return nil
}
func TestPacketHandler(t *testing.T) {
lan := &Network{
Name: "lan",
Prefix4: mustPrefix("192.168.0.0/24"),
Prefix6: mustPrefix("fd00:916::/64"),
}
internet := NewInternet()
client := &Machine{Name: "client"}
nat := &Machine{Name: "nat"}
server := &Machine{Name: "server"}
ifClient := client.Attach("eth0", lan)
ifNATWAN := nat.Attach("wan", internet)
ifNATLAN := nat.Attach("lan", lan)
ifServer := server.Attach("server", internet)
lan.SetDefaultGateway(ifNATLAN)
nat.PacketHandler = &trivialNAT{
clientIP: ifClient.V4(),
lanIf: ifNATLAN,
wanIf: ifNATWAN,
}
ctx := context.Background()
clientPC, err := client.ListenPacket(ctx, "udp4", ":123")
if err != nil {
t.Fatal(err)
}
serverPC, err := server.ListenPacket(ctx, "udp4", ":456")
if err != nil {
t.Fatal(err)
}
const msg = "some message"
serverAddr := netip.AddrPortFrom(ifServer.V4(), 456)
if _, err := clientPC.WriteTo([]byte(msg), net.UDPAddrFromAddrPort(serverAddr)); err != nil {
t.Fatal(err)
}
buf := make([]byte, 1500) // TODO: care about MTUs in the natlab package somewhere
n, addr, err := serverPC.ReadFrom(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
if string(buf) != msg {
t.Errorf("read %q; want %q", buf, msg)
}
mappedAddr := netip.AddrPortFrom(ifNATWAN.V4(), 123)
if addr.String() != mappedAddr.String() {
t.Errorf("addr = %q; want %q", addr, mappedAddr)
}
}
func TestFirewall(t *testing.T) {
wan := NewInternet()
lan := &Network{
Name: "lan",
Prefix4: mustPrefix("10.0.0.0/8"),
}
m := &Machine{Name: "test"}