-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
writer.go
154 lines (137 loc) · 4.28 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package wgcfg
import (
"fmt"
"io"
"net/netip"
"strconv"
"tailscale.com/types/key"
"tailscale.com/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, and so that we can avoid writing information
// about peers that have not changed since the previous time we wrote our
// Config.
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]
// We only want to write the peer header/version if we're about
// to change something about that peer, or if it's a new peer.
// Figure out up-front whether we'll need to do anything for
// this peer, and skip doing anything if not.
//
// If the peer was not present in the previous config, this
// implies that this is a new peer; set all of these to 'true'
// to ensure that we're writing the full peer configuration.
willSetEndpoint := oldPeer.WGEndpoint != p.PublicKey || !wasPresent
willChangeIPs := !cidrsEqual(oldPeer.AllowedIPs, p.AllowedIPs) || !wasPresent
willChangeKeepalive := oldPeer.PersistentKeepalive != p.PersistentKeepalive // if not wasPresent, no need to redundantly set zero (default)
if !willSetEndpoint && !willChangeIPs && !willChangeKeepalive {
// It's safe to skip doing anything here; wireguard-go
// will not remove a peer if it's unspecified unless we
// tell it to (which we do below if necessary).
continue
}
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 willSetEndpoint {
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 willChangeIPs {
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 willChangeKeepalive {
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
}