mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
wgengine/magicsock, tstest/natlab: start hooking up natlab to magicsock
Also adds ephemeral port support to natlab. Work in progress. Pairing with @danderson.
This commit is contained in:
parent
edcbb5394e
commit
6c74065053
4
go.sum
4
go.sum
@ -84,10 +84,6 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
|||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f h1:uFj5bslHsMzxIM8UTjAhq4VXeo6GfNW91rpoh/WMJaY=
|
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f h1:uFj5bslHsMzxIM8UTjAhq4VXeo6GfNW91rpoh/WMJaY=
|
||||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f/go.mod h1:x880GWw5fvrl2DVTQ04ttXQD4DuppTt1Yz6wLibbjNE=
|
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f/go.mod h1:x880GWw5fvrl2DVTQ04ttXQD4DuppTt1Yz6wLibbjNE=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200615180905-687c10194779 h1:zg0rgvhBZGA4nvh17nDKcqkEXw6Nbc/Ma2VBvLaW7LU=
|
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200615180905-687c10194779/go.mod h1:JPm5cTfu1K+qDFRbiHy0sOlHUylYQbpl356sdYFD8V4=
|
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200624060658-de1f1af1f35f h1:hmhdY4xqtJD2rdaKpoNeWf0xLFFAc8dVZXyKMXRWbEM=
|
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200624060658-de1f1af1f35f/go.mod h1:JPm5cTfu1K+qDFRbiHy0sOlHUylYQbpl356sdYFD8V4=
|
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200710044538-9320f191f6b1 h1:zMEeWu/X0l+xFnsbri69miflb3HIKoLwedZHD5xx6Mk=
|
github.com/tailscale/wireguard-go v0.0.0-20200710044538-9320f191f6b1 h1:zMEeWu/X0l+xFnsbri69miflb3HIKoLwedZHD5xx6Mk=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20200710044538-9320f191f6b1/go.mod h1:JPm5cTfu1K+qDFRbiHy0sOlHUylYQbpl356sdYFD8V4=
|
github.com/tailscale/wireguard-go v0.0.0-20200710044538-9320f191f6b1/go.mod h1:JPm5cTfu1K+qDFRbiHy0sOlHUylYQbpl356sdYFD8V4=
|
||||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||||
|
@ -1146,6 +1146,20 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
|
|||||||
if port < 0 || port > 1<<16-1 {
|
if port < 0 || port > 1<<16-1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if n.STUNTestIP != "" {
|
||||||
|
ip, err := netaddr.ParseIP(n.STUNTestIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if proto == probeIPv4 && ip.Is6() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if proto == probeIPv6 && ip.Is4() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return netaddr.IPPort{ip, uint16(port)}.UDPAddr()
|
||||||
|
}
|
||||||
|
|
||||||
switch proto {
|
switch proto {
|
||||||
case probeIPv4:
|
case probeIPv4:
|
||||||
if n.IPv4 != "" {
|
if n.IPv4 != "" {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package stuntest
|
package stuntest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -16,6 +17,7 @@
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/net/stun"
|
"tailscale.com/net/stun"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/types/nettype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stunStats struct {
|
type stunStats struct {
|
||||||
@ -25,18 +27,22 @@ type stunStats struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Serve(t *testing.T) (addr *net.UDPAddr, cleanupFn func()) {
|
func Serve(t *testing.T) (addr *net.UDPAddr, cleanupFn func()) {
|
||||||
|
return ServeWithPacketListener(t, nettype.Std{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeWithPacketListener(t *testing.T, ln nettype.PacketListener) (addr *net.UDPAddr, cleanupFn func()) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
// TODO(crawshaw): use stats to test re-STUN logic
|
// TODO(crawshaw): use stats to test re-STUN logic
|
||||||
var stats stunStats
|
var stats stunStats
|
||||||
|
|
||||||
pc, err := net.ListenPacket("udp4", ":0")
|
pc, err := ln.ListenPacket(context.Background(), "udp4", ":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to open STUN listener: %v", err)
|
t.Fatalf("failed to open STUN listener: %v", err)
|
||||||
}
|
}
|
||||||
addr = &net.UDPAddr{
|
addr = pc.LocalAddr().(*net.UDPAddr)
|
||||||
IP: net.ParseIP("127.0.0.1"),
|
if len(addr.IP) == 0 || addr.IP.IsUnspecified() {
|
||||||
Port: pc.LocalAddr().(*net.UDPAddr).Port,
|
addr.IP = net.ParseIP("127.0.0.1")
|
||||||
}
|
}
|
||||||
doneCh := make(chan struct{})
|
doneCh := make(chan struct{})
|
||||||
go runSTUN(t, pc, &stats, doneCh)
|
go runSTUN(t, pc, &stats, doneCh)
|
||||||
|
@ -117,4 +117,8 @@ type DERPNode struct {
|
|||||||
// of using the default port of 443. If non-zero, TLS
|
// of using the default port of 443. If non-zero, TLS
|
||||||
// verification is skipped.
|
// verification is skipped.
|
||||||
DERPTestPort int `json:",omitempty"`
|
DERPTestPort int `json:",omitempty"`
|
||||||
|
|
||||||
|
// STUNTestIP is used in tests to override the STUN server's IP.
|
||||||
|
// If empty, it's assumed to be the same as the DERP server.
|
||||||
|
STUNTestIP string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
@ -26,10 +28,10 @@
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var traceOn = os.Getenv("NATLAB_TRACE")
|
var traceOn, _ = strconv.ParseBool(os.Getenv("NATLAB_TRACE"))
|
||||||
|
|
||||||
func trace(p []byte, msg string, args ...interface{}) {
|
func trace(p []byte, msg string, args ...interface{}) {
|
||||||
if traceOn == "" {
|
if !traceOn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := packetShort(p)
|
id := packetShort(p)
|
||||||
@ -424,6 +426,32 @@ func (m *Machine) hasv6() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Machine) pickEphemPort() (port uint16, err error) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
for tries := 0; tries < 500; tries++ {
|
||||||
|
port := uint16(rand.Intn(32<<10) + 32<<10)
|
||||||
|
if !m.portInUseLocked(port) {
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New("failed to find an ephemeral port")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Machine) portInUseLocked(port uint16) bool {
|
||||||
|
for ipp := range m.conns4 {
|
||||||
|
if ipp.Port == port {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ipp := range m.conns6 {
|
||||||
|
if ipp.Port == port {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Machine) registerConn4(c *conn) error {
|
func (m *Machine) registerConn4(c *conn) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
@ -467,7 +495,7 @@ func registerConn(conns *map[netaddr.IPPort]*conn, c *conn) error {
|
|||||||
|
|
||||||
func (m *Machine) AddNetwork(n *Network) {}
|
func (m *Machine) AddNetwork(n *Network) {}
|
||||||
|
|
||||||
func (m *Machine) ListenPacket(network, address string) (net.PacketConn, error) {
|
func (m *Machine) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||||
// if udp4, udp6, etc... look at address IP vs unspec
|
// if udp4, udp6, etc... look at address IP vs unspec
|
||||||
var (
|
var (
|
||||||
fam uint8
|
fam uint8
|
||||||
@ -497,11 +525,18 @@ func (m *Machine) ListenPacket(network, address string) (net.PacketConn, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
port, err := strconv.ParseUint(portStr, 10, 16)
|
porti, err := strconv.ParseUint(portStr, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ipp := netaddr.IPPort{IP: ip, Port: uint16(port)}
|
port := uint16(porti)
|
||||||
|
if port == 0 {
|
||||||
|
port, err = m.pickEphemPort()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipp := netaddr.IPPort{IP: ip, Port: port}
|
||||||
|
|
||||||
c := &conn{
|
c := &conn{
|
||||||
m: m,
|
m: m,
|
||||||
@ -552,11 +587,17 @@ type activeRead struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// readDeadlineExceeded reports whether the read deadline is set and has already passed.
|
// canRead reports whether we can do a read.
|
||||||
func (c *conn) readDeadlineExceeded() bool {
|
func (c *conn) canRead() error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
return !c.readDeadline.IsZero() && c.readDeadline.Before(time.Now())
|
if c.closed {
|
||||||
|
return errors.New("closed network connection") // sadface: magic string used by other; don't change
|
||||||
|
}
|
||||||
|
if !c.readDeadline.IsZero() && c.readDeadline.Before(time.Now()) {
|
||||||
|
return errors.New("read deadline exceeded")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) registerActiveRead(ar *activeRead, active bool) {
|
func (c *conn) registerActiveRead(ar *activeRead, active bool) {
|
||||||
@ -609,8 +650,8 @@ func (c *conn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|||||||
|
|
||||||
ar := &activeRead{cancel: cancel}
|
ar := &activeRead{cancel: cancel}
|
||||||
|
|
||||||
if c.readDeadlineExceeded() {
|
if err := c.canRead(); err != nil {
|
||||||
return 0, nil, context.DeadlineExceeded
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.registerActiveRead(ar, true)
|
c.registerActiveRead(ar, true)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package natlab
|
package natlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -49,11 +50,12 @@ func TestSendPacket(t *testing.T) {
|
|||||||
fooAddr := netaddr.IPPort{IP: ifFoo.V4(), Port: 123}
|
fooAddr := netaddr.IPPort{IP: ifFoo.V4(), Port: 123}
|
||||||
barAddr := netaddr.IPPort{IP: ifBar.V4(), Port: 456}
|
barAddr := netaddr.IPPort{IP: ifBar.V4(), Port: 456}
|
||||||
|
|
||||||
fooPC, err := foo.ListenPacket("udp4", fooAddr.String())
|
ctx := context.Background()
|
||||||
|
fooPC, err := foo.ListenPacket(ctx, "udp4", fooAddr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
barPC, err := bar.ListenPacket("udp4", barAddr.String())
|
barPC, err := bar.ListenPacket(ctx, "udp4", barAddr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -93,15 +95,16 @@ func TestMultiNetwork(t *testing.T) {
|
|||||||
ifNATLAN := nat.Attach("ethlan", lan)
|
ifNATLAN := nat.Attach("ethlan", lan)
|
||||||
ifServer := server.Attach("eth0", internet)
|
ifServer := server.Attach("eth0", internet)
|
||||||
|
|
||||||
clientPC, err := client.ListenPacket("udp", ":123")
|
ctx := context.Background()
|
||||||
|
clientPC, err := client.ListenPacket(ctx, "udp", ":123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
natPC, err := nat.ListenPacket("udp", ":456")
|
natPC, err := nat.ListenPacket(ctx, "udp", ":456")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
serverPC, err := server.ListenPacket("udp", ":789")
|
serverPC, err := server.ListenPacket(ctx, "udp", ":789")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -184,11 +187,12 @@ func TestPacketHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clientPC, err := client.ListenPacket("udp4", ":123")
|
ctx := context.Background()
|
||||||
|
clientPC, err := client.ListenPacket(ctx, "udp4", ":123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
serverPC, err := server.ListenPacket("udp4", ":456")
|
serverPC, err := server.ListenPacket(ctx, "udp4", ":456")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
25
types/nettype/nettype.go
Normal file
25
types/nettype/nettype.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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 nettype defines an interface that doesn't exist in the Go net package.
|
||||||
|
package nettype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PacketListener defines the ListenPacket method as implemented
|
||||||
|
// by net.ListenConfig, net.ListenPacket, and tstest/natlab.
|
||||||
|
type PacketListener interface {
|
||||||
|
ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Std implements PacketListener using the Go net package's ListenPacket func.
|
||||||
|
type Std struct{}
|
||||||
|
|
||||||
|
func (Std) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {
|
||||||
|
var conf net.ListenConfig
|
||||||
|
return conf.ListenPacket(ctx, network, address)
|
||||||
|
}
|
@ -49,6 +49,7 @@
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
|
"tailscale.com/types/nettype"
|
||||||
"tailscale.com/types/opt"
|
"tailscale.com/types/opt"
|
||||||
"tailscale.com/types/structs"
|
"tailscale.com/types/structs"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
@ -82,6 +83,9 @@ type Conn struct {
|
|||||||
udpRecvCh chan udpReadResult
|
udpRecvCh chan udpReadResult
|
||||||
derpRecvCh chan derpReadResult
|
derpRecvCh chan derpReadResult
|
||||||
|
|
||||||
|
// packetListener optionally specifies a test hook to open a PacketConn.
|
||||||
|
packetListener nettype.PacketListener
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
mu sync.Mutex // guards all following fields
|
mu sync.Mutex // guards all following fields
|
||||||
|
|
||||||
@ -227,6 +231,10 @@ type Options struct {
|
|||||||
// IdleFunc optionally provides a func to return how long
|
// IdleFunc optionally provides a func to return how long
|
||||||
// it's been since a TUN packet was sent or received.
|
// it's been since a TUN packet was sent or received.
|
||||||
IdleFunc func() time.Duration
|
IdleFunc func() time.Duration
|
||||||
|
|
||||||
|
// PacketListener optionally specifies how to create PacketConns.
|
||||||
|
// It's meant for testing.
|
||||||
|
PacketListener nettype.PacketListener
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) logf() logger.Logf {
|
func (o *Options) logf() logger.Logf {
|
||||||
@ -273,6 +281,7 @@ func NewConn(opts Options) (*Conn, error) {
|
|||||||
c.logf = opts.logf()
|
c.logf = opts.logf()
|
||||||
c.epFunc = opts.endpointsFunc()
|
c.epFunc = opts.endpointsFunc()
|
||||||
c.idleFunc = opts.IdleFunc
|
c.idleFunc = opts.IdleFunc
|
||||||
|
c.packetListener = opts.PacketListener
|
||||||
|
|
||||||
if err := c.initialBind(); err != nil {
|
if err := c.initialBind(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -2002,6 +2011,13 @@ func (c *Conn) initialBind() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) listenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) {
|
||||||
|
if c.packetListener != nil {
|
||||||
|
return c.packetListener.ListenPacket(ctx, network, addr)
|
||||||
|
}
|
||||||
|
return netns.Listener().ListenPacket(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
||||||
host := ""
|
host := ""
|
||||||
if v, _ := strconv.ParseBool(os.Getenv("IN_TS_TEST")); v {
|
if v, _ := strconv.ParseBool(os.Getenv("IN_TS_TEST")); v {
|
||||||
@ -2011,13 +2027,13 @@ func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
|||||||
var err error
|
var err error
|
||||||
listenCtx := context.Background() // unused without DNS name to resolve
|
listenCtx := context.Background() // unused without DNS name to resolve
|
||||||
if c.pconnPort == 0 && DefaultPort != 0 {
|
if c.pconnPort == 0 && DefaultPort != 0 {
|
||||||
pc, err = netns.Listener().ListenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, DefaultPort))
|
pc, err = c.listenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, DefaultPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
|
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pc == nil {
|
if pc == nil {
|
||||||
pc, err = netns.Listener().ListenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, c.pconnPort))
|
pc, err = c.listenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, c.pconnPort))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock: bind(%s/%v): %v", which, c.pconnPort, err)
|
c.logf("magicsock: bind(%s/%v): %v", which, c.pconnPort, err)
|
||||||
@ -2026,7 +2042,7 @@ func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
|
|||||||
if *ruc == nil {
|
if *ruc == nil {
|
||||||
*ruc = new(RebindingUDPConn)
|
*ruc = new(RebindingUDPConn)
|
||||||
}
|
}
|
||||||
(*ruc).Reset(pc.(*net.UDPConn))
|
(*ruc).Reset(pc)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2043,7 +2059,7 @@ func (c *Conn) Rebind() {
|
|||||||
if err := c.pconn4.pconn.Close(); err != nil {
|
if err := c.pconn4.pconn.Close(); err != nil {
|
||||||
c.logf("magicsock: link change close failed: %v", err)
|
c.logf("magicsock: link change close failed: %v", err)
|
||||||
}
|
}
|
||||||
packetConn, err := netns.Listener().ListenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.pconnPort))
|
packetConn, err := c.listenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.pconnPort))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.logf("magicsock: link change rebound port: %d", c.pconnPort)
|
c.logf("magicsock: link change rebound port: %d", c.pconnPort)
|
||||||
c.pconn4.pconn = packetConn.(*net.UDPConn)
|
c.pconn4.pconn = packetConn.(*net.UDPConn)
|
||||||
@ -2054,7 +2070,7 @@ func (c *Conn) Rebind() {
|
|||||||
c.pconn4.mu.Unlock()
|
c.pconn4.mu.Unlock()
|
||||||
}
|
}
|
||||||
c.logf("magicsock: link change, binding new port")
|
c.logf("magicsock: link change, binding new port")
|
||||||
packetConn, err := netns.Listener().ListenPacket(listenCtx, "udp4", host+":0")
|
packetConn, err := c.listenPacket(listenCtx, "udp4", host+":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("magicsock: link change failed to bind new port: %v", err)
|
c.logf("magicsock: link change failed to bind new port: %v", err)
|
||||||
return
|
return
|
||||||
@ -2481,10 +2497,10 @@ type RebindingUDPConn struct {
|
|||||||
ippCache ippCache
|
ippCache ippCache
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pconn *net.UDPConn
|
pconn net.PacketConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RebindingUDPConn) Reset(pconn *net.UDPConn) {
|
func (c *RebindingUDPConn) Reset(pconn net.PacketConn) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
old := c.pconn
|
old := c.pconn
|
||||||
c.pconn = pconn
|
c.pconn = pconn
|
||||||
@ -2539,7 +2555,7 @@ func (c *RebindingUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
|
|||||||
pconn := c.pconn
|
pconn := c.pconn
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
n, err := pconn.WriteToUDP(b, addr)
|
n, err := pconn.WriteTo(b, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
pconn2 := c.pconn
|
pconn2 := c.pconn
|
||||||
|
@ -32,8 +32,10 @@
|
|||||||
"tailscale.com/net/stun/stuntest"
|
"tailscale.com/net/stun/stuntest"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
|
"tailscale.com/tstest/natlab"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
|
"tailscale.com/types/nettype"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
"tailscale.com/wgengine/tstun"
|
"tailscale.com/wgengine/tstun"
|
||||||
)
|
)
|
||||||
@ -334,6 +336,16 @@ func makeNestable(t *testing.T) (logf logger.Logf, setT func(t *testing.T)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTwoDevicePing(t *testing.T) {
|
func TestTwoDevicePing(t *testing.T) {
|
||||||
|
t.Run("real", func(t *testing.T) {
|
||||||
|
testTwoDevicePing(t, false)
|
||||||
|
})
|
||||||
|
t.Run("natlab", func(t *testing.T) {
|
||||||
|
t.Skip("TODO: finish")
|
||||||
|
testTwoDevicePing(t, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTwoDevicePing(t *testing.T, useNatlab bool) {
|
||||||
tstest.PanicOnLog()
|
tstest.PanicOnLog()
|
||||||
rc := tstest.NewResourceCheck()
|
rc := tstest.NewResourceCheck()
|
||||||
defer rc.Assert(t)
|
defer rc.Assert(t)
|
||||||
@ -344,7 +356,28 @@ func TestTwoDevicePing(t *testing.T) {
|
|||||||
|
|
||||||
derpServer, derpAddr, derpCleanupFn := runDERP(t, logf)
|
derpServer, derpAddr, derpCleanupFn := runDERP(t, logf)
|
||||||
defer derpCleanupFn()
|
defer derpCleanupFn()
|
||||||
stunAddr, stunCleanupFn := stuntest.Serve(t)
|
|
||||||
|
packetConn := func(m *natlab.Machine) nettype.PacketListener {
|
||||||
|
if m == nil {
|
||||||
|
return nettype.Std{}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
var stunTestIP = "127.0.0.1"
|
||||||
|
var stunMachine, machine1, machine2 *natlab.Machine
|
||||||
|
if useNatlab {
|
||||||
|
stunMachine = &natlab.Machine{Name: "stun"}
|
||||||
|
machine1 = &natlab.Machine{Name: "machine1"}
|
||||||
|
machine2 = &natlab.Machine{Name: "machine2"}
|
||||||
|
internet := natlab.NewInternet()
|
||||||
|
stunIf := stunMachine.Attach("eth0", internet)
|
||||||
|
machine1.Attach("eth0", internet)
|
||||||
|
machine2.Attach("eth0", internet)
|
||||||
|
stunTestIP = stunIf.V4().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
stunAddr, stunCleanupFn := stuntest.ServeWithPacketListener(t, packetConn(stunMachine))
|
||||||
defer stunCleanupFn()
|
defer stunCleanupFn()
|
||||||
|
|
||||||
derpMap := &tailcfg.DERPMap{
|
derpMap := &tailcfg.DERPMap{
|
||||||
@ -361,6 +394,7 @@ func TestTwoDevicePing(t *testing.T) {
|
|||||||
IPv6: "none",
|
IPv6: "none",
|
||||||
STUNPort: stunAddr.Port,
|
STUNPort: stunAddr.Port,
|
||||||
DERPTestPort: derpAddr.Port,
|
DERPTestPort: derpAddr.Port,
|
||||||
|
STUNTestIP: stunTestIP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -369,7 +403,8 @@ func TestTwoDevicePing(t *testing.T) {
|
|||||||
|
|
||||||
epCh1 := make(chan []string, 16)
|
epCh1 := make(chan []string, 16)
|
||||||
conn1, err := NewConn(Options{
|
conn1, err := NewConn(Options{
|
||||||
Logf: logger.WithPrefix(logf, "conn1: "),
|
Logf: logger.WithPrefix(logf, "conn1: "),
|
||||||
|
PacketListener: packetConn(machine1),
|
||||||
EndpointsFunc: func(eps []string) {
|
EndpointsFunc: func(eps []string) {
|
||||||
epCh1 <- eps
|
epCh1 <- eps
|
||||||
},
|
},
|
||||||
@ -383,7 +418,8 @@ func TestTwoDevicePing(t *testing.T) {
|
|||||||
|
|
||||||
epCh2 := make(chan []string, 16)
|
epCh2 := make(chan []string, 16)
|
||||||
conn2, err := NewConn(Options{
|
conn2, err := NewConn(Options{
|
||||||
Logf: logger.WithPrefix(logf, "conn2: "),
|
Logf: logger.WithPrefix(logf, "conn2: "),
|
||||||
|
PacketListener: packetConn(machine2),
|
||||||
EndpointsFunc: func(eps []string) {
|
EndpointsFunc: func(eps []string) {
|
||||||
epCh2 <- eps
|
epCh2 <- eps
|
||||||
},
|
},
|
||||||
@ -396,6 +432,14 @@ func TestTwoDevicePing(t *testing.T) {
|
|||||||
conn2.SetDERPMap(derpMap)
|
conn2.SetDERPMap(derpMap)
|
||||||
|
|
||||||
ports := []uint16{conn1.LocalPort(), conn2.LocalPort()}
|
ports := []uint16{conn1.LocalPort(), conn2.LocalPort()}
|
||||||
|
if useNatlab {
|
||||||
|
// TODO: ...
|
||||||
|
} else {
|
||||||
|
addrs := []netaddr.IPPort{
|
||||||
|
// netaddr.IPPort
|
||||||
|
}
|
||||||
|
_ = addrs
|
||||||
|
}
|
||||||
cfgs := makeConfigs(t, ports)
|
cfgs := makeConfigs(t, ports)
|
||||||
|
|
||||||
if err := conn1.SetPrivateKey(cfgs[0].PrivateKey); err != nil {
|
if err := conn1.SetPrivateKey(cfgs[0].PrivateKey); err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user