#!/usr/bin/env bash # ────────────────────────────────────────────────────────────────────── # snmp-walk.sh — Live SNMP walk → parse → HTML viewer pipeline # # Usage: # ./snmp-walk.sh # uses SNMP_TARGET from .env # ./snmp-walk.sh 10.0.0.1 # override target IP # ./snmp-walk.sh 10.0.0.1 full # override target + walk mode # # Reads configuration from .env (community, version, walk mode). # Produces: walks/{IP}_{TIMESTAMP}_walk.txt # walks/{IP}_{TIMESTAMP}_walk_monitoring.json # walks/nid-viewer.html # ────────────────────────────────────────────────────────────────────── set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${SCRIPT_DIR}/.env" # ── Load .env ──────────────────────────────────────────────────────── if [[ -f "$ENV_FILE" ]]; then set -a # shellcheck source=/dev/null source "$ENV_FILE" set +a else echo "Warning: ${ENV_FILE} not found — using defaults" >&2 fi # ── Resolve parameters (CLI overrides .env) ────────────────────────── TARGET="${1:-${SNMP_TARGET:-}}" WALK_MODE="${2:-${SNMP_WALK_MODE:-targeted}}" VERSION="${SNMP_VERSION:-2c}" if [[ -z "$TARGET" ]]; then echo "Error: No target IP specified." echo " Set SNMP_TARGET in .env or pass as argument: $0 " exit 1 fi # ── Validate prerequisites ─────────────────────────────────────────── for cmd in snmpwalk python3; do if ! command -v "$cmd" &>/dev/null; then echo "Error: '$cmd' not found. Please install it." >&2 exit 1 fi done # Prefer snmpbulkwalk (GETBULK PDUs — much faster) over snmpwalk (GETNEXT) if command -v snmpbulkwalk &>/dev/null; then WALK_CMD=snmpbulkwalk BULK_ARGS=() # use default -Cr10; higher values truncate on some devices else WALK_CMD=snmpwalk BULK_ARGS=() fi # ── Build snmpwalk auth flags ──────────────────────────────────────── SNMP_AUTH=() if [[ "$VERSION" == "3" ]]; then SNMP_AUTH+=(-v3) SNMP_AUTH+=(-u "${SNMP_V3_USER:?SNMP_V3_USER required for v3}") SNMP_AUTH+=(-l "${SNMP_V3_SEC_LEVEL:-authPriv}") if [[ "${SNMP_V3_SEC_LEVEL:-authPriv}" != "noAuthNoPriv" ]]; then SNMP_AUTH+=(-a "${SNMP_V3_AUTH_PROTO:-SHA}") SNMP_AUTH+=(-A "${SNMP_V3_AUTH_PASS:?SNMP_V3_AUTH_PASS required}") fi if [[ "${SNMP_V3_SEC_LEVEL:-authPriv}" == "authPriv" ]]; then SNMP_AUTH+=(-x "${SNMP_V3_PRIV_PROTO:-AES}") SNMP_AUTH+=(-X "${SNMP_V3_PRIV_PASS:?SNMP_V3_PRIV_PASS required}") fi else SNMP_AUTH+=(-v "${VERSION}" -c "${SNMP_COMMUNITY:-public}") fi # ── Define OID subtrees ────────────────────────────────────────────── # Targeted: only what the viewer/parser needs TARGETED_OIDS=( .1.3.6.1.2.1.1 # System (sysDescr, sysName, sysUpTime, …) .1.3.6.1.2.1.2 # IF-MIB (interface table) .1.3.6.1.2.1.4 # IP-MIB (addresses, routes) .1.3.6.1.2.1.31 # IF-MIB extensions (ifName, ifAlias, 64-bit counters) .1.3.6.1.2.1.55 # IPv6-MIB .1.3.111.2.802.1.1.13 # LLDP-MIB (neighbors, stats) .1.3.6.1.4.1.22420.1.1 # ACD-DESC-MIB (device identity, connectors, sensors) .1.3.6.1.4.1.22420.2.1 # ACD-ALARM-MIB .1.3.6.1.4.1.22420.2.2 # ACD-FILTER-MIB .1.3.6.1.4.1.22420.2.3 # ACD-POLICY-MIB (L2 policies, stats) .1.3.6.1.4.1.22420.2.4 # ACD-SFP-MIB (transceiver info/diag) .1.3.6.1.4.1.22420.2.6 # ACD-REGULATOR-MIB .1.3.6.1.4.1.22420.2.8 # ACD-SMAP-MIB (CoS profiles) .1.3.6.1.4.1.22420.2.9 # ACD-PORT-MIB (port config/status) ) # ── Prepare output paths ───────────────────────────────────────────── TIMESTAMP="$(date +%Y-%m-%d_%H-%M-%S)" SAFE_IP="${TARGET//./-}" WALKS_DIR="${SCRIPT_DIR}/walks" WALK_FILE="${WALKS_DIR}/${SAFE_IP}_${TIMESTAMP}_walk.txt" ERROR_FILE="${WALKS_DIR}/${SAFE_IP}_${TIMESTAMP}_errors.txt" mkdir -p "$WALKS_DIR" # ── Execute SNMP walk ───────────────────────────────────────────────── echo "═══════════════════════════════════════════════════════" echo " SNMP NID Viewer — Live Walk" echo "═══════════════════════════════════════════════════════" echo " Target: ${TARGET}" echo " Version: SNMPv${VERSION}" echo " Mode: ${WALK_MODE}" echo " Walker: ${WALK_CMD}${BULK_ARGS:+ (max-rep=${BULK_ARGS[1]#-Cr})}" echo " Output: ${WALK_FILE}" echo "═══════════════════════════════════════════════════════" echo "" WALK_START="$(date +%s)" if [[ "$WALK_MODE" == "full" ]]; then echo "[1/3] Walking full OID tree (.1) ..." "$WALK_CMD" -On -OQ "${BULK_ARGS[@]}" "${SNMP_AUTH[@]}" "$TARGET" .1 \ > "$WALK_FILE" 2> "$ERROR_FILE" || true else echo "[1/3] Walking ${#TARGETED_OIDS[@]} targeted subtrees ..." : > "$WALK_FILE" : > "$ERROR_FILE" for oid in "${TARGETED_OIDS[@]}"; do echo " ↳ ${oid}" "$WALK_CMD" -On -OQ "${BULK_ARGS[@]}" "${SNMP_AUTH[@]}" "$TARGET" "$oid" \ >> "$WALK_FILE" 2>> "$ERROR_FILE" || true done fi WALK_END="$(date +%s)" WALK_LINES="$(wc -l < "$WALK_FILE")" WALK_SECS=$(( WALK_END - WALK_START )) echo " ✓ Walk complete: ${WALK_LINES} lines in ${WALK_SECS}s" # Remove empty error file if [[ ! -s "$ERROR_FILE" ]]; then rm -f "$ERROR_FILE" fi if [[ "$WALK_LINES" -eq 0 ]]; then echo "" echo "Error: Walk returned no data. Check:" echo " - Device reachability (ping ${TARGET})" echo " - SNMP community/credentials in .env" echo " - Firewall rules (UDP 161)" [[ -s "$ERROR_FILE" ]] && echo "" && cat "$ERROR_FILE" exit 1 fi # ── Parse walk → monitoring JSON ────────────────────────────────────── echo "" echo "[2/3] Parsing walk data ..." python3 "${SCRIPT_DIR}/snmp-parse.py" "$WALK_FILE" MONITORING_JSON="${WALK_FILE%.txt}_monitoring.json" if [[ ! -f "$MONITORING_JSON" ]]; then echo "Error: snmp-parse.py did not produce ${MONITORING_JSON}" >&2 exit 1 fi echo " ✓ Monitoring JSON: ${MONITORING_JSON}" # ── Build HTML viewer ───────────────────────────────────────────────── echo "" echo "[3/3] Building HTML viewer ..." python3 "${SCRIPT_DIR}/build_nid_viewer.py" "$MONITORING_JSON" echo " ✓ Viewer: ${WALKS_DIR}/nid-viewer.html" # ── Summary ─────────────────────────────────────────────────────────── echo "" echo "═══════════════════════════════════════════════════════" echo " Done! Open walks/nid-viewer.html in a browser." echo "═══════════════════════════════════════════════════════"