/*
-govpn -- high-performance secure virtual private network daemon
+govpn -- simple secure virtual private network daemon
Copyright (C) 2014 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+// Simple secure virtual private network daemon
package main
import (
+ "bytes"
"encoding/binary"
"encoding/hex"
"flag"
"io/ioutil"
"log"
"net"
+ "os"
+ "os/exec"
+ "os/signal"
"time"
- "code.google.com/p/go.crypto/poly1305"
- "code.google.com/p/go.crypto/salsa20"
+ "golang.org/x/crypto/poly1305"
+ "golang.org/x/crypto/salsa20"
)
var (
bindAddr = flag.String("bind", "", "Bind to address")
ifaceName = flag.String("iface", "tap0", "TAP network interface")
keyPath = flag.String("key", "", "Path to authentication key file")
+ upPath = flag.String("up", "", "Path to up-script")
+ downPath = flag.String("down", "", "Path to down-script")
mtu = flag.Int("mtu", 1500, "MTU")
+ nonceDiff = flag.Int("noncediff", 1, "Allow nonce difference")
timeoutP = flag.Int("timeout", 60, "Timeout seconds")
verboseP = flag.Bool("v", false, "Increase verbosity")
)
S20BS = 64
HeartBeatSize = 12
HeartBeatMark = "\x00\x00\x00HEARTBEAT"
+ // Maximal amount of bytes transfered with single key (4 GiB)
+ MaxBytesPerKey = 4294967296
)
type TAP interface {
size int
}
+func ScriptCall(path *string) {
+ if *path == "" {
+ return
+ }
+ cmd := exec.Command(*path, *ifaceName)
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ if err := cmd.Run(); err != nil {
+ fmt.Println(time.Now(), "script error: ", err.Error(), string(out.Bytes()))
+ }
+}
+
func main() {
flag.Parse()
timeout := *timeoutP
verbose := *verboseP
+ noncediff := uint64(*nonceDiff)
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
// Key decoding
var p *Peer
timeouts := 0
+ bytes := 0
states := make(map[string]*Handshake)
nonce := make([]byte, NonceSize)
keyAuth := new([KeySize]byte)
tag := new([poly1305.TagSize]byte)
buf := make([]byte, *mtu+S20BS)
emptyKey := make([]byte, KeySize)
- ethPkt := make([]byte, maxIfacePktSize)
- udpPktDataBuf := make([]byte, *mtu)
if !serverMode {
states[remote.String()] = HandshakeStart(conn, remote, key)
}
heartbeat := time.Tick(time.Second * time.Duration(timeout/3))
+ go func() { <-heartbeat }()
heartbeatMark := []byte(HeartBeatMark)
+ termSignal := make(chan os.Signal, 1)
+ signal.Notify(termSignal, os.Interrupt, os.Kill)
+
finished := false
for {
if finished {
break
}
+ if !serverMode && bytes > MaxBytesPerKey {
+ states[remote.String()] = HandshakeStart(conn, remote, key)
+ bytes = 0
+ }
select {
+ case <-termSignal:
+ finished = true
case <-heartbeat:
go func() { ethSink <- -1 }()
case udpPkt = <-udpSink:
udpSinkReady <- true
continue
}
- copy(udpPktDataBuf, udpBuf[:udpPkt.size])
- udpSinkReady <- true
- udpPktData = udpPktDataBuf[:udpPkt.size]
+ udpPktData = udpBuf[:udpPkt.size]
if isValidHandshakePkt(udpPktData) {
addr = udpPkt.addr.String()
state, exists := states[addr]
state = &Handshake{addr: udpPkt.addr}
states[addr] = state
}
- p = state.Server(conn, key, udpPktData)
+ p = state.Server(noncediff, conn, key, udpPktData)
} else {
if !exists {
fmt.Print("[HS?]")
+ udpSinkReady <- true
continue
}
- p = state.Client(conn, key, udpPktData)
+ p = state.Client(noncediff, conn, key, udpPktData)
}
if p != nil {
fmt.Print("[HS-OK]")
+ if peer == nil {
+ go ScriptCall(upPath)
+ }
peer = p
delete(states, addr)
}
+ udpSinkReady <- true
continue
}
if peer == nil {
+ udpSinkReady <- true
continue
}
nonceRecv, _ := binary.Uvarint(udpPktData[:8])
- if peer.nonceRecv >= nonceRecv {
+ if nonceRecv < peer.nonceRecv-noncediff {
fmt.Print("R")
+ udpSinkReady <- true
continue
}
copy(buf[:KeySize], emptyKey)
)
copy(keyAuth[:], buf[:KeySize])
if !poly1305.Verify(tag, udpPktData[:udpPkt.size-poly1305.TagSize], keyAuth) {
+ udpSinkReady <- true
fmt.Print("T")
continue
}
+ udpSinkReady <- true
peer.nonceRecv = nonceRecv
timeouts = 0
frame = buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]
+ bytes += len(frame)
if string(frame[0:HeartBeatSize]) == HeartBeatMark {
continue
}
ethSinkReady <- true
continue
}
+ peer.nonceOur = peer.nonceOur + 2
+ binary.PutUvarint(nonce, peer.nonceOur)
+ copy(buf[:KeySize], emptyKey)
if ethPktSize > -1 {
- copy(ethPkt, ethBuf[:ethPktSize])
+ copy(buf[S20BS:], ethBuf[:ethPktSize])
ethSinkReady <- true
} else {
- copy(ethPkt, heartbeatMark)
+ copy(buf[S20BS:], heartbeatMark)
ethPktSize = HeartBeatSize
}
- peer.nonceOur = peer.nonceOur + 2
- binary.PutUvarint(nonce, peer.nonceOur)
- copy(buf[:KeySize], emptyKey)
- copy(buf[S20BS:], ethPkt[:ethPktSize])
salsa20.XORKeyStream(buf, buf, nonce, peer.key)
copy(buf[S20BS-NonceSize:S20BS], nonce)
copy(keyAuth[:], buf[:KeySize])
dataToSend := buf[S20BS-NonceSize : S20BS+ethPktSize]
poly1305.Sum(tag, dataToSend, keyAuth)
+ bytes += len(dataToSend)
if _, err := conn.WriteTo(append(dataToSend, tag[:]...), peer.addr); err != nil {
log.Println("Error sending UDP", err)
}
}
}
}
+ ScriptCall(downPath)
}