#!/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: # /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"