DDoS BGP Community Signaling: Coordinating Upstream Mitigation with ISPs

Back in late 2021 we got hit with a 1.3 Tbps carpet-bombing attack that didn't even touch our origin — it flattened two transit providers before it ever reached our edge. Our scrubbing centers were idle, our rate-limiters were fine, and still, customer traffic was dropping because the pipes feeding our PoPs were full. That's the moment you realize local mitigation isn't enough, and you need your upstreams to act before the traffic hits you. That's where BGP community signaling comes in.

When Your Edge Filters Are Already Too Late

For years I thought DDoS defense was about how much you could absorb at the edge. We'd tuned SYN cookies, deployed eBPF filters, built an anycast network that could take a few hundred Gbps per PoP without sweating. It worked great against the stuff that actually reached our servers. What it doesn't solve is the volumetric problem one hop away. If your 100G transit link is saturated with 120G of UDP reflection, it doesn't matter how elegant your filtering is — legitimate packets are getting tail-dropped upstream and your customers see timeouts.

I've seen teams try to solve this by just buying more transit. It helps, but you're playing an expensive game of whack-a-mole. Attackers can generate traffic cheaper than you can buy capacity. The alternative is to signal upstream — to tell your ISPs and peers, in a language their routers understand, "drop this, rate-limit that, or shift this prefix to your scrubbing center" before it congests the interconnect. BGP communities are that language. They're just 32-bit or 64-bit tags attached to your route announcements, but upstreams that honor them will trigger automated actions like blackholing, Flowspec induction, or selective de-preferencing. Without that, you're stuck opening a ticket while your NOC watches graphs go red.

This is fundamentally different from application-layer problems. If you're dealing with DDoS Application Layer Attack Techniques: Slowloris, RUDY, and HTTP Floods, you can handle it locally because the bandwidth is low but the connection cost is high. Volumetric attacks are the opposite — the packets themselves are the weapon. For a refresher on how those floods behave before they hit your BGP session, I've often pointed junior engineers to DDoS Volumetric Attack Patterns: Identifying and Absorbing Bandwidth Floods. Once you understand that upstream saturation is the real bottleneck, signaling stops feeling like an optional BGP trick and starts feeling like core infrastructure.

# Signal a /32 blackhole to upstream AS174 (Cogent) and AS1299 (Arelion)
# This is from one of our ExaBGP signalers - don't announce this from your core routers

neighbor 66.28.4.1 {
    announce 192.0.2.100/32 {
        community [174:666, 1299:666, 64999:0];
        # 174:666 = Cogent blackhole, 1299:666 = Arelion blackhole
        # 64999:0 = Our internal marker: confirmed automated trigger, not manual
        next-hop 192.0.2.1;
    }
}

# Withdraw automatically after 3600s via timer - prevents stale blackholes
# that we forgot about at 3am. Learned that one the hard way.

Decoding the Community Dialects Your Upstreams Actually Honor

Here's the part that trips up everyone the first time: there's no universal BGP community for "please help me, I'm under attack." Every major transit provider has its own dialect, and if you send the wrong value, they'll just ignore it as an unknown community. RFC 1997 defined the original 32-bit communities (ASN:value), and RFC 4360 gave us extended communities for more structured data, but what community 2914:666 means on NTT is completely different from what it means on Level3. You can't guess — you have to negotiate and document it per peer.

Most ISPs publish a community guide somewhere on their NOC site, though half the time it's a PDF from 2016 that hasn't been updated. The common patterns you'll see are pretty consistent though. A blackhole community like 174:666 or 64999:666 tells the upstream to install a /32 with next-hop discard and propagate it within their network (often with NO_EXPORT so it doesn't leak globally). A scrubbing community like 3356:1234 might redirect your prefix through their Arbor or Radware center via next-hop manipulation. And rate-limit communities — say, 2914:410 — tell them to police traffic to that destination at a fixed pps threshold. We maintain a YAML map per upstream that our automation references, because doing this manually under attack is how you typo a community and blackhole the wrong customer.

The mechanics on your side are straightforward, but you need to be precise about propagation. If you tag a route with a blackhole community and forget to add NO_EXPORT (or 64999:0 style no-advertise), you might accidentally blackhole yourself globally. I prefer to use a dedicated signaling session — a small ExaBGP or GoBGP daemon that only announces triggered /32s — rather than mixing signals into our main production BGP sessions. That isolation saved us during a 2022 incident where a buggy script started flapping communities on our primary sessions and nearly withdrew the whole table. Also remember that communities themselves don't do anything; they're just signals. Your upstream must have a route-map or community filter that matches and applies an action. If they don't, you're just decorating your routes for no reason. This coordination is why DDoS DNS Amplification Defense: Stopping Reflected Attacks at CDN Scale now includes an upstream signaling step — we can't absorb 60M pps of DNS reflection at the edge alone, we need the upstream to drop it near the source.

