config

package
v1.3.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: AGPL-3.0 Imports: 1 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClientConfig

type ClientConfig struct {
	RemoteAddr string `toml:"remote_addr"`
	// FallbackAddrs are additional server addresses tried in order whenever the
	// primary cannot be reached (a filtered IP, a blocked port, a CDN edge).
	FallbackAddrs    []string      `toml:"fallback_addrs"`
	Transport        TransportType `toml:"transport"`
	Token            string        `toml:"token"`
	ConnectionPool   int           `toml:"connection_pool"`
	RetryInterval    int           `toml:"retry_interval"`
	Nodelay          bool          `toml:"nodelay"`
	Keepalive        int           `toml:"keepalive_period"`
	LogLevel         string        `toml:"log_level"`
	LogFormat        string        `toml:"log_format"` // "" (text) or "json"
	PPROF            bool          `toml:"pprof"`
	MuxSession       int           `toml:"mux_session"`
	MuxVersion       int           `toml:"mux_version"`
	MaxFrameSize     int           `toml:"mux_framesize"`
	MaxReceiveBuffer int           `toml:"mux_recievebuffer"`
	MaxStreamBuffer  int           `toml:"mux_streambuffer"`
	Sniffer          bool          `toml:"sniffer"`
	WebPort          int           `toml:"web_port"`
	// WebBind is the address the sniffer/monitor page listens on. It has no
	// authentication of any kind and reports the host's CPU, memory, disk and
	// network along with the tunnel's status and per-port traffic, so it
	// defaults to 127.0.0.1 and is reached over an SSH tunnel:
	//   ssh -L 2060:127.0.0.1:2060 root@server
	// Set it to 0.0.0.0 to serve it on every interface as it used to be, or
	// to one address to serve it on a private network only.
	WebBind        string `toml:"web_bind"`
	SnifferLog     string `toml:"sniffer_log"`
	DialTimeout    int    `toml:"dial_timeout"`
	AggressivePool bool   `toml:"aggressive_pool"`
	EdgeIP         string `toml:"edge_ip"`
	// SimpleAuth authorises a wss tunnel by the raw token instead of a proof
	// bound to the TLS session. It exists for one deployment the binding
	// otherwise makes impossible: a TLS-terminating reverse proxy — typically
	// NGINX — in front of the tunnel, which holds a different TLS session from
	// the client so a bound proof can never match. It is off by default because
	// it hands the token to whoever terminates the TLS; turn it on only when a
	// trusted proxy is doing so, and set it on both ends.
	SimpleAuth bool `toml:"simple_auth"`
	SkipOptz   bool `toml:"skip_optz"`
	MSS        int  `toml:"mss"`
	SO_RCVBUF  int  `toml:"so_rcvbuf"`
	SO_SNDBUF  int  `toml:"so_sndbuf"`
	// Proxy routes the connection to the tunnel server through a local or
	// nearby proxy, for a client that cannot open an arbitrary outbound
	// connection itself. One URL: "socks5://127.0.0.1:1080" or
	// "http://user:pass@10.0.0.1:8080". Empty means dial the server directly.
	//
	// It applies only to the connections that reach the server. The dial to the
	// local backend never goes through it — that traffic does not leave the
	// machine, so sending it out and back would be both slower and wrong.
	Proxy string `toml:"proxy"`
	// LocalAddr binds the connections that reach the server to a chosen source
	// address, which on a machine with more than one uplink is what decides
	// which of them the tunnel leaves by. An address on its own is enough; the
	// port is the kernel's to pick. Needs no privilege.
	LocalAddr string `toml:"local_addr"`
	// Interface pins those connections to a named device, for when the source
	// address alone does not settle the route. Linux, and needs CAP_NET_RAW.
	Interface string `toml:"interface"`
	// SOMark stamps an fwmark on their packets, which is what `ip rule` matches
	// on — the way to put the tunnel on a routing table of its own without
	// changing routing for the rest of the machine. Linux, and needs
	// CAP_NET_ADMIN. Zero means none.
	SOMark int `toml:"so_mark"`
	// SOPinTCP restores the old behaviour of pinning SO_RCVBUF/SO_SNDBUF on
	// TCP sockets. Off by default: pinning them stops the kernel auto-tuning
	// the window, which costs a large multiple of the throughput on a fast
	// uplink. The datagram transports set their own buffers regardless.
	SOPinTCP bool `toml:"so_pin_tcp"`
	// ZeroCopy lets the kernel move the bytes of forwarded connections
	// directly between the two sockets, without them passing through this
	// process. It is faster and it is the least proven path here, so it is off
	// by default and turned on per tunnel.
	//
	// Purely local: nothing about it reaches the wire, so the two ends need not
	// agree and it is safe to enable on one side first. It applies only to the
	// plain `tcp` transport on Linux, and only when the tunnel has no bandwidth
	// limit — anything else quietly keeps the buffered path.
	ZeroCopy bool `toml:"zero_copy"`

	Preset string `toml:"preset"`
	// LoadBalance spreads the pool's data connections over every configured
	// address instead of putting them all on the live one. All the addresses
	// must reach the SAME server, since the control channel — and therefore
	// the tunnel's identity — lives on one of them.
	LoadBalance bool `toml:"load_balance"`
	// HealthFailover scores every configured address on a timer and keeps
	// traffic on the healthiest — the multi-exit gaming behaviour. It needs more
	// than one address to do anything, and it overrides LoadBalance, because
	// steering to one best exit is the opposite of spreading across all of them.
	HealthFailover bool `toml:"health_failover"`
	// Embedded so the kcp_* keys sit at the top level of the [client] table
	// alongside every other tuning key.
	KCPConfig
	// Embedded so the pck_* keys sit at the top level too. Only used when
	// transport = "pck".
	PckConfig
}

