// 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. // 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" "gvisor.googlesource.com/gvisor/pkg/tcpip" "gvisor.googlesource.com/gvisor/pkg/tcpip/link/fdbased" "gvisor.googlesource.com/gvisor/pkg/tcpip/link/rawfile" "gvisor.googlesource.com/gvisor/pkg/tcpip/link/tun" "gvisor.googlesource.com/gvisor/pkg/tcpip/network/arp" "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv4" "gvisor.googlesource.com/gvisor/pkg/tcpip/network/ipv6" "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/tcp" "gvisor.googlesource.com/gvisor/pkg/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], " ") } 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(&tcpip.StdClock{}, []string{ipv4.ProtocolName, ipv6.ProtocolName, arp.ProtocolName}, []string{tcp.ProtocolName}) 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) // S/R-FIXME } }