package main
import (
+ "bytes"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
+ "io"
+ "io/ioutil"
"log"
"net"
+ "os"
+ "os/exec"
+ "os/signal"
"time"
"code.google.com/p/go.crypto/poly1305"
"code.google.com/p/go.crypto/salsa20"
- "github.com/chon219/water"
)
var (
remoteAddr = flag.String("remote", "", "Remote server address")
bindAddr = flag.String("bind", "", "Bind to address")
ifaceName = flag.String("iface", "tap0", "TAP network interface")
- keyHex = flag.String("key", "", "Authentication key")
+ 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")
- timeout = flag.Int("timeout", 60, "Timeout seconds")
- verbose = flag.Bool("v", false, "Increase verbosity")
+ timeoutP = flag.Int("timeout", 60, "Timeout seconds")
+ verboseP = flag.Bool("v", false, "Increase verbosity")
)
const (
NonceSize = 8
KeySize = 32
// S20BS is Salsa20's internal blocksize in bytes
- S20BS = 64
+ S20BS = 64
+ HeartBeatSize = 12
+ HeartBeatMark = "\x00\x00\x00HEARTBEAT"
+ // Maximal amount of bytes transfered with single key (4 GiB)
+ MaxBytesPerKey = 4294967296
)
+type TAP interface {
+ io.Reader
+ io.Writer
+}
+
type Peer struct {
addr *net.UDPAddr
key *[KeySize]byte // encryption key
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
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
// Key decoding
- if len(*keyHex) != 64 {
- panic("Key is required argument (64 hex characters)")
+ keyData, err := ioutil.ReadFile(*keyPath)
+ if err != nil {
+ panic("Unable to read keyfile: " + err.Error())
+ }
+ if len(keyData) < 64 {
+ panic("Key must be 64 hex characters long")
}
- keyDecoded, err := hex.DecodeString(*keyHex)
+ keyDecoded, err := hex.DecodeString(string(keyData[0:64]))
if err != nil {
- panic(err)
+ panic("Unable to decode the key: " + err.Error())
}
key := new([KeySize]byte)
copy(key[:], keyDecoded)
+ keyDecoded = nil
+ keyData = nil
// Interface listening
maxIfacePktSize := *mtu - poly1305.TagSize - NonceSize
log.Println("Max MTU", maxIfacePktSize, "on interface", *ifaceName)
- iface, err := water.NewTAP(*ifaceName)
- if err != nil {
- panic(err)
- }
+ iface := NewTAP(*ifaceName)
ethBuf := make([]byte, maxIfacePktSize)
ethSink := make(chan int)
ethSinkReady := make(chan bool)
conn.SetReadDeadline(time.Now().Add(time.Second))
n, addr, err := conn.ReadFromUDP(udpBuf)
if err != nil {
- if *verbose {
+ if verbose {
fmt.Print("B")
}
udpSink <- nil
var udpPkt *UDPPkt
var udpPktData []byte
var ethPktSize int
+ var frame []byte
var addr string
var peer *Peer
var p *Peer
timeouts := 0
+ bytes := 0
states := make(map[string]*Handshake)
nonce := make([]byte, NonceSize)
keyAuth := new([KeySize]byte)
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:
timeouts++
- if !serverMode && timeouts >= *timeout {
+ if !serverMode && timeouts >= timeout {
finished = true
}
if udpPkt == nil {
}
if p != nil {
fmt.Print("[HS-OK]")
+ if peer == nil {
+ go ScriptCall(upPath)
+ }
peer = p
delete(states, addr)
}
}
peer.nonceRecv = nonceRecv
timeouts = 0
- if _, err := iface.Write(buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]); err != nil {
- log.Println("Error writing to iface")
+ frame = buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]
+ bytes += len(frame)
+ if string(frame[0:HeartBeatSize]) == HeartBeatMark {
+ continue
+ }
+ if _, err := iface.Write(frame); err != nil {
+ log.Println("Error writing to iface: ", err)
}
- if *verbose {
+ if verbose {
fmt.Print("r")
}
case ethPktSize = <-ethSink:
ethSinkReady <- true
continue
}
- copy(ethPkt, ethBuf[:ethPktSize])
- ethSinkReady <- true
+ if ethPktSize > -1 {
+ copy(ethPkt, ethBuf[:ethPktSize])
+ ethSinkReady <- true
+ } else {
+ copy(ethPkt, heartbeatMark)
+ ethPktSize = HeartBeatSize
+ }
peer.nonceOur = peer.nonceOur + 2
binary.PutUvarint(nonce, peer.nonceOur)
copy(buf[:KeySize], emptyKey)
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)
}
- if *verbose {
+ if verbose {
fmt.Print("w")
}
}
}
+ ScriptCall(downPath)
}