CRXN documentation / Configuring Bird2

Configuring Bird2

We now need to configure the routing daemon for your router which will allow you to exchange routes with other routers over the tunnels you will setup later. This is at the core of what makes CRXN an inter-network.

The software we use for this procedure is known as BIRD or BIRD Internet Routing Daemon, of which there are two versions:

  1. Bird 1.6
  2. Bird 2

You can use Bird 1.6 but you are on your own then in terms of configuration, the syntax differs slightly but we recommend (and for the rest of this document we will be) using Bird 2 as it comes with many bug fixes and improvements and most distributions (including Debian) now have support for it.

Installation

In order to install the BIRD daemon on your machine you should look for a package named bird2 or something similar (some repositories name it slightly differently - such as just bird). On a Debian-based system you can use:

sudo apt install bird2 -y

You can confirm that the version of BIRD you installed is version 2 with the command bird -v. If that shows the correct version number then continue to the next step:

sudo systemctl enable --now bird

This will ensure that the routing daemon starts on boot.

Configuration

Due to the low complexity of the CRXN network, all configuration data is written to a file. It is also possible to split them into several files.

Full example

<OWNIP> is replaced by an IP address from its own subnet. Often xxxx::1 is used for the router. <OWNNET> is replaced by its subnet or the part you want to propagate. If you have only one node, you can enter your whole /48 network. If you have several nodes and want to give each node a smaller network (e.g. /56 or /52), enter it here. <RID> is a random IPv4 address which is used as unique identifier of the bird instance. You cannot peer with a peer that has the same router ID. <HOSTNAME> can be replaced with the hostname of the router. This can be any string.

BIRD is normally configured to use the base configuration stored at something like /etc/bird/bird.conf or /etc/bird.conf, so open that file up and add the following to it.

define OWNIP = <OWNIP>;
define OWNNET = <OWNNET>;
define OWNNETSET = [<OWNNET>+];

define RID = <RID>;
define HOSTNAME = "<HOSTNAME>";

router id RID;
hostname HOSTNAME;

ipv6 table crxn;

function is_self_net() {
  return net ~ OWNNETSET;
}

function is_valid_network() {
  return net ~ [
    fd00::/8{44,64}
  ];
}

function crxn_import_filter() {
    if (net.type != NET_IP6 || ! is_valid_network() || is_self_net()) then
        reject;

    accept;
}

function crxn_export_filter() {
    if ( ! is_valid_network() ) then
        reject;

    if (source !~ [RTS_STATIC, RTS_BABEL]) then
        reject;

    accept;
}

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table crxn;
        export all;
    };
}

protocol kernel {
    ipv6 {
        table crxn;
        import none;
        export filter {
            krt_prefsrc = OWNIP;
            accept;
        };
    };
}

protocol babel crxn_net {
    randomize router id on;

    interface "crxn_peer1" {
        type wired;
        rxcost 30;
    };

    ipv6 {
        table crxn;
        import where crxn_import_filter();
        import limit 2000 action block;
        export where crxn_export_filter();
    };
}

protocol device {}

Explanations

router id RID;
hostname HOSTNAME;

Here the router ID and hostname bird entered above is communicated.

ipv6 table crxn;

Here an IPv6 routing table with the name crxn is created. Alternatively, you can also use the default routing table master6. You do not have to create the master6 table separately.

Next comes a set of utility functions that will later help us build the filters.

function is_self_net() {
  return net ~ OWNNETSET;
}

This function returns true if the net is the own, otherwise false.

function is_valid_network() {
  return net ~ [
    fd00::/8{44,64}
  ];
}

The CRXN network uses IP addresses in the ULA range (fd80::/8). Here it is checked whether the network is in this range. This function can be used if you want to prevent clearnet addresses from being imported. Furthermore two limits are set with {44,64}: A net may have a maximum size of 64 and a minimum size of 44. The limitation of /64 is to prevent the routing table from becoming too large. For example, someone could otherwise carry out an attack by propagating very many /128 addresses. This prevents such an attack.

Next we build the import and export filters:

function crxn_import_filter() {
    if (net.type != NET_IP6 || ! is_valid_network() || is_self_net()) then
        reject;

    accept;
}

