· 6 min read

BIRD 2 BGP Configuration on a Linux VDS: Filters and Policy

A BIRD 2 BGP setup on a Linux VDS needs four things: a protocol bgp block with your neighbor details, an explicit export filter that announces only your own prefixes, an import filter that rejects junk routes, and a way to verify it with birdc. This guide walks through a complete, working configuration you can adapt, plus the mistakes that most often keep a session from coming up.

All addresses below come from documentation ranges (AS64496, 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32). Replace them with the values from your own provider and RIR records.

What you need before you start

On Nixcon's BGP-enabled VDS, the ASN and prefix ownership check against RPKI ROA and RIR/IRR data is automatic, and your ASN is added to the AS-NIXCON AS-SET. Create the ROA first so provisioning isn't delayed. Our network is AS40368, which is used as the upstream ASN in the examples.

Step 1: Install BIRD 2

On Debian or Ubuntu:

apt update
apt install bird2
bird --version        # confirm 2.x
systemctl enable bird

The main config file is /etc/bird/bird.conf. Check your version: BIRD 1.x uses separate bird6 and different syntax, and the examples here won't work there.

Step 2: Put your prefix on the host

BGP only announces what you tell it to. The prefix usually lives on a dummy interface so the VDS answers on those addresses:

ip link add dummy0 type dummy
ip link set dummy0 up
ip addr add 192.0.2.10/24 dev dummy0

Make this persistent with netplan, systemd-networkd, or ifupdown, depending on your distribution. Otherwise it disappears on reboot.

Step 3: A complete BIRD 2 configuration

# /etc/bird/bird.conf
router id 198.51.100.2;          # any unique IPv4 value; use your peering IP
log syslog all;

define MY_ASN       = 64496;
define UPSTREAM_ASN = 40368;

# Prefixes we are allowed to originate
define MY_PREFIXES_V4 = [ 192.0.2.0/24 ];

# Bogons and reserved space (documentation ranges omitted
# here so the example prefix isn't blocked)
define BOGONS_V4 = [
  0.0.0.0/8+, 10.0.0.0/8+, 100.64.0.0/10+, 127.0.0.0/8+,
  169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+,
  198.18.0.0/15+, 224.0.0.0/4+, 240.0.0.0/4+
];

roa4 table roa_v4;

protocol device {}

# Originate our prefix
protocol static originate_v4 {
  ipv4;
  route 192.0.2.0/24 blackhole;
}

# Install learned routes into the kernel
protocol kernel {
  ipv4 {
    import none;
    export all;
  };
}

filter export_v4 {
  if net ~ MY_PREFIXES_V4 then accept;
  reject;
}

filter import_v4 {
  if net ~ BOGONS_V4 then reject;
  if net ~ MY_PREFIXES_V4 then reject;          # never accept our own space
  if net.len > 24 then reject;                  # too specific
  if net.len < 8 && net.len != 0 then reject;   # too broad
  if bgp_path.len > 64 then reject;
  if roa_check(roa_v4, net, bgp_path.last) = ROA_INVALID then reject;
  accept;
}

protocol bgp nixcon_v4 {
  description "Upstream AS40368 IPv4";
  local 198.51.100.2 as MY_ASN;
  neighbor 198.51.100.1 as UPSTREAM_ASN;
  # password "from-your-provider";   # only if required
  ipv4 {
    next hop self;
    import filter import_v4;
    export filter export_v4;
    import limit 1000 action restart;   # raise for full table
  };
}

The ROA check only works once an RPKI validator feeds the roa_v4 table (see below). Until then, comment out that line or every route comes back as unknown, which still passes, but you gain no protection.

Why the filters look the way they do

BIRD 2 follows RFC 8212 for eBGP: with no filter configured, it neither accepts nor announces anything. That is a safe default, and it explains many "session is up but nothing happens" reports. Be explicit.

Export policy

The export filter is the most important safety control. A leak of someone else's routes from your VDS can cause real damage. Rules to follow:

Import policy

What you import depends on what you ordered. Providers typically offer a choice between Nixcon-only routes, a default route, or the full routing table. Adjust accordingly:

Mode Import filter approach Suggested limit
Provider routes only Accept all after bogon/length checks Small, e.g. 1000
Default route only if net = 0.0.0.0/0 then accept; reject; 1–2 routes
Full table Full filter above (allow /8–/24) Size it to current table, with headroom

Memory also matters. A full table needs a VDS with enough RAM, so check before enabling it.

Run a validator such as Routinator or StayRTR on the VDS or nearby, then point BIRD at it:

protocol rpki validator1 {
  roa4 { table roa_v4; };
  remote 127.0.0.1 port 3323;
  retry keep 90;
  refresh keep 900;
  expire keep 172800;
}

Routes with an invalid origin are then dropped by the roa_check line in the import filter.

Step 5: IPv6

Add a second session over IPv6 with its own channel and filters:

define MY_PREFIXES_V6 = [ 2001:db8:1000::/48 ];

filter export_v6 {
  if net ~ MY_PREFIXES_V6 then accept;
  reject;
}

protocol bgp nixcon_v6 {
  local 2001:db8:ffff::2 as MY_ASN;
  neighbor 2001:db8:ffff::1 as UPSTREAM_ASN;
  ipv6 {
    import filter { if net.len > 48 then reject; accept; };
    export filter export_v6;
  };
}

Add a matching static route and an IPv6 bogon list in production. The /48 here is a documentation-style example; use your real allocation.

Step 6: Apply and verify

birdc configure check     # syntax check
birdc configure           # reload without dropping sessions
birdc show protocols      # state should be "Established"
birdc show protocols all nixcon_v4
birdc show route export nixcon_v4    # what you announce
birdc show route protocol nixcon_v4  # what you learn

Then confirm from the outside. Nixcon's public looking glass supports BGP route, ping, and traceroute queries from Izmir, Frankfurt, and US locations, so you can see whether your prefix is visible and where it's reachable from.

Common mistakes and fixes

When you need DDoS protection

An unprotected BGP announcement exposes your block directly to attack traffic. If your block is a likely target, as with game servers or hosting, consider the DDoS-protected announcement option, or DDoS protection and firewall in front of your services. Your BIRD configuration stays the same, so the same filter discipline applies.

Frequently asked questions

Do I need a full routing table on a BGP VDS?

Not usually. If you only need to announce your own prefix and reach the provider's network, provider-only routes or a default route are enough and use far less memory. A full table is useful when you want to make your own path decisions, but it needs more RAM and a larger import limit.

Why does BIRD 2 announce nothing after the session is Established?

BIRD 2 follows RFC 8212, so an eBGP session with no export filter announces nothing. Add an explicit export filter that accepts your prefixes, and make sure a static or direct route for the prefix exists in BIRD's table.

Should I install BGP-learned routes into the kernel?

Only if you need them for forwarding decisions. With a default-route setup you can keep your existing default gateway and export nothing learned. If you accept a full table, export it to the kernel with a kernel protocol and check the VDS has enough memory.

How do I check that my announcement is visible on the internet?

Run birdc show route export for your session, then query a public looking glass for your prefix. Nixcon's looking glass at lg.nixconllc.com offers BGP route, ping, and traceroute queries from several locations.

Run your own ASN on a BGP-enabled VDS from Nixcon Group LLC.Learn more