BGP Tunnel for DDoS Protection: GRE + BGP Config Guide
A BGP tunnel for DDoS protection is a GRE tunnel between your edge router and a scrubbing provider, with an eBGP session running inside it. You keep your own ASN and IP space, the provider announces your prefixes from its scrubbing edge, and only clean traffic comes back to you through the tunnel.
This guide covers the practical part: addressing, router configs for Linux/BIRD, MikroTik and Juniper, MTU/MSS math, and the mistakes that cause most failed turn-ups. All addresses use documentation ranges. Your provider will give you the real tunnel endpoints and inner addressing.
How the traffic flows
- Your prefix (for example
198.51.100.0/24, origin AS64496) is announced to the internet from the scrubbing edge (AS64501 in our examples). - Attack and legitimate traffic both arrive at the scrubbing edge, where attack traffic is filtered.
- Clean traffic is encapsulated in GRE and sent to your tunnel endpoint.
- Your router decapsulates it and forwards it to your servers.
- Return traffic usually leaves directly through your own transit or peering, so the path is asymmetric by design.
The BGP session inside the tunnel is how you tell the provider which prefixes you want protected. It also gives you a clear up/down signal for the path.
Prerequisites
- Your own ASN and at least one IPv4 /24. A /24 is the longest IPv4 prefix generally accepted on the global table, so it's the practical minimum for this model.
- Valid route objects (IRR) and ROAs for the prefixes, with the scrubbing AS authorized to originate them if the provider requires it.
- A public tunnel endpoint IP that is not inside the protected prefix.
- A router that can terminate GRE and run BGP: Linux, MikroTik RouterOS v7 or Juniper MX/SRX class platforms, for example.
- An AS-SET if the provider uses it for prefix filtering.
Example addressing plan
| Item | Value |
|---|---|
| Your ASN / protected prefix | AS64496 / 198.51.100.0/24 |
| Your tunnel endpoint (public) | 203.0.113.10 |
| Scrubbing edge endpoint (public) | 192.0.2.10 |
| Scrubbing ASN | AS64501 |
| Inner tunnel /30 | 169.254.100.0/30 (provider .1, you .2) |
MTU and MSS first
GRE over IPv4 adds 24 bytes (20-byte outer IP header plus 4-byte GRE header). On a standard 1500-byte underlay:
| Layer | Value |
|---|---|
| Tunnel MTU | 1476 |
| TCP MSS (IPv4) | 1436 |
| TCP MSS (IPv6) | 1416 |
If your underlay MTU is lower (PPPoE, another tunnel, some L2 transport), subtract accordingly. Confirm the provider's expected MTU during turn-up.
One non-obvious detail: the clean traffic travels client-to-server through the tunnel, and the MSS that limits it is the one your servers advertise in their SYN-ACK. That packet usually leaves through your normal transit, not the tunnel. So clamp SYN-ACKs sourced from your protected prefix, not only packets crossing the GRE interface.
Linux + BIRD
Create the tunnel:
ip link add gre-scrub type gre local 203.0.113.10 remote 192.0.2.10 ttl 255
ip addr add 169.254.100.2/30 dev gre-scrub
ip link set gre-scrub mtu 1476 up
# Loose or disabled reverse-path filtering for decapsulated traffic
sysctl -w net.ipv4.conf.gre-scrub.rp_filter=0
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.ip_forward=1
# Clamp MSS on SYN-ACKs from your servers
iptables -t mangle -A FORWARD -s 198.51.100.0/24 -p tcp \
--tcp-flags SYN,ACK SYN,ACK -j TCPMSS --set-mss 1436
Persist this with systemd-networkd, netplan or your distro's tooling. Then configure BIRD 2 (/etc/bird/bird.conf):
router id 203.0.113.10;
protocol device {}
protocol static my_prefixes {
ipv4;
route 198.51.100.0/24 blackhole; # origination route; specifics come from your IGP
}
filter to_scrub {
if net = 198.51.100.0/24 then accept;
reject;
}
protocol bgp scrub {
local 169.254.100.2 as 64496;
neighbor 169.254.100.1 as 64501;
hold time 30;
ipv4 {
import none;
export filter to_scrub;
};
}
import none is deliberate: for most protection setups you don't need to accept anything from the scrubber, and your outbound traffic keeps using your own transit.
MikroTik (RouterOS v7)
/interface gre add name=gre-scrub local-address=203.0.113.10 \
remote-address=192.0.2.10 mtu=1476 keepalive=10s,3
/ip address add address=169.254.100.2/30 interface=gre-scrub
/ip route add dst-address=198.51.100.0/24 blackhole
/ip firewall address-list add list=my-prefixes address=198.51.100.0/24
/routing filter rule add chain=to-scrub rule="if (dst == 198.51.100.0/24) { accept }"
/routing filter rule add chain=to-scrub rule="reject"
/routing filter rule add chain=from-scrub rule="reject"
/routing bgp connection add name=scrub as=64496 \
local.address=169.254.100.2 local.role=ebgp \
remote.address=169.254.100.1 remote.as=64501 \
output.network=my-prefixes output.filter-chain=to-scrub \
input.filter=from-scrub
# MSS clamp for server SYN-ACKs
/ip firewall mangle add chain=forward protocol=tcp tcp-flags=syn,ack \
src-address=198.51.100.0/24 action=change-mss new-mss=1436 passthrough=yes
Also allow GRE (IP protocol 47) from 192.0.2.10 in your input chain, and drop it from everywhere else. Syntax for routing bgp differs between RouterOS 7 point releases, so check against your version.
Juniper (Junos)
GRE on MX needs tunnel services enabled on a PFE/FPC (set chassis fpc 0 pic 0 tunnel-services bandwidth ...). Then:
set routing-options autonomous-system 64496
set routing-options static route 198.51.100.0/24 discard
set interfaces gr-0/0/10 unit 0 tunnel source 203.0.113.10
set interfaces gr-0/0/10 unit 0 tunnel destination 192.0.2.10
set interfaces gr-0/0/10 unit 0 family inet mtu 1476
set interfaces gr-0/0/10 unit 0 family inet tcp-mss 1436
set interfaces gr-0/0/10 unit 0 family inet address 169.254.100.2/30
set policy-options prefix-list MY-PREFIXES 198.51.100.0/24
set policy-options policy-statement TO-SCRUB term 1 from prefix-list-filter MY-PREFIXES exact
set policy-options policy-statement TO-SCRUB term 1 then accept
set policy-options policy-statement TO-SCRUB term 2 then reject
set policy-options policy-statement FROM-SCRUB term 1 then reject
set protocols bgp group SCRUB type external
set protocols bgp group SCRUB peer-as 64501
set protocols bgp group SCRUB neighbor 169.254.100.1 family inet unicast
set protocols bgp group SCRUB neighbor 169.254.100.1 import FROM-SCRUB
set protocols bgp group SCRUB neighbor 169.254.100.1 export TO-SCRUB
TCP MSS handling is direction- and platform-specific on Junos. Verify with a packet capture, and if your servers' SYN-ACKs leave through a different interface, apply tcp-mss there as well.
Verification checklist
ping 169.254.100.1 size 1448 do-not-fragment(1448 + 28 = 1476) across the tunnel succeeds.- The BGP session is Established and your /24 shows as advertised.
- From an external looking glass, the origin path for your /24 goes through the scrubbing AS.
tcpdump -ni gre-scrub(or the equivalent) shows inbound traffic decapsulated on the tunnel.- A TCP session from outside completes and large transfers don't stall.
Common mistakes
- Tunnel endpoint inside the protected prefix. When the prefix is announced from the scrubber, the GRE packets to your endpoint would route back into the tunnel and loop. Use an address outside the protected /24.
- Ignoring MTU. Symptoms: TCP handshakes work, small requests work, large downloads or TLS handshakes hang. Check MTU and MSS, and don't filter ICMP "fragmentation needed" (type 3 code 4).
- Reverse-path filtering. Strict rp_filter or uRPF on the tunnel drops decapsulated traffic because its source addresses are arbitrary internet hosts.
- Open GRE endpoint. Accept protocol 47 only from the provider's endpoint, otherwise anyone can inject traffic into your network.
- Accepting a default route by habit. Import filters should be explicit. A default via the tunnel can pull your outbound traffic through it.
- Missing IRR/ROA objects. Prefix filters at the provider or upstreams will silently drop the announcement.
- Forgetting the other announcement. If you also announce the /24 to your own transit, decide whether you're running always-on (withdraw there) or on-demand (announce through the scrubber only during an attack), and document the procedure.
- Single tunnel, no monitoring. Alert on BGP session state, tunnel keepalives and interface errors.
Always-on vs. on-demand
Always-on keeps your prefix announced from the scrubbing edge all the time, so there is no switchover during an attack. On-demand keeps normal routing until detection fires, then moves the announcement. On-demand depends on fast, accurate detection, which is typically driven by NetFlow, IPFIX or sFlow from your routers with per-prefix bps/pps thresholds.
Where NixMon fits
NixGuard Edge is NixMon's cloud scrubbing service for networks with their own ASN and at least one /24. Your prefixes are announced from the scrubbing edge and clean traffic is delivered over a GRE tunnel with a BGP session inside, as described above. It can run always-on or on-demand, and customer AS-SETs can be used for prefix filtering. NixMon's detection side analyzes NetFlow v5/v9, IPFIX and sFlow, and can trigger BGP blackhole, FlowSpec, on-prem firewall (NixGuard) or Edge actions. You can start a 15-day free trial (no credit card) to see what your own flow data looks like before deciding on a mitigation design.
Whichever provider you choose, test the tunnel, MTU and failover procedure before the first real attack, not during it.
Frequently asked questions
Do I need my own ASN and IP space for a BGP tunnel DDoS setup?
Yes. The model relies on you originating your own prefixes and running eBGP with the scrubbing provider. In practice that means an ASN and at least one IPv4 /24, since smaller prefixes are generally filtered on the global routing table.
What MTU and MSS should I use for a GRE tunnel?
On a 1500-byte underlay, GRE over IPv4 adds 24 bytes, so the tunnel MTU is 1476 and the IPv4 TCP MSS is 1436 (1416 for IPv6). Use lower values if your underlay MTU is smaller, and confirm them with your provider.
Why does my tunnel come up but large downloads stall?
This is almost always an MTU/MSS issue. Check that the tunnel MTU is correct, clamp the MSS on SYN-ACKs from your servers, and make sure ICMP fragmentation-needed messages are not blocked.
Will my outbound traffic go through the scrubbing provider?
Typically not. Inbound traffic returns through the GRE tunnel, while outbound traffic leaves via your normal transit or peering. Use strict import filters on the tunnel BGP session so you don't accept a default route by accident.