Skip to content

SYN floods and the history of TCP-layer denial of service

· 23 min read
Copyright: MIT
The words SYN FLOOD as a large monospace wordmark with an orange half-open arrow, over a faint three-way handshake diagram

In September 1996, a few hundred packets per second took New York’s oldest commercial ISP offline. Not a few hundred thousand. A few hundred. Panix was running mail and shell accounts for tens of thousands of customers, on hardware that could move real traffic, and an attacker armed with a tool that ran on a single machine made its servers stop answering for days. The packets were tiny. The source addresses were forged so the replies went nowhere. And the trick worked because of a counter buried in every TCP stack on earth: a small table of connections that have been started but not finished, and that fills up long before bandwidth or CPU does.

That table is the heart of the SYN flood, and the SYN flood is the oldest denial-of-service attack that still works in 2026. The exhaustion target is not the link and not the processor. It is a fixed-size queue of half-open connections, sized in the low dozens by default on the machines of the era, that an attacker can fill with state the server is obligated to hold and the attacker never pays for. This post is a mechanism-and-history piece. It walks through the three-way handshake and exactly where the half-open state lives, the Panix attack and the Phrack code that made it a commodity, the SYN cookie scheme that Daniel Bernstein and Eric Schenk worked out in the weeks right after Panix went down, the cost the cookie pays in lost TCP options and how Linux later clawed some of that back, and where the attack sits among today’s Layer 3/4 vectors.

The handshake and where the state lives

TCP opens every connection with a three-message exchange. The client sends a segment with the SYN flag set, carrying its initial sequence number. The server replies with a segment that has both SYN and ACK set, acknowledging the client’s sequence number and carrying its own. The client answers with an ACK, and the connection is established. This is the three-way handshake, and the reason it takes three messages rather than two is that both sides need to choose a starting sequence number and confirm the other side received it.

The vulnerable window is the gap between the second message and the third. When the server sends its SYN-ACK, it has committed to the connection. It has chosen a sequence number, it has to remember that number so it can validate the client’s eventual ACK, and it has to remember which client this half-finished connection belongs to. So it allocates a small block of state, a transmission control block, and parks it in a queue while it waits for the third message. The connection is in the SYN-RECEIVED state. RFC 4987, the IETF’s 2007 survey of the attack, calls this a half-open connection: the server has replied with a SYN-ACK and allocated resources, but has not yet received the final ACK that completes the handshake.

Client Server 1. SYN seq=x 2. SYN-ACK seq=y ack=x+1 3. ACK ack=y+1 SYN-RECEIVED state held waiting for message 3 *The server commits state when it sends message 2 and holds it until message 3 arrives. A SYN flood sends message 1 in bulk and never sends message 3, so the held state accumulates.*

That queue has a fixed capacity, the backlog. When an application calls listen() it passes a backlog argument, and the kernel caps the number of connections it will hold in the half-open and newly-established states for that listening socket. RFC 4987 notes that typical default backlog values of the era ran from a half-dozen to several dozen. The exact accounting differs across stacks and has changed over the years, but the shape is constant: there is a bound, it is small relative to the rate at which an attacker can send packets, and once it is full the server has to do something with new SYNs. The usual something is to drop them. A legitimate client whose SYN arrives at a full backlog gets no SYN-ACK, times out, and retries seconds later, by which point the queue is full again.

The half-open state also has a timeout. The server will not wait forever for the third message. It retransmits the SYN-ACK a few times in case it was lost, and after the retransmissions are exhausted it reclaims the slot. On many stacks this works out to somewhere around 75 seconds before a half-open entry is released, though the value is tunable. That timeout is the attacker’s friend and enemy at once: it is long enough that a modest packet rate keeps the queue saturated, and it is the parameter a defender reaches for first.

What the attacker actually sends

The attack is to send SYN segments and never complete the handshake. Each SYN that reaches a listening port creates a half-open entry. If the attacker can create them faster than the server reclaims them, the backlog stays full and legitimate SYNs get dropped. The arithmetic is brutally in the attacker’s favor. With a 75-second hold and a backlog of a few dozen, an attacker needs to land only a handful of SYNs per second to keep the queue pinned. The Panix attacker, by contemporary accounts, was sending on the order of 150 to 210 SYN packets per second, far more than the minimum, which is what made the outage so total.

