/*
GoVPN -- simple secure free software virtual private network daemon
-Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2014-2020 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.
+the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Simple secure free software virtual private network daemon.
+// Simple secure, DPI/censorship-resistant free software VPN daemon.
package main
import (
- "bytes"
"flag"
+ "fmt"
"log"
"net"
"os"
"os/signal"
- "path"
"time"
- "govpn"
+ "go.cypherpunks.ru/govpn/v7"
)
var (
- bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
- peersPath = flag.String("peers", "peers", "Path to peers keys directory")
- mtu = flag.Int("mtu", 1500, "MTU")
- nonceDiff = flag.Int("noncediff", 1, "Allow nonce difference")
- timeoutP = flag.Int("timeout", 60, "Timeout seconds")
+ bindAddr = flag.String("bind", "[::]:1194", "Bind to address")
+ proto = flag.String("proto", "udp", "Protocol to use: udp, tcp or all")
+ confPath = flag.String("conf", "peers.yaml", "Path to configuration YAML")
+ stats = flag.String("stats", "", "Enable stats retrieving on host:port")
+ proxy = flag.String("proxy", "", "Enable HTTP proxy on host:port")
+ egdPath = flag.String("egd", "", "Optional path to EGD socket")
+ 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")
)
-type PeerReadyEvent struct {
- peer *govpn.Peer
- iface string
-}
-
-type PeerState struct {
- peer *govpn.Peer
- tap *govpn.TAP
- sink chan []byte
- ready chan struct{}
- terminate chan struct{}
-}
-
-func NewPeerState(peer *govpn.Peer, iface string) *PeerState {
- tap, sink, ready, terminate, err := govpn.TAPListen(iface)
- if err != nil {
- log.Println("Unable to create Eth", err)
- return nil
- }
- state := PeerState{
- peer: peer,
- tap: tap,
- sink: sink,
- ready: ready,
- terminate: terminate,
- }
- return &state
-}
-
-type EthEvent struct {
- peer *govpn.Peer
- data []byte
- ready chan struct{}
-}
-
func main() {
flag.Parse()
- timeout := time.Second * time.Duration(*timeoutP)
- var err error
+ if *warranty {
+ fmt.Println(govpn.Warranty)
+ return
+ }
+ if *version {
+ fmt.Println(govpn.VersionGet())
+ return
+ }
+ timeout := time.Second * time.Duration(govpn.TimeoutDefault)
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
+ log.Println(govpn.VersionGet())
- govpn.MTU = *mtu
- govpn.Timeout = *timeoutP
- govpn.Noncediff = *nonceDiff
- govpn.PeersInit(*peersPath)
+ confInit()
- bind, err := net.ResolveUDPAddr("udp", *bindAddr)
- if err != nil {
- panic(err)
+ if *egdPath != "" {
+ log.Println("Using", *egdPath, "EGD")
+ govpn.EGDInit(*egdPath)
}
- conn, err := net.ListenUDP("udp", bind)
- if err != nil {
- panic(err)
+
+ if *syslog {
+ govpn.SyslogEnable()
+ }
+
+ switch *proto {
+ case "udp":
+ startUDP()
+ case "tcp":
+ startTCP()
+ case "all":
+ startUDP()
+ startTCP()
+ default:
+ log.Fatalln("Unknown protocol specified")
}
- udpSink, udpBuf, udpReady := govpn.ConnListen(conn)
termSignal := make(chan os.Signal, 1)
signal.Notify(termSignal, os.Interrupt, os.Kill)
hsHeartbeat := time.Tick(timeout)
go func() { <-hsHeartbeat }()
- var addr string
- var state *govpn.Handshake
- var peerState *PeerState
- var peer *govpn.Peer
- var exists bool
- states := make(map[string]*govpn.Handshake)
- peers := make(map[string]*PeerState)
- peerReadySink := make(chan PeerReadyEvent)
- var peerReady PeerReadyEvent
- var udpPkt *govpn.UDPPkt
- var udpPktData []byte
- var ethEvent EthEvent
- ethSink := make(chan EthEvent)
-
- log.Println("Server version", govpn.Version)
- log.Println("Server started")
+ 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)
+ }
+ if *proxy != "" {
+ go proxyStart()
+ }
+ govpn.BothPrintf(`[started bind="%s"]`, *bindAddr)
+ var needsDeletion bool
MainCycle:
for {
select {
case <-termSignal:
+ govpn.BothPrintf(`[terminating bind="%s"]`, *bindAddr)
+ peers.Range(func(_, psI interface{}) bool {
+ ps := psI.(*PeerState)
+ govpn.ScriptCall(
+ confs[*ps.peer.ID].Down,
+ ps.tap.Name,
+ ps.peer.Addr,
+ )
+ return true
+ })
break MainCycle
case <-hsHeartbeat:
now := time.Now()
- for addr, hs := range states {
+
+ handshakes.Range(func(addrI, hsI interface{}) bool {
+ addr := addrI.(string)
+ hs := hsI.(*govpn.Handshake)
if hs.LastPing.Add(timeout).Before(now) {
- log.Println("Deleting handshake state", addr)
- delete(states, addr)
+ govpn.Printf(`[handshake-delete bind="%s" addr="%s"]`, *bindAddr, addr)
+ hs.Zero()
+ handshakes.Delete(addr)
}
- }
- for addr, state := range peers {
- if state.peer.LastPing.Add(timeout).Before(now) {
- log.Println("Deleting peer", state.peer)
- delete(peers, addr)
- downPath := path.Join(
- govpn.PeersPath,
- state.peer.Id.String(),
- "down.sh",
+ return true
+ })
+
+ peers.Range(func(addrI, psI interface{}) bool {
+ addr := addrI.(string)
+ ps := psI.(*PeerState)
+ 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.ID.String(),
)
- go govpn.ScriptCall(downPath, state.tap.Name)
- state.terminate <- struct{}{}
- }
- }
- case peerReady = <-peerReadySink:
- for addr, state := range peers {
- if state.tap.Name != peerReady.iface {
- continue
- }
- delete(peers, addr)
- state.terminate <- struct{}{}
- break
- }
- addr = peerReady.peer.Addr.String()
- state := NewPeerState(peerReady.peer, peerReady.iface)
- if state == nil {
- continue
- }
- peers[addr] = state
- delete(states, addr)
- log.Println("Registered interface", peerReady.iface, "with peer", peer)
- go func(state *PeerState) {
- for data := range state.sink {
- ethSink <- EthEvent{
- peer: state.peer,
- data: data,
- ready: state.ready,
- }
- }
- }(state)
- case ethEvent = <-ethSink:
- if s, exists := peers[ethEvent.peer.Addr.String()]; !exists || s.peer != ethEvent.peer {
- continue
- }
- ethEvent.peer.EthProcess(ethEvent.data, conn, ethEvent.ready)
- case udpPkt = <-udpSink:
- if udpPkt == nil {
- udpReady <- struct{}{}
- continue
- }
- udpPktData = udpBuf[:udpPkt.Size]
- addr = udpPkt.Addr.String()
- if govpn.IsValidHandshakePkt(udpPktData) {
- state, exists = states[addr]
- if !exists {
- state = govpn.HandshakeNew(udpPkt.Addr)
- states[addr] = state
- }
- peer = state.Server(conn, udpPktData)
- if peer != nil {
- log.Println("Peer handshake finished", peer)
- if _, exists = peers[addr]; exists {
- go func() {
- peerReadySink <- PeerReadyEvent{peer, peers[addr].tap.Name}
- }()
- } else {
- go func() {
- upPath := path.Join(govpn.PeersPath, peer.Id.String(), "up.sh")
- result, err := govpn.ScriptCall(upPath, "")
- if err != nil {
- return
- }
- sepIndex := bytes.Index(result, []byte{'\n'})
- if sepIndex < 0 {
- sepIndex = len(result)
- }
- ifaceName := string(result[:sepIndex])
- peerReadySink <- PeerReadyEvent{peer, ifaceName}
- }()
- }
+ peers.Delete(addr)
+ knownPeers.Delete(addr)
+ peersByID.Delete(*ps.peer.ID)
+ go govpn.ScriptCall(
+ confs[*ps.peer.ID].Down,
+ ps.tap.Name,
+ ps.peer.Addr,
+ )
+ ps.terminator <- struct{}{}
}
- udpReady <- struct{}{}
- continue
- }
- peerState, exists = peers[addr]
- if !exists {
- udpReady <- struct{}{}
- continue
- }
- peerState.peer.UDPProcess(udpPktData, peerState.tap, udpReady)
+ return true
+ })
}
}
}