]> Cypherpunks.ru repositories - govpn.git/blobdiff - src/govpn/transport.go
[DOC] Move 'in the media' section lower
[govpn.git] / src / govpn / transport.go
index 7eca502ff41d69c20683d1646a6757f2a5876a10..9c7a73328edb2fef7d8e81ec155e58a22bdf51d8 100644 (file)
@@ -21,7 +21,6 @@ package govpn
 import (
        "encoding/binary"
        "io"
-       "net"
        "time"
 
        "golang.org/x/crypto/poly1305"
@@ -42,9 +41,16 @@ const (
        TimeoutHeartbeat = 4
 )
 
+type RemoteConn interface {
+       io.Writer
+       // Can incoming packets be reordered
+       Reorderable() bool
+}
+
 type Peer struct {
-       Addr *net.UDPAddr
+       Addr string
        Id   *PeerId
+       Conn RemoteConn
 
        // Traffic behaviour
        NoiseEnable bool
@@ -56,6 +62,7 @@ type Peer struct {
        NonceOur     uint64       `json:"-"`
        NonceRecv    uint64       `json:"-"`
        NonceCipher  *xtea.Cipher `json:"-"`
+       nonceExpect  uint64       `json:"-"`
        nonceBucket0 map[uint64]struct{}
        nonceBucket1 map[uint64]struct{}
        nonceFound   bool
@@ -75,7 +82,7 @@ type Peer struct {
        nonceRecv uint64
        frame     []byte
        nonce     []byte
-       pktSize   uint64
+       pktSize   uint16
        size      int
        now       time.Time
 
@@ -93,7 +100,7 @@ type Peer struct {
 }
 
 func (p *Peer) String() string {
-       return p.Id.String() + ":" + p.Addr.String()
+       return p.Id.String() + ":" + p.Addr
 }
 
 // Zero peer's memory state.
@@ -107,8 +114,7 @@ func (p *Peer) Zero() {
 }
 
 var (
-       Emptiness = make([]byte, 1<<14)
-       taps      = make(map[string]*TAP)
+       taps = make(map[string]*TAP)
 )
 
 // Create TAP listening goroutine.
@@ -201,7 +207,7 @@ func cprCycleCalculate(rate int) time.Duration {
        return time.Second / time.Duration(rate*(1<<10)/MTU)
 }
 
-func newPeer(addr *net.UDPAddr, 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)
@@ -213,47 +219,60 @@ func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, key *[SSize]byte) *Pe
                timeout = timeout / TimeoutHeartbeat
        }
        peer := Peer{
-               Addr:         addr,
-               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+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.
-// udpPkt is received data, related to the peer tap interface and
 // 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) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool {
-       p.size = len(udpPkt)
-       copy(p.buf, Emptiness)
-       copy(p.tag[:], udpPkt[p.size-poly1305.TagSize:])
-       copy(p.buf[S20BS:], udpPkt[NonceSize:p.size-poly1305.TagSize])
+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-poly1305.TagSize],
-               p.buf[:S20BS+p.size-poly1305.TagSize],
-               udpPkt[:NonceSize],
+               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, udpPkt[:p.size-poly1305.TagSize], p.keyAuth) {
-               ready <- struct{}{}
+       if !poly1305.Verify(p.tag, p.frame[:p.size-poly1305.TagSize], p.keyAuth) {
                p.FramesUnauth++
                return false
        }
@@ -263,79 +282,86 @@ func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) boo
        // 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.buf, udpPkt[: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
+       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.Uvarint(p.buf[S20BS : S20BS+PktSizeSize])
+       p.pktSize = binary.BigEndian.Uint16(p.buf[S20BS : S20BS+PktSizeSize])
        if p.pktSize == 0 {
                p.HeartbeatRecv++
                return true
        }
-       p.frame = p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize]
        p.BytesPayloadIn += int64(p.pktSize)
-       tap.Write(p.frame)
+       tap.Write(p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize])
        return true
 }
 
-type WriteToUDPer interface {
-       WriteToUDP([]byte, *net.UDPAddr) (int, error)
-}
-
 // Process incoming Ethernet packet.
-// ethPkt is received data, conn is our outgoing connection.
 // 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(ethPkt []byte, conn WriteToUDPer, ready chan struct{}) {
+func (p *Peer) EthProcess(data []byte, ready chan struct{}) {
        p.now = time.Now()
-       p.size = len(ethPkt)
+       p.size = len(data)
        // If this heartbeat is necessary
        if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) {
                return
        }
-       copy(p.buf, Emptiness)
        if p.size > 0 {
-               copy(p.buf[S20BS+PktSizeSize:], ethPkt)
+               copy(p.buf[S20BS+PktSizeSize:], data)
                ready <- struct{}{}
-               binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(p.size))
+               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
-       copy(p.nonce, Emptiness)
-       binary.PutUvarint(p.nonce, p.NonceOur)
+       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)
-       copy(p.buf[S20BS-NonceSize:S20BS], p.nonce)
-       copy(p.keyAuth[:], p.buf[:SSize])
        if p.NoiseEnable {
-               p.frame = p.buf[S20BS-NonceSize : S20BS+MTU-NonceSize-poly1305.TagSize]
+               p.frame = append(p.buf[S20BS:S20BS+MTU-NonceSize-poly1305.TagSize], p.nonce...)
        } else {
-               p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+p.size]
+               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)
@@ -349,5 +375,5 @@ func (p *Peer) EthProcess(ethPkt []byte, conn WriteToUDPer, ready chan struct{})
                }
        }
        p.LastSent = p.now
-       conn.WriteToUDP(append(p.frame, p.tag[:]...), p.Addr)
+       p.Conn.Write(append(p.frame, p.tag[:]...))
 }