There is a subtlety that makes the attack durable: the source addresses are spoofed, and they are spoofed specifically to addresses that will not answer. If the attacker used its real address, the server’s SYN-ACK would come back, the attacker’s own stack would see a SYN-ACK for a connection it never opened, and it would send a RST that tears down the half-open entry and frees the slot. That defeats the purpose. So the attacker forges source addresses that are unrouteable or belong to hosts that are down or silent. RFC 4987 lists this as one of three parameters that determine whether the attack succeeds: the size of each barrage, the frequency of barrages, and the choice of spoofed addresses that stay unresponsive to the SYN-ACKs. Get the third wrong and the victim’s own SYN-ACKs trigger resets that undo your work.

attacker spoofed SYNs backlog (half-open) SYN-RECEIVED SYN-RECEIVED SYN-RECEIVED FULL real client SYN dropped: no slot slot frees only after ~75s timeout *The exhaustion target is the queue, not the wire. A few hundred half-open entries per second is enough to keep a default backlog saturated, so the legitimate SYN never finds a slot.*

Notice what the attack does not require. It does not require a large botnet. It does not require high bandwidth, because SYN segments are tiny, on the order of 40 to 60 bytes plus options. It does not require the attacker to receive any return traffic at all, which is why spoofing works so cleanly here when it fails for connection-oriented attacks. The asymmetry is the whole story: the server pays for state and holds it for tens of seconds, the attacker pays for one small packet and walks away.

There is a second-order effect worth pulling out, because it is where the SYN flood quietly turns into someone else’s problem. Each spoofed SYN forces the victim to emit a SYN-ACK, and that SYN-ACK goes to whatever address was forged into the source field. If the attacker spoofs a single innocent third party’s address into every SYN, the victim becomes an unwitting source of SYN-ACKs aimed at that third party, a small reflection effect layered on top of the exhaustion attack. The reflected volume is bounded, since one SYN yields one SYN-ACK rather than the large multiplier of a true amplification vector, but it muddies attribution and can pull an uninvolved host into the incident. It is one reason the early advice converged so hard on filtering: a packet whose source address is forged is the common ingredient in the exhaustion attack, the reflection nuisance, and the difficulty of tracing any of it.

Panix, Phrack, and the autumn of 1996

The attack was not theoretical when Panix went down. The mechanics had been written up and, worse for everyone running a server, the code had been published. In July 1996, Phrack issue 48 carried “Project Neptune,” credited to daemon9 with route and infinity, a comprehensive analysis of TCP SYN flooding that shipped a working flooder. A companion piece, “IP-spoofing Demystified,” in the same era explained how to forge the source addresses the attack depends on. A second, deeper treatment followed in Phrack 49, published November 8, 1996. Whatever skill barrier had protected servers up to that point was gone. The recipe and the tool were in a magazine that every interested party read.

Panix’s mail service started failing under a SYN flood on September 6, 1996. The attacker spoofed source addresses and sent SYNs at the rate noted above, and the half-open queues on the mail servers filled and stayed full. Panix is sometimes described as New York’s first commercial ISP, and at the time only around 20 million Americans were online at all, so a sustained outage at a provider that size was front-page material in the small world of network operators. The reported recovery was not clean: accounts describe the better part of two days of disruption, with Panix working alongside peers to trace and filter the spoofed flows because there was no in-stack defense to simply switch on.

CERT made the attack official. CERT Advisory CA-1996-21, “TCP SYN Flooding and IP Spoofing Attacks,” issued September 19, 1996, described the half-open connection mechanism, noted that code to conduct the attack had recently appeared in underground magazines and was being used against live sites, and stated plainly that there was no complete solution at the time, only steps to lessen the impact. Read today, the advisory is a snapshot of a community discovering that a fundamental property of the protocol they all depended on could be turned into a weapon with a few hundred packets per second.

SYN cookies: state you do not have to keep

The defensive idea that outlasted all the others came together in the same weeks. The problem is that the server has to remember something between the SYN-ACK and the ACK: at minimum its own initial sequence number, so it can check that the returning ACK acknowledges the right value, plus enough about the connection to set it up if the ACK arrives. The insight, which Daniel Bernstein credits himself with stating on September 16, 1996 and then working out with Eric Schenk over the following weeks, is that the server is already sending the client a 32-bit number it is required to echo back: the initial sequence number in the SYN-ACK. If the server can pack everything it needs to remember into that number, it does not need to store anything at all. It can compute the sequence number from the connection’s parameters, send it, throw the connection away, and reconstruct the state from the ACK when and if it returns.

