From: Bruno Clermont Date: Wed, 8 Feb 2017 10:56:10 +0000 (+0800) Subject: Improve TAP X-Git-Url: http://www.git.cypherpunks.ru/?p=govpn.git;a=commitdiff_plain;h=8f32c1c115a7f686d9cbec4f35b930cbf53e6b21 Improve TAP - use advanced feature of new version of water to improve configuration - add support for windows (TAP only), osx and android - add support for Darwin/OSX (TUN only - add support for Android (using file descriptor) - TAP can be `Close`'d --- diff --git a/src/cypherpunks.ru/govpn/tap.go b/src/cypherpunks.ru/govpn/tap.go index f24630a..6485501 100644 --- a/src/cypherpunks.ru/govpn/tap.go +++ b/src/cypherpunks.ru/govpn/tap.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2017 Sergey Matveev +Copyright (C) 2014-2016 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,24 +20,34 @@ package govpn import ( "io" + + "github.com/Sirupsen/logrus" + "github.com/pkg/errors" +) + +const ( + interfaceTap = "tap" + interfaceTun = "tun" ) // TAP is a TUN or a TAP interface. +// TODO: rename to something more... generic? type TAP struct { Name string Sink chan []byte - dev io.ReadWriter + dev io.ReadWriteCloser } var ( - taps = make(map[string]*TAP) + taps = make(map[string]*TAP) + errUnsupportedInterface = errors.New("Unsupported interface") ) // NewTAP creates a new TUN/TAP virtual interface func NewTAP(ifaceName string, mtu int) (*TAP, error) { - tapRaw, err := newTAPer(ifaceName) + tapRaw, err := newTAPer(&ifaceName) if err != nil { - return nil, err + return nil, errors.Wrap(err, "newTAPer") } tap := TAP{ Name: ifaceName, @@ -60,7 +70,15 @@ func NewTAP(ifaceName string, mtu int) (*TAP, error) { bufZ = !bufZ n, err = tap.dev.Read(buf) if err != nil { - panic("Reading TUN/TAP:" + err.Error()) + logger.WithError(err).WithFields(logrus.Fields{ + "func": logFuncPrefix + "TAP read sink loop", + "name": tap.Name, + "mtu": mtu, + }).Error("Can't read interface") + return + // TODO: need a way to warn consumer that something is wrong + // TODO: to force peer to just disconnect + // TODO: use the client/server error channel? } tap.Sink <- buf[:n] } @@ -68,8 +86,15 @@ func NewTAP(ifaceName string, mtu int) (*TAP, error) { return &tap, nil } -func (t *TAP) Write(data []byte) (n int, err error) { - return t.dev.Write(data) +func (t *TAP) Write(data []byte) (int, error) { + n, err := t.dev.Write(data) + return n, errors.Wrapf(err, "t.dev.Write %d", len(data)) +} + +// Close close TAP/TUN virtual network interface +func (t *TAP) Close() error { + // TODO add chan to stop read loop + return t.dev.Close() } // TAPListen opens an existing TAP (creates if none exists) @@ -80,7 +105,7 @@ func TAPListen(ifaceName string, mtu int) (*TAP, error) { } tap, err := NewTAP(ifaceName, mtu) if err != nil { - return nil, err + return nil, errors.Wrap(err, "NewTAP") } taps[ifaceName] = tap return tap, nil diff --git a/src/cypherpunks.ru/govpn/tap_android.go b/src/cypherpunks.ru/govpn/tap_android.go new file mode 100644 index 0000000..ce425a6 --- /dev/null +++ b/src/cypherpunks.ru/govpn/tap_android.go @@ -0,0 +1,78 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2016 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package govpn + +import ( + "os" + "syscall" + "time" + + "github.com/Sirupsen/logrus" +) + +func TapListenFileDescriptor(fd uintptr, ifaceName string, mtu int) *TAP { + tap, exists := taps[ifaceName] + if exists { + return tap + } + + tap = &TAP{ + Name: ifaceName, + dev: os.NewFile(fd, ifaceName), + Sink: make(chan []byte), + } + go func() { + var n int + var err error + var buf []byte + buf0 := make([]byte, mtu) + buf1 := make([]byte, mtu) + bufZ := false + for { + if bufZ { + buf = buf0 + } else { + buf = buf1 + } + bufZ = !bufZ + n, err = tap.dev.Read(buf) + if err != nil { + e, ok := err.(*os.PathError) + if ok && e.Err == syscall.EAGAIN { + time.Sleep(time.Millisecond * 20) + continue + } + + logger.WithError(err).WithFields(logrus.Fields{ + "func", logFuncPrefix + "TUN read sink loop", + "name": tap.Name, + "mtu": mtu, + }).Error("Can't read interface, stop") + return + // TODO: need a way to warn consumer that something is wrong + // TODO: to force peer to just disconnect + // TODO: use the client/server error channel? + } else { + tap.Sink <- buf[:n] + } + } + }() + taps[ifaceName] = tap + return tap +} diff --git a/src/cypherpunks.ru/govpn/tap_darwin.go b/src/cypherpunks.ru/govpn/tap_darwin.go new file mode 100644 index 0000000..cbb0212 --- /dev/null +++ b/src/cypherpunks.ru/govpn/tap_darwin.go @@ -0,0 +1,38 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2016 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package govpn + +import ( + "io" + "strings" + + "github.com/pkg/errors" + "github.com/songgao/water" +) + +func newTAPer(ifaceName string) (io.ReadWriteCloser, error) { + if !strings.HasPrefix(ifaceName, interfaceTun) { + return nil, errors.Wrap(errUnsupportedInterface, ifaceName) + } + if ifaceName != interfaceTun { + return nil, errors.Errorf("Darwin don't allow to set an interface name, only %q is supported", ifaceName) + } + output, err := water.New(water.Config{DeviceType: water.TUN}) + return output, errors.Wrap(err, "water.New") +} diff --git a/src/cypherpunks.ru/govpn/tap_linux.go b/src/cypherpunks.ru/govpn/tap_linux.go index 2f901eb..72d52f2 100644 --- a/src/cypherpunks.ru/govpn/tap_linux.go +++ b/src/cypherpunks.ru/govpn/tap_linux.go @@ -1,8 +1,19 @@ -// +build linux - /* GoVPN -- simple secure free software virtual private network daemon Copyright (C) 2014-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ package govpn @@ -11,13 +22,35 @@ import ( "io" "strings" + "github.com/pkg/errors" "github.com/songgao/water" ) -func newTAPer(ifaceName string) (io.ReadWriter, error) { - if strings.HasPrefix(ifaceName, "tap") { - return water.NewTAP(ifaceName) +func newTAPer(ifaceName *string) (io.ReadWriteCloser, error) { + config := water.Config{} + + if len(*ifaceName) == 0 { + return nil, errors.New("Can't figure interface type, empty name") + } + + if strings.HasPrefix(*ifaceName, interfaceTap) { + config.DeviceType = water.TAP + if len(*ifaceName) > len(interfaceTap) { + config.Name = *ifaceName + } + } else if strings.HasPrefix(*ifaceName, interfaceTun) { + config.DeviceType = water.TUN + if len(*ifaceName) > len(interfaceTun) { + config.Name = *ifaceName + } } else { - return water.NewTUN(ifaceName) + return nil, errors.Errorf("Unrecognized interface name %q", *ifaceName) + } + + output, err := water.New(config) + if err != nil { + return nil, errors.Wrap(err, "water.New") } + *ifaceName = output.Name() + return output, nil } diff --git a/src/cypherpunks.ru/govpn/tap_windows.go b/src/cypherpunks.ru/govpn/tap_windows.go new file mode 100644 index 0000000..4ac7799 --- /dev/null +++ b/src/cypherpunks.ru/govpn/tap_windows.go @@ -0,0 +1,35 @@ +/* +GoVPN -- simple secure free software virtual private network daemon +Copyright (C) 2014-2016 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package govpn + +import ( + "io" + "strings" + + "github.com/pkg/errors" + "github.com/songgao/water" +) + +func newTAPer(ifaceName *string) (io.ReadWriteCloser, error) { + if strings.HasPrefix(*ifaceName, interfaceTun) { + return nil, errors.Wrap(errUnsupportedInterface, *ifaceName) + } + output, err := water.NewTAP(*ifaceName) + return output, errors.Wrap(err, "water.NewTAP") +}