Setting up ospf on OpenBSD

Posted on Dec 27, 2023
tl;dr: How to use ospf to distribute routes to remotely (ipsec) connected nodes

OpenBSD & ospfd

So, in a previous article we looked at setting up IPSec ikev2 tunnels between some nodes, all conncted via sec interfaces.

Now that we have connectivity, the next step is to distribute some routes. Let’s revisit our network diagram, with some IP ranges shown:

           ┌──────────────┐         ┌──────────────┐
           │              │         │              │
10.0.0.0/24│ reachout     │         │ outreach     │ 10.0.1.0/24
           └──────▲───────┘         └──────▲───────┘
         sec0     │100.64.0.2       sec0   │ 100.64.1.2
                  │                        │
                  │                        │
                  │                        │
                  │                        │
                  │                        │
                  │100.64.0.1              │100.64.1.1
             sec0 │   ┌───────────────┐    │ sec1
                  └───┤               ├────┘
                      │    fw0        │
            ┌────────x►──▲─────────▲─x◄───────────┐
            │            │         │              │
            │            │         │              │
  ┌─────────┴──┐ ┌───────┴────┐ ┌──┴─────────┐ ┌──┴─────────┐
  │vlan100     │ │vlan200     │ │vlan210     │ │vlan220     │
  │172.16.100  │ │172.16.200  │ │172.16.210  │ │172.16.220  │
  └────────────┘ └────────────┘ └────────────┘ └────────────┘

We could simply edit the route tables by hand to create connectivity between all of our hosts, but that soon starts to get messy and complicated - everytime you spring up a new network, you will have to run round and change the configuration on each of the boxes. Assuming you wanted to do that, the easiest way would be to add another line to our hostname.if files, like this:

inet 192.168.168.2/30
inet6 fe80::aaaa
group trusted
up
!route add 10.0.0.0/23 192.168.168.1

ospfd

OpenBSD comes with an OSPF daemon out of the box, and it’s very simple to configure - we just need to run it on our sec interfaces we’ve set up, but it’s not tied to those types of interface - it will work on wg tunnels for example, as well as physical interfaces etc.

Let’s start with the config file - there’s an example one in /etc/examples. We’ll unpick it a bit at a time.

# $OpenBSD: ospfd.conf,v 1.2 2018/08/07 07:06:20 claudio Exp $

# macros
id="100.64.0.1"

# global configuration
router-id $id
#redistribute connected
redistribute default
redistribute 172.23.132.224/28
redistribute 172.20.53.104/32
redistribute 172.22.119.1/32

The first thing to notice is the idline - it’s not actually important what this is set to, but it’s good practice to at least pick one of your own IP addresses. Nodes will exchange messages using this as the ID.

Next up, redistribute default. This means that you want a default route to be shared (i.e. 0.0.0.0/0) in case you want to tell other hosts to use you as a default gateway - they won’t automatically do that though, we will cover that later.

After that, we’re distributing some actual routes.

Going back to our example above, if we wanted to share a route to reachout and outreach of how to get to the vlan100 and vlan200 networks, we’d just pop in the appropriate entry:

redistribute 172.16.100.0/24
redistribute 172.16.200.0/24

Naturally, we can condense all these routes into a single line if we wanted to share all of the networks on the host:

redistribute 172.16.0.0/16

After that, we just need to configure what interfaces we want to send our routes on:

# areas
area 0.0.0.5 {
        interface sec0 {
                auth-type simple
                auth-key SOMERANDHEX
                #type p2p
        }
        interface sec1 {
                auth-type simple
                auth-key SOMERANDHEX
                #type p2p
        }
}

That’s it, with the exception we need to configure the remote hosts with their own configurations. At this point you may need to create some pf.conf rules to allow the traffic - ospf has it’s own protocol:

pass in on sec0 proto ospf all
pass out on sec0 proto ospf all

Start the ospfd daemon on both the nodes, either via the rcctl command or by running it from the command line as ospfd -dvv. There’s a corresponding ospfctl command which you can use to interact with the daemon, for exampe ospfctl reload will force a reload of the config file in case you’ve made changes. Nice!

