|
@@ -1,13 +1,21 @@
|
|
# water
|
|
# water
|
|
|
|
+
|
|
`water` is a native Go library for [TUN/TAP](http://en.wikipedia.org/wiki/TUN/TAP) interfaces.
|
|
`water` is a native Go library for [TUN/TAP](http://en.wikipedia.org/wiki/TUN/TAP) interfaces.
|
|
|
|
|
|
`water` is designed to be simple and efficient. It
|
|
`water` is designed to be simple and efficient. It
|
|
|
|
|
|
* wraps almost only syscalls and uses only Go standard types;
|
|
* wraps almost only syscalls and uses only Go standard types;
|
|
* exposes standard interfaces; plays well with standard packages like `io`, `bufio`, etc..
|
|
* exposes standard interfaces; plays well with standard packages like `io`, `bufio`, etc..
|
|
-* does not handle memory management (allocating/destructing slice). It's up to user to decide how to deal with buffers; whether to use GC.
|
|
|
|
|
|
+* does not handle memory management (allocating/destructing slice). It's up to user to decide whether/how to reuse buffers.
|
|
|
|
+
|
|
|
|
+~~`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.~~
|
|
|
|
+
|
|
|
|
+See https://github.com/songgao/packets for functions for parsing various packets.
|
|
|
|
|
|
-`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.
|
|
|
|
|
|
+## Supported Platforms
|
|
|
|
+
|
|
|
|
+* Linux
|
|
|
|
+* macOS (point-to-point TUN only)
|
|
|
|
|
|
## Installation
|
|
## Installation
|
|
```
|
|
```
|
|
@@ -18,79 +26,125 @@ go get -u github.com/songgao/water/waterutil
|
|
## Documentation
|
|
## Documentation
|
|
[http://godoc.org/github.com/songgao/water](http://godoc.org/github.com/songgao/water)
|
|
[http://godoc.org/github.com/songgao/water](http://godoc.org/github.com/songgao/water)
|
|
|
|
|
|
-[http://godoc.org/github.com/songgao/water/waterutil](http://godoc.org/github.com/songgao/water/waterutil)
|
|
|
|
-
|
|
|
|
## Example
|
|
## Example
|
|
|
|
|
|
|
|
+### TAP on Linux:
|
|
|
|
+
|
|
```go
|
|
```go
|
|
package main
|
|
package main
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
+ "log"
|
|
|
|
+
|
|
|
|
+ "github.com/songgao/packets/ethernet"
|
|
"github.com/songgao/water"
|
|
"github.com/songgao/water"
|
|
- "github.com/songgao/water/waterutil"
|
|
|
|
- "fmt"
|
|
|
|
)
|
|
)
|
|
|
|
|
|
-const BUFFERSIZE = 1522
|
|
|
|
-
|
|
|
|
func main() {
|
|
func main() {
|
|
- ifce, err := water.NewTAP("")
|
|
|
|
- fmt.Printf("%v, %v\n\n", err, ifce)
|
|
|
|
- buffer := make([]byte, BUFFERSIZE)
|
|
|
|
|
|
+ ifce, err := water.NewTAP("O_O")
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ var frame ethernet.Frame
|
|
|
|
+
|
|
for {
|
|
for {
|
|
- _, err = ifce.Read(buffer)
|
|
|
|
|
|
+ frame.Resize(1500)
|
|
|
|
+ n, err := ifce.Read([]byte(frame))
|
|
if err != nil {
|
|
if err != nil {
|
|
- break
|
|
|
|
- }
|
|
|
|
- ethertype := waterutil.MACEthertype(buffer)
|
|
|
|
- if ethertype == waterutil.IPv4 {
|
|
|
|
- packet := waterutil.MACPayload(buffer)
|
|
|
|
- if waterutil.IsIPv4(packet) {
|
|
|
|
- fmt.Printf("Source: %v [%v]\n", waterutil.MACSource(buffer), waterutil.IPv4Source(packet))
|
|
|
|
- fmt.Printf("Destination: %v [%v]\n", waterutil.MACDestination(buffer), waterutil.IPv4Destination(packet))
|
|
|
|
- fmt.Printf("Protocol: %v\n\n", waterutil.IPv4Protocol(packet))
|
|
|
|
- }
|
|
|
|
|
|
+ log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
+ frame = frame[:n]
|
|
|
|
+ log.Printf("Dst: %s\n", frame.Destination())
|
|
|
|
+ log.Printf("Src: %s\n", frame.Source())
|
|
|
|
+ log.Printf("Ethertype: % x\n", frame.Ethertype())
|
|
|
|
+ log.Printf("Payload: % x\n", frame.Payload())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
-This piece of code creates a `TAP` interface, and prints some header information for every IPv4 packet. After pull up the `main.go`, you'll need to bring up the interface and assign IP address. All of these need root permission.
|
|
|
|
|
|
+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.
|
|
|
|
|
|
```bash
|
|
```bash
|
|
sudo go run main.go
|
|
sudo go run main.go
|
|
```
|
|
```
|
|
|
|
|
|
|
|
+In a new terminal:
|
|
|
|
+
|
|
```bash
|
|
```bash
|
|
-sudo ip link set dev tap0 up
|
|
|
|
-sudo ip addr add 10.0.0.1/24 dev tap0
|
|
|
|
|
|
+sudo ip addr add 10.1.0.10/24 dev O_O
|
|
|
|
+sudo ip link set dev O_O up
|
|
```
|
|
```
|
|
|
|
|
|
-Now, try sending some ICMP broadcast message:
|
|
|
|
|
|
+Wait until the output `main.go` terminal, try sending some ICMP broadcast message:
|
|
```bash
|
|
```bash
|
|
-ping -b 10.0.0.255
|
|
|
|
|
|
+ping -c1 -b 10.1.0.255
|
|
```
|
|
```
|
|
|
|
|
|
-You'll see the `main.go` print something like:
|
|
|
|
|
|
+You'll see output containing the IPv4 ICMP frame:
|
|
```
|
|
```
|
|
-<nil>, &{true 0xf84003f058 tap0}
|
|
|
|
|
|
+2016/10/24 03:18:16 Dst: ff:ff:ff:ff:ff:ff
|
|
|
|
+2016/10/24 03:18:16 Src: 72:3c:fc:29:1c:6f
|
|
|
|
+2016/10/24 03:18:16 Ethertype: 08 00
|
|
|
|
+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
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+### TUN on macOS
|
|
|
|
+
|
|
|
|
+```go
|
|
|
|
+package main
|
|
|
|
|
|
-Source: 42:35:da:af:2b:00 [10.0.0.1]
|
|
|
|
-Destination: ff:ff:ff:ff:ff:ff [10.0.0.255]
|
|
|
|
-Protocol: 1
|
|
|
|
|
|
+import (
|
|
|
|
+ "log"
|
|
|
|
|
|
-Source: 42:35:da:af:2b:00 [10.0.0.1]
|
|
|
|
-Destination: ff:ff:ff:ff:ff:ff [10.0.0.255]
|
|
|
|
-Protocol: 1
|
|
|
|
|
|
+ "github.com/songgao/water"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+func main() {
|
|
|
|
+ ifce, err := water.NewTUN("")
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
|
|
-Source: 42:35:da:af:2b:00 [10.0.0.1]
|
|
|
|
-Destination: ff:ff:ff:ff:ff:ff [10.0.0.255]
|
|
|
|
-Protocol: 1
|
|
|
|
|
|
+ log.Printf("Interface Name: %s\n", ifce.Name())
|
|
|
|
+
|
|
|
|
+ packet := make([]byte, 2000)
|
|
|
|
+ for {
|
|
|
|
+ n, err := ifce.Read(packet)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ log.Printf("Packet Received: % x\n", packet[:n])
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Run it!
|
|
|
|
+
|
|
|
|
+```bash
|
|
|
|
+$ sudo go run main.go
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+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):
|
|
|
|
+
|
|
|
|
+```bash
|
|
|
|
+$ sudo ifconfig utun2 10.1.0.10 10.1.0.20 up
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+Now send some ICMP packets to the interface:
|
|
|
|
+
|
|
|
|
+```bash
|
|
|
|
+$ ping 10.1.0.20
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+You'd see the ICMP packets printed out:
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+2016/10/23 20:21:53 Interface Name: utun2
|
|
|
|
+2016/10/23 20:22:40 Packet Received: 00 00 00 02 45 00 00 54 4a 2e 00 00 40 01 1c 5c 0a 01 00 0a 0a 01 00 14 08 00 31 51 f0 f9 00 00 58 0d 7e 80 00 03 14 21 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
|
|
```
|
|
```
|
|
|
|
|
|
## TODO
|
|
## TODO
|
|
-* IPv6 Support in `waterutil`
|
|
|
|
-* Darwin(Mac) Support
|
|
|
|
|
|
+* tuntaposx for TAP on Darwin
|
|
|
|
|
|
## LICENSE
|
|
## LICENSE
|
|
[BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause)
|
|
[BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause)
|