There are three creteria where we do not want to import a route: 1) It is not an IPv6 route. CRXN is an IPv6 network. This setting prevents misconfiguration. 2) The network is not in ULA range. 3) It is the own network. You yourself what best how to reach your own network. One exports it to others. However, someone can try to hjack your network. For example by exporting a smaller (and therefore more precise) prefix. There are several ways to prevent this. One is to not participate in the attack on yourself. Therefore, not to import your own network from others. If the route still exists afterwards, we import it.

function crxn_export_filter() {
    if ( ! is_valid_network() ) then
        reject;

    if (source !~ [RTS_STATIC, RTS_BABEL]) then
        reject;

    accept;
}

To prevent misconfiguration on our own router, we filter out any non-ULA network during export. Furthermore, we only export routes which we have learned statically (see below) (RTS_STATIC) or which we have learned over others via Babel (RTS_BABEL).

protocol static ownnet {
    route OWNNET unreachable;

    ipv6 {
        table crxn;
        export all;
    };
}

Next, we create a static route for our own network and export it to the routing table crxn. If you use the table master6 you can omit the table crxn; statement. We say bird that our own network is not reachable. This ensures that bird now knows a route to our network and exports it. However, we ourselves can still reach devices on our network because they have a more precise prefix and longest prefix match applies.

protocol kernel {
    ipv6 {
        table crxn;
        import none;
        export filter {
            krt_prefsrc = OWNIP;
            accept;
        };
    };
}

In routing, a distinction is made between “Control Plane” and “Forwarding Plane”. The control plane calculates the routes and passes them on to the forwarding plane. The forwarding plane then accepts the packets and forwards them accordingly. On a Linux router, bird is the control plane and the kernel is the forwarding plane, since the kernel is responsible for forwarding IP packets. With krt_prefsrc we specify our source IP address. This is the addresses defined above. This address must exist on an interface. It is generally bound to a dummy interface.

Advantage and disadvantage of persist: The keyword persist can be used to prevent bird from deleting routes from the forwarding plane. Only routes will be updated. The advantage of this is that when bird is terminated, the forwarding plane still knows the routes to the destinations. The disadvantage is that routes that are no longer propagated are retained. If the control plane no longer knows a route to a particular prefix, using persist will not delete the route from the forwarding plane. This can lead to some strange routing.

protocol babel crxn_net {
    randomize router id on;

    interface "crxn_peer1" {
        type wired;
        rxcost 30;
    };

    ipv6 {
        table crxn;
        import where crxn_import_filter();
        import limit 2000 action block;
        export where crxn_export_filter();
    };
}

Here we create the Babel protocol, which is used to communicate with other CRXN routers. The protocol consists of interface and ipv6 among others. The interface can occur several times, ipv6 not. In ipv6 our import/export filters and our routing table crxn are defined accordingly. Furthermore an import limit of 2000 routes is set. If a malicious actor now tries to crash a router, for example by propagating a lot of routes, we protect ourselves with this. If 2000 routes are imported via CRXN, every further route is blocked. Although we protect our memory with this, a high rejection of routes can lead to an increased CPU load.

With interface "crxn*"; you can mark any interface starting with crxn as a peer, but then you cannot fine-tune a peer. Therefore it makes sense to define a separate interface for each peer. As type there are wired and wireless. In general wired is always used. According to the type parameter babel is adjusted. With rxcost we define the “cost”, which a peer needs to us. Without specification this is 96. Here it is recommended to take the latency as cost. If you want to avoid that packets are routed through you, you can increase the cost. In this example, there is a connection to our peer via the interface crxn_peer1. This is a wired connection with a latency of 30ms.

randomize router id on; If a Babel peer is restarted in a short time, other peers may reject its routes. To prevent this, you can randomize the first 32 bits of your router ID at each startup, which will cause other Babel peers to think you are a “new” peer and therefore accept your routes.

protocol device {}

The protocol device is mandatory in bird. This allows bird to communicate with the router. This is necessary for bird to get information about interfaces and the like.

Control

These commands can be entered with in birdc:

To display all Babel peers you can use the following:

show babel neighbors

or

show babel n

View all CRXN routes:

show route protocol crxn

View all CRXN routes with details:

show route protocol crxn all

Display all the routes that you export:

show route export crxn

Display all the routes that you export with details:

show route export crxn all

Reload configuration:

configure

or

c

Help can be obtained by typing the command and then a ?.

Babel

Babel uses port 6696/udp and the multicast address ff02::1:6. Accordingly, port 6696/udp must be enabled locally.