ClientConfig represents the configuration for the client.

type Config

type Config struct {
	Server ServerConfig `toml:"server"`
	Client ClientConfig `toml:"client"`
	// L3 is a direct layer-3 tunnel, and is present only in a configuration
	// that asks for one. It shares nothing with Server and Client: a file
	// without an [l3] table leaves this zero, L3.Enabled() reads false, and
	// the reverse tunnel runs exactly as it always has. See config/l3.go.
	L3 L3Config `toml:"l3"`
	// Direct is a direct layer-4 tunnel — the same forwarded ports, dialled
	// the other way round. Present only in a configuration that asks for one,
	// on the same terms as L3 above. See config/direct.go.
	Direct DirectConfig `toml:"direct"`
}

Config represents the complete configuration, including both server and client settings.

type DirectConfig

type DirectConfig struct {
	// Role is "iran" or "kharej". Empty means there is no direct tunnel in
	// this configuration, which is how every pre-existing config reads.
	//
	// The names are geographic because that is what stays true across both
	// directions: Iran exposes the ports either way, and only who dials
	// changes. "edge" and "origin" are accepted as synonyms, which is what the
	// engine calls them internally.
	Role string `toml:"role"`

	// Addr is the kharej server's host:port on the Iran side, or the address
	// to bind on the kharej side.
	Addr string `toml:"addr"`

	// Token is the shared secret. It never travels on the wire: each end
	// proves it holds the token with an HMAC over two fresh nonces.
	Token string `toml:"token"`

	// Transport is "tcp" (plain, for a payload that is already TLS),
	// "stealth" (the Noise record layer, with no handshake or fingerprint for
	// inspection to match), or "ws"/"wss" (an HTTP upgrade in front of the
	// stream, which is what a CDN will proxy). Empty means "tcp".
	Transport string `toml:"transport"`

	// ServerName is the name the Iran side presents in SNI and the Host
	// header on ws and wss — the domain in front of a CDN. Empty uses the host
	// of Addr and, on wss, skips certificate verification: the tunnel
	// authenticates on the token rather than the certificate, so a
	// self-signed origin is the expected case. Setting it turns verification
	// on.
	ServerName string `toml:"server_name"`

	// TLSCertFile and TLSKeyFile are the kharej side's certificate for wss.
	// Both empty generates a self-signed one, which is what a direct
	// connection to an IP address wants.
	TLSCertFile string `toml:"tls_cert"`
	TLSKeyFile  string `toml:"tls_key"`

	// ACMEDomain switches the kharej side to a Let's Encrypt certificate for
	// that domain instead of a generated one. The domain must resolve to it.
	ACMEDomain string `toml:"acme_domain"`
	ACMEEmail  string `toml:"acme_email"`

	// Ports are the forwarded port mappings, served on the Iran side, in the
	// same syntax the reverse tunnel uses. A target with no host of its own
	// means the loopback of the kharej machine, where the real service
	// listens. The kharej side needs none: every target arrives on the stream
	// that asks for it.
	Ports []string `toml:"ports"`

	// AcceptUDP forwards UDP as well as TCP on those ports. Off unless set,
	// matching the reverse tunnel: a tunnel should not silently start carrying
	// every QUIC flow on port 443.
	AcceptUDP bool `toml:"accept_udp"`

	// MaxConnections caps how many forwarded connections may be open at once
	// (0 = unlimited), and BandwidthMbps caps total throughput in Mbit/s
	// (0 = unlimited). Both are enforced on the Iran side, where the users
	// arrive, and both are off unless set.
	MaxConnections int `toml:"max_connections"`
	BandwidthMbps  int `toml:"bandwidth_mbps"`

	// Preset is the name of the tuning profile the keys below came from. It is
	// a label: the engine reads the individual values, not this. It exists so
	// a management screen can say "Turbo" rather than reciting four numbers.
	Preset string `toml:"preset"`

	// Sessions is how many mux sessions the Iran side keeps open, with new
	// connections spread across them. One is enough for most traffic; more
	// helps where a single connection is being shaped.
	Sessions int `toml:"sessions"`

	// DialTimeout bounds a dial, in seconds. Zero takes the default.
	DialTimeout int `toml:"dial_timeout"`

	// RetryInterval is how long to wait before redialling a dropped session,
	// in seconds. Zero takes the default.
	RetryInterval int `toml:"retry_interval"`

	// Keepalive is the TCP keepalive period on the tunnel connection, in
	// seconds.
	Keepalive int `toml:"keepalive_period"`

	// Nodelay disables Nagle on the tunnel connection.
	Nodelay bool `toml:"nodelay"`

	// MSS clamps the largest TCP payload this end puts in one segment on the
	// tunnel connection. Zero — the default — leaves the decision to the
	// kernel.
	//
	// It is the same key, and the same fix, the reverse tunnel takes: where a
	// path carries less than a full-sized packet and drops the oversized ones
	// without an ICMP reply, nothing on either machine learns. The handshake
	// and the mux's keepalives are small enough to arrive, so the tunnel comes
	// up and looks healthy while every real transfer stalls on the first full
	// segment — and because the socket stays ESTABLISHED throughout, the
	// watchdog sees nothing wrong either.
	//
	// Direct was the only one of the three tunnel kinds with no way to set
	// this. The reverse tunnel has had it since the same failure was diagnosed
	// there, and [l3] measures the path itself.
	//
	// It has to be set at both ends: each end clamps only what it sends.
	MSS int `toml:"mss"`

	// MuxVersion, MaxFrameSize, MaxReceiveBuffer and MaxStreamBuffer tune the
	// mux session. They are the same keys the reverse mux transports take.
	MuxVersion       int `toml:"mux_version"`
	MaxFrameSize     int `toml:"mux_framesize"`
	MaxReceiveBuffer int `toml:"mux_recievebuffer"`
	MaxStreamBuffer  int `toml:"mux_streambuffer"`
}

