|
|
# BGP filtering
|
|
|
|
|
|
Here is an example (or perhaps best practices) of BGP border filtering for a (simple) AS. Such AS has some uplinks, peers and clients. It wants to import everything from uplinks, export its and clients' routes to uplinks and peers, receive routes from peers and clients (either theirs or their clients') and export everything to clients. It is generally a good idea to do a preventive filtering to protect your network (and also your neighbors) from some garbage that may be (usually accidentally) propagated in full BGP feed.
|
|
|
|
|
|
First, generic import functions. We use two strategies of prefix filtering: In function rt_import(), we accept only explicitly configured prefixes (specified in the third argument). We also filter originating ASNs (valid are specified in the second argument). This strategy is used for clients and peers. Note that bgp_path.last returns 0 for routes with aggregated paths. In function rt_import_all() - we accept everything with except of obviously incorrect prefixes, like martians or our own prefixes (specified in function net_local()) and also filter too long (> 24) or too short (< 8) prefixes (as a part of the martian check). This strategy is used for uplinks and peering with route servers.
|
|
|
|
|
|
Besides prefix filtering, we did also some generic validation of route attributes, like checking first AS in AS_PATH, limiting AS_PATH length (to prevent problems with misconfigured routers propagating too long path and buggy routers crashing on that) and checking whether NEXT_HOP attribute is the same as the BGP neighbor address. The last check is not really universal and you may want to omit it or modify it - there are legitimate cases when a neighbor wants to send a different NEXT_HOP attribute (for example, if there are two border routers to that AS and two BGP sessions, the secondary router may propagate the IP address of a primary as a next hop). Function rt_import_rs() implements less strict filtering useful for peering to with route servers (where these checking the first ASN and the next hop is nonsense). More careful admins may in that case check the next hop for being a member of a network used in an internet exchange for peering.
|
|
|
|
|
|
function net_martian()
|
|
|
{
|
|
|
return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
|
|
|
127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
|
|
|
}
|
|
|
|
|
|
function net_local()
|
|
|
{
|
|
|
return net ~ [ 12.10.0.0/16+, 34.10.0.0/16+ ];
|
|
|
}
|
|
|
|
|
|
function rt_import(int asn; int set peer_asns; prefix set peer_nets)
|
|
|
{
|
|
|
if ! (net ~ peer_nets) then return false;
|
|
|
if ! (bgp_path.last ~ peer_asns) then return false;
|
|
|
if bgp_path.first != asn then return false;
|
|
|
if bgp_path.len > 64 then return false;
|
|
|
if bgp_next_hop != from then return false;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
function rt_import_all(int asn)
|
|
|
{
|
|
|
if net_martian() || net_local() then return false;
|
|
|
if bgp_path.first != asn then return false;
|
|
|
if bgp_path.len > 64 then return false;
|
|
|
if bgp_next_hop != from then return false;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
function rt_import_rs(int asn)
|
|
|
{
|
|
|
if net_martian() || net_local() then return false;
|
|
|
if bgp_path.len > 64 then return false;
|
|
|
return true;
|
|
|
}
|
|
|
</pre>
|
|
|
|
|
|
Second, generic export functions. In both cases, we export our own prefixes (configured as static unreachable routes in the static protocol named "static_bgp"). Then we reject non-BGP routes (like from internal OSPF). We add some superfluous checks to prevent accidental propagation of some garbage. And finally, we either propagate all other routes (to the clients) or just routes from our clients (to uplinks or peers). There are several ways how to implement this check.
|
|
|
|
|
|
The first idea would be to check route prefixes against a list, but this would duplicate the the prefix lists (these are already in import filters) and mistakenly export routes received in an unexpected way (like if a link to a client is broken and the router received a prefix to that client from an uplink). The reasonable way to do that check is to check the first ASN in AS_PATH against a list of clients' ASNs or to check the next hop against a list of IPs of clients' border routers. There are also other possible ways (like some kind of marking routes accepted from clients in the import).
|
|
|
|
|
|
function rt_export()
|
|
|
{
|
|
|
if proto = "static_bgp" then return true;
|
|
|
if source != RTS_BGP then return false;
|
|
|
if net_martian() then return false;
|
|
|
if bgp_path.len > 64 then return false;
|
|
|
# return bgp_next_hop ~ [ 100.1.1.1, 100.1.1.2, 200.1.1.1 ];
|
|
|
return bgp_path.first ~ [ 345, 346 ];
|
|
|
}
|
|
|
|
|
|
|
|
|
function rt_export_all()
|
|
|
{
|
|
|
if proto = "static_bgp" then return true;
|
|
|
if source != RTS_BGP then return false;
|
|
|
if net_martian() then return false;
|
|
|
if bgp_path.len > 64 then return false;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
Now, we could make specific filters for neighbors. These are simple, so you may prefer to write them inline, or using _where_ keyword. Uplinks use rt_import_all and rt_export, peers use rt_import() and rt_export() and clients use rt_import() and rt_export_all(). Arguments of rt_import() are the ASNs and prefixes of a neighor _and_ its clients.
|
|
|
|
|
|
filter bgp_in_uplink_123
|
|
|
{
|
|
|
if ! rt_import_all(123) then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
filter bgp_out_uplink_123
|
|
|
{
|
|
|
if ! rt_export() then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
|
|
|
filter bgp_in_peer_234
|
|
|
{
|
|
|
if ! rt_import(234, [ 234, 1234, 2345, 3456 ],
|
|
|
[ 12.34.0.0/16, 23.34.0.0/16, 34.56.0.0/16 ])
|
|
|
then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
filter bgp_out_peer_234
|
|
|
{
|
|
|
if ! rt_export() then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
|
|
|
filter bgp_in_rs
|
|
|
{
|
|
|
if ! rt_import_rs() then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
filter bgp_out_rs
|
|
|
{
|
|
|
if ! rt_export() then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
|
|
|
filter bgp_in_client_345
|
|
|
{
|
|
|
if ! rt_import(345, [ 345 ], [ 34.5.0.0/16 ]) then reject;
|
|
|
accept;
|
|
|
}
|
|
|
|
|
|
filter bgp_out_client_345
|
|
|
{
|
|
|
if ! rt_export_all() then reject;
|
|
|
accept;
|
|
|
} |