mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 22:15:51 +00:00
69cdc30c6d
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>
198 lines
4.4 KiB
Go
198 lines
4.4 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 (
|
|
"bufio"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"inet.af/netaddr"
|
|
)
|
|
|
|
type ParseError struct {
|
|
why string
|
|
offender string
|
|
}
|
|
|
|
func (e *ParseError) Error() string {
|
|
return fmt.Sprintf("%s: %q", e.why, e.offender)
|
|
}
|
|
|
|
func validateEndpoints(s string) error {
|
|
if s == "" {
|
|
// Otherwise strings.Split of the empty string produces [""].
|
|
return nil
|
|
}
|
|
vals := strings.Split(s, ",")
|
|
for _, val := range vals {
|
|
_, _, err := parseEndpoint(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseEndpoint(s string) (host string, port uint16, err error) {
|
|
i := strings.LastIndexByte(s, ':')
|
|
if i < 0 {
|
|
return "", 0, &ParseError{"Missing port from endpoint", s}
|
|
}
|
|
host, portStr := s[:i], s[i+1:]
|
|
if len(host) < 1 {
|
|
return "", 0, &ParseError{"Invalid endpoint host", host}
|
|
}
|
|
uport, err := strconv.ParseUint(portStr, 10, 16)
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
hostColon := strings.IndexByte(host, ':')
|
|
if host[0] == '[' || host[len(host)-1] == ']' || hostColon > 0 {
|
|
err := &ParseError{"Brackets must contain an IPv6 address", host}
|
|
if len(host) > 3 && host[0] == '[' && host[len(host)-1] == ']' && hostColon > 0 {
|
|
maybeV6 := net.ParseIP(host[1 : len(host)-1])
|
|
if maybeV6 == nil || len(maybeV6) != net.IPv6len {
|
|
return "", 0, err
|
|
}
|
|
} else {
|
|
return "", 0, err
|
|
}
|
|
host = host[1 : len(host)-1]
|
|
}
|
|
return host, uint16(uport), nil
|
|
}
|
|
|
|
func parseKeyHex(s string) (*Key, error) {
|
|
k, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
return nil, &ParseError{"Invalid key: " + err.Error(), s}
|
|
}
|
|
if len(k) != KeySize {
|
|
return nil, &ParseError{"Keys must decode to exactly 32 bytes", s}
|
|
}
|
|
var key Key
|
|
copy(key[:], k)
|
|
return &key, nil
|
|
}
|
|
|
|
// FromUAPI generates a Config from r.
|
|
// r should be generated by calling device.IpcGetOperation;
|
|
// it is not compatible with other uapi streams.
|
|
func FromUAPI(r io.Reader) (*Config, error) {
|
|
cfg := new(Config)
|
|
var peer *Peer // current peer being operated on
|
|
deviceConfig := true
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
parts := strings.Split(line, "=")
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("failed to parse line %q, found %d =-separated parts, want 2", line, len(parts))
|
|
}
|
|
key := parts[0]
|
|
value := parts[1]
|
|
|
|
if key == "public_key" {
|
|
if deviceConfig {
|
|
deviceConfig = false
|
|
}
|
|
// Load/create the peer we are now configuring.
|
|
var err error
|
|
peer, err = cfg.handlePublicKeyLine(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
continue
|
|
}
|
|
|
|
var err error
|
|
if deviceConfig {
|
|
err = cfg.handleDeviceLine(key, value)
|
|
} else {
|
|
err = cfg.handlePeerLine(peer, key, value)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func (cfg *Config) handleDeviceLine(key, value string) error {
|
|
switch key {
|
|
case "private_key":
|
|
k, err := parseKeyHex(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// wireguard-go guarantees not to send zero value; private keys are already clamped.
|
|
cfg.PrivateKey = PrivateKey(*k)
|
|
case "listen_port":
|
|
// ignore
|
|
case "fwmark":
|
|
// ignore
|
|
default:
|
|
return fmt.Errorf("unexpected IpcGetOperation key: %v", key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cfg *Config) handlePublicKeyLine(value string) (*Peer, error) {
|
|
k, err := parseKeyHex(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg.Peers = append(cfg.Peers, Peer{})
|
|
peer := &cfg.Peers[len(cfg.Peers)-1]
|
|
peer.PublicKey = *k
|
|
return peer, nil
|
|
}
|
|
|
|
func (cfg *Config) handlePeerLine(peer *Peer, key, value string) error {
|
|
switch key {
|
|
case "endpoint":
|
|
err := validateEndpoints(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer.Endpoints = value
|
|
case "persistent_keepalive_interval":
|
|
n, err := strconv.ParseUint(value, 10, 16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer.PersistentKeepalive = uint16(n)
|
|
case "allowed_ip":
|
|
ipp, err := netaddr.ParseIPPrefix(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer.AllowedIPs = append(peer.AllowedIPs, ipp)
|
|
case "protocol_version":
|
|
if value != "1" {
|
|
return fmt.Errorf("invalid protocol version: %v", value)
|
|
}
|
|
case "preshared_key", "last_handshake_time_sec", "last_handshake_time_nsec", "tx_bytes", "rx_bytes":
|
|
// ignore
|
|
default:
|
|
return fmt.Errorf("unexpected IpcGetOperation key: %v", key)
|
|
}
|
|
return nil
|
|
}
|