With version 2.14 bird have enabled mpls and the exchange of labels.

This now gives a possibility to create a real mpls network. I’ll give a short intro to what I have done and expirenced lately.

First of all I have 8 vps’ running. Mostly in Europe and a single one in the US. I’m using debian bookworm with bird 2.14 downloaded and install from the sid repo.

I have created the following:

Image

All connections between nodes are based on GRE tunnels.

I have created two VRFs on all nodes.

Name Table id Used for
arendtsen 1978 My own that is dedicated to my IPv6 PI range in AS198660
dn42 1042 The network in which I connect to DN42

I have altered the use of /etc/network/interfaces to include /etc/network/interfaces.d/*.init. Then I have created the following files:

Lang:
50-vrfs.init
60-loopbacks.init
70-mpls.init

Those files are the base of the configuration for interconnecting the MPLS network.
You can see the content of the files under the config section.

The next part is the bird configuration.

I have created a structure where the main bird.conf file includes *.conf files from the tree structure. The interesting files here are:

Lang:
bird.conf
tables/mpls.conf
igp/mpls-ospf.conf
bgp/templates.conf
vrf/vrf-arendtsen.conf

The initial setup is to setup the tables.

Lang:
mpls domain mdom;
mpls table t_mpls;

ipv4 table t_mpls_v4;
ipv6 table t_mpls_v6;

vpn4 table t_vpn_v4;
vpn6 table t_vpn_v6;

Those are needed to enable the MPLS.

Next add the following for syncing labels with the kernel:

Lang:
protocol kernel k_mpls {
  mpls {
    table t_mpls;
    export all;
  };
}

This is the very basic setup. You now have enabled MPLS on your local router/server.

The next part is to get bird to communicate with other routers configured just like that.
I am using OSPF as the IGP. My config is here: OSPF config

Once the OSPF is running I create a iBGP session between the routers. But it is from dummy.mpls to dummy.mpls.
Now the BGP session is very interesting as this is where we will exchange routes in the vrf, also known as vpn routes, and labels.

My templates are here: BGP Templates.
The interesting part is this:

Lang:
  ipv4 mpls {
    table t_mpls_v4;
    import all;
    export all;
  };

  ipv6 mpls {
    table t_mpls_v6;
    import all;
    export all;
    extended next hop;
  };

  vpn4 mpls {
    table t_vpn_v4;
    import all;
    export all;
  };

  vpn6 mpls {
    table t_vpn_v6;
    import all;
    export all;
    extended next hop;
  };

  mpls {
    domain mdom;
    table t_mpls;
    label policy aggregate;
    import all;
    export all;
  };
}

This is a multiprotocol peering meaning we will exchange IPv4 and IPv6 MPLS routes. Also vpn4 and vpn6 MPLS routes.
Last we exchange the MPLS labels.
You will notice that I haven choosen the label policy aggregate.
That is choosen in order to limit the number of labels to have in the table.

My peerings are then created by refering to the templates.
Just as you would be used to.

The L3VPN part which is the most interresting here as that is what we want.
My almost static configuration can be found here: vrf/vrf-arendtsen.conf.
The file have directives for intering direct interfaces. Exchaning routes with the kernel and using the correct table it.
The part here is where the magic happens:

Lang:
protocol l3vpn vrf_arendtsen {
  vrf "arendtsen";
  ipv4 { table t_vrf_arendtsen_v4; };
  ipv6 { table t_vrf_arendtsen_v6; };
  vpn4 { table t_vpn_v4; };
  vpn6 { table t_vpn_v6; };
  mpls {
    label policy vrf;
    import all;
    export all;
  };

  rd 198660:1978;
  import target [(rt, 198660, 1978)];
  export target [(rt, 198660, 1978)];

}

Basiclly it tells bird to use the MPLS function.
The import and export target can be used for route leaking if one have those desires.

Now the result on my r02.nl.dn42.dk router is like this:

Lang:
root@r02.nl.dn42.dk:~# ip -f mpls r s
1000 as to 1000 via inet 10.0.254.11 dev tun-mpls-r03-nl proto bird
1001 as to 1009 via inet 10.0.254.11 dev tun-mpls-r03-nl proto bird
1002 as to 1016 via inet 10.0.254.0 dev tun-mpls-r02-de proto bird
1003 as to 1001 via inet 10.0.254.9 dev tun-mpls-r01-de proto bird
1004 as to 1005 via inet 10.0.254.17 dev tun-mpls-r03-de proto bird
...
1018 dev arendtsen proto bird
1019 dev dn42 proto bird
...
1020 as to 1004 via inet 10.0.254.17 dev tun-mpls-r03-de proto bird
1021 as to 1000 via inet6 fe80::74cb:6003 dev tun-mpls-r01-de proto bird
1022 as to 1001 via inet6 fe80::200:5efe:d18d:21c8 dev tun-mpls-r01-lv proto bird
...

This is all the labels that are installed in the kernel.

Lang:
root@r02.nl.dn42.dk:~# ip -br -6 r s vrf arendtsen
2001:67c:25b4::1 dev dummy.arendtsen proto bird metric 32 pref medium
2001:67c:25b4::1 dev dummy.arendtsen proto kernel metric 256 pref medium
2001:67c:25b4::2  encap mpls  1000 via fe80::74cb:6003 dev tun-mpls-r01-de proto bird metric 32 pref medium
2001:67c:25b4::4  encap mpls  1000 via fe80::200:5efe:d18d:21c8 dev tun-mpls-r01-lv proto bird metric 32 pref medium
2001:67c:25b4::5  encap mpls  1000 via fe80::5be4:3446 dev tun-mpls-r02-de proto bird metric 32 pref medium
...
2001:67c:25b4:ff08::/64  encap mpls  1000 via fe80::74cb:6003 dev tun-mpls-r01-de proto bird metric 32 pref medium
2001:67c:25b4:ff0b::/64  encap mpls  1048 via fe80::2d86:59a8 dev tun-mpls-r01-no proto bird metric 32 pref medium
...
default via 2a0c:9a40:1072::1 dev ens18 proto bird metric 32 onlink pref medium

You can see here in the route table for the vrf arendtsen that it referes to MPLS labels.

Lang:
root@r02.nl.dn42.dk:~# birdc show route table t_mpls
BIRD 2.14 ready.
Table t_mpls:
1012                 unicast [mpls_r03_nl 2023-12-11] * (0/10) []
  via fe80::2d8b:a325 on tun-mpls-r03-nl mpls 1009
1015                 unicast [mpls_r02_de 2023-12-12] * (0/10) []
  via fe80::200:5efe:d18d:21c8 on tun-mpls-r01-lv mpls 1000
1018                 unicast [vrf_arendtsen 2023-12-06] * (0/0)
  dev arendtsen
1021                 unicast [mpls_r02_de 2023-12-11] * (0/10) []
  via fe80::74cb:6003 on tun-mpls-r01-de mpls 1000
1024                 unicast [mpls_r01_lv 2023-12-12] * (0/10) []
  via 10.0.254.19 on tun-mpls-r01-lv mpls 1001
1027                 unicast [mpls_r02_de 2023-12-11] * (0/10) []
  via fe80::2d8b:a325 on tun-mpls-r03-nl mpls 1000
1030                 unicast [mpls_r01_de 2023-12-06] * (0/10) []
  via fe80::ff04:2 on wg-mpls-r01-de mpls 1001
1033                 unicast [mpls_r02_de 2023-12-11] * (0/10) []
  via fe80::5be4:3446 on tun-mpls-r02-de mpls 1001
via fe80::b90a:117c on tun-mpls-r01-fr mpls 1045
...

This is the bird table for holding the MPLS labels.

Lang:
root@r02.nl.dn42.dk:~# birdc show route table t_vpn_v6
BIRD 2.14 ready.
Table t_vpn_v6:
198660:1978 2001:67c:25b4::2/128 mpls 1029 unicast [mpls_r01_de 2023-12-06 from fd45:3282:58e7:1::2] * (100/10) [i]
  via fe80::74cb:6003 on tun-mpls-r01-de mpls 1000
198660:1978 2001:67c:25b4::2/128 mpls 1021 unicast [mpls_r02_de 2023-12-11 from fd45:3282:58e7::2] (100/10) [i]
  via fe80::74cb:6003 on tun-mpls-r01-de mpls 1000
198660:1978 2001:67c:25b4::6/128 mpls 1025 unicast [mpls_r01_fr 2023-12-12 from fd45:3282:58e7:1::6] * (100/10) [i]
  via fe80::b90a:117c on tun-mpls-r01-fr mpls 1044
198660:1978 2001:67c:25b4::6/128 mpls 1039 unicast [mpls_r02_de 2023-12-12 from fd45:3282:58e7::2] (100/10) [i]
  via fe80::b90a:117c on tun-mpls-r01-fr mpls 1044
198660:1978 2001:67c:25b4:ff06::/64 mpls 1036 unicast [mpls_r01_lv 2023-12-12 from fd45:3282:58e7:1::5] * (100/10) [i]
  via fe80::200:5efe:d18d:21c8 on tun-mpls-r01-lv mpls 1000
198660:1978 2001:67c:25b4:ff06::/64 mpls 1015 unicast [mpls_r02_de 2023-12-13 from fd45:3282:58e7::2] (100/10) [i]
  via fe80::200:5efe:d18d:21c8 on tun-mpls-r01-lv mpls 1000
198660:1978 2001:67c:25b4::a/128 mpls 1011 unicast [mpls_r03_nl 2023-12-11 from fd45:3282:58e7:1::4] * (100/10) [i]
  via fe80::2d8b:a325 on tun-mpls-r03-nl mpls 1000
198660:1978 2001:67c:25b4::a/128 mpls 1027 unicast [mpls_r02_de 2023-12-11 from fd45:3282:58e7::2] (100/10) [i]
  via fe80::2d8b:a325 on tun-mpls-r03-nl mpls 1000
198660:1978 2001:67c:25b4:ff0f::/64 mpls 1018 unicast [vrf_arendtsen 2023-12-06] * (120/0)
  dev arendtsen
198660:1978 2001:67c:25b4::b/128 mpls 1031 unicast [mpls_r03_de 2023-12-11 from fd45:3282:58e7:1::3] * (100/10) [i]
  via fe80::b9d5:af4d on tun-mpls-r03-de mpls 1004
198660:1978 2001:67c:25b4::b/128 mpls 1008 unicast [mpls_r02_de 2023-12-11 from fd45:3282:58e7::2] (100/10) [i]
  via fe80::b9d5:af4d on tun-mpls-r03-de mpls 1004
...

You can see that the route disginuser is the first of the entry in the route table.

UPDATE:

A packet capture between two routers. Here it is between r02.nl.dn42.dk and r01.de.dn42.dk:

Lang:
17:43:03.273519 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2600:1f16:668:2002:b4f4:4dc9:d384:d763 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 3010, length 16
17:43:03.311901 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2600:1f16:668:2001:c918:12d:7460:7a80 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 3010, length 16
17:43:03.334508 MPLS (label 1000, tc 0, [S], ttl 239) IP6 2600:1f16:668:2000:7ed:4be:e88d:fe5e > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 3010, length 16
17:43:03.766577 MPLS (label 1000, tc 0, [S], ttl 239) IP6 2600:1f18:5b75:b804:2329:4745:282b:63 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 11, seq 829, length 16
17:43:05.165291 MPLS (label 1000, tc 0, [S], ttl 235) IP6 2a05:d01c:68f:ef01:9509:6ebf:a02a:70b3 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 1, seq 14752, length 16
17:43:05.175748 MPLS (label 1000, tc 0, [S], ttl 234) IP6 2a05:d01c:68f:ef02:1831:489e:a966:762d > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 1, seq 14752, length 16
17:43:05.304499 MPLS (label 1000, tc 0, [S], ttl 235) IP6 2a05:d01c:68f:ef00:2fa:6681:d851:a638 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 1, seq 14752, length 16
17:43:05.987560 MPLS (label 1000, tc 0, [S], ttl 239) IP6 2a05:d018:1231:b001:f79b:c455:c711:c437 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 8, seq 23959, length 16
17:43:06.015505 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2a05:d018:1231:b002:2abb:cb4c:d5b8:7bfe > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 8, seq 23959, length 16
17:43:06.038135 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2a05:d018:1231:b000:1ef9:dc3:4d9e:8b2c > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 8, seq 23959, length 16
17:43:06.336348 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2a05:d018:bb5:3300:ff10:a603:f925:9f52 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 21586, length 16
17:43:06.447062 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2a05:d018:bb5:3302:451f:57c1:5a1:9f9c > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 21586, length 16
17:43:06.468186 MPLS (label 1000, tc 0, [S], ttl 234) IP6 2600:1f18:3120:f500:faba:6e5f:c046:9d29 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 13, seq 22531, length 16
17:43:06.508831 MPLS (label 1000, tc 0, [S], ttl 242) IP6 2a05:d018:bb5:3301:8391:eac2:4ef3:f020 > 2001:67c:25b4:11f1::80: ICMP6, echo request, id 5, seq 21586, length 16
17:43:06.535529 IP6 fd45:3282:58e7::1.54133 > fd45:3282:58e7:1::2.179: Flags [P.], seq 1956915523:1956915542, ack 2133487910, win 1020, options [nop,nop,TS val 4117935837 ecr 1833371219], length 19: BGP

There we have the MPLS packet with a label sent over the GRE tunnel.

Also to mention there is a need for a few kernel modules. Thos modules are:

Lang:
mpls_iptunnel
mpls_router
ip_tunnel

I have added those to /etc/modprobe.d/mpls

Also sysctl needs to have these values modified:

Lang:
net.mpls.platform_labels=65535
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.rp_filter=0
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

These are added to the file /etc/sysctl.d/100-mpls.conf