]> Cypherpunks.ru repositories - govpn.git/blob - src/cypherpunks.ru/govpn/peer.go
go vet/lint
[govpn.git] / src / cypherpunks.ru / govpn / peer.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2017 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         "chacha20"
32         "golang.org/x/crypto/blake2b"
33         "golang.org/x/crypto/poly1305"
34 )
35
36 const (
37         NonceSize       = 8
38         NonceBucketSize = 256
39         TagSize         = poly1305.TagSize
40         // S20BS is ChaCha20'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         chacha20.XORKeyStream(macKey, make([]byte, 32), new([16]byte), key)
55         mac, err := blake2b.New256(macKey)
56         if err != nil {
57                 panic(err)
58         }
59         sum := make([]byte, mac.Size())
60         nonces := make(chan *[NonceSize]byte, NonceBucketSize*3)
61         go func() {
62                 for {
63                         buf := new([NonceSize]byte)
64                         binary.BigEndian.PutUint64(buf[:], i)
65                         mac.Write(buf[:])
66                         mac.Sum(sum[0:])
67                         copy(buf[:], sum)
68                         nonces <- buf
69                         mac.Reset()
70                         i += 2
71                 }
72         }()
73         return nonces
74 }
75
76 type Peer struct {
77         // Statistics (they are at the beginning for correct int64 alignment)
78         BytesIn         uint64
79         BytesOut        uint64
80         BytesPayloadIn  uint64
81         BytesPayloadOut uint64
82         FramesIn        uint64
83         FramesOut       uint64
84         FramesUnauth    uint64
85         FramesDup       uint64
86         HeartbeatRecv   uint64
87         HeartbeatSent   uint64
88
89         // Basic
90         Addr string
91         ID   *PeerID
92         Conn io.Writer `json:"-"`
93
94         // Traffic behaviour
95         NoiseEnable bool
96         CPR         int
97         CPRCycle    time.Duration `json:"-"`
98         Encless     bool
99         MTU         int
100
101         key *[SSize]byte
102
103         // Timers
104         Timeout     time.Duration `json:"-"`
105         Established time.Time
106         LastPing    time.Time
107
108         // Receiver
109         BusyR    sync.Mutex `json:"-"`
110         bufR     []byte
111         tagR     *[TagSize]byte
112         keyAuthR *[SSize]byte
113         nonceR   *[16]byte
114         pktSizeR int
115
116         // UDP-related
117         noncesR      chan *[NonceSize]byte
118         nonceRecv    [NonceSize]byte
119         nonceBucketL map[[NonceSize]byte]struct{}
120         nonceBucketM map[[NonceSize]byte]struct{}
121         nonceBucketH map[[NonceSize]byte]struct{}
122
123         // TCP-related
124         NonceExpect  []byte `json:"-"`
125         noncesExpect chan *[NonceSize]byte
126
127         // Transmitter
128         BusyT    sync.Mutex `json:"-"`
129         bufT     []byte
130         tagT     *[TagSize]byte
131         keyAuthT *[SSize]byte
132         nonceT   *[16]byte
133         frameT   []byte
134         noncesT  chan *[NonceSize]byte
135 }
136
137 func (p *Peer) String() string {
138         return p.ID.String() + ":" + p.Addr
139 }
140
141 // Zero peer's memory state.
142 func (p *Peer) Zero() {
143         p.BusyT.Lock()
144         p.BusyR.Lock()
145         SliceZero(p.key[:])
146         SliceZero(p.bufR)
147         SliceZero(p.bufT)
148         SliceZero(p.keyAuthR[:])
149         SliceZero(p.keyAuthT[:])
150         p.BusyT.Unlock()
151         p.BusyR.Unlock()
152 }
153
154 func cprCycleCalculate(conf *PeerConf) time.Duration {
155         if conf.CPR == 0 {
156                 return time.Duration(0)
157         }
158         rate := conf.CPR * 1 << 10
159         if conf.Encless {
160                 rate /= EnclessEnlargeSize + conf.MTU
161         } else {
162                 rate /= conf.MTU
163         }
164         return time.Second / time.Duration(rate)
165 }
166
167 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
168         now := time.Now()
169         timeout := conf.Timeout
170
171         cprCycle := cprCycleCalculate(conf)
172         noiseEnable := conf.Noise
173         if conf.CPR > 0 {
174                 noiseEnable = true
175                 timeout = cprCycle
176         } else {
177                 timeout = timeout / TimeoutHeartbeat
178         }
179
180         bufSize := S20BS + 2*conf.MTU
181         if conf.Encless {
182                 bufSize += EnclessEnlargeSize
183                 noiseEnable = true
184         }
185
186         peer := Peer{
187                 Addr: addr,
188                 ID:   conf.ID,
189                 Conn: conn,
190
191                 NoiseEnable: noiseEnable,
192                 CPR:         conf.CPR,
193                 CPRCycle:    cprCycle,
194                 Encless:     conf.Encless,
195                 MTU:         conf.MTU,
196
197                 key: key,
198
199                 Timeout:     timeout,
200                 Established: now,
201                 LastPing:    now,
202
203                 bufR:     make([]byte, bufSize),
204                 bufT:     make([]byte, bufSize),
205                 tagR:     new([TagSize]byte),
206                 tagT:     new([TagSize]byte),
207                 keyAuthR: new([SSize]byte),
208                 nonceR:   new([16]byte),
209                 keyAuthT: new([SSize]byte),
210                 nonceT:   new([16]byte),
211         }
212
213         if isClient {
214                 peer.noncesT = newNonces(peer.key, 1+2)
215                 peer.noncesR = newNonces(peer.key, 0+2)
216                 peer.noncesExpect = newNonces(peer.key, 0+2)
217         } else {
218                 peer.noncesT = newNonces(peer.key, 0+2)
219                 peer.noncesR = newNonces(peer.key, 1+2)
220                 peer.noncesExpect = newNonces(peer.key, 1+2)
221         }
222
223         peer.NonceExpect = make([]byte, NonceSize)
224         nonce := <-peer.noncesExpect
225         copy(peer.NonceExpect, nonce[:])
226
227         var i int
228         peer.nonceBucketL = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
229         for i = 0; i < NonceBucketSize; i++ {
230                 nonce = <-peer.noncesR
231                 peer.nonceBucketL[*nonce] = struct{}{}
232         }
233         peer.nonceBucketM = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
234         for i = 0; i < NonceBucketSize; i++ {
235                 nonce = <-peer.noncesR
236                 peer.nonceBucketM[*nonce] = struct{}{}
237         }
238         peer.nonceBucketH = make(map[[NonceSize]byte]struct{}, NonceBucketSize)
239         for i = 0; i < NonceBucketSize; i++ {
240                 nonce = <-peer.noncesR
241                 peer.nonceBucketH[*nonce] = struct{}{}
242         }
243
244         return &peer
245 }
246
247 // Process incoming Ethernet packet.
248 // ready channel is TAPListen's synchronization channel used to tell him
249 // that he is free to receive new packets. Encrypted and authenticated
250 // packets will be sent to remote Peer side immediately.
251 func (p *Peer) EthProcess(data []byte) {
252         if len(data) > p.MTU-1 { // 1 is for padding byte
253                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
254                 return
255         }
256         p.BusyT.Lock()
257
258         // Zero size is a heartbeat packet
259         SliceZero(p.bufT)
260         if len(data) == 0 {
261                 p.bufT[S20BS+0] = PadByte
262                 p.HeartbeatSent++
263         } else {
264                 // Copy payload to our internal buffer and we are ready to
265                 // accept the next one
266                 copy(p.bufT[S20BS:], data)
267                 p.bufT[S20BS+len(data)] = PadByte
268                 p.BytesPayloadOut += uint64(len(data))
269         }
270
271         if p.NoiseEnable && !p.Encless {
272                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
273         } else if p.Encless {
274                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
275         } else {
276                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
277         }
278         copy(p.frameT[len(p.frameT)-NonceSize:], (<-p.noncesT)[:])
279         var out []byte
280         copy(p.nonceT[8:], p.frameT[len(p.frameT)-NonceSize:])
281         if p.Encless {
282                 var err error
283                 out, err = EnclessEncode(p.key, p.nonceT, p.frameT[:len(p.frameT)-NonceSize])
284                 if err != nil {
285                         panic(err)
286                 }
287                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
288         } else {
289                 chacha20.XORKeyStream(
290                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
291                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
292                         p.nonceT,
293                         p.key,
294                 )
295                 copy(p.keyAuthT[:], p.bufT[:SSize])
296                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
297                 atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize))
298                 out = append(p.tagT[:], p.frameT...)
299         }
300         p.FramesOut++
301         p.Conn.Write(out)
302         p.BusyT.Unlock()
303 }
304
305 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
306         if len(data) < MinPktLength {
307                 return false
308         }
309         if !p.Encless && len(data) > len(p.bufR)-S20BS {
310                 return false
311         }
312         var out []byte
313         p.BusyR.Lock()
314         copy(p.nonceR[8:], data[len(data)-NonceSize:])
315         if p.Encless {
316                 var err error
317                 out, err = EnclessDecode(p.key, p.nonceR, data[:len(data)-NonceSize])
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                 chacha20.XORKeyStream(
329                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
330                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
331                         p.nonceR,
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 }