The Normal TCP Three-Way Handshake
Before dissecting how SYN floods work, you need to understand what they exploit. Every TCP connection begins with a three-way handshake. The client sends a SYN, the server responds with a SYN-ACK, and the client completes the handshake with an ACK. In packet terms, this is three segments with specific TCP flag combinations.
Here is what a normal handshake looks like in tcpdump:
# Client initiates connection to web server on port 443 # Flags [S] = SYN, seq = initial sequence number (ISN) 14:32:07.481923 IP 203.0.113.50.48291 > 198.51.100.10.443: Flags [S], seq 2841029471, win 65535, options [mss 1460,sackOK,TS val 1938271 ecr 0,nop,wscale 7], length 0 # Server responds with SYN-ACK # Flags [S.] = SYN+ACK, server picks its own ISN, ack = client ISN + 1 14:32:07.482104 IP 198.51.100.10.443 > 203.0.113.50.48291: Flags [S.], seq 3104827539, ack 2841029472, win 65535, options [mss 1460,sackOK,TS val 8472910 ecr 1938271,nop,wscale 7], length 0 # Client completes the handshake with ACK # Flags [.] = ACK only, ack = server ISN + 1 14:32:07.482891 IP 203.0.113.50.48291 > 198.51.100.10.443: Flags [.], ack 3104827540, win 512, options [nop,nop,TS val 1938271 ecr 8472910], length 0
The important details: the client picks a random 32-bit initial sequence number (ISN) in the SYN packet. The server acknowledges it by sending ack = client_ISN + 1 and picks its own ISN for the reverse direction. The final ACK from the client acknowledges the server's ISN. At this point, the connection is ESTABLISHED on both sides, and data can flow.
The TCP flags in the header are single bits in a 6-bit field. SYN is bit 1 (0x02), ACK is bit 4 (0x10), and SYN-ACK is both set (0x12). These values matter when you are writing packet filters.
What a SYN Flood Actually Does
A SYN flood sends thousands or millions of SYN packets per second but never sends the final ACK. The server receives each SYN, allocates memory for the half-open connection in its SYN queue, sends back a SYN-ACK, and waits. The connection sits in the SYN_RECV state, consuming a slot in the kernel's backlog.
The kernel maintains a per-listener SYN backlog queue. Its size is controlled by:
# Maximum number of half-open connections waiting for the final ACK cat /proc/sys/net/ipv4/tcp_max_syn_backlog # Default: 128 on older kernels, 1024 or 4096 on modern distributions # The effective limit is also bounded by the listen() backlog parameter # For nginx: listen 443 backlog=511; # For Apache: ListenBacklog 511
Each slot in the SYN queue stores the source IP, source port, destination port, the server's ISN, TCP options (MSS, window scale, SACK), and a timestamp. On a 64-bit kernel, this is roughly 64 to 128 bytes per entry depending on the options negotiated. The memory cost is modest, but the slot count is the bottleneck. Once the queue is full, the server cannot accept new legitimate connections.
Source IP Spoofing: Why SYN Floods Use Random IPs
Effective SYN floods use spoofed source IP addresses. The attacker crafts raw packets with random source IPs in the IP header. When the server sends its SYN-ACK reply, it goes to the spoofed address, not the attacker. That host either does not exist, is unreachable, or has no knowledge of the connection and silently drops the SYN-ACK (or sends an RST).
This has two effects. First, the server never receives the completing ACK, so the half-open connection lingers in the SYN queue until it times out (typically 75 seconds by default, controlled by tcp_synack_retries). Second, the spoofed IPs make it nearly impossible to block the attack by source address, because each packet comes from a different IP.
A SYN flood generating 50,000 packets per second with random source IPs creates 50,000 unique source addresses per second. Over 75 seconds (the default SYN-ACK retry timeout), that is 3.75 million half-open connection attempts. No SYN queue is that large.
What SYN Flood Traffic Looks Like in tcpdump
During an active SYN flood, running tcpdump -nn -i eth0 'tcp[tcpflags] & tcp-syn != 0' produces output like this:
14:47:03.102847 IP 91.243.18.207.29184 > 198.51.100.10.443: Flags [S], seq 847291053, win 64240, length 0 14:47:03.102851 IP 172.98.47.3.51902 > 198.51.100.10.443: Flags [S], seq 2910374821, win 64240, length 0 14:47:03.102854 IP 45.129.88.241.12740 > 198.51.100.10.443: Flags [S], seq 1029384756, win 64240, length 0 14:47:03.102858 IP 103.214.97.112.43891 > 198.51.100.10.443: Flags [S], seq 3847102938, win 64240, length 0 14:47:03.102861 IP 185.73.24.198.60123 > 198.51.100.10.443: Flags [S], seq 1938472610, win 64240, length 0 14:47:03.102864 IP 212.47.139.85.8847 > 198.51.100.10.443: Flags [S], seq 2048571936, win 64240, length 0 14:47:03.102867 IP 78.193.42.17.33410 > 198.51.100.10.443: Flags [S], seq 917384052, win 64240, length 0 14:47:03.102871 IP 194.61.28.93.47281 > 198.51.100.10.443: Flags [S], seq 3019284710, win 64240, length 0 14:47:03.102874 IP 37.120.209.44.19302 > 198.51.100.10.443: Flags [S], seq 1847392017, win 64240, length 0 14:47:03.102877 IP 155.94.73.168.55190 > 198.51.100.10.443: Flags [S], seq 2738491025, win 64240, length 0
Notice the pattern: every packet comes from a different source IP and source port. The destination is always the same (your server on port 443). The timestamps are microseconds apart, showing dozens of SYNs arriving per millisecond. The window size is often identical across all packets (64240 in this case), which is a telltale sign of a tool-generated flood, because real clients negotiate different window sizes based on their OS and network stack.
Meanwhile, your server is dutifully replying to every single one of these with a SYN-ACK:
14:47:03.102849 IP 198.51.100.10.443 > 91.243.18.207.29184: Flags [S.], seq 3827104958, ack 847291054, win 65535, length 0 14:47:03.102853 IP 198.51.100.10.443 > 172.98.47.3.51902: Flags [S.], seq 1029384562, ack 2910374822, win 65535, length 0 14:47:03.102856 IP 198.51.100.10.443 > 45.129.88.241.12740: Flags [S.], seq 2847103948, ack 1029384757, win 65535, length 0
These SYN-ACKs are going to spoofed addresses. None of them will ever get a completing ACK. Your server is wasting bandwidth sending replies to ghosts.
Wireshark Analysis of a SYN Flood
When you open a PCAP of SYN flood traffic in Wireshark, the first thing to do is filter for SYN-only packets:
# Wireshark display filter: SYN packets that are NOT SYN-ACKs tcp.flags.syn == 1 && tcp.flags.ack == 0 # Count unique source IPs in the capture # Statistics > Endpoints > IPv4 tab # During a flood you will see thousands of unique IPs, each with 1-2 packets # Conversation view (Statistics > Conversations > TCP tab) # Normal traffic: dozens of conversations with hundreds of packets each # SYN flood: thousands of conversations with exactly 1 packet each (the SYN)
The Statistics > Endpoints view is the most revealing. In normal traffic, you see a small number of client IPs with many packets each. During a SYN flood, you see thousands of unique source IPs, each responsible for exactly one packet. This pattern, many sources with one packet each, all targeting the same destination port, is the definitive Wireshark signature of a spoofed SYN flood.
You can also graph the SYN rate over time using Statistics > I/O Graphs with the display filter tcp.flags.syn == 1 && tcp.flags.ack == 0. A normal server sees perhaps 50 to 200 SYNs per second. A SYN flood will show tens of thousands to hundreds of thousands per second, often arriving in sharp bursts.
Kernel Counters During a Flood
The Linux kernel exposes detailed TCP statistics through /proc/net/snmp and /proc/net/netstat. During a SYN flood, several counters spike dramatically:
# Read the critical counters during an active flood
awk '/^TcpExt:/{split($0,h);getline;split($0,v);for(i in h){if(h[i]~/Syncookies|ReqQFull/)printf "%s = %s\n",h[i],v[i]}}' /proc/net/snmp
# Typical output during a moderate SYN flood (sampled 10 seconds apart):
# First sample:
TcpExtSyncookiesSent = 1847291
TcpExtSyncookiesRecv = 312
TcpExtSyncookiesFailed = 47
TcpExtTCPReqQFullDrop = 0
TcpExtTCPReqQFullDoCookies = 1847291
# Second sample (10 seconds later):
TcpExtSyncookiesSent = 2319847
TcpExtSyncookiesRecv = 598
TcpExtSyncookiesFailed = 83
TcpExtTCPReqQFullDrop = 0
TcpExtTCPReqQFullDoCookies = 2319847
The delta tells the story: 472,556 SYN cookies were sent in 10 seconds, which means roughly 47,000 SYNs per second were overflowing the backlog queue. Only 286 of those came back with a valid ACK (the SyncookiesRecv delta), confirming that over 99.9% of the source IPs were spoofed.
You can also use netstat -s to see cumulative TCP statistics in a more readable format:
netstat -s | grep -i "syn"
2319847 SYN cookies sent
598 SYN cookies received
83 invalid SYN cookies received
47291 times the listen queue of a socket overflowed
ss -tn state syn-recv | wc -l
# Output during flood: 4096 (saturated at tcp_max_syn_backlog)
SYN Cookies: The Kernel's Built-In Defense
SYN cookies are a clever kernel-level defense that eliminates the need for a SYN queue entirely. When enabled and the SYN backlog is full, the kernel encodes the connection state directly into the ISN of the SYN-ACK packet. It computes a hash of the source IP, source port, destination IP, destination port, and a secret key, then packs the MSS value and a timestamp into the remaining bits.
When the ACK comes back, the kernel recomputes the hash from the packet headers and verifies the sequence number. If it matches, the connection is valid, and the kernel creates the socket without ever having stored anything in the SYN queue. If the ACK never comes (which is the case for spoofed IPs), no resources were consumed at all.
# Check if SYN cookies are enabled (1 = enabled) cat /proc/sys/net/ipv4/tcp_syncookies # 1 # Enable SYN cookies if not already on sysctl -w net.ipv4.tcp_syncookies=1 # The tradeoff: SYN cookies cannot encode all TCP options # Window scaling and SACK are lost when a connection is established via SYN cookie # This means connections established during a flood may have reduced performance
SYN cookies activate automatically when the backlog is full. You do not need to do anything except ensure
tcp_syncookiesis set to 1. On nearly all modern distributions, this is the default. The kernel seamlessly falls back to SYN cookies under pressure and returns to normal queue behavior when the flood subsides.
Conntrack Table Exhaustion
If your server uses iptables or nftables with connection tracking (which is the default if you have any stateful firewall rules), the SYN flood has a second attack surface: the conntrack table. Every incoming SYN creates a conntrack entry in NEW state, regardless of whether the TCP handshake completes. These entries persist for nf_conntrack_tcp_timeout_syn_recv seconds (default: 60).
# Current conntrack table state during a flood cat /proc/sys/net/netfilter/nf_conntrack_count # 261847 cat /proc/sys/net/netfilter/nf_conntrack_max # 262144 # The table is 99.9% full. New connections will be dropped. # Check dmesg for the smoking gun: dmesg | tail -5 # [482910.273849] nf_conntrack: table full, dropping packet # [482910.274102] nf_conntrack: table full, dropping packet # [482910.274387] nf_conntrack: table full, dropping packet
When the conntrack table fills, the kernel drops all new packets that would create a conntrack entry. This affects everything: new TCP connections, UDP packets, ICMP. Even established connections can be disrupted if their conntrack entries expire and cannot be recreated. This is often more damaging than the SYN queue exhaustion itself.
Mitigation for conntrack exhaustion includes increasing the table size, reducing timeouts for half-open connections, or using the NOTRACK target in the raw table to bypass conntrack for specific traffic:
# Increase conntrack table size (temporary, immediate effect) sysctl -w net.netfilter.nf_conntrack_max=1048576 # Reduce timeout for SYN_RECV entries (default 60, reduce to 10) sysctl -w net.netfilter.nf_conntrack_tcp_timeout_syn_recv=10 # Skip conntrack entirely for traffic to your web server port iptables -t raw -A PREROUTING -p tcp --dport 443 -j NOTRACK iptables -t raw -A OUTPUT -p tcp --sport 443 -j NOTRACK
Detection Signatures
Automated detection systems look for a combination of signals that together form a high-confidence SYN flood signature:
- High SYN-to-ACK ratio. Normal traffic has roughly a 1:1 ratio of SYN packets to completing ACKs. During a SYN flood, the ratio is 100:1 or higher because spoofed sources never complete the handshake.
- Many unique source IPs. A burst of 10,000 SYNs from 10,000 different IPs in one second is not organic traffic. Legitimate traffic spikes come from a moderate number of IPs with multiple requests each.
- SYN-RECV state buildup. The
ss -tn state syn-recvcount climbing towardtcp_max_syn_backlogis a direct indicator. Normal servers rarely have more than 10 connections in this state. - SyncookiesSent rate. The derivative of
TcpExtSyncookiesSentover time. Any non-zero rate means the backlog is full. A rate above 100/second is a confirmed flood. - Uniform packet characteristics. Tool-generated floods often have identical window sizes, identical TTL values, and no TCP options variation. Real clients show diversity in these fields.
# Quick detection script combining multiple signals
#!/bin/bash
SYN_RECV=$(ss -tn state syn-recv | wc -l)
COOKIES=$(awk '/^TcpExt:/{split($0,h);getline;split($0,v);for(i in h)if(h[i]=="TcpExtSyncookiesSent")print v[i]}' /proc/net/snmp)
CT_COUNT=$(cat /proc/sys/net/netfilter/nf_conntrack_count)
CT_MAX=$(cat /proc/sys/net/netfilter/nf_conntrack_max)
echo "SYN_RECV sockets: $SYN_RECV"
echo "SYN cookies sent: $COOKIES"
echo "Conntrack usage: $CT_COUNT / $CT_MAX"
if [ "$SYN_RECV" -gt 500 ]; then
echo "WARNING: Elevated SYN_RECV count - possible SYN flood"
fi
How Flowtriq Detects SYN Floods in Under 1 Second
Flowtriq's node agent polls /proc/net/snmp, /proc/net/netstat, and conntrack counters every second. It tracks the rate of change for TcpExtSyncookiesSent, TcpExtTCPReqQFullDoCookies, and the SYN-RECV socket count. Rather than relying on static thresholds that generate false positives during legitimate traffic spikes, Flowtriq maintains a rolling 24-hour baseline and triggers when the current rate exceeds the expected value by a statistically significant margin.
When a SYN flood is detected, Flowtriq immediately classifies the attack vector (SYN flood vs. SYN-ACK reflection vs. PSH-ACK flood), captures a PCAP sample for forensic analysis, and dispatches alerts to your configured channels: Slack, Discord, PagerDuty, OpsGenie, email, SMS, or webhook. The entire chain from first anomalous packet to alert delivery typically completes in under 2 seconds.
Because the detection happens at the kernel counter level and not through packet inspection, there is no performance overhead. The agent reads a few proc files once per second. It works on bare metal, VPS, cloud instances, and containers without requiring any changes to your network architecture or traffic routing.
Detect SYN floods before your backlog fills
Flowtriq monitors kernel-level TCP counters every second, classifies attack vectors automatically, and sends alerts to Slack, PagerDuty, or any webhook. $9.99/node/month with a free 7-day trial.
Start your free trial →