Skip to main content

PingServer

PingServer is the paired server-side packet tunnel for PingClient. Wrapped requests arrive through PingServer's upstream callback, are restored, and continue toward the next node. Plain responses arrive through the downstream callback, are wrapped, and continue toward the previous node.

It is a pure packet tunnel created with WaterWall's packet-tunnel API. It runs on worker packet lines and does not allocate per-connection line state.

Direction Model

Transform role and callback direction are separate:

DirectionBehavior
upstream PayloadRestores matching wrapped requests, then forwards with tunnelNextUpStreamPayload().
downstream PayloadEncapsulates or rewrites plain responses, then forwards with tunnelPrevDownStreamPayload().

Direct PingClient -> PingServer adjacency is the canonical pair.

plain request -> PingClient -> PingServer -> plain request
plain response <- PingClient <- PingServer <- plain response

Typical Placement

Opposite a PingClient:

TunDevice -> PingClient -> RawSocket
RawSocket -> PingServer -> TunDevice

For local tests, use one direct packet chain:

TesterClient(packet-mode=true) -> PingClient -> PingServer -> TesterServer(packet-mode=true)

Compatibility And Migration

This direction change is intentionally breaking. Change an existing TunDevice -> PingServer -> RawSocket server chain to RawSocket -> PingServer -> TunDevice, replace paired-Bridge Ping layouts with direct PingClient -> PingServer adjacency, and deploy that topology together with the new PingServer binary. An old PingServer must keep the old server topology. PingClient's implementation, settings, and wire format are unchanged, so an older PingClient remains wire-compatible with the new PingServer when their settings match. A coordinated peer upgrade may be operationally simpler, but it is not a protocol requirement. MTU requirements remain strategy-dependent.

Configuration Example

{
"name": "ping-server",
"type": "PingServer",
"settings": {
"strategy": "wrap-in-new-ip-and-icmp-header",
"identifier": 4660,
"check-identifier": true,
"source": "203.0.113.20",
"dest": "198.51.100.10",
"xor-byte": 90,
"roundup-size": true,
"sequence-start": 0,
"ipv4-id-start": 0,
"ttl": 64,
"tos": 0
},
"next": "tun-in"
}

The strategy, identifier, xor-byte, and padding behavior must be compatible with the peer. source and dest are optional in the new-IP strategy and describe the outer packet generated by this side.

Strategies

wrap-in-new-ip-and-icmp-header

Wraps the whole inner IPv4 packet in a fresh outer IPv4 packet:

outer IPv4 header -> ICMP echo header -> original IPv4 packet

Details:

  • input must be a complete, self-consistent IPv4 packet
  • outer protocol is set to ICMP
  • outer ICMP is echo request (type 8) with code 0
  • outer IPv4 ttl, tos, and IPv4 ID come from settings/counters
  • source and dest are optional
  • when omitted, outer source/destination are copied from the inner packet
  • upstream decapsulation accepts ICMP echo request and echo reply packets
  • upstream decapsulation checks identifier only when check-identifier is enabled
  • upstream decapsulation does not verify configured source/destination addresses
  • recovered ICMP payload is forwarded even if it is not a valid IPv4 packet

Invalid IPv4 input is forwarded unchanged. A valid IPv4 packet that is too large to wrap is also forwarded unchanged. If required left padding is not available, the packet is dropped and a warning is logged.

wrap-in-icmp-header-and-reuse-ipv4-addresses

Reuses the existing IPv4 header instead of creating a new outer IPv4 header. This is the default strategy.

existing IPv4 header -> ICMP echo header -> original transport bytes -> metadata trailer

Details:

  • preserves source, destination, TOS, TTL, IPv4 ID, DF flag, and IPv4 options
  • changes IPv4 protocol to ICMP
  • inserts an ICMP echo header after the existing IPv4 header
  • moves the original transport bytes into the ICMP payload
  • appends a 5-byte trailer with magic 0x57 0x50, original protocol, and original transport length
  • forwards fragmented IPv4 packets unchanged
  • forwards packets unchanged when the rewritten packet would not fit

Downstream decapsulation reads the trailer, restores protocol and transport length, recalculates the IPv4 packet checksum, and emits the restored packet.

wrap-in-only-icmp-header

Treats input as raw bytes and prepends only an ICMP echo header:

ICMP echo header -> raw payload

No IPv4 header is emitted by this node. Output is ICMP frame data, not a full IP packet. Use it only when surrounding nodes expect that shape.

change-only-ipv4-protocol-number

Only swaps the IPv4 protocol number in place:

  • requires swap-protocol
  • upstream changes matching request ICMP packets back to swap-protocol
  • downstream changes response packets whose IPv4 protocol equals swap-protocol to ICMP
  • recalculates only the IPv4 header checksum
  • leaves transport bytes unchanged
  • does not use ICMP identifiers, ICMP sequence, XOR, roundup, or outer IPv4 ID

swap-protocol accepts "TCP", "UDP", "ICMP", or an integer protocol number from 0 to 255.

Settings

