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
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)
Is there any support of water library to create tun interface for Android? If so can you please attach the link
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!
Relevant to add new interface for init configuration #15.
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?
Go has deprecated package syscall
since 1.4. I think we should use the golang.org/x/sys
repository.
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
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
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.
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?
Example code in readme:
func main() {
config := water.Config{
DeviceType: water.TAP,
}
config.Name = "O_O"
...
}
config.Name doesn't exist now.
Starter questions:
Read
from the same Ifce
?Write
to the same Ifce
?Read
and Write
calls by multiple threads?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!
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
我在测试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")
}
}
}
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.
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)
}
}
}
}
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)
}
}
}
Is there any way to read ifce with timeout?
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?
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
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/
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
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 .
https://www.wintun.net/ provide pure layer 3 tun device for windows.
Very good project ! I just wanted to congratulate ^^
Can read and write IP packets? Leave the protocol stack to handle the frame header.
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.
Thanks
创建 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
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?
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?
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
.
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.
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,
Is IPv6 TUN supported in this library?
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;
}
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.
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.
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.
On the Windows platform, can I just work with IP packets and not care about frame headers? Thanks.
In my mind, TAP is Test Anything Protocol. That does not seem to be what this library is about.
And what is TUN?
thanks
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
}
}
`
Recently, I may need to implement tun windows down, it may submit a patch.
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.
my mistake
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!
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.
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.