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/)