running OSPFv2/OSPFv3 over OpenVPN

 

Let's assume the following network topology and ip setup.

Topology

network topology

  • Aldebaran
    • public interface eth0: 10.10.1.1/24
    • local network on eth1: 192.168.1.1/24, fd01:c0:a801::1/64
    • vpn sagittarius (point-to-point) on tun0: 172.16.1.1
    • vpn aquarius (point-to-point) on tun1: 172.16.2.1
    • vpn draco on tun2: 172.16.99.1/24
  • Beteigeuze
    • public interface eth0: 10.20.2.2/24
    • local network on eth1: 192.168.2.1/24, fd01:c0:a802::1/64
    • vpn sagittarius (point-to-point) on tun0: 172.16.1.2
    • vpn columba (point-to-point) on tun1: 172.16.3.1
  • Castor
    • public interface eth0: 10.30.3.3/24
    • local network on eth1: 192.168.3.1/24, fd01:c0:a803::1/64
    • vpn aquarius (point-to-point) on tun0: 172.16.2.2
    • vpn columba (point-to-point) on tun1: 172.16.3.2

Convention

  • ospf/ospfv3 link state must not be advertised over public interfaces (eth0)
  • roadwarriors (draco) usually don't speak ospf/ospv3, keep that interface passive
  • do not assign areas to public interfaces (eth0)
  • Aldebaran is vpn-server for sagittarius, aquarius and draco using ports 11961, 11962 and 1196, respective
  • Beteigeuze is vpn-server for columba using port 11961
  • there is no need for ipv6 transfer networks for ospfv3, link local addresses are being used

Setup OpenVPN

We need to set up openvpn on all servers. I'll use a very basic example with preshared secrets to show the concept. Please use additional security measures in your production, like client certificates, user authentication, strong ciphers, etc.pp.

First, generate some shared keys:

for s in sagittarius aquarius columba draco; do
  openvpn --genkey --secret ${s}.key
done

and push them to the corresponding servers. Then create openvpn's configuration files.

Now start up the daemons

systemctl start openvpn-server@*
systemctl start openvpn-client@*

and wait for them connecting to each other:

ip a show dev tun0
ip a show dev tun1
...

Setup frr

tl;dr: get the configs here:

start the daemons

for service in zebra ospfd ospf6d; do
  systemctl enable --now ${service}
done

basic network setup

conf t
int eth0
 desc public
!
int eth1
 desc local network
!
int tun0
 desc vpn name (e.g. sagittarius)
!
int tun1
 desc ...
! ...
ip forwarding
ipv6 forwarding

ospf

conf t
router ospf
 ! let's use router-id 1 for aldebaran, 2 for beteigeuze, 3 for castor
 router-id 0.0.0.1
 ! do not announce ospf here
 passive-interface eth0
 passive-interface eth1
 ! on aldebaran, do not announce ospf on tun2
 passive-interface tun2
!
int eth1
 ip ospf area 2
!
int tun0
 ip ospf area 0
!
int tun1
 ip ospf area 0
!
! on aldebaran, add tun2 to area 1
int tun2
 ip ospf area 1
Testing
aldebaran# show ip ospf neighbor 

Neighbor ID     Pri State           Dead Time Address         Interface            RXmtL RqstL DBsmL
0.0.0.3           1 Full/DROther      35.849s 172.16.2.2      tun1:172.16.2.1          0     0     0
0.0.0.2           1 Full/DROther      34.149s 172.16.1.2      tun0:172.16.1.1          0     0     0

aldebaran# show ip route ospf
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR, f - OpenFabric,
       > - selected route, * - FIB route

O   172.16.99.0/24 [110/10000] is directly connected, tun2, 00:03:01
O   192.168.1.0/24 [110/10] is directly connected, eth1, 00:03:06
O>* 192.168.2.0/24 [110/10010] via 172.16.1.2, tun0 onlink, 00:02:53
O>* 192.168.3.0/24 [110/10010] via 172.16.2.2, tun1 onlink, 00:02:53

ospfv3

