tutorial

Monitoring OpenBGPD with Vigilmon

OpenBGPD is OpenBSD's security-focused BGP daemon used by ISPs, IXPs, and network operators. Here's how to monitor bgpd process health, BGP session state, prefix counts, RIB size, and route leak detection with Vigilmon.

OpenBGPD is the BGP daemon developed by the OpenBSD project — a privilege-separated, security-first implementation used by ISPs, Internet Exchange Points, and network operators who want a clean, auditable BGP stack. Whether you're announcing your own prefixes to upstream providers, peering at an IXP, or running a route reflector, bgpd is managing the routing decisions that keep your network reachable.

Vigilmon can't query bgpctl directly over the internet, but it can monitor an HTTP health endpoint you expose locally — an approach that keeps all sensitive routing state inside your network while giving you external alerting on bgpd failures, session drops, prefix loss, and route leaks. This tutorial shows you how to build those health endpoints and wire them into Vigilmon.

What You'll Set Up

  • bgpd process liveness health endpoint
  • BGP session state monitor (per neighbor)
  • Prefix count change detector
  • Local RIB size health check
  • Route leak (max-prefix) event monitor
  • Cron heartbeat for the bgpctl polling script
  • Alert routing to Slack, PagerDuty, or email

Prerequisites

  • OpenBGPD 7.x on OpenBSD 7.x (or the portable version on Linux)
  • bgpd running with at least one configured neighbor
  • A web server (OpenBSD httpd, Nginx, or Apache) for health endpoints
  • A free Vigilmon account

Step 1: Create the bgpd Health Endpoint

The core health endpoint runs bgpctl show summary and translates the output into an HTTP response. Expose this via a shell script served through your web server's CGI or via a small daemon.

Create the script:

#!/bin/sh
# /var/www/cgi-bin/bgpd-health

SOCKET=/var/run/bgpd.sock

# Check that bgpd socket exists and is accessible
if [ ! -S "$SOCKET" ]; then
    printf 'Status: 503 Service Unavailable\r\nContent-Type: application/json\r\n\r\n'
    printf '{"bgpd":"down","reason":"socket_missing"}\n'
    exit 0
fi

# Try a quick bgpctl query
if bgpctl -s "$SOCKET" show summary >/dev/null 2>&1; then
    printf 'Status: 200 OK\r\nContent-Type: application/json\r\n\r\n'
    printf '{"bgpd":"up"}\n'
else
    printf 'Status: 503 Service Unavailable\r\nContent-Type: application/json\r\n\r\n'
    printf '{"bgpd":"down","reason":"bgpctl_failed"}\n'
fi

Enable CGI in OpenBSD httpd (/etc/httpd.conf):

server "localhost" {
    listen on 127.0.0.1 port 8080
    location "/cgi-bin/*" {
        fastcgi socket "/run/slowcgi.sock"
        root "/"
    }
    root "/var/www"
}

Start slowcgi and httpd, then test locally:

curl http://127.0.0.1:8080/cgi-bin/bgpd-health
# {"bgpd":"up"}

In Vigilmon, monitor this endpoint externally by forwarding the port through your firewall or reverse-proxying it behind an authenticated path. For security, restrict access to Vigilmon's probe IP ranges.


Step 2: Monitor BGP Session State Per Neighbor

BGP session flaps are the most common operational event — a neighbor drops out of Established state and your prefixes from that peer disappear. Create a session state health endpoint:

#!/bin/sh
# /var/www/cgi-bin/bgp-sessions

SOCKET=/var/run/bgpd.sock
DOWN_COUNT=0
TOTAL=0

# Parse bgpctl show summary for non-Established sessions
while IFS= read -r line; do
    case "$line" in
        *"Established"*) TOTAL=$((TOTAL + 1)) ;;
        *"Active"*|*"Idle"*|*"Connect"*|*"OpenSent"*|*"OpenConfirm"*)
            DOWN_COUNT=$((DOWN_COUNT + 1))
            TOTAL=$((TOTAL + 1))
            ;;
    esac
done <<EOF
$(bgpctl -s "$SOCKET" show summary 2>/dev/null)
EOF

if [ "$DOWN_COUNT" -gt 0 ]; then
    printf 'Status: 503 Service Unavailable\r\nContent-Type: application/json\r\n\r\n'
    printf '{"sessions_down":%d,"sessions_total":%d}\n' "$DOWN_COUNT" "$TOTAL"
else
    printf 'Status: 200 OK\r\nContent-Type: application/json\r\n\r\n'
    printf '{"sessions_down":0,"sessions_total":%d,"status":"all_established"}\n' "$TOTAL"
fi

Monitor http://your-router/cgi-bin/bgp-sessions with an expected status of 200. Any non-Established session returns 503 and triggers an alert.


Step 3: Monitor BGP Prefix Counts

A sudden drop in prefix count from a neighbor — more than 10% — typically indicates the neighbor withdrew routes due to a policy change, link failure, or misconfiguration. Monitor prefix counts with a comparison script:

#!/bin/sh
# /var/www/cgi-bin/bgp-prefixes
# Reports current prefix counts per neighbor

SOCKET=/var/run/bgpd.sock
OUTPUT=""
STATUS=200

bgpctl -s "$SOCKET" show neighbor 2>/dev/null | while IFS= read -r line; do
    case "$line" in
        "BGP neighbor"*)
            NEIGHBOR=$(echo "$line" | awk '{print $3}')
            ;;
        *"Received prefixes"*)
            COUNT=$(echo "$line" | awk '{print $NF}')
            OUTPUT="$OUTPUT,\"$NEIGHBOR\":$COUNT"
            ;;
    esac