DirectConfig is a direct tunnel: the same forwarded ports the reverse tunnel serves, with the tunnel dialled the other way round.

In the reverse tunnel the Iran server listens and kharej dials in. Where that inbound connection cannot be made — a provider that filters connections arriving from abroad, a port blocked in one direction only — the tunnel cannot come up at all, even though the user-facing ports on Iran are fine. Here Iran dials out instead, which is the ordinary direction and the one a filter is least likely to touch. The ports do not move: Iran still exposes them and kharej still holds the real service.

Like [l3], this lives in its own table and shares nothing with [server] and [client]. A file without a [direct] table cannot reach this code, and one with it never reaches the reverse engine.

# Iran — dials out, needs no inbound port of its own
[direct]
role  = "iran"
addr  = "KHAREJ_IP:8443"
token = "SAME_LONG_TOKEN"
ports = ["443", "2053-2060"]

# Kharej — listens
[direct]
role  = "kharej"
addr  = "0.0.0.0:8443"
token = "SAME_LONG_TOKEN"

func (DirectConfig) Enabled

func (d DirectConfig) Enabled() bool

Enabled reports whether this configuration describes a direct tunnel. It is the single gate between this engine and the others: false — what every configuration written before this existed returns — means nothing in the direct path is reachable.

func (DirectConfig) ResolvedRole

func (d DirectConfig) ResolvedRole() string

ResolvedRole maps what an operator writes onto what the engine calls it. Geography is the user-facing name because it does not change with the direction; edge and origin are what the code uses, and are accepted here so a config written either way works.

type KCPConfig

type KCPConfig struct {
	MTU          int  `toml:"kcp_mtu"`
	Interval     int  `toml:"kcp_interval"`
	Resend       int  `toml:"kcp_resend"`
	NoDelay      int  `toml:"kcp_nodelay"`
	NoCongestion int  `toml:"kcp_nocongestion"`
	SndWnd       int  `toml:"kcp_sndwnd"`
	RcvWnd       int  `toml:"kcp_rcvwnd"`
	AckNoDelay   bool `toml:"kcp_acknodelay"`
	// DataShards/ParityShards enable forward error correction: for every
	// DataShards packets, ParityShards extra packets are sent so that many
	// losses are repaired instantly instead of waiting for a retransmit.
	DataShards   int `toml:"kcp_datashards"`
	ParityShards int `toml:"kcp_parityshards"`
}

KCPConfig holds the tuning of the KCP transport: a reliable, retransmitting protocol carried inside UDP datagrams. Every field is filled from the chosen performance preset, so a config never has to be edited by hand.

func (KCPConfig) WithDefaults

func (k KCPConfig) WithDefaults() KCPConfig

WithDefaults returns a copy with any unset field filled in, so a config written by an older version — or by hand — can never produce a KCP session with a zero window or a zero tick interval.

type L3Config

type L3Config struct {
	// Mode is "dial" or "listen". Empty means there is no layer-3 tunnel in
	// this configuration, which is how every pre-existing config reads.
	Mode string `toml:"mode"`

	// Addr is the peer's host:port when dialling, or the address to bind when
	// listening.
	Addr string `toml:"addr"`

	// Token is the shared secret. It is the only credential: the handshake
	// derives its pre-shared key from it, and a peer without it is answered
	// with silence.
	Token string `toml:"token"`

	// Carrier is the datagram transport underneath: "udp" (the default, and
	// the right choice on a path that does not interfere), "pck" (raw TCP
	// segments, so a capture sees an ordinary flow), "quic" (a real QUIC
	// session, so a capture sees HTTP/3), "sni" (pck, plus a TLS ClientHello
	// naming an allowed domain at the start of the flow), "xdi" (inside ICMP
	// echo) or "spoof" (raw IP with a forged source). All but udp and quic are
	// Linux-only and need CAP_NET_RAW.
	//
	// A reliable carrier is not an option here — see the l3 package doc for
	// why stacking retransmission is actively harmful rather than merely
	// wasteful.
	Carrier string `toml:"carrier"`

	// Encap is "ipip" (the default, and free) or "gre" (four bytes, or eight
	// with a key). One tunnel carries both IPv4 and IPv6 either way.
	Encap string `toml:"encap"`

	// GREKey is the RFC 2890 key, letting more than one logical tunnel share
	// a carrier. Zero omits the field. Ignored unless encap is "gre".
	GREKey uint32 `toml:"gre_key"`

	// SNIDomain is the server name the "sni" carrier puts in that hello. Empty
	// uses the built-in default. It has to be a domain the path already lets
	// through — which one that is depends on the route, so it is the operator's
	// to choose and to test.
	SNIDomain string `toml:"sni_domain"`

	// Iface is the interface to create. Empty makes "bp0".
	Iface string `toml:"iface"`

	// LocalIP is this end's address on the tunnel, normally with a prefix:
	// "10.10.0.1/30". A bare address requires PeerIP and makes a
	// point-to-point link instead.
	LocalIP string `toml:"local_ip"`

	// PeerIP is the other end's address on the tunnel.
	PeerIP string `toml:"peer_ip"`

	// MTU is the interface MTU. Zero takes a deliberately conservative
	// default, because a layer-3 tunnel whose packets are slightly too large
	// does not fail loudly — it passes small flows and stalls large ones.
	MTU int `toml:"mtu"`

	// SockBuf sizes the carrier's socket buffers in bytes. Zero takes the
	// carrier default of 4 MiB.
	SockBuf int `toml:"sockbuf"`

	// FECData and FECParity add forward error correction to the carrier: for
	// every FECData datagrams, FECParity extra ones are sent, and any FECParity
	// of the group may be lost without losing anything. Both zero — the default
	// — is no error correction.
	//
	// It is redundancy, not reliability: nothing is retransmitted and nothing
	// waits for a timer, which is what makes it safe here where a reliable
	// carrier is not (see the l3 package doc). It costs the parity's share of
	// the bandwidth, so it earns its keep on a path that drops packets steadily
	// and wastes it on one that does not.
	//
	// Both ends must set the same pair: the scheme is not negotiated, and a
	// receiver expecting a different one cannot rebuild anything.
	FECData   int `toml:"fec_data"`
	FECParity int `toml:"fec_parity"`

	// Paths spreads the udp carrier over this many sockets, on consecutive
	// ports starting at the one in Addr. One (or zero) is a single socket.
	//
	// It is for a path that rate-limits per flow: one socket is one flow and
	// gets one allowance however much headroom the link has. Both ends must set
	// the same number, and the extra ports must be open. The obfuscated
	// carriers do not take it — they already vary their source per packet.
	Paths int `toml:"paths"`

	// Preset is the name of the tuning profile the two keys below came from.
	// It is a label: the engine reads the values, not this.
	Preset string `toml:"preset"`

	// TxQueueLen is how many packets the kernel may hold for the interface
	// while the tunnel drains it. Deeper is not better — a queue is latency,
	// and a full one is the jitter. Zero takes the default.
	TxQueueLen int `toml:"txqueuelen"`

	// Qdisc is the queueing discipline on the interface, and is the setting
	// that actually decides jitter. Empty takes fq_codel, which drops when
	// packets start waiting instead of letting the queue grow into delay.
	Qdisc string `toml:"qdisc"`

	// AutoMTU measures what the path really carries, once the tunnel is up, and
	// sets the interface to match.
	//
	// The MTU is the one setting here that cannot be derived from anything in
	// this file, and the one that fails worst when it is wrong: too high and
	// the tunnel comes up, answers every health check, carries ping and SSH,
	// and stalls every download and every TLS handshake. The packets that
	// matter are the large ones, and they are dropped out on the path with
	// nothing coming back to say so.
	//
	// On by default. A peer too old to answer a probe leaves the configured
	// figure untouched, so turning it on cannot break an existing pair.
	AutoMTU *bool `toml:"auto_mtu"`

	// MSSClamp caps the TCP segment size of connections crossing the tunnel,
	// which is the fix for the failure a layer-3 tunnel produces most often and
	// advertises least: ping works, pages load, and every large transfer stalls
	// forever, because both endpoints negotiated a segment from their own
	// 1500-byte interfaces and the ICMP message that would have corrected them
	// was dropped somewhere on the way.
	//
	// Zero — the default — derives it from the MTU, which is right for almost
	// every tunnel. A positive value sets it explicitly. -1 turns it off, for a
	// host whose firewall is managed elsewhere.
	MSSClamp int `toml:"mss_clamp"`

	// Ports are forwarded port mappings served over the tunnel, in the same
	// syntax as the reverse tunnel's: "443", "443=8443", "10000-10009", and
	// "443=10.0.0.1:80|10.0.0.2:80" for several backends. A target with no
	// host of its own means PeerIP, which is what almost every mapping wants.
	//
	// Empty is the plain layer-3 case: the tunnel carries whatever the kernel
	// routes into the interface and forwards no ports of its own.
	Ports []string `toml:"ports"`

	// AcceptUDP forwards UDP as well as TCP on the mapped ports. Off unless
	// set, matching the reverse tunnel's default and for the same reason: a
	// tunnel should not silently start carrying every QUIC flow on port 443.
	AcceptUDP bool `toml:"accept_udp"`

	// MaxConnections and BandwidthMbps cap the forwarded ports above
	// (0 = unlimited each). They do not apply to routed traffic: the tunnel
	// carries whatever the kernel puts into the interface, which has no
	// connections to count.
	MaxConnections int `toml:"max_connections"`
	BandwidthMbps  int `toml:"bandwidth_mbps"`

	// Embedded so the spoof_* keys sit at the top level of the [l3] table.
	// This is the only table they are read from, and they are read only when
	// carrier = "spoof".
	SpoofConfig

	// Embedded the same way for the pck_* keys, read only when
	// carrier = "pck".
	PckConfig
}

