]> Cypherpunks.ru repositories - govpn.git/blob - govpn.go
Fewer copying → higher performance
[govpn.git] / govpn.go
1 /*
2 govpn -- high-performance secure virtual private network daemon
3 Copyright (C) 2014 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 package main
19
20 import (
21         "bytes"
22         "encoding/binary"
23         "encoding/hex"
24         "flag"
25         "fmt"
26         "io"
27         "io/ioutil"
28         "log"
29         "net"
30         "os"
31         "os/exec"
32         "os/signal"
33         "time"
34
35         "code.google.com/p/go.crypto/poly1305"
36         "code.google.com/p/go.crypto/salsa20"
37 )
38
39 var (
40         remoteAddr = flag.String("remote", "", "Remote server address")
41         bindAddr   = flag.String("bind", "", "Bind to address")
42         ifaceName  = flag.String("iface", "tap0", "TAP network interface")
43         keyPath    = flag.String("key", "", "Path to authentication key file")
44         upPath     = flag.String("up", "", "Path to up-script")
45         downPath   = flag.String("down", "", "Path to down-script")
46         mtu        = flag.Int("mtu", 1500, "MTU")
47         timeoutP   = flag.Int("timeout", 60, "Timeout seconds")
48         verboseP   = flag.Bool("v", false, "Increase verbosity")
49 )
50
51 const (
52         NonceSize = 8
53         KeySize   = 32
54         // S20BS is Salsa20's internal blocksize in bytes
55         S20BS         = 64
56         HeartBeatSize = 12
57         HeartBeatMark = "\x00\x00\x00HEARTBEAT"
58         // Maximal amount of bytes transfered with single key (4 GiB)
59         MaxBytesPerKey = 4294967296
60 )
61
62 type TAP interface {
63         io.Reader
64         io.Writer
65 }
66
67 type Peer struct {
68         addr      *net.UDPAddr
69         key       *[KeySize]byte // encryption key
70         nonceOur  uint64         // nonce for our messages
71         nonceRecv uint64         // latest received nonce from remote peer
72 }
73
74 type UDPPkt struct {
75         addr *net.UDPAddr
76         size int
77 }
78
79 func ScriptCall(path *string) {
80         if *path == "" {
81                 return
82         }
83         cmd := exec.Command(*path, *ifaceName)
84         var out bytes.Buffer
85         cmd.Stdout = &out
86         if err := cmd.Run(); err != nil {
87                 fmt.Println(time.Now(), "script error: ", err.Error(), string(out.Bytes()))
88         }
89 }
90
91 func main() {
92         flag.Parse()
93         timeout := *timeoutP
94         verbose := *verboseP
95         log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
96
97         // Key decoding
98         keyData, err := ioutil.ReadFile(*keyPath)
99         if err != nil {
100                 panic("Unable to read keyfile: " + err.Error())
101         }
102         if len(keyData) < 64 {
103                 panic("Key must be 64 hex characters long")
104         }
105         keyDecoded, err := hex.DecodeString(string(keyData[0:64]))
106         if err != nil {
107                 panic("Unable to decode the key: " + err.Error())
108         }
109         key := new([KeySize]byte)
110         copy(key[:], keyDecoded)
111         keyDecoded = nil
112         keyData = nil
113
114         // Interface listening
115         maxIfacePktSize := *mtu - poly1305.TagSize - NonceSize
116         log.Println("Max MTU", maxIfacePktSize, "on interface", *ifaceName)
117         iface := NewTAP(*ifaceName)
118         ethBuf := make([]byte, maxIfacePktSize)
119         ethSink := make(chan int)
120         ethSinkReady := make(chan bool)
121         go func() {
122                 for {
123                         <-ethSinkReady
124                         n, err := iface.Read(ethBuf)
125                         if err != nil {
126                                 panic(err)
127                         }
128                         ethSink <- n
129                 }
130         }()
131         ethSinkReady <- true
132
133         // Network address parsing
134         if (len(*bindAddr) > 1 && len(*remoteAddr) > 1) ||
135                 (len(*bindAddr) == 0 && len(*remoteAddr) == 0) {
136                 panic("Either -bind or -remote must be specified only")
137         }
138         var conn *net.UDPConn
139         var remote *net.UDPAddr
140         serverMode := false
141         bindTo := "0.0.0.0:0"
142
143         if len(*bindAddr) > 1 {
144                 bindTo = *bindAddr
145                 serverMode = true
146         }
147
148         bind, err := net.ResolveUDPAddr("udp", bindTo)
149         if err != nil {
150                 panic(err)
151         }
152         conn, err = net.ListenUDP("udp", bind)
153         if err != nil {
154                 panic(err)
155         }
156
157         if len(*remoteAddr) > 1 {
158                 remote, err = net.ResolveUDPAddr("udp", *remoteAddr)
159                 if err != nil {
160                         panic(err)
161                 }
162         }
163
164         udpBuf := make([]byte, *mtu)
165         udpSink := make(chan *UDPPkt)
166         udpSinkReady := make(chan bool)
167         go func(conn *net.UDPConn) {
168                 for {
169                         <-udpSinkReady
170                         conn.SetReadDeadline(time.Now().Add(time.Second))
171                         n, addr, err := conn.ReadFromUDP(udpBuf)
172                         if err != nil {
173                                 if verbose {
174                                         fmt.Print("B")
175                                 }
176                                 udpSink <- nil
177                         } else {
178                                 udpSink <- &UDPPkt{addr, n}
179                         }
180                 }
181         }(conn)
182         udpSinkReady <- true
183
184         // Process packets
185         var udpPkt *UDPPkt
186         var udpPktData []byte
187         var ethPktSize int
188         var frame []byte
189         var addr string
190         var peer *Peer
191         var p *Peer
192
193         timeouts := 0
194         bytes := 0
195         states := make(map[string]*Handshake)
196         nonce := make([]byte, NonceSize)
197         keyAuth := new([KeySize]byte)
198         tag := new([poly1305.TagSize]byte)
199         buf := make([]byte, *mtu+S20BS)
200         emptyKey := make([]byte, KeySize)
201
202         if !serverMode {
203                 states[remote.String()] = HandshakeStart(conn, remote, key)
204         }
205
206         heartbeat := time.Tick(time.Second * time.Duration(timeout/3))
207         go func() { <-heartbeat }()
208         heartbeatMark := []byte(HeartBeatMark)
209
210         termSignal := make(chan os.Signal, 1)
211         signal.Notify(termSignal, os.Interrupt, os.Kill)
212
213         finished := false
214         for {
215                 if finished {
216                         break
217                 }
218                 if !serverMode && bytes > MaxBytesPerKey {
219                         states[remote.String()] = HandshakeStart(conn, remote, key)
220                         bytes = 0
221                 }
222                 select {
223                 case <-termSignal:
224                         finished = true
225                 case <-heartbeat:
226                         go func() { ethSink <- -1 }()
227                 case udpPkt = <-udpSink:
228                         timeouts++
229                         if !serverMode && timeouts >= timeout {
230                                 finished = true
231                         }
232                         if udpPkt == nil {
233                                 udpSinkReady <- true
234                                 continue
235                         }
236                         udpPktData = udpBuf[:udpPkt.size]
237                         if isValidHandshakePkt(udpPktData) {
238                                 addr = udpPkt.addr.String()
239                                 state, exists := states[addr]
240                                 if serverMode {
241                                         if !exists {
242                                                 state = &Handshake{addr: udpPkt.addr}
243                                                 states[addr] = state
244                                         }
245                                         p = state.Server(conn, key, udpPktData)
246                                 } else {
247                                         if !exists {
248                                                 fmt.Print("[HS?]")
249                                                 udpSinkReady <- true
250                                                 continue
251                                         }
252                                         p = state.Client(conn, key, udpPktData)
253                                 }
254                                 if p != nil {
255                                         fmt.Print("[HS-OK]")
256                                         if peer == nil {
257                                                 go ScriptCall(upPath)
258                                         }
259                                         peer = p
260                                         delete(states, addr)
261                                 }
262                                 udpSinkReady <- true
263                                 continue
264                         }
265                         if peer == nil {
266                                 udpSinkReady <- true
267                                 continue
268                         }
269                         nonceRecv, _ := binary.Uvarint(udpPktData[:8])
270                         if peer.nonceRecv >= nonceRecv {
271                                 fmt.Print("R")
272                                 udpSinkReady <- true
273                                 continue
274                         }
275                         copy(buf[:KeySize], emptyKey)
276                         copy(tag[:], udpPktData[udpPkt.size-poly1305.TagSize:])
277                         copy(buf[S20BS:], udpPktData[NonceSize:udpPkt.size-poly1305.TagSize])
278                         salsa20.XORKeyStream(
279                                 buf[:S20BS+udpPkt.size-poly1305.TagSize],
280                                 buf[:S20BS+udpPkt.size-poly1305.TagSize],
281                                 udpPktData[:NonceSize],
282                                 peer.key,
283                         )
284                         copy(keyAuth[:], buf[:KeySize])
285                         if !poly1305.Verify(tag, udpPktData[:udpPkt.size-poly1305.TagSize], keyAuth) {
286                                 udpSinkReady <- true
287                                 fmt.Print("T")
288                                 continue
289                         }
290                         udpSinkReady <- true
291                         peer.nonceRecv = nonceRecv
292                         timeouts = 0
293                         frame = buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]
294                         bytes += len(frame)
295                         if string(frame[0:HeartBeatSize]) == HeartBeatMark {
296                                 continue
297                         }
298                         if _, err := iface.Write(frame); err != nil {
299                                 log.Println("Error writing to iface: ", err)
300                         }
301                         if verbose {
302                                 fmt.Print("r")
303                         }
304                 case ethPktSize = <-ethSink:
305                         if ethPktSize > maxIfacePktSize {
306                                 panic("Too large packet on interface")
307                         }
308                         if peer == nil {
309                                 ethSinkReady <- true
310                                 continue
311                         }
312                         peer.nonceOur = peer.nonceOur + 2
313                         binary.PutUvarint(nonce, peer.nonceOur)
314                         copy(buf[:KeySize], emptyKey)
315                         if ethPktSize > -1 {
316                                 copy(buf[S20BS:], ethBuf[:ethPktSize])
317                                 ethSinkReady <- true
318                         } else {
319                                 copy(buf[S20BS:], heartbeatMark)
320                                 ethPktSize = HeartBeatSize
321                         }
322                         salsa20.XORKeyStream(buf, buf, nonce, peer.key)
323                         copy(buf[S20BS-NonceSize:S20BS], nonce)
324                         copy(keyAuth[:], buf[:KeySize])
325                         dataToSend := buf[S20BS-NonceSize : S20BS+ethPktSize]
326                         poly1305.Sum(tag, dataToSend, keyAuth)
327                         bytes += len(dataToSend)
328                         if _, err := conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil {
329                                 log.Println("Error sending UDP", err)
330                         }
331                         if verbose {
332                                 fmt.Print("w")
333                         }
334                 }
335         }
336         ScriptCall(downPath)
337 }