done

printf 'Status: 200 OK\r\nContent-Type: application/json\r\n\r\n'
printf '{%s}\n' "${OUTPUT#,}"

Use Vigilmon's keyword check feature to alert if a prefix count in the response body drops below a threshold you specify. Alternatively, extend the script to compare against a stored baseline and return 503 if any neighbor's count drops more than 10%.


Step 4: Monitor Local RIB Size

The Local RIB contains routes selected by the BGP decision process after import filters. An unexpected shrinkage indicates filter misconfiguration, policy errors, or mass route withdrawal.

#!/bin/sh
# /var/www/cgi-bin/bgp-rib

SOCKET=/var/run/bgpd.sock
MIN_ROUTES=100  # Set this to your expected minimum

RIB_COUNT=$(bgpctl -s "$SOCKET" show rib 2>/dev/null | grep -c "^[0-9]" || echo 0)

if [ "$RIB_COUNT" -lt "$MIN_ROUTES" ]; then
    printf 'Status: 503 Service Unavailable\r\nContent-Type: application/json\r\n\r\n'
    printf '{"rib_routes":%d,"min_expected":%d,"status":"low_rib"}\n' "$RIB_COUNT" "$MIN_ROUTES"
else
    printf 'Status: 200 OK\r\nContent-Type: application/json\r\n\r\n'
    printf '{"rib_routes":%d,"status":"ok"}\n' "$RIB_COUNT"
fi

Adjust MIN_ROUTES to match your expected steady-state. If you're a full-table customer, set it to 900000 or whatever your historical minimum has been.


Step 5: Monitor for Route Leaks (Max-Prefix Events)

OpenBGPD supports max-prefix limits in neighbor configuration — if a neighbor sends more prefixes than the configured limit, bgpd logs an event and optionally tears down the session. Monitor for these events:

#!/bin/sh
# /var/www/cgi-bin/bgp-maxprefix
# Checks bgpd log for max-prefix exceeded events in the last 5 minutes

LOG=/var/log/bgpd.log
THRESHOLD=5  # Minutes to look back

EVENTS=$(grep "max-prefix" "$LOG" 2>/dev/null | \
    awk -v cutoff="$(date -r $(($(date +%s) - THRESHOLD * 60)) '+%b %d %H:%M')" \
    '$0 >= cutoff' | wc -l | tr -d ' ')

if [ "$EVENTS" -gt 0 ]; then
    printf 'Status: 503 Service Unavailable\r\nContent-Type: application/json\r\n\r\n'
    printf '{"max_prefix_events":%d,"window_minutes":%d}\n' "$EVENTS" "$THRESHOLD"
else
    printf 'Status: 200 OK\r\nContent-Type: application/json\r\n\r\n'
    printf '{"max_prefix_events":0,"status":"ok"}\n'
fi

Monitor this every 2 minutes. A route leak event is always urgent — a single alert is better than waiting for session-level indicators.


Step 6: Add a Cron Heartbeat for the Polling Script

The health endpoints above depend on bgpctl being able to reach the bgpd socket. To confirm the entire monitoring pipeline is functioning, add a cron heartbeat:

# /etc/cron.d/bgpd-monitor (or crontab entry)
*/5 * * * * _bgpd /usr/local/bin/bgpd-monitor-ping.sh
#!/bin/sh
# /usr/local/bin/bgpd-monitor-ping.sh
# Runs bgpctl and pings Vigilmon heartbeat if successful

if bgpctl show summary >/dev/null 2>&1; then
    curl -s --max-time 10 https://vigilmon.online/heartbeat/YOUR_HEARTBEAT_ID
fi

In Vigilmon:

  1. Click Add MonitorCron Heartbeat.
  2. Set the expected interval to 10 minutes (double the cron frequency to allow for slight delays).
  3. Copy the heartbeat URL and paste it into the script above.

If bgpctl can no longer reach bgpd, the cron job silently fails, the heartbeat stops, and Vigilmon alerts.


Step 7: Configure Alert Channels

  1. Go to Alert Channels in Vigilmon and add Slack, PagerDuty, or email.
  2. For the bgpd process health and BGP session monitors, set Consecutive failures before alert to 1 — BGP session drops are operational events that need immediate attention.
  3. For the RIB size and prefix count monitors, set to 2 — a single probe can occasionally time out during heavy UPDATE storms.
  4. Configure Alert escalation so that if bgpd is down and the primary on-call doesn't acknowledge within 5 minutes, the alert escalates to a secondary contact.

Summary

| Monitor | Target | What It Catches | |---|---|---| | bgpd process | /cgi-bin/bgpd-health | bgpd crash, socket failure | | Session state | /cgi-bin/bgp-sessions | Any neighbor leaving Established | | Prefix counts | /cgi-bin/bgp-prefixes | Route withdrawal, peer filter changes | | Local RIB | /cgi-bin/bgp-rib | Filter misconfiguration, mass withdrawal | | Max-prefix events | /cgi-bin/bgp-maxprefix | Route leak detection | | Cron heartbeat | Vigilmon heartbeat URL | bgpctl pipeline failure |

OpenBGPD's privilege-separated architecture and clean configuration make it a solid choice for production BGP operations — but production BGP means production monitoring. With Vigilmon watching bgpd health, session state, prefix counts, and max-prefix events, you'll have the situational awareness to respond to routing incidents before they become network outages.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →