BGP filters on EdgeOS

Hello Internet Exchange

I’ve been playing with Bird these days, and all of this in preparation for an anycast project that I hope will come my way later in the year.

Since my setup was pretty much contained within my homelab, I hit some limitations that stopped me from making progress in the direction that I was hopping for.

To keep this introduction short, I am going to ommit the details of the project and just say that I made a decision to expand my homelab and join the Internet Exchange.

I’ve applied for an ASN number via LIR back in May, and AS62184 has been assigned to me. I’ve used LIR SERVICES to facilitate the application.

As for the Exchange I’ve decided on KleyReX in Franfkurt, Germany because at this time they offer a free 100M port, and of course keeping my costs down is a priority here.

To connect all of this up I’ve acquired a second hand Ubiquiti EdgeRouter PRO and a 4GB memory upgrade (HYNIX HMT351S6CFR8C-PB to be specific), so that the multiple full tables would fit comfortably.

It’s been shipped to Frankfurt and is operational now.

So what’s next now?

Defining the Peering Policy

As soon as I connected to KleyReX the requests for peering started coming my way, but I wasn’t ready and had to defer them until such time that I had filters in place.

So, I needed filters, but what was I going to filter? Well, I needed a peering policy to define that.

With that in mind, I spent a few evenings researching what the big boys do and what the industry practices were. I then had that mixed what I already knew and wanted to do. The result was this list:

* MD5 Authentication is strongly preferred.
* Accept prefixes of length /24 and shorter for IPv4 and /48 or shorter for IPv6.
* Max-prefix filters are used for all peerings.
* Discard prefixes where NEXT_HOP doesn’t match the neighbour’s IP Address.
* Discard prefixes where first AS in the AS_PATH doesn’t match neighbour’s AS.
* Discard prefixes with Private AS anywhere in the AS_PATH.
* Discard bogon prefixes.

Now that I had policy I had to make sure it was enforced!

Building the filters

Firstly, lets define a new BGP neighbour:

set protocols bgp 206001 neighbor 193.189.82.197 description 'Google Inc. IPv4 (AS15169) via kleyrex'
set protocols bgp 206001 neighbor 193.189.82.197 remote-as 15169
set protocols bgp 206001 neighbor 193.189.82.197 password PASSWORD
set protocols bgp 206001 neighbor 193.189.82.197 route-map import import4-AS15169
set protocols bgp 206001 neighbor 193.189.82.197 route-map export export4-AS15169
set protocols bgp 206001 neighbor 193.189.82.197 address-family ipv6-unicast route-map import deny6
set protocols bgp 206001 neighbor 193.189.82.197 address-family ipv6-unicast route-map export deny6
set protocols bgp 206001 neighbor 193.189.82.197 soft-reconfiguration inbound
set protocols bgp 206001 neighbor 193.189.82.197 update-source 193.189.82.XXX
set protocols bgp 206001 neighbor 193.189.82.197 remove-private-as
set protocols bgp 206001 neighbor 193.189.82.197 maximum-prefix 15000

As you have probably noticed the above takes care of both the MD5 Authentication is strongly preferred and Max-prefix filters are used for all peerings requirements.

Now with that in place let’s take each of the remaining policy points one by one and build a configuration to enforce them.

Accept prefixes of length /24 and shorter for IPv4 and /48 or shorter for IPv6.

set policy prefix-list too-long-or-too-short4 rule 1 action deny
set policy prefix-list too-long-or-too-short4 rule 1 le 25
set policy prefix-list too-long-or-too-short4 rule 1 ge 7
set policy prefix-list too-long-or-too-short4 rule 1 prefix 0.0.0.0/0

set policy prefix-list6 too-long-or-too-short6 rule 1 action deny
set policy prefix-list6 too-long-or-too-short6 rule 1 le 49
set policy prefix-list6 too-long-or-too-short6 rule 1 ge 7
set policy prefix-list6 too-long-or-too-short6 rule 1 prefix '::/0'

set policy route-map import4-AS15169 description "Google Inc. IPv4 IMPORT"
set policy route-map import4-AS15169 rule 1 action deny
set policy route-map import4-AS15169 rule 1 match ip address prefix-list too-long-or-too-short4

Discard prefixes with Private AS anywhere in the AS_PATH.

set policy as-path-list private rule 1 action permit
set policy as-path-list private rule 1 regex '_6(4(5(1[2-9]|[2-9][0-9])|[6-9][0-9][0-9])|5([0-4][0-9][0-9]|5([0-2][0-9]|3[0-5])))_'
set policy as-path-list private rule 2 action permit
set policy as-path-list private rule 2 regex '_42([0-8][0-9][0-9][0-9][0-9][0-9][0-9][0-9]|9([0-3][0-9][0-9][0-9][0-9][0-9][0-9]|4([0-8][0-9][0-9][0-9][0-9][0-9]|9([0-5][0-9][0-9][0-9][0-9]|6([0-6][0-9][0-9][0-9]|7([0-1][0-9][0-9]|2([0-8][0-9]|9[0-4])))))))_'

set policy route-map import4-AS15169 rule 2 action deny
set policy route-map import4-AS15169 rule 2 match as-path private
set policy route-map import4-AS15169 rule 99 action deny

Only permit prefixes with a valid IRRDB entry (and therefore Discard bogon prefixes)

set policy route-map import4-AS15169 rule 100 match ip address prefix-list prefix4-AS-GOOGLE

We also need to build the list of prefixes to accept, but this subject is intentionally left for another blog post.

Discard prefixes where NEXT_HOP doesn’t match the neighbour’s IP Address.

set policy prefix-list nexthop4-AS15169 rule 1 action permit
set policy prefix-list nexthop4-AS15169 rule 1 prefix 193.189.82.197/32
set policy route-map import4-AS15169 rule 100 match ip nexthop prefix-list nexthop4-AS15169

Discard prefixes where first AS in the AS_PATH doesn’t match neighbour’s AS.

set policy as-path-list path-AS15169 rule 1 action permit
set policy as-path-list path-AS15169 rule 1 regex "^15169_"
set policy route-map import4-AS15169 rule 100 match as-path path-AS15169

Automation

I had wrapped everything discussed here in a set of scripts that I now publish as asn-tools, and they have now been extended to support IPv6 neighbours and the ripe whois import/export rules generator.

So the config used in the above example would be generated by confgen.rb (part of asn-tools) as follows:

Usage confgen.rb [options]
-r, --router=ROUTER Router hostname
-t, --type=TYPE Peer type
-a, --as=AS AS Number
-A, --all Generate configuration for all peeers
-h, --help Display this screen

$ confgen.rb -r rtr1 -t peering -a 15169