Passive BGP routeserver

BIRD3 can be used to make a passive BGP route server.

This I use to make desktop and systems in my network internet aware, though the long term aim is that ISP have something like this that lets its customers connect on TCP 179 and therefore able to learn lots of things about the internet in its native format.

Whilst the viability of this may change, indirectly referring to more content, for now presumes the downstream does not have any separate ASN assignment, most likely uses upstream ASN as a placeholder, and the upstream most likely does not allow such users to announce, or just uses it to do link steering and is not forwarded on.

It also minimises any manual work for upstream, set this up once for all customers to enjoy with no per-customer manual intervention, as it is handled by dynamic bgp.

BIRD most likely does need patching to allow this scenario, otherwise it may start refusing incoming TCP 179 connections when repeatly bringing up and down.

In BIRD3, simply replace bgp_set_start_state(p, BSS_PREPARE); with bgp_set_start_state(p, p->passive ? BSS_CONNECT : BSS_PREPARE); in proto/bgp/bgp.c function static int bgp_start(struct proto *P) and this problem is fixed.

If using the older BIRD2, in proto/bgp/bgp.c replace p->start_state = BSS_PREPARE; with p->start_state = p->passive ? BSS_CONNECT : BSS_PREPARE;

Sample BIRD routeserver Config

This just happens to match the config here. As I have no upstream, use a fake table from kernel table 254 for now, and reflect up/down status of dsl0 with that.

BIRD is very able to guess a router id in some situations.

router id 0.0.0.0;

protocol device {
        scan time 60;
}

protocol kernel {
	learn;
	scan time 60;
	ipv4 { import all; };
	kernel table 254;
	metric 0;
}
protocol kernel {
	learn;
	scan time 60;
	ipv6 { import all; };
	kernel table 254;
	metric 0;
}

protocol direct {
        ipv4;
        ipv6;
        interface "br24", "sit0";
}

filter exporter {
        if ifname = "dsl0" then { accept; }
	if ifname = "br24" then { accept; }
	if ifname = "sit0" then { accept; }
        reject;
}

template bgp clients {
	direct;
	ttl security 255;
	passive on; graceful restart on;
	long lived graceful restart on; 
	ipv4 { export filter exporter; import none; };
	ipv6 { export filter exporter; import none; };

	strict bind on;
	free bind on;
	local as 0xfeedd0be;
}
template bgp clients20v4 from clients {
	source address 192.168.20.1;	
}
template bgp clients20v6 from clients {
	source address
include "br20.conf";;
}

protocol bgp from clients20v4 { neighbor range 192.168.20.0/22 internal; }
protocol bgp from clients20v6 { neighbor range fec0:0:0:20::/62 internal; }

For completeness, the client's bird config. It uses VRF so the "Internet" routes are in vrf20. It does not announce back to upstream so do not care about the router id.

bird here re-advertises into SLAAC, thus replacing radvd in that role, also internally some links have large mtu and use routes to deal with broken mtu instead. And 10.0.0.0 and fec0:0:0:4e4e:0:ff:fe00:0 are deep internal facing.

router id 127.0.0.1;
ipv4 table nn20v4;
ipv6 table nn20v6;

filter radvcompat {
	if  (net ~ [2000::/3]) then {
		krt_mtu = 9200;
		accept;
	}
	if  (net ~ [fec0:0:0::/48{48,64}]) then {
		krt_mtu = 9200;
		accept;
	}
	reject;
}
filter kernelcompat {

	if  (net ~ [2000::/3]) then {
		reject;
	}
	if  (net ~ [2000::/3+]) then {
		krt_mtu = 9200;
		accept;
	}
	if  (net ~ [fec0:0:0::/48+]) then {
		krt_mtu = 9200;
		accept;
	}
	reject;
}

filter notdefault {
	if  (net ~ [fec0:0:0::/48+]) then {
		krt_mtu = 9200;
		accept;
	}
	if  (net ~ [2000::/3+]) then {
		krt_mtu = 1500;
		accept;
	}
	reject;
}

protocol kernel kernel20v4 {
	scan time 60;
	vrf "v20";
	learn;
	ipv4 {
		table nn20v4;
		import all;   # grab the netns 10.0.0.0
		export all;   # Actually insert routes into the kernel routing table
	};
	kernel table 20;
	# do not use default of 32, overrules direct routes at 256
	metric 0;
}

protocol kernel kernel20v6 {
	scan time 60;
	vrf "v20";
	learn;
	ipv6 {
		table nn20v6;
		import filter notdefault;   # grab the netns 10.0.0.0
		export filter kernelcompat;   # Actually insert routes into the kernel routing table
	};
	kernel table 20;
	# do not use default of 32, overrules direct routes at 256
	metric 0;
}

# overrule routes from bgp that are available direct
protocol direct {
        ipv4 {
		table nn20v4;
	};
        ipv6 {
		table nn20v6;
	};
	vrf "v20";
        interface "br20", "br21", "br22", "br23", "br24", "br25";
}

protocol device {
	scan time 60;
}

protocol bgp bgp20 {
	strict bind yes;
	interface "br20";

        source address
include "downstream-ipv6.conf";;

        local
include "downstream-ipv6.conf";
	as 0xfeedd0be;

        neighbor 
include "upstream-ipv6.conf";
	internal;

        direct;
        ttl security 255;
	ipv4 { 
		table nn20v4;
	        import all;
	        export none;
	};
	ipv6 { 
		table nn20v6;
	        import filter notdefault;
	        export none;
	};
}

ipv6 table radv_routes;                 # Manually configured routes go here

protocol static {
        ipv6 { table nn20v6; };
        route 2000::/3 via 
include "upstream-ipv6.conf";
{
#               ra_preference = RA_PREF_HIGH;
#               ra_lifetime = 0xffffffff;
#		krt_mtu = 1500;
        };
}

# may fight smcroute or radvd,
# bird should have raw6 socket and may not report not opening it
# also may be limit on number of routes
protocol radv radv20 {
	description "routes for nn guests";
	propagate routes yes; 

        ipv6 { table nn20v6; export filter radvcompat; };
#        ipv6 { table radv_routes; export filter notdefault; };

	vrf "v20";
        interface "cr20" {
		managed yes;
		other config yes;
		default preference high;
		default lifetime 0; # do not use a default route 😸
		route preference high;
		route lifetime 0xffffffff;

		link mtu 65535;
		
		current hop limit 255;
	        rdnss fec0:0:0:4e4e:0:ff:fe00:0;

	        dnssl {
	                lifetime 3600;
	                domain "lan.home.arpa";
	                domain "lab.home.arpa";
	        };
	};
}

protocol radv radv99 {
	description "routes for nn guests";
	propagate routes yes; 

	vrf "v99";
        interface "cr99" {
		managed yes;
		other config yes;
		default preference high;
		default lifetime 0; # do not use a default route 😸
		route preference high;
		route lifetime 0xffffffff;
		link mtu 65535;

		# sets target ipv6 enclosing mtu
		# cat /proc/sys/net/ipv6/conf/brcr20/mtu
#		link mtu 1500;
		
		current hop limit 255;
	        rdnss fec0:0:0:4e4e:0:ff:fe00:0;

	        dnssl {
	                lifetime 3600;
	                domain "lan.home.arpa";
	                domain "lab.home.arpa";
	        };
	};
}