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

Advanced 45 min Apr 28, 2026 1,182 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) ||
           exi

Vous ne voulez pas gérer cela vous-même ?

Nous gérons l'infrastructure des entreprises qui dépendent de leur disponibilité. Entièrement infogéré, avec un interlocuteur fixe qui connaît votre environnement.

Vous avez un interlocuteur fixe qui connaît votre installation

À son bureau à Rotterdam 10:06 · joignable par message, sans formulaire de ticket