FortiGate and BGP Blackhole Routing

Spoiler alert: Current FortiOS versions do not allow BGP blackhole routing. But we have a workaround ready for you.

Table of Contents

    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:

    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.

    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.

    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:

    1. 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
    1. 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.

    Loading

    Leave a Reply

    Your email address will not be published. Required fields are marked *