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