# BIRD 2.x - Tagging outbound announcements with upstream-specific scrubbing communities
# Applied on our border routers, not the signaler

filter bgp_out_upstream_3356 {
    if net ~ 203.0.113.0/24 then {
        # Normal announcement - no community
        accept;
    }
    if net ~ 192.0.2.100/32 then {
        # This /32 was injected locally by our detector, tag for Lumen scrubbing
        # 3356:123 = redirect to scrubbing, 3356:2000 = advertise with low local-pref in their net
        bgp_community.add((3356,123));
        bgp_community.add((3356,2000));
        bgp_community.add((65535,65281)); # NO_EXPORT to limit scope
        accept;
    }
    reject;
}

protocol bgp lumen_transit {
    local as 65001;
    neighbor 4.68.1.1 as 3356;
    export filter bgp_out_upstream_3356;
}

Building a Pre-Negotiated Playbook That Works at 2 AM

The biggest lesson from our last decade isn't technical, it's operational. You cannot negotiate communities during an attack. When we onboard a new transit or peering partner, the first doc we exchange is the community matrix: what they support, what values trigger what actions, what prefixes they're willing to accept (/32s? /24s? Flowspec?), and how long the action lasts before auto-withdrawal. We put that in a runbook that both NOCs sign off on, test it quarterly with a benign prefix, and integrate it into our detection pipeline. The HTTP Working Group Specifications (https://httpwg.org/specs/) and performance guidance from web.dev Articles (https://web.dev/articles/) are great for tuning what happens after the traffic is clean, but they don't help if the traffic never reaches you — that part is pure BGP hygiene and trust.

Our current stack is pretty simple: our flow monitors and sFlow collectors feed into a detector that classifies volumetric vs. application-layer events. If it's volumetric and exceeds a threshold (say, >40% of transit capacity for >60 seconds), the detector calls our signaling orchestrator. That orchestrator checks the playbook — "for AS174, use blackhole for /32 victim, for AS3356 use scrubbing for /24 under attack" — and pushes the announcement via ExaBGP. It also posts to Slack and opens a ticket automatically with the upstream's NOC, because even automated signals should be visible to humans. We set short TTLs on all signals, usually 30-60 minutes, so we don't permanently blackhole a customer IP after they've recovered. You'd be surprised how often teams forget the withdrawal.

If you haven't built this yet, start small. Pick your top two transits, ask their NOC for their blackhole and scrubbing communities, and test with a discard IP you own. Measure how long it takes from when you announce to when their looking glass shows the discard route. That propagation delay is your real mitigation time, not your detection time. And document everything — community strings, max prefix lengths, contact methods — where your on-call can find it without hunting through email.

Upstream Signal Type Typical Community Example Propagation Delay Mitigated Traffic Reduction Best For
Remote Triggered Blackhole (RTBH) /32 174:666, 1299:666 + NO_EXPORT 15 - 45 sec 95-100% to target IP (clean drop) Single-IP carpet bomb, needs fast drop
Scrubbing Center Redirect 3356:123, 2914:1000 45 - 120 sec 80-90% clean, 10-20% via scrubbed path /24 under attack, keep IP reachable
Flowspec Filter Push Extended: 8011:0:xxx (via separate NLRI) 10 - 30 sec 60-85% of specific protocol/port DNS/NTP amplification, port-specific
Rate-Limit / Police Community 2914:410, 6461:410 30 - 90 sec 50-70% capped at threshold When you can't blackhole, need throttling

Putting Communities to Work: Automating Your Blackhole and Scrubbing Triggers

Knowing the dialect is one thing; wiring it so you don't have to hand-type a BGP announcement while your NOC is on fire at 2 AM is another. We've learned the hard way — you don't want a human in the loop for the first 90 seconds of a volumetric hit. Your automation should be able to detect, validate, and signal upstream without taking your entire prefix offline by accident.

The safest pattern we've deployed across CDN PoPs is a dedicated, tightly scoped trigger router or ExaBGP daemon that only announces /32 host routes with the pre-negotiated community. It doesn't handle production traffic at all. Your detection pipeline — whether it's FastNetMon, sFlow telemetry, or your own scrubbing controller — hits an API, that API validates the target IP is actually yours and currently under attack, and only then injects the route. We enforce hard guardrails: max 5 blackholed /32s per hour, automatic withdrawal after 15 minutes, and a hard requirement for no-export so you don't accidentally leak a blackhole to the global table. Your upstreams should be stripping communities on ingress anyway, but don't rely on it.

# /etc/exabgp/exabgp.conf - isolated blackhole trigger (AS64512)
# This host does NOT forward traffic. It only signals upstreams.

