2019-01-02 01:55:51 +01:00
|
|
|
/* SPDX-License-Identifier: MIT
|
2018-05-03 15:04:00 +02:00
|
|
|
*
|
2020-05-02 10:08:26 +02:00
|
|
|
* Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved.
|
2018-05-03 15:04:00 +02:00
|
|
|
*/
|
|
|
|
|
2019-03-03 04:04:41 +01:00
|
|
|
package device
|
2018-03-08 16:44:27 +01:00
|
|
|
|
2019-05-29 18:18:20 +02:00
|
|
|
import (
|
|
|
|
"bytes"
|
2020-12-15 00:07:23 +01:00
|
|
|
"errors"
|
2020-07-30 18:20:49 +02:00
|
|
|
"fmt"
|
2020-12-14 23:12:56 +01:00
|
|
|
"io"
|
2019-10-12 18:44:05 +02:00
|
|
|
"net"
|
2020-12-15 00:07:23 +01:00
|
|
|
"sync"
|
2019-05-29 18:18:20 +02:00
|
|
|
"testing"
|
2019-10-12 18:44:05 +02:00
|
|
|
"time"
|
|
|
|
|
2020-01-07 16:43:17 +01:00
|
|
|
"golang.zx2c4.com/wireguard/tun/tuntest"
|
2019-05-29 18:18:20 +02:00
|
|
|
)
|
2018-03-08 16:44:27 +01:00
|
|
|
|
2020-07-30 18:20:49 +02:00
|
|
|
func getFreePort(t *testing.T) string {
|
|
|
|
l, err := net.ListenPacket("udp", "localhost:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer l.Close()
|
|
|
|
return fmt.Sprintf("%d", l.LocalAddr().(*net.UDPAddr).Port)
|
|
|
|
}
|
|
|
|
|
2020-12-14 23:12:56 +01:00
|
|
|
// uapiCfg returns a reader that contains cfg formatted use with IpcSetOperation.
|
|
|
|
// cfg is a series of alternating key/value strings.
|
|
|
|
// uapiCfg exists because editors and humans like to insert
|
|
|
|
// whitespace into configs, which can cause failures, some of which are silent.
|
|
|
|
// For example, a leading blank newline causes the remainder
|
|
|
|
// of the config to be silently ignored.
|
|
|
|
func uapiCfg(cfg ...string) io.ReadSeeker {
|
|
|
|
if len(cfg)%2 != 0 {
|
|
|
|
panic("odd number of args to uapiReader")
|
|
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
for i, s := range cfg {
|
|
|
|
buf.WriteString(s)
|
|
|
|
sep := byte('\n')
|
|
|
|
if i%2 == 0 {
|
|
|
|
sep = '='
|
|
|
|
}
|
|
|
|
buf.WriteByte(sep)
|
|
|
|
}
|
|
|
|
return bytes.NewReader(buf.Bytes())
|
|
|
|
}
|
|
|
|
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
// genConfigs generates a pair of configs that connect to each other.
|
|
|
|
// The configs use distinct, probably-usable ports.
|
2020-12-14 23:12:56 +01:00
|
|
|
func genConfigs(t *testing.T) (cfgs [2]io.Reader) {
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
var port1, port2 string
|
|
|
|
for port1 == port2 {
|
|
|
|
port1 = getFreePort(t)
|
|
|
|
port2 = getFreePort(t)
|
|
|
|
}
|
2020-12-14 23:12:56 +01:00
|
|
|
|
|
|
|
cfgs[0] = uapiCfg(
|
|
|
|
"private_key", "481eb0d8113a4a5da532d2c3e9c14b53c8454b34ab109676f6b58c2245e37b58",
|
|
|
|
"listen_port", port1,
|
|
|
|
"replace_peers", "true",
|
|
|
|
"public_key", "f70dbb6b1b92a1dde1c783b297016af3f572fef13b0abb16a2623d89a58e9725",
|
|
|
|
"protocol_version", "1",
|
|
|
|
"replace_allowed_ips", "true",
|
|
|
|
"allowed_ip", "1.0.0.2/32",
|
|
|
|
"endpoint", "127.0.0.1:"+port2,
|
|
|
|
)
|
|
|
|
cfgs[1] = uapiCfg(
|
|
|
|
"private_key", "98c7989b1661a0d64fd6af3502000f87716b7c4bbcf00d04fc6073aa7b539768",
|
|
|
|
"listen_port", port2,
|
|
|
|
"replace_peers", "true",
|
|
|
|
"public_key", "49e80929259cebdda4f322d6d2b1a6fad819d603acd26fd5d845e7a123036427",
|
|
|
|
"protocol_version", "1",
|
|
|
|
"replace_allowed_ips", "true",
|
|
|
|
"allowed_ip", "1.0.0.1/32",
|
|
|
|
"endpoint", "127.0.0.1:"+port1,
|
|
|
|
)
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-15 00:07:23 +01:00
|
|
|
// A testPair is a pair of testPeers.
|
|
|
|
type testPair [2]testPeer
|
|
|
|
|
|
|
|
// A testPeer is a peer used for testing.
|
|
|
|
type testPeer struct {
|
|
|
|
tun *tuntest.ChannelTUN
|
|
|
|
dev *Device
|
|
|
|
ip net.IP
|
|
|
|
}
|
|
|
|
|
|
|
|
type SendDirection bool
|
|
|
|
|
|
|
|
const (
|
|
|
|
Ping SendDirection = true
|
|
|
|
Pong SendDirection = false
|
|
|
|
)
|
|
|
|
|
|
|
|
func (pair *testPair) Send(t *testing.T, ping SendDirection, done chan struct{}) {
|
|
|
|
t.Helper()
|
|
|
|
p0, p1 := pair[0], pair[1]
|
|
|
|
if !ping {
|
|
|
|
// pong is the new ping
|
|
|
|
p0, p1 = p1, p0
|
|
|
|
}
|
|
|
|
msg := tuntest.Ping(p0.ip, p1.ip)
|
|
|
|
p1.tun.Outbound <- msg
|
|
|
|
timer := time.NewTimer(5 * time.Second)
|
|
|
|
defer timer.Stop()
|
|
|
|
var err error
|
|
|
|
select {
|
|
|
|
case msgRecv := <-p0.tun.Inbound:
|
|
|
|
if !bytes.Equal(msg, msgRecv) {
|
|
|
|
err = errors.New("ping did not transit correctly")
|
|
|
|
}
|
|
|
|
case <-timer.C:
|
|
|
|
err = errors.New("ping did not transit")
|
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// The error may have occurred because the test is done.
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
// Real error.
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// genTestPair creates a testPair.
|
|
|
|
func genTestPair(t *testing.T) (pair testPair) {
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
const maxAttempts = 10
|
|
|
|
NextAttempt:
|
|
|
|
for i := 0; i < maxAttempts; i++ {
|
|
|
|
cfg := genConfigs(t)
|
|
|
|
// Bring up a ChannelTun for each config.
|
2020-12-15 00:07:23 +01:00
|
|
|
for i := range pair {
|
|
|
|
p := &pair[i]
|
|
|
|
p.tun = tuntest.NewChannelTUN()
|
|
|
|
if i == 0 {
|
|
|
|
p.ip = net.ParseIP("1.0.0.1")
|
|
|
|
} else {
|
|
|
|
p.ip = net.ParseIP("1.0.0.2")
|
|
|
|
}
|
|
|
|
p.dev = NewDevice(p.tun.TUN(), NewLogger(LogLevelDebug, fmt.Sprintf("dev%d: ", i)))
|
|
|
|
p.dev.Up()
|
|
|
|
if err := p.dev.IpcSetOperation(cfg[i]); err != nil {
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
// genConfigs attempted to pick ports that were free.
|
|
|
|
// There's a tiny window between genConfigs closing the port
|
|
|
|
// and us opening it, during which another process could
|
|
|
|
// start using it. We probably just lost that race.
|
|
|
|
// Try again from the beginning.
|
|
|
|
// If there's something permanent wrong,
|
|
|
|
// we'll see that when we run out of attempts.
|
|
|
|
t.Logf("failed to configure device %d: %v", i, err)
|
|
|
|
continue NextAttempt
|
|
|
|
}
|
|
|
|
// The device might still not be up, e.g. due to an error
|
|
|
|
// in RoutineTUNEventReader's call to dev.Up that got swallowed.
|
|
|
|
// Assume it's due to a transient error (port in use), and retry.
|
2020-12-15 00:07:23 +01:00
|
|
|
if !p.dev.isUp.Get() {
|
|
|
|
t.Logf("device %d did not come up, trying again", i)
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
continue NextAttempt
|
|
|
|
}
|
|
|
|
// The device is up. Close it when the test completes.
|
2020-12-15 00:07:23 +01:00
|
|
|
t.Cleanup(p.dev.Close)
|
device: make test setup more robust
Picking two free ports to use for a test is difficult.
The free port we selected might no longer be free when we reach
for it a second time.
On my machine, this failure mode led to failures approximately
once per thousand test runs.
Since failures are rare, and threading through and checking for
all possible errors is complicated, fix this with a big hammer:
Retry if either device fails to come up.
Also, if you accidentally pick the same port twice, delightful confusion ensues.
The handshake failures manifest as crypto errors, which look scary.
Again, fix with retries.
To make these retries easier to implement, use testing.T.Cleanup
instead of defer to close devices. This requires Go 1.14.
Update go.mod accordingly. Go 1.13 is no longer supported anyway.
With these fixes, 'go test -race' ran 100,000 times without failure.
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2020-12-09 04:23:56 +01:00
|
|
|
}
|
|
|
|
return // success
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Fatalf("genChannelTUNs: failed %d times", maxAttempts)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTwoDevicePing(t *testing.T) {
|
2020-12-15 00:07:23 +01:00
|
|
|
pair := genTestPair(t)
|
2019-10-12 18:44:05 +02:00
|
|
|
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
2020-12-15 00:07:23 +01:00
|
|
|
pair.Send(t, Ping, nil)
|
2019-10-12 18:44:05 +02:00
|
|
|
})
|
|
|
|
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
2020-12-15 00:07:23 +01:00
|
|
|
pair.Send(t, Pong, nil)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestConcurrencySafety does other things concurrently with tunnel use.
|
|
|
|
// It is intended to be used with the race detector to catch data races.
|
|
|
|
func TestConcurrencySafety(t *testing.T) {
|
|
|
|
pair := genTestPair(t)
|
|
|
|
done := make(chan struct{})
|
|
|
|
|
|
|
|
const warmupIters = 10
|
|
|
|
var warmup sync.WaitGroup
|
|
|
|
warmup.Add(warmupIters)
|
|
|
|
go func() {
|
|
|
|
// Send data continuously back and forth until we're done.
|
|
|
|
// Note that we may continue to attempt to send data
|
|
|
|
// even after done is closed.
|
|
|
|
i := warmupIters
|
|
|
|
for ping := Ping; ; ping = !ping {
|
|
|
|
pair.Send(t, ping, done)
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
if i > 0 {
|
|
|
|
warmup.Done()
|
|
|
|
i--
|
2019-10-12 18:44:05 +02:00
|
|
|
}
|
|
|
|
}
|
2020-12-15 00:07:23 +01:00
|
|
|
}()
|
|
|
|
warmup.Wait()
|
|
|
|
|
|
|
|
// coming soon: more things here...
|
|
|
|
|
|
|
|
close(done)
|
2019-10-12 18:44:05 +02:00
|
|
|
}
|
2018-03-08 16:44:27 +01:00
|
|
|
|
2019-05-29 18:18:20 +02:00
|
|
|
func assertNil(t *testing.T, err error) {
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 22:44:06 +02:00
|
|
|
func assertEqual(t *testing.T, a, b []byte) {
|
|
|
|
if !bytes.Equal(a, b) {
|
2019-05-29 18:18:20 +02:00
|
|
|
t.Fatal(a, "!=", b)
|
|
|
|
}
|
|
|
|
}
|
2019-10-12 18:44:05 +02:00
|
|
|
|
|
|
|
func randDevice(t *testing.T) *Device {
|
|
|
|
sk, err := newPrivateKey()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
tun := newDummyTUN("dummy")
|
|
|
|
logger := NewLogger(LogLevelError, "")
|
|
|
|
device := NewDevice(tun, logger)
|
|
|
|
device.SetPrivateKey(sk)
|
|
|
|
return device
|
|
|
|
}
|