Skip to content
LinkState
Go back

ICMP Is Not Enough for Reachability CI

Pre‑Deployment Reachability Gates: ICMP, TCP SYN, and Protocol‑Native Handshakes

Introduction

Pre‑deployment reachability gates are lightweight probes run before a configuration change, software rollout, or traffic shift. They verify that a target endpoint can be reached from the source under the expected network conditions. Three probe families are commonly used:

Probe familyLayerWhat it validates
ICMP Echo Request/ReplyIP (protocol 1)Basic IP‑layer reachability
TCP SYN handshakeTransport (TCP)Port‑level reachability and stateful‑firewall handling
Protocol‑native handshakeApplicationFull stack validation (TLS, HTTP, SSH, DB, etc.)

Each family trades off coverage, false confidence, and operational cost.

Why Use Reachability Gates?

The effectiveness of a gate depends on how closely the probe mirrors the actual data plane that will carry production traffic. The sections below dissect each probe family, enumerate strengths and weaknesses, and give concrete implementation patterns.


ICMP Echo Reachability

Protocol Overview

ICMP Echo Request (type 8, code 0) carries an identifier, sequence number, and optional payload. The responder returns an Echo Reply (type 0, code 0) mirroring those fields. The exchange occurs at IP layer 3; no transport‑layer state is created.

Advantages

Disadvantages

False Confidence

ICMP reachability does not guarantee higher‑layer service availability. Common sources of false confidence:

Failure modeWhy ICMP succeedsImpact on upper layers
ACL permits ICMP but blocks TCP/UDPICMP type 8/0 allowed, other protocols droppedApplication traffic blackholed
Rate‑limited ICMP (e.g., icmp ratelimit)Probe slips under limit, burst traffic droppedSudden loss under load
Asymmetric routing (ICMP returns via different path)Reply reaches source, forward path broken for dataOne‑way connectivity
Host‑based firewall: ICMP allow, application denyEcho reply generated, but service port filteredService unavailable
Path MTU black‑hole (ICMP Fragmentation Needed blocked)Echo fits within MTU, larger application packets droppedApplication‑level fragmentation failures

Thus, an ICMP pass confirms only IP‑layer reachability; it says nothing about transport‑layer policies, application‑layer authentication, or service‑specific QoS.

Security Considerations

Apply the same egress/ingress controls to ICMP probes as to any other traffic, and consider authenticating the probe source (IPsec/MACsec) if the network treats ICMP as a trusted control plane.


TCP SYN Handshake Reachability

Protocol Overview

The TCP three‑way handshake starts with a SYN packet (flag SYN=1) from client to a destination port. If the port is listening and not filtered, the server replies with SYN‑ACK (SYN=1, ACK=1). For a reachability probe the sender aborts after receiving SYN‑ACK (or after a timeout if no reply), producing a “half‑open” or SYN scan.

Advantages

Disadvantages

False Negatives

A SYN probe may be blocked or dropped even when the service is reachable via other means:

Resource Utilization

Mitigation: rate‑limit SYN probes, reuse sockets (e.g., non‑blocking connect() with immediate close), and monitor firewall SYN‑queue counters.


Protocol‑Native Handshake Reachability

Overview

Protocol‑native handshakes emulate the exact exchange a real client performs with the target service. Examples:

ProtocolNative handshake probe
TLS/HTTPSTCP SYN → TLS ClientHello → (optional) ServerHello → verify certificate
HTTP/1.1TCP SYN → HTTP GET request → parse response status line
SSHTCP SYN → exchange of protocol version strings → (optional) key‑exchange init
DNS (UDP/TCP)UDP query for a known record (e.g., hostname.example.com. IN A)
MySQLTCP SYN → handshake packet → authentication attempt (with dummy credentials)
gRPCTCP SYN → HTTP/2 preface → SETTINGS frame

The probe may stop after a successful response (e.g., TLS ServerHello with a valid cert) or after a defined number of round‑trips, depending on the validation goal.

Advantages

Disadvantages

Implementation Complexity

Limited Coverage for Non‑Standard Protocols

Despite these challenges, protocol‑native probes are preferred when the cost of a false negative (deploying a change that breaks service) outweighs the operational overhead.


Troubleshooting Reachability Issues

Identifying False Confidence

