Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Sys() for access to fd/handle #62

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
language: go
go:
- "1.12.1"
- "1.10.8"

go_import_path: github.com/songgao/water
install: go get -u golang.org/x/lint/golint
script: make ci
Expand All @@ -10,4 +8,11 @@ matrix:
include:
- os: linux
dist: xenial
go: 1.12.1
- os: linux
dist: xenial
go: 1.10.8
- os: osx
go: 1.12.1
- os: osx
go: 1.10.8
32 changes: 11 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
# water
[![Build Status](https://travis-ci.org/songgao/water.svg?branch=master)](https://travis-ci.org/songgao/water)
[![GoDoc](https://godoc.org/github.com/songgao/water?status.svg)](https://godoc.org/github.com/songgao/water)

`water` is a native Go library for [TUN/TAP](http://en.wikipedia.org/wiki/TUN/TAP) interfaces.

`water` is designed to be simple and efficient. It

* wraps almost only syscalls and uses only Go standard types;
* exposes standard interfaces; plays well with standard packages like `io`, `bufio`, etc..
* does not handle memory management (allocating/destructing slice). It's up to user to decide whether/how to reuse buffers.

~~`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.~~

See https://github.com/songgao/packets for functions for parsing various packets.
`water` is a pure Go package for working with
[TUN/TAP](http://en.wikipedia.org/wiki/TUN/TAP) interfaces. It works well with
Go's standard library and has no external dependency.

## Supported Platforms

Expand All @@ -19,15 +12,18 @@ See https://github.com/songgao/packets for functions for parsing various packets
* macOS (point-to-point TUN only)

## Installation

```
go get -u github.com/songgao/water
go get -u github.com/songgao/water/waterutil
```

## Documentation
[http://godoc.org/github.com/songgao/water](http://godoc.org/github.com/songgao/water)

## Example
* [Linux](http://godoc.org/github.com/songgao/water?GOOS=linux)
* [macOS](http://godoc.org/github.com/songgao/water?GOOS=darwin)
* [Windows](http://godoc.org/github.com/songgao/water?GOOS=windows)

## Getting Started

### TAP on Linux:

Expand Down Expand Up @@ -234,9 +230,3 @@ If you are going to use multiple TAP devices on the Windows, there is a way to s
},
})
```

## TODO
* tuntaposx for TAP on Darwin

## Alternatives
`tuntap`: [https://code.google.com/p/tuntap/](https://code.google.com/p/tuntap/)
13 changes: 10 additions & 3 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// Package water is a simple TUN/TAP interface library that efficiently works
// with standard packages like io, bufio, etc.. Use waterutil with it to work
// with TUN/TAP packets/frames.
// Package water encapsulates system calls for working with TUN/TAP interfaces,
// in pure Go.
//
// Platform specific docs:
//
// Linux: https://godoc.org/github.com/songgao/water?GOOS=lnux
//
// macOS: https://godoc.org/github.com/songgao/water?GOOS=darwin
//
// Windows: https://godoc.org/github.com/songgao/water?GOOS=windows
package water
50 changes: 24 additions & 26 deletions if.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,36 @@ import (
"io"
)

// Interface is a TUN/TAP interface.
//
// MultiQueue(Linux kernel > 3.8): With MultiQueue enabled, user should hold multiple
// interfaces to send/receive packet in parallel.
// Kernel document about MultiQueue: https://www.kernel.org/doc/Documentation/networking/tuntap.txt
type Interface struct {
isTAP bool
// Interface represents a TUN/TAP interface.
type Interface interface {
// Use Read() and Write() methods to read from and write into this TUN/TAP
// interface. In TAP interface, each call corresponds to an Ethernet frame.
// In TUN mode, each call corresponds to an IP packet.
io.ReadWriteCloser
name string

// IsTUN returns true if ifce is a TUN interface.
IsTUN() bool
// IsTAP returns true if ifce is a TAP interface.
IsTAP() bool
// DeviceType returns the interface's device type.
Type() DeviceType
// Name returns the name of the interface.
Name() string
// Sys returns the underlying system interface for the interface. This is
// useful if caller needs to perform system calls directly on the tun/tap
// device.
//
// On Unix systems, this returns a file descriptor of uintptr type. On
// Windows, this returns a syscall.Handle.
Sys() interface{}
}

// DeviceType is the type for specifying device types.
type DeviceType int

// TUN and TAP device types.
// Constants for TUN and TAP interfaces.
const (
_ = iota
_ DeviceType = iota
TUN
TAP
)
Expand Down Expand Up @@ -49,7 +62,7 @@ func defaultConfig() Config {
var zeroConfig Config

// New creates a new TUN/TAP interface using config.
func New(config Config) (ifce *Interface, err error) {
func New(config Config) (ifce Interface, err error) {
if zeroConfig == config {
config = defaultConfig()
}
Expand All @@ -63,18 +76,3 @@ func New(config Config) (ifce *Interface, err error) {
return nil, errors.New("unknown device type")
}
}

// IsTUN returns true if ifce is a TUN interface.
func (ifce *Interface) IsTUN() bool {
return !ifce.isTAP
}

// IsTAP returns true if ifce is a TAP interface.
func (ifce *Interface) IsTAP() bool {
return ifce.isTAP
}

// Name returns the interface name of ifce, e.g. tun0, tap1, tun0, etc..
func (ifce *Interface) Name() string {
return ifce.name
}
4 changes: 2 additions & 2 deletions if_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// ifName cannot be specified on windows, you will need ifce.Name() to use some cmds.
//
// Deprecated: This function may be removed in the future. Please use New() instead.
func NewTAP(ifName string) (ifce *Interface, err error) {
func NewTAP(ifName string) (ifce Interface, err error) {
fmt.Println("Deprecated: NewTAP(..) may be removed in the future. Please use New() instead.")
config := Config{DeviceType: TAP}
config.Name = ifName
Expand All @@ -22,7 +22,7 @@ func NewTAP(ifName string) (ifce *Interface, err error) {
// ifName cannot be specified on windows, you will need ifce.Name() to use some cmds.
//
// Deprecated: This function will be removed in the future. Please use New() instead.
func NewTUN(ifName string) (ifce *Interface, err error) {
func NewTUN(ifName string) (ifce Interface, err error) {
fmt.Println("Deprecated: NewTUN(..) may be removed in the future. Please use New() instead.")
config := Config{DeviceType: TUN}
config.Name = ifName
Expand Down
32 changes: 32 additions & 0 deletions if_nix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// +build linux darwin

package water

import "io"

type ifce struct {
deviceType DeviceType
fd uintptr
name string
io.ReadWriteCloser
}

func (i *ifce) IsTUN() bool {
return i.deviceType == TUN
}

func (i *ifce) IsTAP() bool {
return i.deviceType == TAP
}

func (i *ifce) Type() DeviceType {
return i.deviceType
}

func (i *ifce) Name() string {
return i.name
}

func (i *ifce) Sys() interface{} {
return i.fd
}
2 changes: 1 addition & 1 deletion ipv4_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func setupIfce(t *testing.T, self net.IP, remote net.IP, dev string) {
}
}

func teardownIfce(t *testing.T, ifce *Interface) {
func teardownIfce(t *testing.T, ifce Interface) {
if err := ifce.Close(); err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion ipv4_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func setupIfce(t *testing.T, ipNet net.IPNet, dev string) {
}
}

func teardownIfce(t *testing.T, ifce *Interface) {
func teardownIfce(t *testing.T, ifce Interface) {
if err := ifce.Close(); err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion ipv4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

const BUFFERSIZE = 1522

func startRead(t *testing.T, ifce *Interface) (dataChan <-chan []byte, errChan <-chan error) {
func startRead(t *testing.T, ifce Interface) (dataChan <-chan []byte, errChan <-chan error) {
dataCh := make(chan []byte)
errCh := make(chan error)
go func() {
Expand Down
2 changes: 1 addition & 1 deletion ipv4_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func setupIfce(t *testing.T, ipNet net.IPNet, dev string) {
}
}

func teardownIfce(t *testing.T, ifce *Interface) {
func teardownIfce(t *testing.T, ifce Interface) {
if err := ifce.Close(); err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 5 additions & 0 deletions params_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ type PlatformSpecificParams struct {
// interface. From version 3.8, Linux supports multiqueue tuntap which can
// uses multiple file descriptors (queues) to parallelize packets sending
// or receiving.
//
// MultiQueue (Linux kernel > 3.8): With MultiQueue enabled, user should
// hold multiple interfaces to send/receive packet in parallel. Kernel
// document about MultiQueue:
// https://www.kernel.org/doc/Documentation/networking/tuntap.txt
MultiQueue bool
}

Expand Down
10 changes: 6 additions & 4 deletions syscalls_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ type sockaddrCtl struct {

var sockaddrCtlSize uintptr = 32

func openDev(config Config) (ifce *Interface, err error) {
func openDev(config Config) (Interface, error) {
if config.DeviceType != TUN {
return nil, errors.New("only tun is implemented on this platform")
}
var fd int
var err error
// Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ...
//
// In sys/socket.h:
Expand Down Expand Up @@ -118,9 +119,10 @@ func openDev(config Config) (ifce *Interface, err error) {
return nil, fmt.Errorf("setting non-blocking error")
}

return &Interface{
isTAP: false,
name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]),
return &ifce{
deviceType: config.DeviceType,
fd: uintptr(fd),
name: string(ifName.name[:ifNameSize-1 /* -1 is for \0 */]),
ReadWriteCloser: &tunReadCloser{
f: os.NewFile(uintptr(fd), string(ifName.name[:])),
},
Expand Down
10 changes: 6 additions & 4 deletions syscalls_linux_go1.11.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"syscall"
)

func openDev(config Config) (ifce *Interface, err error) {
func openDev(config Config) (Interface, error) {
var fdInt int
var err error
if fdInt, err = syscall.Open(
"/dev/net/tun", os.O_RDWR|syscall.O_NONBLOCK, 0); err != nil {
return nil, err
Expand All @@ -19,9 +20,10 @@ func openDev(config Config) (ifce *Interface, err error) {
return nil, err
}

return &Interface{
isTAP: config.DeviceType == TAP,
ReadWriteCloser: os.NewFile(uintptr(fdInt), "tun"),
return &ifce{
deviceType: config.DeviceType,
fd: uintptr(fdInt),
name: name,
ReadWriteCloser: os.NewFile(uintptr(fdInt), "tun"),
}, nil
}
10 changes: 6 additions & 4 deletions syscalls_linux_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"os"
)

func openDev(config Config) (ifce *Interface, err error) {
func openDev(config Config) (Interface, error) {
var file *os.File
var err error
if file, err = os.OpenFile(
"/dev/net/tun", os.O_RDWR, 0); err != nil {
return nil, err
Expand All @@ -18,9 +19,10 @@ func openDev(config Config) (ifce *Interface, err error) {
return nil, err
}

return &Interface{
isTAP: config.DeviceType == TAP,
ReadWriteCloser: file,
return &ifce{
deviceType: config.DeviceType,
fd: file.Fd(),
name: name,
ReadWriteCloser: file,
}, nil
}
Loading