]> Cypherpunks.ru repositories - govpn.git/commitdiff
Huge code refactoring
authorSergey Matveev <stargrave@stargrave.org>
Sun, 13 Sep 2015 16:39:55 +0000 (19:39 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 13 Sep 2015 17:33:22 +0000 (20:33 +0300)
* Code simplification. Lesser synchronization channels
* Processing parallelization of clients on the server side
* Double buffers for network/Ethernet packets prefetching
* Removed length prefix in TCP messages. Now they are
  indistinguishable from random
* Constant time nonce uniqueness checking
* Increased performance

Signed-off-by: Sergey Matveev <stargrave@stargrave.org>
20 files changed:
doc/developer.texi
doc/netproto.texi
doc/todo.texi
doc/transport.texi
doc/user.texi
src/govpn/cmd/govpn-client/main.go
src/govpn/cmd/govpn-client/proxy.go
src/govpn/cmd/govpn-client/tcp.go
src/govpn/cmd/govpn-client/udp.go
src/govpn/cmd/govpn-server/common.go [new file with mode: 0644]
src/govpn/cmd/govpn-server/main.go
src/govpn/cmd/govpn-server/proxy.go
src/govpn/cmd/govpn-server/tcp.go
src/govpn/cmd/govpn-server/udp.go
src/govpn/common.go
src/govpn/govpn.go
src/govpn/handshake.go
src/govpn/peer.go [new file with mode: 0644]
src/govpn/tap.go
src/govpn/transport.go [deleted file]

index 81360b26b0dde4c396d16e2b9dc12e6c34343a08..60352254bbbdb391b2f08e17b97e38539e9ac605 100644 (file)
@@ -19,10 +19,10 @@ and @url{http://ed25519.cr.yp.to/, Ed25519}.
 @url{https://en.wikipedia.org/wiki/PBKDF2, PBKDF2} based on
 @url{https://en.wikipedia.org/wiki/SHA-2, SHA-512}.
 @item Packet overhead
-26 bytes per packet. Two more bytes in TCP mode.
+26 bytes per packet.
 @item Handshake overhead
 4 UDP (2 from client, 2 from server) packets (round-trips for TCP),
-264 bytes total payload (8 bytes more in TCP mode).
+264 bytes total payload.
 @item Entropy required
 832 bits in average on client, 832 bits in average on server side per
 handshake.
index 3aef447e4997fcc02890310e1b5cd9aeb867d734..d033656901f090a735b39a268ad49b577969b867 100644 (file)
@@ -3,8 +3,7 @@
 
 You can use either UDP or TCP underlying network transport protocols.
 
-TCP consumes more traffic: two additional bytes per packet. Also it is
-has more complex and slightly slower code. Moreover because of packet
-loss and TCP reliability it can lead to "meltdown" effect: significant
-performance decrease of underlying TCP connections. So generally TCP is
-not advisable for VPNs, but it can help with some nasty firewalls.
+TCP is more resource hungry. Moreover because of packet loss and TCP
+reliability it can lead to "meltdown" effect: significant performance
+loss of underlying TCP connections. Generally TCP is not advisable for
+VPNs, but it can help with some nasty firewalls.
index f8064cfa8902dc4bb01dd2e4c54f0541e63a49b0..1372c20ac1a9aba2226638f6f8d82e0803637ae1 100644 (file)
@@ -5,6 +5,5 @@
 @item Ability to tunnel only specified TCP connections, without full
   featured VPN solution. Similar to ssh -R.
 @item Ability to noise handshake packets sizes.
-@item Increase performance.
 @item Randomize ports usage.
 @end itemize
index 776c8c8edd77f7fdab5299d61a22586fe1bda5f0..97fdce8ba04e397dca7542d38a3d9214196166cb 100644 (file)
@@ -2,45 +2,60 @@
 @section Transport protocol
 
 @verbatim
-[PktLen] + ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE) + ENCn(SERIAL) +
-    AUTH(ENC(KEY, ENCn(SERIAL), DATA_SIZE+DATA+NOISE) + ENCn(SERIAL))
+TAG || ENCRYPTED || NONCE <-- PACKET
+ ^         ^          ^
+ |         |          |
+ |         |          +------------+
+ |         |                       |
+ |         +------------+          |
+ |                      |          |
+ +-->AUTH(AUTH_KEY, ENCRYPTED || NONCE)
+                        ^          ^
+                        |          |
++-----------------------+          |
+|                                  |
+|                   +--------------+
+|                   |
++--> ENCRYPT(KEY, NONCE, PAYLOAD)
+                    ^       ^
+                    |       |
+                    |       +--> SIZE || DATA [|| NOISE]
+                    |
+                    +--> PRP(PRP_KEY, SERIAL)
 @end verbatim
 
-All transport and handshake messages are indistinguishable from
-pseudo random noise, except when using TCP connections.
-
-@code{PktLen} is used only with TCP connections. It is big-endian
-@emph{uint16} length of the whole packet (except @code{PktLen} itself).
-
 @code{SERIAL} is message's serial number. Odds are reserved for
 client(->server) messages, evens for server(->client) messages.
 
-@code{ENCn} is XTEA block cipher algorithm used here as PRP (pseudo
+@code{PRP} is XTEA block cipher algorithm used here as PRP (pseudo
 random permutation function) to obfuscate @code{SERIAL}. Plaintext
 @code{SERIAL} state is kept in peers internal state, but encrypted
-before transmission. XTEA is compact and fast enough. Salsa20 is PRF
-function and requires much more code to create PRP from it.
+before transmission.
 
 XTEA's encryption key is the first 128-bit of Salsa20's output with
 established common key and zero nonce (message nonces start from 1).
 
-@code{ENC} is Salsa20 stream cipher, with established session @code{KEY}
-and obfuscated @code{SERIAL} used as a nonce. First 256 bits of
-Salsa20's output is used as Poly1305 authentication key, next 256 bits
-are ignored. All remaining output is XORed with the data, encrypting it.
+@verbatim
+PRP_KEY = ENCRYPT(KEY, 0, 128-bit)
+@end verbatim
+
+@code{ENCRYPT} is Salsa20 stream cipher, with established session
+@code{KEY} and obfuscated @code{SERIAL} used as a nonce. 512 bit of
+Salsa20's output is ignored and only remaining is XORed with ther data,
+encrypting it.
 
-@code{DATA_SIZE} is big-endian @emph{uint16} storing length of the
+@code{SIZE} is big-endian @emph{uint16} storing length of the
 @code{DATA}.
 
 @code{NOISE} is optional. It is just some junk data, intended to fill up
 packet to MTU size. This is useful for concealing payload packets length.
 
 @code{AUTH} is Poly1305 authentication function. First 256 bits of
-Salsa20 output are used as a one-time key for @code{AUTH}.
+Salsa20's output are used as a one-time key for @code{AUTH}.
+
+@verbatim
+AUTH_KEY = ENCRYPT(KEY, NONCE, 256 bit)
+@end verbatim
 
 To prevent replay attacks we must remember received @code{SERIAL}s and
-if meet one, then drop it. Basically we could just store latest number
-and check if received one is greater, but because of UDP packets
-reordering this can lead to valid packets dropping and overall
-performance degradation. We store up to 256 seen nonces in hash
-structure, in two swapping buckets.
+drop when receiving duplicate ones.
index 9a5a02516450a173d005bd4b4d45ed66dc69b90c..6eae9a3bcde5081df8eeb0fda83c0364f53b1fb9 100644 (file)
@@ -11,7 +11,7 @@ automate it using up and down shell scripts.
 
 What network performance can user expect? For example single
 @emph{Intel i5-2450M 2.5 GHz} core on @emph{FreeBSD 10.2 amd64}
-with @emph{Go 1.5} gives 435 Mbps TCP (over UDP) throughput.
+with @emph{Go 1.5.1} gives 786 Mbps (UDP transport) throughput.
 
 @menu
 * EGD:: Entropy gathering daemon
index 6d312ce20e26873ca6c4153c8b30f1e41fd88ade..d88bb46028c26643713e7a1cc09aa368d562e355 100644 (file)
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Simple secure free software virtual private network daemon client.
+// Simple secure, DPI/censorship-resistant free software VPN daemon client.
 package main
 
 import (
@@ -46,11 +46,17 @@ var (
        noisy      = flag.Bool("noise", false, "Enable noise appending")
        cpr        = flag.Int("cpr", 0, "Enable constant KiB/sec out traffic rate")
        egdPath    = flag.String("egd", "", "Optional path to EGD socket")
+
+       conf        *govpn.PeerConf
+       tap         *govpn.TAP
+       timeout     int
+       firstUpCall bool = true
+       knownPeers  govpn.KnownPeers
 )
 
 func main() {
        flag.Parse()
-       timeout := *timeoutP
+       timeout = *timeoutP
        var err error
        log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
 
@@ -67,7 +73,7 @@ func main() {
        }
 
        pub, priv := govpn.NewVerifier(id, govpn.StringFromFile(*keyPath))
-       conf := &govpn.PeerConf{
+       conf = &govpn.PeerConf{
                Id:          id,
                Timeout:     time.Second * time.Duration(timeout),
                NoiseEnable: *noisy,
@@ -76,41 +82,13 @@ func main() {
                DSAPriv:     priv,
        }
        govpn.PeersInitDummy(id, conf)
+       log.Println(govpn.VersionGet())
 
-       var conn govpn.RemoteConn
-       var sink chan []byte
-       var ready chan struct{}
-       switch *proto {
-       case "udp":
-               conn, sink, ready = startUDP()
-       case "tcp":
-               if *proxyAddr != "" {
-                       conn, sink, ready = proxyTCP()
-               } else {
-                       conn, sink, ready = startTCP()
-               }
-       default:
-               log.Fatalln("Unknown protocol specified")
-       }
-
-       tap, ethSink, ethReady, _, err := govpn.TAPListen(
-               *ifaceName,
-               time.Second*time.Duration(timeout),
-               *cpr,
-       )
+       tap, err = govpn.TAPListen(*ifaceName)
        if err != nil {
                log.Fatalln("Can not listen on TAP interface:", err)
        }
 
-       timeouts := 0
-       firstUpCall := true
-       var peer *govpn.Peer
-       var ethPkt []byte
-       var pkt []byte
-       knownPeers := govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
-
-       log.Println(govpn.VersionGet())
-       log.Println("Connected to", *proto, *remoteAddr)
        log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
        if *stats != "" {
                log.Println("Stats are going to listen on", *stats)
@@ -124,65 +102,35 @@ func main() {
        termSignal := make(chan os.Signal, 1)
        signal.Notify(termSignal, os.Interrupt, os.Kill)
 
-       log.Println("Starting handshake")
-       handshake := govpn.HandshakeStart(*remoteAddr, conn, conf)
-
 MainCycle:
        for {
-               if peer != nil && (peer.BytesIn+peer.BytesOut) > govpn.MaxBytesPerKey {
-                       peer.Zero()
-                       peer = nil
-                       handshake = govpn.HandshakeStart(*remoteAddr, conn, conf)
-                       log.Println("Rehandshaking")
+               timeouted := make(chan struct{})
+               rehandshaking := make(chan struct{})
+               termination := make(chan struct{})
+               switch *proto {
+               case "udp":
+                       go startUDP(timeouted, rehandshaking, termination)
+               case "tcp":
+                       if *proxyAddr != "" {
+                               go proxyTCP(timeouted, rehandshaking, termination)
+                       } else {
+                               go startTCP(timeouted, rehandshaking, termination)
+                       }
+               default:
+                       log.Fatalln("Unknown protocol specified")
                }
                select {
                case <-termSignal:
+                       log.Fatalln("Finishing...")
+                       termination <- struct{}{}
                        break MainCycle
-               case ethPkt = <-ethSink:
-                       if peer == nil {
-                               if len(ethPkt) > 0 {
-                                       ethReady <- struct{}{}
-                               }
-                               continue
-                       }
-                       peer.EthProcess(ethPkt, ethReady)
-               case pkt = <-sink:
-                       timeouts++
-                       if timeouts >= timeout {
-                               break MainCycle
-                       }
-                       if pkt == nil {
-                               ready <- struct{}{}
-                               continue
-                       }
-
-                       if peer == nil {
-                               if govpn.IDsCache.Find(pkt) == nil {
-                                       log.Println("Invalid identity in handshake packet")
-                                       ready <- struct{}{}
-                                       continue
-                               }
-                               if p := handshake.Client(pkt); p != nil {
-                                       log.Println("Handshake completed")
-                                       if firstUpCall {
-                                               go govpn.ScriptCall(*upPath, *ifaceName)
-                                               firstUpCall = false
-                                       }
-                                       peer = p
-                                       handshake.Zero()
-                                       handshake = nil
-                               }
-                               ready <- struct{}{}
-                               continue
-                       }
-                       if peer == nil {
-                               ready <- struct{}{}
-                               continue
-                       }
-                       if peer.PktProcess(pkt, tap, ready) {
-                               timeouts = 0
-                       }
+               case <-timeouted:
+                       break MainCycle
+               case <-rehandshaking:
                }
+               close(timeouted)
+               close(rehandshaking)
+               close(termination)
        }
        govpn.ScriptCall(*downPath, *ifaceName)
 }
index 98c9098e2dbf16ce7580ba7be2221fc907529993..a0a9a5459739bb9cfdc44607e1f41f46e482fc86 100644 (file)
@@ -24,11 +24,9 @@ import (
        "log"
        "net"
        "net/http"
-
-       "govpn"
 )
 
-func proxyTCP() (govpn.RemoteConn, chan []byte, chan struct{}) {
+func proxyTCP(timeouted, rehandshaking, termination chan struct{}) {
        proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr)
        if err != nil {
                log.Fatalln("Can not resolve proxy address:", err)
@@ -52,9 +50,5 @@ func proxyTCP() (govpn.RemoteConn, chan []byte, chan struct{}) {
        if err != nil || resp.StatusCode != http.StatusOK {
                log.Fatalln("Unexpected response from proxy")
        }
-       sink := make(chan []byte)
-       ready := make(chan struct{})
-       go handleTCP(conn, sink, ready)
-       go func() { ready <- struct{}{} }()
-       return TCPSender{conn}, sink, ready
+       go handleTCP(conn, timeouted, rehandshaking, termination)
 }
index dcf7b14b88f503add1378aadcb1c3005df76ff9f..49d3ae92cf02e86a9f5c2e5937997ae756b124f7 100644 (file)
@@ -19,84 +19,144 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
+       "bytes"
        "encoding/binary"
        "log"
        "net"
+       "sync/atomic"
+       "time"
 
        "govpn"
 )
 
-// TCPSender prepends size prefix to each outgoing packet.
-type TCPSender struct {
-       conn *net.TCPConn
-}
-
-func (c TCPSender) Write(data []byte) (int, error) {
-       size := make([]byte, 2)
-       binary.BigEndian.PutUint16(size, uint16(len(data)))
-       return c.conn.Write(append(size, data...))
-}
-
-func (c TCPSender) Reorderable() bool {
-       return false
-}
-
-func startTCP() (govpn.RemoteConn, chan []byte, chan struct{}) {
+func startTCP(timeouted, rehandshaking, termination chan struct{}) {
        remote, err := net.ResolveTCPAddr("tcp", *remoteAddr)
        if err != nil {
                log.Fatalln("Can not resolve remote address:", err)
        }
-       c, err := net.DialTCP("tcp", nil, remote)
-       conn := TCPSender{c}
+       conn, err := net.DialTCP("tcp", nil, remote)
        if err != nil {
-               log.Fatalln("Can not connect TCP:", err)
+               log.Fatalln("Can not connect to remote address:", err)
        }
-       sink := make(chan []byte)
-       ready := make(chan struct{})
-       go handleTCP(c, sink, ready)
-       go func() { ready <- struct{}{} }()
-       return conn, sink, ready
+       handleTCP(conn, timeouted, rehandshaking, termination)
 }
 
-func handleTCP(conn *net.TCPConn, sink chan []byte, ready chan struct{}) {
-       var err error
-       var n int
-       var sizeNbuf int
-       sizeBuf := make([]byte, 2)
-       var sizeNeed uint16
-       var bufN uint16
+func handleTCP(conn *net.TCPConn, timeouted, rehandshaking, termination chan struct{}) {
+       hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
        buf := make([]byte, govpn.MTU)
+       var n int
+       var err error
+       var prev int
+       var peer *govpn.Peer
+       var terminator chan struct{}
+HandshakeCycle:
        for {
-               <-ready
-               if sizeNbuf != 2 {
-                       n, err = conn.Read(sizeBuf[sizeNbuf:2])
-                       if err != nil {
-                               break
-                       }
-                       sizeNbuf += n
-                       if sizeNbuf != 2 {
-                               sink <- nil
-                               continue
-                       }
-                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
-                       if int(sizeNeed) > govpn.MTU-2 {
-                               log.Println("Invalid TCP size, skipping")
-                               sizeNbuf = 0
-                               sink <- nil
-                               continue
-                       }
-                       bufN = 0
+               select {
+               case <-termination:
+                       break HandshakeCycle
+               default:
+               }
+               if prev == govpn.MTU {
+                       break HandshakeCycle
+               }
+
+               conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       // Either EOFed or timeouted
+                       break HandshakeCycle
                }
-       ReadMore:
-               if sizeNeed != bufN {
-                       n, err = conn.Read(buf[bufN:sizeNeed])
-                       if err != nil {
-                               break
+
+               prev += n
+               peerId := govpn.IDsCache.Find(buf[:prev])
+               if peerId == nil {
+                       continue
+               }
+               peer = hs.Client(buf[:prev])
+               prev = 0
+               if peer == nil {
+                       continue
+               }
+               log.Println("Handshake completed")
+               knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
+               if firstUpCall {
+                       go govpn.ScriptCall(*upPath, *ifaceName)
+                       firstUpCall = false
+               }
+               hs.Zero()
+               terminator = make(chan struct{})
+               go func() {
+                       heartbeat := time.NewTicker(peer.Timeout)
+                       var data []byte
+               Processor:
+                       for {
+                               select {
+                               case <-heartbeat.C:
+                                       peer.EthProcess(nil)
+                               case <-terminator:
+                                       break Processor
+                               case data = <-tap.Sink:
+                                       peer.EthProcess(data)
+                               }
                        }
-                       bufN += uint16(n)
-                       goto ReadMore
+                       heartbeat.Stop()
+                       peer.Zero()
+               }()
+               break HandshakeCycle
+       }
+       if hs != nil {
+               hs.Zero()
+       }
+       if peer == nil {
+               return
+       }
+
+       nonceExpectation := make([]byte, govpn.NonceSize)
+       binary.BigEndian.PutUint64(nonceExpectation, peer.NonceExpect)
+       peer.NonceCipher.Encrypt(nonceExpectation, nonceExpectation)
+       prev = 0
+       var i int
+TransportCycle:
+       for {
+               select {
+               case <-termination:
+                       break TransportCycle
+               default:
+               }
+               if prev == govpn.MTU {
+                       timeouted <- struct{}{}
+                       break TransportCycle
+               }
+               conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       // Either EOFed or timeouted
+                       timeouted <- struct{}{}
+                       break TransportCycle
+               }
+               prev += n
+       CheckMore:
+               i = bytes.Index(buf[:prev], nonceExpectation)
+               if i == -1 {
+                       continue
                }
-               sizeNbuf = 0
-               sink <- buf[:sizeNeed]
+               if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
+                       timeouted <- struct{}{}
+                       break TransportCycle
+               }
+               if atomic.LoadInt64(&peer.BytesIn)+atomic.LoadInt64(&peer.BytesOut) > govpn.MaxBytesPerKey {
+                       log.Println("Need rehandshake")
+                       rehandshaking <- struct{}{}
+                       break TransportCycle
+               }
+               binary.BigEndian.PutUint64(nonceExpectation, peer.NonceExpect)
+               peer.NonceCipher.Encrypt(nonceExpectation, nonceExpectation)
+               copy(buf, buf[i+govpn.NonceSize:prev])
+               prev = prev - i - govpn.NonceSize
+               goto CheckMore
+       }
+       if terminator != nil {
+               terminator <- struct{}{}
        }
+       peer.Zero()
 }
index 57a457f1a38c23d4da122feba23cd520b9336e41..0df7ab13a4b2c23f2b223932f24dc4a74a49af41 100644 (file)
@@ -21,49 +21,101 @@ package main
 import (
        "log"
        "net"
+       "sync/atomic"
        "time"
 
        "govpn"
 )
 
-type UDPSender struct {
-       conn *net.UDPConn
-}
-
-func (c UDPSender) Write(data []byte) (int, error) {
-       return c.conn.Write(data)
-}
-
-func (c UDPSender) Reorderable() bool {
-       return true
-}
-
-func startUDP() (govpn.RemoteConn, chan []byte, chan struct{}) {
+func startUDP(timeouted, rehandshaking, termination chan struct{}) {
        remote, err := net.ResolveUDPAddr("udp", *remoteAddr)
        if err != nil {
                log.Fatalln("Can not resolve remote address:", err)
        }
-       c, err := net.DialUDP("udp", nil, remote)
+       conn, err := net.DialUDP("udp", nil, remote)
        if err != nil {
                log.Fatalln("Can not listen on UDP:", err)
        }
-       sink := make(chan []byte)
-       ready := make(chan struct{})
-       go func() {
-               buf := make([]byte, govpn.MTU)
-               var n int
-               var err error
-               for {
-                       <-ready
-                       c.SetReadDeadline(time.Now().Add(time.Second))
-                       n, err = c.Read(buf)
-                       if err != nil {
-                               sink <- nil
-                               continue
+
+       hs := govpn.HandshakeStart(*remoteAddr, conn, conf)
+       buf := make([]byte, govpn.MTU)
+       var n int
+       var timeouts int
+       var peer *govpn.Peer
+       var terminator chan struct{}
+MainCycle:
+       for {
+               select {
+               case <-termination:
+                       break MainCycle
+               default:
+               }
+
+               conn.SetReadDeadline(time.Now().Add(time.Second))
+               n, err = conn.Read(buf)
+               if timeouts == timeout {
+                       log.Println("Timeouted")
+                       timeouted <- struct{}{}
+                       break
+               }
+               if err != nil {
+                       timeouts++
+                       continue
+               }
+               if peer != nil {
+                       if peer.PktProcess(buf[:n], tap, true) {
+                               timeouts = 0
+                       } else {
+                               timeouts++
+                       }
+                       if atomic.LoadInt64(&peer.BytesIn)+atomic.LoadInt64(&peer.BytesOut) > govpn.MaxBytesPerKey {
+                               log.Println("Need rehandshake")
+                               terminator <- struct{}{}
+                               terminator = nil
+                               rehandshaking <- struct{}{}
+                               break MainCycle
                        }
-                       sink <- buf[:n]
+                       continue
+               }
+               if govpn.IDsCache.Find(buf[:n]) == nil {
+                       log.Println("Invalid identity in handshake packet")
+                       continue
+               }
+               timeouts = 0
+               peer = hs.Client(buf[:n])
+               if peer == nil {
+                       continue
+               }
+               log.Println("Handshake completed")
+               knownPeers = govpn.KnownPeers(map[string]**govpn.Peer{*remoteAddr: &peer})
+               if firstUpCall {
+                       go govpn.ScriptCall(*upPath, *ifaceName)
+                       firstUpCall = false
                }
-       }()
-       ready <- struct{}{}
-       return UDPSender{c}, sink, ready
+               hs.Zero()
+               terminator = make(chan struct{})
+               go func() {
+                       heartbeat := time.NewTicker(peer.Timeout)
+                       var data []byte
+               Processor:
+                       for {
+                               select {
+                               case <-heartbeat.C:
+                                       peer.EthProcess(nil)
+                               case <-terminator:
+                                       break Processor
+                               case data = <-tap.Sink:
+                                       peer.EthProcess(data)
+                               }
+                       }
+                       heartbeat.Stop()
+                       peer.Zero()
+               }()
+       }
+       if terminator != nil {
+               terminator <- struct{}{}
+       }
+       if hs != nil {
+               hs.Zero()
+       }
 }
diff --git a/src/govpn/cmd/govpn-server/common.go b/src/govpn/cmd/govpn-server/common.go
new file mode 100644 (file)
index 0000000..92e1bb1
--- /dev/null
@@ -0,0 +1,62 @@
+package main
+
+import (
+       "bytes"
+       "path"
+       "sync"
+       "time"
+
+       "govpn"
+)
+
+type PeerState struct {
+       peer       *govpn.Peer
+       terminator chan struct{}
+       tap        *govpn.TAP
+}
+
+var (
+       handshakes map[string]*govpn.Handshake = make(map[string]*govpn.Handshake)
+       hsLock     sync.RWMutex
+
+       peers     map[string]*PeerState = make(map[string]*PeerState)
+       peersLock sync.RWMutex
+
+       peersById     map[govpn.PeerId]string = make(map[govpn.PeerId]string)
+       peersByIdLock sync.RWMutex
+
+       knownPeers govpn.KnownPeers
+       kpLock     sync.RWMutex
+)
+
+func peerReady(ps PeerState) {
+       var data []byte
+       heartbeat := time.NewTicker(ps.peer.Timeout)
+Processor:
+       for {
+               select {
+               case <-heartbeat.C:
+                       ps.peer.EthProcess(nil)
+               case <-ps.terminator:
+                       break Processor
+               case data = <-ps.tap.Sink:
+                       ps.peer.EthProcess(data)
+               }
+       }
+       ps.peer.Zero()
+       heartbeat.Stop()
+}
+
+func callUp(peerId *govpn.PeerId) (string, error) {
+       upPath := path.Join(govpn.PeersPath, peerId.String(), "up.sh")
+       result, err := govpn.ScriptCall(upPath, "")
+       if err != nil {
+               return "", err
+       }
+       sepIndex := bytes.Index(result, []byte{'\n'})
+       if sepIndex < 0 {
+               sepIndex = len(result)
+       }
+       ifaceName := string(result[:sepIndex])
+       return ifaceName, nil
+}
index 2aa5100e03627643da236dcc35c943903bc58ac9..dd338a2ce913dc596f3e29b98428aaf9bc1519f1 100644 (file)
@@ -16,11 +16,10 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Simple secure free software virtual private network daemon.
+// Simple secure, DPI/censorship-resistant free software VPN daemon.
 package main
 
 import (
-       "bytes"
        "flag"
        "log"
        "net"
@@ -42,48 +41,6 @@ var (
        egdPath   = flag.String("egd", "", "Optional path to EGD socket")
 )
 
-type Pkt struct {
-       addr  string
-       conn  govpn.RemoteConn
-       data  []byte
-       ready chan struct{}
-}
-
-type PeerReadyEvent struct {
-       peer  *govpn.Peer
-       iface string
-}
-
-type PeerState struct {
-       peer      *govpn.Peer
-       tap       *govpn.TAP
-       sink      chan []byte
-       ready     chan struct{}
-       terminate chan struct{}
-}
-
-func NewPeerState(peer *govpn.Peer, iface string) *PeerState {
-       tap, sink, ready, terminate, err := govpn.TAPListen(iface, peer.Timeout, peer.CPR)
-       if err != nil {
-               log.Println("Unable to create Eth", err)
-               return nil
-       }
-       state := PeerState{
-               peer:      peer,
-               tap:       tap,
-               sink:      sink,
-               ready:     ready,
-               terminate: terminate,
-       }
-       return &state
-}
-
-type EthEvent struct {
-       peer  *govpn.Peer
-       data  []byte
-       ready chan struct{}
-}
-
 func main() {
        flag.Parse()
        timeout := time.Second * time.Duration(govpn.TimeoutDefault)
@@ -92,21 +49,21 @@ func main() {
 
        govpn.MTU = *mtu
        govpn.PeersInit(*peersPath)
+       knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
 
        if *egdPath != "" {
                log.Println("Using", *egdPath, "EGD")
                govpn.EGDInit(*egdPath)
        }
 
-       sink := make(chan Pkt)
        switch *proto {
        case "udp":
-               startUDP(sink)
+               startUDP()
        case "tcp":
-               startTCP(sink)
+               startTCP()
        case "all":
-               startUDP(sink)
-               startTCP(sink)
+               startUDP()
+               startTCP()
        default:
                log.Fatalln("Unknown protocol specified")
        }
@@ -117,22 +74,6 @@ func main() {
        hsHeartbeat := time.Tick(timeout)
        go func() { <-hsHeartbeat }()
 
-       var state *govpn.Handshake
-       var peerState *PeerState
-       var peer *govpn.Peer
-       var exists bool
-       states := make(map[string]*govpn.Handshake)
-       peers := make(map[string]*PeerState)
-       peerReadySink := make(chan PeerReadyEvent)
-       knownPeers := govpn.KnownPeers(make(map[string]**govpn.Peer))
-       var peerReady PeerReadyEvent
-       var pkt Pkt
-       var ethEvent EthEvent
-       var peerId *govpn.PeerId
-       var peerConf *govpn.PeerConf
-       var handshakeProcessForce bool
-       ethSink := make(chan EthEvent)
-
        log.Println("Max MTU on TAP interface:", govpn.TAPMaxMTU())
        if *stats != "" {
                log.Println("Stats are going to listen on", *stats)
@@ -143,10 +84,11 @@ func main() {
                go govpn.StatsProcessor(statsPort, &knownPeers)
        }
        if *proxy != "" {
-               go proxyStart(sink)
+               go proxyStart()
        }
        log.Println("Server started")
 
+       var needsDeletion bool
 MainCycle:
        for {
                select {
@@ -154,128 +96,39 @@ MainCycle:
                        break MainCycle
                case <-hsHeartbeat:
                        now := time.Now()
-                       for addr, hs := range states {
+                       hsLock.Lock()
+                       for addr, hs := range handshakes {
                                if hs.LastPing.Add(timeout).Before(now) {
                                        log.Println("Deleting handshake state", addr)
                                        hs.Zero()
-                                       delete(states, addr)
+                                       delete(handshakes, addr)
                                }
                        }
-                       for addr, state := range peers {
-                               if state.peer.LastPing.Add(timeout).Before(now) {
-                                       log.Println("Deleting peer", state.peer)
+                       peersLock.Lock()
+                       peersByIdLock.Lock()
+                       kpLock.Lock()
+                       for addr, ps := range peers {
+                               ps.peer.BusyR.Lock()
+                               needsDeletion = ps.peer.LastPing.Add(timeout).Before(now)
+                               ps.peer.BusyR.Unlock()
+                               if needsDeletion {
+                                       log.Println("Deleting peer", ps.peer)
                                        delete(peers, addr)
                                        delete(knownPeers, addr)
+                                       delete(peersById, *ps.peer.Id)
                                        downPath := path.Join(
                                                govpn.PeersPath,
-                                               state.peer.Id.String(),
+                                               ps.peer.Id.String(),
                                                "down.sh",
                                        )
-                                       go govpn.ScriptCall(downPath, state.tap.Name)
-                                       state.terminate <- struct{}{}
-                                       state.peer.Zero()
+                                       go govpn.ScriptCall(downPath, ps.tap.Name)
+                                       ps.terminator <- struct{}{}
                                }
                        }
-               case peerReady = <-peerReadySink:
-                       for addr, state := range peers {
-                               if state.tap.Name != peerReady.iface {
-                                       continue
-                               }
-                               delete(peers, addr)
-                               delete(knownPeers, addr)
-                               state.terminate <- struct{}{}
-                               state.peer.Zero()
-                               break
-                       }
-                       state := NewPeerState(peerReady.peer, peerReady.iface)
-                       if state == nil {
-                               continue
-                       }
-                       peers[peerReady.peer.Addr] = state
-                       knownPeers[peerReady.peer.Addr] = &peerReady.peer
-                       states[peerReady.peer.Addr].Zero()
-                       delete(states, peerReady.peer.Addr)
-                       log.Println("Registered interface", peerReady.iface, "with peer", peer)
-                       go func(state *PeerState) {
-                               for data := range state.sink {
-                                       ethSink <- EthEvent{
-                                               peer:  state.peer,
-                                               data:  data,
-                                               ready: state.ready,
-                                       }
-                               }
-                       }(state)
-               case ethEvent = <-ethSink:
-                       if s, exists := peers[ethEvent.peer.Addr]; !exists || s.peer != ethEvent.peer {
-                               continue
-                       }
-                       ethEvent.peer.EthProcess(ethEvent.data, ethEvent.ready)
-               case pkt = <-sink:
-                       if pkt.data == nil {
-                               pkt.ready <- struct{}{}
-                               continue
-                       }
-                       handshakeProcessForce = false
-               HandshakeProcess:
-                       if _, exists = peers[pkt.addr]; handshakeProcessForce || !exists {
-                               peerId = govpn.IDsCache.Find(pkt.data)
-                               if peerId == nil {
-                                       log.Println("Unknown identity from", pkt.addr)
-                                       pkt.ready <- struct{}{}
-                                       continue
-                               }
-                               peerConf = peerId.Conf()
-                               if peerConf == nil {
-                                       log.Println("Can not get peer configuration", peerId.String())
-                                       pkt.ready <- struct{}{}
-                                       continue
-                               }
-                               state, exists = states[pkt.addr]
-                               if !exists {
-                                       state = govpn.HandshakeNew(pkt.addr, pkt.conn, peerConf)
-                                       states[pkt.addr] = state
-                               }
-                               peer = state.Server(pkt.data)
-                               if peer != nil {
-                                       log.Println("Peer handshake finished", peer)
-                                       if _, exists = peers[pkt.addr]; exists {
-                                               go func() {
-                                                       peerReadySink <- PeerReadyEvent{
-                                                               peer, peers[pkt.addr].tap.Name,
-                                                       }
-                                               }()
-                                       } else {
-                                               go func() {
-                                                       upPath := path.Join(govpn.PeersPath, peer.Id.String(), "up.sh")
-                                                       result, err := govpn.ScriptCall(upPath, "")
-                                                       if err != nil {
-                                                               return
-                                                       }
-                                                       sepIndex := bytes.Index(result, []byte{'\n'})
-                                                       if sepIndex < 0 {
-                                                               sepIndex = len(result)
-                                                       }
-                                                       ifaceName := string(result[:sepIndex])
-                                                       peerReadySink <- PeerReadyEvent{peer, ifaceName}
-                                               }()
-                                       }
-                               }
-                               if !handshakeProcessForce {
-                                       pkt.ready <- struct{}{}
-                               }
-                               continue
-                       }
-                       peerState, exists = peers[pkt.addr]
-                       if !exists {
-                               pkt.ready <- struct{}{}
-                               continue
-                       }
-                       // If it fails during processing, then try to work with it
-                       // as with handshake packet
-                       if !peerState.peer.PktProcess(pkt.data, peerState.tap, pkt.ready) {
-                               handshakeProcessForce = true
-                               goto HandshakeProcess
-                       }
+                       hsLock.Unlock()
+                       peersLock.Unlock()
+                       peersByIdLock.Unlock()
+                       kpLock.Unlock()
                }
        }
 }
index f1e84192bff8f739c189c77f53f9a8ee2eaa50a4..c05a3604887492cfb28405cad3c64b0ec355efd7 100644 (file)
@@ -23,9 +23,7 @@ import (
        "net/http"
 )
 
-type proxyHandler struct {
-       sink chan Pkt
-}
+type proxyHandler struct{}
 
 func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        conn, _, err := w.(http.Hijacker).Hijack()
@@ -34,17 +32,14 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
                return
        }
        conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
-       ready := make(chan struct{}, 1)
-       go handleTCP(conn, p.sink, ready)
-       ready <- struct{}{}
-
+       go handleTCP(conn)
 }
 
-func proxyStart(sink chan Pkt) {
+func proxyStart() {
        log.Println("HTTP proxy listening on:", *proxy)
        s := &http.Server{
                Addr:    *proxy,
-               Handler: proxyHandler{sink},
+               Handler: proxyHandler{},
        }
        log.Println("HTTP proxy result:", s.ListenAndServe())
 }
index d02565c6138c8ac08a94c60b8d7ed10a2ba92313..8aedfa9345dbb7b204a8b221f96d8381bccbfd98 100644 (file)
@@ -19,28 +19,16 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
+       "bytes"
        "encoding/binary"
        "log"
        "net"
+       "time"
 
        "govpn"
 )
 
-type TCPSender struct {
-       conn net.Conn
-}
-
-func (c TCPSender) Write(data []byte) (int, error) {
-       size := make([]byte, 2)
-       binary.BigEndian.PutUint16(size, uint16(len(data)))
-       return c.conn.Write(append(size, data...))
-}
-
-func (c TCPSender) Reorderable() bool {
-       return false
-}
-
-func startTCP(sink chan Pkt) {
+func startTCP() {
        bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
        if err != nil {
                log.Fatalln("Can not resolve bind address:", err)
@@ -52,59 +40,148 @@ func startTCP(sink chan Pkt) {
        log.Println("Listening on TCP", *bindAddr)
        go func() {
                for {
-                       conn, _ := listener.AcceptTCP()
-                       ready := make(chan struct{}, 1)
-                       go handleTCP(conn, sink, ready)
-                       ready <- struct{}{}
+                       conn, err := listener.AcceptTCP()
+                       if err != nil {
+                               log.Println("Error accepting TCP:", err)
+                               continue
+                       }
+                       go handleTCP(conn)
                }
        }()
 }
 
-func handleTCP(conn net.Conn, sink chan Pkt, ready chan struct{}) {
+func handleTCP(conn net.Conn) {
        addr := conn.RemoteAddr().String()
-       var err error
-       var n int
-       var sizeNbuf int
-       sizeBuf := make([]byte, 2)
-       var sizeNeed uint16
-       var bufN uint16
        buf := make([]byte, govpn.MTU)
+       var n int
+       var err error
+       var prev int
+       var hs *govpn.Handshake
+       var ps *PeerState
+       var peer *govpn.Peer
+       var tap *govpn.TAP
+       var conf *govpn.PeerConf
        for {
-               <-ready
-               if sizeNbuf != 2 {
-                       n, err = conn.Read(sizeBuf[sizeNbuf:2])
-                       if err != nil {
+               if prev == govpn.MTU {
+                       break
+               }
+               conn.SetReadDeadline(time.Now().Add(time.Duration(govpn.TimeoutDefault) * time.Second))
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       // Either EOFed or timeouted
+                       break
+               }
+               prev += n
+               peerId := govpn.IDsCache.Find(buf[:prev])
+               if peerId == nil {
+                       continue
+               }
+               if hs == nil {
+                       conf = peerId.Conf()
+                       if conf == nil {
+                               log.Println("Can not get peer configuration:", peerId.String())
                                break
                        }
-                       sizeNbuf += n
-                       if sizeNbuf != 2 {
-                               sink <- Pkt{ready: ready}
-                               continue
+                       hs = govpn.NewHandshake(addr, conn, conf)
+               }
+               peer = hs.Server(buf[:prev])
+               prev = 0
+               if peer == nil {
+                       continue
+               }
+               hs.Zero()
+               peersByIdLock.RLock()
+               addrPrev, exists := peersById[*peer.Id]
+               peersByIdLock.RUnlock()
+               if exists {
+                       peersLock.RLock()
+                       tap = peers[addrPrev].tap
+                       ps = &PeerState{
+                               peer:       peer,
+                               tap:        tap,
+                               terminator: peers[addrPrev].terminator,
                        }
-                       sizeNeed = binary.BigEndian.Uint16(sizeBuf)
-                       if int(sizeNeed) > govpn.MTU-2 {
-                               log.Println("Invalid TCP size, skipping")
-                               sizeNbuf = 0
-                               sink <- Pkt{ready: ready}
-                               continue
+                       peersLock.RUnlock()
+                       ps.terminator <- struct{}{}
+                       peersLock.Lock()
+                       peersByIdLock.Lock()
+                       kpLock.Lock()
+                       delete(peers, addrPrev)
+                       delete(knownPeers, addrPrev)
+                       delete(peersById, *peer.Id)
+                       peers[addr] = ps
+                       knownPeers[addr] = &peer
+                       peersById[*peer.Id] = addr
+                       peersLock.Unlock()
+                       peersByIdLock.Unlock()
+                       kpLock.Unlock()
+                       go peerReady(*ps)
+                       log.Println("Rehandshake finished:", peer.Id.String())
+               } else {
+                       ifaceName, err := callUp(peer.Id)
+                       if err != nil {
+                               break
                        }
-                       bufN = 0
-               }
-       ReadMore:
-               if sizeNeed != bufN {
-                       n, err = conn.Read(buf[bufN:sizeNeed])
+                       tap, err = govpn.TAPListen(ifaceName)
                        if err != nil {
+                               log.Println("Unable to create TAP:", err)
                                break
                        }
-                       bufN += uint16(n)
-                       goto ReadMore
+                       ps = &PeerState{
+                               peer:       peer,
+                               tap:        tap,
+                               terminator: make(chan struct{}, 1),
+                       }
+                       go peerReady(*ps)
+                       peersLock.Lock()
+                       peersByIdLock.Lock()
+                       kpLock.Lock()
+                       peers[addr] = ps
+                       peersById[*peer.Id] = addr
+                       knownPeers[addr] = &peer
+                       peersLock.Unlock()
+                       peersByIdLock.Unlock()
+                       kpLock.Unlock()
+                       log.Println("New peer:", peer.Id.String())
+               }
+               break
+       }
+       if hs != nil {
+               hs.Zero()
+       }
+       if peer == nil {
+               return
+       }
+
+       nonceExpectation := make([]byte, govpn.NonceSize)
+       binary.BigEndian.PutUint64(nonceExpectation, peer.NonceExpect)
+       peer.NonceCipher.Encrypt(nonceExpectation, nonceExpectation)
+       prev = 0
+       var i int
+       for {
+               if prev == govpn.MTU {
+                       break
+               }
+               conn.SetReadDeadline(time.Now().Add(conf.Timeout))
+               n, err = conn.Read(buf[prev:])
+               if err != nil {
+                       // Either EOFed or timeouted
+                       break
+               }
+               prev += n
+       CheckMore:
+               i = bytes.Index(buf[:prev], nonceExpectation)
+               if i == -1 {
+                       continue
                }
-               sizeNbuf = 0
-               sink <- Pkt{
-                       addr,
-                       TCPSender{conn},
-                       buf[:sizeNeed],
-                       ready,
+               if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
+                       break
                }
+               binary.BigEndian.PutUint64(nonceExpectation, peer.NonceExpect)
+               peer.NonceCipher.Encrypt(nonceExpectation, nonceExpectation)
+               copy(buf, buf[i+govpn.NonceSize:prev])
+               prev = prev - i - govpn.NonceSize
+               goto CheckMore
        }
+       peer.Zero()
 }
index e6bbc5edb700882e124e46daf051c570e8448390..4a57d87aa7d2098ba4bb8327f85bb6d9654a5568 100644 (file)
@@ -21,7 +21,6 @@ package main
 import (
        "log"
        "net"
-       "time"
 
        "govpn"
 )
@@ -35,41 +34,163 @@ func (c UDPSender) Write(data []byte) (int, error) {
        return c.conn.WriteToUDP(data, c.addr)
 }
 
-func (c UDPSender) Reorderable() bool {
-       return true
-}
+var (
+       // Buffers for UDP parallel processing
+       udpBufs chan []byte = make(chan []byte, 1<<8)
+)
 
-func startUDP(sink chan Pkt) {
+func startUDP() {
        bind, err := net.ResolveUDPAddr("udp", *bindAddr)
-       ready := make(chan struct{})
        if err != nil {
                log.Fatalln("Can not resolve bind address:", err)
        }
-       lconn, err := net.ListenUDP("udp", bind)
+       conn, err := net.ListenUDP("udp", bind)
        if err != nil {
                log.Fatalln("Can not listen on UDP:", err)
        }
        log.Println("Listening on UDP", *bindAddr)
+       udpBufs <- make([]byte, govpn.MTU)
        go func() {
-               buf := make([]byte, govpn.MTU)
-               var n int
+               var buf []byte
                var raddr *net.UDPAddr
+               var addr string
+               var n int
                var err error
+               var ps *PeerState
+               var hs *govpn.Handshake
+               var addrPrev string
+               var exists bool
+               var peerId *govpn.PeerId
+               var peer *govpn.Peer
+               var conf *govpn.PeerConf
                for {
-                       <-ready
-                       lconn.SetReadDeadline(time.Now().Add(time.Second))
-                       n, raddr, err = lconn.ReadFromUDP(buf)
+                       buf = <-udpBufs
+
+                       n, raddr, err = conn.ReadFromUDP(buf)
                        if err != nil {
-                               sink <- Pkt{ready: ready}
-                               continue
+                               break
+                       }
+                       addr = raddr.String()
+
+                       peersLock.RLock()
+                       ps, exists = peers[addr]
+                       peersLock.RUnlock()
+                       if !exists {
+                               goto CheckHandshake
+                       }
+                       go func(ps *govpn.Peer, tap *govpn.TAP, buf []byte, n int) {
+                               peer.PktProcess(buf[:n], tap, true)
+                               udpBufs <- buf
+                       }(ps.peer, ps.tap, buf, n)
+                       continue
+               CheckHandshake:
+                       hsLock.RLock()
+                       hs, exists = handshakes[addr]
+                       hsLock.RUnlock()
+                       if !exists {
+                               goto CheckID
+                       }
+                       peer = hs.Server(buf[:n])
+                       if peer == nil {
+                               goto Finished
+                       }
+
+                       log.Println("Peer handshake finished:", addr)
+                       hs.Zero()
+                       hsLock.Lock()
+                       delete(handshakes, addr)
+                       hsLock.Unlock()
+
+                       go func() {
+                               udpBufs <- make([]byte, govpn.MTU)
+                               udpBufs <- make([]byte, govpn.MTU)
+                       }()
+                       peersByIdLock.RLock()
+                       addrPrev, exists = peersById[*peer.Id]
+                       peersByIdLock.RUnlock()
+                       if exists {
+                               peersLock.RLock()
+                               ps = &PeerState{
+                                       peer:       peer,
+                                       tap:        peers[addrPrev].tap,
+                                       terminator: peers[addrPrev].terminator,
+                               }
+                               peersLock.RUnlock()
+                               ps.terminator <- struct{}{}
+                               peersLock.Lock()
+                               peersByIdLock.Lock()
+                               kpLock.Lock()
+                               delete(peers, addrPrev)
+                               delete(knownPeers, addrPrev)
+                               delete(peersById, *peer.Id)
+                               peers[addr] = ps
+                               knownPeers[addr] = &peer
+                               peersById[*peer.Id] = addr
+                               peersLock.Unlock()
+                               peersByIdLock.Unlock()
+                               kpLock.Unlock()
+                               go func(ps PeerState) {
+                                       peerReady(ps)
+                                       <-udpBufs
+                                       <-udpBufs
+                               }(*ps)
+                               log.Println("Rehandshake finished:", peer.Id.String())
+                       } else {
+                               go func(addr string, peer *govpn.Peer) {
+                                       ifaceName, err := callUp(peer.Id)
+                                       if err != nil {
+                                               return
+                                       }
+                                       tap, err := govpn.TAPListen(ifaceName)
+                                       if err != nil {
+                                               log.Println("Unable to create TAP:", err)
+                                               return
+                                       }
+                                       ps = &PeerState{
+                                               peer:       peer,
+                                               tap:        tap,
+                                               terminator: make(chan struct{}, 1),
+                                       }
+                                       go func(ps PeerState) {
+                                               peerReady(ps)
+                                               <-udpBufs
+                                               <-udpBufs
+                                       }(*ps)
+                                       peersLock.Lock()
+                                       peersByIdLock.Lock()
+                                       kpLock.Lock()
+                                       peers[addr] = ps
+                                       knownPeers[addr] = &peer
+                                       peersById[*peer.Id] = addr
+                                       peersLock.Unlock()
+                                       peersByIdLock.Unlock()
+                                       kpLock.Unlock()
+                                       log.Println("New peer:", peer.Id.String())
+                               }(addr, peer)
+                       }
+                       goto Finished
+               CheckID:
+                       peerId = govpn.IDsCache.Find(buf[:n])
+                       if peerId == nil {
+                               log.Println("Unknown identity from:", addr)
+                               goto Finished
                        }
-                       sink <- Pkt{
-                               raddr.String(),
-                               UDPSender{lconn, raddr},
-                               buf[:n],
-                               ready,
+                       conf = peerId.Conf()
+                       if conf == nil {
+                               log.Println("Can not get peer configuration:", peerId.String())
+                               goto Finished
                        }
+                       hs = govpn.NewHandshake(
+                               addr,
+                               UDPSender{conn: conn, addr: raddr},
+                               conf,
+                       )
+                       hs.Server(buf[:n])
+                       hsLock.Lock()
+                       handshakes[addr] = hs
+                       hsLock.Unlock()
+               Finished:
+                       udpBufs <- buf
                }
        }()
-       ready <- struct{}{}
 }
index fbafd56ab680de6afc61eb50f41890e1ed1babfc..263ef45941ef94cc2f591b93d24b0dc81e699997 100644 (file)
@@ -23,6 +23,7 @@ import (
        "os"
        "os/exec"
        "runtime"
+       "time"
 )
 
 const (
@@ -62,3 +63,10 @@ func sliceZero(data []byte) {
 func VersionGet() string {
        return "GoVPN version " + Version + " built with " + runtime.Version()
 }
+
+func cprCycleCalculate(rate int) time.Duration {
+       if rate == 0 {
+               return time.Duration(0)
+       }
+       return time.Second / time.Duration(rate*(1<<10)/MTU)
+}
index 9a7b1143c97c1daac5fb9f2ef26ccadba3b344ca..996f31ff903db113da307ccd488ac60461116509 100644 (file)
@@ -16,5 +16,5 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Simple secure free software virtual private network daemon
+// Simple secure, DPI/censorship-resistant free software VPN daemon.
 package govpn
index d99154a9885717ea850db5d28b3f1ecd20250e25..b771d498fafb0c161f3aec4b7db855f1a3065a0f 100644 (file)
@@ -22,6 +22,7 @@ import (
        "crypto/rand"
        "crypto/subtle"
        "encoding/binary"
+       "io"
        "log"
        "time"
 
@@ -40,7 +41,7 @@ const (
 
 type Handshake struct {
        addr     string
-       conn     RemoteConn
+       conn     io.Writer
        LastPing time.Time
        Conf     *PeerConf
        dsaPubH  *[ed25519.PublicKeySize]byte
@@ -133,7 +134,7 @@ func dhKeyGen(priv, pub *[32]byte) *[32]byte {
 }
 
 // Create new handshake state.
-func HandshakeNew(addr string, conn RemoteConn, conf *PeerConf) *Handshake {
+func NewHandshake(addr string, conn io.Writer, conf *PeerConf) *Handshake {
        state := Handshake{
                addr:     addr,
                conn:     conn,
@@ -160,8 +161,8 @@ func idTag(id *PeerId, data []byte) []byte {
 // Start handshake's procedure from the client. It is the entry point
 // for starting the handshake procedure. // First handshake packet
 // will be sent immediately.
-func HandshakeStart(addr string, conn RemoteConn, conf *PeerConf) *Handshake {
-       state := HandshakeNew(addr, conn, conf)
+func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake {
+       state := NewHandshake(addr, conn, conf)
        var dhPubRepr *[32]byte
        state.dhPriv, dhPubRepr = dhKeypairGen()
 
diff --git a/src/govpn/peer.go b/src/govpn/peer.go
new file mode 100644 (file)
index 0000000..5c6e13b
--- /dev/null
@@ -0,0 +1,309 @@
+package govpn
+
+import (
+       "encoding/binary"
+       "io"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "golang.org/x/crypto/poly1305"
+       "golang.org/x/crypto/salsa20"
+       "golang.org/x/crypto/xtea"
+)
+
+const (
+       NonceSize       = 8
+       NonceBucketSize = 128
+       TagSize         = poly1305.TagSize
+       // S20BS is Salsa20's internal blocksize in bytes
+       S20BS = 64
+       // Maximal amount of bytes transfered with single key (4 GiB)
+       MaxBytesPerKey int64 = 1 << 32
+       // Size of packet's size mark in bytes
+       PktSizeSize = 2
+       // Heartbeat rate, relative to Timeout
+       TimeoutHeartbeat = 4
+)
+
+func newNonceCipher(key *[32]byte) *xtea.Cipher {
+       nonceKey := make([]byte, 16)
+       salsa20.XORKeyStream(
+               nonceKey,
+               make([]byte, 32),
+               make([]byte, xtea.BlockSize),
+               key,
+       )
+       ciph, err := xtea.NewCipher(nonceKey)
+       if err != nil {
+               panic(err)
+       }
+       return ciph
+}
+
+type Peer struct {
+       Addr string
+       Id   *PeerId
+       Conn io.Writer
+
+       // Traffic behaviour
+       NoiseEnable bool
+       CPR         int
+       CPRCycle    time.Duration `json:"-"`
+
+       // Cryptography related
+       Key          *[SSize]byte `json:"-"`
+       NonceCipher  *xtea.Cipher `json:"-"`
+       nonceRecv    uint64
+       nonceLatest  uint64
+       nonceOur     uint64
+       NonceExpect  uint64 `json:"-"`
+       nonceBucket0 map[uint64]struct{}
+       nonceBucket1 map[uint64]struct{}
+       nonceFound0  bool
+       nonceFound1  bool
+       nonceBucketN int32
+
+       // Timers
+       Timeout       time.Duration `json:"-"`
+       Established   time.Time
+       LastPing      time.Time
+       LastSent      time.Time
+       willSentCycle time.Time
+
+       // Statistics
+       BytesIn         int64
+       BytesOut        int64
+       BytesPayloadIn  int64
+       BytesPayloadOut int64
+       FramesIn        int
+       FramesOut       int
+       FramesUnauth    int
+       FramesDup       int
+       HeartbeatRecv   int
+       HeartbeatSent   int
+
+       // Receiver
+       BusyR    sync.Mutex
+       bufR     []byte
+       tagR     *[TagSize]byte
+       keyAuthR *[SSize]byte
+       pktSizeR uint16
+
+       // Transmitter
+       BusyT    sync.Mutex
+       bufT     []byte
+       tagT     *[TagSize]byte
+       keyAuthT *[SSize]byte
+       frameT   []byte
+       now      time.Time
+}
+
+func (p *Peer) String() string {
+       return p.Id.String() + ":" + p.Addr
+}
+
+// Zero peer's memory state.
+func (p *Peer) Zero() {
+       p.BusyT.Lock()
+       p.BusyR.Lock()
+       sliceZero(p.Key[:])
+       sliceZero(p.bufR)
+       sliceZero(p.bufT)
+       sliceZero(p.keyAuthR[:])
+       sliceZero(p.keyAuthT[:])
+       p.BusyT.Unlock()
+       p.BusyR.Unlock()
+}
+
+func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
+       now := time.Now()
+       timeout := conf.Timeout
+
+       cprCycle := cprCycleCalculate(conf.CPR)
+       noiseEnable := conf.NoiseEnable
+       if conf.CPR > 0 {
+               noiseEnable = true
+               timeout = cprCycle
+       } else {
+               timeout = timeout / TimeoutHeartbeat
+       }
+
+       peer := Peer{
+               Addr: addr,
+               Id:   conf.Id,
+               Conn: conn,
+
+               NoiseEnable: noiseEnable,
+               CPR:         conf.CPR,
+               CPRCycle:    cprCycle,
+
+               Key:          key,
+               NonceCipher:  newNonceCipher(key),
+               nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
+               nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
+
+               Timeout:     timeout,
+               Established: now,
+               LastPing:    now,
+
+               bufR:     make([]byte, S20BS+MTU+NonceSize),
+               bufT:     make([]byte, S20BS+MTU+NonceSize),
+               tagR:     new([TagSize]byte),
+               tagT:     new([TagSize]byte),
+               keyAuthR: new([SSize]byte),
+               keyAuthT: new([SSize]byte),
+       }
+       if isClient {
+               peer.nonceOur = 1
+               peer.NonceExpect = 0 + 2
+       } else {
+               peer.nonceOur = 0
+               peer.NonceExpect = 1 + 2
+       }
+       return &peer
+
+}
+
+// Process incoming Ethernet packet.
+// ready channel is TAPListen's synchronization channel used to tell him
+// that he is free to receive new packets. Encrypted and authenticated
+// packets will be sent to remote Peer side immediately.
+func (p *Peer) EthProcess(data []byte) {
+       p.now = time.Now()
+       p.BusyT.Lock()
+
+       // Zero size is a heartbeat packet
+       if len(data) == 0 {
+               // If this heartbeat is necessary
+               if !p.LastSent.Add(p.Timeout).Before(p.now) {
+                       p.BusyT.Unlock()
+                       return
+               }
+               p.bufT[S20BS+0] = byte(0)
+               p.bufT[S20BS+1] = byte(0)
+               p.HeartbeatSent++
+       } else {
+               // Copy payload to our internal buffer and we are ready to
+               // accept the next one
+               binary.BigEndian.PutUint16(
+                       p.bufT[S20BS:S20BS+PktSizeSize],
+                       uint16(len(data)),
+               )
+               copy(p.bufT[S20BS+PktSizeSize:], data)
+               p.BytesPayloadOut += int64(len(data))
+       }
+
+       if p.NoiseEnable {
+               p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize]
+       } else {
+               p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize]
+       }
+       p.nonceOur += 2
+       binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
+       p.NonceCipher.Encrypt(
+               p.frameT[len(p.frameT)-NonceSize:],
+               p.frameT[len(p.frameT)-NonceSize:],
+       )
+       for i := 0; i < SSize; i++ {
+               p.bufT[i] = byte(0)
+       }
+       salsa20.XORKeyStream(
+               p.bufT[:S20BS+len(p.frameT)-NonceSize],
+               p.bufT[:S20BS+len(p.frameT)-NonceSize],
+               p.frameT[len(p.frameT)-NonceSize:],
+               p.Key,
+       )
+
+       copy(p.keyAuthT[:], p.bufT[:SSize])
+       poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
+
+       atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
+       p.FramesOut++
+
+       if p.CPRCycle != time.Duration(0) {
+               p.willSentCycle = p.LastSent.Add(p.CPRCycle)
+               if p.willSentCycle.After(p.now) {
+                       time.Sleep(p.willSentCycle.Sub(p.now))
+                       p.now = p.willSentCycle
+               }
+       }
+
+       p.LastSent = p.now
+       p.Conn.Write(append(p.tagT[:], p.frameT...))
+       p.BusyT.Unlock()
+}
+
+func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
+       p.BusyR.Lock()
+       for i := 0; i < SSize; i++ {
+               p.bufR[i] = byte(0)
+       }
+       copy(p.bufR[S20BS:], data[TagSize:])
+       salsa20.XORKeyStream(
+               p.bufR[:S20BS+len(data)-TagSize-NonceSize],
+               p.bufR[:S20BS+len(data)-TagSize-NonceSize],
+               data[len(data)-NonceSize:],
+               p.Key,
+       )
+
+       copy(p.keyAuthR[:], p.bufR[:SSize])
+       copy(p.tagR[:], data[:TagSize])
+       if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
+               p.FramesUnauth++
+               p.BusyR.Unlock()
+               return false
+       }
+
+       // Check if received nonce is known to us in either of two buckets.
+       // If yes, then this is ignored duplicate.
+       // Check from the oldest bucket, as in most cases this will result
+       // in constant time check.
+       // If Bucket0 is filled, then it becomes Bucket1.
+       p.NonceCipher.Decrypt(
+               data[len(data)-NonceSize:],
+               data[len(data)-NonceSize:],
+       )
+       p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
+       if reorderable {
+               _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
+               _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
+               if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
+                       p.FramesDup++
+                       p.BusyR.Unlock()
+                       return false
+               }
+               p.nonceBucket0[p.nonceRecv] = struct{}{}
+               p.nonceBucketN++
+               if p.nonceBucketN == NonceBucketSize {
+                       p.nonceBucket1 = p.nonceBucket0
+                       p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
+                       p.nonceBucketN = 0
+               }
+       } else {
+               if p.nonceRecv != p.NonceExpect {
+                       p.FramesDup++
+                       p.BusyR.Unlock()
+                       return false
+               }
+               p.NonceExpect += 2
+       }
+       if p.nonceRecv > p.nonceLatest {
+               p.nonceLatest = p.nonceRecv
+       }
+
+       p.FramesIn++
+       atomic.AddInt64(&p.BytesIn, int64(len(data)))
+       p.LastPing = time.Now()
+       p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize])
+
+       if p.pktSizeR == 0 {
+               p.HeartbeatRecv++
+               p.BusyR.Unlock()
+               return true
+       }
+       p.BytesPayloadIn += int64(p.pktSizeR)
+       tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR])
+       p.BusyR.Unlock()
+       return true
+}
index c5e3bf835cfee5f993f4eeff946c035bd005617e..9c999b41374b2750fa05490331219b388fb88107 100644 (file)
@@ -29,14 +29,18 @@ const (
 )
 
 type TAP struct {
-       Name   string
-       dev    io.ReadWriter
-       buf    []byte
-       sink   chan []byte
-       ready  chan struct{}
-       synced bool
+       Name string
+       Sink chan []byte
+       dev  io.ReadWriter
+       buf0 []byte
+       buf1 []byte
+       bufZ bool
 }
 
+var (
+       taps = make(map[string]*TAP)
+)
+
 // Return maximal acceptable TAP interface MTU. This is daemon's MTU
 // minus nonce, MAC, packet size mark and Ethernet header sizes.
 func TAPMaxMTU() int {
@@ -50,23 +54,28 @@ func NewTAP(ifaceName string) (*TAP, error) {
                return nil, err
        }
        tap := TAP{
-               Name:   ifaceName,
-               dev:    tapRaw,
-               buf:    make([]byte, maxIfacePktSize),
-               sink:   make(chan []byte),
-               ready:  make(chan struct{}),
-               synced: false,
+               Name: ifaceName,
+               dev:  tapRaw,
+               buf0: make([]byte, maxIfacePktSize),
+               buf1: make([]byte, maxIfacePktSize),
+               Sink: make(chan []byte),
        }
        go func() {
                var n int
                var err error
+               var buf []byte
                for {
-                       <-tap.ready
-                       n, err = tap.dev.Read(tap.buf)
+                       if tap.bufZ {
+                               buf = tap.buf0
+                       } else {
+                               buf = tap.buf1
+                       }
+                       tap.bufZ = !tap.bufZ
+                       n, err = tap.dev.Read(buf)
                        if err != nil {
                                panic("Reading TAP:" + err.Error())
                        }
-                       tap.sink <- tap.buf[:n]
+                       tap.Sink <- buf[:n]
                }
        }()
        return &tap, nil
@@ -75,3 +84,16 @@ func NewTAP(ifaceName string) (*TAP, error) {
 func (t *TAP) Write(data []byte) (n int, err error) {
        return t.dev.Write(data)
 }
+
+func TAPListen(ifaceName string) (*TAP, error) {
+       tap, exists := taps[ifaceName]
+       if exists {
+               return tap, nil
+       }
+       tap, err := NewTAP(ifaceName)
+       if err != nil {
+               return nil, err
+       }
+       taps[ifaceName] = tap
+       return tap, nil
+}
diff --git a/src/govpn/transport.go b/src/govpn/transport.go
deleted file mode 100644 (file)
index 9c7a733..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
-
-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 <http://www.gnu.org/licenses/>.
-*/
-
-package govpn
-
-import (
-       "encoding/binary"
-       "io"
-       "time"
-
-       "golang.org/x/crypto/poly1305"
-       "golang.org/x/crypto/salsa20"
-       "golang.org/x/crypto/xtea"
-)
-
-const (
-       NonceSize       = 8
-       NonceBucketSize = 128
-       // S20BS is Salsa20's internal blocksize in bytes
-       S20BS = 64
-       // Maximal amount of bytes transfered with single key (4 GiB)
-       MaxBytesPerKey int64 = 1 << 32
-       // Size of packet's size mark in bytes
-       PktSizeSize = 2
-       // Heartbeat rate, relative to Timeout
-       TimeoutHeartbeat = 4
-)
-
-type RemoteConn interface {
-       io.Writer
-       // Can incoming packets be reordered
-       Reorderable() bool
-}
-
-type Peer struct {
-       Addr string
-       Id   *PeerId
-       Conn RemoteConn
-
-       // Traffic behaviour
-       NoiseEnable bool
-       CPR         int
-       CPRCycle    time.Duration `json:"-"`
-
-       // Cryptography related
-       Key          *[SSize]byte `json:"-"`
-       NonceOur     uint64       `json:"-"`
-       NonceRecv    uint64       `json:"-"`
-       NonceCipher  *xtea.Cipher `json:"-"`
-       nonceExpect  uint64       `json:"-"`
-       nonceBucket0 map[uint64]struct{}
-       nonceBucket1 map[uint64]struct{}
-       nonceFound   bool
-       nonceBucketN int32
-
-       // Timers
-       Timeout       time.Duration `json:"-"`
-       Established   time.Time
-       LastPing      time.Time
-       LastSent      time.Time
-       willSentCycle time.Time
-
-       // This variables are initialized only once to relief GC
-       buf       []byte
-       tag       *[poly1305.TagSize]byte
-       keyAuth   *[32]byte
-       nonceRecv uint64
-       frame     []byte
-       nonce     []byte
-       pktSize   uint16
-       size      int
-       now       time.Time
-
-       // Statistics
-       BytesIn         int64
-       BytesOut        int64
-       BytesPayloadIn  int64
-       BytesPayloadOut int64
-       FramesIn        int
-       FramesOut       int
-       FramesUnauth    int
-       FramesDup       int
-       HeartbeatRecv   int
-       HeartbeatSent   int
-}
-
-func (p *Peer) String() string {
-       return p.Id.String() + ":" + p.Addr
-}
-
-// Zero peer's memory state.
-func (p *Peer) Zero() {
-       sliceZero(p.Key[:])
-       sliceZero(p.tag[:])
-       sliceZero(p.keyAuth[:])
-       sliceZero(p.buf)
-       sliceZero(p.frame)
-       sliceZero(p.nonce)
-}
-
-var (
-       taps = make(map[string]*TAP)
-)
-
-// Create TAP listening goroutine.
-// This function takes required TAP interface name, opens it and allocates
-// a buffer where all frame data will be written, channel where information
-// about number of read bytes is sent to, synchronization channel (external
-// processes tell that read buffer can be used again) and possible channel
-// opening error.
-func TAPListen(ifaceName string, timeout time.Duration, cpr int) (*TAP, chan []byte, chan struct{}, chan struct{}, error) {
-       var tap *TAP
-       var err error
-       tap, exists := taps[ifaceName]
-       if !exists {
-               tap, err = NewTAP(ifaceName)
-               if err != nil {
-                       return nil, nil, nil, nil, err
-               }
-               taps[ifaceName] = tap
-       }
-       sink := make(chan []byte)
-       sinkReady := make(chan struct{})
-       sinkTerminate := make(chan struct{})
-       sinkSkip := make(chan struct{})
-
-       go func() {
-               cprCycle := cprCycleCalculate(cpr)
-               if cprCycle != time.Duration(0) {
-                       timeout = cprCycle
-               } else {
-                       timeout = timeout / TimeoutHeartbeat
-               }
-               heartbeat := time.Tick(timeout)
-               var pkt []byte
-       ListenCycle:
-               for {
-                       select {
-                       case <-sinkTerminate:
-                               break ListenCycle
-                       case <-heartbeat:
-                               go func() { sink <- make([]byte, 0) }()
-                               continue
-                       case <-sinkSkip:
-                       case <-sinkReady:
-                               tap.ready <- struct{}{}
-                               tap.synced = true
-                       }
-               HeartbeatCatched:
-                       select {
-                       case <-heartbeat:
-                               go func() { sink <- make([]byte, 0) }()
-                               goto HeartbeatCatched
-                       case <-sinkTerminate:
-                               break ListenCycle
-                       case pkt = <-tap.sink:
-                               tap.synced = false
-                               sink <- pkt
-                       }
-               }
-               close(sink)
-               close(sinkReady)
-               close(sinkTerminate)
-       }()
-       if exists && tap.synced {
-               sinkSkip <- struct{}{}
-       } else {
-               sinkReady <- struct{}{}
-       }
-       return tap, sink, sinkReady, sinkTerminate, nil
-}
-
-func newNonceCipher(key *[32]byte) *xtea.Cipher {
-       nonceKey := make([]byte, 16)
-       salsa20.XORKeyStream(
-               nonceKey,
-               make([]byte, 32),
-               make([]byte, xtea.BlockSize),
-               key,
-       )
-       ciph, err := xtea.NewCipher(nonceKey)
-       if err != nil {
-               panic(err)
-       }
-       return ciph
-}
-
-func cprCycleCalculate(rate int) time.Duration {
-       if rate == 0 {
-               return time.Duration(0)
-       }
-       return time.Second / time.Duration(rate*(1<<10)/MTU)
-}
-
-func newPeer(isClient bool, addr string, conn RemoteConn, conf *PeerConf, key *[SSize]byte) *Peer {
-       now := time.Now()
-       timeout := conf.Timeout
-       cprCycle := cprCycleCalculate(conf.CPR)
-       noiseEnable := conf.NoiseEnable
-       if conf.CPR > 0 {
-               noiseEnable = true
-               timeout = cprCycle
-       } else {
-               timeout = timeout / TimeoutHeartbeat
-       }
-       peer := Peer{
-               Addr:        addr,
-               Conn:        conn,
-               Timeout:     timeout,
-               Established: now,
-               LastPing:    now,
-               Id:          conf.Id,
-               NoiseEnable: noiseEnable,
-               CPR:         conf.CPR,
-               CPRCycle:    cprCycle,
-               NonceRecv:   0,
-               Key:         key,
-               NonceCipher: newNonceCipher(key),
-               buf:         make([]byte, MTU+S20BS+NonceSize+poly1305.TagSize),
-               tag:         new([poly1305.TagSize]byte),
-               keyAuth:     new([SSize]byte),
-               nonce:       make([]byte, NonceSize),
-       }
-       if isClient {
-               peer.NonceOur = 1
-               peer.nonceExpect = 0 + 2
-       } else {
-               peer.NonceOur = 0
-               peer.nonceExpect = 1 + 2
-       }
-       if conn.Reorderable() {
-               peer.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
-               peer.nonceBucket1 = make(map[uint64]struct{}, NonceBucketSize)
-       }
-       return &peer
-}
-
-// Process incoming UDP packet.
-// ConnListen'es synchronization channel used to tell him that he is
-// free to receive new packets. Authenticated and decrypted packets
-// will be written to the interface immediately (except heartbeat ones).
-func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool {
-       p.size = len(data)
-       p.frame = make([]byte, p.size)
-       copy(p.frame, data)
-       ready <- struct{}{}
-
-       copy(p.buf[S20BS:], p.frame[:p.size-NonceSize-poly1305.TagSize])
-       for i := 0; i < S20BS; i++ {
-               p.buf[i] = byte(0)
-       }
-       salsa20.XORKeyStream(
-               p.buf[:S20BS+p.size-NonceSize-poly1305.TagSize],
-               p.buf[:S20BS+p.size-NonceSize-poly1305.TagSize],
-               p.frame[p.size-NonceSize-poly1305.TagSize:p.size-poly1305.TagSize],
-               p.Key,
-       )
-       copy(p.tag[:], p.frame[p.size-poly1305.TagSize:])
-       copy(p.keyAuth[:], p.buf[:SSize])
-       if !poly1305.Verify(p.tag, p.frame[:p.size-poly1305.TagSize], p.keyAuth) {
-               p.FramesUnauth++
-               return false
-       }
-
-       // Check if received nonce is known to us in either of two buckets.
-       // If yes, then this is ignored duplicate.
-       // Check from the oldest bucket, as in most cases this will result
-       // in constant time check.
-       // If Bucket0 is filled, then it becomes Bucket1.
-       p.NonceCipher.Decrypt(
-               p.nonce,
-               p.frame[p.size-NonceSize-poly1305.TagSize:p.size-poly1305.TagSize],
-       )
-
-       p.nonceRecv = binary.BigEndian.Uint64(p.nonce)
-       if p.Conn.Reorderable() {
-               if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound {
-                       p.FramesDup++
-                       return false
-               }
-               if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound {
-                       p.FramesDup++
-                       return false
-               }
-               p.nonceBucket0[p.NonceRecv] = struct{}{}
-               p.nonceBucketN++
-               if p.nonceBucketN == NonceBucketSize {
-                       p.nonceBucket1 = p.nonceBucket0
-                       p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
-                       p.nonceBucketN = 0
-               }
-       } else {
-               if p.nonceRecv != p.nonceExpect {
-                       p.FramesDup++
-                       return false
-               }
-               p.nonceExpect += 2
-       }
-
-       p.FramesIn++
-       p.BytesIn += int64(p.size)
-       p.LastPing = time.Now()
-       p.NonceRecv = p.nonceRecv
-       p.pktSize = binary.BigEndian.Uint16(p.buf[S20BS : S20BS+PktSizeSize])
-       if p.pktSize == 0 {
-               p.HeartbeatRecv++
-               return true
-       }
-       p.BytesPayloadIn += int64(p.pktSize)
-       tap.Write(p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize])
-       return true
-}
-
-// Process incoming Ethernet packet.
-// ready channel is TAPListen's synchronization channel used to tell him
-// that he is free to receive new packets. Encrypted and authenticated
-// packets will be sent to remote Peer side immediately.
-func (p *Peer) EthProcess(data []byte, ready chan struct{}) {
-       p.now = time.Now()
-       p.size = len(data)
-       // If this heartbeat is necessary
-       if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) {
-               return
-       }
-       if p.size > 0 {
-               copy(p.buf[S20BS+PktSizeSize:], data)
-               ready <- struct{}{}
-               binary.BigEndian.PutUint16(p.buf[S20BS:S20BS+PktSizeSize], uint16(p.size))
-               p.BytesPayloadOut += int64(p.size)
-       } else {
-               p.buf[S20BS+0] = byte(0)
-               p.buf[S20BS+1] = byte(0)
-               p.HeartbeatSent++
-       }
-
-       p.NonceOur += 2
-       binary.BigEndian.PutUint64(p.nonce, p.NonceOur)
-       p.NonceCipher.Encrypt(p.nonce, p.nonce)
-
-       for i := 0; i < S20BS; i++ {
-               p.buf[i] = byte(0)
-       }
-       salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key)
-       if p.NoiseEnable {
-               p.frame = append(p.buf[S20BS:S20BS+MTU-NonceSize-poly1305.TagSize], p.nonce...)
-       } else {
-               p.frame = append(p.buf[S20BS:S20BS+PktSizeSize+p.size], p.nonce...)
-       }
-       copy(p.keyAuth[:], p.buf[:SSize])
-       poly1305.Sum(p.tag, p.frame, p.keyAuth)
-
-       p.BytesOut += int64(len(p.frame) + poly1305.TagSize)
-       p.FramesOut++
-
-       if p.CPRCycle != time.Duration(0) {
-               p.willSentCycle = p.LastSent.Add(p.CPRCycle)
-               if p.willSentCycle.After(p.now) {
-                       time.Sleep(p.willSentCycle.Sub(p.now))
-                       p.now = p.willSentCycle
-               }
-       }
-       p.LastSent = p.now
-       p.Conn.Write(append(p.frame, p.tag[:]...))
-}