neighbor 203.0.113.1 {
    router-id 192.0.2.10;
    local-address 192.0.2.10;
    local-as 64512;
    peer-as 64512; # iBGP to your border routers

    api {
        processes [ mitigation-api ];
    }
}

process mitigation-api {
    run /usr/local/bin/mitigation-daemon.py;
    encoder json;
}

# Example API command to blackhole a single host via Lumen/NTT-style community
# announce route 198.51.100.42/32 next-hop 192.0.2.1 community [3356:666, 2914:666, no-export]
# withdraw route 198.51.100.42/32 next-hop 192.0.2.1

# For scrubbing instead of drop: redirect to scrubbing center via community
# announce route 198.51.100.42/32 next-hop 192.0.2.1 community [64999:0, 64999:1001, no-export]

Advanced Patterns: Flowspec, Selective Scrubbing, and When Upstreams Ignore You

Blackholing a /32 is a blunt instrument. It works fine for a 40 Gbps DDoS NTP Reflection Mitigation: Blocking Monlist-Based Amplification Attacks hammering a single VIP, but it falls apart when you're dealing with a 100+ Gbps DDoS Memcached Amplification Defense: Neutralizing 51,000x Amplification Factor or a modern DDoS Carpet Bombing Defense: When Attackers Spread Traffic Across Your Entire Subnet where the attacker sprays 500 Mbps across every IP in your /24. If you blackhole the whole /24, you've done the attacker's job for them. That's where you need to graduate from simple blackhole communities to selective scrubbing and BGP Flowspec.

The pattern that scales is community-triggered diversion, not discard. Instead of 64999:666 (drop), you signal 64999:1001 or whatever your ISP uses for "redirect to scrubbing." Your upstream then reroutes just the dirty traffic — say, UDP/11211 or UDP/123 with packet sizes >400 bytes — to their cleaner, while legitimate traffic still hits your edge. Flowspec takes this further and lets you push a full 5-tuple filter upstream. But here's the troubleshooting reality: about 30% of the time our Flowspec rule didn't take, it was because the upstream was silently stripping communities or our border was missing the send-community extended knob. Always validate with show bgp neighbors 203.0.113.1 advertised-routes detail and confirm the community actually egresses. And run a weekly test — announce a test /32 with your blackhole community, confirm you see the discard route on the upstream looking glass within 60-90 seconds, then withdraw. If you haven't tested it this month, it won't work when you need it.

Frequently Asked Questions

What's the difference between a blackhole community and just announcing a /32 with a blackhole next-hop like 192.0.2.1?

Functionally they can do the same thing, but the next-hop method (RFC 7999, e.g., 192.0.2.1 or 198.51.100.1) requires your upstream to have a static discard route configured. The community method is an explicit contract — you're saying "please apply your blackhole policy to this prefix" rather than hoping they route a magic IP to null0. Most Tier 1s prefer communities because it's explicit, audible, and easier for them to filter and automate. Use whichever your upstream documents, but don't assume one works if they only support the other.

How long does it take for upstream mitigation to kick in after I send the community?

In our experience, 45 to 90 seconds for automated upstreams like NTT (2914), Lumen (3356), and Telia (1299) if your session is healthy and you include no-export. If you're signaling via a downstream ISP that has to relay to its own peers, add another 60-120 seconds. That's why pre-negotiation matters — with scrubbing communities, the diversion is often slower (2-4 minutes) because it triggers route-policy re-evaluation and GRE tunneling. If you haven't seen action in 3 minutes, check that your announcement wasn't filtered for RPKI invalid or missing communities.

Can I use BGP communities to mitigate carpet-bombing attacks that hit my whole subnet?

Not with a simple /32 blackhole — you'll need to drop or scrub too much. For carpet-bombing, you're better off pushing a Flowspec rule (e.g., drop UDP fragments or high-port UDP with rate-limit 10 Mbps) or using a scrubbing community that diverts the entire /24 to a cleaner but preserves TCP. One trick we've used is announcing a more-specific /25 with a scrubbing community while keeping the covering /24 clean — some upstreams will honor longest-match diversion. Test this pattern *before* an attack, because not all providers support de-aggregating your prefix like that.

Will upstreams forward my blackhole community to their peers and accidentally null-route me globally?

They shouldn't if they're following BCP, but we've seen leaks. That's why you must always attach no-export (RFC 1997) and the RFC 7999 BLACKHOLE extended community (65535:666) alongside the provider-specific one. That combo tells every compliant router: "discard here, don't send this further." And limit your announcements to /32s — most providers will reject a blackhole larger than /24 anyway to protect you from self-inflicted damage. Log and alert on any withdrawal failure, so you don't leave a host dark for hours.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker