Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyKCPSettings(session *kcp.UDPSession, s KCPSettings)
- func DecodeControlAck(ack string) (token, nonce string, muxVersion int)
- func DefaultTCPFlagList() []string
- func EffectiveRPFilter(iface string) (value int, key string)
- func EncodeControlAck(token, nonce string, muxVersion int) string
- func FormatTCPFlagList(in []TCPFlags) []string
- func GenerateSelfSigned(host string) (tls.Certificate, error)
- func HTTPSConfig(s TLSSettings, logf func(string, ...any)) (*tls.Config, error)
- func InterfaceTowardPeer(peerReal string) string
- func KCPDial(remoteAddr, token string, s KCPSettings) (*kcp.UDPSession, error)
- func KCPListen(bindAddr, token string, s KCPSettings) (*kcp.Listener, io.Closer, error)
- func ListenReuseControl(network, address string, s syscall.RawConn) error
- func ListenWithBuffers(network, address string, rcvBufSize, sndBufSize, mss int, ...) (net.Listener, error)
- func MarkUDP(addr string) string
- func NewPckPacketConn(listening bool, token, addr string, carrier PcapCarrier) (net.PacketConn, net.Addr, error)
- func NewPoolNonce() (string, error)
- func NewQUICStreamConn(stream *quic.Stream, conn *quic.Conn) net.Conn
- func NewSpoofPacketConn(server bool, token string, c SpoofCarrier, realPeer net.IP) (net.PacketConn, error)
- func NewXdiPacketConn(listening bool, token, addr string) (net.PacketConn, net.Addr, error)
- func NoiseClientConn(raw net.Conn, token string, timeout time.Duration) (net.Conn, error)
- func NoiseServerConn(raw net.Conn, token string, timeout time.Duration) (net.Conn, error)
- func PckOverhead() int
- func PinTCPBuffers() bool
- func QUICDial(ctx context.Context, remoteAddr string, s QUICSettings) (*quic.Conn, error)
- func ReadDatagram(r io.Reader, buf []byte) (int, error)
- func ResolveMuxVersion(configured int) int
- func ResolveRemoteAddr(remoteAddr string) (int, string, error)
- func ResolveSpoofDirections(profile, uplink, downlink string) (SpoofProfile, SpoofProfile)
- func ResolveStaticMuxVersion(configured int) int
- func ServerTLSConfig(s TLSSettings, logf func(string, ...any)) (*tls.Config, error)
- func SetPinTCPBuffers(pin bool)
- func SmuxConfig(version int, s MuxSettings) *smux.Config
- func SplitUDPTarget(addr string) (target string, isUDP bool)
- func SpoofDiag(conn net.PacketConn) string
- func SpoofOverhead(p SpoofProfile) int
- func SuggestedTCPFlagCycles() []struct{ ... }
- func TcpDialer(ctx context.Context, remoteAddress string, timeout time.Duration, ...) (*net.TCPConn, error)
- func TcpDialerVia(ctx context.Context, out *Outbound, remoteAddress string, ...) (*net.TCPConn, error)
- func TunnelCongestion() (active, requested string)
- func WSSBindingProof(ekm []byte, token string) string
- func WSSServerProof(cs *tls.ConnectionState, token string) (string, error)
- func WebSocketDialer(ctx context.Context, out *Outbound, addr string, edgeIP string, path string, ...) (*websocket.Conn, error)
- func WriteDatagram(w io.Writer, payload []byte) error
- func XdiOverhead() int
- type EndpointScore
- type Endpoints
- func (e *Endpoints) All() []string
- func (e *Endpoints) Current() string
- func (e *Endpoints) EnableHealthSteering(ctx context.Context, interval time.Duration, log func(string))
- func (e *Endpoints) Len() int
- func (e *Endpoints) Next() string
- func (e *Endpoints) Rotate() string
- func (e *Endpoints) SetSpread(on bool)
- func (e *Endpoints) Spread() bool
- type KCPSettings
- type MuxSettings
- type Outbound
- type PcapCarrier
- type PoolNonce
- type ProxyConfig
- type QUICListener
- type QUICSettings
- type QUICStreamConn
- type SpoofCarrier
- type SpoofDPI
- type SpoofProfile
- type SpoofRawSender
- type TCPFlags
- type TLSSettings
Constants ¶
const MaxDatagram = 65535
MaxDatagram is the largest forwarded datagram. It is what a two-byte length header can describe, and more than IP itself will carry in one packet.
const MuxVersionAuto = 0
MuxVersionAuto is the configured value that means "negotiate it". It is the zero value, so a config that never mentions mux_version negotiates.
const WSSBindingLabel = "EXPORTER-hashem-wss-binding-v1"
WSSBindingLabel is the RFC 5705 exporter label for the credential binding.
Variables ¶
var TCPCongestion = "bbr"
TCPCongestion is the congestion control algorithm requested per socket. BBR keeps the pipe full on a long, mildly-lossy path where the loss-based default (cubic) reads loss as congestion and backs off. Applied best effort: a kernel without it simply keeps its default.
Functions ¶
func ApplyKCPSettings ¶
func ApplyKCPSettings(session *kcp.UDPSession, s KCPSettings)
ApplyKCPSettings pushes the tuning onto a live KCP session. It is called on every accepted and dialled session, on both sides.
func DecodeControlAck ¶
DecodeControlAck splits what EncodeControlAck packed. A malformed answer yields an empty token, which fails the caller's comparison — so a server that answers with something else is rejected rather than half-trusted.
func DefaultTCPFlagList ¶
func DefaultTCPFlagList() []string
DefaultTCPFlagList is what a tunnel uses when the operator says nothing: the flags every segment of an established, data-carrying connection has.
func EffectiveRPFilter ¶
EffectiveRPFilter is the rp_filter the kernel actually applies to packets arriving on iface: the MAXIMUM of conf.all and conf.<iface>. It returns that value and the sysctl key it came from, so a caller can name the setting to change. 1 (strict) is the only value that drops forged sources; 0 and 2 pass them.
An empty or unreadable interface falls back to conf.all alone — still better than never looking past it, it just cannot catch an interface stricter than all.
func EncodeControlAck ¶
EncodeControlAck packs the server's answer to a v2 control handshake: the token the client checks the server by, the nonce its pool connections will carry, and the mux version it must use for this run (see mux.go).
The fixed-width fields go first, so everything splits apart on known offsets and the token — arbitrary user input, which may contain any byte at all — is simply whatever is left. A separator would have to be a byte the token cannot contain, and there is no such byte.
func FormatTCPFlagList ¶
FormatTCPFlagList renders a parsed cycle back to config spelling.
func GenerateSelfSigned ¶
func GenerateSelfSigned(host string) (tls.Certificate, error)
GenerateSelfSigned makes a throwaway certificate for one run, in memory.
host is cosmetic — nothing verifies it by default — but a certificate that names the host it is served from is what an inspecting eye expects.
func HTTPSConfig ¶
HTTPSConfig builds a *tls.Config for an ordinary HTTPS server — the web panel. It offers the same two ways to get a certificate as the tunnel listeners, and deliberately skips the ALPN pinning they need: that exists so a WebSocket upgrade can never be negotiated away to HTTP/2, and a panel has no upgrade to protect.
Renewal is not something the caller has to arrange. Both paths hand back a config whose certificate is resolved per handshake, so an ACME certificate reissued at the 60-day mark of its 90-day life is served from the next connection onward without restarting the panel.
func InterfaceTowardPeer ¶
InterfaceTowardPeer names the interface the kernel would receive the peer's packets on — the one it routes toward the peer's real address. Empty when it cannot be determined, which is not an error: the caller falls back to conf.all.
func KCPDial ¶
func KCPDial(remoteAddr, token string, s KCPSettings) (*kcp.UDPSession, error)
KCPDial opens a KCP session to remoteAddr with the tuning applied, over UDP or — when the settings ask for it — over ICMP echo (the xdi transport).
func KCPListen ¶
KCPListen opens a KCP listener on bindAddr. It yields reliable, ordered sessions carried inside UDP datagrams — or, when the settings ask for it, inside ICMP echo (xdi), forged raw IP (spoof) or hand-built TCP (pck).
It returns the listener and a Closer for the underlying carrier socket. The two are separate because kcp-go does not own a socket handed to it through ServeConn: listener.Close() leaves the raw socket bound, so the caller must close the returned Closer as well or the next restart cannot bind the port. For the plain UDP path the Closer is a no-op — kcp owns that socket.
func ListenReuseControl ¶
ListenReuseControl sets SO_REUSEADDR on a listening socket, so a restart does not have to wait out TIME_WAIT on the port.
It never fails the listen: a kernel that refuses the option still binds and accepts perfectly well, and the only cost is that an immediate restart may have to retry.
func ListenWithBuffers ¶
func ListenWithBuffers(network, address string, rcvBufSize, sndBufSize, mss int, keepAlivePeriod time.Duration, dis_nodelay bool) (net.Listener, error)
listenWithBuffers creates a TCP listener with specified SO_RCVBUF and SO_SNDBUF sizes. It returns a net.Listener and an error if any.
func NewPckPacketConn ¶
func NewPckPacketConn(listening bool, token, addr string, carrier PcapCarrier) (net.PacketConn, net.Addr, error)
NewPckPacketConn opens the packet-level TCP carrier as a bare net.PacketConn.
addr is the tunnel address: the bind address on the listening side, the peer's on the dialling side. The returned net.Addr is where the caller should send, and is nil on the listening side, which learns each peer from the segments that arrive exactly as a UDP socket would.
func NewPoolNonce ¶
NewPoolNonce returns a fresh random nonce for one run of a tunnel.
func NewQUICStreamConn ¶
NewQUICStreamConn wraps a stream and its connection as a net.Conn.
func NewSpoofPacketConn ¶
func NewSpoofPacketConn(server bool, token string, c SpoofCarrier, realPeer net.IP) (net.PacketConn, error)
NewSpoofPacketConn opens the spoof carrier as a bare net.PacketConn.
This file held the WireGuard-pipe mode: a raw UDP flow relayed over the spoof channel instead of a KCP tunnel, on the reasoning that WireGuard brings its own encryption and its own handling of a lossy link, so stacking KCP under it only doubles the reliability layer. That mode was removed. Its buffer sizer and its relay loop were not, and stayed here as an export surface with no consumer — the direct tunnel carries a whole private network now, which is what the pipe was reached for.
realPeer is the peer's real address: the server's for a client, the client's for a server.
func NewXdiPacketConn ¶
NewXdiPacketConn opens the ICMP-echo carrier as a bare net.PacketConn.
ICMP has no ports, so only the host of addr is used and its port is ignored; tunnels sharing the host's raw ICMP socket are separated by the tag derived from their token.
func NoiseClientConn ¶
NoiseClientConn performs the initiator side of the handshake over raw and, on success, returns a net.Conn that transparently encrypts everything written to it and decrypts everything read from it.
func NoiseServerConn ¶
NoiseServerConn is the responder side of NoiseClientConn.
func PckOverhead ¶
func PckOverhead() int
PckOverhead is what the pck framing costs inside the path MTU, exported so the KCP layer can size its datagrams under it.
func PinTCPBuffers ¶
func PinTCPBuffers() bool
PinTCPBuffers reports whether TCP buffers should be pinned.
func QUICDial ¶
QUICDial opens a QUIC connection to remoteAddr with the tuning applied. The socket it rides in is bound locally so its buffers can be sized to match.
func ReadDatagram ¶
ReadDatagram reads one length-prefixed datagram into buf and returns its size. buf must be at least MaxDatagram bytes, or a legal datagram that does not fit would have to be discarded mid-frame, which desynchronises the stream — every following datagram would be read from the wrong offset.
func ResolveMuxVersion ¶
ResolveMuxVersion turns a configured value into the version the server will impose. Auto becomes 2: the question is only ever asked on a handshake the client had to be new to speak, and a new client uses what it is told.
func ResolveSpoofDirections ¶
func ResolveSpoofDirections(profile, uplink, downlink string) (SpoofProfile, SpoofProfile)
ResolveSpoofDirections turns the profile knobs into an uplink/downlink pair: each direction is its own setting if given, otherwise the symmetric profile, otherwise udp. Validation happens at load time (checkSpoof), so a parse error here falls back to udp rather than failing.
func ResolveStaticMuxVersion ¶
ResolveStaticMuxVersion is the choice for every mux transport that cannot negotiate — KCP and xDi (whose control channel is itself a smux session, so there is no muxless channel to agree over) and the websocket-mux transports (which were simply never given a negotiation handshake). Both ends resolve independently and must land on the same number from the same config, which they do because the resolution is deterministic.
Auto becomes 1, not 2, and the difference is the whole point: these transports used version 1 on the previous release, so a 1.7.0 end meeting a 1.6.5 end has to choose 1 or the two disagree and smux tears every session down. An operator who wants 2 sets it explicitly on both ends. (smux rejects any version that is not 1 or 2 outright — "unsupported protocol version" — which is what a bare auto value of 0 produced before this existed, and is the regression this prevents.)
This is deliberately not ResolveMuxVersion: that one answers auto with 2, because it is only ever consulted on the tcp/tcpmux negotiation handshake, where the server imposes the version and a new client obeys. Here there is no one to impose it, so backward compatibility decides.
func ServerTLSConfig ¶
ServerTLSConfig builds a *tls.Config for a listener.
Both paths go through GetCertificate rather than a fixed certificate, so a renewed certificate is picked up without restarting the tunnel. That matters more than it sounds: Let's Encrypt certificates last 90 days, and a scheme that needed a restart would mean a scheduled interruption every couple of months on every tunnel using one.
func SetPinTCPBuffers ¶
func SetPinTCPBuffers(pin bool)
SetPinTCPBuffers records whether TCP buffers should be pinned. The engine calls it once, before any listener or dialer exists.
func SmuxConfig ¶
func SmuxConfig(version int, s MuxSettings) *smux.Config
SmuxConfig builds a session configuration for a settled version.
func SplitUDPTarget ¶
SplitUDPTarget strips the UDP mark, reporting whether it was there.
func SpoofDiag ¶
func SpoofDiag(conn net.PacketConn) string
SpoofDiag returns the carrier's startup note, or "" for anything that is not a spoof carrier or has nothing to say.
It is worth surfacing because the XDP path declines silently by design: a kernel too old, or a verifier rejection, falls back rather than failing, and an operator who turned it on has no other way to learn which happened.
func SpoofOverhead ¶
func SpoofOverhead(p SpoofProfile) int
SpoofOverhead is what the forged-source carrier costs for a profile: the IP header and the profile's L4 header. It does not include the optional DPI padding, which the caller adds if it has turned padding on.
func SuggestedTCPFlagCycles ¶
func SuggestedTCPFlagCycles() []struct{ Value, Desc string }
SuggestedTCPFlagCycles are the combinations worth offering in a menu, with what each is for. They are all things an established connection genuinely sends, so any of them can carry data without looking wrong on its own; what varies is how the flow reads as a whole.
func TcpDialer ¶
func TcpDialer(ctx context.Context, remoteAddress string, timeout time.Duration, keepAlive time.Duration, nodelay bool, retry int, SO_RCVBUF int, SO_SNDBUF, mss int) (*net.TCPConn, error)
TcpDialer opens a connection straight to remoteAddress, by whatever route the kernel would choose.
This is the dialer for anything that must not be redirected — above all the dial to the local backend, which never leaves the machine, and which a proxy or an interface binding meant for the uplink would simply break. It has no way to take either, which is what keeps that traffic off them by construction rather than by remembering. Tunnel connections use TcpDialerVia.
func TcpDialerVia ¶
func TcpDialerVia(ctx context.Context, out *Outbound, remoteAddress string, timeout time.Duration, keepAlive time.Duration, nodelay bool, retry int, SO_RCVBUF int, SO_SNDBUF, mss int) (*net.TCPConn, error)
TcpDialerVia opens a connection to remoteAddress the way out describes: from a chosen source address or interface, marked for a routing rule, through a proxy, or any combination — and straight to it when out is nil.
The socket is dialled here whatever out says, so it carries the tunnel's buffer sizes, congestion control, keepalive and MSS whether it ends up talking to the server or to a proxy standing in front of it; only where it goes and what handshake follows differ. Anything out asks for that cannot be applied fails the attempt, and the retry and backoff below cover it exactly as a refused dial would be.
func TunnelCongestion ¶
func TunnelCongestion() (active, requested string)
TunnelCongestion reports the congestion control algorithm the tunnel's TCP sockets end up running under, and the one they asked for.
An empty active value means the question could not be answered — the tunnel is not on Linux, or /proc is not readable — which is reported as unknown rather than guessed at.
func WSSBindingProof ¶
WSSBindingProof returns the value the client sends and the server checks: HMAC-SHA256 of the exported keying material, keyed by the tunnel token.
func WSSServerProof ¶
func WSSServerProof(cs *tls.ConnectionState, token string) (string, error)
WSSServerProof computes the proof the server expects for a connection, from the same keying material the client exported. A man in the middle that terminated the client's TLS has a different session here, so its material — and therefore this proof — does not match what the client sent. It fails closed for the same reason wssClientBinding does.
func WebSocketDialer ¶
func WriteDatagram ¶
WriteDatagram writes one length-prefixed datagram.
A zero-length datagram is a real thing on the wire — a keepalive, a probe — and is framed like any other, so the far end sends a zero-length datagram too rather than nothing at all.
func XdiOverhead ¶
func XdiOverhead() int
XdiOverhead is what the ICMP-echo carrier costs: the IP header, the echo header, and the tag-and-direction prefix that lets several tunnels share the host's single raw ICMP socket.
Types ¶
type EndpointScore ¶
type EndpointScore struct {
Addr string
RTTms float64
JitterMs float64
LossPct float64
Score float64
Reachable bool
}
EndpointScore is one exit's measured health.
func ScoreEndpoint ¶
func ScoreEndpoint(addr string, samples int) EndpointScore
ScoreEndpoint measures one exit with a short ICMP burst. An unreachable exit comes back with Reachable=false and a score that sorts it last.
func ScoreEndpoints ¶
func ScoreEndpoints(addrs []string, samples int) []EndpointScore
ScoreEndpoints measures every exit and returns them best-first. Handy for the operator-facing "which exit should I pin?" view.
type Endpoints ¶
type Endpoints struct {
// contains filtered or unexported fields
}
Endpoints is an ordered, rotating list of server addresses a client can dial.
It exists because a single address is a single point of failure: the server's IP may be filtered from the client's network while another IP, another port, or a CDN edge of the same server still works. The control-channel loop calls Rotate() every time a connection attempt fails, so the client walks the list until something connects — and every data connection then uses whichever endpoint is currently live.
A one-element list behaves exactly like a plain address, so existing tunnels are unaffected.
func NewEndpoints ¶
NewEndpoints builds the list from a primary address plus optional fallbacks, trimming blanks and dropping duplicates while preserving order.
func (*Endpoints) EnableHealthSteering ¶
func (e *Endpoints) EnableHealthSteering(ctx context.Context, interval time.Duration, log func(string))
EnableHealthSteering starts a background loop that scores every endpoint on `interval` and steers Current()/Next() to the healthiest, with hysteresis so the choice does not flap. It no-ops on a single endpoint, and it turns spread off because steering and spreading are opposite intentions. The loop stops when ctx is done. `log`, if non-nil, is called with a one-line note whenever the preferred exit actually changes.
func (*Endpoints) Next ¶
Next returns the endpoint a *new* data connection should use, advancing a separate cursor each call so the pool spreads itself across every endpoint instead of piling onto one.
This is what turns a fallback list into load balancing and multipath: with spread enabled the pool ends up holding connections over several addresses at once, so one throttled or congested route only slows the share of traffic riding on it rather than the whole tunnel.
The control channel deliberately keeps using Current(): it must stay on one endpoint, because it is the connection the server identifies the peer by.
With spread disabled — or a single endpoint — this is exactly Current(), so existing tunnels behave as before.
func (*Endpoints) Rotate ¶
Rotate advances to the next endpoint and returns it. With a single endpoint it is a no-op, so simple setups never change behaviour.
type KCPSettings ¶
type KCPSettings struct {
MTU int
Interval int
Resend int
NoDelay int
NoCongestion int
SndWnd int
RcvWnd int
AckNoDelay bool
// DataShards/ParityShards configure forward error correction. Both ends
// MUST use the same values — the parity layer sits below KCP itself, so a
// mismatch means the peers cannot decode each other's packets at all.
DataShards int
ParityShards int
// SO_RCVBUF/SO_SNDBUF size the underlying UDP socket. On a high-latency
// link these matter more than for TCP, because a KCP sender can have a full
// window in flight with no kernel-side congestion control to pace it.
SO_RCVBUF int
SO_SNDBUF int
// UseICMP carries the KCP session inside ICMP echo instead of UDP — the xdi
// transport. Everything above the packet layer is identical, so the only
// thing this changes is which kind of socket the datagrams ride in. See
// icmpconn_linux.go.
UseICMP bool
// Pck, when set, carries the KCP session inside TCP segments this process
// builds and reads through a packet socket — the "TCP + PCK" transport.
// Again only the packet layer differs. Unlike Spoof it forges no address:
// the source is this machine's real one, so the server learns each client
// from the wire exactly as a UDP socket would. See pckconn_linux.go.
Pck *PcapCarrier
// Logf, when set, receives the startup notes: the effective MTU and FEC
// on both ends, and — for the pck carrier — the egress it discovered and
// whether the kernel-RST guard installed. It exists because a KCP tunnel that
// never connects is otherwise silent: FEC is not negotiated, so a shard
// mismatch between the two ends drops every packet with nothing in the log,
// and the pck carrier's interface/next-hop/guard decisions were computed and
// then thrown away. Nil disables the notes. It is only ever called at
// startup, never on the data path.
Logf func(format string, args ...any)
}
KCPSettings carries the tuning of a KCP session from the config all the way down to the socket. Both the server and the client side fill it from the same preset, so the two ends of a tunnel always agree.
type MuxSettings ¶
MuxSettings is the tuning both ends apply once they agree on a version.
type Outbound ¶
type Outbound struct {
// Proxy routes the connection through a SOCKS5 or HTTP proxy.
Proxy *ProxyConfig
// LocalAddr is the source address to bind, as host or host:port. A bare
// address gets port 0, so the kernel still picks the port.
LocalAddr string
// Interface is the device name to pin the socket to (Linux).
Interface string
// Mark is the fwmark to stamp on the packets, 0 for none (Linux).
Mark int
}
Outbound describes how a tunnel connection leaves this machine. A nil *Outbound is the ordinary case: dial directly, and let the kernel route.
func (*Outbound) IsSet ¶
IsSet reports whether anything at all is configured, so a caller can skip building one.
type PcapCarrier ¶
type PcapCarrier struct {
// Port is the TCP port the tunnel's segments are addressed to on the
// server. It is the tunnel port the operator configured, so what shows up
// in a capture is the port they expect.
Port uint16
// Interface and GatewayMAC override the automatic egress lookup. Both empty
// — the normal case — means the route to the peer decides. See
// discoverEgress.
Interface string
GatewayMAC string
// Flags is the cycle of TCP flag combinations stamped on outgoing segments,
// one per packet. Empty means PSH+ACK on every segment.
Flags []TCPFlags
// PeerIP is the server's address, known to the client before it sends
// anything. The server leaves it empty and learns each client from the
// packets that arrive, exactly as a UDP socket would.
PeerIP string
// Token is the tunnel's shared secret, used here only to derive the client's
// source port. See pckClientPort.
Token string
}
PcapCarrier is the pck carrier's tuning, carried in KCPSettings the same way the ICMP and spoof carriers are.
type PoolNonce ¶
type PoolNonce struct {
// contains filtered or unexported fields
}
PoolNonce holds the nonce of the current run.
It is read by every accepted connection and written when the control channel is established or torn down, so it is behind a lock rather than a plain string.
func (*PoolNonce) Clear ¶
func (p *PoolNonce) Clear()
Clear forgets the nonce, so connections carrying the previous run's value are no longer accepted.
type ProxyConfig ¶
type ProxyConfig struct {
// Scheme is "socks5" or "http".
Scheme string
// Address is the proxy's host:port.
Address string
// User and Password are the credentials, empty when the proxy takes none.
User string
Password string
}
ProxyConfig is a parsed proxy URL, or nil when the tunnel dials directly.
func ParseProxy ¶
func ParseProxy(raw string) (*ProxyConfig, error)
ParseProxy turns a configured proxy URL into something dialable. An empty string means no proxy, which is not an error.
It is strict about the scheme, because the two it understands behave completely differently on the wire and a typo would otherwise surface as a tunnel that cannot connect for no stated reason.
func (*ProxyConfig) String ¶
func (p *ProxyConfig) String() string
String renders the proxy for a log line, with the password removed. A tunnel logs the route it is taking, and a password in a log file outlives the session that wrote it.
type QUICListener ¶
QUICListener bundles a quic listener with the UDP socket it rides on. quic-go never closes a socket the caller supplied, so without this the socket would outlive the listener — and on a restart the transport could not rebind its own port. Closing this closes both.
func QUICListen ¶
func QUICListen(bindAddr string, s QUICSettings) (*QUICListener, error)
QUICListen opens a QUIC listener on bindAddr. The returned listener yields connections whose streams carry the tunnel's traffic, each one TLS 1.3 encrypted.
func (*QUICListener) Close ¶
func (l *QUICListener) Close() error
Close shuts the listener down and releases the UDP socket underneath it.
type QUICSettings ¶
type QUICSettings struct {
// KeepAlivePeriod sends a PING often enough to keep a NAT/firewall mapping
// alive on an otherwise idle tunnel. Zero disables it.
KeepAlivePeriod time.Duration
// MaxIdleTimeout tears the connection down after this long with no packets.
// It is negotiated to the lower of the two ends' values.
MaxIdleTimeout time.Duration
// SO_RCVBUF/SO_SNDBUF size the underlying UDP socket. The kernel default is a
// few hundred KB, which a flood overruns in a blink; the preset's several MB
// is what keeps QUIC fed under load. See the UDP transport for the same fix.
SO_RCVBUF int
SO_SNDBUF int
}
QUICSettings carries the tuning of a QUIC endpoint from the config down to the socket. QUIC brings its own TLS 1.3, congestion control and loss recovery, so unlike KCP there is nothing to hand-tune for the link itself — only the idle timeout, the keepalive that holds a NAT mapping open, and the datagram socket buffers.
type QUICStreamConn ¶
QUICStreamConn adapts a QUIC stream to net.Conn. A stream carries Read, Write, Close and the deadlines already; it only lacks the peer addresses, which come from the connection it belongs to. This lets a stream flow through everything that expects a net.Conn — the framing helpers, the connection handler, the usage counters — with no special-casing.
func (*QUICStreamConn) LocalAddr ¶
func (q *QUICStreamConn) LocalAddr() net.Addr
func (*QUICStreamConn) RemoteAddr ¶
func (q *QUICStreamConn) RemoteAddr() net.Addr
type SpoofCarrier ¶
type SpoofCarrier struct {
Uplink SpoofProfile
Downlink SpoofProfile
SrcIP string // forged source address, empty to keep the real one
SrcPool []string // forged sources to rotate through; SrcIP is a member
PeerIP string // peer's real IPv4; required when listening, derived when dialling
Interface string // egress device to pin the raw socket to, empty for none
XDPIface string // NIC to attach the XDP receive fast path to, empty = off
SockBuf int // SO_SNDBUF/SO_RCVBUF for the carrier's sockets, 0 = default
PeerSrcIP string // expected forged source of inbound packets, empty = accept any
ReplySplit bool // icmp/icmpv6: the dialling side sends Echo Request, the other Echo Reply
MTU int // fragment sends larger than this, 0 = default 1500
DPI SpoofDPI // optional obfuscation knobs (ttl/dscp/port/padding/fake-tls)
}
SpoofCarrier is the IP-spoofing carrier's tuning, passed down to the raw socket.
Uplink is the dialling→listening profile, Downlink the reverse; for a symmetric tunnel they are equal. The carrier's own constructor picks which is its send and which its receive from the side it is on.
type SpoofDPI ¶
type SpoofDPI struct {
TTLJitter bool // vary the IP TTL per packet across a pool of OS defaults
RandomDSCP bool // vary the IP DSCP/ToS per packet across plausible values
ShufflePort bool // randomise the L4 SOURCE port per packet (udp/tcp)
PortMin uint16 // low end of the source-port shuffle range
PortMax uint16 // high end of the source-port shuffle range
Padding bool // append self-describing random padding to each payload
PaddingMax uint8 // most padding bytes to add (1..255); 0 means a small default
FakeTLS bool // prepend a fake TLS record header (tcp profile only)
}
SpoofDPI gathers the optional obfuscation knobs the carrier can apply to make the forged flow harder to fingerprint, ported from the reference spooftunnel. Every one is off by default; those that change the wire (padding, fake TLS) must be set the same on both ends, while the header cosmetics (ttl, dscp, source-port shuffle) need no agreement because the receiver ignores those fields.
func SpoofDPIFromConfig ¶
func SpoofDPIFromConfig(sc config.SpoofConfig) SpoofDPI
SpoofDPIFromConfig maps the flat spoof_* obfuscation knobs of a SpoofConfig onto the carrier's SpoofDPI struct. It lives in the network package (which already depends on config) so the server and client both build the struct the same way from one place, rather than each repeating the field mapping.
type SpoofProfile ¶
type SpoofProfile string
SpoofProfile is the L4 shim the carrier wraps around each datagram. It changes only what the packet looks like to inspection; everything above the L4 header is identical across profiles.
const ( // SpoofProfileUDP wraps the payload in a UDP header. The default: the host // kernel has no automatic answer to an unknown UDP datagram, so nothing on // the machine fights the forged flow. SpoofProfileUDP SpoofProfile = "udp" // SpoofProfileTCP wraps the payload in a TCP header, which is what the // reference tools send. The host kernel WILL answer a forged TCP segment to // a port it is not listening on with a RST, which tears the flow down — so // this profile installs a targeted iptables rule that drops the kernel's // outbound RSTs for the tunnel's port. Needs the iptables binary. SpoofProfileTCP SpoofProfile = "tcp" // SpoofProfileICMP wraps the payload in an ICMP Echo Request, so on the wire // the tunnel looks like ping traffic, with a forged source. Both ends send // Echo Requests; the receiver keeps only those, so the kernel's automatic // Echo Reply is ignored and no firewall rule is needed. SpoofProfileICMP SpoofProfile = "icmp" // SpoofProfileICMPv6 carries the payload in an ICMPv6 Echo Request (type // 128) that rides inside an ORDINARY IPv4 packet whose protocol byte is 58 // (ICMPv6's number). It is not real IPv6 — the outer packet is IPv4 with a // forged IPv4 source, exactly like the icmp profile — but many filters that // clamp down hard on ICMP and UDP leave protocol 58 comparatively open, // because IPv6 itself is cut off and its ICMP is treated as harmless. The // reference spoof-tunnel ships this same trick. The kernel does not process // proto-58 payloads in an IPv4 packet as ICMP, so unlike the icmp profile it // generates no automatic reply and needs no suppression. SpoofProfileICMPv6 SpoofProfile = "icmpv6" // SpoofProfileIPIP carries the payload directly as the body of an IP-in-IP // packet (protocol 4), with no L4 header at all. Some filters that inspect // UDP/TCP/ICMP wave IP-in-IP through, since it is normally a router-to-router // encapsulation. There is no port or identifier to demultiplex on, so a // receiver leans on spoof_peer_src_ip and the encryption above; pin the // former for a clean feed. SpoofProfileIPIP SpoofProfile = "ipip" // SpoofProfileProto58 carries the payload directly as the body of an IPv4 // packet whose protocol byte is 58, with no L4 header at all. It is the // reference spoof-tunnel's "proto58", and it is the icmpv6 profile with the // echo header taken off: the same protocol number that filters tend to leave // open, without the eight bytes that make it look like a ping. // // Which to reach for is a question about the filter, not about efficiency. // A path that passes protocol 58 whatever is inside it takes this; one that // looks at the payload and expects an ICMPv6 message takes icmpv6. Like ipip // and gre it carries no port, so a receiver leans on spoof_peer_src_ip and // the encryption above — and two tunnels sharing this host on protocol 58, // including an icmpv6 one, are told apart by exactly that. SpoofProfileProto58 SpoofProfile = "proto58" // SpoofProfileGRE carries the payload after a minimal 4-byte GRE header // (protocol 47), the generic routing encapsulation firewalls see between // routers. Like ipip it has no port to demultiplex on; pin spoof_peer_src_ip. SpoofProfileGRE SpoofProfile = "gre" )
func ParseSpoofProfile ¶
func ParseSpoofProfile(s string) (SpoofProfile, error)
ParseSpoofProfile validates a profile string, defaulting an empty one to UDP.
type SpoofRawSender ¶
type SpoofRawSender struct {
// contains filtered or unexported fields
}
SpoofRawSender sends individual UDP datagrams with a forged source address. It is the send half of the spoof carrier, exposed on its own for the spoof-capability tester, which needs to emit probes from many different forged sources without standing up a whole tunnel.
Linux only, and needs a raw socket (root or CAP_NET_RAW).
func NewSpoofRawSender ¶
func NewSpoofRawSender(iface string) (*SpoofRawSender, error)
NewSpoofRawSender opens the raw send socket, optionally pinned to an interface.
func (*SpoofRawSender) Close ¶
func (s *SpoofRawSender) Close() error
type TCPFlags ¶
type TCPFlags uint8
TCPFlags is one combination of TCP header flags.
func ParseTCPFlagList ¶
ParseTCPFlagList parses a whole cycle of flag combinations. An empty list means the default, which is the one real bulk data carries.
func ParseTCPFlags ¶
ParseTCPFlags turns a combination like "PA" into its bits. The spelling is tcpdump's, so an operator can copy what they see in a capture of real traffic straight into the config.
type TLSSettings ¶
type TLSSettings struct {
// CertFile and KeyFile point at a PEM pair. Used when ACMEDomain is empty.
CertFile string
KeyFile string
// ACMEDomain, when set, switches to Let's Encrypt for that domain. It must
// resolve to this server.
ACMEDomain string
// ACMEEmail is optional; Let's Encrypt uses it for expiry warnings.
ACMEEmail string
// ACMECacheDir is where issued certificates and the account key are kept.
// Losing it only means re-issuing, but doing that repeatedly hits rate
// limits, so it should be on persistent storage.
ACMECacheDir string
// FallbackCertFile and FallbackKeyFile are served when Let's Encrypt cannot
// issue. Only the panel sets them, and only because of what the alternative
// is: autocert answers a handshake it has no certificate for by failing it,
// so a domain that does not resolve yet, a blocked port 80, a rejected
// contact address or an unreachable CA all end the same way — every browser
// gets a TLS error and the operator has locked themselves out of the page
// they would fix it from. A self-signed certificate warns once and lets
// them in. Tunnels leave these empty: their client is not a person who can
// decide to accept a warning.
FallbackCertFile string
FallbackKeyFile string
// SelfSignedHost is the name to put in a certificate generated because none
// was configured. Cosmetic — nothing validates it — and empty gives
// "localhost".
SelfSignedHost string
}
TLSSettings describes how a listener should obtain its certificate.
func (TLSSettings) UsesACME ¶
func (s TLSSettings) UsesACME() bool
UsesACME reports whether these settings request a Let's Encrypt certificate.
Source Files
¶
- acmehttp.go
- bindopt_linux.go
- bpf_linux.go
- congestion.go
- endpoints.go
- healthscore.go
- icmpconn_linux.go
- icmpframe.go
- icmpguard_linux.go
- iptables_linux.go
- kcp.go
- l3conn.go
- listener.go
- mux.go
- noise.go
- outbound.go
- pckconn_linux.go
- pckframe.go
- pckguard_linux.go
- pckport.go
- pckroute_linux.go
- poolnonce.go
- proxy.go
- quic.go
- resolver.go
- reuse_port_unix.go
- rpfilter.go
- selfsigned.go
- sockopt_linux.go
- spoofbpf.go
- spoofcarrier.go
- spoofconn_linux.go
- spoofdpi_config.go
- spoofframe.go
- spoofpacket.go
- spoofpipe.go
- spoofsender_linux.go
- spoofxdp.go
- spoofxdp_linux.go
- tcp_dialer_unix.go
- tlsconf.go
- tuning.go
- udpframe.go
- utls.go
- ws_dialer.go
- wssbind.go