--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "bytes"
+
+ "github.com/pkg/errors"
+
+ "cypherpunks.ru/govpn"
+)
+
+func preUpAction(path string) govpn.TunnelPreUpAction {
+ if len(path) == 0 {
+ return nil
+ }
+
+ return func(ctx govpn.PeerContext) (*govpn.TAP, error) {
+ result, err := govpn.ScriptCall(path, ctx.Config.Iface, ctx.RemoteAddress)
+ if err != nil {
+ return nil, errors.Wrap(err, "govpn.ScriptCall")
+ }
+ if len(ctx.Config.Iface) == 0 {
+ sepIndex := bytes.Index(result, []byte{'\n'})
+ if sepIndex < 0 {
+ sepIndex = len(result)
+ }
+ ctx.Config.Iface = string(result[:sepIndex])
+ }
+
+ if len(ctx.Config.Iface) == 0 {
+ return nil, errors.Errorf("Script %q didn't returned an interface name", path)
+ }
+
+ tap, err := govpn.TAPListen(ctx.Config.Iface, ctx.Config.MTU)
+ if err != nil {
+ return nil, errors.Wrap(err, "govpn.TAPListen")
+ }
+ return tap, nil
+ }
+}
+++ /dev/null
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "bytes"
- "sync"
-
- "cypherpunks.ru/govpn"
-)
-
-// PeerState holds server side state of a single connecting/connected peer
-type PeerState struct {
- peer *govpn.Peer
- terminator chan struct{}
- tap *govpn.TAP
-}
-
-var (
- handshakes map[string]*govpn.Handshake = make(map[string]*govpn.Handshake)
- hsLock sync.RWMutex
-
- peers = make(map[string]*PeerState)
- peersLock sync.RWMutex
-
- peersByID = make(map[govpn.PeerID]string)
- peersByIDLock sync.RWMutex
-
- knownPeers govpn.KnownPeers
- kpLock sync.RWMutex
-)
-
-func callUp(peerID *govpn.PeerID, remoteAddr string) (string, error) {
- ifaceName := confs[*peerID].Iface
- if confs[*peerID].Up != "" {
- result, err := govpn.ScriptCall(confs[*peerID].Up, ifaceName, remoteAddr)
- if err != nil {
- govpn.Printf(
- `[script-failed bind="%s" path="%s" err="%s"]`,
- *bindAddr,
- confs[*peerID].Up,
- err,
- )
- return "", err
- }
- if ifaceName == "" {
- sepIndex := bytes.Index(result, []byte{'\n'})
- if sepIndex < 0 {
- sepIndex = len(result)
- }
- ifaceName = string(result[:sepIndex])
- }
- }
- if ifaceName == "" {
- govpn.Printf(`[tap-failed bind="%s" peer="%s"]`, *bindAddr, *peerID)
- }
- return ifaceName, nil
-}
package main
import (
- "errors"
"io/ioutil"
- "log"
"time"
+ "github.com/Sirupsen/logrus"
+ "github.com/pkg/errors"
"gopkg.in/yaml.v2"
"cypherpunks.ru/govpn"
)
-const (
- RefreshRate = time.Minute
-)
+const refreshRate = time.Minute
var (
- confs map[govpn.PeerID]*govpn.PeerConf
+ confs peerConfigurations
idsCache *govpn.MACCache
+ logger *logrus.Logger
)
+type peerConfigurations map[govpn.PeerID]*govpn.PeerConf
+
+func (peerConfs peerConfigurations) Get(peerID govpn.PeerID) *govpn.PeerConf {
+ pc, exists := peerConfs[peerID]
+ if !exists {
+ return nil
+ }
+ return pc
+}
+
+type peerConf struct {
+ Name string `yaml:"name"`
+ Iface string `yaml:"iface"`
+ MTU int `yaml:"mtu"`
+ Up string `yaml:"up"`
+ Down string `yaml:"down"`
+ TimeoutInt int `yaml:"timeout"`
+ Noise bool `yaml:"noise"`
+ CPR int `yaml:"cpr"`
+ Encless bool `yaml:"encless"`
+ TimeSync int `yaml:"timesync"`
+ VerifierRaw string `yaml:"verifier"`
+}
+
func confRead() (*map[govpn.PeerID]*govpn.PeerConf, error) {
data, err := ioutil.ReadFile(*confPath)
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, "ioutil.ReadFile")
}
- confsRaw := new(map[string]govpn.PeerConf)
+ confsRaw := new(map[string]peerConf)
err = yaml.Unmarshal(data, confsRaw)
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, "yaml.Unmarshal")
}
confs := make(map[govpn.PeerID]*govpn.PeerConf, len(*confsRaw))
for name, pc := range *confsRaw {
verifier, err := govpn.VerifierFromString(pc.VerifierRaw)
if err != nil {
- return nil, errors.New("Unable to decode verifier: " + err.Error())
+ return nil, errors.Wrap(err, "govpn.VerifierFromString")
}
if pc.Encless {
pc.Noise = true
pc.MTU = govpn.MTUDefault
}
if pc.MTU > govpn.MTUMax {
- govpn.Printf(`[mtu-high bind="%s" value="%d" overriden="%d"]`, *bindAddr, pc.MTU, govpn.MTUMax)
+ logger.WithFields(logrus.Fields{
+ "bind": *bindAddr,
+ "previous_mtu": pc.MTU,
+ "new_mtu": govpn.MTUMax,
+ }).Warning("Overriden MTU")
pc.MTU = govpn.MTUMax
}
conf := govpn.PeerConf{
Name: name,
Iface: pc.Iface,
MTU: pc.MTU,
- Up: pc.Up,
- Down: pc.Down,
+ PreUp: preUpAction(pc.Up),
+ Down: govpn.RunScriptAction(&pc.Down),
Noise: pc.Noise,
CPR: pc.CPR,
Encless: pc.Encless,
TimeSync: pc.TimeSync,
}
if pc.TimeoutInt <= 0 {
- pc.TimeoutInt = govpn.TimeoutDefault
+ conf.Timeout = govpn.TimeoutDefault
+ } else {
+ conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
}
- conf.Timeout = time.Second * time.Duration(pc.TimeoutInt)
confs[*verifier.ID] = &conf
}
return &confs, nil
}
func confRefresh() error {
+ fields := logrus.Fields{
+ "func": "confRefresh",
+ }
+ logger.WithFields(fields).Debug("Check configuration file")
newConfs, err := confRead()
if err != nil {
- govpn.Printf(`[conf-parse-failed bind="%s" err="%s"]`, *bindAddr, err)
- return err
+ return errors.Wrap(err, "confRead")
}
confs = *newConfs
- idsCache.Update(newConfs)
+ logger.WithFields(fields).WithField("newConfs", len(confs)).Debug("idsCache.Update")
+ if err = idsCache.Update(newConfs); err != nil {
+ return errors.Wrap(err, "idsCache.Update")
+ }
+ logger.WithFields(fields).Debug("Done")
return nil
}
func confInit() {
idsCache = govpn.NewMACCache()
- if err := confRefresh(); err != nil {
- log.Fatalln(err)
+ err := confRefresh()
+ fields := logrus.Fields{"func": "confInit"}
+ if err != nil {
+ logger.WithError(err).WithFields(fields).Fatal("Couldn't perform initial configuration read")
}
go func() {
for {
- time.Sleep(RefreshRate)
- confRefresh()
+ time.Sleep(refreshRate)
+ if err = confRefresh(); err != nil {
+ logger.WithError(err).WithFields(fields).Error("Couldn't refresh configuration")
+ }
}
}()
}
/*
GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
import (
"flag"
"fmt"
- "log"
- "net"
- "os"
- "os/signal"
- "time"
+
+ "github.com/Sirupsen/logrus"
"cypherpunks.ru/govpn"
+ "cypherpunks.ru/govpn/server"
)
var (
syslog = flag.Bool("syslog", false, "Enable logging to syslog")
version = flag.Bool("version", false, "Print version information")
warranty = flag.Bool("warranty", false, "Print warranty information")
+ logLevel = flag.String("log_level", "warning", "Log level")
)
func main() {
+ var err error
+ fields := logrus.Fields{"func": "main"}
+
flag.Parse()
if *warranty {
fmt.Println(govpn.Warranty)
fmt.Println(govpn.VersionGet())
return
}
- timeout := time.Second * time.Duration(govpn.TimeoutDefault)
- log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
- log.Println(govpn.VersionGet())
- confInit()
- knownPeers = govpn.KnownPeers(make(map[string]**govpn.Peer))
+ logger, err = govpn.NewLogger(*logLevel, *syslog)
+ if err != nil {
+ logrus.WithFields(fields).WithError(err).Fatal("Couldn't initialize logging")
+ }
+ govpn.SetLogger(logger)
if *egdPath != "" {
- log.Println("Using", *egdPath, "EGD")
+ logger.WithField("egd_path", *egdPath).WithFields(fields).Debug("Init EGD")
govpn.EGDInit(*egdPath)
}
- if *syslog {
- govpn.SyslogEnable()
- }
+ confInit()
- switch *proto {
- case "udp":
- startUDP()
- case "tcp":
- startTCP()
- case "all":
- startUDP()
- startTCP()
- default:
- log.Fatalln("Unknown protocol specified")
+ serverConfig := server.Configuration{
+ BindAddress: *bindAddr,
+ ProxyAddress: *proxy,
+ Timeout: govpn.TimeoutDefault,
+ }
+ if serverConfig.Protocol, err = govpn.NewProtocolFromString(*proto); err != nil {
+ logger.WithError(err).WithFields(fields).WithField("proto", *proto).Fatal("Invalid protocol")
+ }
+ if err = serverConfig.Validate(); err != nil {
+ logger.WithError(err).WithFields(fields).Fatal("Invalid configuration")
}
- termSignal := make(chan os.Signal, 1)
- signal.Notify(termSignal, os.Interrupt, os.Kill)
-
- hsHeartbeat := time.Tick(timeout)
- go func() { <-hsHeartbeat }()
+ srv := server.NewServer(serverConfig, confs, idsCache, logger, govpn.CatchSignalShutdown())
if *stats != "" {
- log.Println("Stats are going to listen on", *stats)
- statsPort, err := net.Listen("tcp", *stats)
- if err != nil {
- log.Fatalln("Can not listen on stats port:", err)
- }
- go govpn.StatsProcessor(statsPort, &knownPeers)
+ go govpn.StatsProcessor(*stats, srv.KnownPeers())
}
- if *proxy != "" {
- go proxyStart()
- }
- govpn.BothPrintf(`[started bind="%s"]`, *bindAddr)
-
- var needsDeletion bool
-MainCycle:
- for {
- select {
- case <-termSignal:
- govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr)
- for _, ps := range peers {
- govpn.ScriptCall(
- confs[*ps.peer.ID].Down,
- ps.tap.Name,
- ps.peer.Addr,
- )
- }
- break MainCycle
- case <-hsHeartbeat:
- now := time.Now()
- hsLock.Lock()
- for addr, hs := range handshakes {
- if hs.LastPing.Add(timeout).Before(now) {
- govpn.Printf(`[handshake-delete bind="%s" addr="%s"]`, *bindAddr, addr)
- hs.Zero()
- delete(handshakes, addr)
- }
- }
- peersLock.Lock()
- peersByIDLock.Lock()
- kpLock.Lock()
- for addr, ps := range peers {
- ps.peer.BusyR.Lock()
- needsDeletion = ps.peer.LastPing.Add(timeout).Before(now)
- ps.peer.BusyR.Unlock()
- if needsDeletion {
- govpn.Printf(`[peer-delete bind="%s" peer="%s"]`, *bindAddr, ps.peer)
- delete(peers, addr)
- delete(knownPeers, addr)
- delete(peersByID, *ps.peer.ID)
- go govpn.ScriptCall(
- confs[*ps.peer.ID].Down,
- ps.tap.Name,
- ps.peer.Addr,
- )
- ps.terminator <- struct{}{}
- }
- }
- hsLock.Unlock()
- peersLock.Unlock()
- peersByIDLock.Unlock()
- kpLock.Unlock()
- }
+
+ go srv.MainCycle()
+ if err = <-srv.Error; err != nil {
+ logger.WithError(err).Fatal("Fatal error")
}
}
+++ /dev/null
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "net/http"
-
- "cypherpunks.ru/govpn"
-)
-
-type proxyHandler struct{}
-
-func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- conn, _, err := w.(http.Hijacker).Hijack()
- if err != nil {
- govpn.Printf(`[proxy-hijack-failed bind="%s" err="%s"]`, *bindAddr, err)
- return
- }
- conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
- go handleTCP(conn)
-}
-
-func proxyStart() {
- govpn.BothPrintf(`[proxy-listen bind="%s" addr="%s"]`, *bindAddr, *proxy)
- s := &http.Server{
- Addr: *proxy,
- Handler: proxyHandler{},
- }
- govpn.BothPrintf(`[proxy-finished bind="%s" result="%s"]`, *bindAddr, s.ListenAndServe())
-}
+++ /dev/null
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "bytes"
- "log"
- "net"
- "time"
-
- "cypherpunks.ru/govpn"
-)
-
-func startTCP() {
- bind, err := net.ResolveTCPAddr("tcp", *bindAddr)
- if err != nil {
- log.Fatalln("Can not resolve bind address:", err)
- }
- listener, err := net.ListenTCP("tcp", bind)
- if err != nil {
- log.Fatalln("Can not listen on TCP:", err)
- }
- govpn.BothPrintf(`[tcp-listen bind="%s"]`, *bindAddr)
- go func() {
- for {
- conn, err := listener.AcceptTCP()
- if err != nil {
- govpn.Printf(`[tcp-accept-failed bind="%s" err="%s"]`, *bindAddr, err)
- continue
- }
- go handleTCP(conn)
- }
- }()
-}
-
-func handleTCP(conn net.Conn) {
- addr := conn.RemoteAddr().String()
- buf := make([]byte, govpn.EnclessEnlargeSize+2*govpn.MTUMax)
- var n int
- var err error
- var prev int
- var hs *govpn.Handshake
- var ps *PeerState
- var peer *govpn.Peer
- var tap *govpn.TAP
- var conf *govpn.PeerConf
- for {
- if prev == len(buf) {
- break
- }
- conn.SetReadDeadline(time.Now().Add(time.Duration(govpn.TimeoutDefault) * time.Second))
- n, err = conn.Read(buf[prev:])
- if err != nil {
- // Either EOFed or timeouted
- break
- }
- prev += n
- peerID := idsCache.Find(buf[:prev])
- if peerID == nil {
- continue
- }
- if hs == nil {
- conf = confs[*peerID]
- if conf == nil {
- govpn.Printf(
- `[conf-get-failed bind="%s" peer="%s"]`,
- *bindAddr, peerID.String(),
- )
- break
- }
- hs = govpn.NewHandshake(addr, conn, conf)
- }
- peer = hs.Server(buf[:prev])
- prev = 0
- if peer == nil {
- continue
- }
- hs.Zero()
- govpn.Printf(
- `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
- *bindAddr, addr, peerID.String(),
- )
- peersByIDLock.RLock()
- addrPrev, exists := peersByID[*peer.ID]
- peersByIDLock.RUnlock()
- if exists {
- peersLock.Lock()
- peers[addrPrev].terminator <- struct{}{}
- tap = peers[addrPrev].tap
- ps = &PeerState{
- peer: peer,
- tap: tap,
- terminator: make(chan struct{}),
- }
- go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
- peersByIDLock.Lock()
- kpLock.Lock()
- delete(peers, addrPrev)
- delete(knownPeers, addrPrev)
- peers[addr] = ps
- knownPeers[addr] = &peer
- peersByID[*peer.ID] = addr
- peersLock.Unlock()
- peersByIDLock.Unlock()
- kpLock.Unlock()
- govpn.Printf(
- `[rehandshake-completed bind="%s" peer="%s"]`,
- *bindAddr, peerID.String(),
- )
- } else {
- ifaceName, err := callUp(peer.ID, peer.Addr)
- if err != nil {
- peer = nil
- break
- }
- tap, err = govpn.TAPListen(ifaceName, peer.MTU)
- if err != nil {
- govpn.Printf(
- `[tap-failed bind="%s" peer="%s" err="%s"]`,
- *bindAddr, peerID.String(), err,
- )
- peer = nil
- break
- }
- ps = &PeerState{
- peer: peer,
- tap: tap,
- terminator: make(chan struct{}, 1),
- }
- go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
- peersLock.Lock()
- peersByIDLock.Lock()
- kpLock.Lock()
- peers[addr] = ps
- peersByID[*peer.ID] = addr
- knownPeers[addr] = &peer
- peersLock.Unlock()
- peersByIDLock.Unlock()
- kpLock.Unlock()
- govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peerID.String())
- }
- break
- }
- if hs != nil {
- hs.Zero()
- }
- if peer == nil {
- return
- }
-
- prev = 0
- var i int
- for {
- if prev == len(buf) {
- break
- }
- conn.SetReadDeadline(time.Now().Add(conf.Timeout))
- n, err = conn.Read(buf[prev:])
- if err != nil {
- // Either EOFed or timeouted
- break
- }
- prev += n
- CheckMore:
- if prev < govpn.MinPktLength {
- continue
- }
- i = bytes.Index(buf[:prev], peer.NonceExpect)
- if i == -1 {
- continue
- }
- if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
- govpn.Printf(
- `[packet-unauthenticated bind="%s" addr="%s" peer="%s"]`,
- *bindAddr, addr, peer.ID.String(),
- )
- break
- }
- copy(buf, buf[i+govpn.NonceSize:prev])
- prev = prev - i - govpn.NonceSize
- goto CheckMore
- }
- peer.Zero()
-}
+++ /dev/null
-/*
-GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2017 Sergey Matveev <stargrave@stargrave.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "log"
- "net"
-
- "cypherpunks.ru/govpn"
-)
-
-type UDPSender struct {
- conn *net.UDPConn
- addr *net.UDPAddr
-}
-
-func (c UDPSender) Write(data []byte) (int, error) {
- return c.conn.WriteToUDP(data, c.addr)
-}
-
-var (
- // Buffers for UDP parallel processing
- udpBufs = make(chan []byte, 1<<8)
-)
-
-func startUDP() {
- bind, err := net.ResolveUDPAddr("udp", *bindAddr)
- if err != nil {
- log.Fatalln("Can not resolve bind address:", err)
- }
- conn, err := net.ListenUDP("udp", bind)
- if err != nil {
- log.Fatalln("Can not listen on UDP:", err)
- }
- govpn.BothPrintf(`[udp-listen bind="%s"]`, *bindAddr)
-
- udpBufs <- make([]byte, govpn.MTUMax)
- go func() {
- var buf []byte
- var raddr *net.UDPAddr
- var addr string
- var n int
- var err error
- var ps *PeerState
- var hs *govpn.Handshake
- var addrPrev string
- var exists bool
- var peerID *govpn.PeerID
- var conf *govpn.PeerConf
- for {
- buf = <-udpBufs
- n, raddr, err = conn.ReadFromUDP(buf)
- if err != nil {
- govpn.Printf(`[receive-failed bind="%s" err="%s"]`, *bindAddr, err)
- break
- }
- addr = raddr.String()
-
- peersLock.RLock()
- ps, exists = peers[addr]
- peersLock.RUnlock()
- if exists {
- go func(peer *govpn.Peer, tap *govpn.TAP, buf []byte, n int) {
- peer.PktProcess(buf[:n], tap, true)
- udpBufs <- buf
- }(ps.peer, ps.tap, buf, n)
- continue
- }
-
- hsLock.RLock()
- hs, exists = handshakes[addr]
- hsLock.RUnlock()
- if !exists {
- peerID = idsCache.Find(buf[:n])
- if peerID == nil {
- govpn.Printf(`[identity-unknown bind="%s" addr="%s"]`, *bindAddr, addr)
- udpBufs <- buf
- continue
- }
- conf = confs[*peerID]
- if conf == nil {
- govpn.Printf(
- `[conf-get-failed bind="%s" peer="%s"]`,
- *bindAddr, peerID.String(),
- )
- udpBufs <- buf
- continue
- }
- hs := govpn.NewHandshake(
- addr,
- UDPSender{conn: conn, addr: raddr},
- conf,
- )
- hs.Server(buf[:n])
- udpBufs <- buf
- hsLock.Lock()
- handshakes[addr] = hs
- hsLock.Unlock()
- continue
- }
-
- peer := hs.Server(buf[:n])
- if peer == nil {
- udpBufs <- buf
- continue
- }
- govpn.Printf(
- `[handshake-completed bind="%s" addr="%s" peer="%s"]`,
- *bindAddr, addr, peerID.String(),
- )
- hs.Zero()
- hsLock.Lock()
- delete(handshakes, addr)
- hsLock.Unlock()
-
- go func() {
- udpBufs <- make([]byte, govpn.MTUMax)
- udpBufs <- make([]byte, govpn.MTUMax)
- }()
- peersByIDLock.RLock()
- addrPrev, exists = peersByID[*peer.ID]
- peersByIDLock.RUnlock()
- if exists {
- peersLock.Lock()
- peers[addrPrev].terminator <- struct{}{}
- psNew := &PeerState{
- peer: peer,
- tap: peers[addrPrev].tap,
- terminator: make(chan struct{}),
- }
- go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
- govpn.PeerTapProcessor(peer, tap, terminator)
- <-udpBufs
- <-udpBufs
- }(psNew.peer, psNew.tap, psNew.terminator)
- peersByIDLock.Lock()
- kpLock.Lock()
- delete(peers, addrPrev)
- delete(knownPeers, addrPrev)
- peers[addr] = psNew
- knownPeers[addr] = &peer
- peersByID[*peer.ID] = addr
- peersLock.Unlock()
- peersByIDLock.Unlock()
- kpLock.Unlock()
- govpn.Printf(
- `[rehandshake-completed bind="%s" peer="%s"]`,
- *bindAddr, peer.ID.String(),
- )
- } else {
- go func(addr string, peer *govpn.Peer) {
- ifaceName, err := callUp(peer.ID, peer.Addr)
- if err != nil {
- return
- }
- tap, err := govpn.TAPListen(ifaceName, peer.MTU)
- if err != nil {
- govpn.Printf(
- `[tap-failed bind="%s" peer="%s" err="%s"]`,
- *bindAddr, peer.ID.String(), err,
- )
- return
- }
- psNew := &PeerState{
- peer: peer,
- tap: tap,
- terminator: make(chan struct{}),
- }
- go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
- govpn.PeerTapProcessor(peer, tap, terminator)
- <-udpBufs
- <-udpBufs
- }(psNew.peer, psNew.tap, psNew.terminator)
- peersLock.Lock()
- peersByIDLock.Lock()
- kpLock.Lock()
- peers[addr] = psNew
- knownPeers[addr] = &peer
- peersByID[*peer.ID] = addr
- peersLock.Unlock()
- peersByIDLock.Unlock()
- kpLock.Unlock()
- govpn.Printf(`[peer-created bind="%s" peer="%s"]`, *bindAddr, peer.ID.String())
- }(addr, peer)
- }
- udpBufs <- buf
- }
- }()
-}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+ "github.com/pkg/errors"
+
+ "cypherpunks.ru/govpn"
+)
+
+const logFuncPrefix = "govpn/server."
+
+var (
+ errMisconfiguredTap = errors.New("No PreUp and no Iface, can't create interface")
+ errPreUpNoTap = errors.New("PreUp didn't returned an interface, and Iface is unset")
+)
+
+func (s *Server) callUp(peer *govpn.Peer, proto govpn.Protocol) (*govpn.TAP, error) {
+ var (
+ tap *govpn.TAP
+ conf = s.confs.Get(*peer.ID)
+ err error
+ isConfigIface = len(conf.Iface) != 0
+ fields = s.LogFields()
+ )
+ fields["func"] = logFuncPrefix + "Server.callUp"
+
+ if !isConfigIface && conf.PreUp == nil {
+ return nil, errors.Wrapf(errMisconfiguredTap, "interface:%q, PreUp:%q", conf.Iface, conf.PreUp)
+ }
+
+ if conf.PreUp != nil {
+ s.logger.WithFields(fields).Debug("PreUp defined, execute it")
+ tap, err = conf.PreUp(govpn.PeerContext{
+ RemoteAddress: peer.Addr,
+ Protocol: proto,
+ Config: *conf,
+ })
+ if err != nil {
+ return nil, errors.Wrap(err, "conf.PreUp")
+ }
+ s.logger.WithFields(fields).WithField("tap", tap).Debug("PreUp finished")
+ } else {
+ s.logger.WithFields(fields).Debug("No PreUp defined, skip")
+ }
+
+ if tap == nil {
+ s.logger.WithFields(fields).Debug("PreUp didn't returned an interface, create one")
+ if !isConfigIface {
+ return nil, errors.Wrapf(errPreUpNoTap, "interface:%q tap:%q", conf.Iface, tap)
+ }
+
+ if tap, err = govpn.TAPListen(conf.Iface, peer.MTU); err != nil {
+ return nil, errors.Wrap(err, "govpn.TAPListen")
+ }
+ }
+
+ conf.Iface = tap.Name
+
+ if conf.Up == nil {
+ s.logger.WithFields(fields).Debug("Got interface, no Up")
+ return tap, nil
+ }
+ s.logger.WithFields(fields).Debug("Got interface, execute Up")
+
+ err = conf.Up(govpn.PeerContext{
+ RemoteAddress: peer.Addr,
+ Protocol: proto,
+ Config: *conf,
+ })
+ if err != nil {
+ return nil, errors.Wrap(err, "conf.Up")
+ }
+ s.logger.WithFields(fields).Debug("Got interface, Up executed")
+ return tap, nil
+}
+
+func (s *Server) callDown(ps *PeerState) error {
+ fields := s.LogFields()
+ fields["func"] = logFuncPrefix + "Server.callDown"
+
+ conf := s.confs.Get(*ps.peer.ID)
+ if conf == nil {
+ s.logger.WithFields(fields).Error("Couldn't get configuration")
+ return nil
+ }
+ if conf.Down == nil {
+ s.logger.WithFields(fields).Debug("No Down, skip")
+ return nil
+ }
+ s.logger.WithFields(fields).Debug("Execute Down")
+ err := conf.Down(govpn.PeerContext{
+ RemoteAddress: ps.peer.Addr,
+ Config: *conf,
+ Protocol: ps.peer.Protocol,
+ })
+ s.logger.WithFields(fields).Debug("Down executed")
+ return errors.Wrap(err, "peer.Down")
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+ "net/http"
+
+ "github.com/Sirupsen/logrus"
+)
+
+type proxyHandler struct {
+ goVpnServer *Server
+}
+
+func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ conn, _, err := w.(http.Hijacker).Hijack()
+ if err != nil {
+ p.goVpnServer.logger.WithError(err).WithFields(
+ logrus.Fields{
+ "func": logFuncPrefix + "proxyHandler.ServeHTTP",
+ "address": p.goVpnServer.configuration.BindAddress,
+ },
+ ).Error("Proxy hijack failed")
+ return
+ }
+ conn.Write([]byte("HTTP/1.0 200 OK\n\n"))
+ go p.goVpnServer.handleTCP(conn)
+}
+
+func (s *Server) proxyStart() {
+ fields := logrus.Fields{
+ "func": logFuncPrefix + "Server.proxyStart",
+ "address": s.configuration.BindAddress,
+ "proxy": s.configuration.ProxyAddress,
+ }
+ s.logger.WithFields(fields).Info("Proxy Listen")
+ httpServer := &http.Server{
+ Addr: s.configuration.ProxyAddress,
+ Handler: proxyHandler{
+ goVpnServer: s,
+ },
+ }
+ if err := httpServer.ListenAndServe(); err != nil {
+ s.logger.WithFields(fields).WithError(err).Error("Proxy failed")
+ return
+ }
+ s.logger.WithFields(fields).Info("Proxy finished")
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+ "sync"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/pkg/errors"
+
+ "cypherpunks.ru/govpn"
+)
+
+// PeerConfigurer is used by a GoVPN server to figure the configuration of a single peer
+type PeerConfigurer interface {
+ Get(govpn.PeerID) *govpn.PeerConf
+}
+
+// MACPeerFinder is used by GoVPN server to figure the PeerID from handshake data
+type MACPeerFinder interface {
+ Find([]byte) (*govpn.PeerID, error)
+}
+
+// PeerState hold server side state of a single connecting/connected peer
+type PeerState struct {
+ peer *govpn.Peer
+ terminator chan struct{}
+ tap *govpn.TAP
+}
+
+// Configuration hold GoVPN server configuration
+type Configuration struct {
+ BindAddress string
+ ProxyAddress string
+ Protocol govpn.Protocol
+ Timeout time.Duration
+}
+
+// Validate return an error if a configuration is invalid
+func (c *Configuration) Validate() error {
+ if len(c.BindAddress) == 0 {
+ return errors.New("Missing BindAddress")
+ }
+ return nil
+}
+
+// LogFields return a logrus compatible logging context
+func (c *Configuration) LogFields() logrus.Fields {
+ const prefix = "srv_conf_"
+ f := logrus.Fields{
+ prefix + "bind": c.BindAddress,
+ prefix + "protocol": c.Protocol.String(),
+ prefix + "timeout": c.Timeout.String(),
+ }
+ if len(c.ProxyAddress) > 0 {
+ f[prefix+"proxy"] = c.ProxyAddress
+ }
+ return f
+}
+
+// Server is a GoVPN server instance
+type Server struct {
+ configuration Configuration
+ termSignal chan interface{}
+
+ idsCache MACPeerFinder
+ confs PeerConfigurer
+
+ handshakes map[string]*govpn.Handshake
+ hsLock sync.RWMutex
+
+ peers map[string]*PeerState
+ peersLock sync.RWMutex
+
+ peersByID map[govpn.PeerID]string
+ peersByIDLock sync.RWMutex
+
+ knownPeers govpn.KnownPeers
+ kpLock sync.RWMutex
+
+ logger *logrus.Logger
+
+ // Error channel receives any kind of routine errors
+ Error chan error
+}
+
+// LogFields return a logrus compatible logging context
+func (s *Server) LogFields() logrus.Fields {
+ const prefix = "srv_"
+ return logrus.Fields{
+ prefix + "hs": len(s.handshakes),
+ prefix + "peers": len(s.peers),
+ prefix + "known": len(s.knownPeers),
+ }
+}
+
+// KnownPeers return GoVPN peers.
+// used to get client statistics.
+func (s *Server) KnownPeers() *govpn.KnownPeers {
+ return &s.knownPeers
+}
+
+// NewServer return a configured GoVPN server, to listen network connection MainCycle must be executed
+func NewServer(serverConf Configuration, peerConfs PeerConfigurer, idsCache MACPeerFinder, logger *logrus.Logger, termSignal chan interface{}) *Server {
+ govpn.SetLogger(logger)
+ return &Server{
+ configuration: serverConf,
+ confs: peerConfs,
+ termSignal: termSignal,
+ idsCache: idsCache,
+ handshakes: make(map[string]*govpn.Handshake),
+ peers: make(map[string]*PeerState),
+ peersByID: make(map[govpn.PeerID]string),
+ knownPeers: govpn.KnownPeers(make(map[string]**govpn.Peer)),
+ Error: make(chan error, 1),
+ logger: logger,
+ }
+}
+
+// MainCycle main loop that handle connecting/connected client
+func (s *Server) MainCycle() {
+ switch s.configuration.Protocol {
+ case govpn.ProtocolUDP:
+ s.startUDP()
+ case govpn.ProtocolTCP:
+ s.startTCP()
+ case govpn.ProtocolALL:
+ s.startUDP()
+ s.startTCP()
+ default:
+ s.Error <- errors.New("Unknown protocol specified")
+ return
+ }
+
+ if len(s.configuration.ProxyAddress) > 0 {
+ go s.proxyStart()
+ }
+ fields := logrus.Fields{"func": logFuncPrefix + "Server.MainCycle"}
+
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Starting...")
+
+ var needsDeletion bool
+ var err error
+ hsHeartbeat := time.Tick(s.configuration.Timeout)
+ go func() { <-hsHeartbeat }()
+
+MainCycle:
+ for {
+ select {
+ case <-s.termSignal:
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Terminating")
+ for _, ps := range s.peers {
+ if err = s.callDown(ps); err != nil {
+ s.logger.WithFields(fields).WithError(err).WithFields(ps.peer.LogFields()).Error("Failed to run callDown")
+ }
+ if err = ps.tap.Close(); err != nil {
+ logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't close TAP")
+ }
+ }
+ // empty value signals that everything is fine
+ s.Error <- nil
+ break MainCycle
+ case <-hsHeartbeat:
+ logrus.WithFields(fields).Debug("Heartbeat")
+ now := time.Now()
+ s.hsLock.Lock()
+ for addr, hs := range s.handshakes {
+ if hs.LastPing.Add(s.configuration.Timeout).Before(now) {
+ logrus.WithFields(fields).WithFields(hs.LogFields()).Debug("handshake is expired, delete")
+ hs.Zero()
+ delete(s.handshakes, addr)
+ }
+ }
+ s.peersLock.Lock()
+ s.peersByIDLock.Lock()
+ s.kpLock.Lock()
+ for addr, ps := range s.peers {
+ ps.peer.BusyR.Lock()
+ needsDeletion = ps.peer.LastPing.Add(s.configuration.Timeout).Before(now)
+ ps.peer.BusyR.Unlock()
+ if needsDeletion {
+ logrus.WithFields(fields).WithFields(ps.peer.LogFields()).Info("Delete peer")
+ delete(s.peers, addr)
+ delete(s.knownPeers, addr)
+ delete(s.peersByID, *ps.peer.ID)
+ if err = s.callDown(ps); err != nil {
+ logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't execute callDown")
+ }
+ if err = ps.tap.Close(); err != nil {
+ logrus.WithError(err).WithFields(fields).WithFields(ps.peer.LogFields()).Error("Couldn't close TAP")
+ }
+ ps.terminator <- struct{}{}
+ }
+ }
+ s.hsLock.Unlock()
+ s.peersLock.Unlock()
+ s.peersByIDLock.Unlock()
+ s.kpLock.Unlock()
+ }
+ }
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+ "bytes"
+ "net"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/pkg/errors"
+
+ "cypherpunks.ru/govpn"
+)
+
+func (s *Server) startTCP() {
+ bind, err := net.ResolveTCPAddr("tcp", s.configuration.BindAddress)
+ if err != nil {
+ s.Error <- errors.Wrap(err, "net.ResolveTCPAddr")
+ return
+ }
+ listener, err := net.ListenTCP("tcp", bind)
+ if err != nil {
+ s.Error <- errors.Wrapf(err, "net.ListenTCP %q", bind.String())
+ return
+ }
+ fields := logrus.Fields{
+ "func": logFuncPrefix + "Server.startTCP",
+ "bind": bind.String(),
+ }
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Listen")
+ go func() {
+ for {
+ conn, err := listener.AcceptTCP()
+ if err != nil {
+ s.logger.WithError(err).WithFields(fields).WithFields(s.LogFields()).Error("Failed to accept TCP connection")
+ continue
+ }
+ go s.handleTCP(conn)
+ }
+ }()
+}
+
+func (s *Server) handleTCP(conn net.Conn) {
+ addr := conn.RemoteAddr().String()
+ buf := make([]byte, govpn.EnclessEnlargeSize+2*govpn.MTUMax)
+ var n int
+ var err error
+ var prev int
+ var hs *govpn.Handshake
+ var ps *PeerState
+ var peer *govpn.Peer
+ var deadLine time.Time
+ var tap *govpn.TAP
+ var conf *govpn.PeerConf
+ fields := logrus.Fields{
+ "func": logFuncPrefix + "Server.handleTCP",
+ "remote": addr,
+ }
+ for {
+ if prev == len(buf) {
+ // TODO log why
+ break
+ }
+
+ deadLine = time.Now().Add(govpn.TimeoutDefault)
+ if err = conn.SetReadDeadline(deadLine); err != nil {
+ s.Error <- errors.Wrapf(err, "conn.SetReadDeadline %s", deadLine.String())
+ return
+ }
+ n, err = conn.Read(buf[prev:])
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Can't read connection: either EOFed or timeouted")
+ break
+ }
+ prev += n
+ peerID, err := s.idsCache.Find(buf[:prev])
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Couldn't lookup for peer in ids")
+ continue
+ }
+ if peerID == nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).Debug("Couldn't find peer")
+ continue
+ }
+ if hs == nil {
+ conf = s.confs.Get(*peerID)
+ if conf == nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Error("Configuration get failed")
+ break
+ }
+ hs = govpn.NewHandshake(addr, conn, conf)
+ }
+ peer, err = hs.Server(buf[:prev])
+ if err != nil {
+ s.logger.WithFields(fields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer")
+ continue
+ }
+ prev = 0
+ if peer == nil {
+ continue
+ }
+
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Handshake completed")
+
+ hs.Zero()
+ s.peersByIDLock.RLock()
+ addrPrev, exists := s.peersByID[*peer.ID]
+ s.peersByIDLock.RUnlock()
+
+ if exists {
+ s.peersLock.Lock()
+ s.peers[addrPrev].terminator <- struct{}{}
+ tap = s.peers[addrPrev].tap
+ ps = &PeerState{
+ peer: peer,
+ tap: tap,
+ terminator: make(chan struct{}),
+ }
+ peer.Protocol = govpn.ProtocolTCP
+ go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
+ s.peersByIDLock.Lock()
+ s.kpLock.Lock()
+ delete(s.peers, addrPrev)
+ delete(s.knownPeers, addrPrev)
+ s.peers[addr] = ps
+ s.knownPeers[addr] = &peer
+ s.peersByID[*peer.ID] = addr
+ s.peersLock.Unlock()
+ s.peersByIDLock.Unlock()
+ s.kpLock.Unlock()
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Debug("Rehandshake completed")
+ } else {
+ tap, err = s.callUp(peer, govpn.ProtocolTCP)
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).WithError(err).Error("TAP failed")
+ peer = nil
+ break
+ }
+ ps = &PeerState{
+ peer: peer,
+ tap: tap,
+ terminator: make(chan struct{}, 1),
+ }
+ peer.Protocol = govpn.ProtocolTCP
+ go govpn.PeerTapProcessor(ps.peer, ps.tap, ps.terminator)
+ s.peersLock.Lock()
+ s.peersByIDLock.Lock()
+ s.kpLock.Lock()
+ s.peers[addr] = ps
+ s.peersByID[*peer.ID] = addr
+ s.knownPeers[addr] = &peer
+ s.peersLock.Unlock()
+ s.peersByIDLock.Unlock()
+ s.kpLock.Unlock()
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Peer created")
+ }
+ break
+ }
+ if hs != nil {
+ hs.Zero()
+ }
+ if peer == nil {
+ return
+ }
+
+ prev = 0
+ var i int
+ for {
+ if prev == len(buf) {
+ break
+ }
+ deadLine = time.Now().Add(conf.Timeout)
+ if err = conn.SetReadDeadline(deadLine); err != nil {
+ s.Error <- errors.Wrapf(err, "conn.SetReadDeadline %s", deadLine.String())
+ return
+ }
+ n, err = conn.Read(buf[prev:])
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Can't read connection: either EOFed or timeouted")
+ break
+ }
+ prev += n
+ CheckMore:
+ if prev < govpn.MinPktLength {
+ continue
+ }
+ i = bytes.Index(buf[:prev], peer.NonceExpect)
+ if i == -1 {
+ continue
+ }
+ if !peer.PktProcess(buf[:i+govpn.NonceSize], tap, false) {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Warn("Packet unauthenticated")
+ break
+ }
+ copy(buf, buf[i+govpn.NonceSize:prev])
+ prev = prev - i - govpn.NonceSize
+ goto CheckMore
+ }
+ peer.Zero()
+}
--- /dev/null
+/*
+GoVPN -- simple secure free software virtual private network daemon
+Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package server
+
+import (
+ "net"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/pkg/errors"
+
+ "cypherpunks.ru/govpn"
+)
+
+type udpSender struct {
+ conn *net.UDPConn
+ addr *net.UDPAddr
+}
+
+func (c udpSender) Write(data []byte) (int, error) {
+ return c.conn.WriteToUDP(data, c.addr)
+}
+
+// TODO move to udpSender (?)
+// buffers for UDP parallel processing
+var udpBufs = make(chan []byte, 1<<8)
+
+func (s *Server) startUDP() {
+ bind, err := net.ResolveUDPAddr("udp", s.configuration.BindAddress)
+ if err != nil {
+ s.Error <- errors.Wrap(err, "net.ResolveUDPAddr")
+ return
+ }
+ conn, err := net.ListenUDP("udp", bind)
+ if err != nil {
+ s.Error <- errors.Wrapf(err, "net.ListenUDP %q", bind.String())
+ return
+ }
+
+ fields := logrus.Fields{
+ "func": logFuncPrefix + "Server.startUDP",
+ "bind": bind.String(),
+ }
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Info("Listen")
+ udpBufs <- make([]byte, govpn.MTUMax)
+ go func() {
+ var buf []byte
+ var raddr *net.UDPAddr
+ var addr string
+ var n int
+ var err error
+ var ps *PeerState
+ var hs *govpn.Handshake
+ var addrPrev string
+ var exists bool
+ var peerID *govpn.PeerID
+ var conf *govpn.PeerConf
+ for {
+ s.logger.WithFields(fields).Debug("Wait for UDP buffer")
+ buf = <-udpBufs
+ n, raddr, err = conn.ReadFromUDP(buf)
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithError(err).Debug("Receive failure")
+ break
+ }
+ addr = raddr.String()
+ loopFields := logrus.Fields{"addr": addr}
+
+ s.logger.WithFields(fields).WithFields(loopFields).Debug("Got UDP buffer, check if peer exists")
+ s.peersLock.RLock()
+ ps, exists = s.peers[addr]
+ s.peersLock.RUnlock()
+ if exists {
+ s.logger.WithFields(fields).WithFields(loopFields).Debug("Already known peer, PktProcess")
+ go func(peer *govpn.Peer, tap *govpn.TAP, buf []byte, n int) {
+ peer.PktProcess(buf[:n], tap, true)
+ udpBufs <- buf
+ }(ps.peer, ps.tap, buf, n)
+ continue
+ }
+
+ logrus.WithFields(fields).WithFields(loopFields).Debug("New peer")
+ s.hsLock.RLock()
+ hs, exists = s.handshakes[addr]
+ s.hsLock.RUnlock()
+ if !exists {
+ logrus.WithFields(fields).WithFields(loopFields).Debug("No handshake yet, try to figure peer ID")
+ peerID, err = s.idsCache.Find(buf[:n])
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).WithError(err).Debug("Couldn't lookup for peer in ids")
+ udpBufs <- buf
+ continue
+ }
+ if peerID == nil {
+ s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).Debug("Identity unknown")
+ udpBufs <- buf
+ continue
+ }
+
+ loopFields["peer_id"] = peerID.String()
+ s.logger.WithFields(fields).WithFields(loopFields).Debug("Found peer ID")
+ conf = s.confs.Get(*peerID)
+ if conf == nil {
+ s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(s.configuration.LogFields()).Error("Peer try to connect, but not configured")
+ udpBufs <- buf
+ continue
+ }
+
+ s.logger.WithFields(loopFields).WithFields(fields).Debug("Got configuration, perform handshake")
+ hs = govpn.NewHandshake(
+ addr,
+ udpSender{conn: conn, addr: raddr},
+ conf,
+ )
+ _, err := hs.Server(buf[:n])
+ udpBufs <- buf
+ if err != nil {
+ s.logger.WithFields(loopFields).WithFields(fields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer: handshake failed")
+ continue
+ }
+ s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).Info("Hashshake started, continue next packet")
+
+ s.hsLock.Lock()
+ s.handshakes[addr] = hs
+ s.hsLock.Unlock()
+ continue
+ }
+
+ logrus.WithFields(fields).WithFields(loopFields).Debug("Already go handshake, finish it")
+ peer, err := hs.Server(buf[:n])
+ if err != nil {
+ s.logger.WithFields(fields).WithFields(loopFields).WithError(err).WithFields(s.LogFields()).Error("Can't create new peer: handshake failed")
+ udpBufs <- buf
+ continue
+ }
+ if peer == nil {
+ s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).Error("Couldn't continue handshake")
+ udpBufs <- buf
+ continue
+ }
+
+ s.logger.WithFields(fields).WithFields(s.LogFields()).WithFields(loopFields).WithFields(peer.LogFields()).Info("Handshake completed")
+
+ hs.Zero()
+ s.hsLock.Lock()
+ delete(s.handshakes, addr)
+ s.hsLock.Unlock()
+
+ go func() {
+ udpBufs <- make([]byte, govpn.MTUMax)
+ udpBufs <- make([]byte, govpn.MTUMax)
+ }()
+ s.peersByIDLock.RLock()
+ addrPrev, exists = s.peersByID[*peer.ID]
+ s.peersByIDLock.RUnlock()
+
+ if exists {
+ s.logger.WithFields(fields).WithFields(loopFields).Debug("Peer already exists")
+ s.peersLock.Lock()
+ s.peers[addrPrev].terminator <- struct{}{}
+ psNew := &PeerState{
+ peer: peer,
+ tap: s.peers[addrPrev].tap,
+ terminator: make(chan struct{}),
+ }
+ peer.Protocol = govpn.ProtocolUDP
+
+ go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
+ govpn.PeerTapProcessor(peer, tap, terminator)
+ <-udpBufs
+ <-udpBufs
+ }(psNew.peer, psNew.tap, psNew.terminator)
+
+ s.peersByIDLock.Lock()
+ s.kpLock.Lock()
+ delete(s.peers, addrPrev)
+ delete(s.knownPeers, addrPrev)
+ s.peers[addr] = psNew
+ s.knownPeers[addr] = &peer
+ s.peersByID[*peer.ID] = addr
+ s.peersLock.Unlock()
+ s.peersByIDLock.Unlock()
+ s.kpLock.Unlock()
+
+ s.logger.WithFields(fields).WithFields(loopFields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Debug("Rehandshake completed")
+ } else {
+ go func(addr string, peer *govpn.Peer) {
+ s.logger.WithFields(fields).WithFields(loopFields).Debug("Peer do not already exists")
+ tap, err := s.callUp(peer, govpn.ProtocolUDP)
+ if err != nil {
+ s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).WithError(err).Error("TAP failed")
+ return
+ }
+ psNew := &PeerState{
+ peer: peer,
+ tap: tap,
+ terminator: make(chan struct{}),
+ }
+ peer.Protocol = govpn.ProtocolUDP
+ go func(peer *govpn.Peer, tap *govpn.TAP, terminator chan struct{}) {
+ govpn.PeerTapProcessor(peer, tap, terminator)
+ <-udpBufs
+ <-udpBufs
+ }(psNew.peer, psNew.tap, psNew.terminator)
+ s.peersLock.Lock()
+ s.peersByIDLock.Lock()
+ s.kpLock.Lock()
+ s.peers[addr] = psNew
+ s.knownPeers[addr] = &peer
+ s.peersByID[*peer.ID] = addr
+ s.peersLock.Unlock()
+ s.peersByIDLock.Unlock()
+ s.kpLock.Unlock()
+ s.logger.WithFields(loopFields).WithFields(fields).WithFields(s.LogFields()).WithFields(peer.LogFields()).Info("Peer initialized")
+ }(addr, peer)
+ }
+ udpBufs <- buf
+ }
+ }()
+}