That is a SYN cookie. The initial sequence number stops being a random value the server has to remember and becomes a function the server can recompute. Bernstein’s own description lays out the encoding he settled on. The top 5 bits hold t mod 32, where t is a 32-bit counter that increases every 64 seconds. The next 3 bits hold an encoding of a maximum segment size the server selected in response to the client’s advertised MSS. The bottom 24 bits hold a server-chosen secret function of the client IP address and port, the server IP address and port, and t. Bernstein’s recommended construction for that secret value is to encode the inputs into 16 bytes, run them through Rijndael under a secret key, and take the first 24 bits of the output.

32-bit initial sequence number = the cookie 5 bits t mod 32 3 bits MSS idx 24 bits keyed hash of (client IP:port, server IP:port, t) coarse clock 8 values unforgeable without the secret key ACK arrives carrying seq+1; server recomputes the hash and checks t is still recent *The cookie packs a coarse timestamp, an MSS index, and a keyed hash into the one 32-bit field the client is forced to echo. No per-connection memory is held until the ACK proves the client received the SYN-ACK.*

When the ACK comes back, it carries the cookie value plus one in its acknowledgment field. The server recomputes the hash from the connection parameters and the current time counter, checks that the returning value matches and that t is recent enough to be valid, and if both hold it trusts that this is a real client that received the SYN-ACK it sent. Only then does it build the connection state. A spoofed SYN that never produced a real client gets a SYN-ACK into the void and costs the server nothing, because the server kept nothing. The backlog can no longer be exhausted, because under cookie mode the backlog is bypassed entirely for the half-open phase.

The coarse timestamp in the top bits is what bounds the cookie’s lifetime, and that bound is load-bearing in two directions. It has to be loose enough that a legitimate client’s ACK, delayed by a slow path or a couple of retransmissions, still arrives while the encoded t counts as recent. It has to be tight enough that an attacker who captures or guesses a valid cookie cannot replay it indefinitely. With t advancing every 64 seconds and only the low bits carried, a cookie is acceptable for a small window of counter values and then stops validating, which forces any replay to be near-real-time. The 24-bit hash is the other half of the guarantee: without the server’s secret key, an attacker cannot produce a value that will pass the recomputation, so it cannot fabricate a completing ACK for a connection it never legitimately started. The security of the whole scheme rests on that key staying secret and on the hash being hard to invert, which is why Bernstein reached for a real block cipher rather than a cheap checksum.

It is worth being precise about what a cookie does and does not defeat. It defeats the spoofed flood completely, because a spoofed source can never return the ACK that the cookie demands. It does nothing about a flood from real, non-spoofed addresses that do complete the handshake, because those produce genuine connections and genuine state. That is a different attack with a different answer, and it is why cookies are described as a SYN-flood defense specifically rather than a general anti-DDoS measure.

Schenk’s specific contribution, in Bernstein’s telling, was the idea of adding the cookie to the client’s initial sequence number rather than replacing it, which let the scheme respect TCP’s requirement that sequence numbers advance. The first running implementation was Jeff Weisberg’s for SunOS, about a month after the idea; Schenk released a Linux implementation in February 1997; FreeBSD picked it up in version 4.5, in early 2002. The technique was worked out by people who could see the Panix outage and the Phrack code in front of them and wanted a defense that did not depend on tracing spoofed packets after the fact.

A SYN cookie is not free, and the cost is the reason it is a fallback rather than the default behavior. The server normally records a handful of things from the client’s SYN that it needs for the rest of the connection: the negotiated maximum segment size, whether window scaling is in use and by how much, whether selective acknowledgment is supported, and so on. These ride in TCP options on the SYN. When the server discards its half-open state and keeps only a 32-bit cookie, almost all of that information is gone. There are only so many bits to work with, and they are mostly spent on the hash that makes the cookie unforgeable.

The classic scheme keeps the MSS, because it has to, encoded into those 3 bits. Three bits means the server is restricted to 8 distinct MSS values it can round a client’s advertised MSS to. Everything else the SYN options carried is lost. RFC 4987 is blunt about the consequence: commonly implemented cookie schemes are incompatible with some TCP options, window scaling among them, and so cookies can reduce performance. The Linux kernel documentation is blunter still. Its note on net.ipv4.tcp_syncookies warns that syncookies seriously violate the TCP protocol, do not allow use of TCP extensions, and can cause serious degradation of some services, giving SMTP relaying as the example. The same note insists syncookies are a fallback facility that must not be used to help a highly loaded server cope with a legitimate connection rate, and points administrators at tuning the backlog and the retry counts instead.

