github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/sample/tun_tcp_connect/main.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build linux
    16  
    17  // This sample creates a stack with TCP and IPv4 protocols on top of a TUN
    18  // device, and connects to a peer. Similar to "nc <address> <port>". While the
    19  // sample is running, attempts to connect to its IPv4 address will result in
    20  // a RST segment.
    21  //
    22  // As an example of how to run it, a TUN device can be created and enabled on
    23  // a linux host as follows (this only needs to be done once per boot):
    24  //
    25  // [sudo] ip tuntap add user <username> mode tun <device-name>
    26  // [sudo] ip link set <device-name> up
    27  // [sudo] ip addr add <ipv4-address>/<mask-length> dev <device-name>
    28  //
    29  // A concrete example:
    30  //
    31  // $ sudo ip tuntap add user wedsonaf mode tun tun0
    32  // $ sudo ip link set tun0 up
    33  // $ sudo ip addr add 192.168.1.1/24 dev tun0
    34  //
    35  // Then one can run tun_tcp_connect as such:
    36  //
    37  // $ ./tun/tun_tcp_connect tun0 192.168.1.2 0 192.168.1.1 1234
    38  //
    39  // This will attempt to connect to the linux host's stack. One can run nc in
    40  // listen mode to accept a connect from tun_tcp_connect and exchange data.
    41  package main
    42  
    43  import (
    44  	"bufio"
    45  	"fmt"
    46  	"log"
    47  	"math/rand"
    48  	"net"
    49  	"os"
    50  	"strconv"
    51  	"time"
    52  
    53  	"github.com/FlowerWrong/netstack/tcpip"
    54  	"github.com/FlowerWrong/netstack/tcpip/buffer"
    55  	"github.com/FlowerWrong/netstack/tcpip/header"
    56  	"github.com/FlowerWrong/netstack/tcpip/link/fdbased"
    57  	"github.com/FlowerWrong/netstack/tcpip/link/rawfile"
    58  	"github.com/FlowerWrong/netstack/tcpip/link/sniffer"
    59  	"github.com/FlowerWrong/netstack/tcpip/link/tun"
    60  	"github.com/FlowerWrong/netstack/tcpip/network/ipv4"
    61  	"github.com/FlowerWrong/netstack/tcpip/stack"
    62  	"github.com/FlowerWrong/netstack/tcpip/transport/tcp"
    63  	"github.com/FlowerWrong/netstack/waiter"
    64  )
    65  
    66  // writer reads from standard input and writes to the endpoint until standard
    67  // input is closed. It signals that it's done by closing the provided channel.
    68  func writer(ch chan struct{}, ep tcpip.Endpoint) {
    69  	defer func() {
    70  		ep.Shutdown(tcpip.ShutdownWrite)
    71  		close(ch)
    72  	}()
    73  
    74  	r := bufio.NewReader(os.Stdin)
    75  	for {
    76  		v := buffer.NewView(1024)
    77  		n, err := r.Read(v)
    78  		if err != nil {
    79  			return
    80  		}
    81  
    82  		v.CapLength(n)
    83  		for len(v) > 0 {
    84  			n, _, err := ep.Write(tcpip.SlicePayload(v), tcpip.WriteOptions{})
    85  			if err != nil {
    86  				fmt.Println("Write failed:", err)
    87  				return
    88  			}
    89  
    90  			v.TrimFront(int(n))
    91  		}
    92  	}
    93  }
    94  
    95  func main() {
    96  	if len(os.Args) != 6 {
    97  		log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-ipv4-address> <local-port> <remote-ipv4-address> <remote-port>")
    98  	}
    99  
   100  	tunName := os.Args[1]
   101  	addrName := os.Args[2]
   102  	portName := os.Args[3]
   103  	remoteAddrName := os.Args[4]
   104  	remotePortName := os.Args[5]
   105  
   106  	rand.Seed(time.Now().UnixNano())
   107  
   108  	addr := tcpip.Address(net.ParseIP(addrName).To4())
   109  	remote := tcpip.FullAddress{
   110  		NIC:  1,
   111  		Addr: tcpip.Address(net.ParseIP(remoteAddrName).To4()),
   112  	}
   113  
   114  	var localPort uint16
   115  	if v, err := strconv.Atoi(portName); err != nil {
   116  		log.Fatalf("Unable to convert port %v: %v", portName, err)
   117  	} else {
   118  		localPort = uint16(v)
   119  	}
   120  
   121  	if v, err := strconv.Atoi(remotePortName); err != nil {
   122  		log.Fatalf("Unable to convert port %v: %v", remotePortName, err)
   123  	} else {
   124  		remote.Port = uint16(v)
   125  	}
   126  
   127  	// Create the stack with ipv4 and tcp protocols, then add a tun-based
   128  	// NIC and ipv4 address.
   129  	s := stack.New(stack.Options{
   130  		NetworkProtocols:   []stack.NetworkProtocol{ipv4.NewProtocol()},
   131  		TransportProtocols: []stack.TransportProtocol{tcp.NewProtocol()},
   132  	})
   133  
   134  	mtu, err := rawfile.GetMTU(tunName)
   135  	if err != nil {
   136  		log.Fatal(err)
   137  	}
   138  
   139  	fd, err := tun.Open(tunName)
   140  	if err != nil {
   141  		log.Fatal(err)
   142  	}
   143  
   144  	linkEP, err := fdbased.New(&fdbased.Options{FDs: []int{fd}, MTU: mtu})
   145  	if err != nil {
   146  		log.Fatal(err)
   147  	}
   148  	if err := s.CreateNIC(1, sniffer.New(linkEP)); err != nil {
   149  		log.Fatal(err)
   150  	}
   151  
   152  	if err := s.AddAddress(1, ipv4.ProtocolNumber, addr); err != nil {
   153  		log.Fatal(err)
   154  	}
   155  
   156  	// Add default route.
   157  	s.SetRouteTable([]tcpip.Route{
   158  		{
   159  			Destination: header.IPv4EmptySubnet,
   160  			NIC:         1,
   161  		},
   162  	})
   163  
   164  	// Create TCP endpoint.
   165  	var wq waiter.Queue
   166  	ep, e := s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
   167  	if err != nil {
   168  		log.Fatal(e)
   169  	}
   170  
   171  	// Bind if a port is specified.
   172  	if localPort != 0 {
   173  		if err := ep.Bind(tcpip.FullAddress{0, "", localPort}); err != nil {
   174  			log.Fatal("Bind failed: ", err)
   175  		}
   176  	}
   177  
   178  	// Issue connect request and wait for it to complete.
   179  	waitEntry, notifyCh := waiter.NewChannelEntry(nil)
   180  	wq.EventRegister(&waitEntry, waiter.EventOut)
   181  	terr := ep.Connect(remote)
   182  	if terr == tcpip.ErrConnectStarted {
   183  		fmt.Println("Connect is pending...")
   184  		<-notifyCh
   185  		terr = ep.GetSockOpt(tcpip.ErrorOption{})
   186  	}
   187  	wq.EventUnregister(&waitEntry)
   188  
   189  	if terr != nil {
   190  		log.Fatal("Unable to connect: ", terr)
   191  	}
   192  
   193  	fmt.Println("Connected")
   194  
   195  	// Start the writer in its own goroutine.
   196  	writerCompletedCh := make(chan struct{})
   197  	go writer(writerCompletedCh, ep)
   198  
   199  	// Read data and write to standard output until the peer closes the
   200  	// connection from its side.
   201  	wq.EventRegister(&waitEntry, waiter.EventIn)
   202  	for {
   203  		v, _, err := ep.Read(nil)
   204  		if err != nil {
   205  			if err == tcpip.ErrClosedForReceive {
   206  				break
   207  			}
   208  
   209  			if err == tcpip.ErrWouldBlock {
   210  				<-notifyCh
   211  				continue
   212  			}
   213  
   214  			log.Fatal("Read() failed:", err)
   215  		}
   216  
   217  		os.Stdout.Write(v)
   218  	}
   219  	wq.EventUnregister(&waitEntry)
   220  
   221  	// The reader has completed. Now wait for the writer as well.
   222  	<-writerCompletedCh
   223  
   224  	ep.Close()
   225  }