Flow-based DDoS Detection and RTBH

Network telemetry is still one of the quickest and most effective ways to spot security threats, from distributed denial-of-service attacks and data exfiltration to lateral movement and botnet activity.

DDoS attacks are now a constant - a sector overview puts the total at around 47.1 million attacks in 2025, roughly 1.5 every second, with the largest recorded attack reaching 31.4 Tbps (StationX, 2026). The same overview puts DDoS downtime at a cost of $22,000 per minute and it’s frequently used in geopolitical conflicts by hacktivist and state-sponsored groups. This is a real weapon that attackers are using today.

Service providers carry an even worse burden. An attack aimed at one customer still has to cross the provider's transit links to reach them and those links are shared. While one customer is being targeted, everyone else on the same uplinks is competing for the remaining bandwidth.

The Network Operations Centre (NOC) is under pressure to react fast but each option has a cost. A blackhole on all traffic would just finish the attacker’s job for them and lifting the blackhole too soon would bring the attacker straight back which is what happened in this lab. At the end of the day, customers remember outages not attacks.

A DDoS attack blackholed, successfully

How it works

~

How it works ~

Using nfdump for Telemetry

nfdump is a free, open-source set of tools for collecting and analysing flow data (NetFlow, IPFIX and sFlow) which are summaries routers send about the traffic passing through them. There are two main parts to it: nfcapd, which receives and stores the records and nfdump which queries them.

Telemetry becomes really useful when it shows you the traffic itself. Where it's coming from, where it's going, which protocols make up the volume and whether the path between source and destination is stable or efficient.

Using ExaBGP for Translation

Described as the BGP Swiss Army Knife of Networking, ExaBGP is a BGP implementation designed to enable network engineers and developers to interact with BGP networks using Python scripts or external programs via a simple API.

Unlike normal routing software, ExaBGP doesn't move any traffic. It just passes messages between programs and routers.

Under the hood

The detector is written in Python and runs on a virtual Ubuntu container in GNS3 just like my Rogue DHCP Server Detector.

  1. Every minute, EDGE (running a VyOS router) exports flow records and this function asks nfdump to total the bytes sent to each destination. SAMPLE_RATE scales the numbers back up if the router only samples some of the packets. It's set to 1 here but without it, every figure on a network would be wrong.

  2. The lookup tables give the targeted IP addresses an identity and show which autonomous system (AS) the traffic is coming from.

  3. Baseline for what normal looks like for each customer. The average traffic and how much it usually varies (the standard deviation, sigma or σ) over the last 30 minutes. Note: a steady customer has a small σ and a busy, spiky one has a large σ.

  4. Each minute, the detector compares traffic against each of the customer’s own threshold and learns each customer’s ‘resting heart rate’. It takes note of its variance and only raises the alarm when traffic jumps beyond it’s ‘resting rate’.

  5. Once the attack is presumed to be over, the detector checks every minute that it goes quiet. If the traffic being received is below the reset level, 1 will be added to the quiet counter. If the traffic is above it, the counter will be reset. After 5 quiet minutes, the blackhole is withdrawn.

  6. What I discovered between v1.0 and v1.1 was that the attacker was still launching the attack. In v1.0 the reset level was set too high for this customer so when the attack slowed down under the blackhole, the detector decided it was over and lifted the blackhole while the attacker was still sending. This is the most crucial and, in hindsight, the most obvious line of the detector.

Run 1 (v1.0) attack

Strictly speaking, this is a denial-of-service (DoS) attack but detection and remotely triggered black hole (RTBH) treat it the same way they would a distributed flood, by total traffic to the customer not by source.

  1. Calm before the storm. Constant background ping run from TRANSIT to 10.0.2.10 (CUSTOMER) establishes baseline

  2. Flood attack from ATTACKER (ping -f 10.0.2.10) intiated and detected by the script. Displays who’s being attacked, from where and how much (298x above baseline)

  3. User/operator then decides whether to intiate blackhole. If yes, an RTBH is annouced which comes less than a minute before but you’ll see this number improve in v1.1,

  4. The traffic is still visible on the EDGE, flow is measured per minute before the drop.

  5. After the third minute of detection, the traffic falls below reset level and the quiet counter starts.

  6. The output says 'CLEARED’ yet there’s still traffic…

What went wrong

  1. The command is investigating who was sending traffic to the customer between 12:24 and 12:28, when the detector said it was quiet

  2. 10.0.1.10 (ATTACKER): 1.7 MB, 99.1%. Highlighted in red, attacker never stopped.

  3. 10.0.12.1 (TRANSIT): 0.9%. The background ping.

Run 2 (v1.1) attack

Same story as Run 1 but with the reset calculation above which bases it on the customer's own traffic instead of the alert threshold. As you can see, the blackhole held for the attack and cleared only once the attack had stopped (stopping flood ping on ATTACKER terminal)

  1. Alert is activated, this time the traffic is 4683x above the baseline

  2. RTBH announced, 11s after the alert

  3. The slowed attack at 0.060, quiet 0/5

  4. One minute here reads 0.120 Mbps. Two of EDGE's 60s exports landed in the same one minute collector file. The detector was unaffected since the count only cares whether traffic is below the reset level. Blackhole held on.

  5. The flood is stopped, traffic drops to 0.001 Mbps and the quiet count finally starts.

  6. Cleared after five quiet minutes

  7. Back to normal with TRANSIT's ping

  1. Same command run during it’s quiet minutes

  2. 100% came from 10.0.12.1, TRANSIT's normal ping. The attacker is gone. TRANSIT sent the same 14,868 bytes as in Run 1's quiet window, but this time there's nothing else, so the quiet minutes really were quiet

Putting it all together

nfcapd stores the flows, nfdump reads them back. 1,000 pings x 84 bytes = 84,000 bytes

Once an alert is approved at the script’s prompt, the detector writes one line

Edge receives 10.0.2.10/32 from ExaBGP, follows the next hop 192.0.2.1 to the blackhole and drop the traffic

The detector declared the attack was over and withdrew the blackhole. Why is there still 0.061 Mbps of traffic hitting the customer?

Base the reset level on the customer’s own traffic and a 10 kbps floor

In v1.0 the blackhole came off while the attack was still going, and the customer got hit again a minute later. I changed one line for v1.1 and ran the same attack. This time the blackhole stayed on for the full 49 minutes and only came off after I stopped the flood.

I went in thinking detection would be the hard bit, but it wasn't. Knowing when the attack had actually finished was much harder than I expected

Nathan House. (October 2026). DDoS Statistics 2026: Attack Data, Records & Trends. [Online]. Station X. Last Updated: October 2026. Available at: https://app.stationx.net/articles/ddos-statistics [Accessed 6 October 2026].

Previous
Previous

ZTNA with Cloudflare