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