First up, lets see how everthing is currently looking:

zsh 571 % ospfctl show
Router ID: 100.64.0.1
Uptime: 4d18h21m
RFC1583 compatibility flag is disabled
SPF delay is 1000 msec(s), hold time between two SPFs is 5000 msec(s)
Number of external LSA(s) 22 (Checksum sum 0x99321)
Number of areas attached to this router: 1

Area ID: 0.0.0.5
  Number of interfaces in this area: 2
  Number of fully adjacent neighbors in this area: 2
  SPF algorithm executed 29 time(s)
  Number LSA(s) 3 (Checksum sum 0x1ab98)

Next, who are we connected to?

zsh 572 % ospfctl show neigh
ID              Pri State        DeadTime Address         Iface     Uptime
100.64.1.1      1   FULL/P2P     00:00:31 192.168.168.1   sec1      3d06h06m
100.64.1.2      1   FULL/P2P     00:00:39 100.64.0.2      sec0      05:18:58

Note the difference between the ID and the Addresses - the ID is taken from the config, the actual connection address is used as the Address.

However, whats really of interest to us is what routes have we recieved from our neighbours?

zsh 573 % ospfctl show rib
Destination          Nexthop           Path Type    Type      Cost    Uptime  
100.64.0.1           0.0.0.0         C Intra-Area   Router    0       4d18h24m
100.64.1.1           192.168.168.1     Intra-Area   Router    10      3d06h08m
100.64.1.2           100.64.0.2        Intra-Area   Router    10      05:20:45
100.64.0.1/32        100.64.0.2        Intra-Area   Network   20      05:20:45
100.64.0.2/32        0.0.0.0         C Intra-Area   Network   10      05:20:50
100.64.1.1/32        100.64.0.2        Intra-Area   Network   20      05:20:40
100.64.1.2/32        192.168.168.1     Intra-Area   Network   20      05:20:40
192.168.168.1/32     0.0.0.0         C Intra-Area   Network   10      3d06h08m
192.168.168.2/32     192.168.168.1     Intra-Area   Network   20      3d06h08m
0.0.0.0/0            192.168.168.1     Type 1 ext   Network   110     3d06h08m
172.16.0.0/24        100.64.0.2        Type 1 ext   Network   110     05:20:45
172.16.100.0/24      100.64.0.2        Type 1 ext   Network   110     05:20:45
172.16.200.0/24      100.64.0.2        Type 1 ext   Network   110     05:20:45
172.16.210.0/24      100.64.0.2        Type 1 ext   Network   110     05:20:45
172.16.220.0/24      100.64.0.2        Type 1 ext   Network   110     05:20:45
172.20.53.97/32      192.168.168.1     Type 1 ext   Network   110     3d06h08m
172.20.53.102/32     192.168.168.1     Type 1 ext   Network   110     3d06h08m
172.23.132.250/32    100.64.0.2        Type 1 ext   Network   110     05:20:45
172.23.132.252/32    192.168.168.1     Type 1 ext   Network   110     3d06h08m
172.23.132.253/32    192.168.168.1     Type 1 ext   Network   110     3d06h08m
172.23.132.254/32    192.168.168.1     Type 1 ext   Network   110     3d06h08m
192.168.23.0/25      100.64.0.2        Type 1 ext   Network   110     05:20:45
192.168.23.1/32      100.64.0.2        Type 1 ext   Network   110     05:20:45
192.168.23.3/32      100.64.0.2        Type 1 ext   Network   110     05:20:45
192.168.23.8/32      100.64.0.2        Type 1 ext   Network   110     05:20:45
192.168.23.14/32     100.64.0.2        Type 1 ext   Network   110     05:20:45

We’ve saved ourselves quite some time in not having to create those routes manually!

Let’s look at our routing tables next:

zsh 577 [1] % netstat -rn | head -10 && netstat -rn | grep 172.16.200  
Routing tables

