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