import (
"flag"
- "io"
"log"
"net"
"os"
}
govpn.PeersInitDummy(id, conf)
- var conn io.Writer
+ var conn govpn.RemoteConn
var sink chan []byte
var ready chan struct{}
switch *proto {
import (
"bufio"
"encoding/base64"
- "io"
"log"
"net"
"net/http"
+
+ "govpn"
)
-func proxyTCP() (io.Writer, chan []byte, chan struct{}) {
+func proxyTCP() (govpn.RemoteConn, chan []byte, chan struct{}) {
proxyAddr, err := net.ResolveTCPAddr("tcp", *proxyAddr)
if err != nil {
log.Fatalln("Can not resolve proxy address:", err)
import (
"encoding/binary"
- "io"
"log"
"net"
return c.conn.Write(append(size, data...))
}
-func startTCP() (io.Writer, chan []byte, chan struct{}) {
+func (c TCPSender) Reorderable() bool {
+ return false
+}
+
+func startTCP() (govpn.RemoteConn, chan []byte, chan struct{}) {
remote, err := net.ResolveTCPAddr("tcp", *remoteAddr)
if err != nil {
log.Fatalln("Can not resolve remote address:", err)
package main
import (
- "io"
"log"
"net"
"time"
"govpn"
)
-func startUDP() (io.Writer, chan []byte, chan struct{}) {
+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{}) {
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 := io.Writer(c)
if err != nil {
log.Fatalln("Can not listen on UDP:", err)
}
}
}()
ready <- struct{}{}
- return conn, sink, ready
+ return UDPSender{c}, sink, ready
}
import (
"bytes"
"flag"
- "io"
"log"
"net"
"os"
type Pkt struct {
addr string
- conn io.Writer
+ conn govpn.RemoteConn
data []byte
ready chan struct{}
}
return c.conn.Write(append(size, data...))
}
+func (c TCPSender) Reorderable() bool {
+ return false
+}
+
func startTCP(sink chan Pkt) {
bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
if err != nil {
return c.conn.WriteToUDP(data, c.addr)
}
+func (c UDPSender) Reorderable() bool {
+ return true
+}
+
func startUDP(sink chan Pkt) {
bind, err := net.ResolveUDPAddr("udp", *bindAddr)
ready := make(chan struct{})
"crypto/rand"
"crypto/subtle"
"encoding/binary"
- "io"
"log"
"time"
type Handshake struct {
addr string
- conn io.Writer
+ conn RemoteConn
LastPing time.Time
Conf *PeerConf
dsaPubH *[ed25519.PublicKeySize]byte
}
// Create new handshake state.
-func HandshakeNew(addr string, conn io.Writer, conf *PeerConf) *Handshake {
+func HandshakeNew(addr string, conn RemoteConn, conf *PeerConf) *Handshake {
state := Handshake{
addr: addr,
conn: conn,
// 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 io.Writer, conf *PeerConf) *Handshake {
+func HandshakeStart(addr string, conn RemoteConn, conf *PeerConf) *Handshake {
state := HandshakeNew(addr, conn, conf)
-
var dhPubRepr *[32]byte
state.dhPriv, dhPubRepr = dhKeypairGen()
// Switch peer
peer := newPeer(
+ false,
h.addr,
h.conn,
h.Conf,
- 0,
keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize]))
h.LastPing = time.Now()
return peer
}
// Switch peer
- peer := newPeer(h.addr, h.conn, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:]))
+ peer := newPeer(
+ true,
+ h.addr,
+ h.conn,
+ h.Conf,
+ keyFromSecrets(h.sServer[:], h.sClient[:]),
+ )
h.LastPing = time.Now()
return peer
default:
TimeoutHeartbeat = 4
)
+type RemoteConn interface {
+ io.Writer
+ // Can incoming packets be reordered
+ Reorderable() bool
+}
+
type Peer struct {
Addr string
Id *PeerId
- Conn io.Writer
+ Conn RemoteConn
// Traffic behaviour
NoiseEnable bool
NonceOur uint64 `json:"-"`
NonceRecv uint64 `json:"-"`
NonceCipher *xtea.Cipher `json:"-"`
+ nonceExpect uint64 `json:"-"`
nonceBucket0 map[uint64]struct{}
nonceBucket1 map[uint64]struct{}
nonceFound bool
return time.Second / time.Duration(rate*(1<<10)/MTU)
}
-func newPeer(addr string, conn io.Writer, conf *PeerConf, nonce int, key *[SSize]byte) *Peer {
+func newPeer(isClient bool, addr string, conn RemoteConn, conf *PeerConf, key *[SSize]byte) *Peer {
now := time.Now()
timeout := conf.Timeout
cprCycle := cprCycleCalculate(conf.CPR)
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,
- NonceOur: uint64(nonce),
- NonceRecv: uint64(0),
- nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
- nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
- Key: key,
- NonceCipher: newNonceCipher(key),
- buf: make([]byte, MTU+S20BS),
- tag: new([poly1305.TagSize]byte),
- keyAuth: new([SSize]byte),
- nonce: make([]byte, NonceSize),
+ 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),
+ 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
}
// If Bucket0 is filled, then it becomes Bucket1.
p.NonceCipher.Decrypt(p.buf, data[:NonceSize])
ready <- struct{}{}
+
p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize])
- 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
+ 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++
conf *PeerConf
)
-type Dummy struct{
+type Dummy struct {
dst *[]byte
}
return len(b), nil
}
+func (d Dummy) Reorderable() bool {
+ return true
+}
+
func init() {
MTU = 1500
peerId, _ = IDDecode("ffffffffffffffffffffffffffffffff")
NoiseEnable: false,
CPR: 0,
}
- peer = newPeer("foo", Dummy{&ciphertext}, conf, 128, new([SSize]byte))
+ peer = newPeer(true, "foo", Dummy{&ciphertext}, conf, new([SSize]byte))
plaintext = make([]byte, 789)
ready = make(chan struct{})
go func() {
func BenchmarkDec(b *testing.B) {
peer.EthProcess(plaintext, ready)
- peer = newPeer("foo", Dummy{nil}, conf, 128, new([SSize]byte))
+ peer = newPeer(true, "foo", Dummy{nil}, conf, new([SSize]byte))
b.ResetTimer()
for i := 0; i < b.N; i++ {
peer.nonceBucket0 = make(map[uint64]struct{}, 1)