tstest/integration/vms: end-to-end UDP test (#2361)

This tests incoming and outgoing UDP traffic. It would test incoming UDP
traffic however our socks server doesn't seem to allow for connecting to
destinations over UDP. When the socks server gets that support the
incoming test should pass without issue.

Signed-off-by: Christine Dodrill <xe@tailscale.com>
This commit is contained in:
Christine Dodrill 2021-07-08 15:58:18 -04:00 committed by GitHub
parent a19eea965f
commit dc78be12c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,80 @@
// Copyright (c) 2021 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.
// +build ignore
// Command udp_tester exists because all of these distros being tested don't
// have a consistent tool for doing UDP traffic. This is a very hacked up tool
// that does that UDP traffic so these tests can be done.
package main
import (
"flag"
"fmt"
"io"
"log"
"net"
"os"
)
var (
client = flag.String("client", "", "host:port to connect to for sending UDP")
server = flag.String("server", "", "host:port to bind to for receiving UDP")
)
func main() {
flag.Parse()
if *client == "" && *server == "" {
log.Fatal("specify -client or -server")
}
if *client != "" {
conn, err := net.Dial("udp", *client)
if err != nil {
log.Fatalf("can't dial %s: %v", *client, err)
}
log.Printf("dialed to %s", conn.RemoteAddr())
defer conn.Close()
buf := make([]byte, 1024)
n, err := os.Stdin.Read(buf)
if err != nil && err != io.EOF {
log.Fatalf("can't read from stdin: %v", err)
}
nn, err := conn.Write(buf[:n])
if err != nil {
log.Fatalf("can't write to %s: %v", conn.RemoteAddr(), err)
}
if n != nn {
log.Fatalf("wanted to write %d bytes, wrote %d bytes", n, nn)
}
}
if *server != "" {
addr, err := net.ResolveUDPAddr("udp", *server)
if err != nil {
log.Fatalf("can't resolve %s: %v", *server, err)
}
ln, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatalf("can't listen %s: %v", *server, err)
}
defer ln.Close()
buf := make([]byte, 1024)
n, _, err := ln.ReadFromUDP(buf)
if err != nil {
log.Fatal(err)
}
if n == 0 {
log.Fatal("got nothing")
}
fmt.Print(string(buf[:n]))
}
}

View File

@ -859,6 +859,122 @@ func (h Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
t.Fatalf("wanted reported ip to be %q, got: %q", string(ipBytes), string(testIPBytes))
}
})
t.Run("outgoing-udp-ipv4", func(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("can't get working directory: %v", err)
}
dir := t.TempDir()
run(t, cwd, "go", "build", "-o", filepath.Join(dir, "udp_tester"), "./udp_tester.go")
sftpCli, err := sftp.NewClient(cli)
if err != nil {
t.Fatalf("can't connect over sftp to copy binaries: %v", err)
}
defer sftpCli.Close()
copyFile(t, sftpCli, filepath.Join(dir, "udp_tester"), "/udp_tester")
uaddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("::", "0"))
if err != nil {
t.Fatalf("can't resolve udp listener addr: %v", err)
}
buf := make([]byte, 1024)
ln, err := net.ListenUDP("udp", uaddr)
if err != nil {
t.Fatalf("can't listen for UDP traffic: %v", err)
}
defer ln.Close()
sess, err := cli.NewSession()
if err != nil {
t.Fatalf("can't open session: %v", err)
}
defer sess.Close()
sess.Stdin = strings.NewReader("hi")
sess.Stdout = logger.FuncWriter(t.Logf)
sess.Stderr = logger.FuncWriter(t.Logf)
_, port, _ := net.SplitHostPort(ln.LocalAddr().String())
go func() {
cmd := fmt.Sprintf("/udp_tester -client %s\n", net.JoinHostPort("100.64.0.1", port))
time.Sleep(10 * time.Millisecond)
t.Logf("sending packet: %s", cmd)
err := sess.Run(cmd)
if err != nil {
t.Errorf("can't send UDP packet: %v", err)
}
}()
t.Log("listening for packet")
n, _, err := ln.ReadFromUDP(buf)
if err != nil {
t.Fatal(err)
}
if n == 0 {
t.Fatal("got nothing")
}
if !bytes.Contains(buf, []byte("hi")) {
t.Fatal("did not get UDP message")
}
})
t.Run("incoming-udp-ipv4", func(t *testing.T) {
// vms_test.go:947: can't dial: socks connect udp 127.0.0.1:36497->100.64.0.2:33409: network not implemented
t.Skip("can't make outgoing sockets over UDP with our socks server")
sess, err := cli.NewSession()
if err != nil {
t.Fatalf("can't open session: %v", err)
}
defer sess.Close()
ip, err := sess.Output("tailscale ip -4")
if err != nil {
t.Fatalf("can't nab ipv4 address: %v", err)
}
port, err := getProbablyFreePortNumber()
if err != nil {
t.Fatalf("unable to fetch port number: %v", err)
}
go func() {
time.Sleep(10 * time.Millisecond)
conn, err := h.testerDialer.Dial("udp", net.JoinHostPort(string(bytes.TrimSpace(ip)), strconv.Itoa(port)))
if err != nil {
t.Errorf("can't dial: %v", err)
}
fmt.Fprint(conn, securePassword)
}()
sess, err = cli.NewSession()
if err != nil {
t.Fatalf("can't open session: %v", err)
}
defer sess.Close()
sess.Stderr = logger.FuncWriter(t.Logf)
msg, err := sess.Output(
fmt.Sprintf(
"/udp_tester -server %s",
net.JoinHostPort(string(bytes.TrimSpace(ip)), strconv.Itoa(port)),
),
)
if msg := string(bytes.TrimSpace(msg)); msg != securePassword {
t.Fatalf("wanted %q from vm, got: %q", securePassword, msg)
}
})
}
func runTestCommands(t *testing.T, timeout time.Duration, cli *ssh.Client, batch []expect.Batcher) {