Skip to content

Instantly share code, notes, and snippets.

@comzyh
Last active June 18, 2020 05:51
Show Gist options
  • Save comzyh/f8972fa97ca8efbe158afcf4cf038927 to your computer and use it in GitHub Desktop.
Save comzyh/f8972fa97ca8efbe158afcf4cf038927 to your computer and use it in GitHub Desktop.
Demo intergate songgao/water with google/netstack for Tun
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/adapters/gonet"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/link/channel"
"github.com/google/netstack/tcpip/network/ipv4"
"github.com/google/netstack/tcpip/network/ipv6"
"github.com/google/netstack/tcpip/stack"
"github.com/google/netstack/tcpip/transport/icmp"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
"github.com/songgao/water"
"github.com/google/netstack/waiter"
)
var (
ipstack *stack.Stack
)
func echo(c *gonet.Conn) {
defer c.Close()
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")
}
func main() {
name := "tun0"
var mtu uint32 = 1500
var err error
// create tun by water
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{
Name: name,
},
})
if err != nil {
log.Fatal(err)
}
// create tcpip stack
ipstack = stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol(), ipv6.NewProtocol()},
TransportProtocols: []stack.TransportProtocol{tcp.NewProtocol(), udp.NewProtocol(), icmp.NewProtocol4()},
})
// create Link endpoint. 512 is the size of internal go channel
linkEP := channel.New(512, mtu, "")
// 1 is the NIC id, you can use any number you like
var nicID tcpip.NICID = 1
// create virtual NIC.
if err := ipstack.CreateNIC(nicID, linkEP); err != nil {
log.Fatal(err)
}
// IPv4 0.0.0.0/0
subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4)))
ipstack.AddAddressRange(nicID, ipv4.ProtocolNumber, subnet)
// IPv6 [::]/0
subnet, _ = tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 16)), tcpip.AddressMask(strings.Repeat("\x00", 16)))
ipstack.AddAddressRange(nicID, ipv6.ProtocolNumber, subnet)
if err != nil {
log.Fatal(err)
}
// use Forwarder to accept any connection from stack
fwd := tcp.NewForwarder(ipstack, 0, 16, func(r *tcp.ForwarderRequest) {
fmt.Printf("ForwarderRequest: %v\n", r)
var wq waiter.Queue
ep, err := r.CreateEndpoint(&wq)
if err != nil {
log.Fatalf("r.CreateEndpoint() = %v", err)
}
r.Complete(false)
c := gonet.NewConn(&wq, ep)
log.Printf("endpoint: %v", ep.Info().(*tcp.EndpointInfo).ID)
// TCP echo
go echo(c)
})
ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, fwd.HandlePacket)
// start Read loop. read ip packet from tun and write it to ipstack
go func() {
packet := make([]byte, 2000)
for {
n, err := ifce.Read(packet)
if err != nil {
log.Fatal(err)
}
log.Printf("Packet Received: % x\n", packet[:n])
var p tcpip.NetworkProtocolNumber
switch header.IPVersion(packet) {
case header.IPv4Version:
p = header.IPv4ProtocolNumber
case header.IPv6Version:
p = header.IPv6ProtocolNumber
}
linkEP.Inject(p, buffer.View(packet[:n]).ToVectorisedView())
}
}()
// start write loop. read ip packet from ipstack and write it to tun
go func() {
for {
packet := <-linkEP.C
log.Printf("Packet Write out: % x % x\n", packet.Header, packet.Payload)
ifce.Write(buffer.NewVectorisedView(len(packet.Header)+len(packet.Payload), []buffer.View{packet.Header, packet.Payload}).ToView())
}
}()
exitCh := make(chan os.Signal, 1)
log.Print("Waiting....")
<-exitCh
os.Exit(0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment