Configure BIRD BGP routing daemon for advanced routing policies and network automation

Advanced 45 min Apr 28, 2026 126 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up BIRD 2.15 BGP daemon with advanced routing policies, route filtering, and automated network management. Configure BGP peering, implement complex routing decisions, and set up route aggregation for production networks.

Prerequisites

  • Root or sudo access
  • Basic BGP knowledge
  • Network interface with public IP
  • AS number assignment

What this solves

BIRD (Berkeley Internet Routing Daemon) provides advanced BGP routing capabilities for networks requiring sophisticated routing policies and automation. This tutorial covers BIRD 2.15 installation, BGP neighbor configuration, route filtering, and policy implementation for production network environments.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install BIRD routing daemon

Install BIRD 2.x which provides unified IPv4 and IPv6 routing support with advanced BGP features.

sudo apt install -y bird2 bird2-doc
sudo dnf install -y bird2 bird2-doc

Enable IP forwarding

Enable IP forwarding to allow the system to route traffic between networks.

echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Create BIRD configuration directory structure

Set up organized configuration directories for better management of routing policies and filters.

sudo mkdir -p /etc/bird/filters
sudo mkdir -p /etc/bird/policies
sudo mkdir -p /etc/bird/functions
sudo chown -R bird:bird /etc/bird
sudo chmod 755 /etc/bird /etc/bird/filters /etc/bird/policies /etc/bird/functions

Configure main BIRD configuration

Create the main BIRD configuration file with router ID, logging, and basic protocol setup.

# BIRD 2.15 Configuration

Router ID - use your primary interface IP

router id 203.0.113.10;

Logging configuration

log syslog all; log "/var/log/bird.log" { debug, trace, info, remote, warning, error, auth, fatal, bug };

Include additional configuration files

include "/etc/bird/filters/*.conf"; include "/etc/bird/policies/*.conf"; include "/etc/bird/functions/*.conf";

Enable ECMP (Equal Cost Multi-Path) routing

protocol kernel kernel4 { ipv4 { import none; export filter { if source = RTS_BGP then { accept; } reject; }; }; merge paths yes; learn yes; } protocol kernel kernel6 { ipv6 { import none; export filter { if source = RTS_BGP then { accept; } reject; }; }; merge paths yes; learn yes; }

Device protocol for interface monitoring

protocol device { scan time 10; }

Direct protocol for connected routes

protocol direct { ipv4; ipv6; interface "eth", "ens"; }

Static routes (if needed)

protocol static static4 { ipv4; # Add static routes here if needed # route 192.168.1.0/24 via 203.0.113.1; } protocol static static6 { ipv6; # Add IPv6 static routes here if needed }

Create BGP filter functions

Define reusable filter functions for common BGP route processing tasks.

# BGP Filter Functions

Function to check if route is a bogon (should not be advertised)

function is_bogon_prefix() { if net ~ [ 0.0.0.0/8+, # RFC 1122 "this network" 10.0.0.0/8+, # RFC 1918 private 100.64.0.0/10+, # RFC 6598 Carrier Grade NAT 127.0.0.0/8+, # RFC 1122 loopback 169.254.0.0/16+, # RFC 3927 link local 172.16.0.0/12+, # RFC 1918 private 192.0.0.0/24+, # RFC 6890 IETF Protocol Assignments 192.0.2.0/24+, # RFC 5737 TEST-NET-1 192.168.0.0/16+, # RFC 1918 private 198.18.0.0/15+, # RFC 2544 benchmarking 198.51.100.0/24+, # RFC 5737 TEST-NET-2 203.0.113.0/24+, # RFC 5737 TEST-NET-3 224.0.0.0/4+, # RFC 3171 multicast 240.0.0.0/4+ # RFC 1112 reserved ] then return true; return false; }

Function to check valid AS path length

function is_valid_as_path_length() { if bgp_path.len > 64 then return false; if bgp_path.len < 1 then return false; return true; }

Function to set BGP communities for route classification

function set_route_communities() { # Set informational communities if source = RTS_STATIC then { bgp_community.add((65000,100)); # Static route } if source = RTS_BGP then { bgp_community.add((65000,200)); # BGP learned } return true; }

Function for prefix length filtering

function is_valid_prefix_length() { # IPv4 prefix length limits if net.type = NET_IP4 then { if net.len < 8 then return false; # Too broad if net.len > 24 then return false; # Too specific } # IPv6 prefix length limits if net.type = NET_IP6 then { if net.len < 12 then return false; # Too broad if net.len > 48 then return false; # Too specific } return true; }

Create import and export filters

Define sophisticated import and export filters for BGP route processing and security.

# BGP Import Filter
filter bgp_import_filter
{
    # Reject bogon prefixes
    if is_bogon_prefix() then {
        print "Rejecting bogon prefix: ", net;
        reject;
    }
    
    # Check AS path validity
    if !is_valid_as_path_length() then {
        print "Rejecting route with invalid AS path length: ", net;
        reject;
    }
    
    # Check prefix length limits
    if !is_valid_prefix_length() then {
        print "Rejecting route with invalid prefix length: ", net;
        reject;
    }
    
    # Reject routes with private AS numbers in path
    if bgp_path ~ [64512..65534, 4200000000..4294967294] then {
        print "Rejecting route with private AS in path: ", net;
        reject;
    }
    
    # Set local preference based on communities
    if bgp_community ~ [(65000,10)] then {
        bgp_local_pref = 200; # Preferred routes
    }
    if bgp_community ~ [(65000,20)] then {
        bgp_local_pref = 100; # Normal routes
    }
    if bgp_community ~ [(65000,30)] then {
        bgp_local_pref = 50;  # Backup routes
    }
    
    # Default local preference if not set
    if bgp_local_pref = 0 then {
        bgp_local_pref = 100;
    }
    
    set_route_communities();
    accept;
}

BGP Export Filter

filter bgp_export_filter { # Only export our own prefixes and selected routes if source != RTS_STATIC && source != RTS_BGP then { reject; } # Don't export bogon prefixes if is_bogon_prefix() then { reject; } # Export policy based on communities if bgp_community ~ [(65000,999)] then { reject; # No-export community } # Set MED (Multi-Exit Discriminator) based on route type if source = RTS_STATIC then { bgp_med = 0; # Prefer static routes } else { bgp_med = 100; # BGP learned routes } # Prepend AS path for traffic engineering if bgp_community ~ [(65000,666)] then { bgp_path.prepend(bgp_local_as); bgp_path.prepend(bgp_local_as); } accept; }

Configure route aggregation policies

Set up route aggregation to reduce routing table size and improve network efficiency.

# Route Aggregation Configuration

Define aggregate routes

protocol static aggregates4 { ipv4; # Aggregate smaller prefixes into larger ones # Example: aggregate /24 routes into /22 route 203.0.112.0/22 blackhole { bgp_community.add((65000,500)); # Aggregated route marker bgp_local_pref = 200; }; } protocol static aggregates6 { ipv6; # IPv6 aggregation example route 2001:db8::/32 blackhole { bgp_community.add((65000,500)); # Aggregated route marker bgp_local_pref = 200; }; }

Filter for aggregate route announcement

filter aggregate_export_filter { # Only announce aggregates if more specific routes exist if bgp_community ~ [(65000,500)] then { # Check if we have more specific routes in routing table if source = RTS_STATIC then { accept; } } reject; }

Configure BGP sessions with upstream providers

Set up BGP peering sessions with different types of neighbors including upstream providers and peers.

# Add this to the end of /etc/bird/bird.conf

BGP Session with Upstream Provider 1

protocol bgp upstream1 { description "Upstream Provider 1"; local as 65001; neighbor 203.0.113.1 as 174; ipv4 { import filter bgp_import_filter; export filter bgp_export_filter; next hop self; }; # Connection parameters hold time 180; keepalive time 60; connect retry time 30; # Authentication (optional) password "your_bgp_password_here"; # BGP attributes default bgp_local_pref 100; default bgp_med 0; }

BGP Session with Peer Network

protocol bgp peer1 { description "Peer Network 1"; local as 65001; neighbor 203.0.113.2 as 65002; ipv4 { import filter { # More restrictive import for peers if is_bogon_prefix() then reject; if !is_valid_prefix_length() then reject; if bgp_path.len > 10 then reject; # Shorter AS paths only bgp_local_pref = 150; # Prefer peer routes set_route_communities(); accept; }; export filter { # Only export our own routes to peers if source = RTS_STATIC then accept; if bgp_community ~ [(65000,100)] then accept; reject; }; }; hold time 180; keepalive time 60; multihop 2; }

BGP Session with Route Server (IX)

protocol bgp routeserver1 { description "Internet Exchange Route Server"; local as 65001; neighbor 203.0.113.254 as 65000; # Route server AS ipv4 { import filter { # Accept diverse routes from route server if is_bogon_prefix() then reject; if !is_valid_as_path_length() then reject; if !is_valid_prefix_length() then reject; # Set local preference based on AS path length if bgp_path.len <= 3 then bgp_local_pref = 120; else if bgp_path.len <= 5 then bgp_local_pref = 110; else bgp_local_pref = 100; set_route_communities(); accept; }; export filter bgp_export_filter; add paths tx; add paths rx; }; rs client; hold time 180; keepalive time 60; }

Configure advanced routing policies

Implement sophisticated routing policies for traffic engineering and redundancy.

# Advanced Routing Policies

Traffic Engineering Policy

filter traffic_engineering_policy { # Primary path selection if bgp_community ~ [(65001,100)] then { bgp_local_pref = 300; # Highest preference accept; } # Secondary path with AS prepending if bgp_community ~ [(65001,200)] then { bgp_local_pref = 200; # Prepend for outbound traffic engineering bgp_path.prepend(bgp_local_as); accept; } # Backup path if bgp_community ~ [(65001,300)] then { bgp_local_pref = 100; # Heavy prepending for backup bgp_path.prepend(bgp_local_as); bgp_path.prepend(bgp_local_as); bgp_path.prepend(bgp_local_as); accept; } # Geographic-based routing if bgp_community ~ [(65001,1001)] then { # European routes bgp_local_pref = 250; } if bgp_community ~ [(65001,1002)] then { # US routes bgp_local_pref = 150; } if bgp_community ~ [(65001,1003)] then { # Asian routes bgp_local_pref = 120; } accept; }

Load Balancing Policy

filter load_balancing_policy { # Enable ECMP for equal cost paths if bgp_local_pref >= 200 then { # Mark routes for ECMP consideration bgp_community.add((65000,777)); } # Weighted load balancing using communities if bgp_community ~ [(65001,5000)] then { bgp_local_pref = 200; # 50% traffic weight } if bgp_community ~ [(65001,3000)] then { bgp_local_pref = 180; # 30% traffic weight } if bgp_community ~ [(65001,2000)] then { bgp_local_pref = 160; # 20% traffic weight } accept; }

Set up automated BGP monitoring and alerting

Create scripts for monitoring BGP session status and automated alerting.

#!/bin/bash

BIRD BGP Monitoring Script

LOG_FILE="/var/log/bird_monitor.log" ALERT_EMAIL="admin@example.com"

Function to log messages

log_message() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE" }

Check BIRD daemon status

check_bird_status() { if ! systemctl is-active --quiet bird; then log_message "ERROR: BIRD daemon is not running" echo "BIRD daemon is down on $(hostname)" | mail -s "BIRD Alert" "$ALERT_EMAIL" return 1 fi return 0 }

Check BGP session status

check_bgp_sessions() { local sessions_down=0 # Get BGP protocol status while IFS= read -r line; do if echo "$line" | grep -q "BGP.*down"; then protocol_name=$(echo "$line" | awk '{print $1}') log_message "WARNING: BGP session $protocol_name is down" echo "BGP session $protocol_name is down on $(hostname)" | mail -s "BGP Session Alert" "$ALERT_EMAIL" sessions_down=$((sessions_down + 1)) fi done < <(birdc show protocols | grep -E "^(upstream|peer|routeserver)") return $sessions_down }

Check routing table size

check_routing_table_size() { local route_count route_count=$(birdc show route count | grep -o '[0-9]\+' | head -1) if [ "$route_count" -lt 100 ]; then log_message "WARNING: Low route count: $route_count" echo "Low route count ($route_count) on $(hostname)" | mail -s "Routing Table Alert" "$ALERT_EMAIL" fi log_message "INFO: Current route count: $route_count" }

Main monitoring logic

main() { log_message "Starting BIRD monitoring check" check_bird_status if [ $? -eq 0 ]; then check_bgp_sessions check_routing_table_size fi log_message "BIRD monitoring check completed" }

Run main function

main

Make monitoring script executable and set up cron job

Configure automated monitoring to run every 5 minutes.

sudo chmod +x /usr/local/bin/bird_monitor.sh
sudo chown bird:bird /usr/local/bin/bird_monitor.sh

Add cron job for automated monitoring

echo "/5 * /usr/local/bin/bird_monitor.sh" | sudo crontab -u bird -

Configure log rotation

Set up log rotation for BIRD logs to prevent disk space issues.

/var/log/bird.log /var/log/bird_monitor.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 bird bird
    postrotate
        systemctl reload bird
    endscript
}

Enable and start BIRD service

Enable BIRD to start automatically on boot and start the service.

sudo systemctl enable bird
sudo systemctl start bird
sudo systemctl status bird

Verify your setup

Check that BIRD is running correctly and BGP sessions are established.

# Check BIRD daemon status
sudo systemctl status bird

Check BIRD configuration syntax

sudo bird -p

Access BIRD control interface

sudo birdc

Inside birdc, run these commands:

show status

show protocols

show protocols all bgp

show route

show route export upstream1

exit

Check routing table

ip route show

Verify BGP sessions

sudo birdc show protocols | grep BGP

Check route counts

sudo birdc show route count

Monitor logs

sudo tail -f /var/log/bird.log
Note: BGP sessions may take several minutes to establish. Use sudo birdc show protocols all bgp to see detailed session status and troubleshoot connection issues.

Advanced configuration examples

Route redistribution from OSPF

Example configuration for redistributing OSPF routes into BGP.

# Add OSPF protocol
protocol ospf v2 ospf_internal {
    ipv4 {
        import all;
        export none;
    };
    area 0.0.0.0 {
        interface "eth1" {
            cost 10;
            hello 10;
            retransmit 5;
            wait 40;
            dead 40;
        };
    };
}

Modify BGP export filter to include OSPF routes

filter bgp_export_with_ospf { # Export static routes if source = RTS_STATIC then accept; # Export selected OSPF routes if source = RTS_OSPF then { # Only export specific OSPF networks if net ~ [203.0.114.0/24, 203.0.115.0/24] then { bgp_community.add((65000,300)); # OSPF redistributed bgp_med = 200; accept; } } reject; }

Conditional route advertisement

Configure conditional advertisement based on route existence.

# Conditional Advertisement Policy
filter conditional_advertisement
{
    # Only advertise aggregate if more specific routes exist
    if net = 203.0.112.0/22 then {
        # Check if any /24 within aggregate exists
        if exists(203.0.112.0/24) || exists(203.0.113.0/24) ||
           exists(203.0.114.0/24) || exists(203.0.115.0/24) then {
            bgp_community.add((65000,600)); # Conditional route
            accept;
        }
        reject;
    }
    
    # Normal processing for other routes
    accept;
}

Common issues

SymptomCauseFix
BGP sessions won't establishFirewall blocking TCP/179 or wrong neighbor IPCheck sudo ufw allow 179/tcp and verify neighbor configuration
"Configuration file syntax error"Missing semicolons or bracket mismatchRun sudo bird -p to check syntax
Routes not being advertisedExport filter rejecting routesCheck export filter logic and use birdc show route export protocol_name
BIRD daemon crashes on startupInvalid router ID or interface issuesCheck router ID matches real interface IP and interfaces exist
No routes in kernel tableKernel protocol not exporting BGP routesVerify kernel protocol export filter allows source = RTS_BGP
High memory usageLarge routing tables with full BGP feedsImplement prefix filtering and route aggregation
Session flappingHold timer too low or network instabilityIncrease hold time to 180+ seconds and check network connectivity
Security Warning: Always implement proper prefix filtering and bogon rejection. Never accept or announce RFC 1918 private addresses over external BGP sessions. Use strong BGP passwords and consider implementing BGP security extensions like RPKI when available.

Performance optimization

Memory and CPU tuning

Optimize BIRD for handling large routing tables and high session counts.

# Add these settings to improve performance

Increase protocol processing timeouts

timeformat protocol short;

Optimize memory usage for large tables

table master4 { # Pre-allocate memory for better performance gc threshold 10000; gc period 300; };

Enable multi-threading for route processing (BIRD 2.0.8+)

protocol kernel kernel4 { ipv4 { import none; export all; }; scan time 20; merge paths yes; kernel table 254; }

Monitoring BGP performance

Advanced monitoring integration with external systems.

#!/bin/bash

Export BIRD metrics for monitoring systems

BGP session count by state

echo "# HELP bird_bgp_sessions_total Total BGP sessions by state" echo "# TYPE bird_bgp_sessions_total gauge" birdc show protocols | grep -c "BGP.*up" | sed 's/^/bird_bgp_sessions_total{state="up"} /' birdc show protocols | grep -c "BGP.*down" | sed 's/^/bird_bgp_sessions_total{state="down"} /'

Route count

echo "# HELP bird_routes_total Total routes in routing table" echo "# TYPE bird_routes_total gauge" birdc show route count | grep -o '[0-9]\+' | head -1 | sed 's/^/bird_routes_total /'

Memory usage

echo "# HELP bird_memory_usage_bytes BIRD memory usage in bytes" echo "# TYPE bird_memory_usage_bytes gauge" birdc show memory | grep 'Routing tables' | awk '{print "bird_memory_usage_bytes{type=\"routing_tables\"}", $4*1024}' birdc show memory | grep 'Route attributes' | awk '{print "bird_memory_usage_bytes{type=\"route_attributes\"}", $4*1024}'

You can integrate BIRD monitoring with existing infrastructure using tools covered in our Prometheus and Grafana monitoring guides or centralized logging setup.

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. See how we run infrastructure like this for European teams.

Need help?

Don't want to manage this yourself?

We handle private cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.