Table of Contents

Class LocalTcpStackOptions

Namespace
VpnHood.Net.TcpStack
Assembly
VpnHood.Net.TcpStack.dll

Provides the options of a LocalTcpStack: its windows, buffers, timeouts and connection limits, so its memory can be sized per platform.

public sealed class LocalTcpStackOptions
Inheritance
LocalTcpStackOptions
Inherited Members

Remarks

The stack checks the options when it is made and throws ArgumentException for one out of its range. The buffer and window defaults are the full-size values Android throughput was tuned against: change them only with a throughput measurement. The retransmission timeout backs off as RFC 6298 has it, and the delayed-ACK default stays under the 200 millisecond minimum RTO of Linux and Android peers.

Properties

AcceptQueueCapacity

Gets how many connections that completed their handshake a listener holds until they are accepted, or 0 or less, the default, for no limit. A listener whose queue is full closes a new connection instead.

public int AcceptQueueCapacity { get; init; }

Property Value

int

AdmissionMemoryLimitMb

Gets the process footprint, in megabytes, at or above which the stack drops new SYNs silently, with no reset, or null, the default, for no such limit.

public double? AdmissionMemoryLimitMb { get; init; }

Property Value

double?

Remarks

The footprint is read from VhMemory.Instance, the figure a host's memory limit is enforced against, such as iOS's phys_footprint; with no memory reader installed, there is no limit. Each admitted connection costs memory outside the stack's own buffers, such as a QUIC stream or a TLS connection to the server, so under memory pressure closing one connection for another only churns. The peer sends a dropped SYN again, about a second later, when the footprint may have receded; existing connections keep working.

Default

Gets new full-size options, for desktops and Android, where throughput matters and memory is plentiful.

public static LocalTcpStackOptions Default { get; }

Property Value

LocalTcpStackOptions

DefaultMss

Gets the segment size the stack sends with when the peer's SYN advertises none, in bytes.

public ushort DefaultMss { get; init; }

Property Value

ushort

Remarks

It must be from MinMss to MaxMss. The default is 536, RFC 879's.

DelayedAckTimeout

Gets how long a delayed ACK waits before the stack sends it, at its next check: the stack checks every 100 milliseconds. The default is 50 milliseconds, so the ACK nominally goes out 50 to 150 milliseconds after the segment, later when the stack is busy, or sooner when another segment draws it: under the 200 millisecond minimum RTO of a Linux or Android peer, which a later ACK would trip into a needless retransmission.

public TimeSpan DelayedAckTimeout { get; init; }

Property Value

TimeSpan

Remarks

It must be more than zero and at most 400 milliseconds: with the check interval on top, the ACK then nominally leaves within the 500 milliseconds RFC 1122 allows a delayed ACK, which only a busy stack may exceed.

EvictionMinIdle

Gets how long a connection must have been idle, neither sending nor receiving, before the stack may close it to admit a new one when MaxConnections is reached. The default, MaxValue, closes none: a full stack answers a new SYN with a reset.

public TimeSpan EvictionMinIdle { get; init; }

Property Value

TimeSpan

Remarks

The stack closes the connection idle the longest, if it has been idle this long; its stream reads as ended, so what forwards it ends too. Once the app has closed a connection's stream, idle means no progress in the sequence numbers. With no such connection, the new SYN is answered with a reset. A connection that turns active while it is chosen may still be closed, and with zero, any connection may be. It is used only with MaxConnections above 0, and must be zero or more.

IdleCheckInterval

Gets how often the stack checks its connections against IdleTimeout. A longer interval wakes the device less often. The default is 1 minute.

public TimeSpan IdleCheckInterval { get; init; }

Property Value

TimeSpan

Remarks

It must be from 1 millisecond to 49 days, and shorter than IdleTimeout.

IdleTimeout

Gets how long a connection may go without sending or receiving before the stack closes it. The default is 15 minutes.

public TimeSpan IdleTimeout { get; init; }

Property Value

TimeSpan

Remarks

It must be longer than IdleCheckInterval, the interval it is checked at, so a connection nominally stays up to one interval longer. Once the app has closed its stream, idle means no progress in the sequence numbers, which each check samples, so the connection may stay almost two intervals longer; acknowledgments that move nothing do not count. A lower value frees memory sooner.

Ios

Gets new small-footprint options for an iOS Network Extension's tight memory: starting points, to tune against real traffic.

