diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 95bdcbd..3a84f44 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3,4 +3,5 @@ Harshal Sheth KOJIMA Takanori Sean Purser-Haskell daregod -Lucus Lee \ No newline at end of file +Lucus Lee +Arroyo Networks, LLC \ No newline at end of file diff --git a/README.md b/README.md index d723413..b53f1db 100644 --- a/README.md +++ b/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. diff --git a/if.go b/if.go index 2113288..9f90485 100644 --- a/if.go +++ b/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. diff --git a/if_linux.go b/if_linux.go new file mode 100644 index 0000000..7752dc0 --- /dev/null +++ b/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) +} diff --git a/if_unix.go b/if_unix.go deleted file mode 100644 index f2de759..0000000 --- a/if_unix.go +++ /dev/null @@ -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") - } -} diff --git a/if_windows.go b/if_windows.go deleted file mode 100644 index 3ef1f8e..0000000 --- a/if_windows.go +++ /dev/null @@ -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) -} diff --git a/ipv4_test.go b/ipv4_test.go index 3a675b2..ec6d89b 100644 --- a/ipv4_test.go +++ b/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) } diff --git a/params_darwin.go b/params_darwin.go new file mode 100644 index 0000000..bba950e --- /dev/null +++ b/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{} +} diff --git a/params_linux.go b/params_linux.go new file mode 100644 index 0000000..41203f6 --- /dev/null +++ b/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{} +} diff --git a/params_unix.go b/params_unix.go deleted file mode 100644 index 4a926c7..0000000 --- a/params_unix.go +++ /dev/null @@ -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{} -} diff --git a/syscalls_darwin.go b/syscalls_darwin.go index 6a473f8..c998557 100644 --- a/syscalls_darwin.go +++ b/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") -} diff --git a/syscalls_linux.go b/syscalls_linux.go index e1ba63f..50a945c 100644 --- a/syscalls_linux.go +++ b/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)) + +} diff --git a/syscalls_other.go b/syscalls_other.go index ab3b9e5..ed49244 100644 --- a/syscalls_other.go +++ b/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") } diff --git a/syscalls_windows.go b/syscalls_windows.go index 5dc4013..a06c572 100644 --- a/syscalls_windows.go +++ b/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) }