| // 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) |
| } |
| } |