]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/peer.go
213e8174786df4db45d72abfa40a20043851b9a4
[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         "crypto/subtle"
24         "encoding/binary"
25         "io"
26         "log"
27         "sync"
28         "sync/atomic"
29         "time"
30
31         "golang.org/x/crypto/blake2b"
32         "golang.org/x/crypto/poly1305"
33         "golang.org/x/crypto/salsa20"
34 )
35
36 const (
37         NonceSize       = 8
38         NonceBucketSize = 256
39         TagSize         = poly1305.TagSize
40         // S20BS is Salsa20's internal blocksize in bytes
41         S20BS = 64
42         // Maximal amount of bytes transfered with single key (4 GiB)
43         MaxBytesPerKey uint64 = 1 << 32
44         // Heartbeat rate, relative to Timeout
45         TimeoutHeartbeat = 4
46         // Minimal valid packet length
47         MinPktLength = 1 + 16 + 8
48         // Padding byte
49         PadByte = byte(0x80)
50 )
51
52 func newNonces(key *[32]byte, i uint64) chan *[NonceSize]byte {
53         macKey := make([]byte, 32)
54         salsa20.XORKeyStream(macKey, make([]byte, 32), make([]byte, 8), key)
55         mac, err := blake2b.New256(macKey)
56         if err != nil {
57                 panic(err)
58         }
59         nonces := make(chan *[NonceSize]byte, NonceBucketSize*3)
60         go func() {
61                 for {
62                         buf := new([NonceSize]byte)
63                         binary.BigEndian.PutUint64(buf[:], i)
64                         mac.Write(buf[:])
65                         mac.Sum(buf[:0])
66                         nonces <- buf
67                         mac.Reset()
68                         i += 2
69                 }
70         }()
71         return nonces
72 }
73
74 type Peer struct {
75         // Statistics (they are at the beginning for correct int64 alignment)
76         BytesIn         uint64
77         BytesOut        uint64
78         BytesPayloadIn  uint64
79         BytesPayloadOut uint64
80         FramesIn        uint64
81         FramesOut       uint64
82         FramesUnauth    uint64
83         FramesDup       uint64
84         HeartbeatRecv   uint64
85         HeartbeatSent   uint64
86
87         // Basic
88         Addr string
89         Id   *PeerId
90         Conn io.Writer `json:"-"`
91
92         // Traffic behaviour
93         NoiseEnable bool
94         CPR         int
95         CPRCycle    time.Duration `json:"-"`
96         Encless     bool
97         MTU         int
98
99         key *[SSize]byte `json:"-"`
100
101         // Timers
102         Timeout     time.Duration `json:"-"`
103         Established time.Time
104         LastPing    time.Time
105
106         // Receiver
107         BusyR    sync.Mutex `json:"-"`
108         bufR     []byte
109         tagR     *[TagSize]byte
110         keyAuthR *[SSize]byte
111         pktSizeR int
112
113         // UDP-related
114         noncesR      chan *[NonceSize]byte
115         nonceRecv    [NonceSize]byte
116         nonceBucketL map[[NonceSize]byte]struct{}
117         nonceBucketM map[[NonceSize]byte]struct{}
118         nonceBucketH map[[NonceSize]byte]struct{}
119
120         // TCP-related
121         NonceExpect  []byte `json:"-"`
122         noncesExpect chan *[NonceSize]byte
123
124         // Transmitter
125         BusyT    sync.Mutex `json:"-"`
126         bufT     []byte
127         tagT     *[TagSize]byte
128         keyAuthT *[SSize]byte
129         frameT   []byte
130         noncesT  chan *[NonceSize]byte
131 }
132
133 func (p *Peer) String() string {
134         return p.Id.String() + ":" + p.Addr
135 }
136
137 // Zero peer's memory state.
138 func (p *Peer) Zero() {
139         p.BusyT.Lock()
140         p.BusyR.Lock()
141         SliceZero(p.key[:])
142         SliceZero(p.bufR)
143         SliceZero(p.bufT)
144         SliceZero(p.keyAuthR[:])
145         SliceZero(p.keyAuthT[:])
146         p.BusyT.Unlock()
147         p.BusyR.Unlock()
148 }
149
150 func cprCycleCalculate(conf *PeerConf) time.Duration {
151         if conf.CPR == 0 {
152                 return time.Duration(0)
153         }
154         rate := conf.CPR * 1 << 10
155         if conf.Encless {
156                 rate /= EnclessEnlargeSize + conf.MTU
157         } else {
158                 rate /= conf.MTU
159         }
160         return time.Second / time.Duration(rate)
161 }
162
163 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
164         now := time.Now()
165         timeout := conf.Timeout
166
167         cprCycle := cprCycleCalculate(conf)
168         noiseEnable := conf.Noise
169         if conf.CPR > 0 {
170                 noiseEnable = true
171                 timeout = cprCycle
172         } else {
173                 timeout = timeout / TimeoutHeartbeat
174         }
175
176         bufSize := S20BS + 2*conf.MTU
177         if conf.Encless {
178                 bufSize += EnclessEnlargeSize
179                 noiseEnable = true
180         }
181
182         peer := Peer{
183                 Addr: addr,
184                 Id:   conf.Id,
185                 Conn: conn,
186
187                 NoiseEnable: noiseEnable,
188                 CPR:         conf.CPR,
189                 CPRCycle:    cprCycle,
190                 Encless:     conf.Encless,
191                 MTU:         conf.MTU,
192
193                 key: key,
194
195                 Timeout:     timeout,
196                 Established: now,
197                 LastPing:    now,
198
199                 bufR:     make([]byte, bufSize),
200                 bufT:     make([]byte, bufSize),
201                 tagR:     new([TagSize]byte),
202                 tagT:     new([TagSize]byte),
203                 keyAuthR: new([SSize]byte),
204                 keyAuthT: new([SSize]byte),
205         }
206
207         if isClient {
208                 peer.noncesT = newNonces(peer.key, 1+2)
209                 peer.noncesR = newNonces(peer.key, 0+2)
210                 peer.noncesExpect = newNonces(peer.key, 0+2)
211         } else {
212                 peer.noncesT = newNonces(peer.key, 0+2)
213                 peer.noncesR = newNonces(peer.key, 1+2)
214                 peer.noncesExpect = newNonces(peer.key, 1+2)
215         }
216
217         peer.NonceExpect = make([]byte, NonceSize)
218         nonce := <-peer.noncesExpect
219         copy(peer.NonceExpect, nonce[:])
220
221         var i int
222         peer.nonceBucketL = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
223         for i = 0; i < NonceBucketSize; i++ {
224                 nonce = <-peer.noncesR
225                 peer.nonceBucketL[*nonce] = struct{}{}
226         }
227         peer.nonceBucketM = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
228         for i = 0; i < NonceBucketSize; i++ {
229                 nonce = <-peer.noncesR
230                 peer.nonceBucketM[*nonce] = struct{}{}
231         }
232         peer.nonceBucketH = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
233         for i = 0; i < NonceBucketSize; i++ {
234                 nonce = <-peer.noncesR
235                 peer.nonceBucketH[*nonce] = struct{}{}
236         }
237
238         return &peer
239 }
240
241 // Process incoming Ethernet packet.
242 // ready channel is TAPListen's synchronization channel used to tell him
243 // that he is free to receive new packets. Encrypted and authenticated
244 // packets will be sent to remote Peer side immediately.
245 func (p *Peer) EthProcess(data []byte) {
246         if len(data) > p.MTU-1 { // 1 is for padding byte
247                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
248                 return
249         }
250         p.BusyT.Lock()
251
252         // Zero size is a heartbeat packet
253         SliceZero(p.bufT)
254         if len(data) == 0 {
255                 p.bufT[S20BS+0] = PadByte
256                 p.HeartbeatSent++
257         } else {
258                 // Copy payload to our internal buffer and we are ready to
259                 // accept the next one
260                 copy(p.bufT[S20BS:], data)
261                 p.bufT[S20BS+len(data)] = PadByte
262                 p.BytesPayloadOut += uint64(len(data))
263         }
264
265         if p.NoiseEnable && !p.Encless {
266                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
267         } else if p.Encless {
268                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
269         } else {
270                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
271         }
272         copy(p.frameT[len(p.frameT)-NonceSize:], (<-p.noncesT)[:])
273         var out []byte
274         if p.Encless {
275                 var err error
276                 out, err = EnclessEncode(
277                         p.key,
278                         p.frameT[len(p.frameT)-NonceSize:],
279                         p.frameT[:len(p.frameT)-NonceSize],
280                 )
281                 if err != nil {
282                         panic(err)
283                 }
284                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
285         } else {
286                 salsa20.XORKeyStream(
287                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
288                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
289                         p.frameT[len(p.frameT)-NonceSize:],
290                         p.key,
291                 )
292                 copy(p.keyAuthT[:], p.bufT[:SSize])
293                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
294                 atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize))
295                 out = append(p.tagT[:], p.frameT...)
296         }
297         p.FramesOut++
298         p.Conn.Write(out)
299         p.BusyT.Unlock()
300 }
301
302 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
303         if len(data) < MinPktLength {
304                 return false
305         }
306         if !p.Encless && len(data) > len(p.bufR)-S20BS {
307                 return false
308         }
309         var out []byte
310         p.BusyR.Lock()
311         if p.Encless {
312                 var err error
313                 out, err = EnclessDecode(
314                         p.key,
315                         data[len(data)-NonceSize:],
316                         data[:len(data)-NonceSize],
317                 )
318                 if err != nil {
319                         p.FramesUnauth++
320                         p.BusyR.Unlock()
321                         return false
322                 }
323         } else {
324                 for i := 0; i < SSize; i++ {
325                         p.bufR[i] = 0
326                 }
327                 copy(p.bufR[S20BS:], data[TagSize:])
328                 salsa20.XORKeyStream(
329                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
330                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
331                         data[len(data)-NonceSize:],
332                         p.key,
333                 )
334                 copy(p.keyAuthR[:], p.bufR[:SSize])
335                 copy(p.tagR[:], data[:TagSize])
336                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
337                         p.FramesUnauth++
338                         p.BusyR.Unlock()
339                         return false
340                 }
341                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
342         }
343
344         if reorderable {
345                 copy(p.nonceRecv[:], data[len(data)-NonceSize:])
346                 _, foundL := p.nonceBucketL[p.nonceRecv]
347                 _, foundM := p.nonceBucketM[p.nonceRecv]
348                 _, foundH := p.nonceBucketH[p.nonceRecv]
349                 // If found is none of buckets: either it is too old,
350                 // or too new (many packets were lost)
351                 if !(foundL || foundM || foundH) {
352                         p.FramesDup++
353                         p.BusyR.Unlock()
354                         return false
355                 }
356                 // Delete seen nonce
357                 if foundL {
358                         delete(p.nonceBucketL, p.nonceRecv)
359                 }
360                 if foundM {
361                         delete(p.nonceBucketM, p.nonceRecv)
362                 }
363                 if foundH {
364                         delete(p.nonceBucketH, p.nonceRecv)
365                 }
366                 // If we are dealing with the latest bucket, create the new one
367                 if foundH {
368                         p.nonceBucketL, p.nonceBucketM = p.nonceBucketM, p.nonceBucketH
369                         p.nonceBucketH = make(map[[NonceSize]byte]struct{})
370                         var nonce *[NonceSize]byte
371                         for i := 0; i < NonceBucketSize; i++ {
372                                 nonce = <-p.noncesR
373                                 p.nonceBucketH[*nonce] = struct{}{}
374                         }
375                 }
376         } else {
377                 if subtle.ConstantTimeCompare(data[len(data)-NonceSize:], p.NonceExpect) != 1 {
378                         p.FramesDup++
379                         p.BusyR.Unlock()
380                         return false
381                 }
382                 copy(p.NonceExpect, (<-p.noncesExpect)[:])
383         }
384
385         p.FramesIn++
386         atomic.AddUint64(&p.BytesIn, uint64(len(data)))
387         p.LastPing = time.Now()
388         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
389         if p.pktSizeR == -1 {
390                 p.BusyR.Unlock()
391                 return false
392         }
393         // Validate the pad
394         for i := p.pktSizeR + 1; i < len(out); i++ {
395                 if out[i] != 0 {
396                         p.BusyR.Unlock()
397                         return false
398                 }
399         }
400
401         if p.pktSizeR == 0 {
402                 p.HeartbeatRecv++
403                 p.BusyR.Unlock()
404                 return true
405         }
406         p.BytesPayloadIn += uint64(p.pktSizeR)
407         tap.Write(out[:p.pktSizeR])
408         p.BusyR.Unlock()
409         return true
410 }
411
412 func PeerTapProcessor(peer *Peer, tap *TAP, terminator chan struct{}) {
413         var data []byte
414         var now time.Time
415         lastSent := time.Now()
416         heartbeat := time.NewTicker(peer.Timeout)
417         if peer.CPRCycle == time.Duration(0) {
418         RawProcessor:
419                 for {
420                         select {
421                         case <-terminator:
422                                 break RawProcessor
423                         case <-heartbeat.C:
424                                 now = time.Now()
425                                 if lastSent.Add(peer.Timeout).Before(now) {
426                                         peer.EthProcess(nil)
427                                         lastSent = now
428                                 }
429                         case data = <-tap.Sink:
430                                 peer.EthProcess(data)
431                                 lastSent = time.Now()
432                         }
433                 }
434         } else {
435         CPRProcessor:
436                 for {
437                         data = nil
438                         select {
439                         case <-terminator:
440                                 break CPRProcessor
441                         case data = <-tap.Sink:
442                                 peer.EthProcess(data)
443                         default:
444                         }
445                         if data == nil {
446                                 peer.EthProcess(nil)
447                         }
448                         time.Sleep(peer.CPRCycle)
449                 }
450         }
451         close(terminator)
452         peer.Zero()
453         heartbeat.Stop()
454 }