2023-09-16 22:29:30 -04:00
|
|
|
# Quantum's `bird` Filter Library
|
|
|
|
|
|
|
|
This is meant to be a starter repository containing sample `bird` 2.x config
|
|
|
|
files that you can use to build your own BGP filters. Filters are provided as
|
|
|
|
composable `bird` functions and enables you to harness the full power of the
|
|
|
|
`bird` filter mini-programming language, as an alternative to a more declarative
|
|
|
|
solution like [PathVector][pv].
|
|
|
|
|
|
|
|
## Quick start
|
|
|
|
|
|
|
|
1. Make sure `bird` 2.x is installed, e.g. on Debian or Ubuntu, through
|
2024-01-30 00:40:55 -05:00
|
|
|
`sudo apt install bird2`. This library has not been tested on versions
|
|
|
|
older than 2.0.13, so you may run into syntax errors on earlier versions.
|
|
|
|
In these cases, you'll need to look into backports or PPAs for a newer
|
|
|
|
version.
|
2023-09-16 22:29:30 -04:00
|
|
|
2. Clone this repository:
|
|
|
|
```
|
|
|
|
git clone https://github.com/quantum5/bird-filter.git
|
|
|
|
cd bird-filter
|
|
|
|
```
|
|
|
|
3. Customize [`filter_bgp.conf`][filter] by editing it. Pay special attention
|
|
|
|
to anything tagged `FIXME`.
|
|
|
|
4. Install `filter_bgp.conf` into your `bird` configuration directory
|
|
|
|
(`/etc/bird` by default):
|
|
|
|
```
|
|
|
|
sudo cp filter_bgp.conf /etc/bird
|
|
|
|
```
|
|
|
|
|
|
|
|
## Defining BGP sessions
|
|
|
|
|
2023-10-07 00:48:28 -04:00
|
|
|
You can use [`skeleton.conf`][skeleton] as a basic `bird` starting config.
|
|
|
|
Remember to read the `NOTE`s and change the things marked `FIXME`.
|
|
|
|
|
|
|
|
Also note that in this config, static protocol routes are internal to `bird`
|
|
|
|
and will not be exported to the kernel routing table. You can change this by
|
|
|
|
changing the export rules for `protocol kernel`.
|
2023-09-16 22:29:30 -04:00
|
|
|
|
|
|
|
This filter library makes use of two basic static protocols:
|
|
|
|
* `node_v4`: IPv4 routes to be exported by the `export_cone` helper.
|
|
|
|
* `node_v6`: IPv6 routes to be exported by the `export_cone` helper.
|
|
|
|
|
|
|
|
For example, to advertise `198.51.100.0/24` and `2001:db8:1000::/36`:
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol static node_v4 {
|
|
|
|
ipv4 {};
|
|
|
|
route 198.51.100.0/24 reject;
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol static node_v6 {
|
|
|
|
ipv6 {};
|
|
|
|
route 2001:db8:1000::/36 reject;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Two additional static protocols are used to aid with traffic engineering for
|
|
|
|
anycast prefixes:
|
|
|
|
* `node_v4_anycast`: IPv4 routes to be exported by the `export_anycast` helper.
|
|
|
|
* `node_v6_anycast`: IPv6 routes to be exported by the `export_anycast` helper.
|
|
|
|
|
|
|
|
You can add `protocol` blocks to this config for each BGP neighbour. This is
|
|
|
|
dependent on the neighbour type.
|
|
|
|
|
|
|
|
In the follow examples, we assume the following local preferences:
|
|
|
|
* 50 for upstreams;
|
|
|
|
* 90 for IXPs;
|
|
|
|
* 100 for direct peers; and
|
|
|
|
* 120 for downstreams.
|
|
|
|
|
|
|
|
### Upstreams
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol bgp example_upstream_v4 {
|
|
|
|
description "Example Upstream (IPv4)";
|
|
|
|
local 192.0.2.25 as 64500;
|
|
|
|
neighbor 192.0.2.24 as 64501;
|
|
|
|
default bgp_local_pref 50;
|
|
|
|
|
|
|
|
ipv4 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_transit(64501, false);
|
|
|
|
export where export_cone(64501);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol bgp example_upstream_v6 {
|
|
|
|
description "Example Upstream (IPv6)";
|
|
|
|
local 2001:db8:2000::2 as 64500;
|
|
|
|
neighbor 2001:db8:2000::1 as 64501;
|
|
|
|
default bgp_local_pref 50;
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_transit(64501, false);
|
|
|
|
export where export_cone(64501);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are AS64500 and establishes BGP sessions over
|
|
|
|
both IPv4 and IPv6 with an upstream AS64501 and exports your entire cone. It
|
|
|
|
also assumes your upstream is sending you a full table and filters out the
|
|
|
|
default route. If you expect a default route instead, use
|
|
|
|
`import where import_transit(64501, true)`.
|
|
|
|
|
|
|
|
To export your anycast as well, you can simply do
|
|
|
|
`export where export_cone(64501) || export_anycast()`.
|
|
|
|
|
|
|
|
### Peers
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol bgp example_peer_v4 {
|
|
|
|
description "Example Peer (IPv4)";
|
|
|
|
local 192.0.2.25 as 64500;
|
|
|
|
neighbor 192.0.2.28 as 64502;
|
|
|
|
default bgp_local_pref 100;
|
|
|
|
|
|
|
|
ipv4 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_peer_trusted(64502);
|
|
|
|
export where export_cone(64502);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol bgp example_peer_v6 {
|
|
|
|
description "Example Peer (IPv6)";
|
|
|
|
local 2001:db8:2000::2 as 64500;
|
|
|
|
neighbor 2001:db8:2000::10 as 64502;
|
|
|
|
default bgp_local_pref 100;
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_peer_trusted(64502);
|
|
|
|
export where export_cone(64502);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are AS64500 and establishes BGP sessions over
|
|
|
|
both IPv4 and IPv6 with a peer AS64502 and exports your entire cone. It assumes
|
|
|
|
your peer is trusted and doesn't provide any IRR filtering. If you don't trust
|
|
|
|
your peer, see the [IRR filtering](#irr-filtering) section below.
|
|
|
|
|
|
|
|
### IXP route servers
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol bgp example_ixp_v4 {
|
|
|
|
description "Example IXP Route Servers (IPv4)";
|
|
|
|
local 203.0.113.3 as 64500;
|
|
|
|
neighbor 203.0.113.1 as 64503;
|
|
|
|
default bgp_local_pref 90;
|
|
|
|
|
|
|
|
ipv4 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_ixp_trusted(64503);
|
|
|
|
export where export_cone(64503);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol bgp example_ixp_v6 {
|
|
|
|
description "Example IXP Route Servers (IPv6)";
|
|
|
|
local 2001:db8:3000::3 as 64500;
|
|
|
|
neighbor 2001:db8:3000::1 as 64503;
|
|
|
|
default bgp_local_pref 90;
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_ixp_trusted(64503);
|
|
|
|
export where export_cone(64503);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are AS64500 and establishes BGP sessions over
|
|
|
|
both IPv4 and IPv6 with the IXP route server whose ASN is 64503 and exports
|
|
|
|
your entire cone. It assumes your IXP is trusted and doesn't provide any IRR
|
|
|
|
filtering. If you don't trust your IXP, see the [IRR filtering](#irr-filtering)
|
|
|
|
section below.
|
|
|
|
|
|
|
|
### Downstreams
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol bgp example_downstream_v4 {
|
|
|
|
description "Example Downstream (IPv4)";
|
|
|
|
local 203.0.113.3 as 64500;
|
|
|
|
neighbor 203.0.113.7 as 64504;
|
|
|
|
default bgp_local_pref 120;
|
|
|
|
|
|
|
|
ipv4 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_downstream(64504, IRR_DOWNSTREAM_V4, IRR_DOWNSTREAM_ASN);
|
|
|
|
export where export_to_downstream();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol bgp example_downstream_v6 {
|
|
|
|
description "Example Downstream (IPv6)";
|
|
|
|
local 2001:db8:3000::3 as 64500;
|
|
|
|
neighbor 2001:db8:3000::7 as 64504;
|
|
|
|
default bgp_local_pref 120;
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
import keep filtered;
|
|
|
|
import where import_downstream(64504, IRR_DOWNSTREAM_V6, IRR_DOWNSTREAM_ASN);
|
|
|
|
export where export_to_downstream();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are AS64500 and establishes BGP sessions over
|
|
|
|
both IPv4 and IPv6 with a downstream whose ASN is 64504 and exports all your
|
|
|
|
routes. For your protection, downstream imports without IRR is *not* supported.
|
|
|
|
For details about setting up IRR, see the [IRR filtering](#irr-filtering)
|
|
|
|
section below.
|
|
|
|
|
2023-09-29 02:15:19 -04:00
|
|
|
### Route collectors
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol bgp route_collector {
|
2024-06-01 14:33:57 -04:00
|
|
|
description "Example Route Collector";
|
2023-09-29 02:15:19 -04:00
|
|
|
local 2001:db8:2000::2 as 64500;
|
|
|
|
neighbor 2001:db8:9000::1 as 64505;
|
|
|
|
multihop;
|
|
|
|
|
|
|
|
ipv4 {
|
|
|
|
add paths on;
|
|
|
|
import none;
|
|
|
|
export where export_monitoring();
|
|
|
|
};
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
add paths on;
|
|
|
|
import none;
|
|
|
|
export where export_monitoring();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are AS64500 and establishes a multihop BGP
|
|
|
|
session with your route collector over IPv6, using multiprotocol BGP to export
|
|
|
|
routes for both IPv4 and IPv6 in a single session, using `add paths` to also
|
|
|
|
all routes instead of the best routes available.
|
|
|
|
|
2023-09-16 22:29:30 -04:00
|
|
|
## BGP communities
|
|
|
|
|
|
|
|
The following large informational communities are implemented by default:
|
|
|
|
* `YOUR_ASN:1:x`: route received from IXP with ID x;
|
|
|
|
* `YOUR_ASN:2:x`: route received from neighbour with ASN x;
|
|
|
|
* `YOUR_ASN:3:100`: route received from peer;
|
|
|
|
* `YOUR_ASN:3:101`: route received from IXP route server;
|
|
|
|
* `YOUR_ASN:3:102`: route received from upstream; and
|
|
|
|
* `YOUR_ASN:3:103`: route received from downstream.
|
|
|
|
|
|
|
|
The following large control communities are implemented by default and can be
|
|
|
|
used by downstreams:
|
|
|
|
* `YOUR_ASN:10:x`: do not export route to ASx;
|
|
|
|
* `YOUR_ASN:11:x`: prepend `YOUR_ASN` once upon export to ASx;
|
|
|
|
* `YOUR_ASN:12:x`: prepend `YOUR_ASN` twice upon export to ASx; and
|
2024-08-15 02:07:18 -04:00
|
|
|
* `YOUR_ASN:13:x`: prepend `YOUR_ASN` thrice upon export to ASx.
|
2023-09-16 22:29:30 -04:00
|
|
|
|
|
|
|
## IRR filtering
|
|
|
|
|
|
|
|
1. Follow [`irr-filters.example`][irr-conf] and create `/etc/bird/irr-filters`
|
|
|
|
for the peers you would like to filter. (To use alternative locations, edit
|
|
|
|
[`make-irr-filter`][irr-script] accordingly.)
|
|
|
|
2. Run `make-irr-filter` to re-generate IRR filters.
|
|
|
|
3. Add `include "filter_irr.conf";` into your `bird.conf`.
|
|
|
|
4. Instead of `import_peer_trusted(asn)` or `import_ixp_trusted(ixp_id)`, use
|
|
|
|
`import_peer(asn, IRR_PEER_V4, IRR_PEER_ASN)` or
|
|
|
|
`import_peer(asn, IRR_PEER_V6, IRR_PEER_ASN)`, and similarly for IXPs.
|
|
|
|
5. Create a cron job that runs `make-irr-filter` followed by `birdc configure`.
|
|
|
|
Daily is a reasonable cadence.
|
|
|
|
|
2024-03-29 03:42:54 -04:00
|
|
|
## PeeringDB prefix limits
|
|
|
|
|
|
|
|
1. Follow [`prefix-limits.example`][prefix-conf] and create
|
|
|
|
`/etc/bird/prefix-limits` for peers for whom you'd like to enforce a prefix
|
|
|
|
limit.
|
|
|
|
2. Adjust [`make-prefix-limits`][prefix-script] to use your own PeeringDB mirror
|
|
|
|
if you risk getting rate limited.
|
|
|
|
3. Run `make-prefix-limits` to re-generate the prefix limits file.
|
|
|
|
4. Add `include "prefix_limit.conf";` into your `bird.conf`.
|
|
|
|
5. You can use constants like `LIMIT_AS200351_V4` or `LIMIT_AS200351_V6` in your
|
|
|
|
`bird.conf`, for example:
|
|
|
|
```
|
|
|
|
protocol bgp peer_v6 {
|
|
|
|
...
|
|
|
|
|
|
|
|
ipv6 {
|
|
|
|
import limit LIMIT_AS23456_V6 action disable;
|
|
|
|
...
|
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
6. Create a cron job that runs `make-prefix-limits` followed by
|
|
|
|
`birdc configure`. Daily is a reasonable cadence.
|
|
|
|
|
2023-09-26 21:07:17 -04:00
|
|
|
## RPKI filtering
|
|
|
|
|
|
|
|
While this filter library implements RPKI, you still need to populate the
|
|
|
|
`rpki4` and `rpki6` routing tables via an `rpki` protocol in `bird`. Otherwise,
|
|
|
|
all routes will be treated as RPKI unknown. This can be configured as follows:
|
|
|
|
|
|
|
|
```
|
|
|
|
protocol rpki {
|
|
|
|
roa4 { table rpki4; };
|
|
|
|
roa6 { table rpki6; };
|
|
|
|
transport tcp;
|
|
|
|
remote "127.0.0.1" port 9001;
|
|
|
|
retry keep 90;
|
|
|
|
refresh keep 900;
|
|
|
|
expire keep 172800;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The example above assumes you are running the RTR protocol on `127.0.0.1:9001`.
|
|
|
|
This may be provided by something like Routinator, `rtrtr`, `gortr`, or
|
|
|
|
something similar. I recommend using `rtrtr` to pull a JSON feed from someone's
|
|
|
|
Routinator instance over HTTPS.
|
|
|
|
|
2023-09-16 22:29:30 -04:00
|
|
|
[pv]: https://pathvector.io/
|
|
|
|
[filter]: filter_bgp.conf
|
|
|
|
[skeleton]: skeleton.conf
|
|
|
|
[irr-conf]: irr-filters.example
|
|
|
|
[irr-script]: make-irr-filter
|
2024-03-29 03:42:54 -04:00
|
|
|
[prefix-conf]: prefix-limits.example
|
|
|
|
[prefix-script]: make-prefix-limits
|