L3Config is a direct layer-3 tunnel: an interface on each host carrying whole IP packets between them, rather than a set of forwarded ports.

It lives in its own [l3] table, entirely apart from [server] and [client]. That separation is deliberate and load-bearing: a configuration that does not mention [l3] cannot reach the layer-3 engine, and one that does never reaches the reverse tunnel. There is no key the two share and no state they pass between them, so nothing about this can perturb a working reverse tunnel.

A minimal pair, with the Iran side dialling out:

# Iran
[l3]
mode     = "dial"
addr     = "KHAREJ_IP:9000"
token    = "SAME_LONG_TOKEN"
local_ip = "10.10.0.1/30"
peer_ip  = "10.10.0.2"

# Kharej
[l3]
mode     = "listen"
addr     = "0.0.0.0:9000"
token    = "SAME_LONG_TOKEN"
local_ip = "10.10.0.2/30"
peer_ip  = "10.10.0.1"

Both ends must agree on the token, the encapsulation and the carrier. Which end dials is the direct/reverse question at this layer, and it is free: whichever host can accept an inbound connection listens, and the other dials.

func (L3Config) AutoMTUEnabled

func (l L3Config) AutoMTUEnabled() bool

AutoMTUEnabled reports whether the path should be measured. A pointer in the struct and a method here, because the answer for a key that is absent is "yes" — and a plain bool cannot tell an absent key from an explicit false.

func (L3Config) Enabled

func (l L3Config) Enabled() bool

Enabled reports whether this configuration describes a layer-3 tunnel. It is the single gate between the two engines: false — which is what every configuration written before this existed returns — means nothing in the layer-3 path is reachable.

type PckConfig

type PckConfig struct {
	// PckInterface pins the carrier to a named egress device. Empty — the
	// normal case — lets the route to the peer choose, which is right on every
	// host with one uplink and on most with several.
	PckInterface string `toml:"pck_interface"`
	// PckGatewayMAC is the next hop's hardware address, used when frames are
	// injected at the link layer. Empty means read it from the kernel's
	// neighbour table, which is where it already is. Set it only where that
	// lookup is wrong — some virtualised networks answer ARP with an address
	// the hypervisor then rewrites.
	PckGatewayMAC string `toml:"pck_gateway_mac"`
	// PckFlags is the cycle of TCP flag combinations stamped on outgoing
	// segments, one per packet, spelled as in tcpdump: ["PA"] is push+ack, the
	// flags bulk data carries and the default. A longer cycle varies the
	// pattern for a path that matches on it. Both ends may differ — each side
	// only decides what it sends.
	PckFlags []string `toml:"pck_flags"`
}

PckConfig holds the packet-level TCP carrier's settings. Every field is optional: the transport works out its own egress from the route to the peer, and the defaults are what an ordinary data-carrying connection looks like. They exist to correct a wrong guess on an unusual host, not to be filled in.

type ServerConfig

