FRRouting (FRR) is an open source routing suite that runs BGP, OSPF, IS-IS and other protocols on Linux, using the kernel routing table to forward traffic. BGP is the protocol networks use to exchange routes on the Internet, and running it on a Linux server lets you announce your own IP prefix, receive routes from upstream providers and fail over between them. In this tutorial you will install FRR on Ubuntu 24.04, establish an eBGP session with an upstream provider, announce a prefix with strict filters, add a second upstream with traffic preferences and verify every step with vtysh.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 with a non-root user with
sudoprivileges, for example a CubePath VPS or bare metal server. - A BGP session agreed with your upstream provider, including their ASN, their peer IP address and the IP address on your side. For public announcements you need your own public ASN and an IP prefix registered to you with valid route objects (IRR and RPKI ROA).
- Basic knowledge of IP addressing and routing.
If you do not have a real upstream yet, you can reproduce everything in a lab with two Linux VMs, each running FRR, using private ASNs (64512 to 65534).
This guide uses the following example values. Replace them with your own everywhere:
| Item | Example value |
|---|---|
| Your ASN | 64512 |
| Your prefix to announce | 203.0.113.0/24 |
| Your IP on the link to provider A | 198.51.100.2/30 on eth0 |
| Provider A peer IP and ASN | 198.51.100.1, AS 65010 |
| Your IP on the link to provider B (Step 7) | 192.0.2.2/30 on eth1 |
| Provider B peer IP and ASN (Step 7) | 192.0.2.1, AS 65020 |
Step 1 - Installing FRRouting from the official repository
Ubuntu ships an FRR package, but the FRR project maintains its own APT repository with the current stable release. Install the tools needed to add the repository:
sudo apt update
sudo apt install curl gnupg
Download the repository signing key into /etc/apt/keyrings:
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://deb.frrouting.org/frr/keys.gpg -o /etc/apt/keyrings/frrouting.gpg
Add the frr-stable repository for your Ubuntu release:
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 frr-reload.py script used to apply configuration changes without restarting:
sudo apt update
sudo apt install frr frr-pythontools
Check the installed version:
vtysh --version | head -1
vtysh version 10.x.x
Step 2 - Enabling the BGP daemon
FRR runs one daemon per protocol, and /etc/frr/daemons decides which ones start. zebra (which talks to the kernel) and staticd always start, but bgpd is disabled by default.
Open the file:
sudo nano /etc/frr/daemons
Change the bgpd line to:
bgpd=yes
Restart FRR so the new daemon starts:
sudo systemctl restart frr
Verify that bgpd is running:
sudo systemctl status frr --no-pager
● frr.service - FRRouting
Loaded: loaded (/usr/lib/systemd/system/frr.service; enabled; preset: enabled)
Active: active (running)
...
├─ ... /usr/lib/frr/zebra -d -F traditional -A 127.0.0.1 -s 90000000
├─ ... /usr/lib/frr/bgpd -d -F traditional -A 127.0.0.1
└─ ... /usr/lib/frr/staticd -d -F traditional -A 127.0.0.1
Step 3 - Allowing BGP through the firewall
BGP runs over TCP port 179. Allow it only from your peer, not from the whole Internet:
sudo ufw allow from 198.51.100.1 to any port 179 proto tcp comment 'BGP provider A'
If UFW is not enabled yet, make sure SSH is allowed before enabling it:
sudo ufw allow OpenSSH
sudo ufw enable
Step 4 - Configuring the BGP session and filters
FRR stores the whole configuration in /etc/frr/frr.conf. Editing this file and reloading is easier to review and version than typing commands into vtysh one by one.
Two FRR defaults shape the configuration:
- Since FRR 7.4,
bgp ebgp-requires-policyis enabled: an eBGP session with no inbound and outbound route map accepts and sends nothing. This protects you from leaking routes by accident, so always define both route maps. - The
networkstatement only announces a prefix that exists in the routing table. A static route toblackholeguarantees that it is always there, independently of interface state.
Open the configuration file:
sudo nano /etc/frr/frr.conf
Replace its contents with the following configuration:
frr defaults traditional
hostname router1
log syslog informational
service integrated-vtysh-config
!
ip route 203.0.113.0/24 blackhole
!
ip prefix-list MY-PREFIXES seq 10 permit 203.0.113.0/24
!
ip prefix-list UPSTREAM-IN seq 10 permit 0.0.0.0/0
!
route-map PROVIDER-A-OUT permit 10
match ip address prefix-list MY-PREFIXES
exit
!
route-map PROVIDER-A-IN permit 10
match ip address prefix-list UPSTREAM-IN
exit
!
router bgp 64512
bgp router-id 198.51.100.2
bgp log-neighbor-changes
neighbor 198.51.100.1 remote-as 65010
neighbor 198.51.100.1 description provider-a
!
address-family ipv4 unicast
network 203.0.113.0/24
neighbor 198.51.100.1 soft-reconfiguration inbound
neighbor 198.51.100.1 route-map PROVIDER-A-IN in
neighbor 198.51.100.1 route-map PROVIDER-A-OUT out
exit-address-family
exit
What each part does:
ip route 203.0.113.0/24 blackholeinstalls the aggregate in the routing table. Traffic for addresses inside the prefix that are not assigned to a more specific route is discarded instead of looping back to the provider.MY-PREFIXESwithPROVIDER-A-OUTguarantee that you only ever announce your own prefix, even if you later learn other routes.UPSTREAM-INwithPROVIDER-A-INaccept only a default route from the provider. That is the right choice for a server: a full Internet table needs more than 1 GB of RAM per session and is only useful when you have several upstreams and want per-destination path selection.soft-reconfiguration inboundkeeps a copy of everything the peer sends, before filtering, so you can inspect it later.
If your provider requires a TCP MD5 password for the session, add neighbor 198.51.100.1 password your_bgp_password inside router bgp.
Check the syntax before applying it:
sudo vtysh --dryrun -f /etc/frr/frr.conf
The command prints nothing when the file is valid. Apply the changes without restarting the daemons:
sudo systemctl reload frr
Step 5 - Verifying the session and the routes
Show the BGP summary:
sudo vtysh -c "show bgp ipv4 unicast summary"
BGP router identifier 198.51.100.2, local AS number 64512 VRF default vrf-id 0
...
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
198.51.100.1 4 65010 12 10 3 0 0 00:04:31 1 1 provider-a
A number in State/PfxRcd means the session is Established and shows how many prefixes you accepted after filtering. PfxSnt is the number of prefixes you announce. If you see Active, Connect or Idle instead, go to the Troubleshooting section.
Confirm that you announce only your prefix:
sudo vtysh -c "show bgp ipv4 unicast neighbors 198.51.100.1 advertised-routes"
Network Next Hop Metric LocPrf Weight Path
*> 203.0.113.0/24 0.0.0.0 0 32768 i
Total number of prefixes 1
Compare what the provider sends with what you accepted:
sudo vtysh -c "show bgp ipv4 unicast neighbors 198.51.100.1 received-routes"
sudo vtysh -c "show bgp ipv4 unicast neighbors 198.51.100.1 routes"
The first command lists everything received (thanks to soft-reconfiguration inbound); the second only the routes that passed PROVIDER-A-IN.
Finally, check that FRR installed the default route in the kernel:
ip route show proto bgp
default via 198.51.100.1 dev eth0 metric 20
From an external network, you can confirm the announcement is visible on the Internet with a public looking glass or a route collector such as RIPE RIS, searching for 203.0.113.0/24.
Step 6 - Tagging announcements with BGP communities
Communities are labels attached to routes. Many providers offer communities that change how they handle your announcement, such as prepending, not announcing to certain peers, or blackholing an address under attack. The values are provider specific, so check their documentation.
To tag your announcements to provider A, add a set community line to the outbound route map in /etc/frr/frr.conf:
route-map PROVIDER-A-OUT permit 10
match ip address prefix-list MY-PREFIXES
set community 64512:100 additive
exit
additive adds the community to any that the route already carries instead of replacing them. To act on communities that a peer sends you, define a list with bgp community-list standard NAME permit ASN:VALUE and use match community NAME in an inbound route map, together with the prefix list match so your inbound filter stays strict.
Reload and check the community on your own route:
sudo systemctl reload frr
sudo vtysh -c "show bgp ipv4 unicast 203.0.113.0/24"
The output includes a line such as Community: 64512:100.
Step 7 - Adding a second upstream provider
With two upstreams, you control which one is used for outgoing traffic with local-preference (higher wins), and you influence incoming traffic by making the backup path longer with AS path prepending.
First allow BGP from the second peer:
sudo ufw allow from 192.0.2.1 to any port 179 proto tcp comment 'BGP provider B'
Then edit /etc/frr/frr.conf. Replace the existing PROVIDER-A-IN route map with the version below, which sets a local preference, and add the two route maps for provider B. The outbound map to B prepends your ASN twice, so the Internet sees a longer path through B and prefers A for incoming traffic:
route-map PROVIDER-A-IN permit 10
match ip address prefix-list UPSTREAM-IN
set local-preference 200
exit
!
route-map PROVIDER-B-IN permit 10
match ip address prefix-list UPSTREAM-IN
set local-preference 100
exit
!
route-map PROVIDER-B-OUT permit 10
match ip address prefix-list MY-PREFIXES
set as-path prepend 64512 64512
exit
Inside the existing router bgp 64512 block, add the second neighbor next to the first one, and its settings inside the existing address-family ipv4 unicast section. The lines to add are:
router bgp 64512
neighbor 192.0.2.1 remote-as 65020
neighbor 192.0.2.1 description provider-b
!
address-family ipv4 unicast
neighbor 192.0.2.1 soft-reconfiguration inbound
neighbor 192.0.2.1 route-map PROVIDER-B-IN in
neighbor 192.0.2.1 route-map PROVIDER-B-OUT out
exit-address-family
exit
Validate and reload:
sudo vtysh --dryrun -f /etc/frr/frr.conf
sudo systemctl reload frr
Check which default route BGP selected:
sudo vtysh -c "show bgp ipv4 unicast 0.0.0.0/0"
Paths: (2 available, best #1, table default)
65010
198.51.100.1 from 198.51.100.1 (...)
Origin IGP, localpref 200, valid, external, best (Local Pref)
65020
192.0.2.1 from 192.0.2.1 (...)
Origin IGP, localpref 100, valid, external
Outgoing traffic uses provider A. If that session goes down, the route through provider B is installed automatically. Test it in a maintenance window by shutting down the session to A:
sudo vtysh -c "configure terminal" -c "router bgp 64512" -c "neighbor 198.51.100.1 shutdown"
ip route show proto bgp
default via 192.0.2.1 dev eth1 metric 20
Bring it back with the same command using no neighbor 198.51.100.1 shutdown. Changes made in vtysh like this are not saved to frr.conf unless you run write memory, so a reload restores the file's state.
Troubleshooting
The session stays in Active or Connect. There is no TCP connection. Check that you can reach the peer IP, that UFW allows port 179 from it, and that the provider has configured your side correctly:
ping -c 3 198.51.100.1
sudo ss -tnp | grep ':179'
The session is Established but PfxRcd or PfxSnt shows (Policy). No route map is applied in that direction, and bgp ebgp-requires-policy blocks everything. Apply both in and out route maps to the neighbor.
Your prefix is not announced. The network statement requires an exact match in the routing table. Check that the static route exists:
sudo vtysh -c "show ip route 203.0.113.0/24"
If the provider receives the route but does not propagate it, the cause is usually outside your server: missing IRR route objects, an RPKI ROA that does not match your ASN and prefix length, or a provider filter that has not been updated.
Configuration changes do not take effect. Read the FRR logs:
sudo journalctl -u frr -n 50 --no-pager
After changing a route map, the session keeps the old results until routes are re-evaluated. Trigger a soft refresh with sudo vtysh -c "clear bgp ipv4 unicast 198.51.100.1 soft", which does not reset the session.
Conclusion
You installed FRRouting from the official repository on Ubuntu 24.04, established an eBGP session with explicit inbound and outbound policy, announced your prefix, tagged it with communities and added a second upstream with local preference and AS path prepending for failover.
Next steps:
- Add IPv6 with a separate
address-family ipv6 unicastblock andipv6 prefix-listfilters. - Enable BFD (
bfdd) for sub-second failure detection on the BGP sessions. - Use OSPF with FRR for internal routing between your own routers, and keep BGP for the edge.
