FRRouting BGP on Linux: eBGP and iBGP Configuration - 夜莺博客

FRRouting BGP on Linux: eBGP and iBGP Configuration

FRRouting (FRR) is the de facto standard for running real routing protocols on Linux - it evolved from Quagga and powers everything from homelab routers to production BGP edge servers announcing prefixes to the Internet. Configuration happens in vtysh, a Cisco-style CLI, and the daemons file decides which protocols start. This guide walks through installing FRR, enabling bgpd, and configuring eBGP and iBGP sessions with prefix advertisement, filtering, dual-stack address families and operational hygiene, based on the official FRR documentation.

Install FRR and Enable the BGP Daemon

# Ubuntu/Debian (using the FRR apt repo)
curl -s https://deb.frrouting.org/frr/keys.gpg | sudo tee /usr/share/keyrings/frrouting.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/frrouting.gpg] https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable" | sudo tee /etc/apt/sources.list.d/frr.list
sudo apt update && sudo apt install frr frr-pythontools

# enable only the daemons you need
sudo sed -i 's/bgpd=no/bgpd=yes/' /etc/frr/daemons
sudo systemctl restart frr

FRR is a set of daemons, not one binary. zebra owns the kernel routing table and interfaces, and bgpd owns BGP; the other daemons (ospfd, isisd, bfdd, staticd, mgmtd) are enabled the same way in /etc/frr/daemons. Turn on bfdd as well if you plan to use BFD for fast failure detection, and leave everything you are not using disabled so you have fewer moving pieces to reason about during an incident.

sudo systemctl status frr
sudo vtysh -c "show version"
cat /etc/frr/daemons | grep -v '^#' | grep yes

Basic eBGP Session

sudo vtysh
configure terminal
router bgp 65001
 bgp router-id 1.1.1.1
 neighbor 10.0.0.2 remote-as 65002
 neighbor 10.0.0.2 description Peer-AS65002
 address-family ipv4 unicast
  network 192.168.1.0/24
  neighbor 10.0.0.2 activate
 exit-address-family
exit
write memory

The network statement only advertises a prefix if it exists in the kernel routing table - originate it on a loopback or dummy interface (ip addr add 192.168.1.1/24 dev lo) or zebra will not announce it. Set no bgp default ipv4-unicast and explicit activate when running dual-stack or you will advertise IPv4 to every neighbor unintentionally.

Two FRR-specific behaviours bite people setting up their first session. First, ebgp-requires-policy: FRR 8.0 and later expect every eBGP neighbor to have inbound and outbound filtering configured, and a session with no policy generates warnings and does not behave the way older releases did. Either attach filters (shown below) or turn the requirement off explicitly:

router bgp 65001
 no bgp ebgp-requires-policy
 bgp log-neighbor-changes

Second, timers and transport. The defaults (60 s keepalive, 180 s hold) are fine over a stable link but slow to notice a dead peer on a flapping one. Tighten them deliberately, and remember that both ends negotiate the lower values:

router bgp 65001
 neighbor 10.0.0.2 timers 10 30
 neighbor 10.0.0.2 password S3cret-BGP-MD5
 neighbor 10.0.0.2 ttl-security hops 1

password enables TCP MD5 signature protection and ttl-security hops 1 enables the GTSM check so only directly connected peers can form the session. On a Linux host, confirm that forwarding is actually enabled and that reverse-path filtering is not dropping traffic your design needs:

sysctl net.ipv4.ip_forward
sysctl net.ipv4.conf.all.rp_filter

iBGP Session (Full Mesh or Route Reflector)

router bgp 65001
 bgp router-id 1.1.1.1
 neighbor 2.2.2.2 remote-as 65001
 neighbor 2.2.2.2 update-source lo      ! peer over loopbacks
 address-family ipv4 unicast
  neighbor 2.2.2.2 activate
  neighbor 2.2.2.2 next-hop-self        ! needed when no IGP carries next-hops
 exit-address-family

iBGP requires full mesh or route reflectors. Turn a router into a reflector with neighbor X route-reflector-client under the address family. Peering over loopbacks with update-source is standard practice because it survives individual link failures; add neighbor 2.2.2.2 disable-connected-check when you are peering between two loopbacks in the same IP subnet and multihop is not required, or neighbor 2.2.2.2 ebgp-multihop 3 for eBGP sessions that are not directly connected.

router bgp 65001
 bgp cluster-id 9.9.9.9
 neighbor 2.2.2.2 remote-as 65001
 neighbor 2.2.2.2 update-source lo
 address-family ipv4 unicast
  neighbor 2.2.2.2 activate
  neighbor 2.2.2.2 route-reflector-client
  neighbor 2.2.2.2 next-hop-self

next-hop-self is the classic omission: without it, an edge router advertises an external next-hop that iBGP speakers inside the AS cannot resolve, and the paths stay in the BGP table marked inaccessible. Give every speaker a loopback, advertise those loopbacks in OSPF or ISIS, and set next-hop-self on the edge routers. For large iBGP meshes, neighbor X route-reflector-client plus a fixed bgp cluster-id is usually preferable to confederations, though FRR supports both:

router bgp 65010
 bgp confederation identifier 65000
 bgp confederation peers 65001 65002

Filtering with Prefix-Lists and Route-Maps

ip prefix-list ALLOW_ONLY seq 5 permit 192.168.1.0/24
ip prefix-list ALLOW_ONLY seq 10 deny 0.0.0.0/0 le 32
route-map FILTER_IN permit 10
 match ip address prefix-list ALLOW_ONLY
exit
router bgp 65001
 neighbor 10.0.0.2 route-map FILTER_IN in
exit

Read that prefix-list carefully: the first entry permits exactly one /24; the second denies everything else, including more-specifics of the permitted range. Filters applied in the wrong direction are the most common cause of "the session is up but I see no routes". Pair prefix filtering with an AS_PATH filter so a peer cannot announce somebody else's address space:

ip as-path access-list 10 permit ^65002$
ip as-path access-list 10 permit ^65002 .*
router bgp 65001
 neighbor 10.0.0.2 filter-list 10 in

Then bound the damage a peer can do with a prefix limit — a full-table feed that suddenly grows past its expected size should not be allowed to fill your memory:

router bgp 65001
 neighbor 10.0.0.2 maximum-prefix 5000 warning-only

warning-only logs and alerts without tearing the session down; omit it when you want FRR to shut the neighbor down on breach. Finally, if you filter with communities or want to change policy without resetting the session, enable soft reconfiguration inbound so the router keeps the received routes for re-evaluation:

router bgp 65001
 neighbor 10.0.0.2 soft-reconfiguration inbound
 neighbor 10.0.0.2 send-community

Dual-Stack: Advertise IPv6 Too

With the family-level configuration model, adding IPv6 is a matter of activating the same neighbors in another address family. This is where no bgp default ipv4-unicast pays off, because nothing is advertised until you say so:

router bgp 65001
 bgp router-id 1.1.1.1
 no bgp default ipv4-unicast
 neighbor 2001:db8::2 remote-as 65002
 address-family ipv4 unicast
  neighbor 10.0.0.2 activate
  network 192.168.1.0/24
 exit-address-family
 address-family ipv6 unicast
  neighbor 2001:db8::2 activate
  network 2001:db8:1::/48
 exit-address-family

The same rule applies in IPv6 as in IPv4: the network statement advertises a prefix only if zebra has it in the kernel table, so originate your aggregate on a dummy interface or a Null0 route rather than hoping FRR will pick it up.

Unnumbered Peering and Multihop Sessions

FRR supports RFC 5549-style unnumbered BGP, which removes the need to allocate a point-to-point subnet per link. Point the neighbor statement at an interface instead of an address:

router bgp 65001
 neighbor eth1 interface remote-as external
 neighbor eth1 description Fabric-Uplink

This is the pattern used by most FRR-based data center fabrics and by homelab setups built on containerlab or network namespaces: an interface-based neighbor, an IPv6 link-local address underneath, and no per-link addressing to document. For sessions between loopbacks, keep the address-based form and add a multihop or connected-check setting so FRR does not reject the peer for being unreachable at hop 1:

router bgp 65001
 neighbor 2.2.2.2 remote-as 65002
 neighbor 2.2.2.2 update-source lo
 neighbor 2.2.2.2 ebgp-multihop 3

The same rules as Cisco apply: ebgp-multihop requires a route to the peer address in the RIB, and the TTL specified must cover the real hop count — too low and the session stays in Active forever with no log message that points at the cause.

Also decide early how prefixes will be originated, because this is where most FRR setups leak or blackhole routes. There are three workable patterns: a network statement that matches a prefix already present in the RIB (from a loopback, a dummy interface, or a connected subnet); a redistribute statement with a route-map that stamps the right community and local preference, typically used when the prefix source is a kernel route managed by another tool; and a static Null0 route plus an exact-match network statement, which is the cleanest way to announce an aggregate that you own without depending on an interface staying up. Whichever you choose, verify with show bgp ipv4 unicast that the prefix is present locally and with show bgp ipv4 unicast neighbors X advertised-routes that it is actually being sent, since a locally valid route that fails an outbound route-map is invisible in the summary output.

Verification Commands

show ip bgp summary
show ip bgp neighbors 10.0.0.2
show ip bgp
show running-config

FRR 8 and later prefer the explicit family form, and these are the commands you will actually live in during an incident:

show bgp ipv4 unicast summary
show bgp ipv4 unicast neighbors 10.0.0.2
show bgp ipv4 unicast neighbors 10.0.0.2 advertised-routes
show bgp ipv4 unicast neighbors 10.0.0.2 received-routes
show bgp ipv4 unicast 192.168.1.0/24
show bgp ipv6 unicast summary
show ip route bgp
show bfd peers

In show bgp summary, check the state column first (anything other than Established tells you the problem is in the transport or the router-id), then Up/Down, then PfxsRcd and PfxsSent. A session that is up with zero prefixes received is a filtering or policy problem, not a connectivity problem. To force re-evaluation after a policy change without dropping the session:

clear bgp ipv4 unicast 10.0.0.2 soft in
clear bgp ipv4 unicast 10.0.0.2 soft out

Operations: Persistence, Reloads and Logging

FRR keeps a single integrated configuration in /etc/frr/frr.conf. write memory inside vtysh persists your changes, but if you edit the file directly you should reload rather than restart, so existing sessions and their state are preserved where possible:

sudo vtysh -c "write memory"
sudo systemctl reload frr
sudo vtysh -c "show running-config"
sudo vtysh -c "configure terminal" -c "router bgp 65001" -c "do show bgp summary"

Add logging before you need it, and add it at the right severity — BGP debug output on a full-table session can generate megabytes per minute:

log syslog informational
log file /var/log/frr/bgp.log
debug bgp neighbor-events
debug bgp updates in
no debug bgp updates in

On a Linux router, remember that BGP is only half the forwarding path: net.ipv4.ip_forward must be 1, the BGP-learned routes have to make it from zebra into the kernel FIB (show ip route bgp proves it), and firewall rules or rp_filter can drop traffic the routing table says should be forwarded. When you build labs, running FRR inside containers or network namespaces with veth pairs is far faster than VM-based topologies, and the same vtysh commands apply.

One operational detail that costs people time: FRR, unlike a hardware router, does not own the box. A kernel upgrade, a network interface renamed by systemd's predictable naming, or a firewall reload can take the data plane away while bgpd still reports an established session and a healthy table. Add a readiness check that validates both layers — session state and forwarding — rather than trusting show bgp summary alone.

Related Guides on This Site

BGP theory applies equally here: review the BGP best-path algorithm, route-reflector cluster-id for larger iBGP meshes, route flap damping tuning for stability, and BGP session stuck in Idle/Active when a session refuses to come up.

原文链接:https://oneuptime.com/blog/post/2026-03-20-bgp-linux-frrouting/view