]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/peer.go
Refactor and simplify CPR work code
[govpn.git] / src / cypherpunks.ru / govpn / peer.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package govpn
20
21 import (
22         "bytes"
23         "encoding/binary"
24         "io"
25         "log"
26         "sync"
27         "sync/atomic"
28         "time"
29
30         "golang.org/x/crypto/poly1305"
31         "golang.org/x/crypto/salsa20"
32         "golang.org/x/crypto/xtea"
33 )
34
35 const (
36         NonceSize       = 8
37         NonceBucketSize = 128
38         TagSize         = poly1305.TagSize
39         // S20BS is Salsa20's internal blocksize in bytes
40         S20BS = 64
41         // Maximal amount of bytes transfered with single key (4 GiB)
42         MaxBytesPerKey uint64 = 1 << 32
43         // Heartbeat rate, relative to Timeout
44         TimeoutHeartbeat = 4
45         // Minimal valid packet length
46         MinPktLength = 1 + 16 + 8
47         // Padding byte
48         PadByte = byte(0x80)
49 )
50
51 func newNonceCipher(key *[32]byte) *xtea.Cipher {
52         nonceKey := make([]byte, 16)
53         salsa20.XORKeyStream(
54                 nonceKey,
55                 make([]byte, 32),
56                 make([]byte, xtea.BlockSize),
57                 key,
58         )
59         ciph, err := xtea.NewCipher(nonceKey)
60         if err != nil {
61                 panic(err)
62         }
63         return ciph
64 }
65
66 type Peer struct {
67         // Statistics (they are at the beginning for correct int64 alignment)
68         BytesIn         uint64
69         BytesOut        uint64
70         BytesPayloadIn  uint64
71         BytesPayloadOut uint64
72         FramesIn        uint64
73         FramesOut       uint64
74         FramesUnauth    uint64
75         FramesDup       uint64
76         HeartbeatRecv   uint64
77         HeartbeatSent   uint64
78
79         // Basic
80         Addr string
81         Id   *PeerId
82         Conn io.Writer `json:"-"`
83
84         // Traffic behaviour
85         NoiseEnable bool
86         CPR         int
87         CPRCycle    time.Duration `json:"-"`
88         Encless     bool
89         MTU         int
90
91         // Cryptography related
92         Key          *[SSize]byte `json:"-"`
93         NonceCipher  *xtea.Cipher `json:"-"`
94         nonceRecv    uint64
95         nonceLatest  uint64
96         nonceOur     uint64
97         NonceExpect  uint64 `json:"-"`
98         nonceBucket0 map[uint64]struct{}
99         nonceBucket1 map[uint64]struct{}
100         nonceFound0  bool
101         nonceFound1  bool
102         nonceBucketN int32
103
104         // Timers
105         Timeout     time.Duration `json:"-"`
106         Established time.Time
107         LastPing    time.Time
108
109         // Receiver
110         BusyR    sync.Mutex `json:"-"`
111         bufR     []byte
112         tagR     *[TagSize]byte
113         keyAuthR *[SSize]byte
114         pktSizeR int
115
116         // Transmitter
117         BusyT    sync.Mutex `json:"-"`
118         bufT     []byte
119         tagT     *[TagSize]byte
120         keyAuthT *[SSize]byte
121         frameT   []byte
122 }
123
124 func (p *Peer) String() string {
125         return p.Id.String() + ":" + p.Addr
126 }
127
128 // Zero peer's memory state.
129 func (p *Peer) Zero() {
130         p.BusyT.Lock()
131         p.BusyR.Lock()
132         SliceZero(p.Key[:])
133         SliceZero(p.bufR)
134         SliceZero(p.bufT)
135         SliceZero(p.keyAuthR[:])
136         SliceZero(p.keyAuthT[:])
137         p.BusyT.Unlock()
138         p.BusyR.Unlock()
139 }
140
141 func (p *Peer) NonceExpectation(buf []byte) {
142         binary.BigEndian.PutUint64(buf, p.NonceExpect)
143         p.NonceCipher.Encrypt(buf, buf)
144 }
145
146 func cprCycleCalculate(conf *PeerConf) time.Duration {
147         if conf.CPR == 0 {
148                 return time.Duration(0)
149         }
150         rate := conf.CPR * 1 << 10
151         if conf.Encless {
152                 rate /= EnclessEnlargeSize + conf.MTU
153         } else {
154                 rate /= conf.MTU
155         }
156         return time.Second / time.Duration(rate)
157 }
158
159 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
160         now := time.Now()
161         timeout := conf.Timeout
162
163         cprCycle := cprCycleCalculate(conf)
164         noiseEnable := conf.Noise
165         if conf.CPR > 0 {
166                 noiseEnable = true
167                 timeout = cprCycle
168         } else {
169                 timeout = timeout / TimeoutHeartbeat
170         }
171
172         bufSize := S20BS + 2*conf.MTU
173         if conf.Encless {
174                 bufSize += EnclessEnlargeSize
175                 noiseEnable = true
176         }
177         peer := Peer{
178                 Addr: addr,
179                 Id:   conf.Id,
180                 Conn: conn,
181
182                 NoiseEnable: noiseEnable,
183                 CPR:         conf.CPR,
184                 CPRCycle:    cprCycle,
185                 Encless:     conf.Encless,
186                 MTU:         conf.MTU,
187
188                 Key:          key,
189                 NonceCipher:  newNonceCipher(key),
190                 nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
191                 nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
192
193                 Timeout:     timeout,
194                 Established: now,
195                 LastPing:    now,
196
197                 bufR:     make([]byte, bufSize),
198                 bufT:     make([]byte, bufSize),
199                 tagR:     new([TagSize]byte),
200                 tagT:     new([TagSize]byte),
201                 keyAuthR: new([SSize]byte),
202                 keyAuthT: new([SSize]byte),
203         }
204         if isClient {
205                 peer.nonceOur = 1
206                 peer.NonceExpect = 0 + 2
207         } else {
208                 peer.nonceOur = 0
209                 peer.NonceExpect = 1 + 2
210         }
211         return &peer
212
213 }
214
215 // Process incoming Ethernet packet.
216 // ready channel is TAPListen's synchronization channel used to tell him
217 // that he is free to receive new packets. Encrypted and authenticated
218 // packets will be sent to remote Peer side immediately.
219 func (p *Peer) EthProcess(data []byte) {
220         if len(data) > p.MTU-1 { // 1 is for padding byte
221                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
222                 return
223         }
224         p.BusyT.Lock()
225
226         // Zero size is a heartbeat packet
227         SliceZero(p.bufT)
228         if len(data) == 0 {
229                 p.bufT[S20BS+0] = PadByte
230                 p.HeartbeatSent++
231         } else {
232                 // Copy payload to our internal buffer and we are ready to
233                 // accept the next one
234                 copy(p.bufT[S20BS:], data)
235                 p.bufT[S20BS+len(data)] = PadByte
236                 p.BytesPayloadOut += uint64(len(data))
237         }
238
239         if p.NoiseEnable && !p.Encless {
240                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
241         } else if p.Encless {
242                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
243         } else {
244                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
245         }
246         p.nonceOur += 2
247         binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
248         p.NonceCipher.Encrypt(
249                 p.frameT[len(p.frameT)-NonceSize:],
250                 p.frameT[len(p.frameT)-NonceSize:],
251         )
252         var out []byte
253         if p.Encless {
254                 var err error
255                 out, err = EnclessEncode(
256                         p.Key,
257                         p.frameT[len(p.frameT)-NonceSize:],
258                         p.frameT[:len(p.frameT)-NonceSize],
259                 )
260                 if err != nil {
261                         panic(err)
262                 }
263                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
264         } else {
265                 salsa20.XORKeyStream(
266                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
267                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
268                         p.frameT[len(p.frameT)-NonceSize:],
269                         p.Key,
270                 )
271                 copy(p.keyAuthT[:], p.bufT[:SSize])
272                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
273                 atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize))
274                 out = append(p.tagT[:], p.frameT...)
275         }
276         p.FramesOut++
277         p.Conn.Write(out)
278         p.BusyT.Unlock()
279 }
280
281 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
282         if len(data) < MinPktLength {
283                 return false
284         }
285         if !p.Encless && len(data) > len(p.bufR)-S20BS {
286                 return false
287         }
288         var out []byte
289         p.BusyR.Lock()
290         if p.Encless {
291                 var err error
292                 out, err = EnclessDecode(
293                         p.Key,
294                         data[len(data)-NonceSize:],
295                         data[:len(data)-NonceSize],
296                 )
297                 if err != nil {
298                         p.FramesUnauth++
299                         p.BusyR.Unlock()
300                         return false
301                 }
302         } else {
303                 for i := 0; i < SSize; i++ {
304                         p.bufR[i] = 0
305                 }
306                 copy(p.bufR[S20BS:], data[TagSize:])
307                 salsa20.XORKeyStream(
308                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
309                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
310                         data[len(data)-NonceSize:],
311                         p.Key,
312                 )
313                 copy(p.keyAuthR[:], p.bufR[:SSize])
314                 copy(p.tagR[:], data[:TagSize])
315                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
316                         p.FramesUnauth++
317                         p.BusyR.Unlock()
318                         return false
319                 }
320                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
321         }
322
323         // Check if received nonce is known to us in either of two buckets.
324         // If yes, then this is ignored duplicate.
325         // Check from the oldest bucket, as in most cases this will result
326         // in constant time check.
327         // If Bucket0 is filled, then it becomes Bucket1.
328         p.NonceCipher.Decrypt(
329                 data[len(data)-NonceSize:],
330                 data[len(data)-NonceSize:],
331         )
332         p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
333         if reorderable {
334                 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
335                 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
336                 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
337                         p.FramesDup++
338                         p.BusyR.Unlock()
339                         return false
340                 }
341                 p.nonceBucket0[p.nonceRecv] = struct{}{}
342                 p.nonceBucketN++
343                 if p.nonceBucketN == NonceBucketSize {
344                         p.nonceBucket1 = p.nonceBucket0
345                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
346                         p.nonceBucketN = 0
347                 }
348         } else {
349                 if p.nonceRecv != p.NonceExpect {
350                         p.FramesDup++
351                         p.BusyR.Unlock()
352                         return false
353                 }
354                 p.NonceExpect += 2
355         }
356         if p.nonceRecv > p.nonceLatest {
357                 p.nonceLatest = p.nonceRecv
358         }
359
360         p.FramesIn++
361         atomic.AddUint64(&p.BytesIn, uint64(len(data)))
362         p.LastPing = time.Now()
363         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
364         if p.pktSizeR == -1 {
365                 p.BusyR.Unlock()
366                 return false
367         }
368         // Validate the pad
369         for i := p.pktSizeR + 1; i < len(out); i++ {
370                 if out[i] != 0 {
371                         p.BusyR.Unlock()
372                         return false
373                 }
374         }
375
376         if p.pktSizeR == 0 {
377                 p.HeartbeatRecv++
378                 p.BusyR.Unlock()
379                 return true
380         }
381         p.BytesPayloadIn += uint64(p.pktSizeR)
382         tap.Write(out[:p.pktSizeR])
383         p.BusyR.Unlock()
384         return true
385 }
386
387 func PeerTapProcessor(peer *Peer, tap *TAP, terminator chan struct{}) {
388         var data []byte
389         var now time.Time
390         lastSent := time.Now()
391         heartbeat := time.NewTicker(peer.Timeout)
392         if peer.CPRCycle == time.Duration(0) {
393         RawProcessor:
394                 for {
395                         select {
396                         case <-terminator:
397                                 break RawProcessor
398                         case <-heartbeat.C:
399                                 now = time.Now()
400                                 if lastSent.Add(peer.Timeout).Before(now) {
401                                         peer.EthProcess(nil)
402                                         lastSent = now
403                                 }
404                         case data = <-tap.Sink:
405                                 peer.EthProcess(data)
406                                 lastSent = time.Now()
407                         }
408                 }
409         } else {
410         CPRProcessor:
411                 for {
412                         data = nil
413                         select {
414                         case <-terminator:
415                                 break CPRProcessor
416                         case data = <-tap.Sink:
417                                 peer.EthProcess(data)
418                         default:
419                         }
420                         if data == nil {
421                                 peer.EthProcess(nil)
422                         }
423                         time.Sleep(peer.CPRCycle)
424                 }
425         }
426         close(terminator)
427         peer.Zero()
428         heartbeat.Stop()
429 }