NEW RESEARCH: WHAT IS A LAYER 7 DDOS ATTACK, AND WHY YOUR HOST DOESN'T STOP IT.Read the report Sign in

Where a Flood Goes.

In October two HTTP/2 floods reached our edge, the larger at a million requests a second. Our reports described what arrived. This is where it went: through an XDP program in the network driver and an edge written in Rust, both our own, and how the two divide the work.

91.5 millionpackets reached our network card in the twelve minutes of the 23 October flood.
34.3 millionof them (37.5%) were ended by XDP in the driver, before the Linux kernel built a socket buffer for them.
24% → 42%the share ended at XDP in the flood's first minute and in its last.
0country or network blocklists. Every verdict came from what a client did on its own requests and connections.
The path of a packet

Two programs, one path

Every packet that reaches Shieldify on ports 80 and 443 passes through two programs we wrote ourselves:

  • Breakwave, an XDP program. XDP (eXpress Data Path) runs an eBPF program inside the network driver, on every packet as it arrives, before the Linux kernel allocates a socket buffer for it or runs it through the network stack. Ending a packet there is about as cheap as handling a packet gets in Linux.
  • The edge, a reverse proxy written in Rust. It terminates TLS, speaks HTTP/1.1 and HTTP/2, runs our checks and the web application firewall, caches, and forwards what passes to the website's own server, the origin.

Between the two sits the kernel's TCP stack, which we leave as it is.

Who decides

The edge decides, XDP enforces

XDP is fast because it sees little: a packet's addresses and ports, and the first bytes of a connection. It cannot see inside TLS, it cannot read a request, and it cannot tell one household from the next behind a shared address. The edge sees all of that: the whole request, the connection it came on, and how the client behaved across both.

So decisions are made in the edge, per client, on evidence from that client's own requests and connections. When the edge judges a client to be part of an attack, it writes the verdict into BPF maps, hash tables that the edge and the XDP program share. From the client's next packet on, XDP enforces the verdict in the driver.

Nothing is decided in XDP on its own. A client the edge has not judged is never dropped there: its packets go through to the edge, and the edge checks it. We do not drop traffic by country, by network or by reputation list, and during an attack we do not switch to letting only known clients in. The door stays open, and the evidence decides who is turned away.

What XDP does

What XDP does with a verdict

A verdict is narrow: it is about one attacker and the website it attacked. From then on XDP ends that attacker's packets to that website in the driver, before a TLS handshake or a request costs anything further up. Everyone else, on that website and on every other, passes as before.

Narrow matters because of who else is out there. Many addresses in these floods are home connections, and many home connections are shared by whole neighbourhoods behind carrier-grade NAT. A verdict about one device must not lock out the people next to it.

The flood from inside

Twelve minutes, seen from the driver

On 23 October the tool behind our 20 October flood came back for a login page. For just under twelve minutes, Breakwave counted every packet that arrived on ports 80 and 443, and every packet it ended:

Show the numbers
Minute (UTC)Packets arrivedEnded at XDPShare
02:497,932,6821,909,06524.1%
02:507,553,2822,898,23438.4%
02:518,299,8342,939,76335.4%
02:528,360,5182,946,06535.2%
02:538,102,5852,993,11036.9%
02:548,030,5432,970,81537.0%
02:557,774,9763,017,06438.8%
02:567,628,9413,048,24240.0%
02:577,518,5123,055,27140.6%
02:587,403,5603,083,53641.6%
02:597,396,5943,084,87941.7%
03:005,521,2172,333,33642.3%
Total91,523,24434,279,38037.5%

Of 91.5 million packets, 34.3 million never left the network driver. In the flood's first minute XDP ended 24% of its packets; by the last it ended 42%, as the edge's verdicts added up.

The shorter 20 October flood looked the same from the driver: about 37.3 million packets in three and a half minutes, of which 13.3 million (36%) ended at XDP, and up to 43% in a single minute.

Why not all of it

Why the rest still reaches the edge

If ending packets in the driver is so cheap, why did six in ten still go through to the edge? Because every device has to show itself before anything is decided about it. A client's first connection always reaches the edge, and these floods kept bringing new devices: on 20 October a new crowd arrived every minute, and on 23 October about 1.07 million addresses took part.

That is the price of never dropping a visitor we have not judged, and we pay it on purpose. It sets the edge's job: take the first requests of a million unknown devices, decide on each of them, and pass the verdict down before the device's next connection arrives.

Why Rust

Why we wrote the edge in Rust

Until 18 October 2026 our edge ran on OpenResty, nginx with LuaJIT. Behind it, in the background, we had been building our own proxy in Rust. On 18 October it took over, carrying the protection logic we had refined on OpenResty for months. Floods like these were the main reason for the move:

  • One process, shared state. nginx runs separate worker processes, and they share state through shared-memory dictionaries guarded by locks. Under a flood every request read and wrote that state, and the workers waited on each other. Our edge keeps its state in one process, in structures built for many threads.
  • HTTP/2 under our control. A flood is many streams on few connections. The edge decides on a stream as soon as its headers arrive, takes no more work from a connection it has already refused, and hands the client to XDP from the request path itself.
  • Verdicts straight into the maps. The edge writes the BPF maps itself, with no helper process in between, so a verdict reaches the driver as soon as it is made.
  • Memory safety where it matters most. The edge parses everything the internet sends it. Rust takes whole classes of memory bugs out of the code that does that.
What we leave out

What we leave out on purpose

No country or network blocks

These floods came from home connections in more than fifty countries, on the same networks real customers use. Blocking them blocks customers.

No "known clients only" mode

Under attack, a new visitor is still a visitor. Clients we have not judged are checked, never dropped.

No limits per address alone

Spread over a million addresses, each address sends its share slowly. A limit low enough to catch that also locks out people who share an address.

How we measured

Data and method

The packet counts come from Breakwave's own per-minute counters: every packet to ports 80 and 443 that reached the program, and every packet it ended. The counts are exact. Times are in UTC and minutes are whole UTC minutes, so the first and last minutes of a flood include a few seconds of ordinary traffic. Request and address figures come from our two flood reports. We publish no addresses, and we do not name the website.

More research

Under AttackRight Now?

Point one DNS record to Shieldify and protection starts, also in the middle of an attack.