type ServerConfig struct {
	BindAddr         string        `toml:"bind_addr"`
	Transport        TransportType `toml:"transport"`
	Token            string        `toml:"token"`
	Nodelay          bool          `toml:"nodelay"`
	Keepalive        int           `toml:"keepalive_period"`
	ChannelSize      int           `toml:"channel_size"`
	LogLevel         string        `toml:"log_level"`
	LogFormat        string        `toml:"log_format"` // "" (text) or "json"
	Ports            []string      `toml:"ports"`
	PPROF            bool          `toml:"pprof"`
	MuxSession       int           `toml:"mux_session"`
	MuxVersion       int           `toml:"mux_version"`
	MaxFrameSize     int           `toml:"mux_framesize"`
	MaxReceiveBuffer int           `toml:"mux_recievebuffer"`
	MaxStreamBuffer  int           `toml:"mux_streambuffer"`
	Sniffer          bool          `toml:"sniffer"`
	WebPort          int           `toml:"web_port"`
	// WebBind is the address the sniffer/monitor page listens on. It has no
	// authentication of any kind and reports the host's CPU, memory, disk and
	// network along with the tunnel's status and per-port traffic, so it
	// defaults to 127.0.0.1 and is reached over an SSH tunnel:
	//   ssh -L 2060:127.0.0.1:2060 root@server
	// Set it to 0.0.0.0 to serve it on every interface as it used to be, or
	// to one address to serve it on a private network only.
	WebBind     string `toml:"web_bind"`
	SnifferLog  string `toml:"sniffer_log"`
	TLSCertFile string `toml:"tls_cert"`
	TLSKeyFile  string `toml:"tls_key"`
	// ACMEDomain switches wss/wssmux to a Let's Encrypt certificate for this
	// domain instead of the generated self-signed one. The domain must resolve
	// to this server. Empty keeps the self-signed certificate.
	ACMEDomain string `toml:"acme_domain"`
	ACMEEmail  string `toml:"acme_email"`
	// SimpleAuth authorises a wss tunnel by the raw token instead of a proof
	// bound to the TLS session. It exists for one deployment the binding
	// otherwise makes impossible: a TLS-terminating reverse proxy — typically
	// NGINX — in front of the tunnel, which holds a different TLS session from
	// the client so a bound proof can never match. It is off by default because
	// it hands the token to whoever terminates the TLS; turn it on only when a
	// trusted proxy is doing so, and set it on both ends.
	SimpleAuth bool `toml:"simple_auth"`
	Heartbeat  int  `toml:"heartbeat"`
	MuxCon     int  `toml:"mux_con"`
	// AcceptUDP turns UDP forwarding on for the exposed ports. It is off unless
	// set: a forwarded port carries TCP only until the operator asks for UDP as
	// well. The pointer distinguishes "not set" (off) from an explicit
	// accept_udp = true, so a config with no line does not forward UDP.
	AcceptUDP *bool `toml:"accept_udp"`
	SkipOptz  bool  `toml:"skip_optz"`
	MSS       int   `toml:"mss"`
	SO_RCVBUF int   `toml:"so_rcvbuf"`
	SO_SNDBUF int   `toml:"so_sndbuf"`
	// SOPinTCP restores the old behaviour of pinning SO_RCVBUF/SO_SNDBUF on
	// TCP sockets. Off by default: pinning them stops the kernel auto-tuning
	// the window, which costs a large multiple of the throughput on a fast
	// uplink. The datagram transports set their own buffers regardless.
	SOPinTCP bool `toml:"so_pin_tcp"`
	// ZeroCopy lets the kernel move the bytes of forwarded connections
	// directly between the two sockets, without them passing through this
	// process. It is faster and it is the least proven path here, so it is off
	// by default and turned on per tunnel.
	//
	// Purely local: nothing about it reaches the wire, so the two ends need not
	// agree and it is safe to enable on one side first. It applies only to the
	// plain `tcp` transport on Linux, and only when the tunnel has no bandwidth
	// limit — anything else quietly keeps the buffered path.
	ZeroCopy bool `toml:"zero_copy"`

	ProxyProtocol bool `toml:"proxy_protocol"`
	// MaxConnections caps simultaneous forwarded connections (0 = unlimited).
	MaxConnections int `toml:"max_connections"`
	// BandwidthMbps caps total tunnel throughput in Mbit/s (0 = unlimited).
	BandwidthMbps int    `toml:"bandwidth_mbps"`
	Preset        string `toml:"preset"`
	// Embedded so the kcp_* keys sit at the top level of the [server] table
	// alongside every other tuning key.
	KCPConfig
	// Embedded so the pck_* keys sit at the top level too. Only used when
	// transport = "pck".
	PckConfig
}

ServerConfig represents the configuration for the server.

func (ServerConfig) ForwardsUDP

func (s ServerConfig) ForwardsUDP() bool

ForwardsUDP reports whether the forwarded ports should carry UDP as well as TCP. It is off unless the operator turns it on.

It was briefly the other way — on unless turned off — so that Xray and Shadowsocks UDP would work without a hidden switch. That default did more harm than the problem it solved: a browser's QUIC is UDP on port 443, so every web tunnel silently began carrying every QUIC flow, and on the connection-pooled transports (ws/wss and the mux family) those long-lived flows each hold a pooled connection for as long as the browser keeps them — which starves the TCP forwards sharing the pool. The visible symptom was a site half-loading (images stalled while audio played) that a restart fixed for a while. So the default is back to off: the tunnels that genuinely need UDP — a VPN, a game, an Xray inbound — turn it on, and a plain web or proxy tunnel is not made to carry traffic nobody asked it to.

type SpoofConfig

