Interaction of OSPFv2 and PtP tunnels (bug?)

LEdoian bird at pokemon.ledoian.cz
Tue May 27 03:43:59 CEST 2025


Hello!

I seem to have hit a strange inconsistency when OSPFv2 does not create routes for its PtP transit interfaces and am not sure whether I am misunderstanding the protocol. I managed to reduce my case into the following setup (full Linux netns reproducer script is at the end):

There are three machines, called Local, Remote and Foreign and OSPFv2 running among them. Local is the middle one, having a virtual ethernet to Foreign (IP addresses 10.2.0.0/24) and a 4in6 tunnel to Remote (10.1.0.1 and 10.1.0.2). Moreover, the Local and Remote machines have a dummy interface with another local address (10.0.0.1, 10.0.0.2).

In this case, OSPF converges, but the routing tables on Local and Remote are missing the route for 10.1.0.0/30:

```
Table master4:
10.2.0.0/24          unicast [ospf1 03:24:08.362] * I (10/100) [0.0.0.3]
        dev en_f
10.0.0.1/32          unicast [ospf1 03:23:27.361] * I (10/0) [0.0.0.1]
        dev dummy0
10.0.0.2/32          unicast [ospf1 03:23:43.363] * I (10/100) [0.0.0.2]
        via 10.1.0.2 on tun0
```

OSPF is about the 10.1.0.0/30 addresses, breaking connection between Foreign and Remote.

Surprisingly, if the tunnel interface is marked as stub, then the route is imported in the same manner as the route on the veth (but the toy network now has two independent instances of OSPF, effectively being split).

The missing route can be added by enabling the Direct protocols but this feels quite off, as the route should not be external to OSPF. Also, the Direct protocol documentation suggests that this should not be necessary: “OSPF protocol creates device routes for its interfaces itself […]”.

My reproducer runs on Arch Linux with BIRD 3.1.1, but also appears on my router running BIRD 2.13.1.

Am I misunderstanding something, or is this actually a bug?

Best regards,
LEdoian

---

#!/bin/bash
# intended to be run inside `unshare -rnmpf --mount-proc`
# uses tmux to keep sessions and to run BIRDs in background

set -e

dir="$(mktemp -d)"
mount -t tmpfs bird-repro "$dir"
trap 'cd / && umount --recursive "$dir" && rmdir "$dir"' EXIT # does not work
cd "$dir"

cat >bird.inc <<GO
log stderr all;
debug protocols all;
protocol device {
}
protocol direct{
	ipv4;
	disabled;
}
protocol kernel ker4 {
	learn;
	ipv4 {
		preference 100;
		import all;
		export all;
	};
}
protocol ospf v2 {
	ipv4 {
		preference 10;
		import all;
		export all;
	};
	area 0 {
		interface "tun*" {
			cost 100;
			#stub;
		};
		interface "dummy*" {
			cost 100;
			stub;
		};
		interface "en*" {
			cost 100;
		};
	};
}
GO

cat >local.conf <<GO
router id 0.0.0.1;
include "bird.inc";
GO

cat >remote.conf <<GO
router id 0.0.0.2;
include "bird.inc";
GO

cat >foreign.conf <<GO
router id 0.0.0.3;
include "bird.inc";
GO

for x in remote local; do
	touch $x
	unshare --net=$x ip link add dummy0 type dummy
	nsenter --net=$x ip link set dummy0 up
done
nsenter --net=local ip addr add 10.0.0.1/32 dev dummy0
nsenter --net=remote ip addr add 10.0.0.2/32 dev dummy0

ip link add name ve0 type veth peer ve1
ip link set ve0 netns ./local
ip link set ve1 name ve0
ip link set ve0 netns ./remote
nsenter --net=local ip addr add fd00::1/64 dev ve0
nsenter --net=remote ip addr add fd00::2/64 dev ve0
nsenter --net=local ip link set ve0 up
nsenter --net=remote ip link set ve0 up

nsenter --net=local ip link add tun0 type ip6tnl mode ipip6 local fd00::1 remote fd00::2
nsenter --net=local ip link set tun0 up
nsenter --net=local ip addr add 10.1.0.1 dev tun0 peer 10.1.0.2

nsenter --net=remote ip link add tun0 type ip6tnl mode ipip6 local fd00::2 remote fd00::1
nsenter --net=remote ip link set tun0 up
nsenter --net=remote ip addr add 10.1.0.2 dev tun0 peer 10.1.0.1

touch foreign
unshare --net=foreign true
ip link add name en_f type veth peer en_l
ip link set en_f netns ./local
ip link set en_l netns ./foreign
nsenter --net=foreign ip link set en_l up
nsenter --net=foreign ip addr add 10.2.0.3/24 dev en_l
nsenter --net=local ip link set en_f up
nsenter --net=local ip addr add 10.2.0.2/24 dev en_f

nsenter --net=local sysctl net.ipv4.conf.all.forwarding=1

for x in local remote foreign; do
	nsenter --net=$x tmux -S $x.tmux new-session -d bird -d -c $x.conf -s $x.bird
done

tmux


More information about the Bird-users mailing list