aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Fifield <david@bamsoftware.com>2022-12-11 17:51:09 -0700
committerDavid Fifield <david@bamsoftware.com>2022-12-14 23:02:26 -0700
commit936a1f81382c95950c4659afec1b3fe875c681d9 (patch)
tree2590f7590bd86aa8282752232e86f8c54aa77789
parentc6fabb212d7eff3f58021eb0f806376772c2bd4d (diff)
downloadsnowflake-936a1f81382c95950c4659afec1b3fe875c681d9.tar.gz
snowflake-936a1f81382c95950c4659afec1b3fe875c681d9.zip
Add a num-turbotunnel server transport option.
Replaces the hardcoded numKCPInstances.
-rw-r--r--server/README.md25
-rw-r--r--server/lib/snowflake.go7
-rw-r--r--server/server.go20
3 files changed, 45 insertions, 7 deletions
diff --git a/server/README.md b/server/README.md
index 5ac7102..2b1fb7a 100644
--- a/server/README.md
+++ b/server/README.md
@@ -70,6 +70,26 @@ setcap 'cap_net_bind_service=+ep' /usr/local/bin/snowflake-server
```
+# Multiple KCP state machines
+
+The server internally uses a network protocol called KCP
+to manage and persist client sessions.
+Each KCP scheduler runs on a single thread.
+When there are many simultaneous users (thousands),
+a single KCP scheduler can be a bottleneck.
+The `num-turbotunnel` pluggable transport option
+lets you control the number of KCP instances,
+which can help with CPU scaling:
+https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/40200
+
+There is currently no way to set this option automatically.
+You have to tune it manually.
+
+```
+ServerTransportOptions snowflake num-turbotunnel=2
+```
+
+
# Controlling source addresses
Use the `orport-srcaddr` pluggable transport option to control what source addresses
@@ -83,6 +103,11 @@ Use `ServerTransportOptions` in torrc to set the option:
ServerTransportOptions snowflake orport-srcaddr=127.0.2.0/24
```
+You can combine it with other options:
+```
+ServerTransportOptions snowflake num-turbotunnel=2 orport-srcaddr=127.0.2.0/24
+```
+
Specifying a source address range other than the default 127.0.0.1
can help with conserving localhost ephemeral ports on servers
that receive a lot of connections:
diff --git a/server/lib/snowflake.go b/server/lib/snowflake.go
index 2af5dbb..4078358 100644
--- a/server/lib/snowflake.go
+++ b/server/lib/snowflake.go
@@ -55,11 +55,6 @@ const (
WindowSize = 65535
// StreamSize controls the maximum amount of in flight data between a client and server.
StreamSize = 1048576 //1MB
-
- // numKCPInstances is the number of parallel KCP state machines to run.
- // Clients are assigned to a particular KCP instance by a hash of their
- // ClientID.
- numKCPInstances = 2
)
// Transport is a structure with methods that conform to the Go PT v2.1 API
@@ -76,7 +71,7 @@ func NewSnowflakeServer(getCertificate func(*tls.ClientHelloInfo) (*tls.Certific
// Listen starts a listener on addr that will accept both turbotunnel
// and legacy Snowflake connections.
-func (t *Transport) Listen(addr net.Addr) (*SnowflakeListener, error) {
+func (t *Transport) Listen(addr net.Addr, numKCPInstances int) (*SnowflakeListener, error) {
listener := &SnowflakeListener{
addr: addr,
queue: make(chan net.Conn, 65534),
diff --git a/server/server.go b/server/server.go
index 3d66d35..912fead 100644
--- a/server/server.go
+++ b/server/server.go
@@ -14,6 +14,7 @@ import (
"os"
"os/signal"
"path/filepath"
+ "strconv"
"strings"
"sync"
"syscall"
@@ -280,7 +281,24 @@ func main() {
orPortSrcAddr = ipnet
}
- ln, err := transport.Listen(bindaddr.Addr)
+ numKCPInstances := 1
+ // Are we requested to run a certain number of KCP state
+ // machines?
+ if value, ok := bindaddr.Options.Get("num-turbotunnel"); ok {
+ n, err := strconv.Atoi(value)
+ if err == nil && n < 1 {
+ err = fmt.Errorf("cannot be less than 1")
+ }
+ if err != nil {
+ err = fmt.Errorf("parsing num-turbotunnel: %w", err)
+ log.Println(err)
+ pt.SmethodError(bindaddr.MethodName, err.Error())
+ continue
+ }
+ numKCPInstances = n
+ }
+
+ ln, err := transport.Listen(bindaddr.Addr, numKCPInstances)
if err != nil {
log.Printf("error opening listener: %s", err)
pt.SmethodError(bindaddr.MethodName, err.Error())