When a probe reports success but subsequent traffic fails, follow this checklist:

  1. Layer isolation – verify that the probe succeeded at the same layer as the failure (e.g., ICMP pass but TCP SYN fail → ACL issue).
  2. Path symmetry – run traceroute in both directions; asymmetric routes often manifest as ICMP success but TCP failure.
  3. Application‑layer validation – if the probe was ICMP or TCP SYN, repeat with a protocol‑native probe targeting the exact service port and payload.
  4. Stateful device logs – check firewall, NAT, or load‑balancer logs for dropped packets after the probe’s point of success.
  5. Rate‑limit and burst behavior – send a short burst of probes (e.g., 10 packets) and observe whether loss appears only under load.
  6. Payload inspection – for TCP SYN, capture the SYN‑ACK with tcpdump; if options like MSS or window scaling are stripped, a middlebox may be interfering.

Debugging ICMP Failures

# See if Echo Request leaves host and Echo Reply returns
tcpdump -i any icmp and host <dest>
# Check for rate‑limit messages
dmesg | grep -i icmp
# Verify sysctl settings
sysctl net.ipv4.icmp_echo_ignore_all
sysctl net.ipv4.icmp_echo_ignore_broadcasts

Debugging TCP SYN Failures

# Basic SYN probe with netcat
nc -zv -w2 <host> <port>
# Or with hping3
hping3 -S -p <port> -c 1 <host>
# Capture SYN‑flagged packets
tcpdump -Ss 0 -i any 'tcp[tcpflags] & tcp-syn != 0'
# Check SYN‑cookie status
cat /proc/sys/net/ipv4/tcp_syncookies
# Inspect SYN‑queue drops
netstat -s | grep -i syn
# or
ss -s

Analyzing Protocol‑Native Handshake Errors


Code Examples

ICMP Echo Request (Python)

#!/usr/bin/env python3
import socket
import struct
import time
import os

def icmp_ping(dest_addr, timeout=2, payload_size=32):
    """
    Send a single ICMP Echo Request and wait for Echo Reply.
    Returns (success: bool, rtt_ms: float|None)
    """
    # Requires CAP_NET_RAW or root on Linux
    icmp = socket.getprotobyname("icmp")
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
    except PermissionError as exc:
        raise RuntimeError("ICMP socket requires elevated privileges") from exc
    sock.settimeout(timeout)

    my_id = os.getpid() & 0xFFFF
    my_seq = 1
    header = struct.pack("!BBHHH", 8, 0, 0, my_id, my_seq)   # type, code, checksum, id, seq
    payload = b'Q' * payload_size
    packet = header + payload

    # Compute checksum
    def checksum(data):
        if len(data) % 2:
            data += b'\x00'
        s = sum(struct.unpack("!%dH" % (len(data)//2), data))
        s = (s >> 16) + (s & 0xffff)
        s += s >> 16
        return ~s & 0xffff

    chk = checksum(packet)
    header = struct.pack("!BBHHH", 8, 0, socket.htons(chk), my_id, my_seq)
    packet = header + payload

    send_time = time.time()
    sock.sendto(packet, (dest_addr, 0))

    try:
        recv_packet, addr = sock.recvfrom(1024)
        recv_time = time.time()
        # Extract ICMP header (first 8 bytes after IP header)
        icmp_header = recv_packet[20:28]
        icmp_type, code, checksum, packet_id, sequence = struct.unpack("!BBHHH", icmp_header)
        if icmp_type == 0 and packet_id == my_id and sequence == my_seq:
            rtt_ms = (recv_time - send_time) * 1000
            return True, rtt_ms
    except socket.timeout:
        pass
    finally:
        sock.close()
    return False, None

TCP SYN Probe (Python, non‑blocking)

#!/usr/bin/env python3
import socket
import time

def tcp_syn_probe(host, port, timeout=2):
    """
    Perform a SYN probe and return success if SYN‑ACK is received.
    Does not complete the three‑way handshake.
    """
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(False)
    try:
        sock.connect_ex((host, port))   # initiates SYN, returns EINPROGRESS
    except BlockingIOError:
        pass  # expected for non‑blocking connect

    start = time.time()
    while time.time() - start < timeout:
        ready = socket.select([sock], [], [], 0.5)[0]
        if ready:
            # Check if connection succeeded (SYN‑ACK received)
            err = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
            if err == 0:
                sock.close()
                return True
            else:
                # Connection refused or reset
                sock.close()
                return False
        # else timeout, continue loop
    sock.close()
    return False

Protocol‑Native TLS Probe (using OpenSSL CLI)

# Returns 0 on successful TLS handshake with cert verification
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null 2>/dev/null | \
  grep -q 'Verify return code: 0 (ok)' && echo "TLS OK" || echo "TLS FAIL"

*Replace example.com:443 with the target


Share this post on:

Previous Post
How much L2 stretch survives partial failure
Next Post
Identity labels churn faster than ACL templates