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