blob: 3dbf717d79f0a0d4f3dac8075f3b34f24ab45b42 [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
53func genWireguardConf(privKey types.PrivateKey, port uint16, netMap *types.NetworkMap) *wgcfg.Config {
54 c := &wgcfg.Config{
55 Name: "foo",
56 PrivateKey: wgcfg.PrivateKey(privKey),
57 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
58 IP: netMap.Self.VPNIP,
59 Bits: 32, // TODO(giolekva): adapt for IPv6
60 }},
61 ListenPort: port,
62 Peers: make([]wgcfg.Peer, 0, len(netMap.Peers)),
63 }
64 for _, peer := range netMap.Peers {
65 c.Peers = append(c.Peers, wgcfg.Peer{
66 PublicKey: wgcfg.Key(peer.PublicKey),
67 AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
68 IP: peer.VPNIP,
69 Bits: 32,
70 }},
71 Endpoints: peer.DiscoEndpoint,
72 PersistentKeepalive: 15, // TODO(giolekva): make it configurable
73 })
74 }
75 return c
76}
77
78func genRouterConf(netMap *types.NetworkMap) *router.Config {
79 c := &router.Config{
80 LocalAddrs: []netaddr.IPPrefix{netaddr.IPPrefix{
81 IP: netMap.Self.VPNIP,
82 Bits: 32,
83 }},
84 Routes: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
85 }
86 for _, peer := range netMap.Peers {
87 c.Routes = append(c.Routes, netaddr.IPPrefix{
88 IP: peer.VPNIP,
89 Bits: 32,
90 })
91 }
92 return c
93}
94
95func genTailNetMap(privKey types.PrivateKey, port uint16, netMap *types.NetworkMap) *controlclient.NetworkMap {
96 fmt.Println(netMap.Self.IPPort.String())
97 c := &controlclient.NetworkMap{
98 SelfNode: &tailcfg.Node{
99 ID: 0, // TODO(giolekva): maybe IDs should be stored server side.
100 StableID: "0",
101 Name: "0",
102 Key: tailcfg.NodeKey(netMap.Self.PublicKey),
103 DiscoKey: tailcfg.DiscoKey(netMap.Self.DiscoKey),
104 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
105 IP: netMap.Self.VPNIP,
106 Bits: 32,
107 }},
108 AllowedIPs: make([]netaddr.IPPrefix, 0, len(netMap.Peers)),
109 Endpoints: []string{netMap.Self.IPPort.String()},
110 KeepAlive: true, // TODO(giolekva): make it configurable
111 },
112 NodeKey: tailcfg.NodeKey(netMap.Self.PublicKey),
113 PrivateKey: wgkey.Private(privKey),
114 Name: "0",
115 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
116 IP: netMap.Self.VPNIP,
117 Bits: 32,
118 }},
119 LocalPort: port,
120 Peers: make([]*tailcfg.Node, 0, len(netMap.Peers)),
121 }
122 for i, peer := range netMap.Peers {
123 c.Peers = append(c.Peers, &tailcfg.Node{
124 ID: tailcfg.NodeID(i + 1),
125 StableID: tailcfg.StableNodeID(fmt.Sprintf("%d", i+1)),
126 Name: fmt.Sprintf("%d", i+1),
127 Key: tailcfg.NodeKey(peer.PublicKey),
128 DiscoKey: tailcfg.DiscoKey(peer.DiscoKey),
129 Addresses: []netaddr.IPPrefix{netaddr.IPPrefix{
130 IP: peer.VPNIP,
131 Bits: 32,
132 }},
133 AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefix{
134 IP: netMap.Self.VPNIP,
135 Bits: 32,
136 }},
137 Endpoints: []string{peer.IPPort.String()},
138 KeepAlive: true,
139 })
140 }
141 return c
142}
143
144func (e *WireguardEngine) Configure(netMap *types.NetworkMap) error {
145 err := e.wg.Reconfig(
146 genWireguardConf(e.privKey, e.port, netMap),
147 genRouterConf(netMap))
148 if err != nil {
149 return err
150 }
151 e.wg.SetNetworkMap(genTailNetMap(e.privKey, e.port, netMap))
152 e.wg.RequestStatus()
153 return err
154}
155
156func (e *WireguardEngine) DiscoKey() types.DiscoKey {
157 return types.DiscoKey(e.wg.DiscoPublicKey())
158}
159
160func (e *WireguardEngine) DiscoEndpoint() string {
161 k := e.DiscoKey()
162 discoHex := hex.EncodeToString(k[:])
163 return fmt.Sprintf("%s%s", discoHex, controlclient.EndpointDiscoSuffix)
164}
165
166func (e *WireguardEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
167 e.wg.Ping(ip, cb)
168}