]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/peer.go
Configure MTU on per-user basis
[govpn.git] / src / 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 int64 = 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         Addr string
68         Id   *PeerId
69         Conn io.Writer
70
71         // Traffic behaviour
72         NoiseEnable bool
73         CPR         int
74         CPRCycle    time.Duration `json:"-"`
75         EncLess     bool
76         MTU         int
77
78         // Cryptography related
79         Key          *[SSize]byte `json:"-"`
80         NonceCipher  *xtea.Cipher `json:"-"`
81         nonceRecv    uint64
82         nonceLatest  uint64
83         nonceOur     uint64
84         NonceExpect  uint64 `json:"-"`
85         nonceBucket0 map[uint64]struct{}
86         nonceBucket1 map[uint64]struct{}
87         nonceFound0  bool
88         nonceFound1  bool
89         nonceBucketN int32
90
91         // Timers
92         Timeout       time.Duration `json:"-"`
93         Established   time.Time
94         LastPing      time.Time
95         LastSent      time.Time
96         willSentCycle time.Time
97
98         // Statistics
99         BytesIn         int64
100         BytesOut        int64
101         BytesPayloadIn  int64
102         BytesPayloadOut int64
103         FramesIn        int
104         FramesOut       int
105         FramesUnauth    int
106         FramesDup       int
107         HeartbeatRecv   int
108         HeartbeatSent   int
109
110         // Receiver
111         BusyR    sync.Mutex `json:"-"`
112         bufR     []byte
113         tagR     *[TagSize]byte
114         keyAuthR *[SSize]byte
115         pktSizeR int
116
117         // Transmitter
118         BusyT    sync.Mutex `json:"-"`
119         bufT     []byte
120         tagT     *[TagSize]byte
121         keyAuthT *[SSize]byte
122         frameT   []byte
123         now      time.Time
124 }
125
126 func (p *Peer) String() string {
127         return p.Id.String() + ":" + p.Addr
128 }
129
130 // Zero peer's memory state.
131 func (p *Peer) Zero() {
132         p.BusyT.Lock()
133         p.BusyR.Lock()
134         SliceZero(p.Key[:])
135         SliceZero(p.bufR)
136         SliceZero(p.bufT)
137         SliceZero(p.keyAuthR[:])
138         SliceZero(p.keyAuthT[:])
139         p.BusyT.Unlock()
140         p.BusyR.Unlock()
141 }
142
143 func (p *Peer) NonceExpectation(buf []byte) {
144         binary.BigEndian.PutUint64(buf, p.NonceExpect)
145         p.NonceCipher.Encrypt(buf, buf)
146 }
147
148 func newPeer(isClient bool, addr string, conn io.Writer, conf *PeerConf, key *[SSize]byte) *Peer {
149         now := time.Now()
150         timeout := conf.Timeout
151
152         cprCycle := cprCycleCalculate(conf.CPR)
153         noiseEnable := conf.Noise
154         if conf.CPR > 0 {
155                 noiseEnable = true
156                 timeout = cprCycle
157         } else {
158                 timeout = timeout / TimeoutHeartbeat
159         }
160
161         bufSize := S20BS + 2*conf.MTU
162         if conf.EncLess {
163                 bufSize += EncLessEnlargeSize
164                 noiseEnable = true
165         }
166         peer := Peer{
167                 Addr: addr,
168                 Id:   conf.Id,
169                 Conn: conn,
170
171                 NoiseEnable: noiseEnable,
172                 CPR:         conf.CPR,
173                 CPRCycle:    cprCycle,
174                 EncLess:     conf.EncLess,
175                 MTU:         conf.MTU,
176
177                 Key:          key,
178                 NonceCipher:  newNonceCipher(key),
179                 nonceBucket0: make(map[uint64]struct{}, NonceBucketSize),
180                 nonceBucket1: make(map[uint64]struct{}, NonceBucketSize),
181
182                 Timeout:     timeout,
183                 Established: now,
184                 LastPing:    now,
185
186                 bufR:     make([]byte, bufSize),
187                 bufT:     make([]byte, bufSize),
188                 tagR:     new([TagSize]byte),
189                 tagT:     new([TagSize]byte),
190                 keyAuthR: new([SSize]byte),
191                 keyAuthT: new([SSize]byte),
192         }
193         if isClient {
194                 peer.nonceOur = 1
195                 peer.NonceExpect = 0 + 2
196         } else {
197                 peer.nonceOur = 0
198                 peer.NonceExpect = 1 + 2
199         }
200         return &peer
201
202 }
203
204 // Process incoming Ethernet packet.
205 // ready channel is TAPListen's synchronization channel used to tell him
206 // that he is free to receive new packets. Encrypted and authenticated
207 // packets will be sent to remote Peer side immediately.
208 func (p *Peer) EthProcess(data []byte) {
209         if len(data) > p.MTU-1 { // 1 is for padding byte
210                 log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p)
211                 return
212         }
213         p.now = time.Now()
214         p.BusyT.Lock()
215
216         // Zero size is a heartbeat packet
217         SliceZero(p.bufT)
218         if len(data) == 0 {
219                 // If this heartbeat is necessary
220                 if !p.LastSent.Add(p.Timeout).Before(p.now) {
221                         p.BusyT.Unlock()
222                         return
223                 }
224                 p.bufT[S20BS+0] = PadByte
225                 p.HeartbeatSent++
226         } else {
227                 // Copy payload to our internal buffer and we are ready to
228                 // accept the next one
229                 copy(p.bufT[S20BS:], data)
230                 p.bufT[S20BS+len(data)] = PadByte
231                 p.BytesPayloadOut += int64(len(data))
232         }
233
234         if p.NoiseEnable && !p.EncLess {
235                 p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize]
236         } else if p.EncLess {
237                 p.frameT = p.bufT[S20BS : S20BS+p.MTU]
238         } else {
239                 p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize]
240         }
241         p.nonceOur += 2
242         binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur)
243         p.NonceCipher.Encrypt(
244                 p.frameT[len(p.frameT)-NonceSize:],
245                 p.frameT[len(p.frameT)-NonceSize:],
246         )
247         var out []byte
248         if p.EncLess {
249                 var err error
250                 out, err = EncLessEncode(
251                         p.Key,
252                         p.frameT[len(p.frameT)-NonceSize:],
253                         p.frameT[:len(p.frameT)-NonceSize],
254                 )
255                 if err != nil {
256                         panic(err)
257                 }
258                 out = append(out, p.frameT[len(p.frameT)-NonceSize:]...)
259         } else {
260                 salsa20.XORKeyStream(
261                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
262                         p.bufT[:S20BS+len(p.frameT)-NonceSize],
263                         p.frameT[len(p.frameT)-NonceSize:],
264                         p.Key,
265                 )
266                 copy(p.keyAuthT[:], p.bufT[:SSize])
267                 poly1305.Sum(p.tagT, p.frameT, p.keyAuthT)
268                 atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize))
269                 out = append(p.tagT[:], p.frameT...)
270         }
271         p.FramesOut++
272
273         if p.CPRCycle != time.Duration(0) {
274                 p.willSentCycle = p.LastSent.Add(p.CPRCycle)
275                 if p.willSentCycle.After(p.now) {
276                         time.Sleep(p.willSentCycle.Sub(p.now))
277                         p.now = p.willSentCycle
278                 }
279         }
280
281         p.LastSent = p.now
282         p.Conn.Write(out)
283         p.BusyT.Unlock()
284 }
285
286 func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool {
287         if len(data) < MinPktLength {
288                 return false
289         }
290         var out []byte
291         p.BusyR.Lock()
292         if p.EncLess {
293                 var err error
294                 out, err = EncLessDecode(
295                         p.Key,
296                         data[len(data)-NonceSize:],
297                         data[:len(data)-NonceSize],
298                 )
299                 if err != nil {
300                         p.FramesUnauth++
301                         p.BusyR.Unlock()
302                         return false
303                 }
304         } else {
305                 for i := 0; i < SSize; i++ {
306                         p.bufR[i] = 0
307                 }
308                 copy(p.bufR[S20BS:], data[TagSize:])
309                 salsa20.XORKeyStream(
310                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
311                         p.bufR[:S20BS+len(data)-TagSize-NonceSize],
312                         data[len(data)-NonceSize:],
313                         p.Key,
314                 )
315                 copy(p.keyAuthR[:], p.bufR[:SSize])
316                 copy(p.tagR[:], data[:TagSize])
317                 if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) {
318                         p.FramesUnauth++
319                         p.BusyR.Unlock()
320                         return false
321                 }
322                 out = p.bufR[S20BS : S20BS+len(data)-TagSize-NonceSize]
323         }
324
325         // Check if received nonce is known to us in either of two buckets.
326         // If yes, then this is ignored duplicate.
327         // Check from the oldest bucket, as in most cases this will result
328         // in constant time check.
329         // If Bucket0 is filled, then it becomes Bucket1.
330         p.NonceCipher.Decrypt(
331                 data[len(data)-NonceSize:],
332                 data[len(data)-NonceSize:],
333         )
334         p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:])
335         if reorderable {
336                 _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv]
337                 _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv]
338                 if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest {
339                         p.FramesDup++
340                         p.BusyR.Unlock()
341                         return false
342                 }
343                 p.nonceBucket0[p.nonceRecv] = struct{}{}
344                 p.nonceBucketN++
345                 if p.nonceBucketN == NonceBucketSize {
346                         p.nonceBucket1 = p.nonceBucket0
347                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
348                         p.nonceBucketN = 0
349                 }
350         } else {
351                 if p.nonceRecv != p.NonceExpect {
352                         p.FramesDup++
353                         p.BusyR.Unlock()
354                         return false
355                 }
356                 p.NonceExpect += 2
357         }
358         if p.nonceRecv > p.nonceLatest {
359                 p.nonceLatest = p.nonceRecv
360         }
361
362         p.FramesIn++
363         atomic.AddInt64(&p.BytesIn, int64(len(data)))
364         p.LastPing = time.Now()
365         p.pktSizeR = bytes.LastIndexByte(out, PadByte)
366         if p.pktSizeR == -1 {
367                 p.BusyR.Unlock()
368                 return false
369         }
370         // Validate the pad
371         for i := p.pktSizeR + 1; i < len(out); i++ {
372                 if out[i] != 0 {
373                         p.BusyR.Unlock()
374                         return false
375                 }
376         }
377
378         if p.pktSizeR == 0 {
379                 p.HeartbeatRecv++
380                 p.BusyR.Unlock()
381                 return true
382         }
383         p.BytesPayloadIn += int64(p.pktSizeR)
384         tap.Write(out[:p.pktSizeR])
385         p.BusyR.Unlock()
386         return true
387 }