const (
NonceSize = 8
- KeySize = 32
// S20BS is Salsa20's internal blocksize in bytes
S20BS = 64
// Maximal amount of bytes transfered with single key (4 GiB)
}
type Peer struct {
- 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
- buf []byte
- tag *[poly1305.TagSize]byte
- keyAuth *[KeySize]byte
- nonceRecv uint64
- frame []byte
- nonce []byte
- pktSize uint64
+ Addr *net.UDPAddr
+ Id *PeerId
+
+ // Traffic behaviour
+ NoiseEnable bool
+ CPR int
+ CPRCycle time.Duration `json:"-"`
+
+ // Cryptography related
+ Key *[SSize]byte `json:"-"`
+ Noncediff int
+ NonceOur uint64 `json:"-"`
+ NonceRecv uint64 `json:"-"`
+ NonceCipher *xtea.Cipher `json:"-"`
+
+ // 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 uint64
+
+ // Statistics
BytesIn int64
BytesOut int64
BytesPayloadIn int64
return p.Id.String() + ":" + p.Addr.String()
}
-// Zero peer's memory state
+// Zero peer's memory state.
func (p *Peer) Zero() {
sliceZero(p.Key[:])
sliceZero(p.tag[:])
}
var (
- Emptiness = make([]byte, 1<<14)
- taps = make(map[string]*TAP)
- heartbeatPeriod time.Duration
+ Emptiness = make([]byte, 1<<14)
+ taps = make(map[string]*TAP)
)
-func heartbeatPeriodGet() time.Duration {
- if heartbeatPeriod == time.Duration(0) {
- heartbeatPeriod = Timeout / TimeoutHeartbeat
- }
- return heartbeatPeriod
-}
-
// 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) (*TAP, chan []byte, chan struct{}, chan struct{}, 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]
sinkSkip := make(chan struct{})
go func() {
- heartbeat := time.Tick(heartbeatPeriodGet())
+ cprCycle := cprCycleCalculate(cpr)
+ if cprCycle != time.Duration(0) {
+ timeout = cprCycle
+ } else {
+ timeout = timeout / TimeoutHeartbeat
+ }
+ heartbeat := time.Tick(timeout)
var pkt []byte
ListenCycle:
for {
return sink, buf, sinkReady
}
-func newNonceCipher(key *[KeySize]byte) *xtea.Cipher {
+func newNonceCipher(key *[32]byte) *xtea.Cipher {
nonceKey := make([]byte, 16)
salsa20.XORKeyStream(
nonceKey,
- make([]byte, KeySize),
+ make([]byte, 32),
make([]byte, xtea.BlockSize),
key,
)
return ciph
}
-func newPeer(addr *net.UDPAddr, id PeerId, nonce int, key *[KeySize]byte) *Peer {
+func cprCycleCalculate(rate int) time.Duration {
+ if rate == 0 {
+ return time.Duration(0)
+ }
+ return time.Second / time.Duration(rate*(1<<10)/MTU)
+}
+
+func newPeer(addr *net.UDPAddr, conf *PeerConf, nonce int, 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,
- LastPing: time.Now(),
- Id: id,
- NonceOur: uint64(Noncediff + nonce),
- NonceRecv: uint64(Noncediff + 0),
+ Timeout: timeout,
+ Established: now,
+ LastPing: now,
+ Id: conf.Id,
+ NoiseEnable: noiseEnable,
+ CPR: conf.CPR,
+ CPRCycle: cprCycle,
+ Noncediff: conf.Noncediff,
+ NonceOur: uint64(conf.Noncediff + nonce),
+ NonceRecv: uint64(conf.Noncediff + 0),
Key: key,
NonceCipher: newNonceCipher(key),
buf: make([]byte, MTU+S20BS),
tag: new([poly1305.TagSize]byte),
- keyAuth: new([KeySize]byte),
+ keyAuth: new([SSize]byte),
nonce: make([]byte, NonceSize),
}
return &peer
udpPkt[:NonceSize],
p.Key,
)
- copy(p.keyAuth[:], p.buf[:KeySize])
+ copy(p.keyAuth[:], p.buf[:SSize])
if !poly1305.Verify(p.tag, udpPkt[:size-poly1305.TagSize], p.keyAuth) {
ready <- struct{}{}
p.FramesUnauth++
}
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 {
+ if int(p.NonceRecv)-p.Noncediff >= 0 && int(p.nonceRecv) < int(p.NonceRecv)-p.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+PktSizeSize : S20BS+PktSizeSize+p.pktSize]
p.BytesPayloadIn += int64(p.pktSize)
- p.FramesIn++
tap.Write(p.frame)
return true
}
now := time.Now()
size := len(ethPkt)
// If this heartbeat is necessary
- if size == 0 && !p.LastSent.Add(heartbeatPeriodGet()).Before(now) {
+ if size == 0 && !p.LastSent.Add(p.Timeout).Before(now) {
return
}
copy(p.buf, Emptiness)
salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key)
copy(p.buf[S20BS-NonceSize:S20BS], p.nonce)
- copy(p.keyAuth[:], p.buf[:KeySize])
- if NoiseEnable {
+ copy(p.keyAuth[:], p.buf[:SSize])
+ if p.NoiseEnable {
p.frame = p.buf[S20BS-NonceSize : S20BS+MTU-NonceSize-poly1305.TagSize]
} else {
p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+size]
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(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)