Synopsis #
Run BGP on OpenBSD with the base system daemon bgpd(8) . Write its policy in bgpd.conf(5) , manage it with bgpctl(8) , and control the service with rcctl(8) . Add optional origin validation with rpki-client(8) . The examples cover external peering, multiple providers, iBGP within a site or POP, and policies using communities and local preference.
For a complete dual-provider setup with traffic tests, export filtering, and failover, follow Test OpenBGPD with Two Providers .
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
localpreffor inbound path selection inside the AS; use communities for structured policy signaling. - iBGP peers. Use a full mesh for a small network. For larger networks, use route reflectors to reduce the number of peering sessions.
- RPKI. Validate origins for received routes; drop
ovs invalid. Schedule rpki-client(8) to refresh ROAs. - Route checks. Inspect
bgpctl show summary,show neighbor, andshow rib {in,out}after changes and when diagnosing failures. - Change control. Stage edits, syntax-check, and keep a roll-back path (console or timed revert) when touching upstream sessions.
Configuration #
OpenBSD 8.0 defaults to announce as-4byte enforce: a BGP session is established only if the neighbor advertises the 4-byte AS capability. This applies even when both AS numbers fit in two bytes. For a legacy peer that lacks the capability, set announce as-4byte yes in that neighbor block only when the compatibility exception is required.
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
doas rcctl enable bgpd
# Start on boot
doas bgpd -n -f /etc/bgpd.conf
# Syntax check (no start)
doas 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:
203.0.113.2, on the same link as the upstream - The IPv6 family requires suitable IPv6 next-hop reachability; configure and test it with the provider
## /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
}
network prefix-set "our-net"
neighbor 203.0.113.1 {
remote-as 64500
descr "ISP-A"
local-address 203.0.113.2
enforce neighbor-as yes
max-prefix 500000 restart 15 # Tune per provider guidance
announce IPv4 unicast
announce IPv6 unicast
}
# Inbound: accept from the upstream (subject to max-prefix and future filters)
allow from 203.0.113.1
# Outbound: announce only approved prefixes to this neighbor
allow to 203.0.113.1 prefix-set "our-net"
Reload and verify:
doas bgpctl reload
# Graceful reconfiguration
doas bgpctl show summary
# Session state, prefix counts, per-neighbor stats
doas bgpctl show neighbor 203.0.113.1
# Capabilities and last error (if any)
doas 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-Ashould win overISP-Bby 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 }
network prefix-set "our-net"
neighbor 203.0.113.1 {
remote-as 64500
descr "ISP-A"
local-address 203.0.113.2
enforce neighbor-as yes
max-prefix 500000 restart 15
announce IPv4 unicast
announce IPv6 unicast
}
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 15
announce IPv4 unicast
announce IPv6 unicast
}
# Inbound acceptance from both
allow from 203.0.113.1
allow from 198.51.100.1
# Prefer ISP-A inside the AS
match from 203.0.113.1 set localpref 200
match from 198.51.100.1 set localpref 150
# Outbound: announce the same set to both, with a reference community
allow to 203.0.113.1 prefix-set "our-net" set community 64512:100
allow to 198.51.100.1 prefix-set "our-net" set community 64512:100
Verification focus:
doas bgpctl show rib as 64500
# Routes learned from ISP-A (AS path view)
doas bgpctl show rib | egrep '198\.51\.100\.0/24|2001:db8:100::/48'
# Originated prefixes present in the RIB before export
doas bgpctl show rib detail
# Inspect route local preference and communities
A static
networkstatement originates a prefix independently of an existing kernel route. Ensure the forwarding path for that prefix actually works; originating an unreachable aggregate still advertises it. An explicit export policy is also required.
4) iBGP within the AS (small fabric, full mesh) #
Assumptions:
- Two routers,
R1(this host) andR2at10.0.0.2, both in AS64512 - Full-mesh iBGP; R1 preferred as exit to
ISP-Avia higherlocalprefapplied 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 IPv4 unicast
announce IPv6 unicast
}
# iBGP acceptance
allow from 10.0.0.2
# Optional: restrict exchange to internal routes
prefix-set "internal" { 10.10.0.0/16, 10.20.0.0/16 }
allow to 10.0.0.2 prefix-set "internal"
Verification focus:
doas bgpctl show summary
# iBGP session should be Established
doas bgpctl show rib neighbor 10.0.0.2
# Routes accepted from the peer
doas 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 ovs invalid by policy.
doas rpki-client
Schedule rpki-client periodically using cron and reload bgpd only after successful output generation. It is not an rcctl service. Follow the installed rpki-client(8) scheduling example and preserve failed-run diagnostics.
Include the generated configuration in the main configuration:
include "/var/db/rpki-client/openbgpd"
After the normal neighbor import allowances, reject invalid origins:
deny from any ovs invalid
Filter rules use last-match semantics unless quick applies. A later broad allow would override this denial. Check both accepted and rejected routes after adding the policy.
Observe RPKI state:
doas bgpctl show rib in neighbor 203.0.113.1 ovs invalid
# Routes rejected due to invalid origin
doas bgpctl show rib ovs not-found | 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
# Originate the prefix; add the intended neighbor and export policy
Verification #
Core commands with bgpctl(8) :
doas bgpctl show summary
# Session state and counters
doas bgpctl show neighbor <addr>
# Capabilities, timers, last error
doas bgpctl show rib
# Loc-RIB; append 'in' or 'out' with 'neighbor <addr>' for per-peer views
doas bgpctl show rib community 64512:100
# Filter by community; useful for policy checks
doas bgpctl log verbose
# Increase bgpd(8) log verbosity temporarily during troubleshooting
Wire-level and system checks:
doas 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-asmatches the peer’s configured local AS and thatenforce neighbor-asis not blocking expected paths. - No announcements leaving. The route must exist in the RIB (
networkstatement or static route). Confirm that the prefix is originated and an explicitallow torule targets the neighbor. Inspectbgpctl show rib out neighbor .... - Excessive routes from a peer. Choose an appropriate
max-prefixlimit and restart interval in minutes. Constrain scope with prefix-lists orprefix-setfilters. - Inbound path not preferred. Review
localprefon received routes. Check whether more-specifics or MEDs override intent. - RPKI drops unexpected routes. Inspect
bgpctl show rib in neighbor 203.0.113.1 ovs invalidand cross-check current ROAs on the parent ROA repository. Investigate ROA state before relaxing policy. - Flapping after reloads. Use
bgpctl reloadfor 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