blob: 39528098403c57a461499060312cc8ff7f465d0d [file] [log] [blame]
// Copyright 2018 Google Inc.
//
// 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 (
"flag"
"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/arp"
"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"
)
var tap = flag.Bool("tap", false, "use tap istead of tun")
var mac = flag.String("mac", "aa:00:01:01:01:01", "mac address to use in tap device")
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 main() {
flag.Parse()
if len(flag.Args()) != 3 {
log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-address> <local-port>")
}
tunName := flag.Arg(0)
addrName := flag.Arg(1)
portName := flag.Arg(2)
rand.Seed(time.Now().UnixNano())
// Parse the mac address.
maddr, err := net.ParseMAC(*mac)
if err != nil {
log.Fatalf("Bad MAC address: %v", *mac)
}
// 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, arp.ProtocolName}, []string{tcp.ProtocolName}, stack.Options{})
mtu, err := rawfile.GetMTU(tunName)
if err != nil {
log.Fatal(err)
}
var fd int
if *tap {
fd, err = tun.OpenTAP(tunName)
} else {
fd, err = tun.Open(tunName)
}
if err != nil {
log.Fatal(err)
}
linkID := fdbased.New(&fdbased.Options{
FD: fd,
MTU: mtu,
EthernetHeader: *tap,
Address: tcpip.LinkAddress(maddr),
})
if err := s.CreateNIC(1, linkID); err != nil {
log.Fatal(err)
}
if err := s.AddAddress(1, proto, addr); err != nil {
log.Fatal(err)
}
if err := s.AddAddress(1, arp.ProtocolNumber, arp.ProtocolAddress); 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)
}
}