DDoS Mitigation at the Network Edge: A Layered Approach

Kicked TeamJanuary 5, 20267 min read

Run anything with a public IP for long enough and you will meet a denial-of-service attack. The uncomfortable part is that there is no single control that handles them: the technique that absorbs a 200 Gbps UDP reflection is useless against 100k requests per second of perfectly well-formed HTTP.

This is a tour of the layers, what each one costs, and where each one stops working. It is written from the operator's side of the problem rather than as a product pitch.

The Threat Landscape

The attack types a network operator typically contends with:

Attack Type Typical Volume Protocol
UDP reflection (DNS, NTP, memcached) 10–400 Gbps UDP
SYN floods 1–50 Mpps TCP
HTTP floods (L7) 50k–500k RPS HTTP/S
Carpet bombing (spread across a /24) 50–200 Gbps Mixed
GRE/IP-in-IP floods 10–100 Gbps GRE

The key insight: volumetric attacks and application-layer attacks require completely different mitigation strategies. A single solution doesn't cut it.

Note the volumes. Most of that table exceeds the transit capacity of a small or mid-sized network outright — which is the whole reason the upstream layers below exist. If an attack is larger than your port, nothing you do on your own routers will save you, because the congestion is already upstream of them.

Layer 1: Transit Provider Filtering

The first line of defence is before traffic reaches your routers at all. Things worth arranging with every transit provider:

  • Filter bogons — RFC 1918 addresses, unallocated space, and your own prefixes should never arrive from transit
  • Rate-limit ICMP — there is no legitimate use case for gigabits of ICMP
  • Community-based blackholing — tag a prefix with the provider's blackhole community (RFC 7999 defines a well-known one) and they drop it upstream, before it reaches your port

Blackholing is a blunt instrument: it completes the attack's goal for the targeted address while saving everything else. That trade is usually worth it, and it is often the only lever that works when the attack is bigger than your capacity.

Layer 2: BGP Flowspec

Flowspec distributes traffic filtering rules via BGP. Think of it as a firewall rule that propagates across every edge router in seconds.

# Drop UDP traffic to a specific host on port 53
# (a common DNS amplification target)
flow {
  match destination 185.x.x.10/32;
  match protocol udp;
  match destination-port 53;
  then discard;
}

The power of Flowspec is speed and scope. One BGP update and every edge router drops the traffic — no SSH-ing into routers, no manual ACL changes. It is far more surgical than a blackhole, because it can match on protocol and port rather than discarding everything to an address.

A common automation chain looks like:

  1. sFlow samples packets at, say, 1:1000
  2. FastNetMon analyses flows in real time
  3. On detection, it pushes a Flowspec rule via ExaBGP
  4. Edge routers begin dropping within seconds

The caveat: Flowspec rules are still enforced on your own routers, so the traffic has already crossed your transit link. It protects the network behind the edge, not the edge itself.

Layer 3: XDP/eBPF Filtering

For attacks needing more intelligence than "drop all UDP to port X", XDP (eXpress Data Path) runs a filtering program at the NIC driver level, before the kernel networking stack — so packets can be inspected and dropped at high rates with modest CPU cost.

// Simplified XDP program — drop packets matching an attack signature
SEC("xdp")
int xdp_ddos_filter(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end)
        return XDP_PASS;

    if (eth->h_proto != htons(ETH_P_IP))
        return XDP_PASS;

    struct iphdr *ip = (void *)(eth + 1);
    if ((void *)(ip + 1) > data_end)
        return XDP_PASS;

    if (ip->protocol == IPPROTO_UDP) {
        struct udphdr *udp = (void *)ip + (ip->ihl * 4);
        if ((void *)(udp + 1) > data_end)
            return XDP_PASS;

        // Check against attack signatures in a BPF map
        if (is_attack_signature(udp, data_end))
            return XDP_DROP;
    }

    return XDP_PASS;
}

XDP earns its place for:

  • SYN floods — SYN cookie validation at wire speed
  • Carpet bombing — per-IP rate limiting across a /24 without blackholing legitimate traffic
  • Protocol anomalies — dropping malformed packets before they waste CPU

Layer 4: Scrubbing Centres

For volumetric attacks that exceed your port capacity, traffic is diverted through a scrubbing provider using BGP:

  1. Attack detected
  2. The targeted prefix is announced to the scrubbing provider
  3. Traffic flows through the scrubber, which removes attack traffic
  4. Clean traffic is tunnelled back over GRE
  5. When the attack stops, the announcement is withdrawn and traffic flows directly again

This is the layer that actually addresses attacks larger than your own capacity, because the scrubbing happens on someone else's much larger network. The cost is latency — the tunnel detour is real — so it is usually reserved for attacks the cheaper layers cannot absorb.

Layer 5: Application-Layer Protection

L7 attacks bypass everything above, because they use legitimate TCP connections and well-formed requests. There is no packet signature to match. The tools here are different:

  • Rate limiting per IP, per session, per endpoint
  • Challenge pages — proof-of-work or JavaScript challenges that trivial bots fail
  • Behavioural analysis — legitimate users don't request the same endpoint 1000 times per second with identical headers
  • WAF rules — blocking attack patterns (SQLi, XSS) often mixed into DDoS campaigns

Detection: Seeing Attacks Before They Hit

You can't mitigate what you can't see. A conventional detection pipeline:

sFlow (sampled packets)
  → FastNetMon (real-time flow analysis)
    → Anomaly detection (baseline comparison)
      → Threshold alerts + auto-mitigation
        → Dashboard (visibility)
          → On-call notification (if manual intervention is needed)

Baselines matter more than absolute thresholds. A gaming server that normally receives 5 Gbps of UDP isn't alarming; a web server that suddenly receives 5 Gbps of UDP definitely is. A threshold with no baseline behind it produces either constant false alarms or silence.

A Note on Detection vs. Mitigation

Worth being precise about, because the two get conflated in marketing copy: detecting an attack and stopping it are separate capabilities. An IDS in alert-only mode gives you visibility and nothing else — traffic is inspected, logged, and delivered exactly as it would have been otherwise.

That is a legitimate place to start. Inline blocking carries its own risk: a filter that drops a percentage of real traffic can cost more than the attack. Running detection first, building confidence in what the rules would have caught, and only then arming them is the conservative path.

Just don't let a dashboard full of detections be mistaken for mitigation.

Lessons

  1. Automate — manual mitigation is too slow for modern attacks
  2. Layer defences — no single tool handles all attack types
  3. Know your ceiling — if an attack exceeds your transit capacity, only upstream layers help
  4. Watch the false-positive rate — a filter that drops legitimate traffic is an outage you inflicted on yourself
  5. Communicate — customers should see attack data on a dashboard, not find out from Twitter

See the threat map for the detections we publish from the AS210622 edge, or our network page for how the network is put together.