gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  //go:build linux
    16  // +build linux
    17  
    18  // This sample creates a stack with TCP and IPv4 protocols on top of a TUN
    19  // device, and connects to a peer. Similar to "nc <address> <port>". While the
    20  // sample is running, attempts to connect to its IPv4 address will result in
    21  // a RST segment.
    22  //
    23  // As an example of how to run it, a TUN device can be created and enabled on
    24  // a linux host as follows (this only needs to be done once per boot):
    25  //
    26  // [sudo] ip tuntap add user <username> mode tun <device-name>
    27  // [sudo] ip link set <device-name> up
    28  // [sudo] ip addr add <ipv4-address>/<mask-length> dev <device-name>
    29  //
    30  // A concrete example:
    31  //
    32  // $ sudo ip tuntap add user wedsonaf mode tun tun0
    33  // $ sudo ip link set tun0 up
    34  // $ sudo ip addr add 192.168.1.1/24 dev tun0
    35  //
    36  // Then one can run tun_tcp_connect as such:
    37  //
    38  // $ ./tun/tun_tcp_connect tun0 192.168.1.2 0 192.168.1.1 1234
    39  //
    40  // This will attempt to connect to the linux host's stack. One can run nc in
    41  // listen mode to accept a connect from tun_tcp_connect and exchange data.
    42  package main
    43  
    44  import (
    45  	"bytes"
    46  	"fmt"
    47  	"log"
    48  	"math/rand"
    49  	"net"
    50  	"os"
    51  	"strconv"
    52  	"time"
    53  
    54  	"gvisor.dev/gvisor/pkg/tcpip"
    55  	"gvisor.dev/gvisor/pkg/tcpip/header"
    56  	"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
    57  	"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
    58  	"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
    59  	"gvisor.dev/gvisor/pkg/tcpip/link/tun"
    60  	"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
    61  	"gvisor.dev/gvisor/pkg/tcpip/stack"
    62  	"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
    63  	"gvisor.dev/gvisor/pkg/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  	var b bytes.Buffer
    75  	if err := func() error {
    76  		for {
    77  			if _, err := b.ReadFrom(os.Stdin); err != nil {
    78  				return fmt.Errorf("b.ReadFrom failed: %w", err)
    79  			}
    80  
    81  			for b.Len() != 0 {
    82  				if _, err := ep.Write(&b, tcpip.WriteOptions{Atomic: true}); err != nil {
    83  					return fmt.Errorf("ep.Write failed: %s", err)
    84  				}
    85  			}
    86  		}
    87  	}(); err != nil {
    88  		fmt.Println(err)
    89  	}
    90  }
    91  
    92  func main() {
    93  	if len(os.Args) != 6 {
    94  		log.Fatal("Usage: ", os.Args[0], " <tun-device> <local-ipv4-address> <local-port> <remote-ipv4-address> <remote-port>")
    95  	}
    96  
    97  	tunName := os.Args[1]
    98  	addrName := os.Args[2]
    99  	portName := os.Args[3]
   100  	remoteAddrName := os.Args[4]
   101  	remotePortName := os.Args[5]
   102  
   103  	rand.Seed(time.Now().UnixNano())
   104  
   105  	addr := tcpip.AddrFromSlice(net.ParseIP(addrName).To4())
   106  	remote := tcpip.FullAddress{
   107  		NIC:  1,
   108  		Addr: tcpip.AddrFromSlice(net.ParseIP(remoteAddrName).To4()),
   109  	}
   110  
   111  	var localPort uint16
   112  	if v, err := strconv.Atoi(portName); err != nil {
   113  		log.Fatalf("Unable to convert port %v: %v", portName, err)
   114  	} else {
   115  		localPort = uint16(v)
   116  	}
   117  
   118  	if v, err := strconv.Atoi(remotePortName); err != nil {
   119  		log.Fatalf("Unable to convert port %v: %v", remotePortName, err)
   120  	} else {
   121  		remote.Port = uint16(v)
   122  	}
   123  
   124  	// Create the stack with ipv4 and tcp protocols, then add a tun-based
   125  	// NIC and ipv4 address.
   126  	s := stack.New(stack.Options{
   127  		NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol},
   128  		TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
   129  	})
   130  
   131  	mtu, err := rawfile.GetMTU(tunName)
   132  	if err != nil {
   133  		log.Fatal(err)
   134  	}
   135  
   136  	fd, err := tun.Open(tunName)
   137  	if err != nil {
   138  		log.Fatal(err)
   139  	}
   140  
   141  	linkEP, err := fdbased.New(&fdbased.Options{FDs: []int{fd}, MTU: mtu})
   142  	if err != nil {
   143  		log.Fatal(err)
   144  	}
   145  	if err := s.CreateNIC(1, sniffer.New(linkEP)); err != nil {
   146  		log.Fatal(err)
   147  	}
   148  
   149  	protocolAddr := tcpip.ProtocolAddress{
   150  		Protocol:          ipv4.ProtocolNumber,
   151  		AddressWithPrefix: addr.WithPrefix(),
   152  	}
   153  	if err := s.AddProtocolAddress(1, protocolAddr, stack.AddressProperties{}); err != nil {
   154  		log.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", 1, protocolAddr, err)
   155  	}
   156  
   157  	// Add default route.
   158  	s.SetRouteTable([]tcpip.Route{
   159  		{
   160  			Destination: header.IPv4EmptySubnet,
   161  			NIC:         1,
   162  		},
   163  	})
   164  
   165  	// Create TCP endpoint.
   166  	var wq waiter.Queue
   167  	ep, e := s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
   168  	if e != nil {
   169  		log.Fatal(e)
   170  	}
   171  
   172  	// Bind if a port is specified.
   173  	if localPort != 0 {
   174  		if err := ep.Bind(tcpip.FullAddress{Port: localPort}); err != nil {
   175  			log.Fatal("Bind failed: ", err)
   176  		}
   177  	}
   178  
   179  	// Issue connect request and wait for it to complete.
   180  	waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
   181  	wq.EventRegister(&waitEntry)
   182  	terr := ep.Connect(remote)
   183  	if _, ok := terr.(*tcpip.ErrConnectStarted); ok {
   184  		fmt.Println("Connect is pending...")
   185  		<-notifyCh
   186  		terr = ep.LastError()
   187  	}
   188  	wq.EventUnregister(&waitEntry)
   189  
   190  	if terr != nil {
   191  		log.Fatal("Unable to connect: ", terr)
   192  	}
   193  
   194  	fmt.Println("Connected")
   195  
   196  	// Start the writer in its own goroutine.
   197  	writerCompletedCh := make(chan struct{})
   198  	go writer(writerCompletedCh, ep) // S/R-SAFE: sample code.
   199  
   200  	// Read data and write to standard output until the peer closes the
   201  	// connection from its side.
   202  	waitEntry, notifyCh = waiter.NewChannelEntry(waiter.ReadableEvents)
   203  	wq.EventRegister(&waitEntry)
   204  	for {
   205  		_, err := ep.Read(os.Stdout, tcpip.ReadOptions{})
   206  		if err != nil {
   207  			if _, ok := err.(*tcpip.ErrClosedForReceive); ok {
   208  				break
   209  			}
   210  
   211  			if _, ok := err.(*tcpip.ErrWouldBlock); ok {
   212  				<-notifyCh
   213  				continue
   214  			}
   215  
   216  			log.Fatal("Read() failed:", err)
   217  		}
   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  }