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 }