diff --git a/roles/backbone/tasks/main.yml b/roles/backbone/tasks/main.yml
index 2ffb0ba50127f8afa27106b434901251f94078bf..49aa3e5502950dd6e661d2a18261b4b8d37fe992 100644
--- a/roles/backbone/tasks/main.yml
+++ b/roles/backbone/tasks/main.yml
@@ -6,9 +6,53 @@
       - wireguard-tools
     state: present
 
+- ansible.builtin.set_fact:
+    bb_bgp_local_host: "{{ bb_bgp_peers[ansible_facts['hostname']] }}"
+
+- name: Ensure wireguard private key permissions (please create private key manually if this fails)
+  ansible.builtin.file:
+    path: /etc/systemd/network/wg-private.key
+    mode: 0640
+    owner: root
+    group: systemd-network
+
+- name: Deploy loopback netdev interface
+  ansible.builtin.template:
+    src: bbbgplo.netdev.j2
+    dest: "/etc/systemd/network/bbbgplo.netdev"
+    mode: 0640
+    owner: root
+    group: systemd-network
+
+- name: Deploy loopback network interface
+  ansible.builtin.template:
+    src: bbbgplo.network.j2
+    dest: "/etc/systemd/network/bbbgplo.network"
+    mode: 0640
+    owner: root
+    group: systemd-network
+
+- name: Deploy bird base config
+  ansible.builtin.template:
+    src: bird.conf.j2
+    dest: "/etc/bird/bird.conf"
+    mode: 0640
+    owner: bird
+    group: bird
+
+- ansible.builtin.set_fact:
+    bb_bgp_local_host: ""
+
+- name: Create /etc/bird/peers
+  ansible.builtin.file:
+    path: /etc/bird/peers
+    state: directory
+    mode: 0750
+    owner: bird
+    group: bird
+
 - name: Deploy lines
   include_tasks: wireguard_line.yml
   loop: "{{ bb_bgp_peers[ansible_facts['hostname']].lines }}"
   loop_control:
     loop_var: local_line
-  
diff --git a/roles/backbone/tasks/wireguard_tunnel.yml b/roles/backbone/tasks/wireguard_tunnel.yml
index 51cda9db9d82db69468d05fbf9215cc3a9ffa5bc..95339e9ee2bc990cbfb39ed1521c9fd02541388a 100644
--- a/roles/backbone/tasks/wireguard_tunnel.yml
+++ b/roles/backbone/tasks/wireguard_tunnel.yml
@@ -13,30 +13,56 @@
     bb_bgp_remote_host: "{{ bb_bgp_peers[bb_bgp_remote_line.hostname] }}"
     bb_bgp_local_host: "{{ bb_bgp_peers[bb_bgp_local_line.hostname] }}"
 
+- ansible.builtin.set_fact:
+    # WARNING: the shellscript-based backbone generates IPv6 addresses inside
+    # the tunnel from the IPv4 offset! ip6offset is only used to generate
+    # loopback IPs.
+    # BGP session endpoints (addresses in fd21:711:ffff:ffff::) use the IPv4 offset!
+    bb_bgp_local_ip6: "fd21:711:ffff:ffff::{{ tunnel.idx }}:{{ bb_bgp_local_host.ip4offset }}"
+    bb_bgp_remote_ip6: "fd21:711:ffff:ffff::{{ tunnel.idx }}:{{ bb_bgp_remote_host.ip4offset }}"
+    bb_bgp_local_ip4: "100.100.{{ tunnel.idx }}.{{ bb_bgp_local_host.ip4offset }}"
+    bb_bgp_remote_ip4: "100.100.{{ tunnel.idx }}.{{ bb_bgp_remote_host.ip4offset }}"
+    bb_bgp_interface_name: "{{ bb_bgp_local_line_name }}{{ bb_bgp_remote_line_name }}"
+
 - ansible.builtin.debug:
-    msg: "{{ bb_bgp_local_line }}"
+    msg: "{{ bb_bgp_local_host }}"
 - ansible.builtin.debug:
     msg: "{{ bb_bgp_remote_line }}"
 
 - name: Deploy netdev file for wireguard tunnel
   ansible.builtin.template:
     src: wg-tunnel.netdev.j2
-    dest: "/etc/systemd/network/{{ bb_bgp_remote_line_name }}.netdev"
+    dest: "/etc/systemd/network/{{ bb_bgp_interface_name }}.netdev"
     mode: 0750
     owner: root
     group: systemd-network
   vars:
