Installing and configuring BIRD (BGP)

For example, I will install BIRD on Ubuntu Server 20.04 and show an example of configuring BGP.

    Switch to the root user:

    sudo -i
    

    To install, run the command (in Ubuntu Server 20.04 version BIRD 1.6.8 was installed):

    apt install bird
    

    Let’s allow forwarding of IPv4 packets between network interfaces (for example, if the server will be used as a router):

    sysctl net.ipv4.conf.all.forwarding
    cat /proc/sys/net/ipv4/ip_forward
    
    sysctl -w net.ipv4.conf.all.forwarding=1
    
    nano /etc/sysctl.conf
    net.ipv4.conf.all.forwarding=1
    sysctl -p
    

    Let’s set up the logs as I described in the article:
    Setting up BIRD logs

    Rename the original BIRD configuration file:

    mv /etc/bird/bird.conf /etc/bird/bird.conf.original
    

    Create a new one and open it in a text editor:

    touch /etc/bird/bird.conf
    nano /etc/bird/bird.conf
    

    Specify globally router id – router ID, IP address of the server that looks to the world, then in the bgp, ospf, etc. sections. it will be possible to specify additionally other router ids, if you do not specify this globally, then a random IP address on the interface will be selected:

    router id X.X.X.X;
    

    In order not to specify our AS several times in the configuration, we define the my_as variable for it, which we will specify later:

    define my_as=XXXXX;
    

    protocol direct defines the network interfaces that will be monitored (the * symbol means everything, I indicated those that look out into the world to other BGP neighbors):

    protocol direct {
    #       interface "*";
            interface "ens1f0", "ens1f1";
    }
    

    The protocol kernel synchronizes the routes of bird with the routes of the operating system kernel:

    protocol kernel {
            persist off;
            scan time 20;
            learn;
            import all;
            export none;
    }
    

    Let me describe some options:
    persist – do not delete routes after bird stops (persist off – delete).
    scan time 20 – Scan the kernel route table every 20 seconds (if the server has tens of thousands of routes that are constantly changing and bird heavily loads the CPU, for example, the accel-ppp server is running, then you can increase the value by 120)
    learn – learning kernel routes
    import all – default
    export none – by default (since there is only one uplink, I specified none, since I manually specified the default route 0.0.0.0 in netplan and do not use the same one that sends uplink. If you specify it manually and accept it, there will be a warning Netlink: File exists, if there are several uplink neighbors, then the default route specified manually must be removed).

    protocol device is needed to scan the state of interfaces (up/down), for example, every 10 seconds, if there are a lot of interfaces, then you can increase the time:

    protocol device {
            scan time 10;
    }
    

    protocol static allows you to specify static routes (I specified my white network, which I will give Uplink to my neighbor):

    protocol static {
    #   import all;
        preference 254;
        route X.X.X.0/23 blackhole;
    }
    

    Since the entire network for NAT X.X.X.0/23 is specified in the blackhole, this will allow creating and passing a route to the Uplink neighbor, and routes from the same network with a smaller “more specific” mask will have a higher priority than /23 and traffic will go through them..

    An example of a function that defines its networks with white IPs:

    function my_nets()
    prefix set my_nets;
    {
    my_nets = [ X.X.X.0/23+, X.X.2.0/24+ ];
    if net ~ my_nets then return true;
    return false;
    }
    

    Define networks with gray IP:

    function bogons()
    prefix set bogons;
    {
    bogons = [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
    224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
    # Avoid RFC1918 and similar networks
    if net ~ bogons then return true;
    return false;
    }
    

    Let’s create a filter in which we will prohibit accepting our network and gray networks, we will allow everything else:

    filter PROVIDER1_IN {
    if bogons() || my_nets() then reject;
    #bgp_local_pref = 205;
    accept;
    }
    

    Another example of a filter that allows only the default route, and forbids everything else:

    filter DEFAULT {
       if net ~ [ 0.0.0.0/0 ] then {
           accept;
       }
       else reject;
    }
    

    If there are several providers, then we will create more filters:

    filter PROVIDER2_IN {
    #bgp_local_pref = 200;
       if net ~ [ 0.0.0.0/0 ] then {
           accept;
       }
       else reject;
    }
    

    bgp_local_pref allows you to balance the outgoing load, the less – the better, you can apply changes without breaking the session by rereading the provider configuration:

    birdc
    reload IXNFO_COM
    

    Let’s create a filter in which we will allow to export only our network, I will give a few examples:

    filter PROVIDER1_OUT {
        if bogons() then reject;
        if my_nets() then
        {
            bgp_community.delete([(my_as,*)]);
            accept;
        }
        reject;
    }
    
    
    filter PROVIDER1_OUT {
        if bogons() then reject;
    #   if net.len = 32 then reject;
        if my_nets() then
        {
    #       bgp_path.prepend(my_as);
    #       bgp_path.prepend(my_as);bgp_path.prepend(my_as);bgp_path.prepend(my_as);
            bgp_community.empty;
            accept;
        }
        reject;
    }
    

    Now let’s configure BGP for the first provider:

    protocol bgp IXNFO_COM {
        debug { states, events };
        #table master;
        router id X.X.X.X;
        description "IXNFO_COM";
        local as my_as;
        neighbor X.X.X.X as XXXXX;
    
        hold time 60;
        startup hold time 60;
        connect retry time 120;
        keepalive time 20;
        connect delay time 5;
        error wait time 60, 300;
        error forget time 300;
        next hop self;
        path metric 1;
        default bgp_med 0;
    
        source address X.X.X.X;
    
        export where net = X.X.X.0/23;
    #   export filter PROVIDER1_OUT;
        import filter PROVIDER1_IN;
    #   import none;
    
    #    gateway direct;
    }
    

    Let me describe some options:
    hold time – time to wait for a keepalive message from a neighbor, 240 seconds by default.
    connect retry time – time to wait in seconds before trying to connect again.
    error wait time – minimum and maximum delay in seconds between protocol failure and automatic restart, default 60, 300.
    error forget time – maximum time in seconds between two protocol failures that causes an exponential increase in error timeout, default 300.
    keepalive time – delay in seconds between sending two consecutive keepalive messages.
    path metric – path length comparison when choosing the best BGP route, enabled by default.
    export where net – since there is only one network, I directly indicated what can be exported without using a filter.
    import none – if there is only one provider, and the default route is specified manually in the network settings, then nothing needs to be imported from the provider.

    Let’s configure the second provider in the same way:

    protocol bgp PROVIDER2 {
    ...
    }
    

    You can create a template for BGP so as not to duplicate settings for different providers:

    template bgp EXTERNAL {
    debug { states, events };
    local as my_as;
    keepalive time 30;
    hold time 240;
    interpret communities no;
    import keep filtered;
    startup hold time 240;
    connect retry time 120;
    connect delay time 5;
    error wait time 60, 300;
    error forget time 300;
    path metric 1;
    default bgp_med 0;
    default bgp_local_pref 150;
    }
    

    And then specify this template:

    protocol bgp IXNFO_COM from EXTERNAL{
    description "IXNFO_COM";
    neighbor X.X.X.X as XXXXX;
    source address X.X.X.X;
    export filter PROVIDER1_OUT;
    import filter PROVIDER1_IN;
    }
    
    protocol bgp IXNFO_COM2 from EXTERNAL{
    description "IXNFO_COM2";
    neighbor X.X.X.X as XXXXX;
    source address X.X.X.X;
    export filter PROVIDER2_OUT;
    import filter PROVIDER2_IN;
    }
    

    Some more export examples:

    export where (net = 0.0.0.0/0) || ((my_as,XXXXX) ~ bgp_community) || ((my_as,XXXXX) ~ bgp_community);
    
    
    protocol static static_bgp { 
            import all;
            route X.X.X.0/24 reject;
    }
    export where proto = "static_bgp";
    

    If you do not specify Preference, then its default value will be 100.

    Let’s open the BIRD console:

    birdc
    

    Let’s make sure that the session has risen and see the routes:

    show protocols
    show protocols all
    
    show route
    show route all
    show route export IXNFO_COM
    show route advertising-protocol bgp X.X.X.X
    show static
    
    show memory
    show ?
    exit
    ip route | grep -E "(default|nexthop)"
    

    The BIRD version can be viewed like this:

    show status
    

    You can also execute BIRD commands from Linux, for example to monitor the result via Zabbix:

    birdc show route|less
    birdc show protocols
    

    Let’s check if bird autorun is activated when the operating system starts:

    systemctl is-enabled bird.service
    systemctl is-enabled bird6.service
    
    systemctl enable bird.service
    systemctl disable bird6.service
    
    systemctl restart bird.service
    systemctl stop bird6.service
    
    systemctl status bird.service
    systemctl status bird6.service
    

    See also my articles:

    Leave a comment

    Leave a Reply