Engine interface with Wireguard specific implementation.
diff --git a/core/vpn/engine/wireguard.go b/core/vpn/engine/wireguard.go
new file mode 100644
index 0000000..3dbf717
--- /dev/null
+++ b/core/vpn/engine/wireguard.go
@@ -0,0 +1,168 @@
+package engine
+
+import (
+ "encoding/hex"
+ "fmt"
+ "log"
+
+ "github.com/giolekva/pcloud/core/vpn/types"
+
+ "github.com/tailscale/wireguard-go/wgcfg"
+ "inet.af/netaddr"
+ "tailscale.com/control/controlclient"
+ "tailscale.com/ipn/ipnstate"
+ "tailscale.com/tailcfg"
+ "tailscale.com/types/wgkey"
+ "tailscale.com/wgengine"
+ "tailscale.com/wgengine/router"
+)
+
+// Wireguard specific implementation of the Engine interface.
+type WireguardEngine struct {
+ wg wgengine.Engine
+ port uint16
+ privKey types.PrivateKey
+}
+
+// Creates Wireguard engine.
+func NewWireguardEngine(tunName string, port uint16, privKey types.PrivateKey) (Engine, error) {
+ e, err := wgengine.NewUserspaceEngine(log.Printf, tunName, port)
+ if err != nil {
+ return nil, err
+ }
+ return &WireguardEngine{
+ wg: e,
+ port: port,
+ privKey: privKey,
+ }, nil
+}
+
+// Used for unit testing.
+func NewFakeWireguardEngine(port uint16, privKey types.PrivateKey) (Engine, error) {
+ e, err := wgengine.NewFakeUserspaceEngine(log.Printf, port, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &WireguardEngine{
+ wg: e,
+ port: port,
+ privKey: privKey,
+ }, nil
+}
+
+func genWireguardConf(privKey types.PrivateKey, port uint16, netMap *types.NetworkMap) *wgcfg.Config {
+ c := &wgcfg.Config{
+ Name: "foo",
+ PrivateKey: wgcfg.PrivateKey(privKey),
+ Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: netMap.Self.VPNIP,
+ Bits: 32, // TODO(giolekva): adapt for IPv6
+ }},
+ ListenPort: port,
+ Peers: make([]wgcfg.Peer, 0, len(netMap.Peers)),
+ }
+ for _, peer := range netMap.Peers {
+ c.Peers = append(c.Peers, wgcfg.Peer{
+ PublicKey: wgcfg.Key(peer.PublicKey),
+ AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: peer.VPNIP,
+ Bits: 32,
+ }},
+ Endpoints: peer.DiscoEndpoint,
+ PersistentKeepalive: 15, // TODO(giolekva): make it configurable
+ })
+ }
+ return c
+}
+
+func genRouterConf(netMap *types.NetworkMap) *router.Config {
+ c := &router.Config{
+ LocalAddrs: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: netMap.Self.VPNIP,
+ Bits: 32,
+ }},
+ Routes: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
+ }
+ for _, peer := range netMap.Peers {
+ c.Routes = append(c.Routes, netaddr.IPPrefix{
+ IP: peer.VPNIP,
+ Bits: 32,
+ })
+ }
+ return c
+}
+
+func genTailNetMap(privKey types.PrivateKey, port uint16, netMap *types.NetworkMap) *controlclient.NetworkMap {
+ fmt.Println(netMap.Self.IPPort.String())
+ c := &controlclient.NetworkMap{
+ SelfNode: &tailcfg.Node{
+ ID: 0, // TODO(giolekva): maybe IDs should be stored server side.
+ StableID: "0",
+ Name: "0",
+ Key: tailcfg.NodeKey(netMap.Self.PublicKey),
+ DiscoKey: tailcfg.DiscoKey(netMap.Self.DiscoKey),
+ Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: netMap.Self.VPNIP,
+ Bits: 32,
+ }},
+ AllowedIPs: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
+ Endpoints: []string{netMap.Self.IPPort.String()},
+ KeepAlive: true, // TODO(giolekva): make it configurable
+ },
+ NodeKey: tailcfg.NodeKey(netMap.Self.PublicKey),
+ PrivateKey: wgkey.Private(privKey),
+ Name: "0",
+ Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: netMap.Self.VPNIP,
+ Bits: 32,
+ }},
+ LocalPort: port,
+ Peers: make([]*tailcfg.Node, 0, len(netMap.Peers)),
+ }
+ for i, peer := range netMap.Peers {
+ c.Peers = append(c.Peers, &tailcfg.Node{
+ ID: tailcfg.NodeID(i + 1),
+ StableID: tailcfg.StableNodeID(fmt.Sprintf("%d", i+1)),
+ Name: fmt.Sprintf("%d", i+1),
+ Key: tailcfg.NodeKey(peer.PublicKey),
+ DiscoKey: tailcfg.DiscoKey(peer.DiscoKey),
+ Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: peer.VPNIP,
+ Bits: 32,
+ }},
+ AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
+ IP: netMap.Self.VPNIP,
+ Bits: 32,
+ }},
+ Endpoints: []string{peer.IPPort.String()},
+ KeepAlive: true,
+ })
+ }
+ return c
+}
+
+func (e *WireguardEngine) Configure(netMap *types.NetworkMap) error {
+ err := e.wg.Reconfig(
+ genWireguardConf(e.privKey, e.port, netMap),
+ genRouterConf(netMap))
+ if err != nil {
+ return err
+ }
+ e.wg.SetNetworkMap(genTailNetMap(e.privKey, e.port, netMap))
+ e.wg.RequestStatus()
+ return err
+}
+
+func (e *WireguardEngine) DiscoKey() types.DiscoKey {
+ return types.DiscoKey(e.wg.DiscoPublicKey())
+}
+
+func (e *WireguardEngine) DiscoEndpoint() string {
+ k := e.DiscoKey()
+ discoHex := hex.EncodeToString(k[:])
+ return fmt.Sprintf("%s%s", discoHex, controlclient.EndpointDiscoSuffix)
+}
+
+func (e *WireguardEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
+ e.wg.Ping(ip, cb)
+}