Synopsis #
This chapter provides canonical, production-focused patterns for running BGP on OpenBSD using the base system daemon bgpd(8) . It covers configuration in bgpd.conf(5) , runtime operations with bgpctl(8) , service control via rcctl(8) , and optional origin validation with rpki-client(8) . These patterns apply to external peering, multi-provider edges, iBGP within a site or POP, and policy expression with communities and local preference.
Design Considerations #
- Policy-first. Default to “deny” and explicitly allow intended imports and announcements. Keep filters close to neighbors and keep global defaults conservative.
- Deterministic preference. Set
localpref
for inbound path selection inside the AS; use communities for structured policy signaling. - Scale boundaries. In small fabrics, a full-mesh iBGP suffices. For larger topologies, introduce route reflectors and keep reflector state minimal.
- RPKI. Validate origins for received routes; drop
roa invalid
. Schedule rpki-client(8) to refresh ROAs. - Observability. Treat
bgpctl show summary
,show neighbor
, andshow rib {in,out}
as part of every change and incident procedure. - Change control. Stage edits, syntax-check, and keep a roll-back path (console or timed revert) when touching upstream sessions.
Configuration #
1) Service lifecycle and skeleton #
Create a minimal skeleton with the local AS and router ID. Enable the daemon.
## /etc/bgpd.conf — minimal skeleton
AS 64512
router-id 198.51.100.2
# rcctl enable bgpd
# Start on boot
# bgpd -n -f /etc/bgpd.conf
# Syntax check (no start)
# rcctl start bgpd
# Launch the daemon
2) Single-homed eBGP to an upstream (announce specific prefixes) #
Assumptions:
- Public prefixes:
198.51.100.0/24
,2001:db8:100::/48
- Upstream neighbor:
203.0.113.1
, neighbor AS64500
- eBGP source address:
198.51.100.2
## /etc/bgpd.conf — eBGP to one ISP with explicit outbound and guarded inbound
AS 64512
router-id 198.51.100.2
# Prefixes intended for announcement
prefix-set "our-net" {
198.51.100.0/24,
2001:db8:100::/48
}
neighbor 203.0.113.1 {
remote-as 64500
descr "ISP-A"
local-address 198.51.100.2
enforce neighbor-as yes
max-prefix 500000 restart # Tune per provider guidance
announce none # Outbound controlled by match rules below
}
# Inbound: accept from the upstream (subject to max-prefix and future filters)
allow from neighbor 203.0.113.1
# Outbound: announce only approved prefixes to this neighbor
match to neighbor 203.0.113.1 prefix-set "our-net" announce
Reload and verify:
# bgpctl reload
# Graceful reconfiguration
$ bgpctl show summary
# Session state, prefix counts, per-neighbor stats
$ bgpctl show neighbor 203.0.113.1
# Capabilities and last error (if any)
$ bgpctl show rib out neighbor 203.0.113.1
# Only approved prefixes should be advertised
3) Dual-homed eBGP (prefer one upstream with localpref; communities on egress) #
Assumptions:
- Second upstream:
198.51.100.1
, AS64496
- Preference: routes from
ISP-A
should win overISP-B
by higherlocalpref
- Egress tagging: set a reference community on announced routes (example only)
## /etc/bgpd.conf — two providers; deterministic preference and egress tagging
AS 64512
router-id 198.51.100.2
prefix-set "our-net" { 198.51.100.0/24, 2001:db8:100::/48 }
neighbor 203.0.113.1 {
remote-as 64500
descr "ISP-A"
local-address 198.51.100.2
enforce neighbor-as yes
max-prefix 500000 restart
announce none
}
neighbor 198.51.100.1 {
remote-as 64496
descr "ISP-B"
local-address 198.51.100.2
enforce neighbor-as yes
max-prefix 500000 restart
announce none
}
# Inbound acceptance from both
allow from neighbor 203.0.113.1
allow from neighbor 198.51.100.1
# Prefer ISP-A inside the AS
match from neighbor 203.0.113.1 set localpref 200
match from neighbor 198.51.100.1 set localpref 150
# Outbound: announce the same set to both, with a reference community
match to neighbor 203.0.113.1 prefix-set "our-net" set community 64512:100 announce
match to neighbor 198.51.100.1 prefix-set "our-net" set community 64512:100 announce
Verification focus:
$ bgpctl show rib as 64500
# Routes learned from ISP-A (AS path view)
$ bgpctl show rib | egrep '198\.51\.100\.0/24|2001:db8:100::/48'
# Originated prefixes present in the RIB before export
$ bgpctl show neighbor | egrep 'LocalPref|Community'
# Policy markers visible as expected
Announcements require that the route exist in the routing table (for example, via static routes or loopback assignments). Install deterministic originating routes (host /32 or /128 for service IPs, aggregates for blocks) before
bgpd
can export them.
4) iBGP within the AS (small fabric, full mesh) #
Assumptions:
- Two routers,
R1
(this host) andR2
at10.0.0.2
, both in AS64512
- Full-mesh iBGP; R1 preferred as exit to
ISP-A
via higherlocalpref
applied to routes learned from that ISP
## /etc/bgpd.conf — add a simple iBGP peer
AS 64512
router-id 10.0.0.1
neighbor 10.0.0.2 {
remote-as 64512
descr "iBGP-R2"
announce none
}
# iBGP acceptance
allow from neighbor 10.0.0.2
# Optional: restrict exchange to internal routes
prefix-set "internal" { 10.10.0.0/16, 10.20.0.0/16 }
match to neighbor 10.0.0.2 prefix-set "internal" announce
Verification focus:
$ bgpctl show summary
# iBGP session should be Established
$ bgpctl show rib neighbor 10.0.0.2
# Routes accepted from the peer
$ bgpctl show rib out neighbor 10.0.0.2
# Routes advertised to the peer (filtered by "internal")
For larger topologies, introduce route reflectors to avoid a full mesh. Keep reflectors simple and limit re-advertisement to required address families and scopes.
5) RPKI origin validation (using rpki-client) #
Run rpki-client(8)
periodically to fetch and validate ROAs, then have bgpd
load the generated set. Reject roa invalid
by policy.
# rcctl enable rpki_client
# rcctl start rpki_client
# Starts periodic ROA fetch/validation; artifacts under /var/db/rpki-client/
# rpki-client -vv
# On-demand run for initial population and diagnostics
bgpd configuration:
## /etc/bgpd.conf — load ROAs and drop invalids
AS 64512
router-id 198.51.100.2
roa-set {
/var/db/rpki-client/openbgpd
}
# Policy: do not accept routes with invalid origin per ROA
deny from any roa invalid
# Allow remaining imports per neighbor rules
Observe RPKI state:
$ bgpctl show rib roa invalid
# Routes rejected due to invalid origin
$ bgpctl show rib roa unknown | head
# Routes without covering ROAs (informational)
6) Anycast service advertisement inside the AS #
For an internal anycast service (for example, resolver 10.10.255.53/32
), originate the address on each serving node (lo0 alias or static route), and announce using iBGP. ECMP or policy then steers clients.
## /etc/bgpd.conf — originate a host route for anycast via iBGP
AS 64512
router-id 10.0.0.1
network 10.10.255.53/32
# Route must exist in the RIB (lo0 alias or static) before export
Verification #
Core commands with bgpctl(8) :
$ bgpctl show summary
# Session state and counters
$ bgpctl show neighbor <addr>
# Capabilities, timers, last error
$ bgpctl show rib
# Loc-RIB; append 'in' or 'out' with 'neighbor <addr>' for per-peer views
$ bgpctl show rib community 64512:100
# Filter by community; useful for policy checks
$ bgpctl log verbose
# Increase bgpd(8) log verbosity temporarily during troubleshooting
Wire-level and system checks:
# tcpdump -ni egress port 179
# Observe BGP OPEN/KEEPALIVE/UPDATE on the wire
$ route -n show -inet ; route -n show -inet6
# Originating/static routes present for announced prefixes
Troubleshooting #
- Session stuck Idle/Active. Confirm IP reachability,
local-address
, and that PF allows TCP/179. Validate thatremote-as
matches the peer’s configured local AS and thatenforce neighbor-as
is not blocking expected paths. - No announcements leaving. The route must exist in the RIB (
network
statement or static route). Confirm that an explicitmatch ... announce
rule targets the neighbor. Inspectbgpctl show rib out neighbor ...
. - Excessive routes from a peer. Raise or add
max-prefix
withrestart
and awarning
threshold. Constrain scope with prefix-lists orprefix-set
filters. - Inbound path not preferred. Review
localpref
on received routes. Check whether more-specifics or MEDs override intent. - RPKI drops unexpected routes. Inspect
bgpctl show rib roa invalid
and cross-check current ROAs on the parent ROA repository. Investigate ROA state before relaxing policy. - Flapping after reloads. Use
bgpctl reload
for graceful policy updates. For structural changes (peer add/remove), apply during maintenance windows or off-peak hours.
See Also #
- Networking
- Advanced Networking
- Man pages: bgpd(8) , bgpd.conf(5) , bgpctl(8) , rpki-client(8)
- Related: MPLS and Label Distribution
- Related: Reference Architectures