diff options
Diffstat (limited to 'wgengine')
| -rw-r--r-- | wgengine/magicsock/magicsock.go | 30 | ||||
| -rw-r--r-- | wgengine/netstack/netstack.go | 137 | ||||
| -rw-r--r-- | wgengine/userspace.go | 30 |
3 files changed, 190 insertions, 7 deletions
diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 77926c95f..ff4ab5bf9 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -3817,3 +3817,33 @@ type ippCacheKey struct { // derpStr replaces DERP IPs in s with "derp-". func derpStr(s string) string { return strings.ReplaceAll(s, "127.3.3.40:", "derp-") } + +// XXXX temporary hack for demo +func (c *Conn) WhoIs(ip netaddr.IP) string { + c.mu.Lock() + defer c.mu.Unlock() + if c.netMap == nil || !ip.Is4() { + return "" + } + nodeHasIP := func(n *tailcfg.Node) bool { + for _, a := range n.Addresses { + if a.Mask == 32 && a.IP.Addr == ip.As16() { + return true + } + } + return false + } + for _, p := range c.netMap.Peers { + if nodeHasIP(p) { + userProfile, ok := c.netMap.UserProfiles[p.User] + if ok { + if name := userProfile.DisplayName; name != "" { + return name + " from " + p.Hostinfo.Hostname + } + return userProfile.LoginName + " from " + p.Hostinfo.Hostname + } + return fmt.Sprintf("%s from %s", ip, p.Hostinfo.Hostname) + } + } + return fmt.Sprintf("mysterious person %v", ip) +} diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go new file mode 100644 index 000000000..a2e45bee5 --- /dev/null +++ b/wgengine/netstack/netstack.go @@ -0,0 +1,137 @@ +// 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 netstack wires up gVisor's netstack into Tailscale. +package netstack + +import ( + "context" + "fmt" + "log" + "net" + "strings" + + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/channel" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" + "gvisor.dev/gvisor/pkg/waiter" + "inet.af/netaddr" + "tailscale.com/types/logger" + "tailscale.com/wgengine" + "tailscale.com/wgengine/filter" + "tailscale.com/wgengine/magicsock" + "tailscale.com/wgengine/packet" + "tailscale.com/wgengine/tstun" +) + +func Impl(logf logger.Logf, tundev *tstun.TUN, e wgengine.Engine, mc *magicsock.Conn) error { + ipstack := stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()}, + TransportProtocols: []stack.TransportProtocol{tcp.NewProtocol(), udp.NewProtocol(), icmp.NewProtocol4()}, + }) + + const mtu = 1500 + linkEP := channel.New(512, mtu, "") + + const nicID = 1 + if err := ipstack.CreateNIC(nicID, linkEP); err != nil { + log.Fatal(err) + } + + ipstack.AddAddress(nicID, ipv4.ProtocolNumber, tcpip.Address(net.ParseIP("100.96.188.101").To4())) + + // Add 0.0.0.0/0 default route. + subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4))) + ipstack.SetRouteTable([]tcpip.Route{ + { + Destination: subnet, + NIC: nicID, + }, + }) + + // use Forwarder to accept any connection from stack + fwd := tcp.NewForwarder(ipstack, 0, 16, func(r *tcp.ForwarderRequest) { + logf("XXX ForwarderRequest: %v", r) + var wq waiter.Queue + ep, err := r.CreateEndpoint(&wq) + if err != nil { + r.Complete(true) + return + } + r.Complete(false) + c := gonet.NewTCPConn(&wq, ep) + // TCP echo + go echo(c, e, mc) + + }) + ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, fwd.HandlePacket) + + go func() { + for { + packetInfo, ok := linkEP.ReadContext(context.Background()) + if !ok { + logf("XXX ReadContext-for-write = ok=false") + continue + } + pkt := packetInfo.Pkt + hdrNetwork := pkt.NetworkHeader() + hdrTransport := pkt.TransportHeader() + + full := make([]byte, 0, pkt.Size()) + full = append(full, hdrNetwork.View()...) + full = append(full, hdrTransport.View()...) + full = append(full, pkt.Data.ToView()...) + + logf("XXX packet Write out: % x", full) + if err := tundev.InjectOutbound(full); err != nil { + log.Printf("netstack inject outbound: %v", err) + return + } + + } + }() + + tundev.PostFilterIn = func(p *packet.ParsedPacket, t *tstun.TUN) filter.Response { + var pn tcpip.NetworkProtocolNumber + switch p.IPVersion { + case 4: + pn = header.IPv4ProtocolNumber + case 6: + pn = header.IPv6ProtocolNumber + } + logf("XXX packet in (from %v): % x", p.SrcIP, p.Buffer()) + vv := buffer.View(append([]byte(nil), p.Buffer()...)).ToVectorisedView() + packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: vv, + }) + linkEP.InjectInbound(pn, packetBuf) + return filter.Accept + } + return nil +} + +func echo(c *gonet.TCPConn, e wgengine.Engine, mc *magicsock.Conn) { + defer c.Close() + src, _ := netaddr.FromStdIP(c.RemoteAddr().(*net.TCPAddr).IP) + fmt.Fprintf(c, "Hello, %s! Thanks for connecting to me on port %v (Try other ports too!)\nEchoing...\n", + mc.WhoIs(src), + c.LocalAddr().(*net.TCPAddr).Port) + buf := make([]byte, 1500) + for { + n, err := c.Read(buf) + if err != nil { + log.Printf("Err: %v", err) + break + } + c.Write(buf[:n]) + } + log.Print("Connection closed") +} diff --git a/wgengine/userspace.go b/wgengine/userspace.go index c3276631d..a5fe656b4 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -137,6 +137,13 @@ type EngineConfig struct { // Fake determines whether this engine is running in fake mode, // which disables such features as DNS configuration and unrestricted ICMP Echo responses. Fake bool + + // FakeImpl, if non-nil, specifies which type of fake implementation to + // use. Two values are typical: nil, for a basic ping-only fake + // implementation, and netstack.Impl, which brings in gvisor's netstack + // to the binary. The desire to keep that out of some binaries is why + // this func exists, so wgengine need not depend on gvisor. + FakeImpl FakeImplFunc } type Loggify struct { @@ -148,7 +155,9 @@ func (l *Loggify) Write(b []byte) (int, error) { return len(b), nil } -func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) { +type FakeImplFunc func(logger.Logf, *tstun.TUN, Engine, *magicsock.Conn) error + +func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16, impl FakeImplFunc) (Engine, error) { logf("Starting userspace wireguard engine (FAKE tuntap device).") conf := EngineConfig{ Logf: logf, @@ -156,6 +165,7 @@ func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) RouterGen: router.NewFake, ListenPort: listenPort, Fake: true, + FakeImpl: impl, } return NewUserspaceEngineAdvanced(conf) } @@ -217,12 +227,6 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) { e.linkState, _ = getLinkState() logf("link state: %+v", e.linkState) - // Respond to all pings only in fake mode. - if conf.Fake { - e.tundev.PostFilterIn = echoRespondToAll - } - e.tundev.PreFilterOut = e.handleLocalPackets - mon, err := monitor.New(logf, func() { e.LinkChange(false) }) if err != nil { e.tundev.Close() @@ -251,6 +255,18 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) { return nil, fmt.Errorf("wgengine: %v", err) } + if conf.Fake { + if impl := conf.FakeImpl; impl != nil { + if err := impl(logf, e.tundev, e, e.magicConn); err != nil { + return nil, err + } + } else { + // Respond to all pings only in fake mode. + e.tundev.PostFilterIn = echoRespondToAll + } + } + e.tundev.PreFilterOut = e.handleLocalPackets + // flags==0 because logf is already nested in another logger. // The outer one can display the preferred log prefixes, etc. dlog := log.New(&Loggify{logf}, "", 0) |