conf t
router ospf6
 ! let's use router-id 1 for aldebaran, 2 for beteigeuze, 3 for castor
 ospf router-id 0.0.0.1
 interface tun0 area 0.0.0.0
 interface tun1 area 0.0.0.0
 interface eth1 area 0.0.0.2
 ! on aldebaran, add tun2 to area 0.0.0.1
 interface tun2 area 0.0.0.1
!
int eth0
 ! do not announce ospfv3 here
 ipv6 ospf6 passive
!
int eth1
 ! do not announce ospfv3 here
 ipv6 ospf6 passive
!
! on aldebaran, do not announce ospfv3 on tun2
int tun2
 ipv6 ospf6 passive
!
int tun0
 ipv6 ospf6 network point-to-point
!
int tun1
 ipv6 ospf6 network point-to-point
Testing
aldebaran# show ipv6 ospf6 neighbor 
Neighbor ID     Pri    DeadTime    State/IfState         Duration I/F[State]
0.0.0.2           1    00:00:31     Full/PointToPoint    00:39:30 tun0[PointToPoint]
0.0.0.3           1    00:00:32     Full/PointToPoint    00:39:30 tun1[PointToPoint]

aldebaran# show ipv6 route ospf6
Codes: K - kernel route, C - connected, S - static, R - RIPng,
       O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table,
       v - VNC, V - VNC-Direct, A - Babel, D - SHARP, F - PBR,
       f - OpenFabric,
       > - selected route, * - FIB route

O   fd01:c0:a801::/64 [110/10] is directly connected, eth1, 00:03:39
O>* fd01:c0:a802::/64 [110/10010] via fe80::9008:17d:4a6b:41b3, tun0, 00:03:27
O>* fd01:c0:a803::/64 [110/10010] via fe80::37d2:ebfd:821f:2a5, tun1, 00:03:00

non-distributed routes/addresses

In this simple setup the point-to-point addresses are not redistributed via ospf. For example, castor will not know about sagittarius' addresses. That might lead to irritation when communicating with the interface's address:

communication path

To circumvent this, try one of the following:

use an area > 0 source address

This could be for example the local network address (192.168.1.1 on aldebaran) or a loopback address:

conf t
interface lo
 ip address 192.168.255.1/32
 ip ospf area 127

usage:

ping -I 192.168.255.1 192.168.2.1

configure a static interface route

you'll need staticd for that:

systemctl enable --now staticd
! for aldebaran
ip route 172.16.1.0/30 tun0
ip route 172.16.2.0/30 tun1
!
router ospf
 redistribute static
!
! or, if you have static routes your don't want to redistribute
!
! route-map redistribute-area-0 permit 1
!  match interface tun0
! !
! route-map redistribute-area-0 permit 2
!  match interface tun1
! !
! router ospf
!  redistribute static route-map redistribute-area-0

redistribute connected routes

Keep in mind not all connected routes shall be advertised (i.e. hide the public network). Therefore we'll use a route map.

route-map redistribute-area-0 permit 1
 match interface tun0
!
route-map redistribute-area-0 permit 2
 match interface tun1
!
router ospf
 redistribute connected route-map redistribute-area-0

rerouting in case of link failure

The routes to the networks will change in a short couple of time depending of timeout and failover settings.

Testing ospf

  • upper left: show ip route ospf on aldebaran (watch the interfaces)
  • lower left: show ip route ospf on beteigeuze (watch the interfaces)
  • upper right: stopping and starting sagittarius
  • lower right: continuously pinging from aldebaran to beteigeuze

Testing ospfv3

  • upper left: show ipv6 route ospf6 on aldebaran (watch the interfaces)
  • lower left: show ipv6 route ospf6 on beteigeuze (watch the interfaces)
  • upper right: stopping and starting sagittarius
  • lower right: continuously pinging from aldebaran to beteigeuze

try it out

With this bash script you'll create an LXC environment of this article's setup. Tested on Fedora 30. Prerequisites:

  • rpms lxc, lxc-templates, bridge-utils and busybox are installed
  • bash >= 4.3
  • root access (required for the network setup)