]> Cypherpunks.ru repositories - govpn.git/blob - src/govpn/transport.go
9c7a73328edb2fef7d8e81ec155e58a22bdf51d8
[govpn.git] / src / govpn / transport.go
1 /*
2 GoVPN -- simple secure free software virtual private network daemon
3 Copyright (C) 2014-2015 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         "encoding/binary"
23         "io"
24         "time"
25
26         "golang.org/x/crypto/poly1305"
27         "golang.org/x/crypto/salsa20"
28         "golang.org/x/crypto/xtea"
29 )
30
31 const (
32         NonceSize       = 8
33         NonceBucketSize = 128
34         // S20BS is Salsa20's internal blocksize in bytes
35         S20BS = 64
36         // Maximal amount of bytes transfered with single key (4 GiB)
37         MaxBytesPerKey int64 = 1 << 32
38         // Size of packet's size mark in bytes
39         PktSizeSize = 2
40         // Heartbeat rate, relative to Timeout
41         TimeoutHeartbeat = 4
42 )
43
44 type RemoteConn interface {
45         io.Writer
46         // Can incoming packets be reordered
47         Reorderable() bool
48 }
49
50 type Peer struct {
51         Addr string
52         Id   *PeerId
53         Conn RemoteConn
54
55         // Traffic behaviour
56         NoiseEnable bool
57         CPR         int
58         CPRCycle    time.Duration `json:"-"`
59
60         // Cryptography related
61         Key          *[SSize]byte `json:"-"`
62         NonceOur     uint64       `json:"-"`
63         NonceRecv    uint64       `json:"-"`
64         NonceCipher  *xtea.Cipher `json:"-"`
65         nonceExpect  uint64       `json:"-"`
66         nonceBucket0 map[uint64]struct{}
67         nonceBucket1 map[uint64]struct{}
68         nonceFound   bool
69         nonceBucketN int32
70
71         // Timers
72         Timeout       time.Duration `json:"-"`
73         Established   time.Time
74         LastPing      time.Time
75         LastSent      time.Time
76         willSentCycle time.Time
77
78         // This variables are initialized only once to relief GC
79         buf       []byte
80         tag       *[poly1305.TagSize]byte
81         keyAuth   *[32]byte
82         nonceRecv uint64
83         frame     []byte
84         nonce     []byte
85         pktSize   uint16
86         size      int
87         now       time.Time
88
89         // Statistics
90         BytesIn         int64
91         BytesOut        int64
92         BytesPayloadIn  int64
93         BytesPayloadOut int64
94         FramesIn        int
95         FramesOut       int
96         FramesUnauth    int
97         FramesDup       int
98         HeartbeatRecv   int
99         HeartbeatSent   int
100 }
101
102 func (p *Peer) String() string {
103         return p.Id.String() + ":" + p.Addr
104 }
105
106 // Zero peer's memory state.
107 func (p *Peer) Zero() {
108         sliceZero(p.Key[:])
109         sliceZero(p.tag[:])
110         sliceZero(p.keyAuth[:])
111         sliceZero(p.buf)
112         sliceZero(p.frame)
113         sliceZero(p.nonce)
114 }
115
116 var (
117         taps = make(map[string]*TAP)
118 )
119
120 // Create TAP listening goroutine.
121 // This function takes required TAP interface name, opens it and allocates
122 // a buffer where all frame data will be written, channel where information
123 // about number of read bytes is sent to, synchronization channel (external
124 // processes tell that read buffer can be used again) and possible channel
125 // opening error.
126 func TAPListen(ifaceName string, timeout time.Duration, cpr int) (*TAP, chan []byte, chan struct{}, chan struct{}, error) {
127         var tap *TAP
128         var err error
129         tap, exists := taps[ifaceName]
130         if !exists {
131                 tap, err = NewTAP(ifaceName)
132                 if err != nil {
133                         return nil, nil, nil, nil, err
134                 }
135                 taps[ifaceName] = tap
136         }
137         sink := make(chan []byte)
138         sinkReady := make(chan struct{})
139         sinkTerminate := make(chan struct{})
140         sinkSkip := make(chan struct{})
141
142         go func() {
143                 cprCycle := cprCycleCalculate(cpr)
144                 if cprCycle != time.Duration(0) {
145                         timeout = cprCycle
146                 } else {
147                         timeout = timeout / TimeoutHeartbeat
148                 }
149                 heartbeat := time.Tick(timeout)
150                 var pkt []byte
151         ListenCycle:
152                 for {
153                         select {
154                         case <-sinkTerminate:
155                                 break ListenCycle
156                         case <-heartbeat:
157                                 go func() { sink <- make([]byte, 0) }()
158                                 continue
159                         case <-sinkSkip:
160                         case <-sinkReady:
161                                 tap.ready <- struct{}{}
162                                 tap.synced = true
163                         }
164                 HeartbeatCatched:
165                         select {
166                         case <-heartbeat:
167                                 go func() { sink <- make([]byte, 0) }()
168                                 goto HeartbeatCatched
169                         case <-sinkTerminate:
170                                 break ListenCycle
171                         case pkt = <-tap.sink:
172                                 tap.synced = false
173                                 sink <- pkt
174                         }
175                 }
176                 close(sink)
177                 close(sinkReady)
178                 close(sinkTerminate)
179         }()
180         if exists && tap.synced {
181                 sinkSkip <- struct{}{}
182         } else {
183                 sinkReady <- struct{}{}
184         }
185         return tap, sink, sinkReady, sinkTerminate, nil
186 }
187
188 func newNonceCipher(key *[32]byte) *xtea.Cipher {
189         nonceKey := make([]byte, 16)
190         salsa20.XORKeyStream(
191                 nonceKey,
192                 make([]byte, 32),
193                 make([]byte, xtea.BlockSize),
194                 key,
195         )
196         ciph, err := xtea.NewCipher(nonceKey)
197         if err != nil {
198                 panic(err)
199         }
200         return ciph
201 }
202
203 func cprCycleCalculate(rate int) time.Duration {
204         if rate == 0 {
205                 return time.Duration(0)
206         }
207         return time.Second / time.Duration(rate*(1<<10)/MTU)
208 }
209
210 func newPeer(isClient bool, addr string, conn RemoteConn, conf *PeerConf, key *[SSize]byte) *Peer {
211         now := time.Now()
212         timeout := conf.Timeout
213         cprCycle := cprCycleCalculate(conf.CPR)
214         noiseEnable := conf.NoiseEnable
215         if conf.CPR > 0 {
216                 noiseEnable = true
217                 timeout = cprCycle
218         } else {
219                 timeout = timeout / TimeoutHeartbeat
220         }
221         peer := Peer{
222                 Addr:        addr,
223                 Conn:        conn,
224                 Timeout:     timeout,
225                 Established: now,
226                 LastPing:    now,
227                 Id:          conf.Id,
228                 NoiseEnable: noiseEnable,
229                 CPR:         conf.CPR,
230                 CPRCycle:    cprCycle,
231                 NonceRecv:   0,
232                 Key:         key,
233                 NonceCipher: newNonceCipher(key),
234                 buf:         make([]byte, MTU+S20BS+NonceSize+poly1305.TagSize),
235                 tag:         new([poly1305.TagSize]byte),
236                 keyAuth:     new([SSize]byte),
237                 nonce:       make([]byte, NonceSize),
238         }
239         if isClient {
240                 peer.NonceOur = 1
241                 peer.nonceExpect = 0 + 2
242         } else {
243                 peer.NonceOur = 0
244                 peer.nonceExpect = 1 + 2
245         }
246         if conn.Reorderable() {
247                 peer.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
248                 peer.nonceBucket1 = make(map[uint64]struct{}, NonceBucketSize)
249         }
250         return &peer
251 }
252
253 // Process incoming UDP packet.
254 // ConnListen'es synchronization channel used to tell him that he is
255 // free to receive new packets. Authenticated and decrypted packets
256 // will be written to the interface immediately (except heartbeat ones).
257 func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool {
258         p.size = len(data)
259         p.frame = make([]byte, p.size)
260         copy(p.frame, data)
261         ready <- struct{}{}
262
263         copy(p.buf[S20BS:], p.frame[:p.size-NonceSize-poly1305.TagSize])
264         for i := 0; i < S20BS; i++ {
265                 p.buf[i] = byte(0)
266         }
267         salsa20.XORKeyStream(
268                 p.buf[:S20BS+p.size-NonceSize-poly1305.TagSize],
269                 p.buf[:S20BS+p.size-NonceSize-poly1305.TagSize],
270                 p.frame[p.size-NonceSize-poly1305.TagSize:p.size-poly1305.TagSize],
271                 p.Key,
272         )
273         copy(p.tag[:], p.frame[p.size-poly1305.TagSize:])
274         copy(p.keyAuth[:], p.buf[:SSize])
275         if !poly1305.Verify(p.tag, p.frame[:p.size-poly1305.TagSize], p.keyAuth) {
276                 p.FramesUnauth++
277                 return false
278         }
279
280         // Check if received nonce is known to us in either of two buckets.
281         // If yes, then this is ignored duplicate.
282         // Check from the oldest bucket, as in most cases this will result
283         // in constant time check.
284         // If Bucket0 is filled, then it becomes Bucket1.
285         p.NonceCipher.Decrypt(
286                 p.nonce,
287                 p.frame[p.size-NonceSize-poly1305.TagSize:p.size-poly1305.TagSize],
288         )
289
290         p.nonceRecv = binary.BigEndian.Uint64(p.nonce)
291         if p.Conn.Reorderable() {
292                 if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound {
293                         p.FramesDup++
294                         return false
295                 }
296                 if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound {
297                         p.FramesDup++
298                         return false
299                 }
300                 p.nonceBucket0[p.NonceRecv] = struct{}{}
301                 p.nonceBucketN++
302                 if p.nonceBucketN == NonceBucketSize {
303                         p.nonceBucket1 = p.nonceBucket0
304                         p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize)
305                         p.nonceBucketN = 0
306                 }
307         } else {
308                 if p.nonceRecv != p.nonceExpect {
309                         p.FramesDup++
310                         return false
311                 }
312                 p.nonceExpect += 2
313         }
314
315         p.FramesIn++
316         p.BytesIn += int64(p.size)
317         p.LastPing = time.Now()
318         p.NonceRecv = p.nonceRecv
319         p.pktSize = binary.BigEndian.Uint16(p.buf[S20BS : S20BS+PktSizeSize])
320         if p.pktSize == 0 {
321                 p.HeartbeatRecv++
322                 return true
323         }
324         p.BytesPayloadIn += int64(p.pktSize)
325         tap.Write(p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize])
326         return true
327 }
328
329 // Process incoming Ethernet packet.
330 // ready channel is TAPListen's synchronization channel used to tell him
331 // that he is free to receive new packets. Encrypted and authenticated
332 // packets will be sent to remote Peer side immediately.
333 func (p *Peer) EthProcess(data []byte, ready chan struct{}) {
334         p.now = time.Now()
335         p.size = len(data)
336         // If this heartbeat is necessary
337         if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) {
338                 return
339         }
340         if p.size > 0 {
341                 copy(p.buf[S20BS+PktSizeSize:], data)
342                 ready <- struct{}{}
343                 binary.BigEndian.PutUint16(p.buf[S20BS:S20BS+PktSizeSize], uint16(p.size))
344                 p.BytesPayloadOut += int64(p.size)
345         } else {
346                 p.buf[S20BS+0] = byte(0)
347                 p.buf[S20BS+1] = byte(0)
348                 p.HeartbeatSent++
349         }
350
351         p.NonceOur += 2
352         binary.BigEndian.PutUint64(p.nonce, p.NonceOur)
353         p.NonceCipher.Encrypt(p.nonce, p.nonce)
354
355         for i := 0; i < S20BS; i++ {
356                 p.buf[i] = byte(0)
357         }
358         salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key)
359         if p.NoiseEnable {
360                 p.frame = append(p.buf[S20BS:S20BS+MTU-NonceSize-poly1305.TagSize], p.nonce...)
361         } else {
362                 p.frame = append(p.buf[S20BS:S20BS+PktSizeSize+p.size], p.nonce...)
363         }
364         copy(p.keyAuth[:], p.buf[:SSize])
365         poly1305.Sum(p.tag, p.frame, p.keyAuth)
366
367         p.BytesOut += int64(len(p.frame) + poly1305.TagSize)
368         p.FramesOut++
369
370         if p.CPRCycle != time.Duration(0) {
371                 p.willSentCycle = p.LastSent.Add(p.CPRCycle)
372                 if p.willSentCycle.After(p.now) {
373                         time.Sleep(p.willSentCycle.Sub(p.now))
374                         p.now = p.willSentCycle
375                 }
376         }
377         p.LastSent = p.now
378         p.Conn.Write(append(p.frame, p.tag[:]...))
379 }