BGP from Scratch: Setting Up Your First Autonomous System with BIRD 2
Running your own Autonomous System isn't just for ISPs anymore. If you're multihoming across data centers, need provider-independent addressing, or want to control your traffic engineering — BGP is how you get there.
This guide takes you from zero to a working BGP setup with BIRD 2, including getting your ASN, configuring peering, and setting up RPKI.
Prerequisites
- A server (VPS or dedicated) with a public interface
- Your own ASN and at least one IP prefix (see below)
- At least one upstream provider willing to establish a BGP session with you
- Basic Linux networking knowledge
Getting Number Resources
Before you can run BGP, you need:
- An ASN — Your unique identifier in the global routing table
- IP space — At least a /48 IPv6 (or /24 IPv4, though these are scarce and expensive)
Through an RIR Directly
If you're in the RIPE NCC region (Europe, Middle East, parts of Central Asia), you can request resources directly if you're a LIR (Local Internet Registry) member, or through a sponsoring LIR if you're not.
The sponsorship path is most common for small operators:
- Find a LIR sponsor that serves your region
- The LIR submits a resource request to RIPE NCC on your behalf
- Resources are registered under your organization — you maintain ownership
- Annual costs are typically $60-90/year for ASN + IPv6
Important: Resources obtained through sponsorship belong to you, not the LIR. You can transfer sponsorship to any other LIR at any time.
For ARIN (North America), the process is similar but with different fee structures and policies.
Installing BIRD 2
BIRD 2 is the standard BGP daemon for Linux. It's lightweight, fast, and handles both IPv4 and IPv6 in a single instance.
# Debian/Ubuntu
sudo apt install bird2
# Or build from source for latest version
git clone https://gitlab.nic.cz/labs/bird.git
cd bird
autoreconf
./configure --prefix=/usr --sysconfdir=/etc/bird
make && sudo make install
BIRD 2 Configuration
The main config lives at /etc/bird/bird.conf. Here's a production-ready starting point:
# /etc/bird/bird.conf
# Your ASN
define MY_ASN = 215000;
# Your prefixes
define MY_PREFIX_V6 = 2001:db8:abcd::/48;
# Router ID (use a stable IPv4 address, even for IPv6-only networks)
router id 10.0.0.1;
# Logging
log syslog all;
log "/var/log/bird.log" { warning, error, fatal };
# Device protocol - scans interfaces
protocol device {
scan time 10;
}
# Direct protocol - learns connected routes
protocol direct {
ipv6;
interface "lo";
}
# Static routes for your prefixes (announce via BGP)
protocol static static_v6 {
ipv6;
route MY_PREFIX_V6 blackhole;
}
# Kernel protocol - installs routes into OS routing table
protocol kernel {
ipv6 {
export filter {
# Only install routes learned from BGP
if source = RTS_BGP then accept;
reject;
};
import none;
};
scan time 15;
learn;
}
# ============ Filters ============
# What we announce to upstreams
filter export_upstream {
# Only announce our own prefixes
if net = MY_PREFIX_V6 then accept;
reject;
}
# What we accept from upstreams
filter import_upstream {
# Accept default route
if net = ::/0 then accept;
# Reject bogons
if net ~ [ ::/8+, fe80::/10+, fc00::/7+ ] then reject;
# Reject too-specific prefixes
if net.len > 48 then reject;
accept;
}
# ============ BGP Sessions ============
# Template for upstream providers
template bgp upstream {
local as MY_ASN;
ipv6 {
import filter import_upstream;
export filter export_upstream;
import limit 200000 action restart;
};
graceful restart on;
long lived graceful restart on;
error wait time 30, 300;
error forget time 3600;
}
# Upstream Provider A
protocol bgp upstream_a from upstream {
neighbor 2001:db8:1::1 as 64500;
description "Transit Provider A";
}
# Upstream Provider B
protocol bgp upstream_b from upstream {
neighbor 2001:db8:2::1 as 64501;
description "Transit Provider B";
}
Understanding the Data Flow
Your server
├── BIRD 2 daemon
│ ├── static_v6: Blackhole route for your prefix
│ ├── upstream_a: BGP session with Provider A
│ │ ├── Export: Your prefix (2001:db8:abcd::/48)
│ │ └── Import: Routes from Provider A (including ::/0)
│ ├── upstream_b: BGP session with Provider B
│ │ ├── Export: Your prefix
│ │ └── Import: Routes from Provider B
│ └── kernel: Installs best routes into Linux routing table
└── Linux kernel
└── Routing table with learned routes
When you start BIRD:
- The
static_v6protocol creates a blackhole route for your prefix - BIRD announces your prefix to both upstreams via BGP
- Upstreams propagate your route to the global Internet
- Upstreams send you their routes (or just a default route)
- BIRD selects the best path and installs it in the kernel
Establishing Sessions
After starting BIRD, check session status:
sudo birdc show protocols all upstream_a
Expected progression:
Idle → Connect → OpenSent → OpenConfirm → Established
If it sticks at Connect or Active:
- Check firewall allows TCP port 179 (BGP)
- Verify neighbor IP and ASN are correct
- Confirm the upstream has configured their end
# Debug BGP issues
sudo birdc show protocols all upstream_a
sudo birdc show route export upstream_a
sudo birdc show route import table upstream_a
# Check if your prefix is being announced
sudo birdc show route for 2001:db8:abcd::/48 all
Securing Your Routes with RPKI
RPKI (Resource Public Key Infrastructure) protects against BGP hijacking by cryptographically verifying which ASN is authorized to announce a prefix.
Step 1: Create ROAs in the RIPE Portal
Log into the LIR portal (or ask your sponsoring LIR) and create Route Origin Authorizations:
Prefix: 2001:db8:abcd::/48
Origin ASN: AS215000
Max Length: 48
Step 2: Run an RPKI Validator
# Install Routinator (RPKI validator by NLnet Labs)
sudo apt install routinator
# Initialize the TAL (Trust Anchor Locators)
routinator init --accept-arin-rpa
# Run as a service
sudo systemctl enable --now routinator
Routinator serves validated ROAs over RTR protocol on port 3323.
Step 3: Configure BIRD to Use RPKI
# RPKI validator connection
protocol rpki rpki_validator {
roa6 { table roa_v6; };
remote "127.0.0.1" port 3323;
retry keep 5;
refresh keep 30;
expire 600;
}
# ROA table
roa6 table roa_v6;
# Updated import filter with RPKI validation
filter import_upstream_rpki {
if net = ::/0 then accept;
if net ~ [ ::/8+, fe80::/10+, fc00::/7+ ] then reject;
if net.len > 48 then reject;
# RPKI validation
if (roa_check(roa_v6, net, bgp_path.last) = ROA_INVALID) then {
print "RPKI INVALID: ", net, " from AS", bgp_path.last;
reject;
}
accept;
}
Traffic Engineering Basics
With two upstreams, you can control traffic flow:
Prefer One Upstream for Outbound
filter import_upstream_preferred {
# Same as import_upstream...
# Prefer this upstream by setting higher local preference
bgp_local_pref = 200;
accept;
}
filter import_upstream_backup {
# Lower preference = backup path
bgp_local_pref = 100;
accept;
}
Influence Inbound Traffic with AS Path Prepending
filter export_upstream_prepend {
if net = MY_PREFIX_V6 then {
# Make this path look longer = less preferred by remote networks
bgp_path.prepend(MY_ASN);
bgp_path.prepend(MY_ASN);
accept;
}
reject;
}
Monitoring
Looking Glass
Check if your prefix is visible globally:
# Use Hurricane Electric's looking glass
curl -s "https://lg.he.net/api/v1/routes/2001:db8:abcd::/48" | jq
# Or check bgp.tools
# https://bgp.tools/prefix/2001:db8:abcd::/48
Alerting
Set up BGP session monitoring:
# Simple check script
birdc show protocols | grep -E "upstream_[ab]" | grep -v Established && \
echo "BGP SESSION DOWN" | mail -s "BGP Alert" noc@yourcompany.com
Common Mistakes
Not filtering bogons: Always filter RFC 1918, RFC 6598, documentation prefixes, and overly specific routes on import.
No import limits: Without
import limit, a misconfigured peer can crash your router by sending millions of routes. Set reasonable limits.Forgetting reverse DNS: Set up PTR records for your IP space. Essential for email and professional appearance.
Single upstream = no benefit: An ASN with one upstream is just extra complexity. The value comes from multihoming.
Not monitoring ROA status: ROAs expire. Set up monitoring to alert before expiry.
Next Steps
Once your basic setup is running:
- Join an Internet Exchange (IX): Free peering with hundreds of networks. Most IXes require an ASN and a /48.
- Set up communities: Use BGP communities to signal routing preferences to your upstreams.
- Automate with IRR: Register your routing policy in an Internet Routing Registry for automated peering.
- Consider Anycast: Announce the same prefix from multiple locations for geographic load balancing.
Running your own AS? What's your setup look like? Share your BIRD configs and peering stories in the comments.
Top comments (0)