summaryrefslogtreecommitdiffhomepage
path: root/wgengine/netstack/netstack.go
diff options
context:
space:
mode:
Diffstat (limited to 'wgengine/netstack/netstack.go')
-rw-r--r--wgengine/netstack/netstack.go137
1 files changed, 137 insertions, 0 deletions
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")
+}