mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-05 23:07:44 +00:00
tstest/natlab/vnet: add more tests
This adds tests for DNS requests, and ignoring IPv6 packets on v4-only networks. No behavior changes. But some things are pulled out into functions. And the mkPacket helpers previously just for tests are moved into non-test code to be used elsewhere to reduce duplication, doing the checksum stuff automatically. Updates #13038 Change-Id: I4dd0b73c75b2b9567b4be3f05a2792999d83f6a3 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
d21ebc28af
commit
82c2c5c597
@ -65,6 +65,11 @@ func nodeMac(n int) MAC {
|
||||
return MAC{0x52, 0xcc, 0xcc, 0xcc, 0xcc, byte(n)}
|
||||
}
|
||||
|
||||
func routerMac(n int) MAC {
|
||||
// 52=TS then 0xee for 'etwork
|
||||
return MAC{0x52, 0xee, 0xee, 0xee, 0xee, byte(n)}
|
||||
}
|
||||
|
||||
var lanSLAACBase = netip.MustParseAddr("fe80::50cc:ccff:fecc:cc01")
|
||||
|
||||
// nodeLANIP6 returns a node number's Link Local SLAAC IPv6 address,
|
||||
@ -148,7 +153,7 @@ func (c *Config) AddNetwork(opts ...any) *Network {
|
||||
num := len(c.networks) + 1
|
||||
n := &Network{
|
||||
num: num,
|
||||
mac: MAC{0x52, 0xee, 0xee, 0xee, 0xee, byte(num)}, // 52=TS then 0xee for 'etwork
|
||||
mac: routerMac(num),
|
||||
}
|
||||
c.networks = append(c.networks, n)
|
||||
for _, o := range opts {
|
||||
|
@ -1218,18 +1218,11 @@ func (n *network) serializedUDPPacket(src, dst netip.AddrPort, payload []byte, e
|
||||
SrcPort: layers.UDPPort(src.Port()),
|
||||
DstPort: layers.UDPPort(dst.Port()),
|
||||
}
|
||||
udp.SetNetworkLayerForChecksum(ip)
|
||||
|
||||
buffer := gopacket.NewSerializeBuffer()
|
||||
options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||
layers := []gopacket.SerializableLayer{eth, ip, udp, gopacket.Payload(payload)}
|
||||
if eth == nil {
|
||||
layers = layers[1:]
|
||||
return mkPacketErr(ip, udp, gopacket.Payload(payload))
|
||||
} else {
|
||||
return mkPacketErr(eth, ip, udp, gopacket.Payload(payload))
|
||||
}
|
||||
if err := gopacket.SerializeLayers(buffer, options, layers...); err != nil {
|
||||
return nil, fmt.Errorf("serializing UDP from %v to %v: %v", src, dst, err)
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// HandleEthernetPacketForRouter handles a packet that is
|
||||
@ -1434,14 +1427,12 @@ func (n *network) handleIPv6RouterSolicitation(ep EthernetPacket, rs *layers.ICM
|
||||
},
|
||||
},
|
||||
}
|
||||
icmp.SetNetworkLayerForChecksum(ip)
|
||||
buffer := gopacket.NewSerializeBuffer()
|
||||
options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||
if err := gopacket.SerializeLayers(buffer, options, eth, ip, icmp, ra); err != nil {
|
||||
pkt, err := mkPacketErr(eth, ip, icmp, ra)
|
||||
if err != nil {
|
||||
n.logf("serializing ICMPv6 RA: %v", err)
|
||||
return
|
||||
}
|
||||
n.writeEth(buffer.Bytes())
|
||||
n.writeEth(pkt)
|
||||
}
|
||||
|
||||
func (n *network) handleIPv6NeighborSolicitation(ep EthernetPacket, ns *layers.ICMPv6NeighborSolicitation) {
|
||||
@ -2203,3 +2194,35 @@ func (c *NodeAgentClient) EnableHostFirewall(ctx context.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mkPacket(layers ...gopacket.SerializableLayer) []byte {
|
||||
return must.Get(mkPacketErr(layers...))
|
||||
}
|
||||
|
||||
func mkPacketErr(ll ...gopacket.SerializableLayer) ([]byte, error) {
|
||||
var nl gopacket.NetworkLayer
|
||||
for _, la := range ll {
|
||||
switch la := la.(type) {
|
||||
case *layers.IPv4:
|
||||
nl = la
|
||||
case *layers.IPv6:
|
||||
nl = la
|
||||
}
|
||||
}
|
||||
for _, la := range ll {
|
||||
switch la := la.(type) {
|
||||
case *layers.TCP:
|
||||
la.SetNetworkLayerForChecksum(nl)
|
||||
case *layers.UDP:
|
||||
la.SetNetworkLayerForChecksum(nl)
|
||||
case *layers.ICMPv6:
|
||||
la.SetNetworkLayerForChecksum(nl)
|
||||
}
|
||||
}
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||
if err := gopacket.SerializeLayers(buf, opts, ll...); err != nil {
|
||||
return nil, fmt.Errorf("serializing packet: %v", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
@ -21,6 +21,11 @@
|
||||
"tailscale.com/util/must"
|
||||
)
|
||||
|
||||
const (
|
||||
ethType4 = layers.EthernetTypeIPv4
|
||||
ethType6 = layers.EthernetTypeIPv6
|
||||
)
|
||||
|
||||
// TestPacketSideEffects tests that upon receiving certain
|
||||
// packets, other packets and/or log statements are generated.
|
||||
func TestPacketSideEffects(t *testing.T) {
|
||||
@ -36,7 +41,7 @@ type netTest struct {
|
||||
}{
|
||||
{
|
||||
netName: "basic",
|
||||
setup: newTwoNodesSameNetworkServer,
|
||||
setup: newTwoNodesSameNetwork,
|
||||
tests: []netTest{
|
||||
{
|
||||
name: "drop-rando-ethertype",
|
||||
@ -63,6 +68,37 @@ type netTest struct {
|
||||
pktSubstr("Unable to decode EthernetType 4660"),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "dns-request-v4",
|
||||
pkt: mkDNSReq(4),
|
||||
check: all(
|
||||
numPkts(1),
|
||||
pktSubstr("Data=[52, 52, 0, 3] IP=52.52.0.3"),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "dns-request-v6",
|
||||
pkt: mkDNSReq(6),
|
||||
check: all(
|
||||
numPkts(1),
|
||||
pktSubstr(" IP=2052::3 "),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
netName: "v4",
|
||||
setup: newTwoNodesSameV4Network,
|
||||
tests: []netTest{
|
||||
{
|
||||
name: "no-v6-reply-on-v4-only",
|
||||
pkt: mkIPv6RouterSolicit(nodeMac(1), nodeLANIP6(1)),
|
||||
check: all(
|
||||
numPkts(0),
|
||||
logSubstr("dropping IPv6 packet on v4-only network"),
|
||||
),
|
||||
},
|
||||
// TODO(bradfitz): DHCP request + response
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -170,16 +206,7 @@ func mkIPv6RouterSolicit(srcMAC MAC, srcIP netip.Addr) []byte {
|
||||
}},
|
||||
}
|
||||
icmp.SetNetworkLayerForChecksum(ip)
|
||||
return mkEth(macAllRouters, srcMAC, layers.EthernetTypeIPv6, mkPacket(ip, icmp, ra))
|
||||
}
|
||||
|
||||
func mkPacket(layers ...gopacket.SerializableLayer) []byte {
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||
if err := gopacket.SerializeLayers(buf, opts, layers...); err != nil {
|
||||
panic(fmt.Sprintf("serializing packet: %v", err))
|
||||
}
|
||||
return buf.Bytes()
|
||||
return mkEth(macAllRouters, srcMAC, ethType6, mkPacket(ip, icmp, ra))
|
||||
}
|
||||
|
||||
func mkAllNodesPing(srcMAC MAC, srcIP netip.Addr) []byte {
|
||||
@ -194,7 +221,68 @@ func mkAllNodesPing(srcMAC MAC, srcIP netip.Addr) []byte {
|
||||
TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeEchoRequest, 0),
|
||||
}
|
||||
icmp.SetNetworkLayerForChecksum(ip)
|
||||
return mkEth(macAllNodes, srcMAC, layers.EthernetTypeIPv6, mkPacket(ip, icmp))
|
||||
return mkEth(macAllNodes, srcMAC, ethType6, mkPacket(ip, icmp))
|
||||
}
|
||||
|
||||
// mkDNSReq makes a DNS request to "control.tailscale" using the source IPs as
|
||||
// defined in this test file.
|
||||
//
|
||||
// ipVer must be 4 or 6:
|
||||
// If 4, it makes an A record request.
|
||||
// If 6, it makes a AAAA record request.
|
||||
//
|
||||
// (Yes, this is technically unrelated (you can request A records over IPv6 or
|
||||
// AAAA records over IPv4), but for test coverage reasons, assume that the ipVer
|
||||
// of 6 means to also request an AAAA record.)
|
||||
func mkDNSReq(ipVer int) []byte {
|
||||
eth := &layers.Ethernet{
|
||||
SrcMAC: nodeMac(1).HWAddr(),
|
||||
DstMAC: routerMac(1).HWAddr(),
|
||||
EthernetType: layers.EthernetTypeIPv4,
|
||||
}
|
||||
if ipVer == 6 {
|
||||
eth.EthernetType = layers.EthernetTypeIPv6
|
||||
}
|
||||
|
||||
var ip serializableNetworkLayer
|
||||
switch ipVer {
|
||||
case 4:
|
||||
ip = &layers.IPv4{
|
||||
Version: 4,
|
||||
Protocol: layers.IPProtocolUDP,
|
||||
SrcIP: net.ParseIP("192.168.0.101"),
|
||||
TTL: 64,
|
||||
DstIP: FakeDNSIPv4().AsSlice(),
|
||||
}
|
||||
case 6:
|
||||
ip = &layers.IPv6{
|
||||
Version: 6,
|
||||
HopLimit: 64,
|
||||
NextHeader: layers.IPProtocolUDP,
|
||||
SrcIP: net.ParseIP("2000:52::1"),
|
||||
DstIP: FakeDNSIPv6().AsSlice(),
|
||||
}
|
||||
default:
|
||||
panic("bad ipVer")
|
||||
}
|
||||
|
||||
udp := &layers.UDP{
|
||||
SrcPort: 12345,
|
||||
DstPort: 53,
|
||||
}
|
||||
udp.SetNetworkLayerForChecksum(ip)
|
||||
dns := &layers.DNS{
|
||||
ID: 789,
|
||||
Questions: []layers.DNSQuestion{{
|
||||
Name: []byte("control.tailscale"),
|
||||
Type: layers.DNSTypeA,
|
||||
Class: layers.DNSClassIN,
|
||||
}},
|
||||
}
|
||||
if ipVer == 6 {
|
||||
dns.Questions[0].Type = layers.DNSTypeAAAA
|
||||
}
|
||||
return mkPacket(eth, ip, udp, dns)
|
||||
}
|
||||
|
||||
// sideEffects gathers side effects as a result of sending a packet and tests
|
||||
@ -269,7 +357,15 @@ func numPkts(want int) func(*sideEffects) error {
|
||||
}
|
||||
}
|
||||
|
||||
func newTwoNodesSameNetworkServer() (*Server, error) {
|
||||
func newTwoNodesSameNetwork() (*Server, error) {
|
||||
var c Config
|
||||
nw := c.AddNetwork("192.168.0.1/24", "2052::1/64")
|
||||
c.AddNode(nw)
|
||||
c.AddNode(nw)
|
||||
return New(&c)
|
||||
}
|
||||
|
||||
func newTwoNodesSameV4Network() (*Server, error) {
|
||||
var c Config
|
||||
nw := c.AddNetwork("192.168.0.1/24")
|
||||
c.AddNode(nw)
|
||||
@ -286,7 +382,7 @@ func TestProtocolQEMU(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skipf("skipping on %s", runtime.GOOS)
|
||||
}
|
||||
s := must.Get(newTwoNodesSameNetworkServer())
|
||||
s := must.Get(newTwoNodesSameNetwork())
|
||||
defer s.Close()
|
||||
s.SetLoggerForTest(t.Logf)
|
||||
|
||||
@ -329,7 +425,7 @@ func TestProtocolUnixDgram(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skipf("skipping on %s", runtime.GOOS)
|
||||
}
|
||||
s := must.Get(newTwoNodesSameNetworkServer())
|
||||
s := must.Get(newTwoNodesSameNetwork())
|
||||
defer s.Close()
|
||||
s.SetLoggerForTest(t.Logf)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user