README | 9 +++++++-- govpn.go | 81 ++++++++++++++++++++++++++++------------------------- handshake.go | 2 -- diff --git a/README b/README index e321da780221718f0c30886582802fceb1ecc19c..34d8be8314e4247805b5579f49cf66290e2827b1 100644 --- a/README +++ b/README @@ -42,7 +42,7 @@ * Fast handshake CONSOLE OUTPUT LEGEND -B -- bad UDP packet (some system error) +B -- bad or timeouted UDP packet (maybe network is inactive) T -- bad tag on packet (MiTM, unordered packet) R -- invalid sequence number (MiTM, unordered packet) [HS?] -- unknown handshake message @@ -77,7 +77,12 @@ pc% ip link set mtu 1462 dev tap10 pc% ip addr add 172.16.0.2/24 dev tap10 pc% ip link set up dev tap10 pc% ip route add default via 172.16.0.1 - pc% govpn -key KEY -iface tap10 -remote 192.168.0.1:1194 + pc% while :; do govpn -key KEY -iface tap10 -remote 192.168.0.1:1194; done + +If client won't finish handshake during -timeout, then it will exit. +If no packets are received from remote side during timeout, then daemon +will stop sending packets to the client and client will exit. In every +cases you have to rehandshake again. TECHNICAL INTERNALS diff --git a/govpn.go b/govpn.go index a6db5e34438c27f8d0d13789d7792ec4b6cb42bb..fcd1a00b898591fe2ccc034685ce2d82ca164a02 100644 --- a/govpn.go +++ b/govpn.go @@ -31,46 +31,34 @@ "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") + mtu = flag.Int("mtu", 1500, "MTU") + timeout = flag.Int("timeout", 60, "Timeout seconds") + verbose = flag.Bool("v", false, "Increase verbosity") +) + const ( - NonceSize = 8 - AliveTimeout = time.Second * 90 - KeySize = 32 + NonceSize = 8 + KeySize = 32 // S20BS is Salsa20's internal blocksize in bytes S20BS = 64 ) type Peer struct { addr *net.UDPAddr - lastPing time.Time key *[KeySize]byte // encryption key - nonceOur uint64 // nonce for our messages - nonceRecv uint64 // latest received nonce from remote peer -} - -func (p *Peer) IsAlive() bool { - if (p == nil) || (p.lastPing.Add(AliveTimeout).Before(time.Now())) { - return false - } - return true -} - -func (p *Peer) SetAlive() { - p.lastPing = time.Now() + nonceOur uint64 // nonce for our messages + nonceRecv uint64 // latest received nonce from remote peer } type UDPPkt struct { addr *net.UDPAddr size int } - -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") - mtu = flag.Int("mtu", 1500, "MTU") - verbose = flag.Bool("v", false, "Increase verbosity") -) func main() { flag.Parse() @@ -99,7 +87,7 @@ ethSink := make(chan int) ethSinkReady := make(chan bool) go func() { for { - <- ethSinkReady + <-ethSinkReady n, err := iface.Read(ethBuf) if err != nil { panic(err) @@ -141,28 +129,34 @@ } } udpBuf := make([]byte, *mtu) - udpSink := make(chan UDPPkt) + udpSink := make(chan *UDPPkt) udpSinkReady := make(chan bool) go func(conn *net.UDPConn) { for { - <- udpSinkReady + <-udpSinkReady + conn.SetReadDeadline(time.Now().Add(time.Second)) n, addr, err := conn.ReadFromUDP(udpBuf) if err != nil { - fmt.Print("B") + if *verbose { + fmt.Print("B") + } + udpSink <- nil + } else { + udpSink <- &UDPPkt{addr, n} } - udpSink <- UDPPkt{addr, n} } }(conn) udpSinkReady <- true // Process packets - var udpPkt UDPPkt + var udpPkt *UDPPkt var udpPktData []byte var ethPktSize int var addr string - var peer Peer + var peer *Peer var p *Peer + timeouts := 0 states := make(map[string]*Handshake) nonce := make([]byte, NonceSize) keyAuth := new([KeySize]byte) @@ -173,13 +167,24 @@ ethPkt := make([]byte, maxIfacePktSize) udpPktDataBuf := make([]byte, *mtu) if !serverMode { - log.Println("starting handshake with", *remoteAddr) states[remote.String()] = HandshakeStart(conn, remote, key) } + finished := false for { + if finished { + break + } select { case udpPkt = <-udpSink: + timeouts++ + if !serverMode && timeouts >= *timeout { + finished = true + } + if udpPkt == nil { + udpSinkReady <- true + continue + } copy(udpPktDataBuf, udpBuf[:udpPkt.size]) udpSinkReady <- true udpPktData = udpPktDataBuf[:udpPkt.size] @@ -201,12 +206,12 @@ p = state.Client(conn, key, udpPktData) } if p != nil { fmt.Print("[HS-OK]") - peer = *p + peer = p delete(states, addr) } continue } - if !peer.IsAlive() { + if peer == nil { continue } nonceRecv, _ := binary.Uvarint(udpPktData[:8]) @@ -229,7 +234,7 @@ fmt.Print("T") continue } peer.nonceRecv = nonceRecv - peer.SetAlive() + timeouts = 0 if _, err := iface.Write(buf[S20BS : S20BS+udpPkt.size-NonceSize-poly1305.TagSize]); err != nil { log.Println("Error writing to iface") } @@ -240,7 +245,7 @@ case ethPktSize = <-ethSink: if ethPktSize > maxIfacePktSize { panic("Too large packet on interface") } - if !peer.IsAlive() { + if peer == nil { ethSinkReady <- true continue } diff --git a/handshake.go b/handshake.go index a3403ea2d978755762e9a04c5aa4d5089353f1ee..072a7bd5be7424c1ae2bbe6551278f434f2f27e2 100644 --- a/handshake.go +++ b/handshake.go @@ -184,7 +184,6 @@ } // Switch peer peer := Peer{addr: h.addr, nonceOur: 0, nonceRecv: 0} - peer.SetAlive() peer.key = KeyFromSecrets(h.sServer[:], decRs[8+8:]) fmt.Print("[OK]") return &peer @@ -252,7 +251,6 @@ } // Switch peer peer := Peer{addr: h.addr, nonceOur: 1, nonceRecv: 0} - peer.SetAlive() peer.key = KeyFromSecrets(h.sServer[:], h.sClient[:]) fmt.Print("[OK]") return &peer