OSPF (Open Shortest Path First) is a link-state routing protocol: every router learns the full topology of its area and computes the shortest path to each network, so routes adapt automatically when a link fails. It is the usual choice for routing inside your own network, while BGP handles routing between networks. In this tutorial you will use FRRouting (FRR) to run OSPF between two Ubuntu 24.04 routers, advertise loopback addresses, tune link costs and timers, protect the adjacency with authentication, redistribute static routes and learn the commands to troubleshoot it.

Prerequisites

To follow this tutorial you need:

  • Two servers running Ubuntu 24.04, each with a non-root user with sudo privileges, for example two CubePath VPS connected through a private network.
  • A network link between them where both servers are in the same subnet. The link must pass multicast traffic to 224.0.0.5 and 224.0.0.6, which OSPF uses for hello packets.
  • Basic knowledge of IP addressing and routing.

The examples use this topology. Replace interface names and addresses with yours (list them with ip -br addr):

ItemRouter 1 (r1)Router 2 (r2)
Link interfaceeth1eth1
Link address10.0.12.1/2410.0.12.2/24
Loopback (router ID)10.255.255.1/3210.255.255.2/32

The loopback addresses are stable identifiers that do not depend on any physical interface, which is why they are used as router IDs and as the addresses other services connect to.

Step 1 - Installing FRRouting on both routers

Run this step on both routers. FRR is available from the FRR project's APT repository, which provides the current stable release. Add the signing key and repository:

sudo apt update
sudo apt install curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://deb.frrouting.org/frr/keys.gpg -o /etc/apt/keyrings/frrouting.gpg
echo "deb [signed-by=/etc/apt/keyrings/frrouting.gpg] https://deb.frrouting.org/frr $(. /etc/os-release && echo "$VERSION_CODENAME") frr-stable" | sudo tee /etc/apt/sources.list.d/frr.list

Install FRR and frr-pythontools, which provides the reload script used by systemctl reload frr:

sudo apt update
sudo apt install frr frr-pythontools

Enable the OSPF daemon. Open the daemons file:

sudo nano /etc/frr/daemons

Change the ospfd line to:

ospfd=yes

Restart FRR:

sudo systemctl restart frr

Confirm that ospfd is running:

pgrep -a ospfd
2481 /usr/lib/frr/ospfd -d -F traditional -A 127.0.0.1

Step 2 - Enabling IP forwarding and allowing OSPF traffic

If the routers must forward traffic for other hosts (not only reach each other), the kernel needs IP forwarding enabled. Create a sysctl drop-in file on both routers:

echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-routing.conf
sudo sysctl --system

OSPF is not TCP or UDP: it is IP protocol 89. If UFW is enabled, allow all traffic from the link subnet on the link interface so that OSPF packets and routed traffic between the routers are accepted:

sudo ufw allow in on eth1 from 10.0.12.0/24 comment 'OSPF link'

Note: UFW also filters forwarded traffic. If the routers route traffic between other networks, add sudo ufw route allow rules for those networks as well.

Step 3 - Configuring OSPF on router 1

FRR keeps its configuration in /etc/frr/frr.conf. Modern FRR lets you enable OSPF per interface with ip ospf area, which is clearer than the older network ... area statements because the configuration sits next to the interface it affects.

On r1, open the file:

sudo nano /etc/frr/frr.conf

Replace its contents with:

frr defaults traditional
hostname r1
log syslog informational
service integrated-vtysh-config
!
interface lo
 ip address 10.255.255.1/32
 ip ospf area 0
 ip ospf passive
exit
!
interface eth1
 ip ospf area 0
 ip ospf network point-to-point
 ip ospf cost 10
exit
!
router ospf
 ospf router-id 10.255.255.1
 log-adjacency-changes detail
exit

What each block does:

  • interface lo adds the loopback address (FRR's zebra daemon configures it in the kernel) and advertises it in area 0. ip ospf passive advertises the network without sending hellos on that interface, which is correct for any interface with no OSPF neighbors.
  • interface eth1 runs OSPF on the link. point-to-point skips the Designated Router election that OSPF performs on broadcast networks, so adjacencies form faster. Use it whenever exactly two routers share the link.
  • ip ospf cost 10 sets the link cost explicitly. Virtual NICs often report no link speed, so relying on automatic cost calculation gives unpredictable results.
  • ospf router-id fixes the router ID to the loopback, so it does not change if interfaces go up or down.

Validate and apply the configuration:

sudo vtysh --dryrun -f /etc/frr/frr.conf
sudo systemctl reload frr

The dry run prints nothing when the file is valid.

Step 4 - Configuring OSPF on router 2

On r2, create the same configuration with its own hostname, loopback and router ID:

sudo nano /etc/frr/frr.conf
frr defaults traditional
hostname r2
log syslog informational
service integrated-vtysh-config
!
interface lo
 ip address 10.255.255.2/32
 ip ospf area 0
 ip ospf passive
exit
!
interface eth1
 ip ospf area 0
 ip ospf network point-to-point
 ip ospf cost 10
exit
!
router ospf
 ospf router-id 10.255.255.2
 log-adjacency-changes detail
exit

Apply it:

sudo vtysh --dryrun -f /etc/frr/frr.conf
sudo systemctl reload frr

The network type, area, hello and dead intervals must match on both sides of a link, otherwise no adjacency forms.

Step 5 - Verifying the adjacency and routes

On either router, list the OSPF neighbors:

sudo vtysh -c "show ip ospf neighbor"
Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
10.255.255.2      1 Full/-          00:01:12          35.402s 10.0.12.2       eth1:10.0.12.1                       0     0     0

Full means the two routers have synchronized their link-state databases. The - after it indicates a point-to-point link, where there is no DR or BDR. The states a neighbor goes through are:

StateMeaning
DownNo hellos received from the neighbor
InitHello received, but the neighbor does not list this router yet
2-WayBidirectional communication (final state between two non-DR routers on a broadcast link)
ExStart / ExchangeNegotiating and exchanging database descriptions
LoadingRequesting missing link-state advertisements
FullDatabases synchronized, routes calculated

Now check the OSPF routes on r1:

sudo vtysh -c "show ip route ospf"
O>* 10.255.255.2/32 [110/10] via 10.0.12.2, eth1, weight 1, 00:00:42
O   10.0.12.0/24 [110/10] is directly connected, eth1, weight 1, 00:01:30

O marks OSPF routes, >* the selected route installed in the kernel, and [110/10] the administrative distance and the total OSPF cost. Confirm connectivity between loopbacks, using the local loopback as source:

ping -c 3 -I 10.255.255.1 10.255.255.2
3 packets transmitted, 3 received, 0% packet loss, time 2003ms

Step 6 - Tuning cost and timers

Path cost and ECMP

OSPF chooses the path with the lowest total cost. When you have two links between sites, give the backup link a higher cost in its interface block, for example:

interface eth2
 ip ospf area 0
 ip ospf network point-to-point
 ip ospf cost 100
exit

If you prefer costs derived from link speed, set a reference bandwidth in megabits per second under router ospf on every router, so that faster links get lower costs. With auto-cost reference-bandwidth 100000, a 100 Gbps link has cost 1 and a 10 Gbps link cost 10. Use the same value on all routers.

When two paths have the same cost, FRR installs both and balances traffic across them (ECMP). Limit the number of paths with maximum-paths 4 under router ospf if needed.

Faster failure detection

By default, OSPF sends a hello every 10 seconds and declares a neighbor down after 40 seconds without hellos. To detect failures faster, lower both values on the link interface, on both routers:

interface eth1
 ip ospf area 0
 ip ospf network point-to-point
 ip ospf cost 10
 ip ospf hello-interval 2
 ip ospf dead-interval 8
exit

Reload FRR on both routers and check the timers:

sudo systemctl reload frr
sudo vtysh -c "show ip ospf interface eth1" | grep -i timer
  Timer intervals configured, Hello 2s, Dead 8s, Wait 8s, Retransmit 5

For sub-second detection, use BFD (bfdd) together with OSPF instead of pushing hello timers lower.

Step 7 - Authenticating OSPF neighbors

Without authentication, any host on the link that speaks OSPF can inject routes into your network. Add MD5 authentication to the link interface on both routers, with the same key ID and key. Replace your_ospf_key with a random string of up to 16 characters:

interface eth1
 ip ospf area 0
 ip ospf network point-to-point
 ip ospf cost 10
 ip ospf hello-interval 2
 ip ospf dead-interval 8
 ip ospf authentication message-digest
 ip ospf message-digest-key 1 md5 your_ospf_key
exit

The adjacency drops when you apply this on the first router and returns once the second router has the same key. Reload both and confirm:

sudo systemctl reload frr
sudo vtysh -c "show ip ospf interface eth1" | grep -i auth
sudo vtysh -c "show ip ospf neighbor"

The first command shows Cryptographic authentication enabled, and the neighbor is back in Full state.

Because frr.conf now contains a secret, check that its permissions only allow the frr user and group to read it:

ls -l /etc/frr/frr.conf
-rw-r----- 1 frr frr 612 Sep 25 10:12 /etc/frr/frr.conf

Step 8 - Redistributing static routes

Networks that are not running OSPF, such as a static route to a customer network, can be injected into OSPF with redistribution. Always filter what you redistribute with a route map, so a mistake in a static route does not spread through the network.

On r1, add a static route, a prefix list, a route map and the redistribute statement to /etc/frr/frr.conf:

ip route 192.168.50.0/24 10.0.12.254
!
ip prefix-list STATIC-TO-OSPF seq 10 permit 192.168.0.0/16 le 24
!
route-map STATIC-TO-OSPF permit 10
 match ip address prefix-list STATIC-TO-OSPF
exit
!
router ospf
 ospf router-id 10.255.255.1
 log-adjacency-changes detail
 redistribute static metric 20 metric-type 2 route-map STATIC-TO-OSPF
exit

Replace 10.0.12.254 with a real next hop for that network. The route is only redistributed while its next hop is reachable. metric-type 2 (the default) keeps the external metric fixed at 20 across the network, while metric-type 1 adds the internal OSPF cost to reach the router that injects it.

Reload and check the external link-state advertisement on r2:

sudo vtysh -c "show ip ospf database external"
sudo vtysh -c "show ip route 192.168.50.0/24"

The route appears on r2 as O>* 192.168.50.0/24 [110/20] via 10.0.12.1, marked as an external type 2 route.

Similarly, default-information originate under router ospf advertises a default route into OSPF from the router that has Internet access.

Step 9 - Using multiple areas and stub areas

A single area 0 works well for dozens of routers. In larger networks, split the topology into areas connected to area 0 by Area Border Routers (ABRs). Routers in a non-backbone area only see summaries of other areas, which reduces the size of their database and the impact of flapping links.

On an ABR, place each interface in its area:

interface eth1
 ip ospf area 0
exit
!
interface eth2
 ip ospf area 1
exit

If area 1 has only one exit to the rest of the network, make it a stub area. The ABR then replaces external routes with a default route, and routers in the area need less memory. Add this under router ospf on the ABR and on every router in area 1:

router ospf
 area 1 stub
exit
  • area 1 stub no-summary, configured on the ABR, creates a totally stubby area that also hides inter-area routes and only receives a default route.
  • area 1 nssa (not-so-stubby area) is a stub area that can still inject its own external routes, for example a site with a local static route.

The stub flag must be identical on all routers in the area, or they will not form adjacencies. Check the area type with:

sudo vtysh -c "show ip ospf" | grep -A2 "Area ID"

Troubleshooting

No neighbor appears. Hellos are not arriving. Capture OSPF packets on the link interface of each router:

sudo tcpdump -ni eth1 ip proto 89

If you see only outgoing hellos, the other router is not sending them, the firewall drops them, or the virtual network does not deliver multicast. Check UFW with sudo ufw status verbose and the interface configuration with sudo vtysh -c "show ip ospf interface eth1".

The neighbor stays in Init or 2-Way, or disappears repeatedly. Compare show ip ospf interface eth1 on both routers: area, network type, hello and dead intervals and authentication must match. Enable detailed logs temporarily:

sudo vtysh -c "debug ospf event" -c "debug ospf packet hello"
sudo journalctl -u frr -f

Messages about mismatched intervals or authentication point to the parameter that differs. Disable debugging afterwards with sudo vtysh -c "no debug ospf event" -c "no debug ospf packet hello".

The neighbor stays in ExStart or Exchange. This usually means the MTU differs on the two ends of the link. Check it with ip link show eth1 and set the same MTU on both interfaces. Only if you cannot change it, add ip ospf mtu-ignore to the interface on both routers.

The adjacency is Full but a route is missing. Check that the network is actually in OSPF (show ip ospf interface lists the interface in the right area) and look for it in the database with sudo vtysh -c "show ip ospf database". For redistributed routes, verify the source route exists with show ip route static and that the route map permits it.

Conclusion

You installed FRRouting on two Ubuntu 24.04 routers, formed an OSPF adjacency on a point-to-point link, advertised loopback addresses, tuned costs and timers, secured the adjacency with MD5 authentication, redistributed a filtered static route and saw how areas and stub areas keep larger networks manageable.

Next steps:

  • Add IPv6 routing with OSPFv3 (ospf6d), which uses a separate configuration block.
  • Enable BFD for faster failure detection than OSPF timers allow.
  • Use BGP with FRR at the edge of your network and OSPF internally to carry loopbacks and next hops.