public static LocalTcpStackOptions Ios { get; }

Property Value

LocalTcpStackOptions

Remarks

An estimate for live connections: on the receive side, 40 connections × 64 KB window = 2.5 MB unread at most, about 3.6 MB in the pipes' 4 KB segments; on the send side, 40 × 16 KB = 0.6 MB; about 4.2 MB in all. It leaves out allocator overhead, what closed connections' streams still hold, and memory outside the stack, such as QUIC streams.

MaxConnections

Gets the most connections the stack holds at once, handshakes included, or 0, the default, for no limit.

public int MaxConnections { get; init; }

Property Value

int

Remarks

When the stack is full, a new connection's SYN takes the place of an idle connection the stack closes for it (EvictionMinIdle), or else is answered with a reset. While memory is short (AdmissionMemoryLimitMb), SYNs are dropped before this check. A limit bounds the live connections' buffers to about (ReceiveWindowSize + RetransmitBufferSize) × this value, besides allocator overhead and what closed connections' streams still hold. It must be 0 or more.

MaxMss

Gets the largest segment the stack sends, in bytes, which it also advertises in its SYN-ACK; a larger MSS from the peer is lowered to it.

public ushort MaxMss { get; init; }

Property Value

ushort

Remarks

It must be at least DefaultMss. The default is 1460, an Ethernet frame's.

MinMss

Gets the smallest segment size the stack takes from the peer, in bytes; a smaller advertised MSS is raised to it.

public ushort MinMss { get; init; }

Property Value

ushort

Remarks

It must be from 1 to DefaultMss. The default is 64.

ReceiveWindowSize

Gets the receive window each connection advertises, in bytes: how much data the peer may send that the app has not read yet. It is the main receive-side memory of a connection.

public int ReceiveWindowSize { get; init; }

Property Value

int

Remarks

It must be from 1 to 65535: the stack advertises no window scaling. The default, which the Ios preset keeps too, is 65535.

RetransmitBufferSize

Gets the size of each connection's send buffer, in bytes, which holds what is sent until the peer acknowledges it: no more than this is in flight. It is the main send-side memory of a connection.

public int RetransmitBufferSize { get; init; }

Property Value

int

Remarks

It must be at least MaxMss. The default is 64 KB; the Ios preset's is 16 KB.

RetransmitMaxTimeout

Gets the longest the doubled retransmission timeout grows. A handshake whose SYN-ACK backoff has reached it is given up with a reset at the next expiry; equal to RetransmitTimeout, it leaves a handshake no retransmission at all. The default is 4 seconds.

public TimeSpan RetransmitMaxTimeout { get; init; }

Property Value

TimeSpan

Remarks

It must be at least RetransmitTimeout and at most 2,147,483,647 milliseconds, about 24 days.

RetransmitTimeout

Gets the first retransmission timeout (RTO): when the peer acknowledges nothing new for this long while a SYN-ACK, data or a FIN is unacknowledged, the stack sends the oldest unacknowledged segment again and doubles the timeout, up to RetransmitMaxTimeout. The default is 500 milliseconds.

public TimeSpan RetransmitTimeout { get; init; }

Property Value

TimeSpan

Remarks

It is the only recovery when the last segment of a burst is lost, as fast retransmit needs later segments to draw duplicate ACKs. Keep it well above the peer's delayed ACK, which commonly holds an acknowledgment for up to 200 milliseconds. The stack checks it every 100 milliseconds, so a retransmission nominally comes up to that much later, and later still when the stack is busy. It must be at least 1 millisecond.

ZeroWindowProbeInterval

Gets how long the stack waits while the peer's window is zero before it sends a zero-window probe. The default is 200 milliseconds.

public TimeSpan ZeroWindowProbeInterval { get; init; }

Property Value

TimeSpan

Remarks

It must be at least 1 millisecond and at most 2,147,483,647 milliseconds, about 24 days.

Methods

ForCurrentPlatform()

Returns the options for the current platform: Ios on iOS and tvOS, whose network extensions are memory-capped, and Default everywhere else, Mac Catalyst included. A stack made without options uses them.

public static LocalTcpStackOptions ForCurrentPlatform()

Returns

LocalTcpStackOptions

New options for the current platform.

Remarks

The choice goes by OS family, not by a memory probe: jetsam enforces an iOS extension's limit, and GetGCMemoryInfo() reports the device's whole memory.