Spoiler alert: Current FortiOS versions do not allow BGP blackhole routing. But we have a workaround ready for you.
What is BGP Blackhole Routing?
There are many providers for Blocklists and also numberous reasons for blocking. One of the very effective and performant ways of blocking traffic is an IP address block in the gateway. While DNS filters can be bypassed by using another DNS server or using the IP address directly, an IP block can not be bypassed that easy from a network user.
BGP is a dynamic routing protocol that is fast and reliable, as well as resource-aware. Using BGP as a protocol to exchange blacklists is not widespread, but it is a very convenient way to exchange this information.
So BGP blocklisting enabled layer three blocking whith the blocklist exchanged over the BGP dynamic routing protocol. This is also called remote triggered blackhole filtering (short: rtbh).
For example, abuse.ch and spamhaus.org provide BGP feeds for blocklisting. There are many more providers of BGP feeds for blocking, but most of them are distributed to a restricted audience only.
What is the challenge?
The FortiGate does not support to discarding traffic using blackhole routing, loopback interfaces or inexistent next hops for BGP Routes. To be very detailed, this is not possible anymore since FortiOS 7.2.1 and newer.
So we’ve tested several ways of using BGP to block the received prefixes:
- Set the next hop to a blackholed address.
The BGP log shows the prefix is received.
BGP: 172.16.1.2-Outgoing [RIB] Update: Received Prefix 1.1.1.1/32 path_id 0
BGP: VRF 0 NSM announce: 1.1.1.1/32
But the route is not imported to the FIB (The “*” is missing in the following routing table).
FGT01 (root) # get router info routing-table database
S *> 192.0.2.64/30 [1/0] is a summary, Null, [1/0]
B > 1.1.1.1/32 [20/0] via 192.0.2.65 (recursive is a summary, Null), 00:13:37, [1/0]
Conclusion: This is not working, the BGP route is never injected into the FIB.
- Set the next hop to a loopback address.
The BGP log shows the prefix is received.
BGP: 172.16.1.2-Outgoing [RIB] Update: Received Prefix 1.1.1.1/32 path_id 0
BGP: [NSM] Verified NH 192.0.2.2 with NSM
BGP: VRF 0 NSM announce: 1.1.1.1/32
But the route is not imported to the FIB and the route is now not even one of the selected routes anymore.
FGT01 (root) # get router info routing-table database
C *> 192.0.2.0/30 is directly connected, stub
B 1.1.1.1/32 [20/0] via 192.0.2.2 inactive, 00:13:37, [1/0]
Conclusion: This is not working, the BGP route is never injected into the FIB.
3. Set next hop a blackhole VDOM.
The BGP log shows the prefix is received.
BGP: 172.16.1.2-Outgoing [RIB] Update: Received Prefix 1.1.1.1/32 path_id 0
BGP: [NSM] Verified NH 192.0.2.130 with NSM
BGP: VRF 0 NSM announce: 1.1.1.1/32
Ant the route is now finally imported to the FIB.
FGT01 (root) # get router info routing-table database
C *> 192.0.2.128/30 is directly connected, npu0_vlink0
B *> 1.1.1.1/32 [20/0] via 192.0.2.130 (recursive is directly connected, npu0_vlink0), 00:13:37, [1/0]
Conclusion: This is working. The BGP prefixes are successfully injected as routes into the FIB exactly as we need. Unfortunately, this solution is unnecessary complicated since an additional VDOM is needed.
The Solution in Detail
There is no nice solution available. We have also checked this with the Fortinet support (in case 10756030).
But we have a workaround that works. Please note, that we are not proud of this solution since it’s by far not an elegant way to solve this case. But it is the only working way known to us to implement BGP blackhole routing at the moment.
Follow these steps to implement BGP blackhole routing:
- Create a new VDOM. We named it “blackhole” and if you do not need logging, disable it first.
config vdom
edit blackhole
config log memory setting
set status disable
end
end
end
- In the blackhole VDOM, create a blackhole route to 0.0.0.0/0 (the whole IPv4 space). If you are using IPv6, do the same for IPv6 also. In a perfect world, no traffic will ever enter this VDOM since we block it. But if someone deletes the deny policy, we still want the traffic to be discarded. Therefore we just create a blackhole route to be sure.
config vdom
edit blackhole
config router static
edit 1
set dst 0.0.0.0 0.0.0.0
set blackhole enable
next
end
end
end
Create an inter-VDOM link from the routing-VDOM (root in this case) to the blackhole VDOM. Please change the IP addresses to unused IP addresses in your environment. Also, we recommend using VLANs to configure this, so you don’t “waste” whe hardware accelerated inter-VDOM-link for this only purpose.
config global
config system interface
edit "npu0_vlink0"
set vdom "root"
set ip 192.0.2.129 255.255.255.252
set type physical
next
edit "npu0_vlink1"
set vdom "blackhole"
set ip 192.0.2.130 255.255.255.252
set type physical
next
end
end
Create a firewall policy that denies the traffic through the root VDOM to the blackhole VDOM. Logging the traffic allows us to create an alert email on hits. Also, if we deny the traffic on the root VDOM, the blackhole VDOM does not need to make a second session and therefore we reduce the load on the FortiGate.
config vdom
edit root
config firewall policy
edit 0
set srcintf "any"
set dstintf "npu0_vlink0"
set srcaddr "all"
set dstaddr "all"
set schedule "always"
set service "ALL"
set logtraffic all
set action deny
next
end
end
end
Configure a route-map to rewrite the next hop for routes learned. So the next hop for routes will be the IP address we have set in the blackhole VDOM.
config vdom
edit root
config router route-map
edit "redirectnexthop"
config rule
edit 1
set action permit
set set-ip-nexthop 192.0.2.130
unset set-ip-prefsrc
next
edit 2
set action deny
unset set-ip-prefsrc
next
end
next
end
end
end
And as a last step, configure the BGP router in the root VDOM.
config vdom
edit root
config router bgp
set as 65444
set router-id 172.16.1.1
config neighbor
edit "172.16.1.2"
set ebgp-enforce-multihop enable
set remote-as 65555
set route-map-in "redirectnexthop"
next
end
config network
edit 1
set prefix 192.0.2.64 255.255.255.252
next
end
config redistribute "connected"
end
config redistribute "static"
end
end
end
end
Now, the routing table looks like:
FGT01 (root) # get router info routing-table database
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
V - BGP VPNv4
> - selected route, * - FIB route, p - stale info
Routing table for VRF=0
S *> 0.0.0.0/0 [5/0] via 203.0.113.37, wan, [1/0]
B *> 1.1.1.1/32 [20/0] via 192.0.2.130 (recursive is directly connected, npu0_vlink0), 6d04h43m, [1/0]
C *> 192.0.2.128/30 is directly connected, npu0_vlink0
C *> 192.168.1.0/24 is directly connected, lan
C *> 172.16.1.0/30 is directly connected, lan2
Green are the BGP routes, orange is the next hop that points into the blackhole VDOM and blue is the route into the blackhole VDOM.
Why isn’t this an elegant solution?
It’s pretty simple. If FortiGate still supported an alternative solution, the configuration process would be much simpler.
Test Setup: Configure your own BGP peer
If you want to experiment, you may want to configure your own peer that sends you a BGP blacklist:
config router bgp
set as 65555
set router-id 172.16.1.2
set network-import-check disable
config neighbor
edit "172.16.1.1"
set remote-as 65444
next
end
config network
edit 1
set prefix 1.1.1.1 255.255.255.255
next
end
config redistribute "connected"
end
config redistribute "static"
end
end
Test Setup: Only allow /32 bit prefixes
Some blacklists provide only /32 prefixes. In this case, you can configure your BGP router to only accept /32 prefixes:
config router prefix-list
edit "32-Bit-Subnet-Only"
config rule
edit 1
set prefix 0.0.0.0 0.0.0.0
set ge 32
set le 32
next
edit 2
set action deny
set prefix 0.0.0.0 0.0.0.0
unset ge
unset le
next
end
next
end
config router route-map
edit "redirectnexthop"
config rule
edit 1
set match-ip-address "32-Bit-Subnet-Only"
set set-ip-nexthop 192.0.2.130
unset set-ip-prefsrc
next
edit 2
set action deny
unset set-ip-prefsrc
next
end
next
end
As soon as a larger prefix is announced from a peer, you will receive the following BGP log:
BGP: 172.16.1.2-Outgoing [RIB] Update: Prefix 1.1.1.1/16 path_id 0 denied due to route-map
More Information
Have a look into our FortiGate BGP Troubleshooting Guide for tipps regarding BGP Troubleshooting.
If you have other thoughts, inputs or questions, please use the comment section below to share your knowledge.