From 1fc99e2439c3c0ad25721f80addfde5eb04ccf7e Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Thu, 21 Aug 2014 16:41:09 +0400 Subject: [PATCH] Exit when daemon is becoming dead * Exit if handshake is timeouted * Exit if daemon becomes unavailable * Simplify overall code Signed-off-by: Sergey Matveev --- README | 9 ++++-- govpn.go | 81 ++++++++++++++++++++++++++++------------------------ handshake.go | 2 -- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/README b/README index e321da7..34d8be8 100644 --- a/README +++ b/README @@ -42,7 +42,7 @@ COMPARISON TO OpenVPN 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 @@ Ethernet frame header length, that in my case is 14 bytes long (1476 - 14). 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 a6db5e3..fcd1a00 100644 --- a/govpn.go +++ b/govpn.go @@ -31,31 +31,28 @@ import ( "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 { @@ -63,15 +60,6 @@ type UDPPkt struct { 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() log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile) @@ -99,7 +87,7 @@ func main() { ethSinkReady := make(chan bool) go func() { for { - <- ethSinkReady + <-ethSinkReady n, err := iface.Read(ethBuf) if err != nil { panic(err) @@ -141,28 +129,34 @@ func main() { } 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 @@ func main() { 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 @@ func main() { } 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 @@ func main() { 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 @@ func main() { 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 a3403ea..072a7bd 100644 --- a/handshake.go +++ b/handshake.go @@ -184,7 +184,6 @@ func (h *Handshake) Server(conn *net.UDPConn, key *[32]byte, data []byte) *Peer // 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 @@ func (h *Handshake) Client(conn *net.UDPConn, key *[32]byte, data []byte) *Peer // 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 -- 2.44.0