forked from tailscale/tailscale
-
Notifications
You must be signed in to change notification settings - Fork 0
/
writer.go
132 lines (118 loc) · 3.38 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wgcfg
import (
"fmt"
"io"
"net/netip"
"strconv"
"github.com/qwenode/tailscale/types/key"
"github.com/qwenode/tailscale/types/logger"
)
// ToUAPI writes cfg in UAPI format to w.
// Prev is the previous device Config.
// Prev is required so that we can remove now-defunct peers
// without having to remove and re-add all peers.
func (cfg *Config) ToUAPI(logf logger.Logf, w io.Writer, prev *Config) error {
var stickyErr error
set := func(key, value string) {
if stickyErr != nil {
return
}
_, err := fmt.Fprintf(w, "%s=%s\n", key, value)
if err != nil {
stickyErr = err
}
}
setUint16 := func(key string, value uint16) {
set(key, strconv.FormatUint(uint64(value), 10))
}
setPeer := func(peer Peer) {
set("public_key", peer.PublicKey.UntypedHexString())
}
// Device config.
if !prev.PrivateKey.Equal(cfg.PrivateKey) {
set("private_key", cfg.PrivateKey.UntypedHexString())
}
old := make(map[key.NodePublic]Peer)
for _, p := range prev.Peers {
old[p.PublicKey] = p
}
// Add/configure all new peers.
for _, p := range cfg.Peers {
oldPeer, wasPresent := old[p.PublicKey]
setPeer(p)
set("protocol_version", "1")
// Avoid setting endpoints if the correct one is already known
// to WireGuard, because doing so generates a bit more work in
// calling magicsock's ParseEndpoint for effectively a no-op.
if oldPeer.WGEndpoint != p.PublicKey {
if wasPresent {
// We had an endpoint, and it was wrong.
// By construction, this should not happen.
// If it does, keep going so that we can recover from it,
// but log so that we know about it,
// because it is an indicator of other failed invariants.
// See corp issue 3016.
logf("[unexpected] endpoint changed from %s to %s", oldPeer.WGEndpoint, p.PublicKey)
}
set("endpoint", p.PublicKey.UntypedHexString())
}
// TODO: replace_allowed_ips is expensive.
// If p.AllowedIPs is a strict superset of oldPeer.AllowedIPs,
// then skip replace_allowed_ips and instead add only
// the new ipps with allowed_ip.
if !cidrsEqual(oldPeer.AllowedIPs, p.AllowedIPs) {
set("replace_allowed_ips", "true")
for _, ipp := range p.AllowedIPs {
set("allowed_ip", ipp.String())
}
}
// Set PersistentKeepalive after the peer is otherwise configured,
// because it can trigger handshake packets.
if oldPeer.PersistentKeepalive != p.PersistentKeepalive {
setUint16("persistent_keepalive_interval", p.PersistentKeepalive)
}
}
// Remove peers that were present but should no longer be.
for _, p := range cfg.Peers {
delete(old, p.PublicKey)
}
for _, p := range old {
setPeer(p)
set("remove", "true")
}
if stickyErr != nil {
stickyErr = fmt.Errorf("ToUAPI: %w", stickyErr)
}
return stickyErr
}
func cidrsEqual(x, y []netip.Prefix) bool {
// TODO: re-implement using netaddr.IPSet.Equal.
if len(x) != len(y) {
return false
}
// First see if they're equal in order, without allocating.
exact := true
for i := range x {
if x[i] != y[i] {
exact = false
break
}
}
if exact {
return true
}
// Otherwise, see if they're the same, but out of order.
m := make(map[netip.Prefix]bool)
for _, v := range x {
m[v] = true
}
for _, v := range y {
if !m[v] {
return false
}
}
return true
}