obmp-docker/gobgp/mrt-refresh.sh
sam 88a5546e29 Add GoBGP full-table feed container (roadmap E1)
New gobgp service: GoBGP peers eBGP-multihop with the AS57355 lab
route server (Bromirski) for the full real IPv4 + IPv6 Internet table
and BMP-exports it to the OpenBMP collector, landing in ip_rib as a
monitored peer.

Config follows the route server's published peering spec: local AS
65001, no password, keepalive 3600 / hold-time 7200, IPv4 feed on the
v4 session and IPv6 feed on the v6 session. gobgp/mrt-refresh.sh is a
cron-safe fallback that injects RouteViews MRT RIB dumps when the live
session is down. The live BGP session is not started here — bringing
gobgp up establishes the external session and loads ~1M routes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 07:39:12 -07:00

105 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# mrt-refresh.sh -- MRT full-table fallback loader for the OpenBMP GoBGP feed.
#
# Roadmap E1. The live route server (AS57355) is a single volunteer-run host
# with no SLA. When it is unreachable, the global table in PostgreSQL ip_rib
# would otherwise age out. This script downloads the latest RouteViews full
# MRT RIB dump and injects it into the running gobgpd so the table stays warm.
#
# Designed to be idempotent and cron-safe at a 2-hour cadence:
# - it only downloads a dump it does not already have,
# - it only injects when the live route server session is NOT established,
# - concurrent runs are guarded by a flock.
#
# Run it INSIDE the gobgp container (it shells out to the local `gobgp` CLI):
# docker exec obmp-gobgp /config/mrt-refresh.sh
#
# Example crontab entry on the docker host (every 2 hours):
# 0 */2 * * * docker exec obmp-gobgp /config/mrt-refresh.sh >> /var/log/gobgp-mrt.log 2>&1
set -euo pipefail
# --- tunables ---------------------------------------------------------------
MRT_DIR="${MRT_DIR:-/config/mrt}"
RV_BASE="${RV_BASE:-https://archive.routeviews.org/route-views/bgpdata}"
GOBGP="${GOBGP:-gobgp}"
LOCKFILE="${LOCKFILE:-/tmp/gobgp-mrt-refresh.lock}"
# RouteViews publishes a full RIB dump every 2 hours; dumps land a few minutes
# after the even hour, so we look back a safe margin.
LOOKBACK_HOURS="${LOOKBACK_HOURS:-4}"
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"; }
# --- single-instance guard --------------------------------------------------
exec 9>"${LOCKFILE}"
if ! flock -n 9; then
log "another mrt-refresh run is in progress; exiting"
exit 0
fi
mkdir -p "${MRT_DIR}"
# --- skip if the live route server is up ------------------------------------
# If any AS57355 neighbor is Established, the live feed is authoritative and
# we must NOT inject a stale MRT dump on top of it.
if ${GOBGP} neighbor 2>/dev/null | grep -qiE 'Establ'; then
log "a BGP session is Established; live feed is healthy, skipping MRT inject"
exit 0
fi
log "no Established BGP session; proceeding with MRT fallback"
# --- locate the most recent available RIB dump -----------------------------
# RouteViews RIB dumps:
# <RV_BASE>/YYYY.MM/RIBS/rib.YYYYMMDD.HHMM.bz2
# RIB dumps are taken at even hours (00,02,04,...,22) UTC.
found_url=""
found_file=""
now_epoch="$(date -u +%s)"
for ((h = 0; h <= LOOKBACK_HOURS; h++)); do
ts_epoch=$(( now_epoch - h * 3600 ))
hh="$(date -u -d "@${ts_epoch}" +%H)"
# only even hours carry RIB dumps
if (( 10#${hh} % 2 != 0 )); then
continue
fi
ym="$(date -u -d "@${ts_epoch}" +%Y.%m)"
ymd="$(date -u -d "@${ts_epoch}" +%Y%m%d)"
fname="rib.${ymd}.${hh}00.bz2"
url="${RV_BASE}/${ym}/RIBS/${fname}"
if curl -fsI --max-time 30 "${url}" >/dev/null 2>&1; then
found_url="${url}"
found_file="${fname}"
break
fi
done
if [[ -z "${found_url}" ]]; then
log "ERROR: no RouteViews RIB dump found within ${LOOKBACK_HOURS}h lookback"
exit 1
fi
dest="${MRT_DIR}/${found_file}"
# --- download (idempotent) --------------------------------------------------
if [[ -s "${dest}" ]]; then
log "already have ${found_file}; reusing cached copy"
else
log "downloading ${found_url}"
tmp="${dest}.partial.$$"
curl -fsSL --max-time 600 -o "${tmp}" "${found_url}"
mv -f "${tmp}" "${dest}"
log "downloaded $(du -h "${dest}" | cut -f1) -> ${dest}"
fi
# --- inject into the running gobgpd -----------------------------------------
# `gobgp mrt inject global` reads the bz2 dump directly and installs every
# prefix into the global RIB; BMP export to the collector follows automatically.
log "injecting ${found_file} into gobgpd global RIB"
${GOBGP} mrt inject global "${dest}"
log "MRT inject complete"
# --- housekeeping: keep only the 4 most recent dumps ------------------------
( cd "${MRT_DIR}" && ls -1t rib.*.bz2 2>/dev/null | tail -n +5 | xargs -r rm -f )
log "done"