blob: 35e4747e2cb9205bce2dba368bc61668cb8cc94d [file] [log] [blame]
giolekva3c0e1822021-03-15 00:08:44 +04001package engine
2
3import (
giolekva3c0e1822021-03-15 00:08:44 +04004 "fmt"
5 "log"
6
7 "github.com/giolekva/pcloud/core/vpn/types"
8
9 "github.com/tailscale/wireguard-go/wgcfg"
10 "inet.af/netaddr"
11 "tailscale.com/control/controlclient"
12 "tailscale.com/ipn/ipnstate"
13 "tailscale.com/tailcfg"
14 "tailscale.com/types/wgkey"
15 "tailscale.com/wgengine"
16 "tailscale.com/wgengine/router"
17)
18
19// Wireguard specific implementation of the Engine interface.
20type WireguardEngine struct {
21 wg wgengine.Engine
22 port uint16
23 privKey types.PrivateKey
24}
25
26// Creates Wireguard engine.
27func NewWireguardEngine(tunName string, port uint16, privKey types.PrivateKey) (Engine, error) {
28 e, err := wgengine.NewUserspaceEngine(log.Printf, tunName, port)
29 if err != nil {
30 return nil, err
31 }
32 return &WireguardEngine{
33 wg: e,
34 port: port,
35 privKey: privKey,
36 }, nil
37}
38
39// Used for unit testing.
40func NewFakeWireguardEngine(port uint16, privKey types.PrivateKey) (Engine, error) {
41 e, err := wgengine.NewFakeUserspaceEngine(log.Printf, port, nil)
42 if err != nil {
43 return nil, err
44 }
45 return &WireguardEngine{
46 wg: e,
47 port: port,
48 privKey: privKey,
49 }, nil
50}
51
giolekva98a695d2021-03-15 12:43:20 +040052func genWireguardConf(privKey types.PrivateKey, port uint16,
53 netMap *types.NetworkMap) *wgcfg.Config {
giolekva3c0e1822021-03-15 00:08:44 +040054 c := &wgcfg.Config{
giolekva98a695d2021-03-15 12:43:20 +040055 // TODO(giolekva): we shoudld probably use hostname and share
56 // it with the controller
57 Name: "local-node",
giolekva3c0e1822021-03-15 00:08:44 +040058 PrivateKey: wgcfg.PrivateKey(privKey),
59 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
60 IP: netMap.Self.VPNIP,
61 Bits: 32, // TODO(giolekva): adapt for IPv6
62 }},
63 ListenPort: port,
64 Peers: make([]wgcfg.Peer, 0, len(netMap.Peers)),
65 }
66 for _, peer := range netMap.Peers {
67 c.Peers = append(c.Peers, wgcfg.Peer{
68 PublicKey: wgcfg.Key(peer.PublicKey),
69 AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
70 IP: peer.VPNIP,
71 Bits: 32,
72 }},
73 Endpoints: peer.DiscoEndpoint,
74 PersistentKeepalive: 15, // TODO(giolekva): make it configurable
75 })
76 }
77 return c
78}
79
80func genRouterConf(netMap *types.NetworkMap) *router.Config {
81 c := &router.Config{
82 LocalAddrs: []netaddr.IPPrefix{netaddr.IPPrefix{
83 IP: netMap.Self.VPNIP,
84 Bits: 32,
85 }},
86 Routes: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
87 }
88 for _, peer := range netMap.Peers {
89 c.Routes = append(c.Routes, netaddr.IPPrefix{
90 IP: peer.VPNIP,
91 Bits: 32,
92 })
93 }
94 return c
95}
96
97func genTailNetMap(privKey types.PrivateKey, port uint16, netMap *types.NetworkMap) *controlclient.NetworkMap {
giolekva3c0e1822021-03-15 00:08:44 +040098 c := &controlclient.NetworkMap{
99 SelfNode: &tailcfg.Node{
100 ID: 0, // TODO(giolekva): maybe IDs should be stored server side.
101 StableID: "0",
102 Name: "0",
103 Key: tailcfg.NodeKey(netMap.Self.PublicKey),
104 DiscoKey: tailcfg.DiscoKey(netMap.Self.DiscoKey),
105 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
106 IP: netMap.Self.VPNIP,
107 Bits: 32,
108 }},
109 AllowedIPs: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
110 Endpoints: []string{netMap.Self.IPPort.String()},
111 KeepAlive: true, // TODO(giolekva): make it configurable
112 },
113 NodeKey: tailcfg.NodeKey(netMap.Self.PublicKey),
114 PrivateKey: wgkey.Private(privKey),
115 Name: "0",
116 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
117 IP: netMap.Self.VPNIP,
118 Bits: 32,
119 }},
120 LocalPort: port,
121 Peers: make([]*tailcfg.Node, 0, len(netMap.Peers)),
122 }
123 for i, peer := range netMap.Peers {
124 c.Peers = append(c.Peers, &tailcfg.Node{
125 ID: tailcfg.NodeID(i + 1),
126 StableID: tailcfg.StableNodeID(fmt.Sprintf("%d", i+1)),
127 Name: fmt.Sprintf("%d", i+1),
128 Key: tailcfg.NodeKey(peer.PublicKey),
129 DiscoKey: tailcfg.DiscoKey(peer.DiscoKey),
130 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
131 IP: peer.VPNIP,
132 Bits: 32,
133 }},
134 AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
135 IP: netMap.Self.VPNIP,
136 Bits: 32,
137 }},
138 Endpoints: []string{peer.IPPort.String()},
139 KeepAlive: true,
140 })
141 }
142 return c
143}
144
145func (e *WireguardEngine) Configure(netMap *types.NetworkMap) error {
146 err := e.wg.Reconfig(
147 genWireguardConf(e.privKey, e.port, netMap),
148 genRouterConf(netMap))
149 if err != nil {
150 return err
151 }
152 e.wg.SetNetworkMap(genTailNetMap(e.privKey, e.port, netMap))
153 e.wg.RequestStatus()
154 return err
155}
156
157func (e *WireguardEngine) DiscoKey() types.DiscoKey {
158 return types.DiscoKey(e.wg.DiscoPublicKey())
159}
160
161func (e *WireguardEngine) DiscoEndpoint() string {
giolekvad12813b2021-05-01 19:58:44 +0400162 return e.DiscoKey().Endpoint()
giolekva3c0e1822021-03-15 00:08:44 +0400163}
164
165func (e *WireguardEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
166 e.wg.Ping(ip, cb)
167}