songgao / water Goto Github PK
View Code? Open in Web Editor NEWA simple TUN/TAP library written in native Go.
License: BSD 3-Clause "New" or "Revised" License
A simple TUN/TAP library written in native Go.
License: BSD 3-Clause "New" or "Revised" License
When I wrote a source code as follows:
package main
import (
"flag"
"github.com/songgao/water"
)
func main() {
flag.Parse()
water.NewTUN("tun0")
}
and built and ran with -h
option, a lot of unexpected options was listed:
vagrant@localhost:~/waterexample/src/main$ go build
vagrant@localhost:~/waterexample/src/main$ ls
main main.go
vagrant@localhost:~/waterexample/src/main$ ./main -h
Usage of ./main:
-test.bench string
regular expression to select benchmarks to run
-test.benchmem
print memory allocations for benchmarks
-test.benchtime duration
approximate run time for each benchmark (default 1s)
-test.blockprofile string
write a goroutine blocking profile to the named file after execution
-test.blockprofilerate int
if >= 0, calls runtime.SetBlockProfileRate() (default 1)
-test.count n
run tests and benchmarks n times (default 1)
-test.coverprofile string
write a coverage profile to the named file after execution
-test.cpu string
comma-separated list of number of CPUs to use for each test
-test.cpuprofile string
write a cpu profile to the named file during execution
-test.memprofile string
write a memory profile to the named file after execution
-test.memprofilerate int
if >=0, sets runtime.MemProfileRate
-test.outputdir string
directory in which to write profiles
-test.parallel int
maximum test parallelism (default 2)
-test.run string
regular expression to select tests and examples to run
-test.short
run smaller test suite to save time
-test.timeout duration
if positive, sets an aggregate time limit for all tests
-test.trace string
write an execution trace to the named file after execution
-test.v
verbose: print additional output
It probably seems that ipv4_test_linux.go
and ipv4_test_other.go
caused the situation.
I tried to rename these tile names to:
and built and ran again, the problem was solved.
vagrant@localhost:~/waterexample/src/main$ go build
vagrant@localhost:~/waterexample/src/main$ ./main -h
Usage of ./main:
vagrant@localhost:~/waterexample/src/main$
Would you please consider to rename those file names?
I use openvpn install the tap driver on my windows
C:\Program Files\TAP-Windows\bin
λ .\tapinstall.exe hwids tap0901
ROOT\NET\0000
Name: TAP-Windows Adapter V9
Hardware IDs:
tap0901
1 matching device(s) found.
Then I copy the code and run the program
go run .\main.go
It always show me the error
2018/03/30 19:09:41 Failed to find the tap device in registry with specified ComponentId(), TAP driver may be not installed
exit status 1
Thanks
I tried to start on WINDOWS a new TUN interface while OpenVPN was running and "songgao/water" library couldn't find an interface and doesn't try to create a new one.
Is it possible to have parallel interfaces on windows ?
(Linux and Mac are working fine)
I can't expose fd on windows like #7 before.
When I use type assertion, and get this:
panic: interface conversion: io.ReadWriteCloser is *water.wfile, not *os.File
Environment:
windows10, golang 1.12, amd64
Very good project ! I just wanted to congratulate ^^
Hi,
Is IPv6 TUN supported in this library?
here is my code:
package main
import (
"log"
"github.com/songgao/packets/ethernet"
"github.com/songgao/water"
"fmt"
)
func main() {
ifce, err := water.New(water.Config{
DeviceType: water.TAP,
PlatformSpecificParams:water.PlatformSpecificParams{
ComponentID: "tap0901",
Network: "192.168.1.10/24",
},
})
if err != nil {
log.Fatal(err)
}
var frame ethernet.Frame
for {
frame.Resize(1500)
n, err := ifce.Read([]byte(frame))
if err != nil {
log.Fatal(err)
}
frame = frame[:n]
fmt.Printf("\nDst: %s\n", frame.Destination())
fmt.Printf("Src: %s\n", frame.Source())
fmt.Printf("Ethertype: % x\n", frame.Ethertype())
n, err =ifce.Write(frame)// it block here ,not returen anythin
if err != nil {
log.Fatal(err)
}
}
}
I have been trying to implement simple icmp echo & reply. It might not be correct but for some reasons it's giving me EINVAL when I try to write on it. I am using linux 4.19.0-6-amd64
package main
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/songgao/water"
"log"
"os/exec"
)
func main() {
iface, err := water.New(water.Config{
DeviceType: water.TUN,
})
if err != nil {
log.Fatal(err)
}
if err := exec.Command("ip", "addr", "add", "10.2.0.10/24", "dev", iface.Name()).Run(); err != nil {
log.Fatal(err)
}
//sudo ip link set dev tun0 up
if err := exec.Command("ip", "link", "set", "dev", iface.Name(), "up").Run(); err != nil {
log.Fatal(err)
}
log.Printf("Interface Name: %s\n", iface.Name())
for {
packet := make([]byte, 2000)
n, err := iface.Read(packet)
if err != nil {
log.Fatal(err)
}
var payload gopacket.Payload
var ipv4 layers.IPv4
var icmp layers.ICMPv4
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ipv4, &icmp, &payload)
decodedLayers := make([]gopacket.LayerType, 0, 10)
err = parser.DecodeLayers(packet[:n], &decodedLayers)
for _, typ := range decodedLayers {
switch typ {
case layers.LayerTypeIPv4:
fmt.Println("IP4", "SRC:", ipv4.SrcIP, "DST:", ipv4.DstIP)
case layers.LayerTypeICMPv4:
fmt.Println("ICMP TYPE:", icmp.TypeCode.GoString())
case gopacket.LayerTypePayload:
fmt.Print("Payload : ")
fmt.Print(string(payload[8:]))
buffer := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
&layers.IPv4{
SrcIP: ipv4.DstIP,
DstIP: ipv4.DstIP,
},
&layers.ICMPv4{
TypeCode: layers.ICMPv4TypeEchoReply,
},
payload,
)
if err != nil {
log.Fatalln(err)
}
//writerData := buffer.Bytes()
if _, err := iface.Write(buffer.Bytes()); err != nil {
log.Fatalln(err)
}
}
if err != nil {
fmt.Println(" Error encountered:", err)
}
}
}
}
I'm using govpn to connect from Windows client to my Linux server:
The code to setup windows TAP is got from water example: https://github.com/bclermont/govpn/blob/develop-prev-peer-disconnect/src/cypherpunks.ru/govpn/tap_windows.go
From windows client, everything is fine, tap adapter is setup correctly, set IP address, ping itself ok, and there's traffic go out of it.
In VPN server, the ifconfig shown that there're packets are dropped, and server log said that:
{"data":67,"error":"t.dev.Write 42: write /dev/net/tun: invalid argument"}
I'm not sure it's the same issue with #18 or not.
Go has deprecated package syscall
since 1.4. I think we should use the golang.org/x/sys
repository.
Hello, I have a go program that imports water package to create tap interfaces.
I can compile and run the go application on a ubuntu VM. However, when I make a docker image using dockerfile, I am unable to run the same go application. It complains no such file or directory.
I read that it could be related to cgo dependencies. Could you please help by providing some pointers?
I have tried docker builder with ubuntu, alpine golang:1.14.5. In every case, I am able to compile the go program. The problem is only when I try to run the binary inside the container.
thanks
Venkat
It looks like that Windows code does not support IPv6:
Line 201 in bf1a5d0
I don't have a Windows IPv6 environment so I am afraid I can't help much.
There is a new way to support TAP interface for macOS later than 10.13 without TunTapOSXDriver.
Please see: https://www.zerotier.com/2019/08/21/how-zerotier-eliminated-kernel-extensions-on-macos/
https://www.wintun.net/ provide pure layer 3 tun device for windows.
Is there any support of water library to create tun interface for Android? If so can you please attach the link
Hi,
I want to run some extra syscalls to the device, but looks I could not get the device fd, could you please implement an interface to expose it? Thanks.
Recently, I may need to implement tun windows down, it may submit a patch.
Can read and write IP packets? Leave the protocol stack to handle the frame header.
I an using vpn to connect from iPhone client to my Linux server:
I create a tcp connect between clien and server, read packets from iphone and write to tun interface. But I get an error after write some packets: "write tun: invalid argument".
`
for {
bufPool.Resize(1500)
count, aErr := io.ReadFull(conn, bufPool)
if aErr != nil {
log.Printf("read failed %s\n", err)
return
}
validBuf := bufPool[:count]
n, aErr := gl_ifce.Write(validBuf)
if aErr != nil {
fmt.Println(n, count, aErr)
return
}
}
`
test code
package main
import (
"fmt"
"github.com/songgao/water"
"encoding/hex"
"os"
"time"
)
func main() {
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "%v\n", ifce.Name())
defer ifce.Close()
for i := 1; i < 100; i++ {
data, _ := hex.DecodeString("0200000045000054289d000040013ded0a01000a0a0100140800787f9f45000059db76250003253408090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637")
ifce.Write(data)
time.Sleep(1 * time.Second)
}
}
Running on macos, and tunnel name is utun2, then I execute sudo ifconfig utun2 10.1.0.10 10.1.0.20 up
data is an ICMP packet equivalent as ping 10.1.0.20
I captured at utun2 by wireshark, and get nothing, does my codes or my test get wrong?
Can you please provide a working macOS example, which:
10.0.0.1
)ping 10.0.0.1
works)This program https://github.com/twitchyliquid64/subnet works because it vendors an older version, if you update github.com/songgao/water
in vendor/
you'll see that it no longer works on macOS.
I want to connect to my socks5 via TUN/TAP
Implement UDP TCP via socks5
Can you tell me how to implement it? Thank you!
Attempting to write the default tunnel will cause this error.
Work-around: Update syscalls_linux.go
, the function newTUN
to not use the flag cIFF_NO_PI
. It will work this way.
That is, the line:
name, err := createInterface(file.Fd(), ifName, cIFF_TUN|cIFF_NO_PI)
will be replaced by:
name, err := createInterface(file.Fd(), ifName, cIFF_TUN)
I remember this situation back in the days I was playing with Linux TUN feature. And this also happened back then. This looks like a bug in the Kernel itself. But I guess, since it hasn't been addressed for years in the mainstream and people have got ways around it, it has been left as is.
Thus, I'm guessing it should be addressed at the library level as well. I have this problem on Ubuntu 16.04.1, but I'm thinking it also happens on other systems.
Is it possible to use an existing TAP interface? If not, am I correct to think that it's just a matter to add some code in order to achieve that?
Hi,
I tried with the example "TAP on Linux" in README page, on Ubuntu 16.04, go 1.8.1, strictly following the instructions. After issuing "sudo ip link set dev O_O up", using ifconfig will show
O_O Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.1.0.10 P-t-P:10.1.0.10 Mask:255.255.255.0
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:84 (84.0 B)
For me I had though Link encap should be Ethernet?
"ping -c1 -b 10.1.0.255", the output of the main.go terminal is:
2017/08/27 19:43:08 Dst: 45:00:00:54:00:00
2017/08/27 19:43:08 Src: 40:00:40:01:25:9f
2017/08/27 19:43:08 Ethertype: 0a 01 <---- ( not as the example said Ethertype: 08 00)
2017/08/27 19:43:08 Payload: 00 0a 0a 01 00 ff 08 00 0f 24 7c c1 00 01 4c b0 a2 59 00 00 00 00 b0 3c 0e 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
And I tried to "ping 10.1.0.10", there are ICMP replies but no output from main.go terminal, means the frames not read from TAP?
Is there anything I am doing wrong or misunderstanding? I didn't take a look through the code but want to ask first for shortcut.
Thanks
When I configure water to be a TUN interface and try to print out Ethertype I receive this: Ethertype: 0a 01
I expect a hex similar to what water prints out when using a TAP interface on Linux. Eg: IPv4 = Ethertype{0x08, 0x00} similar to what the doc suggest.
Am I missing something?
Is there any way to read ifce with timeout?
sudo ip addr add 10.1.0.10/24 dev O_O
sudo ip link set dev O_O up
this only works for the request goes to 10.1.0.10/24
are there any way to capture all income and outcome data?
我在测试tap端口时,无法收到其他端口的ping包,用ping -I enp0s3 10.0.42.1 。 但可以收到 ping -b 10.0.42.1 ,why?
func TestTAP(t *testing.T) {
var (
self = net.IPv4(10, 0, 42, 1)
mask = net.IPv4Mask(255, 255, 255, 0)
brd = net.IPv4(10, 0, 42, 255)
)
fmt.Println("Start to test TAP!")
ifce, err := New(Config{DeviceType: TAP})
if err != nil {
t.Fatalf("creating TAP error: %v\n", err)
}
setupIfce(t, net.IPNet{IP: self, Mask: mask}, ifce.Name())
dataCh := make(chan []byte, 8)
startRead(dataCh, ifce)
timeout := time.NewTimer(5 * time.Second).C
readFrame:
for {
select {
case buffer := <-dataCh:
ethertype := waterutil.MACEthertype(buffer)
if ethertype != waterutil.IPv4 {
continue readFrame
}
packet := waterutil.MACPayload(buffer)
if !waterutil.IsIPv4(packet) {
continue readFrame
}
if !waterutil.IPv4Destination(packet).Equal(self) {
continue readFrame
}
if waterutil.IPv4Protocol(packet) != waterutil.ICMP {
continue readFrame
}
t.Logf("received broadcast frame: %#v\n", buffer)
break readFrame
case <-timeout:
t.Fatal("Waiting for ping packet timeout")
}
}
}
Starter questions:
Read
from the same Ifce
?Write
to the same Ifce
?Read
and Write
calls by multiple threads?Example code in readme:
func main() {
config := water.Config{
DeviceType: water.TAP,
}
config.Name = "O_O"
...
}
config.Name doesn't exist now.
my mistake
Hi, I run your test code on windows 10 with go version 1.9,
it cannot find the tap device.
I already installed OpenVPN TAP driver and it works correctly on my PC.
The regkey of the TAP drive is the same as the string in your code and the default ComponentId is also same too.
The newer version linux kernel has supported multiqueue. According to kernel doc, it could handle packet in parallel.
Kernel Doc: https://www.kernel.org/doc/Documentation/networking/tuntap.txt
#include <linux/if.h>
#include <linux/if_tun.h>
int tun_alloc_mq(char *dev, int queues, int *fds)
{
struct ifreq ifr;
int fd, err, i;
if (!dev)
return -1;
memset(&ifr, 0, sizeof(ifr));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
* IFF_MULTI_QUEUE - Create a queue of multiqueue device
*/
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE;
strcpy(ifr.ifr_name, dev);
for (i = 0; i < queues; i++) {
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
goto err;
err = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (err) {
close(fd);
goto err;
}
fds[i] = fd;
}
return 0;
err:
for (--i; i >= 0; i--)
close(fds[i]);
return err;
}
Relevant to add new interface for init configuration #15.
I created a tun and I can see the ping request from another machine 10.1.1.1
but the problem is how can I pass the request to protocol stack and let them process it, and send back through my tun
it's a very strong project.
but not support osx,do you have some solution,the path /dev/net/tap and /dev/net/tun not exist in mac osx .
I tried water on windos,use tap device process some IP packet traffic,
such as :
using mysql GUI client connect to local mysql server through tap device's IP,when opening table (less data),it is very slow , but without tap, it is very fast, i chehcked the go pprof ,find out that 99% CPU time is used by io.Copy.
Looking forward to your suggestion!
I run a TUN mode example on linux work great, but it doesn't work on windows,I only get message like this:
2018/10/25 01:08:21 Packet Received: 60 02 ce ...
2018/10/25 01:08:21 Packet Received: 60 00 00 ...
2018/10/25 01:08:21 Packet Received: 60 00 00 ...
2018/10/25 01:08:22 Packet Received: 60 00 00 ...
2018/10/25 01:08:22 Packet Received: 60 02 ce ...
2018/10/25 01:08:22 Packet Received: 60 00 00 ...
All packets are IPv6 packets, I had tried a ping, which showed in wireshark but program.
创建 tap 接口以后用 ip 命令把创建的接口挪动到其他的 namespace,会报错。使用官方 README.md 中 linux 的样例程序,复现如下:
启动样例程序:
# go run main.go
调整 tap 接口 O_O
# ip netns add O_O
# ip link set O_O netns O_O
# ip netns exec O_O ifconfig O_O 1.1.1.1/24 up
I am running go1.8
on Linux carbonite 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
.
Consider the following test:
func main() {
iface, _ := water.NewTUN("tunnel")
exec.Command("/sbin/ip", "addr", "add", "10.0.0.1/24", "dev", "tunnel").Run()
exec.Command("/sbin/ip", "link", "set", "dev", "tunnel", "up").Run()
c := make(chan bool)
go func() {
iface.Read(make([]byte, 1<<16))
fmt.Println("Read returned")
c <- true
}()
time.Sleep(5 * time.Second)
fmt.Println("calling Close")
iface.Close() // This should unblock Read
<-c // Wait for Read to unblock
}
Running this program hangs forever with the following output:
$ go build main.go
$ sudo ./main
calling Close
But never prints "Read returned" because the Read call is never interrupted even though the iface has been closed.
While it is blocked, ifconfig
still shows the device is up:
$ ifconfig
tunnel Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.0.1 P-t-P:10.0.0.1 Mask:255.255.255.0
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Interestingly, if you ping -b 10.0.0.255
, then the Read operation returns with a valid packet, but if you were to try calling Read again, it properly reports that the device is closed.
The fact that Close does not interrupt pending Read calls is unexpected behavior since it is contrary to how other networking handlers like sockets operate.
Hey there. Thanks for this library! I've been using it for a personal project.
I added some changes to get/set MAC addresses for linux on my fork using syscalls. Would you be interested in a pull request? I ask because my implementation is probably not portable to Windows or Mac and I'm not sure how much you value that.
package main
import (
"log"
"github.com/songgao/water"
)
func main() {
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
PlatformSpecificParams: water.PlatformSpecificParams{
ComponentID: "tap0901",
Network: "192.168.1.10/24",
},
})
if err != nil {
log.Fatal(err)
}
frame := make([]byte, 1<<12)
for {
n, err := ifce.Read(frame)
if err != nil {
log.Fatal(err)
}
log.Printf("data: %v\n", frame[:n])
}
}
after run this program, I ping 192.168.1.10, but program print nothing. Please help!
I want to use water
to build a virtual network kernel interfaces and forward all of TCP/UDP data. Do I need to implement the parsing TCP/UDP by myself?
In my mind, TAP is Test Anything Protocol. That does not seem to be what this library is about.
And what is TUN?
thanks
On the Windows platform, can I just work with IP packets and not care about frame headers? Thanks.
Hi~
I have seen the roadmap about water
that will support for OSX, did you already have any idea to support it?
I found a project tuntaposx, but I think that would be another way that cat support tuntap on OSX without install any software, just like Tunnelblick
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.