Introduction to BGP Routing and Security

Part 2: Preventing Transit


Saturday 31st October 2020

This is part 2 of a multi-part series on BGP routing and security:

This article details several methods that can be used to prevent your Autonomous System (AS) from becoming a so-called 'transit AS', which may be undesirable in certain BGP setups.

Skip to Section:

Introduction to BGP Routing and Security
Part 2: Preventing Transit
┣━━ What is a transit AS, and why is it sometimes undesirable to become one?
┣━━ Key Terminology
┣━━ No-Export Community
┣━━ Restricting Packet Forwarding
┗━━ Part 3 / Conclusion

What is a transit AS, and why is it sometimes undesirable to become one?

A transit AS (Autonomous System) refers to an AS that is able to provide IP transit, i.e. it can receive packets from one peer and forward them on through another. In other words, a transit AS allows 'through' traffic, rather than just traffic explicitly destined for itself. It's a bit like the difference between a main road and a dead end on a road traffic network - a main road always leads somewhere else, while a dead end only leads to itself.

The most common example of transit AS's is large ISPs and transit providers, which each have multiple connections with a diverse range of networks, allowing for resilient routing and connectivity. However, any BGP setup that is multi-homed (i.e. has two or more unique peers) has the potential to become a transit AS, and this is by-design.

For example, if you are peering with two different upstream ISPs, but they don't have a direct connection between each other (or the connection is disrupted), the traffic might end up being routed through your network, as ultimately your network provides a valid path between the two ISPs.

If you are a network provider or ISP yourself, then this is most likely what you want to happen, as ultimately that's what your business is for. However, if you're simply wanting to announce the prefixes for your own network, it may be undesirable to become a transit AS, as your network resources and bandwidth may be used to handle traffic that is completely unrelated to your business operations. To use the road traffic network analogy again - it's like an emergency diversion routing traffic from a major road down a quiet back street and overloading it, much to the dismay of the residents!

This article will detail how you can configure your Quagga BGP daemon to prevent becoming a transit AS.

Key Terminology

Before proceeding with the hands-on content of the article, I've documented some key terminology that may be useful and hasn't been covered so far in this series.

BGP Community

BGP communities are extra bits of information that can be included with route announcements, and are used to control additional settings that may impact routing decisions or propagation.

Lots of BGP communities exist, many of which are provider-specific or for internal use, however there are 4 'well known' BGP communities that are officially defined by IANA:

Packet Forwarding

Packet forwarding refers to the process of accepting packets intended for a particular destination and 'forwarding' them on via another network node.

Usually packet forwarding is performed by dedicated routers and other core networking devices, however the Linux Kernel also has considerable packet forwarding/routing capabilities, which is what we're working with here.

No-Export Community

The primary method for preventing yourself from becoming a transit AS is using the No-Export community, which is used to prevent prefixes from being advertised to any external BGP (eBGP) peers.

You'll need to configure Quagga to apply the No-Export community to all routes that are received from external peers, but not to your own locally-originating routes (as this would prevent them from being shared upstream). This is done using a route map configuration.

Begin by opening a VTY session using vtysh, and entering configuration mode using conf t.

Next, create a new route map named RM_NO_EXPORT with a policy of permit and a sequence number of 10:

quagga(config)# route-map RM_NO_EXPORT permit 10

You should now be in route map configuration mode. Now you can configure the route map to apply the No-Export community:

quagga(config-route-map)# set community no-export

You can now exit route map configuration mode and launch a BGP protocol process for your AS:

quagga(config)# router bgp 64497

From here, you need to configure the RM_NO_EXPORT route map to be applied to all routes that are received from each of your peers:

quagga(config-router)# neighbor 10.0.0.1 route-map RM_NO_EXPORT in

Note that the in option is used to match only inbound routes, in order to ensure that the route map doesn't apply to your own locally-originating routes and prevent them from being shared with peers.

Next, you need to enable the sending of BGP community information for the peer:

quagga(config-router)# neighbor 10.0.0.1 send-community

If you're using IPv6 too, you can repeat the same steps for the IPv6 address family:

