cmd/tailscaled, wgengine: remove --fake, replace with netstack
And add a --socks5-server flag. And fix a race in SOCKS5 replies where the response header was written concurrently with the copy from the backend. Co-authored with Naman Sood. Updates #707 Updates #504 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
@@ -33,7 +33,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/socks5"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/util/dnsname"
|
||||
@@ -55,13 +54,13 @@ type Impl struct {
|
||||
logf logger.Logf
|
||||
|
||||
mu sync.Mutex
|
||||
dns map[string]netaddr.IP // Magic DNS names (both base + FQDN) => first IP
|
||||
dns DNSMap
|
||||
}
|
||||
|
||||
const nicID = 1
|
||||
|
||||
// Create creates and populates a new Impl.
|
||||
func Create(logf logger.Logf, tundev *tstun.TUN, e wgengine.Engine, mc *magicsock.Conn) (wgengine.FakeImpl, error) {
|
||||
func Create(logf logger.Logf, tundev *tstun.TUN, e wgengine.Engine, mc *magicsock.Conn) (*Impl, error) {
|
||||
if mc == nil {
|
||||
return nil, errors.New("nil magicsock.Conn")
|
||||
}
|
||||
@@ -121,33 +120,40 @@ func (ns *Impl) Start() error {
|
||||
ns.ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, udpFwd.HandlePacket)
|
||||
go ns.injectOutbound()
|
||||
ns.tundev.PostFilterIn = ns.injectInbound
|
||||
go ns.socks5Server()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *Impl) updateDNS(nm *netmap.NetworkMap) {
|
||||
ns.mu.Lock()
|
||||
defer ns.mu.Unlock()
|
||||
ns.dns = make(map[string]netaddr.IP)
|
||||
// DNSMap maps MagicDNS names (both base + FQDN) to their first IP.
|
||||
// It should not be mutated once created.
|
||||
type DNSMap map[string]netaddr.IP
|
||||
|
||||
func DNSMapFromNetworkMap(nm *netmap.NetworkMap) DNSMap {
|
||||
ret := make(DNSMap)
|
||||
suffix := nm.MagicDNSSuffix()
|
||||
|
||||
if nm.Name != "" && len(nm.Addresses) > 0 {
|
||||
ip := nm.Addresses[0].IP
|
||||
ns.dns[strings.TrimRight(nm.Name, ".")] = ip
|
||||
ret[strings.TrimRight(nm.Name, ".")] = ip
|
||||
if dnsname.HasSuffix(nm.Name, suffix) {
|
||||
ns.dns[dnsname.TrimSuffix(nm.Name, suffix)] = ip
|
||||
ret[dnsname.TrimSuffix(nm.Name, suffix)] = ip
|
||||
}
|
||||
}
|
||||
for _, p := range nm.Peers {
|
||||
if p.Name != "" && len(p.Addresses) > 0 {
|
||||
ip := p.Addresses[0].IP
|
||||
ns.dns[strings.TrimRight(p.Name, ".")] = ip
|
||||
ret[strings.TrimRight(p.Name, ".")] = ip
|
||||
if dnsname.HasSuffix(p.Name, suffix) {
|
||||
ns.dns[dnsname.TrimSuffix(p.Name, suffix)] = ip
|
||||
ret[dnsname.TrimSuffix(p.Name, suffix)] = ip
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ns *Impl) updateDNS(nm *netmap.NetworkMap) {
|
||||
ns.mu.Lock()
|
||||
defer ns.mu.Unlock()
|
||||
ns.dns = DNSMapFromNetworkMap(nm)
|
||||
}
|
||||
|
||||
func (ns *Impl) updateIPs(nm *netmap.NetworkMap) {
|
||||
@@ -198,8 +204,9 @@ func (ns *Impl) updateIPs(nm *netmap.NetworkMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// resolve resolves addr into an IP:port.
|
||||
func (ns *Impl) resolve(ctx context.Context, addr string) (netaddr.IPPort, error) {
|
||||
// Resolve resolves addr into an IP:port using first the MagicDNS contents
|
||||
// of m, else using the system resolver.
|
||||
func (m DNSMap) Resolve(ctx context.Context, addr string) (netaddr.IPPort, error) {
|
||||
ipp, pippErr := netaddr.ParseIPPort(addr)
|
||||
if pippErr == nil {
|
||||
return ipp, nil
|
||||
@@ -222,9 +229,7 @@ func (ns *Impl) resolve(ctx context.Context, addr string) (netaddr.IPPort, error
|
||||
// Host is not an IP, so assume it's a DNS name.
|
||||
|
||||
// Try MagicDNS first, else otherwise a real DNS lookup.
|
||||
ns.mu.Lock()
|
||||
ip := ns.dns[host]
|
||||
ns.mu.Unlock()
|
||||
ip := m[host]
|
||||
if !ip.IsZero() {
|
||||
return netaddr.IPPort{IP: ip, Port: uint16(port16)}, nil
|
||||
}
|
||||
@@ -242,8 +247,12 @@ func (ns *Impl) resolve(ctx context.Context, addr string) (netaddr.IPPort, error
|
||||
return netaddr.IPPort{IP: ip, Port: uint16(port16)}, nil
|
||||
}
|
||||
|
||||
func (ns *Impl) dialContextTCP(ctx context.Context, addr string) (*gonet.TCPConn, error) {
|
||||
remoteIPPort, err := ns.resolve(ctx, addr)
|
||||
func (ns *Impl) DialContextTCP(ctx context.Context, addr string) (*gonet.TCPConn, error) {
|
||||
ns.mu.Lock()
|
||||
dnsMap := ns.dns
|
||||
ns.mu.Unlock()
|
||||
|
||||
remoteIPPort, err := dnsMap.Resolve(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -342,7 +351,7 @@ func (ns *Impl) forwardTCP(client *gonet.TCPConn, wq *waiter.Queue, address stri
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
server, err := ns.dialContextTCP(ctx, address)
|
||||
server, err := ns.DialContextTCP(ctx, address)
|
||||
if err != nil {
|
||||
ns.logf("netstack: could not connect to server %s: %s", address, err)
|
||||
return
|
||||
@@ -361,21 +370,6 @@ func (ns *Impl) forwardTCP(client *gonet.TCPConn, wq *waiter.Queue, address stri
|
||||
ns.logf("[v2] netstack: forwarder connection to %s closed", address)
|
||||
}
|
||||
|
||||
func (ns *Impl) socks5Server() {
|
||||
ln, err := net.Listen("tcp", "localhost:1080")
|
||||
if err != nil {
|
||||
ns.logf("could not start SOCKS5 listener: %v", err)
|
||||
return
|
||||
}
|
||||
srv := &socks5.Server{
|
||||
Logf: ns.logf,
|
||||
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return ns.dialContextTCP(ctx, addr)
|
||||
},
|
||||
}
|
||||
ns.logf("SOCKS5 server exited: %v", srv.Serve(ln))
|
||||
}
|
||||
|
||||
func (ns *Impl) acceptUDP(r *udp.ForwarderRequest) {
|
||||
ns.logf("[v2] UDP ForwarderRequest: %v", r)
|
||||
var wq waiter.Queue
|
||||
|
||||
+17
-37
@@ -131,6 +131,15 @@ type userspaceEngine struct {
|
||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||
}
|
||||
|
||||
// InternalsGetter is implemented by Engines that can export their internals.
|
||||
type InternalsGetter interface {
|
||||
GetInternals() (*tstun.TUN, *magicsock.Conn)
|
||||
}
|
||||
|
||||
func (e *userspaceEngine) GetInternals() (*tstun.TUN, *magicsock.Conn) {
|
||||
return e.tundev, e.magicConn
|
||||
}
|
||||
|
||||
// RouterGen is the signature for a function that creates a
|
||||
// router.Router.
|
||||
type RouterGen func(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (router.Router, error)
|
||||
@@ -157,36 +166,18 @@ type Config struct {
|
||||
// If zero, a port is automatically selected.
|
||||
ListenPort uint16
|
||||
|
||||
// Fake determines whether this engine is running in fake mode,
|
||||
// which disables such features as DNS configuration and unrestricted ICMP Echo responses.
|
||||
// Fake determines whether this engine should automatically
|
||||
// reply to ICMP pings.
|
||||
Fake bool
|
||||
|
||||
// FakeImplFactory, if non-nil, creates a FakeImpl to use as a fake engine
|
||||
// implementation. Two values are typical: nil, for a basic ping-only fake
|
||||
// implementation, and netstack.Create, which creates a userspace network
|
||||
// stack using gvisor's netstack. The desire to keep netstack out of some
|
||||
// binaries is why the FakeImpl interface exists, so wgengine need not
|
||||
// depend on gvisor.
|
||||
FakeImplFactory FakeImplFactory
|
||||
}
|
||||
|
||||
// FakeImpl is a fake or alternate version of Engine that can be started. See
|
||||
// Config.FakeImplFactory for details.
|
||||
type FakeImpl interface {
|
||||
Start() error
|
||||
}
|
||||
|
||||
// FakeImplFactory is the type of a function used to create FakeImpls.
|
||||
type FakeImplFactory func(logger.Logf, *tstun.TUN, Engine, *magicsock.Conn) (FakeImpl, error)
|
||||
|
||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16, impl FakeImplFactory) (Engine, error) {
|
||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) {
|
||||
logf("Starting userspace wireguard engine (with fake TUN device)")
|
||||
return NewUserspaceEngine(logf, Config{
|
||||
TUN: tstun.NewFakeTUN(),
|
||||
RouterGen: router.NewFake,
|
||||
ListenPort: listenPort,
|
||||
Fake: true,
|
||||
FakeImplFactory: impl,
|
||||
TUN: tstun.NewFakeTUN(),
|
||||
RouterGen: router.NewFake,
|
||||
ListenPort: listenPort,
|
||||
Fake: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -292,18 +283,7 @@ func newUserspaceEngine(logf logger.Logf, rawTUNDev tun.Device, conf Config) (_
|
||||
|
||||
// Respond to all pings only in fake mode.
|
||||
if conf.Fake {
|
||||
if f := conf.FakeImplFactory; f != nil {
|
||||
impl, err := f(logf, e.tundev, e, e.magicConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := impl.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Respond to all pings only in fake mode.
|
||||
e.tundev.PostFilterIn = echoRespondToAll
|
||||
}
|
||||
e.tundev.PostFilterIn = echoRespondToAll
|
||||
}
|
||||
e.tundev.PreFilterOut = e.handleLocalPackets
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestNoteReceiveActivity(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUserspaceEngineReconfig(t *testing.T) {
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0, nil)
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestWatchdog(t *testing.T) {
|
||||
|
||||
t.Run("default watchdog does not fire", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0, nil)
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func TestWatchdog(t *testing.T) {
|
||||
|
||||
t.Run("watchdog fires on blocked getStatus", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0, nil)
|
||||
e, err := NewFakeUserspaceEngine(t.Logf, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user