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