-    local_port: "{{ 12000 + (tunnel.idx|int - 1) * 1000 + bb_bgp_remote_host.ip4offset|int }}"
-    remote_port: "{{ 12000 + (tunnel.idx|int - 1) * 1000 + bb_bgp_local_host.ip4offset|int }}"
+    local_port: "{{ 12000 + tunnel.idx|int * 1000 + bb_bgp_remote_host.ip4offset|int }}"
+    remote_port: "{{ 12000 + tunnel.idx|int * 1000 + bb_bgp_local_host.ip4offset|int }}"
+
+- name: Ensure netdev file permissions
+  ansible.builtin.file:
+    path: "/etc/systemd/network/{{ bb_bgp_interface_name }}.netdev"
+    mode: 0640
+    owner: root
+    group: systemd-network
 
 - name: Deploy network file for wireguard tunnel
   ansible.builtin.template:
     src: wg-tunnel.network.j2
-    dest: "/etc/systemd/network/{{ bb_bgp_remote_line_name }}.network"
-    mode: 0750
+    dest: "/etc/systemd/network/{{ bb_bgp_interface_name }}.network"
+    mode: 0640
     owner: root
     group: systemd-network
 
+- name: Deploy bird peer config file for tunnel
+  ansible.builtin.template:
+    src: bird-peer.conf.j2
+    dest: "/etc/bird/peers/{{ bb_bgp_interface_name }}.network"
+    mode: 0640
+    owner: bird
+    group: bird
+
 - ansible.builtin.set_fact:
     bb_bgp_remote_line_name: ""
     bb_bgp_local_line_name: ""
@@ -44,3 +70,8 @@
     bb_bgp_local_line: ""
     bb_bgp_remote_host: ""
     bb_bgp_local_host: ""
+    bb_bgp_local_ip6: ""
+    bb_bgp_remote_ip6: ""
+    bb_bgp_local_ip4: ""
+    bb_bgp_remote_ip4: ""
+    bb_bgp_interface_name: ""
diff --git a/roles/backbone/templates/bbbgplo.netdev.j2 b/roles/backbone/templates/bbbgplo.netdev.j2
new file mode 100644
index 0000000000000000000000000000000000000000..492364ea2ec20fbae349c494cd3d167125172560
--- /dev/null
+++ b/roles/backbone/templates/bbbgplo.netdev.j2
@@ -0,0 +1,4 @@
+[NetDev]
+Name=bbbgplo
+Kind=dummy
+
diff --git a/roles/backbone/templates/bbbgplo.network.j2 b/roles/backbone/templates/bbbgplo.network.j2
new file mode 100644
index 0000000000000000000000000000000000000000..b90720c083dd7b0c21fd8b37169001ff10669e5e
--- /dev/null
+++ b/roles/backbone/templates/bbbgplo.network.j2
@@ -0,0 +1,19 @@
+[Match]
+Name=bbbgplo
+
+[Network]
+DHCP=no
+
+[Address]
+Address=fd21:b4dc::a38:{{ bb_bgp_local_host.ip6offset  }}/128
+
+[Address]
+Address=10.191.255.{{ bb_bgp_local_host.ip4offset  }}/32
+
+[Route]
+Destination=fd21:b4dc::a38:{{ bb_bgp_local_host.ip6offset  }}/128
+Type=local
+
+[Route]
+Destination=10.191.255.{{ bb_bgp_local_host.ip4offset  }}/32
+Type=local
diff --git a/roles/backbone/templates/bird-peer.conf.j2 b/roles/backbone/templates/bird-peer.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..9253bea44be8e3da8999a3c5b6886b47ddb2c3c5
--- /dev/null
+++ b/roles/backbone/templates/bird-peer.conf.j2
@@ -0,0 +1,23 @@
+protocol bgp {{ bb_bgp_interface_name }}_v6 {
+	local as BB_BGP_ASN;
+	neighbor {{ bb_bgp_remote_ip6 }} as {{ bb_bgp_remote_host.asn }};
+	interface "{{ bb_bgp_interface_name }}";
+	bfd on;
+	ipv6 {
+		import filter ffs_backbone_bgp_import;
+		export filter ffs_backbone_bgp_export;
+	};
+}
+
+protocol bgp {{ bb_bgp_interface_name }}_v4 {
+	local as BB_BGP_ASN;
+	neighbor {{ bb_bgp_remote_ip4 }} as {{ bb_bgp_remote_host.asn }};
+	interface "{{ bb_bgp_interface_name }}";
+	bfd on;
+	ipv4 {
+		import filter ffs_backbone_bgp_import;
+		export filter ffs_backbone_bgp_export;
+	};
+}
+
+
diff --git a/roles/backbone/templates/bird.conf.j2 b/roles/backbone/templates/bird.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..efdc6efbe3d607ff9510f666d0c94cd1ebd2aa95
--- /dev/null
+++ b/roles/backbone/templates/bird.conf.j2
@@ -0,0 +1,57 @@
+log syslog all;
+#debug protocols all;
+#debug channels all;
+                                               
+define BB_BGP_ASN = {{ bb_bgp_local_host.asn }};
+
+router id {{ bb_bgp_local_host.routerid }};
+
+protocol device {
+};
+
+protocol static static_myself_v6 {
+	ipv6;
+	route fd21:b4dc::a38:{{ bb_bgp_local_host.ip6offset  }}/128 via "lo";
+};
+
+protocol static static_myself_v4 {
+	ipv4;
+	route 10.191.255.{{ bb_bgp_local_host.ip4offset  }}/32 via "lo";
+};
+
+function is_default_route() {
+	if net.type = NET_IP4 && net ~ [ 0.0.0.0/0 ] then return true;
+	if net.type = NET_IP6 && net ~ [ ::/0 ] then return true;
+	return false;
+};
+
+filter nodefaultroute {
+	if is_default_route() then reject;
+	accept;
+};
+
+protocol kernel t_kernel {
+        ipv6 {
+                import none;
+                export filter nodefaultroute;
+        };
+};
+
+function is_ffs_net() {
+	if net.type = NET_IP4 && net ~ [ 10.190.0.0/15+ ] then return true;
+	if net.type = NET_IP6 && net ~ [ fd21:b4dc:4b00::/40 ] then return true;
+	return false;
+};
+
+filter ffs_backbone_bgp_import {
+	if is_ffs_net() then accept;
+	else reject;
+};
+
+filter ffs_backbone_bgp_export {
+	if is_ffs_net() then accept;
+	else reject;
+};
+
+include "/etc/bird/peers/*";
+
diff --git a/roles/backbone/templates/wg-tunnel.netdev.j2 b/roles/backbone/templates/wg-tunnel.netdev.j2
index edcccb9e27069aa17ea56c1aea8c930aae586b12..81d10361e681a4c08b97acb7472c77a59a71f9e2 100644
--- a/roles/backbone/templates/wg-tunnel.netdev.j2
+++ b/roles/backbone/templates/wg-tunnel.netdev.j2
@@ -1,11 +1,12 @@
 [NetDev]
