summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/sample/wg_tunnel
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/sample/wg_tunnel')
-rw-r--r--pkg/tcpip/sample/wg_tunnel/README11
-rw-r--r--pkg/tcpip/sample/wg_tunnel/gtun.go135
-rw-r--r--pkg/tcpip/sample/wg_tunnel/main.go1025
-rwxr-xr-xpkg/tcpip/sample/wg_tunnel/run.sh34
-rwxr-xr-xpkg/tcpip/sample/wg_tunnel/setup.sh26
5 files changed, 1231 insertions, 0 deletions
diff --git a/pkg/tcpip/sample/wg_tunnel/README b/pkg/tcpip/sample/wg_tunnel/README
new file mode 100644
index 000000000..ee3fd9f8a
--- /dev/null
+++ b/pkg/tcpip/sample/wg_tunnel/README
@@ -0,0 +1,11 @@
+/go/bin/go mod edit -replace="github.com/insomniacslk/dhcp@v0.0.0=golang.m7n.se/insomniacslk-dhcp@v0.0.0"
+
+
+sudo ip tuntap add tun1 mode tun
+sudo ip l set up tun1
+sudo ip a a 10.1.1.1/24 dev tun1
+sudo ip r a 10.1.2.0/24 dev tun1
+ping 10.1.2.1
+ping 10.1.2.2
+
+./wg_tunnel 10003
diff --git a/pkg/tcpip/sample/wg_tunnel/gtun.go b/pkg/tcpip/sample/wg_tunnel/gtun.go
new file mode 100644
index 000000000..d641fc42d
--- /dev/null
+++ b/pkg/tcpip/sample/wg_tunnel/gtun.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/buffer"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/link/channel"
+ "gvisor.dev/gvisor/pkg/tcpip/stack"
+ wgtun "golang.zx2c4.com/wireguard/tun"
+)
+
+type GoTun struct {
+ events chan wgtun.Event
+ ch *channel.Endpoint
+ stack *stack.Stack
+ ctx context.Context
+ cancel context.CancelFunc
+}
+
+func (tun *GoTun) File() *os.File {
+ fmt.Println("File")
+ return nil
+}
+
+func (tun *GoTun) Read(buff []byte, offset int) (int, error) {
+ fmt.Println("Read ", len(buff), offset)
+
+ p, ok := tun.ch.ReadContext(tun.ctx)
+ if ok == false {
+ fmt.Println("Read error")
+ return 0, nil // FIXME error
+ }
+ vv := p.Pkt.Data
+ v := vv.ToView()
+ h := p.Pkt.Header.View()
+ fmt.Println("Read packet", vv.Size(), len(v), len(h), h)
+
+ if len(buff) - offset < len(h) + len(v) {
+ fmt.Println("Short buffer")
+ return 0, nil // FIXME error
+ }
+
+ copy(buff[offset:], h)
+ copy(buff[offset+len(h):], v)
+ return len(h)+len(v), nil
+}
+
+func versionToProtocol(version int) tcpip.NetworkProtocolNumber {
+ switch version {
+ case header.IPv4Version: return header.IPv4ProtocolNumber
+ case header.IPv6Version: return header.IPv6ProtocolNumber
+ }
+
+ return 0
+}
+
+func (tun *GoTun) Write(buff []byte, offset int) (int, error) {
+ size := len(buff) - offset
+ fmt.Println("Write ", len(buff), offset, size)
+
+ if size < 1 {
+ return 0, nil // FIXME error
+ }
+
+ buffSlice := buff[offset : offset+size]
+
+ pkt := tcpip.PacketBuffer{
+ Data: buffer.NewViewFromBytes(buffSlice).ToVectorisedView(),
+ }
+ //version := buff[offset] & 0x0f
+ protocol := versionToProtocol(header.IPVersion(buffSlice))
+ netProto := tun.stack.NetworkProtocolInstance(protocol)
+ if netProto == nil {
+ fmt.Println("Write not ok")
+ return 0, nil
+ }
+ src, dst := netProto.ParseAddresses(pkt.Data.First())
+ fmt.Println("Write ", src, dst)
+ // TODO change destination address
+ tun.ch.InjectInbound(protocol, pkt) // FIXME detect protocol number
+
+ return size, nil
+}
+
+func (tun *GoTun) Flush() error {
+ // TODO: can flushing be implemented by buffering and using sendmmsg?
+ fmt.Println("Flush")
+ return nil
+}
+
+func (tun *GoTun) MTU() (int, error) {
+ fmt.Println("MTU")
+ return 1280, nil
+}
+
+func (tun *GoTun) Name() (string, error) {
+ fmt.Println("Name")
+ return "foobar", nil
+}
+
+func (tun *GoTun) Events() chan wgtun.Event {
+ fmt.Println("Events")
+ return tun.events
+}
+
+func (tun *GoTun) Close() error {
+ fmt.Println("Close")
+ // TODO
+// tun.cancel()
+ return nil
+}
+
+func CreateGoTun(s *stack.Stack, ch *channel.Endpoint) (wgtun.Device, error) {
+ size := 16
+ ctx, cancel := context.WithCancel(context.Background())
+ tun := &GoTun{
+ ch: ch,
+ events: make(chan wgtun.Event, size),
+ stack: s,
+ ctx: ctx,
+ cancel: cancel,
+ }
+
+// go func() {
+ fmt.Println("Post event")
+ tun.events <- wgtun.EventUp
+ fmt.Println("Posted event")
+// }()
+
+ return tun, nil
+}
diff --git a/pkg/tcpip/sample/wg_tunnel/main.go b/pkg/tcpip/sample/wg_tunnel/main.go
new file mode 100644
index 000000000..e11eee227
--- /dev/null
+++ b/pkg/tcpip/sample/wg_tunnel/main.go
@@ -0,0 +1,1025 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build linux
+
+// This sample creates a stack with TCP and IPv4 protocols on top of a TUN
+// device, and listens on a port. Data received by the server in the accepted
+// connections is echoed back to the clients.
+package main
+
+import (
+ "bufio"
+ "context"
+ "encoding/base64"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "math/rand"
+ "net"
+ "os"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
+ "gvisor.dev/gvisor/pkg/tcpip/link/channel"
+ "gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
+ "gvisor.dev/gvisor/pkg/tcpip/link/loopback"
+ "gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
+ "gvisor.dev/gvisor/pkg/tcpip/link/tun"
+ "gvisor.dev/gvisor/pkg/tcpip/network/arp"
+ "gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
+ "gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
+ "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"
+
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/ipc"
+// wg_tun "golang.zx2c4.com/wireguard/tun"
+
+ "gopkg.in/yaml.v3"
+
+ "github.com/insomniacslk/dhcp/dhcpv4"
+ "github.com/insomniacslk/dhcp/dhcpv6"
+ "github.com/insomniacslk/dhcp/dhcpv6/nclient6"
+ "github.com/insomniacslk/dhcp/iana"
+)
+
+type Route struct {
+ To string `yaml:"to"`
+ Via string `yaml:"via"`
+ Metric int `yaml:"metric"`
+ Mark uint32 `yaml:"mask"`
+ Markmask uint32 `yaml:"markmask"`
+}
+
+type Common struct {
+ Match struct {
+ Name string `yaml:"name"`
+ } `yaml:"match"`
+ Addresses []string `yaml:"addresses"`
+ Nameservers struct {
+ Addresses []string `yaml:"addresses"`
+ } `yaml:"nameservers"`
+ Macaddress string `yaml:"macaddress"`
+ Routes []Route `yaml:"routes"`
+ Mtu uint32 `yaml:"mtu"`
+}
+
+type Ethernet struct {
+ Common `yaml:",inline"`
+}
+
+type Tuntap struct {
+ Common `yaml:",inline"`
+ Mode string `yaml:"mode"`
+ Name string `yaml:"name"`
+}
+
+type WireguardKey []byte
+
+func (wgKey *WireguardKey) UnmarshalYAML(value *yaml.Node) error{
+ key, err := base64.StdEncoding.DecodeString(value.Value)
+ fmt.Println("UnmarshalYAML", key, err)
+ *wgKey = key
+ return err
+}
+
+type WireguardPeer struct {
+ PublicKey WireguardKey `yaml:"public_key"`
+ Endpoint string `yaml:"endpoint"`
+ AllowedIPs []string `yaml:"allowed_ips"`
+ PersistentKeepalive int `yaml:"persistent_keepalive"`
+}
+
+func (peer WireguardPeer) String() string{
+ return fmt.Sprintf("{PublicKey=%v, Endpoint=%v, AllowedIPs=%v, PersistentKeepalive=%v}", peer.PublicKey, peer.Endpoint, peer.AllowedIPs, peer.PersistentKeepalive)
+}
+
+type Wireguard struct {
+ Common `yaml:",inline"`
+ Name string `yaml:"name"`
+ ListenPort uint16 `yaml:"listen_port"`
+ PrivateKey WireguardKey `yaml:"private_key"`
+ Peers []*WireguardPeer `yaml:"peers"`
+}
+
+type Tunnel struct {
+ Common `yaml:",inline"`
+ Mode string `yaml:"mode"`
+ Local string `yaml:"local"`
+ Remote string `yaml:"remote"`
+
+ Conn *net.UDPConn
+ Sd *os.File
+}
+
+type Netplan struct {
+ Network struct {
+ Version int `yaml:"version"`
+ Renderer string `yaml:"renderer"`
+ Ethernets map[string] *Ethernet `yaml:"ethernets"`
+ Tuntaps map[string] *Tuntap `yaml:"tuntaps"`
+ Wireguards map[string] *Wireguard `yaml:"wireguards"`
+ Tunnels map[string] *Tunnel `yaml:"tunnels"`
+ } `yaml:"network"`
+}
+
+func echo(wq *waiter.Queue, ep tcpip.Endpoint) {
+ defer ep.Close()
+
+ // Create wait queue entry that notifies a channel.
+ waitEntry, notifyCh := waiter.NewChannelEntry(nil)
+
+ wq.EventRegister(&waitEntry, waiter.EventIn)
+ defer wq.EventUnregister(&waitEntry)
+
+ for {
+ v, _, err := ep.Read(nil)
+ if err != nil {
+ if err == tcpip.ErrWouldBlock {
+ <-notifyCh
+ continue
+ }
+
+ return
+ }
+
+ ep.Write(tcpip.SlicePayload(v), tcpip.WriteOptions{})
+ }
+}
+
+func addTunLink(s *stack.Stack, nic tcpip.NICID, id string, tap bool, addr tcpip.LinkAddress, tuntap *Tuntap) {
+ var err error
+
+ mtu := tuntap.Mtu
+ tunName := tuntap.Name
+
+ if mtu == 0 {
+ mtu, err = rawfile.GetMTU(tunName)
+ if err != nil {
+ log.Fatal("GetMTU", err)
+ }
+ }
+
+ var fd int
+ if tap {
+ fd, err = tun.OpenTAP(tunName)
+ } else {
+ fd, err = tun.Open(tunName)
+ }
+ if err != nil {
+ log.Fatalf("Open %s %b %s", err, tap, tunName)
+ }
+
+ linkEP, err := fdbased.New(&fdbased.Options{
+ FDs: []int{fd},
+ MTU: mtu,
+ EthernetHeader: tap,
+ Address: addr,
+ })
+ if err := s.CreateNICWithOptions(nic, linkEP, stack.NICOptions{Name: id, Disabled: true}); err != nil {
+ log.Fatal("CreateNIC", err)
+ }
+
+ if tap {
+ if err := s.AddAddress(nic, arp.ProtocolNumber, arp.ProtocolAddress); err != nil {
+ log.Fatal("AddAddress", err)
+ }
+ }
+}
+
+func CheckError(err error) {
+ if err != nil {
+ log.Fatal("Error: " , err)
+ }
+}
+
+func TestFinialize(conn *net.UDPConn) {
+ event := make(chan string)
+
+ runtime.SetFinalizer(conn, func (obj *net.UDPConn) {
+ fmt.Println("Finalize: ", obj.LocalAddr(), obj.RemoteAddr())
+ event <- "Finalize"
+ })
+
+ runtime.GC()
+
+ select {
+ case res := <-event:
+ fmt.Println(res)
+ case <-time.After(2 * time.Second):
+ fmt.Println("No finalize")
+ }
+}
+
+func addRouterLink(s *stack.Stack, nic tcpip.NICID, id string, addr tcpip.LinkAddress,
+ tun *Tunnel) {
+
+ ServerAddr,err := net.ResolveUDPAddr("udp", tun.Remote)
+ CheckError(err)
+
+ LocalAddr, err := net.ResolveUDPAddr("udp", tun.Local)
+ CheckError(err)
+
+ conn, err := net.DialUDP("udp", LocalAddr, ServerAddr)
+ CheckError(err)
+
+ tun.Conn = conn
+
+ fmt.Println("Tunnel ", conn)
+
+ sd, err := conn.File()
+ CheckError(err)
+
+ tun.Sd = sd
+
+ conn.Close()
+
+ var fd int
+ fd = int(sd.Fd())
+ //TestFinialize(&tun.Conn)
+ runtime.GC()
+ linkEP, err := fdbased.New(&fdbased.Options{
+ FDs: []int{fd},
+ MTU: tun.Mtu,
+ EthernetHeader: true,
+// EthernetHeader: false,
+ Address: addr,
+ })
+ CheckError(err)
+
+ fmt.Println("addRouterLink MTU ", tun.Mtu, tun.Conn)
+ //channelSize := 128
+ // linkEP := channel.New(channelSize, mtu, addr)
+ if err := s.CreateNICWithOptions(nic, linkEP, stack.NICOptions{Name: id, Disabled: true}); err != nil {
+ log.Fatal("CreateNIC ", id, err)
+ }
+
+ if err := s.AddAddress(nic, arp.ProtocolNumber, arp.ProtocolAddress); err != nil {
+ log.Fatal("AddAddress", err)
+ }
+
+// client(linkEP.C)
+ fmt.Println("Tunnel ", tun.Conn)
+}
+
+func addWgLink(s *stack.Stack, nic tcpip.NICID, interfaceName string, addr tcpip.LinkAddress) *device.Device {
+ loglevel := device.LogLevelDebug
+
+ chanSize := 1024
+ var chanMtu uint32 = 1420
+ ep := channel.New(chanSize, chanMtu, addr)
+
+ logger := device.NewLogger(loglevel, "(wg_tunnel) ")
+
+ //mtu := 1500
+ // tun, err := wg_tun.CreateTUN(interfaceName, mtu)
+ tun, err := CreateGoTun(s, ep)
+ if err != nil {
+ log.Fatal("CreateGoTun", err)
+ }
+
+
+ fileUAPI, err := func() (*os.File, error) {
+ ENV_WG_UAPI_FD := "WG_UAPI_FD"
+ uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
+ if uapiFdStr == "" {
+ return ipc.UAPIOpen(interfaceName)
+ }
+
+ // use supplied fd
+
+ fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
+ if err != nil {
+ return nil, err
+ }
+
+ return os.NewFile(uintptr(fd), ""), nil
+ }()
+
+ if err != nil {
+ logger.Error.Println("UAPI listen error:", err)
+ log.Fatal("Setup failed")
+ }
+ // daemonize the process
+
+ device := device.NewDevice(tun, logger)
+
+ errs := make(chan error)
+
+ uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
+ if err != nil {
+ logger.Error.Println("Failed to listen on uapi socket:", err)
+ log.Fatal("Setup failed")
+ }
+
+ go func() {
+ for {
+ conn, err := uapi.Accept()
+ if err != nil {
+ errs <- err
+ return
+ }
+ go device.IpcHandle(conn)
+ }
+ }()
+
+ logger.Info.Println("UAPI listener started")
+ fmt.Println("Device ", device)
+
+ if err := s.CreateNICWithOptions(nic, ep, stack.NICOptions{Name: interfaceName, Disabled: true}); err != nil {
+ log.Fatal("CreateNIC", err)
+ }
+
+ return device
+}
+
+func parseAddress(addrName string) (tcpip.Address, tcpip.NetworkProtocolNumber) {
+ ip := net.ParseIP(addrName)
+
+ if ip.To4() != nil {
+ return tcpip.Address(ip.To4()), ipv4.ProtocolNumber
+ } else {
+ return tcpip.Address(ip.To16()), ipv6.ProtocolNumber
+ }
+}
+
+func parseSubnet(subnetName string) (tcpip.Address, tcpip.Subnet, tcpip.NetworkProtocolNumber) {
+ parsedAddr, parsedNet, err := net.ParseCIDR(subnetName)
+ if err != nil {
+ log.Fatalf("Bad IP/CIDR address: %v", subnetName)
+ }
+
+ var addr tcpip.Address
+ var net tcpip.Address
+ var proto tcpip.NetworkProtocolNumber
+
+ if parsedAddr.To4() != nil {
+ addr = tcpip.Address(parsedAddr.To4())
+ net = tcpip.Address(parsedNet.IP.To4())
+ proto = ipv4.ProtocolNumber
+ } else {
+ addr = tcpip.Address(parsedAddr.To16())
+ net = tcpip.Address(parsedNet.IP.To16())
+ proto = ipv6.ProtocolNumber
+ }
+
+ // ones, zeros := parsedNet.Mask.Size()
+
+ mask, err := hex.DecodeString(parsedNet.Mask.String())
+ if err != nil {
+ log.Fatalf("Bad mask", err)
+ }
+
+ subnet, err := tcpip.NewSubnet(net, tcpip.AddressMask(mask))
+ if err != nil {
+ log.Fatalf("Bad subnet", err, net, parsedNet.Mask.String())
+ }
+
+ return addr, subnet, proto
+}
+
+func (routes *Routes) addAddress(s *stack.Stack, nic tcpip.NICID, addrName string) tcpip.NetworkProtocolNumber {
+ // // Parse the IP address. Support both ipv4 and ipv6.
+ addr, subnet, proto := parseSubnet(addrName)
+
+ if false {
+ if err := s.AddAddress(nic, proto, addr); err != nil {
+ log.Fatal("AddAddress", err)
+ }
+ } else {
+ addr := tcpip.ProtocolAddress{
+ Protocol: proto,
+ AddressWithPrefix: tcpip.AddressWithPrefix{
+ Address: addr,
+ PrefixLen: subnet.Prefix(),
+ }}
+
+ fmt.Println("Added address ", addr)
+
+ if err := s.AddProtocolAddress(nic, addr); err != nil {
+ log.Fatalf("AddProtocolAddress", err, nic)
+ }
+
+ route := tcpip.Route{
+ Destination: addr.AddressWithPrefix.Subnet(),
+ NIC: nic,
+ }
+
+ *routes = append(*routes, route)
+ }
+
+ // subnet, err := tcpip.NewSubnet(tcpip.Address(parsedNet.IP),
+ // tcpip.AddressMask(parsedNet.Mask))
+ // if err != nil {
+ // log.Fatal(err)
+ // }
+
+ return proto
+}
+
+type Routes []tcpip.Route
+
+
+func (routes *Routes) OnDuplicateAddressDetectionStatus(nicID tcpip.NICID, addr tcpip.Address, resolved bool, err *tcpip.Error) {
+ fmt.Println("OnDuplicateAddressDetectionStatus ", addr)
+}
+
+func (routes *Routes) OnDefaultRouterDiscovered(nicID tcpip.NICID, addr tcpip.Address) bool {
+ fmt.Println("OnDefaultRouterDiscovered")
+ return true
+}
+
+func (routes *Routes) OnDefaultRouterInvalidated(nicID tcpip.NICID, addr tcpip.Address) {
+}
+
+func (routes *Routes) OnOnLinkPrefixDiscovered(nicID tcpip.NICID, prefix tcpip.Subnet) bool {
+ fmt.Println("OnOnLinkPrefixDiscovered ", prefix)
+ return true
+}
+
+func (routes *Routes) OnOnLinkPrefixInvalidated(nicID tcpip.NICID, prefix tcpip.Subnet) {
+}
+
+func (routes *Routes) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool {
+ return true
+}
+
+func (routes *Routes) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {
+}
+
+func (routes *Routes) OnAutoGenAddressInvalidated(tcpip.NICID, tcpip.AddressWithPrefix) {
+}
+
+func (routes *Routes) OnRecursiveDNSServerOption(nicID tcpip.NICID, addrs []tcpip.Address, lifetime time.Duration) {
+ fmt.Println("OnRecursiveDNSServerOption ", addrs)
+}
+
+func (routes *Routes) OnDHCPv6Configuration(nicID tcpip.NICID, config stack.DHCPv6ConfigurationFromNDPRA) {
+ str := "undefined"
+ switch config {
+ case stack.DHCPv6NoConfiguration: str = "no config"
+ case stack.DHCPv6ManagedAddress: str = "managed"
+ case stack.DHCPv6OtherConfigurations: str = "other"
+ }
+
+ fmt.Println("OnDHCPv6Configuration ", str)
+}
+
+
+func (routes *Routes) addRoute(nic tcpip.NICID, routeCfg Route){
+ _, dest, _ := parseSubnet(routeCfg.To)
+ via, _ := parseAddress(routeCfg.Via)
+
+ route := tcpip.Route{
+ Destination: dest,
+ Gateway: via,
+ NIC: nic,
+ Mark: routeCfg.Mark,
+ Markmask: routeCfg.Markmask,
+ }
+
+ *routes = append(*routes, route)
+}
+
+func protocolToString(proto tcpip.NetworkProtocolNumber) string {
+ switch proto {
+ case ipv4.ProtocolNumber: return "ipv4"
+ case ipv6.ProtocolNumber: return "ipv6"
+ case arp.ProtocolNumber: return "arp "
+ default: return "?"
+ }
+}
+
+func dumpAddress(addr tcpip.ProtocolAddress) {
+ fmt.Println("Addr: ", protocolToString(addr.Protocol),
+ addr.AddressWithPrefix)
+}
+
+func dumpAddresses(s *stack.Stack) {
+ for nic, addrs := range s.AllAddresses() {
+ fmt.Println("NIC:", nic)
+ for _, addr := range addrs {
+ dumpAddress(addr)
+ }
+ }
+}
+
+func dumpRoutes(s *stack.Stack) {
+ for _, r := range s.GetRouteTable() {
+ fmt.Println("Route:", r)
+ }
+}
+
+func (routes *Routes) setupCommon(s *stack.Stack, nic tcpip.NICID, id string, cfg Common) {
+ for _, addr := range cfg.Addresses {
+ routes.addAddress(s, nic, addr)
+ }
+
+ for _, route := range cfg.Routes {
+ fmt.Println("Add Route:", route)
+ routes.addRoute(nic, route)
+ }
+
+ for _, route := range *routes {
+ fmt.Println("Added Route:", route)
+ }
+
+ // TODO check after enabling the NICs
+ // if !s.CheckNIC(nic) {
+ // log.Fatal("not usable ", id)
+ // }
+}
+
+func (routes *Routes) setupLoopback(s *stack.Stack, nic tcpip.NICID, id string, eth *Ethernet) {
+ fmt.Println("Ethernet", id, nic, eth)
+
+ linkEP := loopback.New()
+ if err := s.CreateNICWithOptions(nic, linkEP, stack.NICOptions{Name: id, Disabled: true}); err != nil {
+ log.Fatal("CreateNIC", err)
+ }
+
+ routes.setupCommon(s, nic, id, eth.Common)
+}
+
+func (routes *Routes) setupTunnel(s *stack.Stack, nic tcpip.NICID, id string, tun *Tunnel) {
+ fmt.Println("TUN", id, nic, tun)
+
+ maddr, err := net.ParseMAC(tun.Macaddress)
+ if err != nil {
+ log.Fatalf("Bad MAC address: %v", tun.Macaddress)
+ }
+
+ addRouterLink(s, nic, id, tcpip.LinkAddress(maddr), tun)
+ fmt.Println("Tunnel 20", tun.Conn)
+ routes.setupCommon(s, nic, id, tun.Common)
+ fmt.Println("Tunnel 21", tun.Conn)
+}
+
+func (routes *Routes) setupTuntap(s *stack.Stack, nic tcpip.NICID, id string, tun *Tuntap) {
+ fmt.Println("Tuntap", id, nic, tun)
+
+ maddr, err := net.ParseMAC(tun.Macaddress)
+ if err != nil {
+ log.Fatalf("Bad MAC address: %v", tun.Macaddress)
+ }
+
+ var tap bool
+ switch tun.Mode {
+ case "tun":
+ tap = false
+ case "tap":
+ tap = true
+ default:
+ log.Fatalf("Bad mode: %v", tun.Mode)
+ }
+
+ addTunLink(s, nic, id, tap, tcpip.LinkAddress(maddr), tun)
+ routes.setupCommon(s, nic, id, tun.Common)
+}
+
+func (routes *Routes) setupWG(s *stack.Stack, nic tcpip.NICID, id string, wg *Wireguard) {
+ fmt.Println("WG", id, nic, wg.ListenPort, wg)
+ fmt.Printf("Peers %v\n", wg.Peers)
+
+ maddr, err := net.ParseMAC(wg.Macaddress)
+ if err != nil {
+ log.Fatalf("Bad MAC address: %v", wg.Macaddress)
+ }
+
+ //addTunLink(s, tunNic, tunName, tcpip.LinkAddress(tapMaddr))
+ device := addWgLink(s, nic, wg.Name, tcpip.LinkAddress(maddr))
+
+ var wgCmd strings.Builder
+ fmt.Fprintf(&wgCmd, "private_key=%s\nlisten_port=%d\nreplace_peers=true\n",
+ hex.EncodeToString(wg.PrivateKey), wg.ListenPort)
+ for _, peer := range wg.Peers {
+ fmt.Fprintf(&wgCmd, "public_key=%s\nendpoint=%s\npersistent_keepalive_interval=%d\nreplace_allowed_ips=true\n",
+ hex.EncodeToString(peer.PublicKey), peer.Endpoint, peer.PersistentKeepalive)
+ for _, allowedIp := range peer.AllowedIPs {
+ fmt.Fprintf(&wgCmd, "allowed_ip=%s\n", allowedIp)
+ }
+ wgCmd.WriteString("\n")
+ }
+ str := wgCmd.String()
+ fmt.Println("IpcSetOperation", str)
+ device.IpcSetOperation(bufio.NewReader(strings.NewReader(str)))
+
+ routes.setupCommon(s, nic, id, wg.Common)
+
+ go func() {
+ fmt.Println("Starting ", nic)
+
+ select {
+ // case <-term:
+ // case <-errs:
+ case <-device.Wait():
+ }
+
+ fmt.Println("Finnished ", nic)
+ }()
+}
+
+func KeepAliveTunnel(np *Netplan) {
+ KeepAliveTunnelEx(np, true)
+}
+
+func KeepAliveTunnelEx(np *Netplan, debug bool) {
+ for _, tun := range np.Network.Tunnels {
+ if debug {
+ fmt.Println("Tunnel ", tun.Mode, tun.Local, tun.Remote, tun.Conn, tun.Sd)
+ }
+ runtime.KeepAlive(tun.Conn)
+ }
+}
+
+func withIAPD(iaid [4]byte, prefixLength int) dhcpv6.Modifier {
+ return func(d dhcpv6.DHCPv6) {
+ opt := d.GetOneOption(dhcpv6.OptionIAPD)
+ if opt == nil {
+ opt = &dhcpv6.OptIAPD{}
+ }
+ iaPd := opt.(*dhcpv6.OptIAPD)
+
+ if prefixLength > 0 {
+ iaPrefix := &dhcpv6.OptIAPrefix{}
+ iaPrefix.Prefix.Mask = net.CIDRMask(prefixLength,
+ 128-prefixLength)
+ iaPrefix.Prefix.IP = net.ParseIP("::")
+ iaPd.Options.Add(iaPrefix)
+ }
+ copy(iaPd.IaId[:], iaid[:])
+ d.UpdateOption(iaPd)
+ }
+}
+
+func withIAPDFromAdvertise(adv *dhcpv6.Message) dhcpv6.Modifier {
+ return func(d dhcpv6.DHCPv6) {
+ opt := adv.GetOneOption(dhcpv6.OptionIAPD)
+ if opt != nil {
+ d.AddOption(opt)
+ }
+ }
+}
+
+func withDHCP4oDHCP6Server(addrs ...net.IP) dhcpv6.Modifier {
+ return func(d dhcpv6.DHCPv6) {
+ opt := dhcpv6.OptDHCP4oDHCP6Server{
+ DHCP4oDHCP6Servers: addrs,
+ }
+ d.UpdateOption(&opt)
+ }
+}
+
+func withDHCPv4Msg(msg *dhcpv4.DHCPv4) dhcpv6.Modifier {
+ return func(d dhcpv6.DHCPv6) {
+ opt := dhcpv6.OptDHCPv4Msg{
+ Msg: msg,
+ }
+ d.UpdateOption(&opt)
+ }
+}
+
+func NewDHCPv4Query(modifiers ...dhcpv6.Modifier) (*dhcpv6.Message, error) {
+ msg, err := dhcpv6.NewMessage()
+ if err != nil {
+ return nil, err
+ }
+ //msg.MessageType = dhcpv6.MessageTypeDHCPv4Query FIXME
+ msg.MessageType = 20
+ msg.AddOption(&dhcpv6.OptElapsedTime{})
+ //modifier = append([]dhcpv6.Modifier{WithRequestedOptions()}, modifier...}
+ for _, mod := range modifiers {
+ mod(msg)
+ }
+ return msg, nil
+}
+
+func (routes *Routes) doClient(s *stack.Stack, nic tcpip.NICID) {
+ fmt.Println("doClient start")
+
+ // TODO use link local address
+
+ src := tcpip.Address(net.ParseIP("2001:470:dfae:6300::1:111").To16())
+ dst := tcpip.Address(net.ParseIP("2001:470:dfae:6300::3").To16())
+
+ conn, err := gonet.DialUDP(s,
+ &tcpip.FullAddress{NIC: nic, Addr: src, Port: 546},
+ &tcpip.FullAddress{NIC: nic, Addr: dst, Port: 547},
+ ipv6.ProtocolNumber)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ hwaddr := []byte("ABCDEF")
+
+ client, err := nclient6.NewWithConn(conn, hwaddr, nclient6.WithDebugLogger())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ duid := dhcpv6.Duid{
+ Type: dhcpv6.DUID_LL,
+ HwType: iana.HWTypeEthernet,
+ LinkLayerAddr: hwaddr,
+ }
+
+ fqdnOpt := dhcpv6.WithFQDN(0x1, "gvisor.m7n.se")
+
+ iaPrefix := dhcpv6.OptIAPrefix{}
+ iaPrefix.Prefix.Mask = net.CIDRMask(64, 128-64)
+ //iaPrefix.SetIPv6Prefix(net.ParseIP("::"))
+
+ iaid := []byte{0, 0, 0, 3}
+ ident := []byte{255} // Type IAID+DUID
+ ident = append(ident, iaid...) // IAID
+ ident = append(ident, duid.ToBytes()...) // DUID
+ clientIDOpt := dhcpv4.OptClientIdentifier(ident)
+
+ adv, err := client.Solicit(context.Background(),
+ dhcpv6.WithIAPD([4]byte{0, 0, 0, 1}, &iaPrefix),
+ dhcpv6.WithIAID([4]byte{0, 0, 0, 2}),
+ fqdnOpt,
+ dhcpv6.WithClientID(duid),
+ dhcpv6.WithRequestedOptions(dhcpv6.OptionDHCP4oDHCP6Server),
+ withDHCP4oDHCP6Server(net.ParseIP("fe80::1"),
+ net.ParseIP("fe80::2")))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ //withIAPDFromAdvertise(adv)
+ msg, err := client.Request(context.Background(), adv, fqdnOpt)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ disc, err := dhcpv4.NewDiscovery(hwaddr, dhcpv4.WithOption(clientIDOpt))
+ if err != nil {
+ log.Fatal(err)
+ }
+ disc_query, err := NewDHCPv4Query(withDHCPv4Msg(disc))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ serverAddr := nclient6.AllDHCPRelayAgentsAndServers
+ disc_resp, err := client.SendAndRead(context.Background(), serverAddr, disc_query, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ offer := disc_resp.GetOneOption(dhcpv6.OptionDHCPv4Msg).(*dhcpv6.OptDHCPv4Msg).Msg
+ req, err := dhcpv4.NewRequestFromOffer(offer)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ req_query, err := NewDHCPv4Query(withDHCPv4Msg(req))
+ if err != nil {
+ log.Fatal(err)
+ }
+ ack, err := client.SendAndRead(context.Background(), serverAddr, req_query, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // client.Close()
+ fmt.Println("doClient end", ack)
+
+ iana := msg.GetOneOption(dhcpv6.OptionIANA).(*dhcpv6.OptIANA)
+ for _, addr := range iana.Options.Get(dhcpv6.OptionIAAddr) {
+ str := addr.(*dhcpv6.OptIAAddress).IPv6Addr.String() + "/128"
+ routes.addAddress(s, nic, str)
+
+ _, dest, _ := parseSubnet(addr.(*dhcpv6.OptIAAddress).IPv6Addr.String() + "/64")
+ route := tcpip.Route{
+ Destination: dest,
+ NIC: nic,
+ }
+
+ *routes = append(*routes, route)
+ }
+
+ var loNic tcpip.NICID = 0
+ iapd := msg.GetOneOption(dhcpv6.OptionIAPD).(*dhcpv6.OptIAPD)
+ for _, opt := range iapd.Options.Get(dhcpv6.OptionIAPrefix) {
+ prefix := opt.(*dhcpv6.OptIAPrefix)
+ str := prefix.Prefix.IP.String()+"1"+"/128"
+ routes.addAddress(s, loNic, str)
+ }
+
+ dumpAddresses(s)
+ dumpRoutes(s)
+
+ if false {
+ // FIXME can't send non icmpv6 echo request
+ var wq waiter.Queue
+ ep, e := s.NewEndpoint(icmp.ProtocolNumber6, ipv6.ProtocolNumber, &wq)
+ if err != nil {
+ log.Fatal("NewEndpoint", e)
+ }
+
+ v := []byte{0, 1, 2, 3}
+ raSrc := tcpip.FullAddress{NIC: nic, Addr: src, Port: 0}
+ raDst := tcpip.FullAddress{NIC: nic, Addr: dst, Port: 0}
+ if err := ep.Bind(raSrc); err != nil {
+ log.Fatal("Bind failed: ", err)
+ }
+ fmt.Println("Before write", raSrc.NIC, raSrc.Addr, raSrc.Port, raDst.NIC, raDst.Addr, raDst.Port)
+ ep.Write(tcpip.SlicePayload(v),
+ tcpip.WriteOptions{To:&raDst})
+
+ defer ep.Close()
+ fmt.Println("After write")
+ }
+ // Exchange runs a Solicit-Advertise-Request-Reply transaction on the
+ // specified network interface, and returns a list of DHCPv6 packets
+ // (a "conversation") and an error if any. Notice that Exchange may
+ // return a non-empty packet list even if there is an error. This is
+ // intended, because the transaction may fail at any point, and we
+ // still want to know what packets were exchanged until then.
+ // A default Solicit packet will be used during the "conversation",
+ // which can be manipulated by using modifiers.
+// conversation, err := client.Exchange(iface)
+
+ // Summary() prints a verbose representation of the exchanged packets.
+// for _, packet := range conversation {
+// log.Print(packet.Summary())
+// }
+ // error handling is done *after* printing, so we still print the
+ // exchanged packets if any, as explained above.
+// if err != nil {
+// log.Fatal(err)
+// }
+}
+
+func main() {
+ flag.Parse()
+ if len(flag.Args()) != 1 {
+ log.Fatal("Usage: ", os.Args[0], " <local-port>")
+ }
+
+ data, err := ioutil.ReadFile("config.yaml")
+ if err != nil {
+ log.Fatalf("File reading error", err)
+ }
+
+ var np Netplan
+ err = yaml.Unmarshal(data, &np)
+ fmt.Println("err", err)
+ fmt.Println("res", np)
+
+ portName := flag.Arg(0)
+
+ rand.Seed(time.Now().UnixNano())
+
+ localPort, err := strconv.Atoi(portName)
+ if err != nil {
+ log.Fatalf("Unable to convert port %v: %v", portName, err)
+ }
+
+ routes := Routes{}
+
+ // Create the stack with ip and tcp protocols, then add a tun-based
+ // NIC and address.
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), ipv6.NewProtocol(), arp.NewProtocol()},
+ TransportProtocols: []stack.TransportProtocol{
+ tcp.NewProtocol(),
+ udp.NewProtocol(),
+ //icmp.NewProtocol6(),
+ },
+ //NDPConfigs: stack.DefaultNDPConfigurations(),
+ //NDPDisp: &routes,
+ })
+
+ // FIXME enable
+ s.SetForwarding(true)
+
+ var nic tcpip.NICID = -1
+ var wg2Nic tcpip.NICID = -1
+
+ for id, tun := range np.Network.Ethernets {
+ nic = nic + 1
+ routes.setupLoopback(s, nic, id, tun)
+ }
+
+ for id, tun := range np.Network.Tuntaps {
+ nic = nic + 1
+ routes.setupTuntap(s, nic, id, tun)
+ }
+
+ for id, wg := range np.Network.Wireguards {
+ nic = nic + 1
+ if id == "wg2" {
+ wg2Nic = nic
+ }
+ routes.setupWG(s, nic, id, wg)
+ }
+
+ for id, tun := range np.Network.Tunnels {
+ nic = nic + 1
+ routes.setupTunnel(s, nic, id, tun)
+ }
+
+ nicCount := nic
+
+ for nic = 0; nic < nicCount; nic++ {
+ s.EnableNIC(nic)
+ }
+
+ KeepAliveTunnel(&np)
+
+ // Sort route table for longest prefix match
+ sort.Slice(routes, func(i, j int) bool {
+ return routes[i].Destination.Prefix() > routes[j].Destination.Prefix()
+ })
+
+ s.SetRouteTable(routes)
+
+ // FIXME disabled for now, to test startSolicitingRouters
+ if true {
+ routes.doClient(s, wg2Nic)
+ }
+
+ // Sort route table for longest prefix match
+ sort.Slice(routes, func(i, j int) bool {
+ return routes[i].Destination.Prefix() > routes[j].Destination.Prefix()
+ })
+
+ s.SetRouteTable(routes)
+
+ dumpAddresses(s)
+ dumpRoutes(s)
+
+ KeepAliveTunnel(&np)
+
+ runtime.GC()
+
+ KeepAliveTunnel(&np)
+
+ // Create TCP endpoint, bind it, then start listening.
+ if true {
+ proto := ipv6.ProtocolNumber // Dummy
+ var wq waiter.Queue
+ ep, e := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
+ if err != nil {
+ log.Fatal("NewEndpoint", e)
+ }
+
+ defer ep.Close()
+
+ if err := ep.Bind(tcpip.FullAddress{0, "", uint16(localPort)}); err != nil {
+ log.Fatal("Bind failed: ", err)
+
+}
+ if err := ep.Listen(10); err != nil {
+ log.Fatal("Listen failed: ", err)
+ }
+
+ // Wait for connections to appear.
+ waitEntry, notifyCh := waiter.NewChannelEntry(nil)
+ wq.EventRegister(&waitEntry, waiter.EventIn)
+ defer wq.EventUnregister(&waitEntry)
+
+ fmt.Println("Echo server\n")
+
+ for {
+ n, wq, err := ep.Accept()
+ if err != nil {
+ if err == tcpip.ErrWouldBlock {
+ <-notifyCh
+ continue
+ }
+
+ log.Fatal("Accept() failed:", err)
+ }
+
+ KeepAliveTunnelEx(&np, false)
+ go echo(wq, n)
+ }
+ }
+
+}
diff --git a/pkg/tcpip/sample/wg_tunnel/run.sh b/pkg/tcpip/sample/wg_tunnel/run.sh
new file mode 100755
index 000000000..7b5bf0524
--- /dev/null
+++ b/pkg/tcpip/sample/wg_tunnel/run.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+usage() {
+ echo "$0 [-6] [-tap] -dev <dev>"
+ exit
+}
+
+IPV6=0
+OPTS=
+DEV=
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -4) IPV6=0;;
+ -6) IPV6=1;;
+ -tap) OPTS="$OPTS -tap";;
+ -dev) DEV="$2"; shift;;
+ *) usage;;
+ esac
+
+ shift
+done
+
+if [ -z "$DEV" ]; then
+ usage
+fi
+
+if [ $IPV6 -eq 1 ]; then
+ OPTS="$OPTS $DEV 2001:470:de6f:5311::2/64 10003 2001:470:de6f:5312::1/64 2001:470:de6f:5311::1"
+else
+ OPTS="$OPTS $DEV 10.1.1.2/24 10003 10.1.2.1/24 10.1.1.1"
+fi
+
+exec ./wg_tunnel $OPTS
diff --git a/pkg/tcpip/sample/wg_tunnel/setup.sh b/pkg/tcpip/sample/wg_tunnel/setup.sh
new file mode 100755
index 000000000..043a40be8
--- /dev/null
+++ b/pkg/tcpip/sample/wg_tunnel/setup.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -e
+
+setup() {
+ local mode=$1
+ local dev=$2
+ ip tuntap add $dev mode $mode
+ ip l set up $dev
+ ip a a 10.1.1.1/24 dev $dev
+ ip r a 10.1.0.0/16 via 10.1.1.2 proto static
+ ip a a 2001:470:de6f:5311::1/64 dev $dev
+ ip r a 2001:470:de6f:5312::/64 via 2001:470:de6f:5311::2 proto static
+ ip r a unreachable 2001:470:de6f:5310::/56 proto static || true
+}
+
+usage() {
+ echo "Usage: $0 <mode> <dev>"
+ exit 1
+}
+
+if [ $# -ne 2 ]; then
+ usage
+fi
+
+setup $1 $2