├── .gitignore ├── .travis.yml ├── CONTRIBUTORS ├── LICENSE ├── Makefile ├── README.md ├── doc.go ├── if.go ├── if_linux.go ├── ipv4_darwin_test.go ├── ipv4_go1.11_test.go ├── ipv4_linux_test.go ├── ipv4_other_test.go ├── ipv4_test.go ├── ipv4_windows_test.go ├── params_darwin.go ├── params_linux.go ├── params_others.go ├── params_windows.go ├── syscalls_darwin.go ├── syscalls_darwin_go1.11.go ├── syscalls_darwin_legacy.go ├── syscalls_linux.go ├── syscalls_linux_go1.11.go ├── syscalls_linux_legacy.go ├── syscalls_other.go ├── syscalls_windows.go └── waterutil ├── doc.go ├── ethertypes.go ├── ip_protocols.go ├── tap.go ├── tun.go └── tun_ipv4.go /.gitignore: -------------------------------------------------------------------------------- 1 | water.test 2 | water.test.exe 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - "1.12.1" 4 | - "1.10.8" 5 | go_import_path: github.com/songgao/water 6 | install: go get -u golang.org/x/lint/golint 7 | script: make ci 8 | 9 | matrix: 10 | include: 11 | - os: linux 12 | dist: xenial 13 | - os: osx 14 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Song Gao 2 | Harshal Sheth 3 | KOJIMA Takanori 4 | Sean Purser-Haskell 5 | daregod 6 | Lucus Lee 7 | Arroyo Networks, LLC 8 | Tony Lu 9 | ajee cai 10 | yinheli 11 | Paul Querna 12 | Cuong Manh Le 13 | Neil Alexander 14 | Dmitry Shihovtsev 15 | Yifan Gu [https://github.com/gyf304] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Song Gao 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of water nor the names of its contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .phony: default ci test lint vet gofmt 2 | 3 | 4 | default: 5 | echo 'This make file is for CI.' 6 | exit 1 7 | 8 | ci: test lint vet gofmt 9 | 10 | test: water.test 11 | sudo ./water.test -test.v 12 | 13 | lint: 14 | golint -set_exit_status 15 | 16 | vet: 17 | go vet . 18 | 19 | gofmt: 20 | gofmt -s -e -l . 21 | 22 | water.test: *.go 23 | go test -c 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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 (experimental; APIs might change) 19 | * macOS (point-to-point TUN only) 20 | 21 | ## Installation 22 | ``` 23 | go get -u github.com/songgao/water 24 | go get -u github.com/songgao/water/waterutil 25 | ``` 26 | 27 | ## Documentation 28 | [http://godoc.org/github.com/songgao/water](http://godoc.org/github.com/songgao/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/songgao/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/songgao/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 | ### TAP on Windows: 160 | 161 | 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. 162 | 163 | It's compatible with the Linux code. 164 | 165 | ```go 166 | package main 167 | 168 | import ( 169 | "log" 170 | 171 | "github.com/songgao/packets/ethernet" 172 | "github.com/songgao/water" 173 | ) 174 | 175 | func main() { 176 | ifce, err := water.New(water.Config{ 177 | DeviceType: water.TAP, 178 | }) 179 | if err != nil { 180 | log.Fatal(err) 181 | } 182 | var frame ethernet.Frame 183 | 184 | for { 185 | frame.Resize(1500) 186 | n, err := ifce.Read([]byte(frame)) 187 | if err != nil { 188 | log.Fatal(err) 189 | } 190 | frame = frame[:n] 191 | log.Printf("Dst: %s\n", frame.Destination()) 192 | log.Printf("Src: %s\n", frame.Source()) 193 | log.Printf("Ethertype: % x\n", frame.Ethertype()) 194 | log.Printf("Payload: % x\n", frame.Payload()) 195 | } 196 | } 197 | ``` 198 | 199 | 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. 200 | 201 | ```dos 202 | go run main.go 203 | ``` 204 | 205 | It will output a lot of lines because of some windows services and dhcp. 206 | You will need admin right to assign IP. 207 | 208 | In a new cmd (admin right): 209 | 210 | ```dos 211 | # Replace with your device name, it can be achieved by ifce.Name(). 212 | netsh interface ip set address name="Ehternet 2" source=static addr=10.1.0.10 mask=255.255.255.0 gateway=none 213 | ``` 214 | 215 | The `main.go` terminal should be silenced after IP assignment, try sending some ICMP broadcast message: 216 | 217 | ```dos 218 | ping 10.1.0.255 219 | ``` 220 | 221 | You'll see output containing the IPv4 ICMP frame same as the Linux version. 222 | 223 | #### Specifying interface name 224 | 225 | 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: 226 | 227 | ```go 228 | ifce, err := water.New(water.Config{ 229 | DeviceType: water.TAP, 230 | PlatformSpecificParams: water.PlatformSpecificParams{ 231 | ComponentID: "tap0901", 232 | InterfaceName: "Ethernet 3", 233 | Network: "192.168.1.10/24", 234 | }, 235 | }) 236 | ``` 237 | 238 | ## TODO 239 | * tuntaposx for TAP on Darwin 240 | 241 | ## Alternatives 242 | `tuntap`: [https://code.google.com/p/tuntap/](https://code.google.com/p/tuntap/) 243 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package water is a simple TUN/TAP interface library that efficiently works 2 | // with standard packages like io, bufio, etc.. Use waterutil with it to work 3 | // with TUN/TAP packets/frames. 4 | package water 5 | -------------------------------------------------------------------------------- /if.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | ) 7 | 8 | // Interface is a TUN/TAP interface. 9 | // 10 | // MultiQueue(Linux kernel > 3.8): With MultiQueue enabled, user should hold multiple 11 | // interfaces to send/receive packet in parallel. 12 | // Kernel document about MultiQueue: https://www.kernel.org/doc/Documentation/networking/tuntap.txt 13 | type Interface struct { 14 | isTAP bool 15 | io.ReadWriteCloser 16 | name string 17 | } 18 | 19 | // DeviceType is the type for specifying device types. 20 | type DeviceType int 21 | 22 | // TUN and TAP device types. 23 | const ( 24 | _ = iota 25 | TUN 26 | TAP 27 | ) 28 | 29 | // Config defines parameters required to create a TUN/TAP interface. It's only 30 | // used when the device is initialized. A zero-value Config is a valid 31 | // configuration. 32 | type Config struct { 33 | // DeviceType specifies whether the device is a TUN or TAP interface. A 34 | // zero-value is treated as TUN. 35 | DeviceType DeviceType 36 | 37 | // PlatformSpecificParams defines parameters that differ on different 38 | // platforms. See comments for the type for more details. 39 | PlatformSpecificParams 40 | } 41 | 42 | func defaultConfig() Config { 43 | return Config{ 44 | DeviceType: TUN, 45 | PlatformSpecificParams: defaultPlatformSpecificParams(), 46 | } 47 | } 48 | 49 | var zeroConfig Config 50 | 51 | // New creates a new TUN/TAP interface using config. 52 | func New(config Config) (ifce *Interface, err error) { 53 | if zeroConfig == config { 54 | config = defaultConfig() 55 | } 56 | if config.PlatformSpecificParams == zeroConfig.PlatformSpecificParams { 57 | config.PlatformSpecificParams = defaultPlatformSpecificParams() 58 | } 59 | switch config.DeviceType { 60 | case TUN, TAP: 61 | return openDev(config) 62 | default: 63 | return nil, errors.New("unknown device type") 64 | } 65 | } 66 | 67 | // IsTUN returns true if ifce is a TUN interface. 68 | func (ifce *Interface) IsTUN() bool { 69 | return !ifce.isTAP 70 | } 71 | 72 | // IsTAP returns true if ifce is a TAP interface. 73 | func (ifce *Interface) IsTAP() bool { 74 | return ifce.isTAP 75 | } 76 | 77 | // Name returns the interface name of ifce, e.g. tun0, tap1, tun0, etc.. 78 | func (ifce *Interface) Name() string { 79 | return ifce.name 80 | } 81 | -------------------------------------------------------------------------------- /if_linux.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // NewTAP creates a new TAP interface whose name is ifName. If ifName is empty, a 8 | // default name (tap0, tap1, ... ) will be assigned. ifName should not exceed 9 | // 16 bytes. TAP interfaces are not supported on darwin. 10 | // ifName cannot be specified on windows, you will need ifce.Name() to use some cmds. 11 | // 12 | // Deprecated: This function may be removed in the future. Please use New() instead. 13 | func NewTAP(ifName string) (ifce *Interface, err error) { 14 | fmt.Println("Deprecated: NewTAP(..) may be removed in the future. Please use New() instead.") 15 | config := Config{DeviceType: TAP} 16 | config.Name = ifName 17 | return openDev(config) 18 | } 19 | 20 | // NewTUN creates a new TUN interface whose name is ifName. If ifName is empty, a 21 | // default name (tap0, tap1, ... ) will be assigned. ifName should not exceed 22 | // ifName cannot be specified on windows, you will need ifce.Name() to use some cmds. 23 | // 24 | // Deprecated: This function will be removed in the future. Please use New() instead. 25 | func NewTUN(ifName string) (ifce *Interface, err error) { 26 | fmt.Println("Deprecated: NewTUN(..) may be removed in the future. Please use New() instead.") 27 | config := Config{DeviceType: TUN} 28 | config.Name = ifName 29 | return openDev(config) 30 | } 31 | -------------------------------------------------------------------------------- /ipv4_darwin_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "net" 5 | "os/exec" 6 | "testing" 7 | ) 8 | 9 | func startPing(t *testing.T, dst net.IP, dashB bool) { 10 | params := []string{"-c", "4", dst.String()} 11 | if dashB { 12 | params = append([]string{"-b"}, params...) 13 | } 14 | if err := exec.Command("ping", params...).Start(); err != nil { 15 | t.Fatal(err) 16 | } 17 | } 18 | 19 | func setupIfce(t *testing.T, self net.IP, remote net.IP, dev string) { 20 | if err := exec.Command("ifconfig", dev, "inet", self.String(), remote.String(), "up").Run(); err != nil { 21 | t.Fatal(err) 22 | } 23 | } 24 | 25 | func teardownIfce(t *testing.T, ifce *Interface) { 26 | if err := ifce.Close(); err != nil { 27 | t.Fatal(err) 28 | } 29 | if err := exec.Command("ifconfig", ifce.Name(), "down").Run(); err != nil { 30 | t.Fatal(err) 31 | } 32 | } 33 | 34 | func TestP2PTUN(t *testing.T) { 35 | var ( 36 | self = net.IPv4(10, 0, 42, 1) 37 | remote = net.IPv4(10, 0, 42, 2) 38 | ) 39 | 40 | ifce, err := New(Config{DeviceType: TUN}) 41 | if err != nil { 42 | t.Fatalf("creating TUN error: %v\n", err) 43 | } 44 | defer teardownIfce(t, ifce) 45 | 46 | dataCh, errCh := startRead(t, ifce) 47 | 48 | setupIfce(t, self, remote, ifce.Name()) 49 | startPing(t, remote, false) 50 | 51 | waitForPingOrBust(t, false, false, self, remote, dataCh, errCh) 52 | } 53 | -------------------------------------------------------------------------------- /ipv4_go1.11_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.11 2 | 3 | package water 4 | 5 | import ( 6 | "context" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestCloseUnblockPendingRead(t *testing.T) { 12 | ifce, err := New(Config{DeviceType: TUN}) 13 | if err != nil { 14 | t.Fatalf("creating TUN error: %v\n", err) 15 | } 16 | 17 | c := make(chan struct{}) 18 | go func() { 19 | ifce.Read(make([]byte, 1<<16)) 20 | close(c) 21 | }() 22 | 23 | // make sure ifce.Close() happens after ifce.Read() blocks 24 | time.Sleep(1 * time.Second) 25 | 26 | ifce.Close() 27 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 28 | defer cancel() 29 | 30 | select { 31 | case <-c: 32 | t.Log("Pending Read unblocked") 33 | case <-ctx.Done(): 34 | t.Fatal("Timeouted, pending read blocked") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ipv4_linux_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "net" 5 | "os/exec" 6 | "testing" 7 | ) 8 | 9 | func startPing(t *testing.T, dst net.IP, dashB bool) { 10 | params := []string{"-c", "4", dst.String()} 11 | if dashB { 12 | params = append([]string{"-b"}, params...) 13 | } 14 | if err := exec.Command("ping", params...).Start(); err != nil { 15 | t.Fatal(err) 16 | } 17 | } 18 | 19 | func setupIfce(t *testing.T, ipNet net.IPNet, dev string) { 20 | if err := exec.Command("ip", "link", "set", dev, "up").Run(); err != nil { 21 | t.Fatal(err) 22 | } 23 | if err := exec.Command("ip", "addr", "add", ipNet.String(), "dev", dev).Run(); err != nil { 24 | t.Fatal(err) 25 | } 26 | } 27 | 28 | func teardownIfce(t *testing.T, ifce *Interface) { 29 | if err := ifce.Close(); err != nil { 30 | t.Fatal(err) 31 | } 32 | if err := exec.Command("ip", "link", "set", ifce.Name(), "down").Run(); err != nil { 33 | t.Fatal(err) 34 | } 35 | } 36 | 37 | func TestBroadcastTAP(t *testing.T) { 38 | var ( 39 | self = net.IPv4(10, 0, 42, 1) 40 | mask = net.IPv4Mask(255, 255, 255, 0) 41 | brd = net.IPv4(10, 0, 42, 255) 42 | ) 43 | 44 | ifce, err := New(Config{DeviceType: TAP}) 45 | if err != nil { 46 | t.Fatalf("creating TAP error: %v\n", err) 47 | } 48 | defer teardownIfce(t, ifce) 49 | 50 | dataCh, errCh := startRead(t, ifce) 51 | 52 | setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name()) 53 | startPing(t, brd, true) 54 | 55 | waitForPingOrBust(t, true, true, self, brd, dataCh, errCh) 56 | } 57 | 58 | func TestBroadcastTUN(t *testing.T) { 59 | var ( 60 | self = net.IPv4(10, 0, 42, 1) 61 | mask = net.IPv4Mask(255, 255, 255, 0) 62 | brd = net.IPv4(10, 0, 42, 255) 63 | ) 64 | 65 | ifce, err := New(Config{DeviceType: TUN}) 66 | if err != nil { 67 | t.Fatalf("creating TUN error: %v\n", err) 68 | } 69 | defer teardownIfce(t, ifce) 70 | 71 | dataCh, errCh := startRead(t, ifce) 72 | 73 | setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name()) 74 | startPing(t, brd, true) 75 | 76 | waitForPingOrBust(t, false, true, self, brd, dataCh, errCh) 77 | } 78 | 79 | func TestUnicastTUN(t *testing.T) { 80 | var ( 81 | self = net.IPv4(10, 0, 42, 1) 82 | mask = net.IPv4Mask(255, 255, 255, 0) 83 | remote = net.IPv4(10, 0, 42, 2) 84 | ) 85 | 86 | ifce, err := New(Config{DeviceType: TUN}) 87 | if err != nil { 88 | t.Fatalf("creating TUN error: %v\n", err) 89 | } 90 | defer teardownIfce(t, ifce) 91 | 92 | dataCh, errCh := startRead(t, ifce) 93 | 94 | setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name()) 95 | startPing(t, remote, false) 96 | 97 | waitForPingOrBust(t, false, false, self, remote, dataCh, errCh) 98 | } 99 | -------------------------------------------------------------------------------- /ipv4_other_test.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!windows,!darwin 2 | 3 | package water 4 | 5 | import ( 6 | "net" 7 | "testing" 8 | ) 9 | 10 | func setupIfce(t *testing.T, ipNet net.IPNet, dev string) { 11 | t.Fatal("unsupported platform") 12 | } 13 | -------------------------------------------------------------------------------- /ipv4_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/songgao/water/waterutil" 9 | ) 10 | 11 | const BUFFERSIZE = 1522 12 | 13 | func startRead(t *testing.T, ifce *Interface) (dataChan <-chan []byte, errChan <-chan error) { 14 | dataCh := make(chan []byte) 15 | errCh := make(chan error) 16 | go func() { 17 | for { 18 | buffer := make([]byte, BUFFERSIZE) 19 | n, err := ifce.Read(buffer) 20 | if err != nil { 21 | errCh <- err 22 | } else { 23 | buffer = buffer[:n:n] 24 | dataCh <- buffer 25 | } 26 | } 27 | }() 28 | return dataCh, errCh 29 | } 30 | 31 | func waitForPingOrBust(t *testing.T, 32 | isTAP bool, 33 | expectBroadcast bool, 34 | expectSrc net.IP, 35 | expectDest net.IP, 36 | dataCh <-chan []byte, errCh <-chan error) { 37 | waitForPintTimeout := time.NewTimer(8 * time.Second).C 38 | readFrame: 39 | for { 40 | select { 41 | case buffer := <-dataCh: 42 | var packet []byte 43 | if isTAP { 44 | ethertype := waterutil.MACEthertype(buffer) 45 | if ethertype != waterutil.IPv4 { 46 | continue readFrame 47 | } 48 | if expectBroadcast && !waterutil.IsBroadcast(waterutil.MACDestination(buffer)) { 49 | continue readFrame 50 | } 51 | packet = waterutil.MACPayload(buffer) 52 | } else { 53 | packet = buffer 54 | } 55 | if !waterutil.IsIPv4(packet) { 56 | continue readFrame 57 | } 58 | if !waterutil.IPv4Source(packet).Equal(expectSrc) { 59 | continue readFrame 60 | } 61 | if !waterutil.IPv4Destination(packet).Equal(expectDest) { 62 | continue readFrame 63 | } 64 | if waterutil.IPv4Protocol(packet) != waterutil.ICMP { 65 | continue readFrame 66 | } 67 | t.Logf("received broadcast frame: %#v\n", buffer) 68 | break readFrame 69 | case err := <-errCh: 70 | t.Fatalf("read error: %v", err) 71 | case <-waitForPintTimeout: 72 | t.Fatal("Waiting for broadcast packet timeout") 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ipv4_windows_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os/exec" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func startPing(t *testing.T, dst net.IP, _ bool) { 12 | if err := exec.Command("ping", "-n", "4", dst.String()).Start(); err != nil { 13 | t.Fatal(err) 14 | } 15 | } 16 | 17 | func setupIfce(t *testing.T, ipNet net.IPNet, dev string) { 18 | sargs := fmt.Sprintf("interface ip set address name=REPLACE_ME source=static addr=REPLACE_ME mask=REPLACE_ME gateway=none") 19 | args := strings.Split(sargs, " ") 20 | args[4] = fmt.Sprintf("name=%s", dev) 21 | args[6] = fmt.Sprintf("addr=%s", ipNet.IP) 22 | args[7] = fmt.Sprintf("mask=%d.%d.%d.%d", ipNet.Mask[0], ipNet.Mask[1], ipNet.Mask[2], ipNet.Mask[3]) 23 | cmd := exec.Command("netsh", args...) 24 | if err := cmd.Run(); err != nil { 25 | t.Fatal(err) 26 | } 27 | } 28 | 29 | func teardownIfce(t *testing.T, ifce *Interface) { 30 | if err := ifce.Close(); err != nil { 31 | t.Fatal(err) 32 | } 33 | } 34 | 35 | func TestBroadcastTAP(t *testing.T) { 36 | var ( 37 | self = net.IPv4(10, 0, 42, 1) 38 | mask = net.IPv4Mask(255, 255, 255, 0) 39 | brd = net.IPv4(10, 0, 42, 255) 40 | ) 41 | 42 | ifce, err := New(Config{DeviceType: TAP}) 43 | if err != nil { 44 | t.Fatalf("creating TAP error: %v\n", err) 45 | } 46 | defer teardownIfce(t, ifce) 47 | 48 | dataCh, errCh := startRead(t, ifce) 49 | 50 | setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name()) 51 | startPing(t, brd, true) 52 | 53 | waitForPingOrBust(t, true, true, self, brd, dataCh, errCh) 54 | } 55 | -------------------------------------------------------------------------------- /params_darwin.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | // MacOSDriverProvider enumerates possible MacOS TUN/TAP implementations 4 | type MacOSDriverProvider int 5 | 6 | const ( 7 | // MacOSDriverSystem refers to the default P2P driver 8 | MacOSDriverSystem MacOSDriverProvider = 0 9 | // MacOSDriverTunTapOSX refers to the third-party tuntaposx driver 10 | // see https://sourceforge.net/p/tuntaposx 11 | MacOSDriverTunTapOSX MacOSDriverProvider = 1 12 | ) 13 | 14 | // PlatformSpecificParams defines parameters in Config that are specific to 15 | // macOS. A zero-value of such type is valid, yielding an interface 16 | // with OS defined name. 17 | // Currently it is not possible to set the interface name in macOS. 18 | type PlatformSpecificParams struct { 19 | // Name is the name for the interface to be used. 20 | // 21 | // For TunTapOSXDriver, it should be something like "tap0". 22 | // For SystemDriver, the name should match `utun[0-9]+`, e.g. utun233 23 | Name string 24 | 25 | // Driver should be set if an alternative driver is desired 26 | // e.g. TunTapOSXDriver 27 | Driver MacOSDriverProvider 28 | } 29 | 30 | func defaultPlatformSpecificParams() PlatformSpecificParams { 31 | return PlatformSpecificParams{} 32 | } 33 | -------------------------------------------------------------------------------- /params_linux.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | // DevicePermissions determines the owner and group owner for the newly created 4 | // interface. 5 | type DevicePermissions struct { 6 | // Owner is the ID of the user which will be granted ownership of the 7 | // device. If set to a negative value, the owner value will not be 8 | // changed. By default, Linux sets the owner to -1, which allows any user. 9 | Owner uint 10 | 11 | // Group is the ID of the group which will be granted access to the device. 12 | // If set to a negative value, the group value will not be changed. By 13 | // default, Linux sets the group to -1, which allows any group. 14 | Group uint 15 | } 16 | 17 | // PlatformSpecificParams defines parameters in Config that are specific to 18 | // Linux. A zero-value of such type is valid, yielding an interface 19 | // with OS defined name. 20 | type PlatformSpecificParams struct { 21 | // Name is the name to be set for the interface to be created. This overrides 22 | // the default name assigned by OS such as tap0 or tun0. A zero-value of this 23 | // field, i.e. an empty string, indicates that the default name should be 24 | // used. 25 | Name string 26 | 27 | // Persist specifies whether persistence mode for the interface device 28 | // should be enabled or disabled. 29 | Persist bool 30 | 31 | // Permissions, if non-nil, specifies the owner and group owner for the 32 | // interface. A zero-value of this field, i.e. nil, indicates that no 33 | // changes to owner or group will be made. 34 | Permissions *DevicePermissions 35 | 36 | // MultiQueue specifies whether the multiqueue flag should be set on the 37 | // interface. From version 3.8, Linux supports multiqueue tuntap which can 38 | // uses multiple file descriptors (queues) to parallelize packets sending 39 | // or receiving. 40 | MultiQueue bool 41 | } 42 | 43 | func defaultPlatformSpecificParams() PlatformSpecificParams { 44 | return PlatformSpecificParams{} 45 | } 46 | -------------------------------------------------------------------------------- /params_others.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!darwin,!windows 2 | 3 | package water 4 | 5 | // PlatformSpeficParams 6 | type PlatformSpecificParams struct { 7 | } 8 | 9 | func defaultPlatformSpecificParams() PlatformSpecificParams { 10 | return PlatformSpecificParams{} 11 | } 12 | -------------------------------------------------------------------------------- /params_windows.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | // PlatformSpecificParams defines parameters in Config that are specific to 4 | // Windows. A zero-value of such type is valid. 5 | type PlatformSpecificParams struct { 6 | // ComponentID associates with the virtual adapter that exists in Windows. 7 | // This is usually configured when driver for the adapter is installed. A 8 | // zero-value of this field, i.e., an empty string, causes the interface to 9 | // use the default ComponentId. The default ComponentId is set to tap0901, 10 | // the one used by OpenVPN. 11 | ComponentID string 12 | // InterfaceName is a friendly name of the network adapter as set in Control Panel. 13 | // Of course, you may have multiple tap0901 adapters on the system, in which 14 | // case we need a friendlier way to identify them. 15 | InterfaceName string 16 | // Network is required when creating a TUN interface. The library will call 17 | // net.ParseCIDR() to parse this string into LocalIP, RemoteNetaddr, 18 | // RemoteNetmask. The underlying driver will need those to generate ARP 19 | // response to Windows kernel, to emulate an TUN interface. 20 | // Please note that it cannot perceive the IP changes caused by DHCP, user 21 | // configuration to the adapter and etc,. If IP changed, please reconfigure 22 | // the adapter using syscall, just like openDev(). 23 | // For detail, please refer 24 | // https://github.com/OpenVPN/tap-windows6/blob/master/src/device.c#L431 25 | // and https://github.com/songgao/water/pull/13#issuecomment-270341777 26 | Network string 27 | } 28 | 29 | func defaultPlatformSpecificParams() PlatformSpecificParams { 30 | return PlatformSpecificParams{ 31 | ComponentID: "tap0901", 32 | Network: "192.168.1.10/24", 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /syscalls_darwin.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "math" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | const appleUTUNCtl = "com.apple.net.utun_control" 17 | 18 | /* 19 | * From ioctl.h: 20 | * #define IOCPARM_MASK 0x1fff // parameter length, at most 13 bits 21 | * ... 22 | * #define IOC_OUT 0x40000000 // copy out parameters 23 | * #define IOC_IN 0x80000000 // copy in parameters 24 | * #define IOC_INOUT (IOC_IN|IOC_OUT) 25 | * ... 26 | * #define _IOC(inout,group,num,len) \ 27 | * (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) 28 | * ... 29 | * #define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) 30 | * 31 | * From kern_control.h: 32 | * #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) // get id from name 33 | * 34 | */ 35 | 36 | const appleCTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 37 | 38 | /* 39 | * #define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) 40 | * #define TUNSIFMODE _IOW('t', 94, int) 41 | */ 42 | const appleTUNSIFMODE = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 94 43 | 44 | /* 45 | * struct sockaddr_ctl { 46 | * u_char sc_len; // depends on size of bundle ID string 47 | * u_char sc_family; // AF_SYSTEM 48 | * u_int16_t ss_sysaddr; // AF_SYS_KERNCONTROL 49 | * u_int32_t sc_id; // Controller unique identifier 50 | * u_int32_t sc_unit; // Developer private unit number 51 | * u_int32_t sc_reserved[5]; 52 | * }; 53 | */ 54 | type sockaddrCtl struct { 55 | scLen uint8 56 | scFamily uint8 57 | ssSysaddr uint16 58 | scID uint32 59 | scUnit uint32 60 | scReserved [5]uint32 61 | } 62 | 63 | var sockaddrCtlSize uintptr = 32 64 | 65 | func openDev(config Config) (ifce *Interface, err error) { 66 | if config.Driver == MacOSDriverTunTapOSX { 67 | return openDevTunTapOSX(config) 68 | } 69 | if config.Driver == MacOSDriverSystem { 70 | return openDevSystem(config) 71 | } 72 | return nil, errors.New("unrecognized driver") 73 | } 74 | 75 | // openDevSystem opens tun device on system 76 | func openDevSystem(config Config) (ifce *Interface, err error) { 77 | if config.DeviceType != TUN { 78 | return nil, errors.New("only tun is implemented for SystemDriver, use TunTapOSXDriver for tap") 79 | } 80 | 81 | ifIndex := -1 82 | if config.Name != "" { 83 | const utunPrefix = "utun" 84 | if !strings.HasPrefix(config.Name, utunPrefix) { 85 | return nil, fmt.Errorf("Interface name must be utun[0-9]+") 86 | } 87 | ifIndex, err = strconv.Atoi(config.Name[len(utunPrefix):]) 88 | if err != nil || ifIndex < 0 || ifIndex > math.MaxUint32-1 { 89 | return nil, fmt.Errorf("Interface name must be utun[0-9]+") 90 | } 91 | } 92 | 93 | var fd int 94 | // Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ... 95 | // 96 | // In sys/socket.h: 97 | // #define PF_SYSTEM AF_SYSTEM 98 | // 99 | // In sys/sys_domain.h: 100 | // #define SYSPROTO_CONTROL 2 /* kernel control protocol */ 101 | if fd, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil { 102 | return nil, fmt.Errorf("error in syscall.Socket: %v", err) 103 | } 104 | 105 | var ctlInfo = &struct { 106 | ctlID uint32 107 | ctlName [96]byte 108 | }{} 109 | copy(ctlInfo.ctlName[:], []byte(appleUTUNCtl)) 110 | 111 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(appleCTLIOCGINFO), uintptr(unsafe.Pointer(ctlInfo))); errno != 0 { 112 | err = errno 113 | return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 114 | } 115 | 116 | addrP := unsafe.Pointer(&sockaddrCtl{ 117 | scLen: uint8(sockaddrCtlSize), 118 | scFamily: syscall.AF_SYSTEM, 119 | 120 | /* #define AF_SYS_CONTROL 2 */ 121 | ssSysaddr: 2, 122 | 123 | scID: ctlInfo.ctlID, 124 | scUnit: uint32(ifIndex) + 1, 125 | }) 126 | if _, _, errno := syscall.RawSyscall(syscall.SYS_CONNECT, uintptr(fd), uintptr(addrP), uintptr(sockaddrCtlSize)); errno != 0 { 127 | err = errno 128 | return nil, fmt.Errorf("error in syscall.RawSyscall(syscall.SYS_CONNECT, ...): %v", err) 129 | } 130 | 131 | var ifName struct { 132 | name [16]byte 133 | } 134 | ifNameSize := uintptr(16) 135 | if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), 136 | 2, /* #define SYSPROTO_CONTROL 2 */ 137 | 2, /* #define UTUN_OPT_IFNAME 2 */ 138 | uintptr(unsafe.Pointer(&ifName)), 139 | uintptr(unsafe.Pointer(&ifNameSize)), 0); errno != 0 { 140 | err = errno 141 | return nil, fmt.Errorf("error in syscall.Syscall6(syscall.SYS_GETSOCKOPT, ...): %v", err) 142 | } 143 | 144 | if err = setNonBlock(fd); err != nil { 145 | return nil, fmt.Errorf("setting non-blocking error") 146 | } 147 | 148 | return &Interface{ 149 | isTAP: false, 150 | name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]), 151 | ReadWriteCloser: &tunReadCloser{ 152 | f: os.NewFile(uintptr(fd), string(ifName.name[:])), 153 | }, 154 | }, nil 155 | } 156 | 157 | // openDevTunTapOSX opens tun / tap device, assuming tuntaposx is installed 158 | func openDevTunTapOSX(config Config) (ifce *Interface, err error) { 159 | var fd int 160 | var socketFD int 161 | 162 | if config.DeviceType == TAP && !strings.HasPrefix(config.Name, "tap") { 163 | return nil, errors.New("device name does not start with tap when creating a tap device") 164 | } 165 | if config.DeviceType == TUN && !strings.HasPrefix(config.Name, "tun") { 166 | return nil, errors.New("device name does not start with tun when creating a tun device") 167 | } 168 | if config.DeviceType != TAP && config.DeviceType != TUN { 169 | return nil, errors.New("unsupported DeviceType") 170 | } 171 | if len(config.Name) >= 15 { 172 | return nil, errors.New("device name is too long") 173 | } 174 | 175 | if fd, err = syscall.Open( 176 | "/dev/"+config.Name, os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { 177 | return nil, err 178 | } 179 | // Note that we are not setting NONBLOCK on the fd itself since it breaks tuntaposx 180 | // see https://sourceforge.net/p/tuntaposx/bugs/6/ 181 | 182 | // create socket so we can do SIO ioctls, we are not using it afterwards 183 | if socketFD, err = syscall.Socket(syscall.AF_SYSTEM, syscall.SOCK_DGRAM, 2); err != nil { 184 | return nil, fmt.Errorf("error in syscall.Socket: %v", err) 185 | } 186 | var ifReq = &struct { 187 | ifName [16]byte 188 | ifruFlags int16 189 | pad [16]byte 190 | }{} 191 | copy(ifReq.ifName[:], []byte(config.Name)) 192 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCGIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { 193 | err = errno 194 | return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 195 | } 196 | ifReq.ifruFlags |= syscall.IFF_RUNNING | syscall.IFF_UP 197 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(socketFD), uintptr(syscall.SIOCSIFFLAGS), uintptr(unsafe.Pointer(ifReq))); errno != 0 { 198 | err = errno 199 | return nil, fmt.Errorf("error in syscall.Syscall(syscall.SYS_IOCTL, ...): %v", err) 200 | } 201 | syscall.Close(socketFD) 202 | 203 | return &Interface{ 204 | isTAP: config.DeviceType == TAP, 205 | ReadWriteCloser: os.NewFile(uintptr(fd), "tun"), 206 | name: config.Name, 207 | }, nil 208 | } 209 | 210 | // tunReadCloser is a hack to work around the first 4 bytes "packet 211 | // information" because there doesn't seem to be an IFF_NO_PI for darwin. 212 | type tunReadCloser struct { 213 | f io.ReadWriteCloser 214 | 215 | rMu sync.Mutex 216 | rBuf []byte 217 | 218 | wMu sync.Mutex 219 | wBuf []byte 220 | } 221 | 222 | var _ io.ReadWriteCloser = (*tunReadCloser)(nil) 223 | 224 | func (t *tunReadCloser) Read(to []byte) (int, error) { 225 | t.rMu.Lock() 226 | defer t.rMu.Unlock() 227 | 228 | if cap(t.rBuf) < len(to)+4 { 229 | t.rBuf = make([]byte, len(to)+4) 230 | } 231 | t.rBuf = t.rBuf[:len(to)+4] 232 | 233 | n, err := t.f.Read(t.rBuf) 234 | copy(to, t.rBuf[4:]) 235 | return n - 4, err 236 | } 237 | 238 | func (t *tunReadCloser) Write(from []byte) (int, error) { 239 | 240 | if len(from) == 0 { 241 | return 0, syscall.EIO 242 | } 243 | 244 | t.wMu.Lock() 245 | defer t.wMu.Unlock() 246 | 247 | if cap(t.wBuf) < len(from)+4 { 248 | t.wBuf = make([]byte, len(from)+4) 249 | } 250 | t.wBuf = t.wBuf[:len(from)+4] 251 | 252 | // Determine the IP Family for the NULL L2 Header 253 | ipVer := from[0] >> 4 254 | if ipVer == 4 { 255 | t.wBuf[3] = syscall.AF_INET 256 | } else if ipVer == 6 { 257 | t.wBuf[3] = syscall.AF_INET6 258 | } else { 259 | return 0, errors.New("Unable to determine IP version from packet") 260 | } 261 | 262 | copy(t.wBuf[4:], from) 263 | 264 | n, err := t.f.Write(t.wBuf) 265 | return n - 4, err 266 | } 267 | 268 | func (t *tunReadCloser) Close() error { 269 | return t.f.Close() 270 | } 271 | -------------------------------------------------------------------------------- /syscalls_darwin_go1.11.go: -------------------------------------------------------------------------------- 1 | // +build darwin,go1.11 2 | 3 | package water 4 | 5 | import "syscall" 6 | 7 | func setNonBlock(fd int) error { 8 | return syscall.SetNonblock(fd, true) 9 | } 10 | -------------------------------------------------------------------------------- /syscalls_darwin_legacy.go: -------------------------------------------------------------------------------- 1 | // +build darwin,!go1.11 2 | 3 | package water 4 | 5 | func setNonBlock(fd int) error { 6 | // There's a but pre-go1.11 that causes 'resource temporarily unavailable' 7 | // error in non-blocking mode. So just skip it here. Close() won't be able 8 | // to unblock a pending read, but that's better than being broken. 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /syscalls_linux.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | const ( 11 | cIFFTUN = 0x0001 12 | cIFFTAP = 0x0002 13 | cIFFNOPI = 0x1000 14 | cIFFMULTIQUEUE = 0x0100 15 | ) 16 | 17 | type ifReq struct { 18 | Name [0x10]byte 19 | Flags uint16 20 | pad [0x28 - 0x10 - 2]byte 21 | } 22 | 23 | func ioctl(fd uintptr, request uintptr, argp uintptr) error { 24 | _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(request), argp) 25 | if errno != 0 { 26 | return os.NewSyscallError("ioctl", errno) 27 | } 28 | return nil 29 | } 30 | 31 | func setupFd(config Config, fd uintptr) (name string, err error) { 32 | var flags uint16 = cIFFNOPI 33 | if config.DeviceType == TUN { 34 | flags |= cIFFTUN 35 | } else { 36 | flags |= cIFFTAP 37 | } 38 | if config.PlatformSpecificParams.MultiQueue { 39 | flags |= cIFFMULTIQUEUE 40 | } 41 | 42 | if name, err = createInterface(fd, config.Name, flags); err != nil { 43 | return "", err 44 | } 45 | 46 | if err = setDeviceOptions(fd, config); err != nil { 47 | return "", err 48 | } 49 | 50 | return name, nil 51 | } 52 | 53 | func createInterface(fd uintptr, ifName string, flags uint16) (createdIFName string, err error) { 54 | var req ifReq 55 | req.Flags = flags 56 | copy(req.Name[:], ifName) 57 | 58 | err = ioctl(fd, syscall.TUNSETIFF, uintptr(unsafe.Pointer(&req))) 59 | if err != nil { 60 | return 61 | } 62 | 63 | createdIFName = strings.Trim(string(req.Name[:]), "\x00") 64 | return 65 | } 66 | 67 | func setDeviceOptions(fd uintptr, config Config) (err error) { 68 | if config.Permissions != nil { 69 | if err = ioctl(fd, syscall.TUNSETOWNER, uintptr(config.Permissions.Owner)); err != nil { 70 | return 71 | } 72 | if err = ioctl(fd, syscall.TUNSETGROUP, uintptr(config.Permissions.Group)); err != nil { 73 | return 74 | } 75 | } 76 | 77 | // set clear the persist flag 78 | value := 0 79 | if config.Persist { 80 | value = 1 81 | } 82 | return ioctl(fd, syscall.TUNSETPERSIST, uintptr(value)) 83 | } 84 | -------------------------------------------------------------------------------- /syscalls_linux_go1.11.go: -------------------------------------------------------------------------------- 1 | // +build linux,go1.11 2 | 3 | package water 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | ) 9 | 10 | func openDev(config Config) (ifce *Interface, err error) { 11 | var fdInt int 12 | if fdInt, err = syscall.Open( 13 | "/dev/net/tun", os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil { 14 | return nil, err 15 | } 16 | 17 | name, err := setupFd(config, uintptr(fdInt)) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return &Interface{ 23 | isTAP: config.DeviceType == TAP, 24 | ReadWriteCloser: os.NewFile(uintptr(fdInt), "tun"), 25 | name: name, 26 | }, nil 27 | } 28 | -------------------------------------------------------------------------------- /syscalls_linux_legacy.go: -------------------------------------------------------------------------------- 1 | // +build linux,!go1.11 2 | 3 | package water 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | func openDev(config Config) (ifce *Interface, err error) { 10 | var file *os.File 11 | if file, err = os.OpenFile( 12 | "/dev/net/tun", os.O_RDWR, 0); err != nil { 13 | return nil, err 14 | } 15 | 16 | name, err := setupFd(config, file.Fd()) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return &Interface{ 22 | isTAP: config.DeviceType == TAP, 23 | ReadWriteCloser: file, 24 | name: name, 25 | }, nil 26 | } 27 | -------------------------------------------------------------------------------- /syscalls_other.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!darwin,!windows 2 | 3 | package water 4 | 5 | import "errors" 6 | 7 | func openDev(config Config) (*Interface, error) { 8 | return nil, errors.New("not implemented on this platform") 9 | } 10 | -------------------------------------------------------------------------------- /syscalls_windows.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "sync" 9 | "syscall" 10 | "unsafe" 11 | 12 | "golang.org/x/sys/windows/registry" 13 | ) 14 | 15 | // To use it with windows, you need a tap driver installed on windows. 16 | // https://github.com/OpenVPN/tap-windows6 17 | // or just install OpenVPN 18 | // https://github.com/OpenVPN/openvpn 19 | 20 | const ( 21 | // tapDriverKey is the location of the TAP driver key. 22 | tapDriverKey = `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}` 23 | // netConfigKey is the location of the TAP adapter's network config. 24 | netConfigKey = `SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}` 25 | ) 26 | 27 | var ( 28 | errIfceNameNotFound = errors.New("Failed to find the name of interface") 29 | // Device Control Codes 30 | tap_win_ioctl_get_mac = tap_control_code(1, 0) 31 | tap_win_ioctl_get_version = tap_control_code(2, 0) 32 | tap_win_ioctl_get_mtu = tap_control_code(3, 0) 33 | tap_win_ioctl_get_info = tap_control_code(4, 0) 34 | tap_ioctl_config_point_to_point = tap_control_code(5, 0) 35 | tap_ioctl_set_media_status = tap_control_code(6, 0) 36 | tap_win_ioctl_config_dhcp_masq = tap_control_code(7, 0) 37 | tap_win_ioctl_get_log_line = tap_control_code(8, 0) 38 | tap_win_ioctl_config_dhcp_set_opt = tap_control_code(9, 0) 39 | tap_ioctl_config_tun = tap_control_code(10, 0) 40 | // w32 api 41 | file_device_unknown = uint32(0x00000022) 42 | nCreateEvent, 43 | nResetEvent, 44 | nGetOverlappedResult uintptr 45 | ) 46 | 47 | func init() { 48 | k32, err := syscall.LoadLibrary("kernel32.dll") 49 | if err != nil { 50 | panic("LoadLibrary " + err.Error()) 51 | } 52 | defer syscall.FreeLibrary(k32) 53 | 54 | nCreateEvent = getProcAddr(k32, "CreateEventW") 55 | nResetEvent = getProcAddr(k32, "ResetEvent") 56 | nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult") 57 | } 58 | 59 | func getProcAddr(lib syscall.Handle, name string) uintptr { 60 | addr, err := syscall.GetProcAddress(lib, name) 61 | if err != nil { 62 | panic(name + " " + err.Error()) 63 | } 64 | return addr 65 | } 66 | 67 | func resetEvent(h syscall.Handle) error { 68 | r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0) 69 | if r == 0 { 70 | return err 71 | } 72 | return nil 73 | } 74 | 75 | func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) { 76 | var n int 77 | r, _, err := syscall.Syscall6(nGetOverlappedResult, 4, 78 | uintptr(h), 79 | uintptr(unsafe.Pointer(overlapped)), 80 | uintptr(unsafe.Pointer(&n)), 1, 0, 0) 81 | if r == 0 { 82 | return n, err 83 | } 84 | 85 | return n, nil 86 | } 87 | 88 | func newOverlapped() (*syscall.Overlapped, error) { 89 | var overlapped syscall.Overlapped 90 | r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0) 91 | if r == 0 { 92 | return nil, err 93 | } 94 | overlapped.HEvent = syscall.Handle(r) 95 | return &overlapped, nil 96 | } 97 | 98 | type wfile struct { 99 | fd syscall.Handle 100 | rl sync.Mutex 101 | wl sync.Mutex 102 | ro *syscall.Overlapped 103 | wo *syscall.Overlapped 104 | } 105 | 106 | func (f *wfile) Close() error { 107 | return syscall.Close(f.fd) 108 | } 109 | 110 | func (f *wfile) Write(b []byte) (int, error) { 111 | f.wl.Lock() 112 | defer f.wl.Unlock() 113 | 114 | if err := resetEvent(f.wo.HEvent); err != nil { 115 | return 0, err 116 | } 117 | var n uint32 118 | err := syscall.WriteFile(f.fd, b, &n, f.wo) 119 | if err != nil && err != syscall.ERROR_IO_PENDING { 120 | return int(n), err 121 | } 122 | return getOverlappedResult(f.fd, f.wo) 123 | } 124 | 125 | func (f *wfile) Read(b []byte) (int, error) { 126 | f.rl.Lock() 127 | defer f.rl.Unlock() 128 | 129 | if err := resetEvent(f.ro.HEvent); err != nil { 130 | return 0, err 131 | } 132 | var done uint32 133 | err := syscall.ReadFile(f.fd, b, &done, f.ro) 134 | if err != nil && err != syscall.ERROR_IO_PENDING { 135 | return int(done), err 136 | } 137 | return getOverlappedResult(f.fd, f.ro) 138 | } 139 | 140 | func ctl_code(device_type, function, method, access uint32) uint32 { 141 | return (device_type << 16) | (access << 14) | (function << 2) | method 142 | } 143 | 144 | func tap_control_code(request, method uint32) uint32 { 145 | return ctl_code(file_device_unknown, request, method, 0) 146 | } 147 | 148 | // getdeviceid finds out a TAP device from registry, it *may* requires privileged right to prevent some weird issue. 149 | func getdeviceid(componentID string, interfaceName string) (deviceid string, err error) { 150 | k, err := registry.OpenKey(registry.LOCAL_MACHINE, tapDriverKey, registry.READ) 151 | if err != nil { 152 | return "", fmt.Errorf("Failed to open the adapter registry, TAP driver may be not installed, %v", err) 153 | } 154 | defer k.Close() 155 | // read all subkeys, it should not return an err here 156 | keys, err := k.ReadSubKeyNames(-1) 157 | if err != nil { 158 | return "", err 159 | } 160 | // find the one matched ComponentId 161 | for _, v := range keys { 162 | key, err := registry.OpenKey(registry.LOCAL_MACHINE, tapDriverKey+"\\"+v, registry.READ) 163 | if err != nil { 164 | continue 165 | } 166 | val, _, err := key.GetStringValue("ComponentId") 167 | if err != nil { 168 | key.Close() 169 | continue 170 | } 171 | if val == componentID { 172 | val, _, err = key.GetStringValue("NetCfgInstanceId") 173 | if err != nil { 174 | key.Close() 175 | continue 176 | } 177 | if len(interfaceName) > 0 { 178 | key2 := fmt.Sprintf("%s\\%s\\Connection", netConfigKey, val) 179 | k2, err := registry.OpenKey(registry.LOCAL_MACHINE, key2, registry.READ) 180 | if err != nil { 181 | continue 182 | } 183 | defer k2.Close() 184 | val, _, err := k2.GetStringValue("Name") 185 | if err != nil || val != interfaceName { 186 | continue 187 | } 188 | } 189 | key.Close() 190 | return val, nil 191 | } 192 | key.Close() 193 | } 194 | if len(interfaceName) > 0 { 195 | return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId '%s' and InterfaceName '%s', TAP driver may be not installed or you may have specified an interface name that doesn't exist", componentID, interfaceName) 196 | } 197 | 198 | return "", fmt.Errorf("Failed to find the tap device in registry with specified ComponentId '%s', TAP driver may be not installed", componentID) 199 | } 200 | 201 | // setStatus is used to bring up or bring down the interface 202 | func setStatus(fd syscall.Handle, status bool) error { 203 | var bytesReturned uint32 204 | rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) 205 | code := []byte{0x00, 0x00, 0x00, 0x00} 206 | if status { 207 | code[0] = 0x01 208 | } 209 | return syscall.DeviceIoControl(fd, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) 210 | } 211 | 212 | // setTUN is used to configure the IP address in the underlying driver when using TUN 213 | func setTUN(fd syscall.Handle, network string) error { 214 | var bytesReturned uint32 215 | rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) 216 | 217 | localIP, remoteNet, err := net.ParseCIDR(network) 218 | if err != nil { 219 | return fmt.Errorf("Failed to parse network CIDR in config, %v", err) 220 | } 221 | if localIP.To4() == nil { 222 | return fmt.Errorf("Provided network(%s) is not a valid IPv4 address", network) 223 | } 224 | code2 := make([]byte, 0, 12) 225 | code2 = append(code2, localIP.To4()[:4]...) 226 | code2 = append(code2, remoteNet.IP.To4()[:4]...) 227 | code2 = append(code2, remoteNet.Mask[:4]...) 228 | if len(code2) != 12 { 229 | return fmt.Errorf("Provided network(%s) is not valid", network) 230 | } 231 | if err := syscall.DeviceIoControl(fd, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil); err != nil { 232 | return err 233 | } 234 | return nil 235 | } 236 | 237 | // openDev find and open an interface. 238 | func openDev(config Config) (ifce *Interface, err error) { 239 | // find the device in registry. 240 | deviceid, err := getdeviceid(config.PlatformSpecificParams.ComponentID, config.PlatformSpecificParams.InterfaceName) 241 | if err != nil { 242 | return nil, err 243 | } 244 | path := "\\\\.\\Global\\" + deviceid + ".tap" 245 | pathp, err := syscall.UTF16PtrFromString(path) 246 | if err != nil { 247 | return nil, err 248 | } 249 | // type Handle uintptr 250 | file, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_SYSTEM|syscall.FILE_FLAG_OVERLAPPED, 0) 251 | // if err hanppens, close the interface. 252 | defer func() { 253 | if err != nil { 254 | syscall.Close(file) 255 | } 256 | if err := recover(); err != nil { 257 | syscall.Close(file) 258 | } 259 | }() 260 | if err != nil { 261 | return nil, err 262 | } 263 | var bytesReturned uint32 264 | 265 | // find the mac address of tap device, use this to find the name of interface 266 | mac := make([]byte, 6) 267 | err = syscall.DeviceIoControl(file, tap_win_ioctl_get_mac, &mac[0], uint32(len(mac)), &mac[0], uint32(len(mac)), &bytesReturned, nil) 268 | if err != nil { 269 | return nil, err 270 | } 271 | 272 | // fd := os.NewFile(uintptr(file), path) 273 | ro, err := newOverlapped() 274 | if err != nil { 275 | return 276 | } 277 | wo, err := newOverlapped() 278 | if err != nil { 279 | return 280 | } 281 | fd := &wfile{fd: file, ro: ro, wo: wo} 282 | ifce = &Interface{isTAP: (config.DeviceType == TAP), ReadWriteCloser: fd} 283 | 284 | // bring up device. 285 | if err := setStatus(file, true); err != nil { 286 | return nil, err 287 | } 288 | 289 | //TUN 290 | if config.DeviceType == TUN { 291 | if err := setTUN(file, config.PlatformSpecificParams.Network); err != nil { 292 | return nil, err 293 | } 294 | } 295 | 296 | // find the name of tap interface(u need it to set the ip or other command) 297 | ifces, err := net.Interfaces() 298 | if err != nil { 299 | return 300 | } 301 | 302 | for _, v := range ifces { 303 | if len(v.HardwareAddr) < 6 { 304 | continue 305 | } 306 | if bytes.Equal(v.HardwareAddr[:6], mac[:6]) { 307 | ifce.name = v.Name 308 | return 309 | } 310 | } 311 | 312 | return nil, errIfceNameNotFound 313 | } 314 | -------------------------------------------------------------------------------- /waterutil/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package waterutil provides utility functions for interpreting TUN/TAP MAC farme headers and IP packet headers. It defines some constants such as protocol numbers and ethernet frame types. Use waterutil along with package water to work with TUN/TAP interface data. 3 | 4 | Frames/packets are interpreted in following format (as in TUN/TAP devices): 5 | 6 | TAP - MAC Frame: 7 | No Tagging 8 | +----------------------------------------------------------------------------- 9 | | Octet |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|... 10 | +----------------------------------------------------------------------------- 11 | | Field | MAC Destination | MAC Source |EType| Payload 12 | +----------------------------------------------------------------------------- 13 | 14 | Single-Tagged -- Octets [12,13] == {0x81, 0x00} 15 | +----------------------------------------------------------------------------- 16 | | Octet |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|... 17 | +----------------------------------------------------------------------------- 18 | | Field | MAC Destination | MAC Source | Tag | Payload 19 | +----------------------------------------------------------------------------- 20 | 21 | Double-Tagged -- Octets [12,13] == {0x88, 0xA8} 22 | +----------------------------------------------------------------------------- 23 | | Octet |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|... 24 | +----------------------------------------------------------------------------- 25 | | Field | MAC Destination | MAC Source | Outer Tag | Inner Tag | Payload 26 | +----------------------------------------------------------------------------- 27 | 28 | TUN - IPv4 Packet: 29 | +---------------------------------------------------------------------------------------------------------------+ 30 | | | Octet | 0 | 1 | 2 | 3 | 31 | | Octet | Bit |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31| 32 | +---------------------------------------------------------------------------------------------------------------+ 33 | | 0 | 0 | Version | IHL | DSCP | ECN | Total Length | 34 | +---------------------------------------------------------------------------------------------------------------+ 35 | | 4 | 32 | Identification | Flags | Fragment Offset | 36 | +---------------------------------------------------------------------------------------------------------------+ 37 | | 8 | 64 | Time To Live | Protocol | Header Checksum | 38 | +---------------------------------------------------------------------------------------------------------------+ 39 | | 12 | 96 | Source IP Address | 40 | +---------------------------------------------------------------------------------------------------------------+ 41 | | 16 | 128 | Destination IP Address | 42 | +---------------------------------------------------------------------------------------------------------------+ 43 | | 20 | 160 | Options (if IHL > 5) | 44 | +---------------------------------------------------------------------------------------------------------------+ 45 | | 24 | 192 | | 46 | | 30 | 224 | Payload | 47 | | ... | ... | | 48 | +---------------------------------------------------------------------------------------------------------------+ 49 | 50 | */ 51 | package waterutil 52 | -------------------------------------------------------------------------------- /waterutil/ethertypes.go: -------------------------------------------------------------------------------- 1 | package waterutil 2 | 3 | type Ethertype [2]byte 4 | 5 | // Ethertype values. From: http://en.wikipedia.org/wiki/Ethertype 6 | var ( 7 | IPv4 = Ethertype{0x08, 0x00} 8 | ARP = Ethertype{0x08, 0x06} 9 | WakeOnLAN = Ethertype{0x08, 0x42} 10 | TRILL = Ethertype{0x22, 0xF3} 11 | DECnetPhase4 = Ethertype{0x60, 0x03} 12 | RARP = Ethertype{0x80, 0x35} 13 | AppleTalk = Ethertype{0x80, 0x9B} 14 | AARP = Ethertype{0x80, 0xF3} 15 | IPX1 = Ethertype{0x81, 0x37} 16 | IPX2 = Ethertype{0x81, 0x38} 17 | QNXQnet = Ethertype{0x82, 0x04} 18 | IPv6 = Ethertype{0x86, 0xDD} 19 | EthernetFlowControl = Ethertype{0x88, 0x08} 20 | IEEE802_3 = Ethertype{0x88, 0x09} 21 | CobraNet = Ethertype{0x88, 0x19} 22 | MPLSUnicast = Ethertype{0x88, 0x47} 23 | MPLSMulticast = Ethertype{0x88, 0x48} 24 | PPPoEDiscovery = Ethertype{0x88, 0x63} 25 | PPPoESession = Ethertype{0x88, 0x64} 26 | JumboFrames = Ethertype{0x88, 0x70} 27 | HomePlug1_0MME = Ethertype{0x88, 0x7B} 28 | IEEE802_1X = Ethertype{0x88, 0x8E} 29 | PROFINET = Ethertype{0x88, 0x92} 30 | HyperSCSI = Ethertype{0x88, 0x9A} 31 | AoE = Ethertype{0x88, 0xA2} 32 | EtherCAT = Ethertype{0x88, 0xA4} 33 | EthernetPowerlink = Ethertype{0x88, 0xAB} 34 | LLDP = Ethertype{0x88, 0xCC} 35 | SERCOS3 = Ethertype{0x88, 0xCD} 36 | HomePlugAVMME = Ethertype{0x88, 0xE1} 37 | MRP = Ethertype{0x88, 0xE3} 38 | IEEE802_1AE = Ethertype{0x88, 0xE5} 39 | IEEE1588 = Ethertype{0x88, 0xF7} 40 | IEEE802_1ag = Ethertype{0x89, 0x02} 41 | FCoE = Ethertype{0x89, 0x06} 42 | FCoEInit = Ethertype{0x89, 0x14} 43 | RoCE = Ethertype{0x89, 0x15} 44 | CTP = Ethertype{0x90, 0x00} 45 | VeritasLLT = Ethertype{0xCA, 0xFE} 46 | ) 47 | -------------------------------------------------------------------------------- /waterutil/ip_protocols.go: -------------------------------------------------------------------------------- 1 | package waterutil 2 | 3 | type IPProtocol byte 4 | 5 | // IP Protocols. From: http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers 6 | const ( 7 | HOPOPT = 0x00 8 | ICMP = 0x01 9 | IGMP = 0x02 10 | GGP = 0x03 11 | IPv4Encapsulation = 0x04 12 | ST = 0x05 13 | TCP = 0x06 14 | CBT = 0x07 15 | EGP = 0x08 16 | IGP = 0x09 17 | BBN_RCC_MON = 0x0A 18 | NVP_II = 0x0B 19 | PUP = 0x0C 20 | ARGUS = 0x0D 21 | EMCON = 0x0E 22 | XNET = 0x0F 23 | CHAOS = 0x10 24 | UDP = 0x11 25 | MUX = 0x12 26 | DCN_MEAS = 0x13 27 | HMP = 0x14 28 | PRM = 0x15 29 | XNS_IDP = 0x16 30 | TRUNK_1 = 0x17 31 | TRUNK_2 = 0x18 32 | LEAF_1 = 0x19 33 | LEAF_2 = 0x1A 34 | RDP = 0x1B 35 | IRTP = 0x1C 36 | ISO_TP4 = 0x1D 37 | NETBLT = 0x1E 38 | MFE_NSP = 0x1F 39 | MERIT_INP = 0x20 40 | DCCP = 0x21 41 | ThirdPC = 0x22 42 | IDPR = 0x23 43 | XTP = 0x24 44 | DDP = 0x25 45 | IDPR_CMTP = 0x26 46 | TPxx = 0x27 47 | IL = 0x28 48 | IPv6Encapsulation = 0x29 49 | SDRP = 0x2A 50 | IPv6_Route = 0x2B 51 | IPv6_Frag = 0x2C 52 | IDRP = 0x2D 53 | RSVP = 0x2E 54 | GRE = 0x2F 55 | MHRP = 0x30 56 | BNA = 0x31 57 | ESP = 0x32 58 | AH = 0x33 59 | I_NLSP = 0x34 60 | SWIPE = 0x35 61 | NARP = 0x36 62 | MOBILE = 0x37 63 | TLSP = 0x38 64 | SKIP = 0x39 65 | IPv6_ICMP = 0x3A 66 | IPv6_NoNxt = 0x3B 67 | IPv6_Opts = 0x3C 68 | CFTP = 0x3E 69 | SAT_EXPAK = 0x40 70 | KRYPTOLAN = 0x41 71 | RVD = 0x42 72 | IPPC = 0x43 73 | SAT_MON = 0x45 74 | VISA = 0x46 75 | IPCV = 0x47 76 | CPNX = 0x48 77 | CPHB = 0x49 78 | WSN = 0x4A 79 | PVP = 0x4B 80 | BR_SAT_MON = 0x4C 81 | SUN_ND = 0x4D 82 | WB_MON = 0x4E 83 | WB_EXPAK = 0x4F 84 | ISO_IP = 0x50 85 | VMTP = 0x51 86 | SECURE_VMTP = 0x52 87 | VINES = 0x53 88 | TTP = 0x54 89 | IPTM = 0x54 90 | NSFNET_IGP = 0x55 91 | DGP = 0x56 92 | TCF = 0x57 93 | EIGRP = 0x58 94 | OSPF = 0x59 95 | Sprite_RPC = 0x5A 96 | LARP = 0x5B 97 | MTP = 0x5C 98 | AX_25 = 0x5D 99 | IPIP = 0x5E 100 | MICP = 0x5F 101 | SCC_SP = 0x60 102 | ETHERIP = 0x61 103 | ENCAP = 0x62 104 | GMTP = 0x64 105 | IFMP = 0x65 106 | PNNI = 0x66 107 | PIM = 0x67 108 | ARIS = 0x68 109 | SCPS = 0x69 110 | QNX = 0x6A 111 | A_N = 0x6B 112 | IPComp = 0x6C 113 | SNP = 0x6D 114 | Compaq_Peer = 0x6E 115 | IPX_in_IP = 0x6F 116 | VRRP = 0x70 117 | PGM = 0x71 118 | L2TP = 0x73 119 | DDX = 0x74 120 | IATP = 0x75 121 | STP = 0x76 122 | SRP = 0x77 123 | UTI = 0x78 124 | SMP = 0x79 125 | SM = 0x7A 126 | PTP = 0x7B 127 | FIRE = 0x7D 128 | CRTP = 0x7E 129 | CRUDP = 0x7F 130 | SSCOPMCE = 0x80 131 | IPLT = 0x81 132 | SPS = 0x82 133 | PIPE = 0x83 134 | SCTP = 0x84 135 | FC = 0x85 136 | manet = 0x8A 137 | HIP = 0x8B 138 | Shim6 = 0x8C 139 | ) 140 | -------------------------------------------------------------------------------- /waterutil/tap.go: -------------------------------------------------------------------------------- 1 | package waterutil 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type Tagging int 8 | 9 | // Indicating whether/how a MAC frame is tagged. The value is number of bytes taken by tagging. 10 | const ( 11 | NotTagged Tagging = 0 12 | Tagged Tagging = 4 13 | DoubleTagged Tagging = 8 14 | ) 15 | 16 | func MACDestination(macFrame []byte) net.HardwareAddr { 17 | return net.HardwareAddr(macFrame[:6]) 18 | } 19 | 20 | func MACSource(macFrame []byte) net.HardwareAddr { 21 | return net.HardwareAddr(macFrame[6:12]) 22 | } 23 | 24 | func MACTagging(macFrame []byte) Tagging { 25 | if macFrame[12] == 0x81 && macFrame[13] == 0x00 { 26 | return Tagged 27 | } else if macFrame[12] == 0x88 && macFrame[13] == 0xa8 { 28 | return DoubleTagged 29 | } 30 | return NotTagged 31 | } 32 | 33 | func MACEthertype(macFrame []byte) Ethertype { 34 | ethertypePos := 12 + MACTagging(macFrame) 35 | return Ethertype{macFrame[ethertypePos], macFrame[ethertypePos+1]} 36 | } 37 | 38 | func MACPayload(macFrame []byte) []byte { 39 | return macFrame[12+MACTagging(macFrame)+2:] 40 | } 41 | 42 | func IsBroadcast(addr net.HardwareAddr) bool { 43 | return addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff && addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff 44 | } 45 | 46 | func IsIPv4Multicast(addr net.HardwareAddr) bool { 47 | return addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e 48 | } 49 | -------------------------------------------------------------------------------- /waterutil/tun.go: -------------------------------------------------------------------------------- 1 | package waterutil 2 | 3 | func IsIPv4(packet []byte) bool { 4 | return 4 == (packet[0] >> 4) 5 | } 6 | 7 | func IsIPv6(packet []byte) bool { 8 | return 6 == (packet[0] >> 4) 9 | } 10 | -------------------------------------------------------------------------------- /waterutil/tun_ipv4.go: -------------------------------------------------------------------------------- 1 | package waterutil 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | func IPv4DSCP(packet []byte) byte { 8 | return packet[1] >> 2 9 | } 10 | 11 | func IPv4ECN(packet []byte) byte { 12 | return packet[1] & 0x03 13 | } 14 | 15 | func IPv4Identification(packet []byte) [2]byte { 16 | return [2]byte{packet[4], packet[5]} 17 | } 18 | 19 | func IPv4TTL(packet []byte) byte { 20 | return packet[8] 21 | } 22 | 23 | func IPv4Protocol(packet []byte) IPProtocol { 24 | return IPProtocol(packet[9]) 25 | } 26 | 27 | func IPv4Source(packet []byte) net.IP { 28 | return net.IPv4(packet[12], packet[13], packet[14], packet[15]) 29 | } 30 | 31 | func SetIPv4Source(packet []byte, source net.IP) { 32 | copy(packet[12:16], source.To4()) 33 | } 34 | 35 | func IPv4Destination(packet []byte) net.IP { 36 | return net.IPv4(packet[16], packet[17], packet[18], packet[19]) 37 | } 38 | 39 | func SetIPv4Destination(packet []byte, dest net.IP) { 40 | copy(packet[16:20], dest.To4()) 41 | } 42 | 43 | func IPv4Payload(packet []byte) []byte { 44 | ihl := packet[0] & 0x0F 45 | return packet[ihl*4:] 46 | } 47 | 48 | // For TCP/UDP 49 | func IPv4SourcePort(packet []byte) uint16 { 50 | payload := IPv4Payload(packet) 51 | return (uint16(payload[0]) << 8) | uint16(payload[1]) 52 | } 53 | 54 | func IPv4DestinationPort(packet []byte) uint16 { 55 | payload := IPv4Payload(packet) 56 | return (uint16(payload[2]) << 8) | uint16(payload[3]) 57 | } 58 | 59 | func SetIPv4SourcePort(packet []byte, port uint16) { 60 | payload := IPv4Payload(packet) 61 | payload[0] = byte(port >> 8) 62 | payload[1] = byte(port & 0xFF) 63 | } 64 | 65 | func SetIPv4DestinationPort(packet []byte, port uint16) { 66 | payload := IPv4Payload(packet) 67 | payload[2] = byte(port >> 8) 68 | payload[3] = byte(port & 0xFF) 69 | } 70 | --------------------------------------------------------------------------------