That is why on Linux the default, value 1, does not turn cookies on for every connection. It arms them. The kernel handles SYNs the normal way, queuing half-open state in the backlog, right up until the backlog for a socket overflows. Only then does it start answering with cookies, for the duration of the overflow. Connections that complete during calm periods keep their full set of TCP options; cookies, with their option penalty, kick in only when the alternative is dropping SYNs entirely. Setting the sysctl to 2 forces cookies unconditionally, which exists mainly for testing. The original Linux implementation also had a bug worth remembering: it misread Bernstein’s description and used a single global variable to toggle cookies across all ports, which became CVE-2001-0851.

Some of the lost ground was later recovered with a second echo channel. The TCP timestamp option, when both sides support it, gives the server another value the client is required to echo back. Florian Westphal and Glenn Griffin built on that: the server shaves bits off the timestamp it sends in the SYN-ACK and uses them to carry the window scale, SACK permission, and ECN state that the bare sequence-number cookie could not. The LWN writeup of the work describes shaving the least significant 9 bits of the timestamp, which costs the timestamp its bottom 512 jiffies of resolution but lets the cookie path preserve the high-throughput options. Modern Linux encodes window scaling, selective acknowledgment, and related state this way and restores it when a valid cookie-ACK arrives carrying the echoed timestamp. The performance penalty of cookies is therefore much smaller on a current stack with timestamps enabled than the RFC 4987-era description implies, though it is not zero, and it still requires the client to have offered the timestamp option in the first place.

The other mitigations, and why cookies won

SYN cookies are not the only defense, and for completeness they sit in a row of techniques RFC 4987 catalogs, each trading off differently. The first and most structural is filtering. Because the attack depends on spoofed source addresses, network operators deploying ingress filtering, the practice codified in BCP 38, can drop packets whose source address could not legitimately have come from the network they arrived on. Filtering does not protect a single server so much as deny attackers the ability to spoof in the first place, and the Panix episode is widely cited as the event that pushed source-address filtering from good idea to best common practice. It is also the defense that has aged worst in coverage: decades on, spoofing is still possible from too many corners of the internet for filtering alone to have closed the attack.

The stack-local defenses come in grades. Increasing the backlog buys headroom but does not change the asymmetry; the attacker just sends more, and very large backlogs ran into algorithmic inefficiencies in the data structures of the day. Reducing the SYN-RECEIVED timer reclaims slots faster, which raises the packet rate an attacker needs to keep the queue full, but cut it too short and you start dropping the slow legitimate clients the timer exists to wait for. Recycling the oldest half-open entry when the queue overflows keeps a server answering under light attack but collapses under a fast one, since the attacker’s fresh SYNs evict each other and any real client in the queue along with them. None of these change the fundamental shape; they move the threshold.

The SYN cache is the closer cousin to cookies. Instead of allocating a full transmission control block per half-open connection, the stack keeps a much smaller hash-table entry, with secret bits mixed into the hash to resist a targeted attacker, and only promotes it to a full TCB when the handshake completes. That keeps real per-connection state, so it does not pay the cookie’s option penalty, but it still keeps state, so a large enough flood can exhaust the cache. Several stacks run a hybrid: a SYN cache under normal load, falling back to cookies when even the cache is overwhelmed. And in front of the server entirely sit firewalls and proxies that complete the handshake on the server’s behalf, absorbing the flood at a box built to hold far more half-open state, and only opening a connection to the real server once a client has proven itself.

half-open state held per pending connection bigger backlog full TCB per SYN SYN cache (small entry) SYN cookies ~zero, until ACK none more cookies hold nothing but pay in lost TCP options; the cache keeps options but can still be exhausted *The defenses trade memory against fidelity. Cookies push held state to zero by discarding the options that need storage; the SYN cache keeps a trimmed entry, so it preserves options but can still fill up.*

Cookies won the default-on slot in most stacks not because they are strictly best but because they fail gracefully and need no cooperation from anyone else. Filtering needs every network operator to participate. A bigger backlog needs tuning and still loses to a bigger flood. A scrubbing proxy needs a box in front of the server. Cookies need nothing but the server itself, they cost real state only when the server was about to start dropping SYNs anyway, and even their option penalty has been whittled down. That combination is why a technique sketched out in the weeks after Panix is still compiled into the kernel running this page.

