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 }