mirror of
https://github.com/yggdrasil-network/water.git
synced 2025-05-19 08:25:09 +03:00
Merge pull request #25 from ArroyoNetworks/reattach
linux: Support for Persistent Devices and Setting Owner/Group
This commit is contained in:
commit
99d07fc117
14 changed files with 164 additions and 92 deletions
|
@ -3,4 +3,5 @@ Harshal Sheth <hsheth2@gmail.com>
|
|||
KOJIMA Takanori <tkojima@accense.com>
|
||||
Sean Purser-Haskell <sean.purserhaskell@gmail.com>
|
||||
daregod <daregod@yandex.ru>
|
||||
Lucus Lee <lixin9311@gmail.com>
|
||||
Lucus Lee <lixin9311@gmail.com>
|
||||
Arroyo Networks, LLC <open.source@arroyonetworks.com>
|
|
@ -151,6 +151,11 @@ You'd see the ICMP packets printed out:
|
|||
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
|
||||
```
|
||||
|
||||
#### Caveats
|
||||
|
||||
1. Only Point-to-Point user TUN devices are supported. TAP devices are *not* supported natively by macOS.
|
||||
2. Custom interface names are not supported by macOS. Interface names are automatically generated serially, using the `utun<#>` naming convention.
|
||||
|
||||
### TAP on Windows:
|
||||
|
||||
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.
|
||||
|
|
36
if.go
36
if.go
|
@ -1,6 +1,9 @@
|
|||
package water
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Interface is a TUN/TAP interface.
|
||||
type Interface struct {
|
||||
|
@ -46,29 +49,14 @@ func New(config Config) (ifce *Interface, err error) {
|
|||
if zeroConfig == config {
|
||||
config = defaultConfig()
|
||||
}
|
||||
return newDev(config)
|
||||
}
|
||||
|
||||
// NewTAP creates a new TAP interface whose name is ifName. If ifName is empty, a
|
||||
// default name (tap0, tap1, ... ) will be assigned. ifName should not exceed
|
||||
// 16 bytes. TAP interfaces are not supported on darwin.
|
||||
// ifName cannot be specified on windows, you will need ifce.Name() to use some cmds.
|
||||
//
|
||||
// Note: this function is deprecated and will be removed from the library.
|
||||
// Please use New() instead.
|
||||
func NewTAP(ifName string) (ifce *Interface, err error) {
|
||||
return newTAP(ifName)
|
||||
}
|
||||
|
||||
// NewTUN creates a new TUN interface whose name is ifName. If ifName is empty, a
|
||||
// default name (tap0, tap1, ... ) will be assigned. ifName should not exceed
|
||||
// ifName cannot be specified on windows, you will need ifce.Name() to use some cmds.
|
||||
//
|
||||
// Note: this function is deprecated and will be removed from the library.
|
||||
// Please use New() instead.
|
||||
// 16 bytes. Setting interface name is NOT supported on darwin.
|
||||
func NewTUN(ifName string) (ifce *Interface, err error) {
|
||||
return newTUN(ifName)
|
||||
switch config.DeviceType {
|
||||
case TUN:
|
||||
return newTUN(config)
|
||||
case TAP:
|
||||
return newTAP(config)
|
||||
default:
|
||||
return nil, errors.New("unknown device type")
|
||||
}
|
||||
}
|
||||
|
||||
// IsTUN returns true if ifce is a TUN interface.
|
||||
|
|
32
if_linux.go
Normal file
32
if_linux.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
// +build linux
|
||||
|
||||
package water
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewTAP creates a new TAP interface whose name is ifName. If ifName is empty, a
|
||||
// default name (tap0, tap1, ... ) will be assigned. ifName should not exceed
|
||||
// 16 bytes. TAP interfaces are not supported on darwin.
|
||||
// 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) {
|
||||
fmt.Println("Deprecated: NewTAP(..) may be removed in the future. Please use New() instead.")
|
||||
config := Config{DeviceType: TAP}
|
||||
config.Name = ifName
|
||||
return newTAP(config)
|
||||
}
|
||||
|
||||
// NewTUN creates a new TUN interface whose name is ifName. If ifName is empty, a
|
||||
// default name (tap0, tap1, ... ) will be assigned. ifName should not exceed
|
||||
// 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) {
|
||||
fmt.Println("Deprecated: NewTUN(..) may be removed in the future. Please use New() instead.")
|
||||
config := Config{DeviceType: TUN}
|
||||
config.Name = ifName
|
||||
return newTUN(config)
|
||||
}
|
16
if_unix.go
16
if_unix.go
|
@ -1,16 +0,0 @@
|
|||
// +build linux darwin
|
||||
|
||||
package water
|
||||
|
||||
import "errors"
|
||||
|
||||
func newDev(config Config) (ifce *Interface, err error) {
|
||||
switch config.DeviceType {
|
||||
case TUN:
|
||||
return newTUN(config.Name)
|
||||
case TAP:
|
||||
return newTAP(config.Name)
|
||||
default:
|
||||
return nil, errors.New("unknown device type")
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package water
|
||||
|
||||
import "errors"
|
||||
|
||||
func newDev(config Config) (ifce *Interface, err error) {
|
||||
if config.DeviceType != TAP && config.DeviceType != TUN {
|
||||
return nil, errors.New("unknown device type")
|
||||
}
|
||||
return openDev(config)
|
||||
}
|
|
@ -30,7 +30,7 @@ func TestBroadcast(t *testing.T) {
|
|||
brd = net.IPv4(10, 0, 42, 255)
|
||||
)
|
||||
|
||||
ifce, err := NewTAP("test")
|
||||
ifce, err := New(Config{DeviceType: TAP})
|
||||
if err != nil {
|
||||
t.Fatalf("creating TAP error: %v\n", err)
|
||||
}
|
||||
|
|
14
params_darwin.go
Normal file
14
params_darwin.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
// +build darwin
|
||||
|
||||
package water
|
||||
|
||||
// PlatformSpecificParams defines parameters in Config that are specific to
|
||||
// macOS. A zero-value of such type is valid, yielding an interface
|
||||
// with OS defined name.
|
||||
// Currently it is not possible to set the interface name in macOS.
|
||||
type PlatformSpecificParams struct {
|
||||
}
|
||||
|
||||
func defaultPlatformSpecificParams() PlatformSpecificParams {
|
||||
return PlatformSpecificParams{}
|
||||
}
|
38
params_linux.go
Normal file
38
params_linux.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// +build linux
|
||||
|
||||
package water
|
||||
|
||||
type DevicePermissions struct {
|
||||
// ID of the user which will be granted ownership of the device.
|
||||
// If set to a negative value, the owner value will not be changed.
|
||||
// By default, Linux sets the owner to -1, which allows any user.
|
||||
Owner uint
|
||||
|
||||
// ID of the group which will be granted access to the device.
|
||||
// If set to a negative value, the group value will not be changed.
|
||||
// By default, Linux sets the group to -1, which allows any group.
|
||||
Group uint
|
||||
}
|
||||
|
||||
// PlatformSpecificParams defines parameters in Config that are specific to
|
||||
// Linux. A zero-value of such type is valid, yielding an interface
|
||||
// with OS defined name.
|
||||
type PlatformSpecificParams struct {
|
||||
// Name is the name to be set for the interface to be created. This overrides
|
||||
// the default name assigned by OS such as tap0 or tun0. A zero-value of this
|
||||
// field, i.e. an empty string, indicates that the default name should be
|
||||
// used.
|
||||
Name string
|
||||
|
||||
// Enable or disable persistence mode for the interface device.
|
||||
Persist bool
|
||||
|
||||
// Owner and Group permissions for the device.
|
||||
// A zero-value of this field, i.e. nil, indicates that no changes to owner
|
||||
// or group will be made.
|
||||
Permissions *DevicePermissions
|
||||
}
|
||||
|
||||
func defaultPlatformSpecificParams() PlatformSpecificParams {
|
||||
return PlatformSpecificParams{}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// +build linux darwin
|
||||
|
||||
package water
|
||||
|
||||
// PlatformSpecificParams defines parameters in Config that are specific to
|
||||
// Linux and macOS. A zero-value of such type is valid, yielding an interface
|
||||
// with OS defined name.
|
||||
type PlatformSpecificParams struct {
|
||||
// Name is the name to be set for the interface to be created. This overrides
|
||||
// the default name assigned by OS such as tap0 or tun0. A zero-value of this
|
||||
// field, i.e. an emapty string, indicates that the default name should be
|
||||
// used.
|
||||
Name string
|
||||
}
|
||||
|
||||
func defaultPlatformSpecificParams() PlatformSpecificParams {
|
||||
return PlatformSpecificParams{}
|
||||
}
|
|
@ -61,7 +61,7 @@ type sockaddrCtl struct {
|
|||
|
||||
var sockaddrCtlSize uintptr = 32
|
||||
|
||||
func newTUN(string) (ifce *Interface, err error) {
|
||||
func newTUN(config Config) (ifce *Interface, err error) {
|
||||
var fd int
|
||||
// Supposed to be socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), but ...
|
||||
//
|
||||
|
@ -122,6 +122,10 @@ func newTUN(string) (ifce *Interface, err error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func newTAP(config Config) (ifce *Interface, err error) {
|
||||
return nil, errors.New("tap interface not implemented on this platform")
|
||||
}
|
||||
|
||||
// tunReadCloser is a hack to work around the first 4 bytes "packet
|
||||
// information" because there doesn't seem to be an IFF_NO_PI for darwin.
|
||||
type tunReadCloser struct {
|
||||
|
@ -189,7 +193,3 @@ func (t *tunReadCloser) Close() error {
|
|||
|
||||
return t.f.Close()
|
||||
}
|
||||
|
||||
func newTAP(ifName string) (ifce *Interface, err error) {
|
||||
return nil, errors.New("tap interface not implemented on this platform")
|
||||
}
|
||||
|
|
|
@ -21,28 +21,46 @@ type ifReq struct {
|
|||
pad [0x28 - 0x10 - 2]byte
|
||||
}
|
||||
|
||||
func newTAP(ifName string) (ifce *Interface, err error) {
|
||||
func ioctl(fd uintptr, request int, argp uintptr) error {
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(request), argp)
|
||||
if errno != 0 {
|
||||
return os.NewSyscallError("ioctl", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newTAP(config Config) (ifce *Interface, err error) {
|
||||
file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := createInterface(file.Fd(), ifName, cIFF_TAP|cIFF_NO_PI)
|
||||
name, err := createInterface(file.Fd(), config.Name, cIFF_TAP|cIFF_NO_PI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = setDeviceOptions(file.Fd(), config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ifce = &Interface{isTAP: true, ReadWriteCloser: file, name: name}
|
||||
return
|
||||
}
|
||||
|
||||
func newTUN(ifName string) (ifce *Interface, err error) {
|
||||
func newTUN(config Config) (ifce *Interface, err error) {
|
||||
file, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := createInterface(file.Fd(), ifName, cIFF_TUN|cIFF_NO_PI)
|
||||
name, err := createInterface(file.Fd(), config.Name, cIFF_TUN|cIFF_NO_PI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = setDeviceOptions(file.Fd(), config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ifce = &Interface{isTAP: false, ReadWriteCloser: file, name: name}
|
||||
return
|
||||
}
|
||||
|
@ -51,11 +69,37 @@ func createInterface(fd uintptr, ifName string, flags uint16) (createdIFName str
|
|||
var req ifReq
|
||||
req.Flags = flags
|
||||
copy(req.Name[:], ifName)
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TUNSETIFF), uintptr(unsafe.Pointer(&req)))
|
||||
if errno != 0 {
|
||||
err = errno
|
||||
|
||||
err = ioctl(fd, syscall.TUNSETIFF, uintptr(unsafe.Pointer(&req)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
createdIFName = strings.Trim(string(req.Name[:]), "\x00")
|
||||
return
|
||||
}
|
||||
|
||||
func setDeviceOptions(fd uintptr, config Config) (err error) {
|
||||
|
||||
// Device Permissions
|
||||
if config.Permissions != nil {
|
||||
|
||||
// Set Owner
|
||||
if err = ioctl(fd, syscall.TUNSETOWNER, uintptr(config.Permissions.Owner)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set Group
|
||||
if err = ioctl(fd, syscall.TUNSETGROUP, uintptr(config.Permissions.Group)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Set/Clear Persist Device Flag
|
||||
value := 0
|
||||
if config.Persist {
|
||||
value = 1
|
||||
}
|
||||
return ioctl(fd, syscall.TUNSETPERSIST, uintptr(value))
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ package water
|
|||
|
||||
import "errors"
|
||||
|
||||
func newTAP(ifName string) (ifce *Interface, err error) {
|
||||
func newTAP(config Config) (ifce *Interface, err error) {
|
||||
return nil, errors.New("tap interface not implemented on this platform")
|
||||
}
|
||||
|
||||
func newTUN(ifName string) (ifce *Interface, err error) {
|
||||
func newTUN(config Config) (ifce *Interface, err error) {
|
||||
return nil, errors.New("tap interface not implemented on this platform")
|
||||
}
|
||||
|
|
|
@ -289,14 +289,10 @@ func openDev(config Config) (ifce *Interface, err error) {
|
|||
return nil, errIfceNameNotFound
|
||||
}
|
||||
|
||||
func newTAP(ifName string) (ifce *Interface, err error) {
|
||||
config := defaultConfig()
|
||||
config.DeviceType = TAP
|
||||
func newTAP(config Config) (ifce *Interface, err error) {
|
||||
return openDev(config)
|
||||
}
|
||||
|
||||
func newTUN(ifName string) (ifce *Interface, err error) {
|
||||
config := defaultConfig()
|
||||
config.DeviceType = TUN
|
||||
func newTUN(config Config) (ifce *Interface, err error) {
|
||||
return openDev(config)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue