blob: 92989dfddbd370fe6c7c4d82125f1f229161ca0d [file] [log] [blame]
// Copyright 2016 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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 (
"log"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/link/fdbased"
"github.com/google/netstack/tcpip/link/rawfile"
"github.com/google/netstack/tcpip/link/tun"
"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/tcp"
"github.com/google/netstack/waiter"
)
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(v, nil)
}
}
func main() {
if len(os.Args) != 4 {
log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-address> <local-port>")
}
tunName := os.Args[1]
addrName := os.Args[2]
portName := os.Args[3]
rand.Seed(time.Now().UnixNano())
// Parse the IP address. Support both ipv4 and ipv6.
parsedAddr := net.ParseIP(addrName)
if parsedAddr == nil {
log.Fatalf("Bad IP address: %v", addrName)
}
var addr tcpip.Address
var proto tcpip.NetworkProtocolNumber
if parsedAddr.To4() != nil {
addr = tcpip.Address(parsedAddr.To4())
proto = ipv4.ProtocolNumber
} else if parsedAddr.To16() != nil {
addr = tcpip.Address(parsedAddr.To16())
proto = ipv6.ProtocolNumber
} else {
log.Fatalf("Unknown IP type: %v", addrName)
}
localPort, err := strconv.Atoi(portName)
if err != nil {
log.Fatalf("Unable to convert port %v: %v", portName, err)
}
// Create the stack with ip and tcp protocols, then add a tun-based
// NIC and address.
s := stack.New([]string{ipv4.ProtocolName, ipv6.ProtocolName}, []string{tcp.ProtocolName})
mtu, err := rawfile.GetMTU(tunName)
if err != nil {
log.Fatal(err)
}
fd, err := tun.Open(tunName)
if err != nil {
log.Fatal(err)
}
linkID := fdbased.New(fd, mtu, nil)
if err := s.CreateNIC(1, linkID); err != nil {
log.Fatal(err)
}
if err := s.AddAddress(1, proto, addr); err != nil {
log.Fatal(err)
}
// Add default route.
s.SetRouteTable([]tcpip.Route{
{
Destination: tcpip.Address(strings.Repeat("\x00", len(addr))),
Mask: tcpip.Address(strings.Repeat("\x00", len(addr))),
Gateway: "",
NIC: 1,
},
})
// Create TCP endpoint, bind it, then start listening.
var wq waiter.Queue
ep, e := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq)
if err != nil {
log.Fatal(e)
}
defer ep.Close()
if err := ep.Bind(tcpip.FullAddress{0, "", uint16(localPort)}, nil); 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)
for {
n, wq, err := ep.Accept()
if err != nil {
if err == tcpip.ErrWouldBlock {
<-notifyCh
continue
}
log.Fatal("Accept() failed:", err)
}
go echo(wq, n)
}
}