github.com/labulakalia/water@v0.0.5-0.20231118024244-f351ca6784b6/README.md (about) 1 # water 2 3 `water` is a native Go library for [TUN/TAP](http://en.wikipedia.org/wiki/TUN/TAP) interfaces. 4 5 `water` is designed to be simple and efficient. It 6 7 * wraps almost only syscalls and uses only Go standard types; 8 * exposes standard interfaces; plays well with standard packages like `io`, `bufio`, etc.. 9 * does not handle memory management (allocating/destructing slice). It's up to user to decide whether/how to reuse buffers. 10 11 ~~`water/waterutil` has some useful functions to interpret MAC frame headers and IP packet headers. It also contains some constants such as protocol numbers and ethernet frame types.~~ 12 13 See https://github.com/songgao/packets for functions for parsing various packets. 14 15 ## Supported Platforms 16 17 * Linux 18 * Windows 19 * macOS (point-to-point TUN only) 20 21 ## Installation 22 ``` 23 go get -u github.com/labulakalia/water 24 go get -u github.com/labulakalia/water/waterutil 25 ``` 26 27 ## Documentation 28 [http://godoc.org/github.com/labulakalia/water](http://godoc.org/github.com/labulakalia/water) 29 30 ## Example 31 32 ### TAP on Linux: 33 34 ```go 35 package main 36 37 import ( 38 "log" 39 40 "github.com/songgao/packets/ethernet" 41 "github.com/labulakalia/water" 42 ) 43 44 func main() { 45 config := water.Config{ 46 DeviceType: water.TAP, 47 } 48 config.Name = "O_O" 49 50 ifce, err := water.New(config) 51 if err != nil { 52 log.Fatal(err) 53 } 54 var frame ethernet.Frame 55 56 for { 57 frame.Resize(1500) 58 n, err := ifce.Read([]byte(frame)) 59 if err != nil { 60 log.Fatal(err) 61 } 62 frame = frame[:n] 63 log.Printf("Dst: %s\n", frame.Destination()) 64 log.Printf("Src: %s\n", frame.Source()) 65 log.Printf("Ethertype: % x\n", frame.Ethertype()) 66 log.Printf("Payload: % x\n", frame.Payload()) 67 } 68 } 69 ``` 70 71 This piece of code creates a `TAP` interface, and prints some header information for every frame. After pull up the `main.go`, you'll need to bring up the interface and assign an IP address. All of these need root permission. 72 73 ```bash 74 sudo go run main.go 75 ``` 76 77 In a new terminal: 78 79 ```bash 80 sudo ip addr add 10.1.0.10/24 dev O_O 81 sudo ip link set dev O_O up 82 ``` 83 84 Wait until the output `main.go` terminal, try sending some ICMP broadcast message: 85 ```bash 86 ping -c1 -b 10.1.0.255 87 ``` 88 89 You'll see output containing the IPv4 ICMP frame: 90 ``` 91 2016/10/24 03:18:16 Dst: ff:ff:ff:ff:ff:ff 92 2016/10/24 03:18:16 Src: 72:3c:fc:29:1c:6f 93 2016/10/24 03:18:16 Ethertype: 08 00 94 2016/10/24 03:18:16 Payload: 45 00 00 54 00 00 40 00 40 01 25 9f 0a 01 00 0a 0a 01 00 ff 08 00 01 c1 08 49 00 01 78 7d 0d 58 00 00 00 00 a2 4c 07 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 95 ``` 96 97 ### TUN on macOS 98 99 ```go 100 package main 101 102 import ( 103 "log" 104 105 "github.com/labulakalia/water" 106 ) 107 108 func main() { 109 ifce, err := water.New(water.Config{ 110 DeviceType: water.TUN, 111 }) 112 if err != nil { 113 log.Fatal(err) 114 } 115 116 log.Printf("Interface Name: %s\n", ifce.Name()) 117 118 packet := make([]byte, 2000) 119 for { 120 n, err := ifce.Read(packet) 121 if err != nil { 122 log.Fatal(err) 123 } 124 log.Printf("Packet Received: % x\n", packet[:n]) 125 } 126 } 127 ``` 128 129 Run it! 130 131 ```bash 132 $ sudo go run main.go 133 ``` 134 135 This is a point-to-point only interface. Use `ifconfig` to see its attributes. You need to bring it up and assign IP addresses (apparently replace `utun2` if needed): 136 137 ```bash 138 $ sudo ifconfig utun2 10.1.0.10 10.1.0.20 up 139 ``` 140 141 Now send some ICMP packets to the interface: 142 143 ```bash 144 $ ping 10.1.0.20 145 ``` 146 147 You'd see the ICMP packets printed out: 148 149 ``` 150 2017/03/20 21:17:30 Interface Name: utun2 151 2017/03/20 21:17:40 Packet Received: 45 00 00 54 e9 1d 00 00 40 01 7d 6c 0a 01 00 0a 0a 01 00 14 08 00 ee 04 21 15 00 00 58 d0 a9 64 00 08 fb a5 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 152 ``` 153 154 #### Caveats 155 156 1. Only Point-to-Point user TUN devices are supported. TAP devices are *not* supported natively by macOS. 157 2. Custom interface names are not supported by macOS. Interface names are automatically generated serially, using the `utun<#>` naming convention. 158 159 ### TUN on Windows: 160 To use it with windows,you will need to download file [wintun.dll](https://www.wintun.net/) in running dir. 161 162 ```go 163 package main 164 165 import ( 166 "log" 167 168 "github.com/labulakalia/water" 169 ) 170 171 func main() { 172 ifce, err := water.New(water.Config{ 173 DeviceType: water.TUN, 174 }) 175 if err != nil { 176 log.Fatal(err) 177 } 178 179 log.Printf("Interface Name: %s\n", ifce.Name()) 180 181 packet := make([]byte, 2000) 182 for { 183 n, err := ifce.Read(packet) 184 if err != nil { 185 log.Fatal(err) 186 } 187 log.Printf("Packet Received: % x\n", packet[:n]) 188 } 189 } 190 ``` 191 ```dos 192 go run main.go 193 ``` 194 In a new cmd (admin right): 195 ```dos 196 # Replace with your device name, it can be achieved by ifce.Name(). 197 netsh interface ip set address name="Ehternet 2" source=static addr=10.1.0.10 mask=255.255.255.0 gateway=none 198 ``` 199 200 ```dos 201 ping 10.1.0.255 202 ``` 203 You'll see output containing the IPv4 ICMP frame same as the Linux version. 204 205 ### TAP on Windows: 206 207 To use it with windows, you will need to install a [tap driver](https://github.com/OpenVPN/tap-windows6), or [OpenVPN client](https://github.com/OpenVPN/openvpn) for windows. 208 209 It's compatible with the Linux code. 210 211 ```go 212 package main 213 214 import ( 215 "log" 216 217 "github.com/songgao/packets/ethernet" 218 "github.com/songgao/water" 219 ) 220 221 func main() { 222 ifce, err := water.New(water.Config{ 223 DeviceType: water.TAP, 224 }) 225 if err != nil { 226 log.Fatal(err) 227 } 228 var frame ethernet.Frame 229 230 for { 231 frame.Resize(1500) 232 n, err := ifce.Read([]byte(frame)) 233 if err != nil { 234 log.Fatal(err) 235 } 236 frame = frame[:n] 237 log.Printf("Dst: %s\n", frame.Destination()) 238 log.Printf("Src: %s\n", frame.Source()) 239 log.Printf("Ethertype: % x\n", frame.Ethertype()) 240 log.Printf("Payload: % x\n", frame.Payload()) 241 } 242 } 243 ``` 244 245 Same as Linux version, but you don't need to bring up the device by hand, the only thing you need is to assign an IP address to it. 246 247 ```dos 248 go run main.go 249 ``` 250 251 It will output a lot of lines because of some windows services and dhcp. 252 You will need admin right to assign IP. 253 254 In a new cmd (admin right): 255 256 ```dos 257 # Replace with your device name, it can be achieved by ifce.Name(). 258 netsh interface ip set address name="Ehternet 2" source=static addr=10.1.0.10 mask=255.255.255.0 gateway=none 259 ``` 260 261 The `main.go` terminal should be silenced after IP assignment, try sending some ICMP broadcast message: 262 263 ```dos 264 ping 10.1.0.255 265 ``` 266 267 You'll see output containing the IPv4 ICMP frame same as the Linux version. 268 269 #### Specifying interface name 270 271 If you are going to use multiple TAP devices on the Windows, there is a way to specify an interface name to select the exact device that you need: 272 273 ```go 274 ifce, err := water.New(water.Config{ 275 DeviceType: water.TAP, 276 PlatformSpecificParams: water.PlatformSpecificParams{ 277 ComponentID: "tap0901", 278 InterfaceName: "Ethernet 3", 279 Network: "192.168.1.10/24", 280 }, 281 }) 282 ``` 283 284 ## TODO 285 * tuntaposx for TAP on Darwin 286 287 ## Alternatives 288 `tuntap`: [https://code.google.com/p/tuntap/](https://code.google.com/p/tuntap/)