mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-05 02:16:27 +00:00
@@ -891,6 +891,10 @@ func (s *Server) debugLogf(format string, v ...any) {
|
|||||||
// run serves the client until there's an error.
|
// run serves the client until there's an error.
|
||||||
// If the client hangs up or the server is closed, run returns nil, otherwise run returns an error.
|
// If the client hangs up or the server is closed, run returns nil, otherwise run returns an error.
|
||||||
func (c *sclient) run(ctx context.Context) error {
|
func (c *sclient) run(ctx context.Context) error {
|
||||||
|
fmt.Println("ZZZZ Client Running")
|
||||||
|
defer func() {
|
||||||
|
fmt.Println("ZZZZ Client Stopped")
|
||||||
|
}()
|
||||||
// Launch sender, but don't return from run until sender goroutine is done.
|
// Launch sender, but don't return from run until sender goroutine is done.
|
||||||
var grp errgroup.Group
|
var grp errgroup.Group
|
||||||
sendCtx, cancelSender := context.WithCancel(ctx)
|
sendCtx, cancelSender := context.WithCancel(ctx)
|
||||||
@@ -912,7 +916,9 @@ func (c *sclient) run(ctx context.Context) error {
|
|||||||
c.startStatsLoop(sendCtx)
|
c.startStatsLoop(sendCtx)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
fmt.Println("ZZZZ reading header")
|
||||||
ft, fl, err := readFrameHeader(c.br)
|
ft, fl, err := readFrameHeader(c.br)
|
||||||
|
fmt.Println("ZZZZ read header")
|
||||||
c.debugLogf("read frame type %d len %d err %v", ft, fl, err)
|
c.debugLogf("read frame type %d len %d err %v", ft, fl, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
@@ -1685,6 +1691,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
|
|||||||
inBatch := -1 // for bufferedWriteFrames
|
inBatch := -1 // for bufferedWriteFrames
|
||||||
for {
|
for {
|
||||||
if werr != nil {
|
if werr != nil {
|
||||||
|
fmt.Printf("ZZZZ send loop ending with werr: %s\n", werr)
|
||||||
return werr
|
return werr
|
||||||
}
|
}
|
||||||
inBatch++
|
inBatch++
|
||||||
@@ -1692,6 +1699,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
|
|||||||
// does as many non-flushing writes as possible.
|
// does as many non-flushing writes as possible.
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
fmt.Println("ZZZZ send loop context done")
|
||||||
return nil
|
return nil
|
||||||
case msg := <-c.peerGone:
|
case msg := <-c.peerGone:
|
||||||
werr = c.sendPeerGone(msg.peer, msg.reason)
|
werr = c.sendPeerGone(msg.peer, msg.reason)
|
||||||
@@ -1717,6 +1725,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
|
|||||||
// Flush any writes from the 3 sends above, or from
|
// Flush any writes from the 3 sends above, or from
|
||||||
// the blocking loop below.
|
// the blocking loop below.
|
||||||
if werr = c.bw.Flush(); werr != nil {
|
if werr = c.bw.Flush(); werr != nil {
|
||||||
|
fmt.Printf("ZZZZ flush failed: %s\n", werr)
|
||||||
return werr
|
return werr
|
||||||
}
|
}
|
||||||
if inBatch != 0 { // the first loop will almost always hit default & be size zero
|
if inBatch != 0 { // the first loop will almost always hit default & be size zero
|
||||||
@@ -1728,6 +1737,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
|
|||||||
// Then a blocking select with same:
|
// Then a blocking select with same:
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
fmt.Println("ZZZZ send loop context done")
|
||||||
return nil
|
return nil
|
||||||
case msg := <-c.peerGone:
|
case msg := <-c.peerGone:
|
||||||
werr = c.sendPeerGone(msg.peer, msg.reason)
|
werr = c.sendPeerGone(msg.peer, msg.reason)
|
||||||
|
@@ -8,27 +8,28 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"expvar"
|
"expvar"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"golang.org/x/net/quic"
|
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
"tailscale.com/disco"
|
"tailscale.com/disco"
|
||||||
"tailscale.com/net/memnet"
|
"tailscale.com/net/memnet"
|
||||||
@@ -1458,42 +1459,26 @@ func BenchmarkSendRecvQUIC(b *testing.B) {
|
|||||||
k := key.NewNode()
|
k := key.NewNode()
|
||||||
clientKey := k.Public()
|
clientKey := k.Public()
|
||||||
|
|
||||||
cfg1, cfg2 := generageQUICConfig(), generageQUICConfig()
|
ln, err := quic.ListenAddr("127.0.0.1:0", generateTLSConfig(), nil)
|
||||||
|
|
||||||
e1, err := quic.Listen("udp", "127.0.0.1:0", cfg1)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
defer e1.Close(context.Background())
|
defer ln.Close()
|
||||||
|
|
||||||
e2, err := quic.Listen("udp", "127.0.0.1:0", cfg2)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer e2.Close(context.Background())
|
|
||||||
|
|
||||||
qconnOut, err := e2.Dial(context.Background(), "udp", e1.LocalAddr().String(), cfg2)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer qconnOut.Close()
|
|
||||||
|
|
||||||
qconnIn, err := e1.Accept(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer qconnIn.Close()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_connIn, err := qconnIn.AcceptStream(context.Background())
|
qconnIn, err := ln.Accept(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
defer _connIn.Close()
|
defer qconnIn.CloseWithError(0, "")
|
||||||
connIn := &connWithAddr{_connIn, qconnIn.LocalAddr()}
|
connIn, err := qconnIn.AcceptStream(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer connIn.Close()
|
||||||
|
|
||||||
// read and discard initial byte
|
// read and discard initial byte
|
||||||
if _, err := connIn.Read(make([]byte, 1)); err != nil {
|
if _, err := connIn.Read(make([]byte, 1)); err != nil {
|
||||||
@@ -1502,19 +1487,28 @@ func BenchmarkSendRecvQUIC(b *testing.B) {
|
|||||||
|
|
||||||
brwServer := bufio.NewReadWriter(bufio.NewReader(connIn), bufio.NewWriter(connIn))
|
brwServer := bufio.NewReadWriter(bufio.NewReader(connIn), bufio.NewWriter(connIn))
|
||||||
|
|
||||||
s.Accept(ctx, connIn, brwServer, "test-client")
|
s.Accept(ctx, &connWithAddr{connIn, qconnIn.LocalAddr()}, brwServer, "test-client")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_connOut, err := qconnOut.NewStream(context.Background())
|
tlsConf := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
// NextProtos: []string{"quic-echo-example"},
|
||||||
|
}
|
||||||
|
qconnOut, err := quic.DialAddr(context.Background(), ln.Addr().String(), tlsConf, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
defer _connOut.Close()
|
defer qconnOut.CloseWithError(0, "")
|
||||||
connOut := &connWithAddr{_connOut, qconnOut.LocalAddr()}
|
|
||||||
|
connOut, err := qconnOut.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer connOut.Close()
|
||||||
|
|
||||||
connOut.Write([]byte{0})
|
connOut.Write([]byte{0})
|
||||||
brw := bufio.NewReadWriter(bufio.NewReader(connOut), bufio.NewWriter(connOut))
|
brw := bufio.NewReadWriter(bufio.NewReader(connOut), bufio.NewWriter(connOut))
|
||||||
client, err := NewClient(k, connOut, brw, logger.Discard)
|
client, err := NewClient(k, &connWithAddr{connOut, qconnOut.LocalAddr()}, brw, logger.Discard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("client: %v", err)
|
b.Fatalf("client: %v", err)
|
||||||
}
|
}
|
||||||
@@ -1527,6 +1521,7 @@ func BenchmarkSendRecvQUIC(b *testing.B) {
|
|||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if err := client.Send(clientKey, msg); err != nil {
|
if err := client.Send(clientKey, msg); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
connOut.Close()
|
connOut.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1541,44 +1536,19 @@ func BenchmarkSendRecvQUIC(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, size := range []int{10, 100, 1000, 10000} {
|
// for _, size := range []int{10, 100, 1000, 10000} {
|
||||||
// for _, size := range []int{10} {
|
for _, size := range []int{10} {
|
||||||
b.Run(fmt.Sprintf("msgsize=%d", size), func(b *testing.B) { benchmarkSendRecvSize(b, size) })
|
b.Run(fmt.Sprintf("msgsize=%d", size), func(b *testing.B) { benchmarkSendRecvSize(b, size) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type connWithAddr struct {
|
type connWithAddr struct {
|
||||||
*quic.Stream
|
quic.Stream
|
||||||
localAddr netip.AddrPort
|
localAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type netaddr netip.AddrPort
|
|
||||||
|
|
||||||
func (a netaddr) Network() string { return "udp" }
|
|
||||||
func (a netaddr) String() string { return a.String() }
|
|
||||||
|
|
||||||
func (c *connWithAddr) Write(p []byte) (int, error) {
|
|
||||||
n, err := c.Stream.Write(p)
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
c.Stream.Flush()
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
func (c *connWithAddr) LocalAddr() net.Addr {
|
func (c *connWithAddr) LocalAddr() net.Addr {
|
||||||
return netaddr(c.localAddr)
|
return c.localAddr
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connWithAddr) SetDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connWithAddr) SetReadDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connWithAddr) SetWriteDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func BenchmarkSendRecvPlain(b *testing.B) {
|
// func BenchmarkSendRecvPlain(b *testing.B) {
|
||||||
@@ -1795,63 +1765,26 @@ func TestServerRepliesToPing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generageQUICConfig() *quic.Config {
|
|
||||||
return &quic.Config{
|
|
||||||
TLSConfig: generateTLSConfig(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a bare-bones TLS config for the server
|
// Setup a bare-bones TLS config for the server
|
||||||
func generateTLSConfig() *tls.Config {
|
func generateTLSConfig() *tls.Config {
|
||||||
return &tls.Config{
|
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
Certificates: []tls.Certificate{testCert},
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
CipherSuites: []uint16{
|
|
||||||
tls.TLS_AES_128_GCM_SHA256,
|
|
||||||
tls.TLS_AES_256_GCM_SHA384,
|
|
||||||
tls.TLS_CHACHA20_POLY1305_SHA256,
|
|
||||||
},
|
|
||||||
MinVersion: tls.VersionTLS13,
|
|
||||||
// Default key exchange mechanisms as of Go 1.23 minus X25519Kyber768Draft00,
|
|
||||||
// which bloats the client hello enough to spill into a second datagram.
|
|
||||||
// Tests were written with the assuption each flight in the handshake
|
|
||||||
// fits in one datagram, and it's simpler to keep that property.
|
|
||||||
CurvePreferences: []tls.CurveID{
|
|
||||||
tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testCert = func() tls.Certificate {
|
|
||||||
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return cert
|
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||||
}()
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||||
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||||
|
|
||||||
// localhostCert is a PEM-encoded TLS cert with SAN IPs
|
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||||
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
if err != nil {
|
||||||
// generated from src/crypto/tls:
|
panic(err)
|
||||||
// go run generate_cert.go --ecdsa-curve P256 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
}
|
||||||
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
return &tls.Config{
|
||||||
MIIBrDCCAVKgAwIBAgIPCvPhO+Hfv+NW76kWxULUMAoGCCqGSM49BAMCMBIxEDAO
|
Certificates: []tls.Certificate{tlsCert},
|
||||||
BgNVBAoTB0FjbWUgQ28wIBcNNzAwMTAxMDAwMDAwWhgPMjA4NDAxMjkxNjAwMDBa
|
// NextProtos: []string{"quic-echo-example"},
|
||||||
MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARh
|
}
|
||||||
WRF8p8X9scgW7JjqAwI9nYV8jtkdhqAXG9gyEgnaFNN5Ze9l3Tp1R9yCDBMNsGms
|
}
|
||||||
PyfMPe5Jrha/LmjgR1G9o4GIMIGFMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAK
|
|
||||||
BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSOJri/wLQxq6oC
|
|
||||||
Y6ZImms/STbTljAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAA
|
|
||||||
AAAAAAAAAAAAATAKBggqhkjOPQQDAgNIADBFAiBUguxsW6TGhixBAdORmVNnkx40
|
|
||||||
HjkKwncMSDbUaeL9jQIhAJwQ8zV9JpQvYpsiDuMmqCuW35XXil3cQ6Drz82c+fvE
|
|
||||||
-----END CERTIFICATE-----`)
|
|
||||||
|
|
||||||
// localhostKey is the private key for localhostCert.
|
|
||||||
var localhostKey = []byte(testingKey(`-----BEGIN TESTING KEY-----
|
|
||||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgY1B1eL/Bbwf/MDcs
|
|
||||||
rnvvWhFNr1aGmJJR59PdCN9lVVqhRANCAARhWRF8p8X9scgW7JjqAwI9nYV8jtkd
|
|
||||||
hqAXG9gyEgnaFNN5Ze9l3Tp1R9yCDBMNsGmsPyfMPe5Jrha/LmjgR1G9
|
|
||||||
-----END TESTING KEY-----`))
|
|
||||||
|
|
||||||
// testingKey helps keep security scanners from getting excited about a private key in this file.
|
|
||||||
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
|
||||||
|
Reference in New Issue
Block a user