Browse Source

Merge pull request #25 from ArroyoNetworks/reattach

linux: Support for Persistent Devices and Setting Owner/Group
Song Gao 8 years ago
parent
commit
99d07fc117
14 changed files with 164 additions and 92 deletions
  1. 2 1
      CONTRIBUTORS
  2. 5 0
      README.md
  3. 12 24
      if.go
  4. 32 0
      if_linux.go
  5. 0 16
      if_unix.go
  6. 0 12
      if_windows.go
  7. 1 1
      ipv4_test.go
  8. 14 0
      params_darwin.go
  9. 38 0
      params_linux.go
  10. 0 18
      params_unix.go
  11. 5 5
      syscalls_darwin.go
  12. 51 7
      syscalls_linux.go
  13. 2 2
      syscalls_other.go
  14. 2 6
      syscalls_windows.go

+ 2 - 1
CONTRIBUTORS

@@ -3,4 +3,5 @@ Harshal Sheth <[email protected]>
 KOJIMA Takanori <[email protected]>
 Sean Purser-Haskell <[email protected]>
 daregod <[email protected]>
-Lucus Lee <[email protected]>
+Lucus Lee <[email protected]>
+Arroyo Networks, LLC <[email protected]>

+ 5 - 0
README.md

@@ -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.

+ 12 - 24
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 - 0
if_linux.go

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

+ 0 - 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")
-	}
-}

+ 0 - 12
if_windows.go

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

+ 1 - 1
ipv4_test.go

@@ -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 - 0
params_darwin.go

@@ -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 - 0
params_linux.go

@@ -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{}
+}

+ 0 - 18
params_unix.go

@@ -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{}
-}

+ 5 - 5
syscalls_darwin.go

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

+ 51 - 7
syscalls_linux.go

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

+ 2 - 2
syscalls_other.go

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

+ 2 - 6
syscalls_windows.go

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