-Name={{ bb_bgp_remote_line_name }}
+Name={{ bb_bgp_interface_name }}
 Kind=wireguard
 MTUBytes=1280
 
 [WireGuard]
 ListenPort={{ local_port }}
-PrivateKeyFile=/etc/wireguard/wg-private.key
+PrivateKeyFile=/etc/systemd/network/wg-private.key
+RouteTable=off
 
 [WireGuardPeer]
 PublicKey={{ bb_bgp_remote_host.wg_pubkey }}
diff --git a/roles/backbone/templates/wg-tunnel.network.j2 b/roles/backbone/templates/wg-tunnel.network.j2
new file mode 100644
index 0000000000000000000000000000000000000000..0a3abb1e1a11cafbbcd1f5852df4b0e2292808e8
--- /dev/null
+++ b/roles/backbone/templates/wg-tunnel.network.j2
@@ -0,0 +1,15 @@
+[Match]
+Name={{ bb_bgp_interface_name }}
+
+[Network]
+DHCP=no
+IPv6AcceptRA=false
+IPForward=yes
+
+[Address]
+Address={{ bb_bgp_local_ip4 }}/32
+Peer={{ bb_bgp_remote_ip4 }}/32
+
+[Address]
+Address={{ bb_bgp_local_ip6 }}/128
+Peer={{ bb_bgp_remote_ip6 }}/128
diff --git a/roles/backbone/vars_plugins/backbone_csv_vars.py b/roles/backbone/vars_plugins/backbone_csv_vars.py
index 201b38b8e2bb36ee1a61dc547874338fb89429ba..c90a5db97759ebb0a74fe50ab0a433cb4b906fa6 100644
--- a/roles/backbone/vars_plugins/backbone_csv_vars.py
+++ b/roles/backbone/vars_plugins/backbone_csv_vars.py
@@ -143,7 +143,7 @@ class VarsModule(BaseVarsPlugin):
                 try:
                     tunnels_next_idx[tunnel_key] += 1
                 except KeyError:
-                    tunnels_next_idx[tunnel_key] = 1
+                    tunnels_next_idx[tunnel_key] = 0
                 
                 tunnel = Tunnel(line_a=line_a,
                                 line_b=line_b,