]> Cypherpunks.ru repositories - govpn.git/blob - govpn.go
a6db5e34438c27f8d0d13789d7792ec4b6cb42bb
[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         "encoding/binary"
22         "encoding/hex"
23         "flag"
24         "fmt"
25         "log"
26         "net"
27         "time"
28
29         "code.google.com/p/go.crypto/poly1305"
30         "code.google.com/p/go.crypto/salsa20"
31         "github.com/chon219/water"
32 )
33
34 const (
35         NonceSize    = 8
36         AliveTimeout = time.Second * 90
37         KeySize      = 32
38         // S20BS is Salsa20's internal blocksize in bytes
39         S20BS = 64
40 )
41
42 type Peer struct {
43         addr      *net.UDPAddr
44         lastPing  time.Time
45         key       *[KeySize]byte // encryption key
46         nonceOur  uint64    // nonce for our messages
47         nonceRecv uint64    // latest received nonce from remote peer
48 }
49
50 func (p *Peer) IsAlive() bool {
51         if (p == nil) || (p.lastPing.Add(AliveTimeout).Before(time.Now())) {
52                 return false
53         }
54         return true
55 }
56
57 func (p *Peer) SetAlive() {
58         p.lastPing = time.Now()
59 }
60
61 type UDPPkt struct {
62         addr *net.UDPAddr
63         size int
64 }
65
66 var (
67         remoteAddr = flag.String("remote", "", "Remote server address")
68         bindAddr   = flag.String("bind", "", "Bind to address")
69         ifaceName  = flag.String("iface", "tap0", "TAP network interface")
70         keyHex     = flag.String("key", "", "Authentication key")
71         mtu        = flag.Int("mtu", 1500, "MTU")
72         verbose    = flag.Bool("v", false, "Increase verbosity")
73 )
74
75 func main() {
76         flag.Parse()
77         log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
78
79         // Key decoding
80         if len(*keyHex) != 64 {
81                 panic("Key is required argument (64 hex characters)")
82         }
83         keyDecoded, err := hex.DecodeString(*keyHex)
84         if err != nil {
85                 panic(err)
86         }
87         key := new([KeySize]byte)
88         copy(key[:], keyDecoded)
89
90         // Interface listening
91         maxIfacePktSize := *mtu - poly1305.TagSize - NonceSize
92         log.Println("Max MTU", maxIfacePktSize, "on interface", *ifaceName)
93         iface, err := water.NewTAP(*ifaceName)
94         if err != nil {
95                 panic(err)
96         }
97         ethBuf := make([]byte, maxIfacePktSize)
98         ethSink := make(chan int)
99         ethSinkReady := make(chan bool)
100         go func() {
101                 for {
102                         <- ethSinkReady
103                         n, err := iface.Read(ethBuf)
104                         if err != nil {
105                                 panic(err)
106                         }
107                         ethSink <- n
108                 }
109         }()
110         ethSinkReady <- true
111
112         // Network address parsing
113         if (len(*bindAddr) > 1 && len(*remoteAddr) > 1) ||
114                 (len(*bindAddr) == 0 && len(*remoteAddr) == 0) {
115                 panic("Either -bind or -remote must be specified only")
116         }
117         var conn *net.UDPConn
118         var remote *net.UDPAddr
119         serverMode := false
120         bindTo := "0.0.0.0:0"
121
122         if len(*bindAddr) > 1 {
123                 bindTo = *bindAddr
124                 serverMode = true
125         }
126
127         bind, err := net.ResolveUDPAddr("udp", bindTo)
128         if err != nil {
129                 panic(err)
130         }
131         conn, err = net.ListenUDP("udp", bind)
132         if err != nil {
133                 panic(err)
134         }
135
136         if len(*remoteAddr) > 1 {
137                 remote, err = net.ResolveUDPAddr("udp", *remoteAddr)
138                 if err != nil {
139                         panic(err)
140                 }
141         }
142
143         udpBuf := make([]byte, *mtu)
144         udpSink := make(chan UDPPkt)
145         udpSinkReady := make(chan bool)
146         go func(conn *net.UDPConn) {
147                 for {
148                         <- udpSinkReady
149                         n, addr, err := conn.ReadFromUDP(udpBuf)
150                         if err != nil {
151                                 fmt.Print("B")
152                         }
153                         udpSink <- UDPPkt{addr, n}
154                 }
155         }(conn)
156         udpSinkReady <- true
157
158         // Process packets
159         var udpPkt UDPPkt
160         var udpPktData []byte
161         var ethPktSize int
162         var addr string
163         var peer Peer
164         var p *Peer
165
166         states := make(map[string]*Handshake)
167         nonce := make([]byte, NonceSize)
168         keyAuth := new([KeySize]byte)
169         tag := new([poly1305.TagSize]byte)
170         buf := make([]byte, *mtu+S20BS)
171         emptyKey := make([]byte, KeySize)
172         ethPkt := make([]byte, maxIfacePktSize)
173         udpPktDataBuf := make([]byte, *mtu)
174
175         if !serverMode {
176                 log.Println("starting handshake with", *remoteAddr)
177                 states[remote.String()] = HandshakeStart(conn, remote, key)
178         }
179
180         for {
181                 select {
182                 case udpPkt = <-udpSink:
183                         copy(udpPktDataBuf, udpBuf[:udpPkt.size])
184                         udpSinkReady <- true
185                         udpPktData = udpPktDataBuf[:udpPkt.size]
186                         if isValidHandshakePkt(udpPktData) {
187                                 addr = udpPkt.addr.String()
188                                 state, exists := states[addr]
189                                 if serverMode {
190                                         if !exists {
191                                                 state = &Handshake{addr: udpPkt.addr}
192                                                 states[addr] = state
193                                         }
194                                         p = state.Server(conn, key, udpPktData)
195                                 } else {
196                                         if !exists {
197                                                 fmt.Print("[HS?]")
198                                                 continue
199                                         }
200                                         p = state.Client(conn, key, udpPktData)
201                                 }
202                                 if p != nil {
203                                         fmt.Print("[HS-OK]")
204                                         peer = *p
205                                         delete(states, addr)
206                                 }
207                                 continue
208                         }
209                         if !peer.IsAlive() {
210                                 continue
211                         }
212                         nonceRecv, _ := binary.Uvarint(udpPktData[:8])
213                         if peer.nonceRecv >= nonceRecv {
214                                 fmt.Print("R")
215                                 continue
216                         }
217                         copy(buf[:KeySize], emptyKey)
218                         copy(tag[:], udpPktData[udpPkt.size-poly1305.TagSize:])
219                         copy(buf[S20BS:], udpPktData[NonceSize:udpPkt.size-poly1305.TagSize])
220                         salsa20.XORKeyStream(
221                                 buf[:S20BS+udpPkt.size-poly1305.TagSize],
222                                 buf[:S20BS+udpPkt.size-poly1305.TagSize],
223                                 udpPktData[:NonceSize],
224                                 peer.key,
225                         )
226                         copy(keyAuth[:], buf[:KeySize])
227                         if !poly1305.Verify(tag, udpPktData[:udpPkt.size-poly1305.TagSize], keyAuth) {
228                                 fmt.Print("T")
229                                 continue
230                         }
231                         peer.nonceRecv = nonceRecv
232                         peer.SetAlive()
233                         if _, err := iface.Write(buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]); err != nil {
234                                 log.Println("Error writing to iface")
235                         }
236                         if *verbose {
237                                 fmt.Print("r")
238                         }
239                 case ethPktSize = <-ethSink:
240                         if ethPktSize > maxIfacePktSize {
241                                 panic("Too large packet on interface")
242                         }
243                         if !peer.IsAlive() {
244                                 ethSinkReady <- true
245                                 continue
246                         }
247                         copy(ethPkt, ethBuf[:ethPktSize])
248                         ethSinkReady <- true
249                         peer.nonceOur = peer.nonceOur + 2
250                         binary.PutUvarint(nonce, peer.nonceOur)
251                         copy(buf[:KeySize], emptyKey)
252                         copy(buf[S20BS:], ethPkt[:ethPktSize])
253                         salsa20.XORKeyStream(buf, buf, nonce, peer.key)
254                         copy(buf[S20BS-NonceSize:S20BS], nonce)
255                         copy(keyAuth[:], buf[:KeySize])
256                         dataToSend := buf[S20BS-NonceSize : S20BS+ethPktSize]
257                         poly1305.Sum(tag, dataToSend, keyAuth)
258                         if _, err := conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil {
259                                 log.Println("Error sending UDP", err)
260                         }
261                         if *verbose {
262                                 fmt.Print("w")
263                         }
264                 }
265         }
266 }