type SpoofConfig struct {
	// SpoofProfile is the L4 shim wrapped around each datagram, which decides
	// what the packet looks like to inspection: "udp" (default), "icmp" (looks
	// like ping) or "tcp" (looks like a TCP flow; the receiving side auto-manages
	// an iptables rule to drop the kernel's RSTs). It sets BOTH directions unless
	// SpoofUplink/SpoofDownlink override them.
	SpoofProfile string `toml:"spoof_profile"`
	// SpoofUplink and SpoofDownlink set the profile per direction, for a path
	// whose filtering is not symmetric — e.g. ICMP survives client→server while
	// UDP survives server→client. Uplink is client→server, downlink is
	// server→client; both ends must set the same pair. Empty falls back to
	// SpoofProfile, which is the symmetric case.
	SpoofUplink   string `toml:"spoof_uplink"`
	SpoofDownlink string `toml:"spoof_downlink"`
	// SpoofSrcIP is the forged source address stamped on every outgoing packet.
	// Empty leaves the host's real source in place, which spoofs nothing.
	SpoofSrcIP string `toml:"spoof_src_ip"`
	// SpoofSrcPool is an optional list of forged sources to rotate through: each
	// time the carrier (re)connects it picks one, so the tunnel is not pinned to
	// a single address a firewall might rate-limit or block. SpoofSrcIP, if set,
	// is always a member. Empty means use SpoofSrcIP alone.
	SpoofSrcPool []string `toml:"spoof_src_pool"`
	// SpoofPeerIP is the peer's REAL IPv4 address — where the forged packets are
	// actually routed. On the server it is REQUIRED: because the client forges
	// its source, the server cannot learn where to send replies from the packets
	// themselves and must be told the client's real address. On the client it is
	// optional and defaults to the host of RemoteAddr.
	SpoofPeerIP string `toml:"spoof_peer_ip"`
	// SpoofDstIP is a forged destination written only into the cosmetic L4 shim
	// of the profiles that carry one; the packet is still routed to the real
	// peer. Empty mirrors SpoofSrcIP. Ignored by the udp profile.
	SpoofDstIP string `toml:"spoof_dst_ip"`
	// SpoofInterface pins the raw socket to a named egress device (e.g. "eth0"),
	// for a multi-homed host where the forged source would otherwise pick the
	// wrong link. Empty lets the kernel route by the real destination.
	SpoofInterface string `toml:"spoof_interface"`
	// SpoofXDPInterface, when set to a NIC name (e.g. "eth0"), attaches an XDP/eBPF
	// program to that device to receive the tunnel's forged-source packets in the
	// kernel fast path, before the normal socket stack — higher throughput and
	// lower CPU under load than the default raw-socket receive. Pure Go (no clang
	// or libbpf), opt-in, and best-effort: if the kernel is too old or the attach
	// or verifier fails, the carrier logs it and silently falls back to the
	// ordinary raw/UDP receive, so a working tunnel is never lost to it. Empty
	// disables it. Linux only; needs CAP_BPF/CAP_NET_ADMIN in addition to the
	// carrier's CAP_NET_RAW.
	SpoofXDPInterface string `toml:"spoof_xdp_interface"`
	// SpoofSockBuf sizes the send and receive socket buffers (SO_SNDBUF /
	// SO_RCVBUF) of the raw and UDP sockets the carrier owns, in bytes. A large
	// buffer is what lets the forged-source flow reach real bandwidth: under a
	// burst the kernel parks packets here instead of dropping them before the
	// read loop drains them, which is exactly what throttled the throughput
	// before. 0 uses the carrier default (4 MiB), matching the reference
	// spoof-tunnel; raise it on a fat, high-latency path.
	SpoofSockBuf int `toml:"spoof_sockbuf"`
	// SpoofPeerSrcIP pins the forged source the peer stamps on its packets, so
	// anything arriving with a different source is dropped before the encryption
	// ever looks at it. Empty accepts any source and leaves the demux to the port
	// and the encryption, which is safe but noisier. Set it to the peer's
	// spoof_src_ip for a tighter, cheaper receive path.
	SpoofPeerSrcIP string `toml:"spoof_peer_src_ip"`
	// SpoofICMPReply makes an icmp/icmpv6 tunnel look like a real ping exchange:
	// the client sends Echo Requests and the server answers with Echo Replies,
	// instead of both ends sending Requests. Purely cosmetic camouflage; both
	// ends must set it the same. Ignored by the udp/tcp profiles.
	SpoofICMPReply bool `toml:"spoof_icmp_reply"`
	// SpoofMTU is the largest IP packet the carrier emits before it fragments in
	// userspace; a datagram whose headers push it over this is split into IP
	// fragments the peer's kernel reassembles. 0 uses 1500. Lower it on a path
	// with a smaller MTU so oversize packets fragment cleanly rather than being
	// dropped.
	SpoofMTU int `toml:"spoof_mtu"`

	// SpoofTTLJitter varies the IP TTL per packet across a pool of realistic OS
	// defaults {64,128,255} instead of a fixed 64, to blur TTL-based fingerprints.
	SpoofTTLJitter bool `toml:"spoof_ttl_jitter"`
	// SpoofRandomDSCP varies the IP DSCP/ToS byte per packet across plausible
	// values instead of leaving it 0.
	SpoofRandomDSCP bool `toml:"spoof_random_dscp"`
	// SpoofShufflePort randomises the L4 SOURCE port per packet (udp/tcp) within
	// [SpoofPortMin,SpoofPortMax], so the flow does not sit on one source port.
	// The destination port stays fixed, so the receiver's demux is unaffected.
	SpoofShufflePort bool `toml:"spoof_shuffle_port"`
	SpoofPortMin     int  `toml:"spoof_port_min"`
	SpoofPortMax     int  `toml:"spoof_port_max"`
	// SpoofPadding appends 1..SpoofPaddingMax random bytes to every payload
	// (self-describing, so the receiver strips them), defeating size fingerprints.
	// Both ends must set it the same.
	SpoofPadding    bool `toml:"spoof_padding"`
	SpoofPaddingMax int  `toml:"spoof_padding_max"`
	// SpoofFakeTLS prepends a fake TLS 1.2 record header to each TCP segment, so a
	// middlebox reads it as TLS. TCP profile only; both ends must agree.
	SpoofFakeTLS bool `toml:"spoof_fake_tls"`
}