Internet:
Destination        Gateway            Flags   Refs      Use   Mtu  Prio Iface
default            10.0.0.1           UGS        8  3529968     -     8 vio0 
default            192.168.168.1      UG         0        0     -    32 sec1 
224/4              127.0.0.1          URS        0    84174 32768     8 lo0  
10.0.0/24          10.0.0.225         UCn        1        0     -     4 vio0 
10.0.0.1           00:00:17:27:1e:5a  UHLch      1      600     -     3 vio0 
10.0.0.225         02:00:17:00:f1:93  UHLl       0  3853356     -     1 vio0 
172.16.200/24      100.64.0.2         UG         0        0     -    32 sec0 

If you look at the Prio field, you’ll see some routes have a Priority of 8 - these are static routes someone has manually defined. But some of them are a priority of 32 - these are ospf routes. In a future article, we’ll look at BGP, and those have a priority of 48. Lower is more preferred.

Now, remember we had distribute default in our config? We can see we have a default gateway set on this host, now we have two default routes. However, we will only ever use the one we manually configured because of the prio setting. If you removed your static default route, you’d wind up using the one with the next highest priority, in the case we’d route all traffic for unknown destinations to the 192.168.168.1 gateway.

ospf6d

Setting up IPv6 ospf is done via the ospf6d command - you are expected to run both at the same time. They work slightly differently, but the major difference is that there is no auth in this daemon. That said, there’s not really any auth in the previous one, so I wouldn’t worry too much about it.

zsh 578 % sudo more /etc/ospf6d.conf
redistribute default
redistribute fd1f:abc9:4ab::/48
redistribute 2001:470:6b54::1/48

area 0.0.0.0 {
        interface sec0 {
                type p2p
        }
        interface sec1 {
                type p2p
        }
}

There’s a few other shortcomings in this daemon - for example there is no reload functionality in ospf6ctl. No matter.

zsh 579 % ospf6ctl show
Router ID: 10.0.0.225
Uptime: 4d00h31m
SPF delay is 1 sec(s), hold time between two SPFs is 5 sec(s)
Number of external LSA(s) 33
Number of areas attached to this router: 1

Area ID: 0.0.0.0
  Number of interfaces in this area: 2
  Number of fully adjacent neighbors in this area: 2
  SPF algorithm executed 21 time(s)
  Number LSA(s) 6
zsh 580 % ospf6ctl show neigh
ID              Pri State        DeadTime  Iface       Uptime
10.0.1.29       1   FULL/P2P     00:00:33  sec1        4d00h31m
172.16.0.250    1   FULL/P2P     00:00:33  sec0        05:29:16
zsh 581 % ospf6ctl show rib  
Destination          Nexthop           Path Type    Type      Cost    Uptime  
::10.0.1.29          fe80::bbbb%sec1   Inter-Area   Router    10      4d00h31m
::172.16.0.250       fe80::aaaa%sec0   Inter-Area   Router    10      05:29:36
fd1f:abc9:4ab:a000::/64 fe80::aaaa%sec0   Intra-Area   Network   65545   05:29:36
fd1f:abc9:4ab:b000::/64 fe80::bbbb%sec1   Intra-Area   Network   65545   4d00h31m
fd1f:abc9:4ab:b000::/64 fe80::aaaa%sec0   Intra-Area   Network   65545   05:29:36
fd1f:abc9:4ab:c000::/64 fe80::bbbb%sec1   Intra-Area   Network   65545   4d00h31m
2001:470:b412::/64   fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
2001:470:b412:100::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
2001:470:b412:210::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab::/48   fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab::/48   fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:2000:100::2/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:2000:200::/72 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:2000:200::1/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:2000:300::2/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:3000::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:3100::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:3200::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:3210::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:3220::/64 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:a000::1/128 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:a000::2/128 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:b000::1/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:b000::1/128 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:b000::2/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:b000::2/128 fe80::aaaa%sec0   Type 1 ext   Network   110     05:29:36
fd1f:abc9:4ab:c000::1/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m
fd1f:abc9:4ab:c000::2/128 fe80::bbbb%sec1   Type 1 ext   Network   110     4d00h31m

That’s it!