Where the attack stands in 2026

Thirty years on, the SYN flood has not retired. Cloudflare’s quarterly DDoS reports put SYN floods near the top of Layer 3/4 vectors throughout the recent period: 31% of network-layer attacks in the first quarter of 2025, slipping to 27% in the second, and falling to third place in the third quarter only because a surge of UDP floods, much of it attributed to the Aisuru botnet, briefly displaced it rather than because SYN floods themselves declined. Across 2025, Cloudflare reported mitigating an average of 3,925 network-layer attacks every hour, more than triple the network-layer volume of the year before. The SYN flood is a large, durable slice of that.

What has changed is everything around the single server the original attack targeted. A flood that once exhausted one mail server’s backlog now has to exhaust a defender that may be anycast across hundreds of points of presence, fronted by scrubbing centers built to terminate handshakes and validate clients at a scale no 1996 host could imagine. The stack-level cookie still does its job for the individual machine, but the load-bearing defense for anyone who is a target has moved upstream into the network, into the scrubbing centers and anycast dispersion that absorb volumetric floods and into the rate-limiting machinery that decides which connections are worth completing. The SYN flood also rarely travels alone now. It shows up as one vector in multi-vector campaigns, paired with UDP amplification or with the patient application-layer attacks that exhaust a server in the opposite way, by holding connections open rather than refusing to finish them, in the style of the slow-attack family.

There is a tidy lesson in the half-open queue that the rest of computer security keeps relearning. The vulnerability was never a bug. The three-way handshake does exactly what it was designed to do, and holding state for an incomplete connection is the correct, necessary behavior for the protocol to work at all. The attack simply made the server hold that state on the attacker’s schedule instead of a real client’s. Every asymmetric resource-exhaustion attack since, from connection-holding slow attacks to the HTTP/2 reset trick, is the same observation applied to a different counter: find the piece of state a server is obligated to keep on a stranger’s say-so, and make it keep too much. SYN cookies answered the original by refusing to keep the state at all and recomputing it from a number the client cannot avoid handing back. That move, do not store what you can derive from what the other side must echo, turned out to be one of the more durable ideas in network defense, and it was worked out in a few weeks by two people watching an ISP get knocked off the air with a few hundred packets per second.


Sources & further reading

  • Eddy, W. / IETF (2007), RFC 4987: TCP SYN Flooding Attacks and Common Mitigations — the canonical survey of the attack, the backlog mechanism, the three attack parameters, and the full catalog of mitigations and their tradeoffs.
  • Bernstein, D. J., SYN cookies — the inventor’s own page, with the exact sequence-number encoding (5-bit timestamp, 3-bit MSS, 24-bit keyed hash), the history with Eric Schenk, and the Rijndael construction.
  • CERT/CC (1996), CA-1996-21: TCP SYN Flooding and IP Spoofing Attacks — the September 1996 advisory that made the attack official and noted the published attack code.
  • daemon9, route, infinity (1996), Project Neptune (Phrack 48, file 13) — the Phrack analysis and flooder that turned the attack into a commodity.
  • Wikipedia, SYN flood — handshake, half-open definition, the Panix attack, and the countermeasure list, with primary citations.
  • Wikipedia, SYN cookies — the bit layout, the September 1996 invention, implementation timeline, and CVE-2001-0851.
  • The Linux kernel community, IP Sysctl documentation: tcp_syncookies — the verbatim warning that syncookies violate TCP, disable extensions, and are a fallback, not a load-handling tool.
  • Corbet, J. / LWN (2008), Improving syncookies — Westphal and Griffin’s timestamp-based recovery of window scaling and SACK, including the 9-bit shave.
  • Cloudflare (2025), DDoS threat report for 2025 Q2 — SYN floods at 27% of Layer 3/4 attacks, down from 31% in Q1.
  • Cloudflare (2025), 2025 Q4 DDoS threat report — full-year network-layer volume, the 3,925 attacks-per-hour average, and the record 31.4 Tbps peak.
  • Cloudflare, SYN Flood DDoS Attack — a plain-language walkthrough of the handshake and the half-open state for the spoofed-source case.
  • SmarterMSP (2021), Tech Time Warp: why the Panix attack was a wake-up call — the Panix timeline, packet rate, and recovery account.

Further reading