SpoofConfig holds the IP-spoofing carrier's settings, embedded in L3Config so the spoof_* keys sit at the top level of the [l3] table. It only takes effect when carrier = "spoof"; every field is ignored otherwise.

It used to be embedded in [server] and [client] as well, when spoofing was a reverse transport. It is not any more — see checkSpoof in cmd/defaults.go for why a reverse tunnel over this carrier could never work — and [l3] is the only table these keys are read from.

The carrier forges the source address of the raw packets it sends. Routing still uses the real peer — the server's bind address, the client's remote address — so the packet actually arrives; only the source in the on-wire header is replaced with SpoofSrcIP. The two ends must agree on the profile and, where it matters, on the spoofed addresses. Relay mode — a bare datagram relay to a local UDP socket rather than a tunnel — was a shape of the reverse spoof transport and went with it. The direct tunnel carries a whole private network, which is what the relay was reached for: an inner transport that brings its own reliability (WireGuard, most often) is routed over the tunnel rather than piped through it. Kept as a note because "where did spoof_pipe go" is a question the removal invites, and the file that answered it had nothing else left in it.

type TransportType

type TransportType string

TransportType defines the type of transport.

const (
	TCP    TransportType = "tcp"
	TCPMUX TransportType = "tcpmux"
	WS     TransportType = "ws"
	WSS    TransportType = "wss"
	WSMUX  TransportType = "wsmux"
	WSSMUX TransportType = "wssmux"
	UDP    TransportType = "udp"
	KCP    TransportType = "kcp"
	// QUIC carries the tunnel inside QUIC streams over UDP. Like KCP it survives
	// paths where a long-lived TCP flow stalls, but it brings its own TLS 1.3,
	// stream multiplexing, congestion control and loss recovery — so there is
	// nothing to hand-tune and every byte is encrypted. The certificate is a
	// throwaway self-signed one; the tunnel token is the shared secret.
	QUIC TransportType = "quic"
	// STEALTH is a TCP tunnel wrapped in a Noise (NNpsk0) record layer. It has
	// no TLS fingerprint and no recognisable handshake — on the wire it is
	// indistinguishable from random — so deep packet inspection has nothing to
	// match. The pre-shared key is derived from the tunnel token.
	STEALTH TransportType = "stealth"
	// XDI carries the KCP transport inside ICMP echo instead of UDP —
	// experimental. It is for the network that filters UDP and TCP but not
	// ICMP, where the tunnel rides in ping packets. Linux only, and needs a raw
	// socket. Everything above the packet layer is identical to KCP.
	XDI TransportType = "xdi"
	// SPOOF carries the KCP transport inside raw IPv4 packets whose source
	// address is forged — experimental "IP Spoofing". It is for a path that
	// filters on the real flow's source, or that only lets a particular address
	// pair through: the datagrams route to the real peer as normal, but on the
	// wire they appear to come from spoof_src_ip. Like xdi it is KCP over a
	// hand-built raw socket, so encryption, error correction and the whole
	// tunnel stack sit on top unchanged. Linux only, needs a raw socket, and
	// only works where the upstream network does not drop forged-source packets
	// (no BCP38 egress filtering) — which must be proven on the real route.
	SPOOF TransportType = "spoof"
	// PCK carries the KCP transport inside TCP segments this process builds and
	// reads through a packet socket, instead of through the kernel's TCP stack.
	// It is a TCP transport in everything the wire can see — real source
	// address, real ports, a header with the options and numbering a Linux
	// stack produces — but no socket, no handshake and no connection state
	// exist on either host, so nothing in netfilter or connection tracking is
	// in a position to interfere with it. That is the point: on a path where a
	// kernel TCP flow is reset, throttled or dropped, this one is not visible
	// to the machinery doing it. KCP above supplies the reliability the absent
	// stack would have. Linux only, and needs root or CAP_NET_RAW.
	PCK TransportType = "pck"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL