package govpn
import (
- "crypto/subtle"
"encoding/binary"
+ "io"
"log"
"net"
"time"
NonceSize = 8
KeySize = 32
// S20BS is Salsa20's internal blocksize in bytes
- S20BS = 64
- HeartbeatSize = 12
+ S20BS = 64
// Maximal amount of bytes transfered with single key (4 GiB)
- MaxBytesPerKey = 4294967296
+ MaxBytesPerKey int64 = 1 << 32
+ // Size of packet's size mark in bytes
+ PktSizeSize = 2
+ // Heartbeat rate, relative to Timeout
+ TimeoutHeartbeat = 4
)
type UDPPkt struct {
}
type Peer struct {
- Addr *net.UDPAddr
- Id PeerId
- Key *[KeySize]byte // encryption key
- NonceOur uint64 // nonce for our messages
- NonceRecv uint64 // latest received nonce from remote peer
- NonceCipher *xtea.Cipher // nonce cipher
- LastPing time.Time
- LastSent time.Time
- buf []byte
- tag *[poly1305.TagSize]byte
- keyAuth *[KeySize]byte
- nonceRecv uint64
- Bytes int
- frame []byte
- nonce []byte
+ Addr *net.UDPAddr
+ Id PeerId
+ Key *[KeySize]byte `json:"-"`
+ NonceOur uint64 `json:"-"`
+ NonceRecv uint64 `json:"-"`
+ NonceCipher *xtea.Cipher `json:"-"`
+ LastPing time.Time
+ LastSent time.Time
+ willSentCycle time.Time
+ buf []byte
+ tag *[poly1305.TagSize]byte
+ keyAuth *[KeySize]byte
+ nonceRecv uint64
+ frame []byte
+ nonce []byte
+ pktSize uint64
+ 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.String()
}
+// 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 (
- HeartbeatMark = []byte("\x00\x00\x00HEARTBEAT")
- Emptiness = make([]byte, KeySize)
+ Emptiness = make([]byte, 1<<14)
taps = make(map[string]*TAP)
- heartbeatPeriod *time.Duration
+ heartbeatPeriod time.Duration
)
func heartbeatPeriodGet() time.Duration {
- if heartbeatPeriod == nil {
- period := time.Second * time.Duration(Timeout/4)
- heartbeatPeriod = &period
+ if heartbeatPeriod == time.Duration(0) {
+ heartbeatPeriod = Timeout / TimeoutHeartbeat
}
- return *heartbeatPeriod
+ return heartbeatPeriod
}
// Create TAP listening goroutine.
// all UDP packet data will be saved, channel where information about
// remote address and number of written bytes are stored, and a channel
// used to tell that buffer is ready to be overwritten.
-func ConnListen(conn *net.UDPConn) (chan *UDPPkt, []byte, chan struct{}) {
+func ConnListen(conn *net.UDPConn) (chan UDPPkt, []byte, chan struct{}) {
buf := make([]byte, MTU)
- sink := make(chan *UDPPkt)
+ sink := make(chan UDPPkt)
sinkReady := make(chan struct{})
go func(conn *net.UDPConn) {
var n int
n, addr, err = conn.ReadFromUDP(buf)
if err != nil {
// This is needed for ticking the timeouts counter outside
- sink <- nil
+ sink <- UDPPkt{nil, 0}
continue
}
- sink <- &UDPPkt{addr, n}
+ sink <- UDPPkt{addr, n}
}
}(conn)
sinkReady <- struct{}{}
func newNonceCipher(key *[KeySize]byte) *xtea.Cipher {
nonceKey := make([]byte, 16)
- salsa20.XORKeyStream(nonceKey, make([]byte, KeySize), make([]byte, xtea.BlockSize), key)
+ salsa20.XORKeyStream(
+ nonceKey,
+ make([]byte, KeySize),
+ make([]byte, xtea.BlockSize),
+ key,
+ )
ciph, err := xtea.NewCipher(nonceKey)
if err != nil {
panic(err)
NonceRecv: uint64(Noncediff + 0),
Key: key,
NonceCipher: newNonceCipher(key),
- Bytes: 0,
buf: make([]byte, MTU+S20BS),
tag: new([poly1305.TagSize]byte),
keyAuth: new([KeySize]byte),
// 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 *TAP, ready chan struct{}) bool {
+func (p *Peer) UDPProcess(udpPkt []byte, tap io.Writer, ready chan struct{}) bool {
size := len(udpPkt)
- copy(p.buf[:KeySize], Emptiness)
+ copy(p.buf, Emptiness)
copy(p.tag[:], udpPkt[size-poly1305.TagSize:])
copy(p.buf[S20BS:], udpPkt[NonceSize:size-poly1305.TagSize])
salsa20.XORKeyStream(
copy(p.keyAuth[:], p.buf[:KeySize])
if !poly1305.Verify(p.tag, udpPkt[:size-poly1305.TagSize], p.keyAuth) {
ready <- struct{}{}
+ p.FramesUnauth++
return false
}
p.NonceCipher.Decrypt(p.buf, udpPkt[:NonceSize])
p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize])
if int(p.NonceRecv)-Noncediff >= 0 && int(p.nonceRecv) < int(p.NonceRecv)-Noncediff {
ready <- struct{}{}
+ p.FramesDup++
return false
}
ready <- struct{}{}
+ p.FramesIn++
+ p.BytesIn += int64(size)
p.LastPing = time.Now()
p.NonceRecv = p.nonceRecv
- p.frame = p.buf[S20BS : S20BS+size-NonceSize-poly1305.TagSize]
- p.Bytes += len(p.frame)
- if subtle.ConstantTimeCompare(p.frame[:HeartbeatSize], HeartbeatMark) == 1 {
+ p.pktSize, _ = binary.Uvarint(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)
return true
}
+type WriteToer interface {
+ WriteTo([]byte, net.Addr) (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 *net.UDPConn, ready chan struct{}) {
+func (p *Peer) EthProcess(ethPkt []byte, conn WriteToer, ready chan struct{}) {
now := time.Now()
size := len(ethPkt)
// If this heartbeat is necessary
if size == 0 && !p.LastSent.Add(heartbeatPeriodGet()).Before(now) {
return
}
- copy(p.buf[:KeySize], Emptiness)
+ copy(p.buf, Emptiness)
if size > 0 {
- copy(p.buf[S20BS:], ethPkt)
+ copy(p.buf[S20BS+PktSizeSize:], ethPkt)
ready <- struct{}{}
+ binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(size))
+ p.BytesPayloadOut += int64(size)
} else {
- copy(p.buf[S20BS:], HeartbeatMark)
- size = HeartbeatSize
+ p.HeartbeatSent++
}
- p.NonceOur = p.NonceOur + 2
+ p.NonceOur += 2
copy(p.nonce, Emptiness)
binary.PutUvarint(p.nonce, p.NonceOur)
p.NonceCipher.Encrypt(p.nonce, p.nonce)
salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key)
copy(p.buf[S20BS-NonceSize:S20BS], p.nonce)
copy(p.keyAuth[:], p.buf[:KeySize])
- p.frame = p.buf[S20BS-NonceSize : S20BS+size]
+ if NoiseEnable {
+ p.frame = p.buf[S20BS-NonceSize : S20BS+MTU-NonceSize-poly1305.TagSize]
+ } else {
+ p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+size]
+ }
poly1305.Sum(p.tag, p.frame, p.keyAuth)
- p.Bytes += len(p.frame)
+ p.BytesOut += int64(len(p.frame) + poly1305.TagSize)
+ p.FramesOut++
+
+ if cprEnable {
+ p.willSentCycle = p.LastSent.Add(cprCycle)
+ if p.willSentCycle.After(now) {
+ time.Sleep(p.willSentCycle.Sub(now))
+ now = p.willSentCycle
+ }
+ }
p.LastSent = now
if _, err := conn.WriteTo(append(p.frame, p.tag[:]...), p.Addr); err != nil {
log.Println("Error sending UDP", err)