tailscale/wgengine/wgcfg/writer.go
Josh Bleecher Snyder 69cdc30c6d wgengine/wgcfg: remove Config.ListenPort
We don't use the port that wireguard-go passes to us (via magicsock.connBind.Open).
We ignore it entirely and use the port we selected.

When we tell wireguard-go that we're changing the listen_port,
it calls connBind.Close and then connBind.Open.
And in the meantime, it stops calling the receive functions,
which means that we stop receiving and processing UDP and DERP packets.
And that is Very Bad.

That was never a problem prior to b3ceca1dd7,
because we passed the SkipBindUpdate flag to our wireguard-go fork,
which told wireguard-go not to re-bind on listen_port changes.
That commit eliminated the SkipBindUpdate flag.

We could write a bunch of code to work around the gap.
We could add background readers that process UDP and DERP packets when wireguard-go isn't.
But it's simpler to never create the conditions in which wireguard-go rebinds.

The other scenario in which wireguard-go re-binds is device.Down.
Conveniently, we never call device.Down. We go from device.Up to device.Close,
and the latter only when we're shutting down a magicsock.Conn completely.

Rubber-ducked-by: Avery Pennarun <apenwarr@tailscale.com>
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2021-04-03 10:32:51 -07:00

139 lines
3.0 KiB
Go

// 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"
"sort"
"strconv"
"strings"
"inet.af/netaddr"
)
// 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(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.HexString())
}
// Device config.
if prev.PrivateKey != cfg.PrivateKey {
set("private_key", cfg.PrivateKey.HexString())
}
old := make(map[Key]Peer)
for _, p := range prev.Peers {
old[p.PublicKey] = p
}
// Add/configure all new peers.
for _, p := range cfg.Peers {
oldPeer := old[p.PublicKey]
setPeer(p)
set("protocol_version", "1")
if !endpointsEqual(oldPeer.Endpoints, p.Endpoints) {
set("endpoint", p.Endpoints)
}
// 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 endpointsEqual(x, y string) bool {
// Cheap comparisons.
if x == y {
return true
}
xs := strings.Split(x, ",")
ys := strings.Split(y, ",")
if len(xs) != len(ys) {
return false
}
// Otherwise, see if they're the same, but out of order.
sort.Strings(xs)
sort.Strings(ys)
x = strings.Join(xs, ",")
y = strings.Join(ys, ",")
return x == y
}
func cidrsEqual(x, y []netaddr.IPPrefix) 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[netaddr.IPPrefix]bool)
for _, v := range x {
m[v] = true
}
for _, v := range y {
if !m[v] {
return false
}
}
return true
}