SettingTypeDefaultDescription
strategystringwrap-in-icmp-header-and-reuse-ipv4-addressesPacket disguise mode.
identifierinteger44975 (0xAFAF)ICMP echo identifier for ICMP envelope modes. Range: 0..65535.
check-identifierbooleantrueWhen enabled, upstream ICMP envelopes must match identifier; mismatches are forwarded unchanged.
sequence-startinteger0Initial ICMP echo sequence counter for ICMP envelope modes. Range: 0..65535.
ipv4-id-startinteger0Initial outer IPv4 identification counter for wrap-in-new-ip-and-icmp-header. Range: 0..65535.
ttlinteger64Outer IPv4 TTL for wrap-in-new-ip-and-icmp-header. Range: 0..255.
tosinteger0Outer IPv4 TOS byte for wrap-in-new-ip-and-icmp-header. Range: 0..255.
xor-byteintegerdisabledOptional XOR byte applied to ICMP payload in ICMP envelope modes. Range: 0..255; unset/-1 disables it.
roundup-sizebooleanfalsePads ICMP payload in ICMP envelope modes.
roundupbooleanfalseLegacy alias for roundup-size.
sourceIPv4 stringcopied from inner packetOptional outer source for wrap-in-new-ip-and-icmp-header.
destIPv4 stringcopied from inner packetOptional outer destination for wrap-in-new-ip-and-icmp-header.
swap-protocolstring or integerrequired for protocol-swap strategyProtocol restored from ICMP on the upstream request path.
swap-identifierstring or integerunsetLegacy alias for swap-protocol.

Roundup And XOR

xor-byte and roundup-size apply only to ICMP envelope strategies.

When roundup-size is enabled:

  • wrap-in-new-ip-and-icmp-header and wrap-in-only-icmp-header store the original payload length as a 2-byte prefix inside the ICMP payload, then add random padding
  • wrap-in-icmp-header-and-reuse-ipv4-addresses stores the original transport length in the 5-byte trailer and may add random padding before the trailer
  • decapsulation removes padding using the prefix or trailer

Current PingServer roundup buckets are:

64, 128, 256, 512, 1024

The first bucket differs from PingClient's first bucket, but this is safe because each direction carries explicit length metadata.

When xor-byte is set, the ICMP payload is XORed after padding/trailer construction and XORed again before decapsulation metadata is read.

MTU And Packet-Size Limits

Warning: PingServer does not fragment packets, discover the path MTU, or reduce an upstream TCP MSS. Its size checks use the compile-time kMaxAllowedPacketLength, currently 1500 bytes; they do not use misc.mtu. misc.mtu only becomes relevant here when another node, such as TunDevice, uses it as an input MTU default.

At the current 1500-byte Ping limit, the largest input that each strategy can transform is:

StrategyMinimum added bytesMaximum input, roundup-size=falseMaximum input, roundup-size=true
wrap-in-new-ip-and-icmp-header28-byte outer IPv4 + ICMP headers14721470
wrap-in-icmp-header-and-reuse-ipv4-addresses8-byte ICMP header + 5-byte trailer14871487
wrap-in-only-icmp-header8-byte ICMP header14921490
change-only-ipv4-protocol-number01500not applicable

The ICMP-only row measures its raw input and output frame; this strategy does not emit an IP header.

When a valid input exceeds the applicable limit, the current ICMP envelope strategies normally forward the original packet unchanged instead of fragmenting or wrapping it. This is not a safe substitute for MTU planning: the packet is no longer disguised as the configured ICMP envelope and may be filtered or routed differently.

For a common RawSocket -> PingServer -> TunDevice server path whose real packet MTU is 1500, wrapped requests arrive from RawSocket on PingServer's previous side and are decoded upstream. Plain responses arrive from TunDevice on PingServer's next side and are encoded downstream toward RawSocket. Configure TunDevice.settings.device-mtu no higher than the applicable table value. Setting both misc.mtu and device-mtu to 1400 is a simple conservative choice when the Ping strategy may change, but it is not required when the exact overhead is known.

roundup-size can pad an accepted packet all the way to the 1500-byte Ping limit. Consequently, a 1400-byte input is safe for a direct 1500-byte output path, but roundup may consume the remaining space; it does not necessarily leave 100 bytes for another encapsulation layer after PingServer. Account for every additional header as well, and use the smaller of the real path MTU and every adjacent node's packet limit.

Packet Handling Notes

  • IPv4 packet strategies require complete IPv4 packets whose buffer length exactly matches IPv4 total length.
  • IPv6/non-IPv4 packets that cannot be rewritten are generally forwarded unchanged.
  • Fragmented IPv4 packets are forwarded unchanged in reuse-header mode.
  • Matching upstream ICMP envelopes with malformed roundup metadata or malformed reuse trailers are dropped and logged.
  • Matching upstream ICMP can be echo request or echo reply.
  • If check-identifier is enabled and the ICMP identifier does not match, the packet is forwarded unchanged rather than decapsulated.
  • source/dest are not checked during decapsulation.

Node Metadata

Source-backed metadata:

PropertyValue
node flagskNodeFlagNone
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer3
layer_group_prev_nodekNodeLayer3
layer_group_next_nodekNodeLayer3
required_padding_left28 bytes
line state size0

required_padding_left = 28 covers the worst-case prepend path:

20-byte IPv4 header + 8-byte ICMP echo header

Compatibility Aliases

The parser still accepts several historical misspellings and aliases:

  • warp-in-new-ip-and-icmp-header
  • wrap-in-new-ipv4-and-icmp-header
  • warp-in-new-ipv4-and-icmp-header
  • warp-in-icmp-header-and-reuse-ipv4-addresses
  • wrap-in-icmp-header-and-reuse-ip-addresses
  • warp-in-icmp-header-and-reuse-ip-addresses
  • wrap-in-icmp-header-and-update-ipv4-header
  • warp-in-icmp-header-and-update-ipv4-header
  • warp-in-only-icmp-header
  • wrap-payload-in-only-icmp-header
  • warp-payload-in-only-icmp-header
  • change-only-ip4-protocol-number
  • change-only-ip4-packet-identifier-number

Prefer the canonical strategy names in new configs.