quagga(config-router)# address-family ipv6
quagga(config-router-af)# neighbor fc00:abcd:1234::1 route-map RM_NO_EXPORT in
quagga(config-router-af)# neighbor fc00:abcd:1234::1 send-community

Make sure to repeat these steps for all of the relevant IPv4 and IPv6 peers.

You can now use exit a few times to leave configuration mode, and then use write to save your configuration to disk.

From here, you can also optionally print out your RM_NO_EXPORT route map configuration to make sure that everything is correct:

# show route-map RM_NO_EXPORT
ZEBRA:
route-map RM_NO_EXPORT, permit, sequence 10
  Match clauses:
  Set clauses:
  Call clause:
  Action:
    Exit routemap
BGP:
route-map RM_NO_EXPORT, permit, sequence 10
  Match clauses:
  Set clauses:
    community no-export
  Call clause:
  Action:
    Exit routemap

You can also check that the RM_NO_EXPORT community has been properly applied to inbound routes (though you may need to restart BGPd for this to begin working). In the example below from my own BGPd, you can see that the route received from one of my external neighbours isn't shared outside of my local AS:

# sh ip bgp 172.20.1.0/24
BGP routing table entry for 172.20.1.0/24
Paths: (1 available, best #1, table Default-IP-Routing-Table, not advertised to EBGP peer)
  Not advertised to any peer
  4242423914 4242420119
    192.168.163.37 from 192.168.163.37 (172.20.53.99)
      Origin IGP, localpref 100, valid, external, best
      Community: no-export
      Large Community: 4242420119:2000:2
      Last update: Sun Oct 18 20:53:02 2020

However, if you check a prefix that you're advertising yourself, you'll see that the No-Export community hasn't been applied. You can see this in the following additional example from my own BGPd, where I am advertising 172.20.32.96/28:

# sh ip bgp 172.20.32.96/28
BGP routing table entry for 172.20.32.96/28
Paths: (1 available, best #1, table Default-IP-Routing-Table)
  Advertised to non peer-group peers:
  192.168.163.37
  Local
    0.0.0.0 from 0.0.0.0 (172.20.32.97)
      Origin IGP, metric 0, localpref 100, weight 32768, valid, sourced, local, best
      Last update: Thu Sep 17 09:06:50 2020

Restricting Packet Forwarding

While the No-Export community is reliable at preventing externally-received routes from being advertised to eBGP peers, it's best practise to combine this with at least one other additional 'layer', in order to act as a failsafe should an accidental error or misconfiguration occur.

You should use your local packet filter/firewall, which in my case is iptables, to block all packet forwarding for the relevant network interfaces. For example, the following rule will block forwarding for packets received from 10.0.0.1:

iptables -A FORWARD -s 10.0.0.1 -j REJECT

You can also outright block forwarding for the relevant network interface that you're connected to your peer with:

iptables -A FORWARD -i eth1 -j REJECT

Make sure to set these rules accordingly for your own setup, and ensure that they are automatically set properly at boot, e.g. by putting them in /etc/ufw/before.rules and/or /etc/ufw/before6.rules.

In addition to this, if you're only running Quagga on a single machine that isn't acting as a router for a wider network, you should consider explicitly disabling IPv4 and IPv6 packet forwarding for all network interfaces, which will fully prevent your device being used for general IP transit.

You can do this by setting the following in /etc/sysctl.conf, or alternatively a custom configuration file in /etc/sysctl.d/, such as /etc/sysctl.d/99-disable-packet-forwarding.conf:

net.ipv4.ip_forward=0
net.ipv6.conf.all.forwarding=0

The IPv6 configuration is defined on a per-interface basis, so you should also add a line for each relevant interface, e.g.:

net.ipv6.conf.eth1.forwarding=0
net.ipv6.conf.eth2.forwarding=0

Once your system networking services have been restarted, IP packet forwarding will be disabled.

Part 3 / Conclusion

You've now further configured your Quagga BGPd setup, and are operating as a non-transit AS to ensure that you don't propagate arbitrary routes to your peers.

In the next part, we'll implement route filtering, which is used to control and restrict which routes will be accepted from your peers. Route filtering is a key feature when it comes to mitigating the risk of BGP leaks/hijacks.

This article is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.