GithubHelp home page GithubHelp logo

songgao / water Goto Github PK

View Code? Open in Web Editor NEW
1.9K 57.0 286.0 106 KB

A simple TUN/TAP library written in native Go.

License: BSD 3-Clause "New" or "Revised" License

Go 99.44% Makefile 0.56%
go tun tap networking

water's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

water's Issues

How to have concurrent TUN/TAP interfaces on Windows ?

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)

low performance of tap device on windows

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!

how to listen all network request

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?

Doesn't work as the example indicates

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

Issue running go binary in docker container

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

Bug: write /dev/net/tun: invalid argument on Ubuntu 16.04.4

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.

How to parsing TCP/UDP?

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?

Document threading properties of Ifce

Starter questions:

  • Can multiple goroutines concurrently Read from the same Ifce?
  • Can multiple goroutines concurrently Write to the same Ifce?
  • Are there performance bottlenecks that limit Read and Write calls by multiple threads?

Can't expose device fd on windows

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

Add support for FreeBSD

FreeBSD is very similar to macOS so it should be quite easy.

There is outdated PR for this: #37

Adding support for this will make porting slack's nebula easier.

收不到其他端口发送的包,why

我在测试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")
	}
}

}

Close does not unblock pending Read call

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.

writing to the interface gives EINVAL (22)

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 can't write data to win-tap

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

}

Malformatted Ethertype when using TUN?

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?

how can i use tun as a filter

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

The windows example can't run

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

mac support

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 .

Handle IP packet

Can read and write IP packets? Leave the protocol stack to handle the frame header.

不支持 namespace

创建 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

Can't write to tun

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?

flag.Parse() collects test files by mistake

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:

  • ipv4_test_linux.go -> ipv4_linux_test.go
  • ipv4_test_other.go -> ipv4_other_test.go

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?

How to implement tun/tap in OSX?

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.

Get/Set MAC Addresses

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.

Accessing existing TAP interface

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?

IPv6?

Hi,

Is IPv6 TUN supported in this library?

Support multiqueue tuntap interface

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;
  }

Bug: write /dev/net/tun: invalid argument on Ubuntu 16.04.1

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.

what is TAP? what is TUN?

In my mind, TAP is Test Anything Protocol. That does not seem to be what this library is about.

And what is TUN?

thanks

write tun: invalid argument on Debian

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
    } 

}
`

IPv4 in Windows TUN mode doesn't work

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.

tun device in windows can't read data.

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.