github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/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  	"bytes"
    45  	"fmt"
    46  	"log"
    47  	"math/rand"
    48  	"net"
    49  	"os"
    50  	"strconv"
    51  	"time"
    52  
    53  	"github.com/SagerNet/gvisor/pkg/tcpip"
    54  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    55  	"github.com/SagerNet/gvisor/pkg/tcpip/link/fdbased"
    56  	"github.com/SagerNet/gvisor/pkg/tcpip/link/rawfile"
    57  	"github.com/SagerNet/gvisor/pkg/tcpip/link/sniffer"
    58  	"github.com/SagerNet/gvisor/pkg/tcpip/link/tun"
    59  	"github.com/SagerNet/gvisor/pkg/tcpip/network/ipv4"
    60  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    61  	"github.com/SagerNet/gvisor/pkg/tcpip/transport/tcp"
    62  	"github.com/SagerNet/gvisor/pkg/waiter"
    63  )
    64  
    65  // writer reads from standard input and writes to the endpoint until standard
    66  // input is closed. It signals that it's done by closing the provided channel.
    67  func writer(ch chan struct{}, ep tcpip.Endpoint) {
    68  	defer func() {
    69  		ep.Shutdown(tcpip.ShutdownWrite)
    70  		close(ch)
    71  	}()
    72  
    73  	var b bytes.Buffer
    74  	if err := func() error {
    75  		for {
    76  			if _, err := b.ReadFrom(os.Stdin); err != nil {
    77  				return fmt.Errorf("b.ReadFrom failed: %w", err)
    78  			}
    79  
    80  			for b.Len() != 0 {
    81  				if _, err := ep.Write(&b, tcpip.WriteOptions{Atomic: true}); err != nil {
    82  					return fmt.Errorf("ep.Write failed: %s", err)
    83  				}
    84  			}
    85  		}
    86  	}(); err != nil {
    87  		fmt.Println(err)
    88  	}
    89  }
    90  
    91  func main() {
    92  	if len(os.Args) != 6 {
    93  		log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-ipv4-address> <local-port> <remote-ipv4-address> <remote-port>")
    94  	}
    95  
    96  	tunName := os.Args[1]
    97  	addrName := os.Args[2]
    98  	portName := os.Args[3]
    99  	remoteAddrName := os.Args[4]
   100  	remotePortName := os.Args[5]
   101  
   102  	rand.Seed(time.Now().UnixNano())
   103  
   104  	addr := tcpip.Address(net.ParseIP(addrName).To4())
   105  	remote := tcpip.FullAddress{
   106  		NIC:  1,
   107  		Addr: tcpip.Address(net.ParseIP(remoteAddrName).To4()),
   108  	}
   109  
   110  	var localPort uint16
   111  	if v, err := strconv.Atoi(portName); err != nil {
   112  		log.Fatalf("Unable to convert port %v: %v", portName, err)
   113  	} else {
   114  		localPort = uint16(v)
   115  	}
   116  
   117  	if v, err := strconv.Atoi(remotePortName); err != nil {
   118  		log.Fatalf("Unable to convert port %v: %v", remotePortName, err)
   119  	} else {
   120  		remote.Port = uint16(v)
   121  	}
   122  
   123  	// Create the stack with ipv4 and tcp protocols, then add a tun-based
   124  	// NIC and ipv4 address.
   125  	s := stack.New(stack.Options{
   126  		NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol},
   127  		TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
   128  	})
   129  
   130  	mtu, err := rawfile.GetMTU(tunName)
   131  	if err != nil {
   132  		log.Fatal(err)
   133  	}
   134  
   135  	fd, err := tun.Open(tunName)
   136  	if err != nil {
   137  		log.Fatal(err)
   138  	}
   139  
   140  	linkEP, err := fdbased.New(&fdbased.Options{FDs: []int{fd}, MTU: mtu})
   141  	if err != nil {
   142  		log.Fatal(err)
   143  	}
   144  	if err := s.CreateNIC(1, sniffer.New(linkEP)); err != nil {
   145  		log.Fatal(err)
   146  	}
   147  
   148  	if err := s.AddAddress(1, ipv4.ProtocolNumber, addr); err != nil {
   149  		log.Fatal(err)
   150  	}
   151  
   152  	// Add default route.
   153  	s.SetRouteTable([]tcpip.Route{
   154  		{
   155  			Destination: header.IPv4EmptySubnet,
   156  			NIC:         1,
   157  		},
   158  	})
   159  
   160  	// Create TCP endpoint.
   161  	var wq waiter.Queue
   162  	ep, e := s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
   163  	if e != nil {
   164  		log.Fatal(e)
   165  	}
   166  
   167  	// Bind if a port is specified.
   168  	if localPort != 0 {
   169  		if err := ep.Bind(tcpip.FullAddress{0, "", localPort}); err != nil {
   170  			log.Fatal("Bind failed: ", err)
   171  		}
   172  	}
   173  
   174  	// Issue connect request and wait for it to complete.
   175  	waitEntry, notifyCh := waiter.NewChannelEntry(nil)
   176  	wq.EventRegister(&waitEntry, waiter.WritableEvents)
   177  	terr := ep.Connect(remote)
   178  	if _, ok := terr.(*tcpip.ErrConnectStarted); ok {
   179  		fmt.Println("Connect is pending...")
   180  		<-notifyCh
   181  		terr = ep.LastError()
   182  	}
   183  	wq.EventUnregister(&waitEntry)
   184  
   185  	if terr != nil {
   186  		log.Fatal("Unable to connect: ", terr)
   187  	}
   188  
   189  	fmt.Println("Connected")
   190  
   191  	// Start the writer in its own goroutine.
   192  	writerCompletedCh := make(chan struct{})
   193  	go writer(writerCompletedCh, ep) // S/R-SAFE: sample code.
   194  
   195  	// Read data and write to standard output until the peer closes the
   196  	// connection from its side.
   197  	wq.EventRegister(&waitEntry, waiter.ReadableEvents)
   198  	for {
   199  		_, err := ep.Read(os.Stdout, tcpip.ReadOptions{})
   200  		if err != nil {
   201  			if _, ok := err.(*tcpip.ErrClosedForReceive); ok {
   202  				break
   203  			}
   204  
   205  			if _, ok := err.(*tcpip.ErrWouldBlock); ok {
   206  				<-notifyCh
   207  				continue
   208  			}
   209  
   210  			log.Fatal("Read() failed:", err)
   211  		}
   212  	}
   213  	wq.EventUnregister(&waitEntry)
   214  
   215  	// The reader has completed. Now wait for the writer as well.
   216  	<-writerCompletedCh
   217  
   218  	ep.Close()
   219  }