github.com/clmul/water@v0.0.3-0.20221017135504-100d910a03ab/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 (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  ## TODO
   224  * tuntaposx for TAP on Darwin
   225  
   226  ## Alternatives
   227  `tuntap`: [https://code.google.com/p/tuntap/](https://code.google.com/p/tuntap/)