From dfd81074aeefea658bc7bfa510d81db22304fbf8 Mon Sep 17 00:00:00 2001 From: vwiebe Date: Fri, 5 Jun 2026 10:58:00 -0400 Subject: [PATCH] Initial commit --- install-snmpsim-lab.sh | 768 +++++++++++++++++++++++ install-snmpsim-lab.sh:Zone.Identifier | Bin 0 -> 25 bytes trap-generator.sh | 232 +++++++ trap-generator.sh:Zone.Identifier | Bin 0 -> 25 bytes uninstall-snmpsim-lab.sh | 288 +++++++++ uninstall-snmpsim-lab.sh:Zone.Identifier | Bin 0 -> 25 bytes 6 files changed, 1288 insertions(+) create mode 100644 install-snmpsim-lab.sh create mode 100644 install-snmpsim-lab.sh:Zone.Identifier create mode 100644 trap-generator.sh create mode 100644 trap-generator.sh:Zone.Identifier create mode 100644 uninstall-snmpsim-lab.sh create mode 100644 uninstall-snmpsim-lab.sh:Zone.Identifier diff --git a/install-snmpsim-lab.sh b/install-snmpsim-lab.sh new file mode 100644 index 0000000..ce13098 --- /dev/null +++ b/install-snmpsim-lab.sh @@ -0,0 +1,768 @@ +#!/bin/bash +# ============================================================================= +# install-snmpsim-lab.sh +# +# Installs the SevOne SNMP Simulation Lab into /opt/snmpsim +# All 10 devices use SNMPv2c (community: public) +# +# Components: +# - snmpsim-lextudio : 10 faux Cisco SNMP agents +# - trap-generator.sh : Random SNMP trap sender +# - nflow-generator : Synthetic NetFlow v5 exporter +# +# Usage: +# ./install-snmpsim-lab.sh [OPTIONS] +# +# Options: +# -v, --vm-ip IP address of this VM (SNMP agents bind to this IP) +# -t, --trap-host IP address of the SNMP trap destination (SevOne) +# -n, --netflow-host IP address of the NetFlow collector (SevOne) +# -p, --netflow-port UDP port for NetFlow (default: 9996) +# -T, --trap-port UDP port for SNMP traps (default: 162) +# -h, --help Show this help message +# +# Example: +# ./install-snmpsim-lab.sh \ +# --vm-ip 9.60.154.118 \ +# --trap-host 9.60.151.170 \ +# --netflow-host 9.60.151.170 +# +# ============================================================================= + +set -euo pipefail + +# ── Defaults ───────────────────────────────────────────────────────────────── +INSTALL_DIR="/opt/snmpsim" +SNMPSIM_USER="snmpsim" +NETFLOW_PORT="9996" +TRAP_PORT="162" +VM_IP="" +TRAP_HOST="" +NETFLOW_HOST="" + +# ── Colours for output ──────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Colour + +info() { echo -e "${CYAN}[INFO]${NC} $*"; } +ok() { echo -e "${GREEN}[OK]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } +section() { echo -e "\n${BLUE}══════════════════════════════════════════${NC}"; \ + echo -e "${BLUE} $*${NC}"; \ + echo -e "${BLUE}══════════════════════════════════════════${NC}"; } + +# ── Usage ───────────────────────────────────────────────────────────────────── +usage() { + cat << EOF + +Usage: $(basename "$0") [OPTIONS] + +OPTIONS: + -v, --vm-ip IP address of this VM that SNMP agents will bind to [REQUIRED] + -t, --trap-host Destination IP for SNMP traps (SevOne) [REQUIRED] + -n, --netflow-host Destination IP for NetFlow (SevOne) [REQUIRED] + -p, --netflow-port UDP port for NetFlow collector (default: 9996) + -T, --trap-port UDP port for SNMP trap receiver (default: 162) + -h, --help Show this help + +EXAMPLE: + $(basename "$0") \\ + --vm-ip 9.60.154.118 \\ + --trap-host 9.60.151.170 \\ + --netflow-host 9.60.151.170 + +EOF + exit 0 +} + +# ── Parse arguments ─────────────────────────────────────────────────────────── +if [[ $# -eq 0 ]]; then usage; fi + +while [[ $# -gt 0 ]]; do + case "$1" in + -v|--vm-ip) VM_IP="$2"; shift 2 ;; + -t|--trap-host) TRAP_HOST="$2"; shift 2 ;; + -n|--netflow-host) NETFLOW_HOST="$2"; shift 2 ;; + -p|--netflow-port) NETFLOW_PORT="$2"; shift 2 ;; + -T|--trap-port) TRAP_PORT="$2"; shift 2 ;; + -h|--help) usage ;; + *) error "Unknown option: $1"; usage ;; + esac +done + +# ── Validate required arguments ─────────────────────────────────────────────── +MISSING=0 +[[ -z "$VM_IP" ]] && { error "--vm-ip is required"; MISSING=1; } +[[ -z "$TRAP_HOST" ]] && { error "--trap-host is required"; MISSING=1; } +[[ -z "$NETFLOW_HOST" ]] && { error "--netflow-host is required"; MISSING=1; } +[[ $MISSING -eq 1 ]] && { echo ""; usage; } + +# ── Must run as root ────────────────────────────────────────────────────────── +if [[ $EUID -ne 0 ]]; then + error "This script must be run as root (sudo)" + exit 1 +fi + +# ── Summary ─────────────────────────────────────────────────────────────────── +echo "" +echo -e "${BLUE}╔══════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ SevOne SNMP Lab Installer ║${NC}" +echo -e "${BLUE}╚══════════════════════════════════════════════════╝${NC}" +echo "" +echo -e " Install directory : ${CYAN}${INSTALL_DIR}${NC}" +echo -e " VM IP (SNMP bind) : ${CYAN}${VM_IP}${NC}" +echo -e " Trap destination : ${CYAN}${TRAP_HOST}:${TRAP_PORT}${NC}" +echo -e " NetFlow destination: ${CYAN}${NETFLOW_HOST}:${NETFLOW_PORT}${NC}" +echo "" +read -rp "Proceed with installation? [y/N] " CONFIRM +[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; } + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 1: System dependencies" +# ───────────────────────────────────────────────────────────────────────────── + +# Detect package manager +if command -v apt-get &>/dev/null; then + PKG_MGR="apt" +elif command -v dnf &>/dev/null; then + PKG_MGR="dnf" +elif command -v yum &>/dev/null; then + PKG_MGR="yum" +else + error "Cannot detect package manager (apt/dnf/yum). Install dependencies manually." + exit 1 +fi + +info "Package manager: $PKG_MGR" + +install_pkg() { + info "Installing: $*" + case $PKG_MGR in + apt) DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get install -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" "$@" ;; + dnf) dnf install -y "$@" -q ;; + yum) yum install -y "$@" -q ;; + esac +} + +# Update package index +case $PKG_MGR in + apt) + DEBIAN_FRONTEND=noninteractive apt-get update -qq + DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get upgrade -y -qq -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" + ;; + dnf|yum) : ;; # no-op, dnf/yum update on install +esac + +# Core dependencies +install_pkg python3 python3-pip +install_pkg snmp snmp-mibs-downloader 2>/dev/null || install_pkg net-snmp-utils 2>/dev/null || \ + warn "Could not install net-snmp-utils — snmpwalk/snmptrap may be unavailable" +install_pkg git curl wget + +# Go (for nflow-generator) +if ! command -v go &>/dev/null; then + info "Installing Go..." + case $PKG_MGR in + apt) install_pkg golang-go ;; + dnf|yum) install_pkg golang ;; + esac +else + ok "Go already installed: $(go version)" +fi + +ok "System dependencies installed" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 2: Create snmpsim user and directory structure" +# ───────────────────────────────────────────────────────────────────────────── + +if id "$SNMPSIM_USER" &>/dev/null; then + ok "User '$SNMPSIM_USER' already exists" +else + useradd -r -s /sbin/nologin "$SNMPSIM_USER" + ok "Created user: $SNMPSIM_USER" +fi + +# Main directories +mkdir -p "${INSTALL_DIR}"/{data,logs,run} +for port in $(seq 10000 10009); do + mkdir -p "${INSTALL_DIR}/data/port-${port}" +done + +# PID file directory +mkdir -p /var/run/snmpsim.commands.responder + +# Ownership +chown -R "${SNMPSIM_USER}:${SNMPSIM_USER}" "${INSTALL_DIR}" +chown "${SNMPSIM_USER}:${SNMPSIM_USER}" /var/run/snmpsim.commands.responder + +ok "Directory structure created: ${INSTALL_DIR}" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 3: Install snmpsim-lextudio" +# ───────────────────────────────────────────────────────────────────────────── + +if command -v pip3 &>/dev/null; then + pip3 install --quiet snmpsim +elif command -v pip &>/dev/null; then + pip install --quiet snmpsim-lextudio +else + error "pip not found. Cannot install snmpsim." + exit 1 +fi + +# Find the installed binary +SNMPSIM_BIN=$(command -v snmpsim-command-responder 2>/dev/null || \ + find /usr /root ~/.local -name "snmpsim-command-responder" 2>/dev/null | head -1) + +if [[ -z "$SNMPSIM_BIN" ]]; then + error "snmpsim-command-responder not found after install. Check pip output above." + exit 1 +fi + +ok "snmpsim installed: $SNMPSIM_BIN" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 4: Generate SNMP device data files" +# ───────────────────────────────────────────────────────────────────────────── + +cat > "${INSTALL_DIR}/generate_devices.py" << PYEOF +#!/usr/bin/env python3 +""" +Generate 10 Cisco .snmprec files for snmpsim lab. +Each device gets a port-NNNNN/public.snmprec file. +Counters use the snmpsim numeric variation module for live incrementing. +""" +import os, random + +BASE_OUTPUT_DIR = "${INSTALL_DIR}/data" + +DEVICES = [ + ("core-sw-01", "Cisco IOS Software, Catalyst 6500 Software (s72033_rp-ADVENTERPRISEK9_WAN-M), Version 15.1(2)SY14", "1.3.6.1.4.1.9.1.282", 10000), + ("core-sw-02", "Cisco IOS Software, Catalyst 6500 Software (s72033_rp-ADVENTERPRISEK9_WAN-M), Version 15.1(2)SY14", "1.3.6.1.4.1.9.1.282", 10001), + ("dist-sw-01", "Cisco IOS Software, Catalyst 4500 Software (cat4500e-UNIVERSALK9-M), Version 15.2(4)E10", "1.3.6.1.4.1.9.1.659", 10002), + ("dist-sw-02", "Cisco IOS Software, Catalyst 4500 Software (cat4500e-UNIVERSALK9-M), Version 15.2(4)E10", "1.3.6.1.4.1.9.1.659", 10003), + ("access-sw-01", "Cisco IOS Software, Catalyst 3750 Software (C3750E-UNIVERSALK9-M), Version 15.2(4)E9", "1.3.6.1.4.1.9.1.516", 10004), + ("access-sw-02", "Cisco IOS Software, Catalyst 3750 Software (C3750E-UNIVERSALK9-M), Version 15.2(4)E9", "1.3.6.1.4.1.9.1.516", 10005), + ("access-sw-03", "Cisco IOS Software, Catalyst 3750 Software (C3750E-UNIVERSALK9-M), Version 15.2(4)E9", "1.3.6.1.4.1.9.1.516", 10006), + ("rtr-01", "Cisco IOS Software, Version 15.7(3)M8, RELEASE SOFTWARE (fc2) ISR 4321", "1.3.6.1.4.1.9.1.1861", 10007), + ("rtr-02", "Cisco IOS Software, Version 15.7(3)M8, RELEASE SOFTWARE (fc2) ISR 4321", "1.3.6.1.4.1.9.1.1861", 10008), + ("fw-01", "Cisco Adaptive Security Appliance Software Version 9.16(4)19", "1.3.6.1.4.1.9.1.1902", 10009), +] + +SWITCH_IFACES = [ + ("GigabitEthernet0/1", 1000000000), + ("GigabitEthernet0/2", 1000000000), + ("GigabitEthernet0/3", 1000000000), + ("GigabitEthernet0/4", 1000000000), + ("GigabitEthernet0/5", 1000000000), + ("TenGigabitEthernet1/1", 10000000000), + ("TenGigabitEthernet1/2", 10000000000), +] +ROUTER_IFACES = [ + ("GigabitEthernet0/0/0", 1000000000), + ("GigabitEthernet0/0/1", 1000000000), + ("Serial0/1/0", 1544000), +] +FW_IFACES = [ + ("GigabitEthernet0/0", 1000000000), + ("GigabitEthernet0/1", 1000000000), + ("GigabitEthernet0/2", 1000000000), + ("Management0/0", 1000000000), +] + +MAX32 = 4294967295 + +def rand_mac(seed): + random.seed(seed) + return "".join(f"{random.randint(0,255):02x}" for _ in range(6)) + +def counter(initial, offset, deviation=0): + p = f"initial={initial},offset={offset},cumulative=1,min=0,max={MAX32},wrap=1" + if deviation: p += f",deviation={deviation}" + return ("65:numeric", p) + +def gauge(initial, lo, hi, deviation): + return ("66:numeric", f"initial={initial},min={lo},max={hi},deviation={deviation},rate=0") + +def timeticks(initial): + return ("67:numeric", f"initial={initial},offset=100,cumulative=1,min=0,max={MAX32}") + +def ln(oid, tv): + return f"{oid}|{tv[0]}|{tv[1]}" + +def make_snmprec(idx, hostname, descr, sysoid, ifaces): + random.seed(idx * 1000) + uptime = random.randint(500_000, 80_000_000) + cpu_base = random.randint(5, 35) + mem_used = random.randint(50_000_000, 200_000_000) + mem_free = random.randint(20_000_000, 100_000_000) + lines = [] + + lines.append(f"1.3.6.1.2.1.1.1.0|4|{descr}") + lines.append(f"1.3.6.1.2.1.1.2.0|6|{sysoid}") + lines.append(ln("1.3.6.1.2.1.1.3.0", timeticks(uptime))) + lines.append(f"1.3.6.1.2.1.1.4.0|4|noc@lab.local") + lines.append(f"1.3.6.1.2.1.1.5.0|4|{hostname}.lab.local") + lines.append(f"1.3.6.1.2.1.1.6.0|4|Lab - Rack A") + lines.append(f"1.3.6.1.2.1.1.7.0|2|78") + lines.append(f"1.3.6.1.2.1.2.1.0|2|{len(ifaces)}") + + for i, (ifdescr, ifspeed) in enumerate(ifaces, start=1): + random.seed(idx * 200 + i) + mac = rand_mac(idx * 100 + i) + oper = 1 if i < len(ifaces) else 2 + in_oct = random.randint(10_000_000, 999_000_000) + out_oct = random.randint(10_000_000, 999_000_000) + in_pkt = in_oct // random.randint(64, 512) + out_pkt = out_oct // random.randint(64, 512) + err = random.randint(0, 50) + disc = random.randint(0, 30) + if ifspeed >= 10_000_000_000: + oo, od = random.randint(5_000_000, 20_000_000), random.randint(5_000, 20_000) + elif ifspeed >= 1_000_000_000: + oo, od = random.randint(500_000, 5_000_000), random.randint(500, 5_000) + else: + oo, od = random.randint(10_000, 100_000), random.randint(10, 100) + + lines += [ + f"1.3.6.1.2.1.2.2.1.1.{i}|2|{i}", + f"1.3.6.1.2.1.2.2.1.2.{i}|4|{ifdescr}", + f"1.3.6.1.2.1.2.2.1.3.{i}|2|6", + f"1.3.6.1.2.1.2.2.1.4.{i}|2|1500", + f"1.3.6.1.2.1.2.2.1.5.{i}|66|{ifspeed}", + f"1.3.6.1.2.1.2.2.1.6.{i}|4x|{mac}", + f"1.3.6.1.2.1.2.2.1.7.{i}|2|1", + f"1.3.6.1.2.1.2.2.1.8.{i}|2|{oper}", + ln(f"1.3.6.1.2.1.2.2.1.10.{i}", counter(in_oct, oo, int(oo*0.1))), + ln(f"1.3.6.1.2.1.2.2.1.11.{i}", counter(in_pkt, od, int(od*0.1))), + ln(f"1.3.6.1.2.1.2.2.1.13.{i}", counter(disc, 0, 1)), + ln(f"1.3.6.1.2.1.2.2.1.14.{i}", counter(err, 0, 1)), + ln(f"1.3.6.1.2.1.2.2.1.16.{i}", counter(out_oct, oo, int(oo*0.1))), + ln(f"1.3.6.1.2.1.2.2.1.17.{i}", counter(out_pkt, od, int(od*0.1))), + ln(f"1.3.6.1.2.1.2.2.1.19.{i}", counter(disc, 0, 1)), + ln(f"1.3.6.1.2.1.2.2.1.20.{i}", counter(err, 0, 1)), + ] + + cpu_lo = max(1, cpu_base - 10); cpu_hi = min(99, cpu_base + 20) + cpu_dev = max(3, cpu_base // 3) + lines += [ + f"1.3.6.1.4.1.9.9.109.1.1.1.1.2.1|2|1", + ln("1.3.6.1.4.1.9.9.109.1.1.1.1.3.1", gauge(cpu_base, cpu_lo, cpu_hi, cpu_dev)), + ln("1.3.6.1.4.1.9.9.109.1.1.1.1.4.1", gauge(cpu_base, cpu_lo, cpu_hi, cpu_dev)), + ln("1.3.6.1.4.1.9.9.109.1.1.1.1.5.1", gauge(cpu_base, cpu_lo, cpu_hi, cpu_dev)), + ln("1.3.6.1.4.1.9.2.1.57.0", gauge(cpu_base, cpu_lo, cpu_hi, cpu_dev)), + ln("1.3.6.1.4.1.9.2.1.58.0", gauge(cpu_base, cpu_lo, cpu_hi, cpu_dev)), + f"1.3.6.1.4.1.9.9.48.1.1.1.2.1|4|Processor", + ln("1.3.6.1.4.1.9.9.48.1.1.1.5.1", gauge(mem_free, mem_free-5_000_000, mem_free+5_000_000, 2_000_000)), + ln("1.3.6.1.4.1.9.9.48.1.1.1.6.1", gauge(mem_used, mem_used-5_000_000, mem_used+5_000_000, 2_000_000)), + f"1.3.6.1.4.1.9.9.48.1.1.1.2.2|4|I/O", + ln("1.3.6.1.4.1.9.9.48.1.1.1.5.2", gauge(mem_free//4, mem_free//4-1_000_000, mem_free//4+1_000_000, 500_000)), + ln("1.3.6.1.4.1.9.9.48.1.1.1.6.2", gauge(mem_used//8, mem_used//8-500_000, mem_used//8+500_000, 250_000)), + ] + return "\n".join(lines) + "\n" + +for idx, (hostname, descr, sysoid, port) in enumerate(DEVICES): + ifaces = ROUTER_IFACES if "rtr" in hostname else (FW_IFACES if "fw" in hostname else SWITCH_IFACES) + content = make_snmprec(idx, hostname, descr, sysoid, ifaces) + out_dir = os.path.join(BASE_OUTPUT_DIR, f"port-{port}") + os.makedirs(out_dir, exist_ok=True) + with open(os.path.join(out_dir, "public.snmprec"), "w") as f: + f.write(content) + print(f" port-{port}/public.snmprec ({hostname}, {len(ifaces)} interfaces)") + +print("Done.") +PYEOF + +info "Generating device data files..." +python3 "${INSTALL_DIR}/generate_devices.py" +chown -R "${SNMPSIM_USER}:${SNMPSIM_USER}" "${INSTALL_DIR}/data" +ok "Device data files generated" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 5: Create start / stop scripts" +# ───────────────────────────────────────────────────────────────────────────── + +cat > "${INSTALL_DIR}/start-sim.sh" << EOF +#!/bin/bash +# ============================================================================= +# Start 10 Cisco SNMP agents — all SNMPv2c, community: public +# ============================================================================= + +SNMPSIM_BIN="${SNMPSIM_BIN}" +BASE_DATA_DIR="${INSTALL_DIR}/data" +LOGFILE="${INSTALL_DIR}/logs/snmpsim.log" +PID_DIR="/var/run/snmpsim.commands.responder" +VM_IP="${VM_IP}" + +declare -A DEVICES=( + [10000]="core-sw-01" + [10001]="core-sw-02" + [10002]="dist-sw-01" + [10003]="dist-sw-02" + [10004]="access-sw-01" + [10005]="access-sw-02" + [10006]="access-sw-03" + [10007]="rtr-01" + [10008]="rtr-02" + [10009]="fw-01" +) + +echo "Starting 10 Cisco SNMP agents on \${VM_IP} (SNMPv2c, community: public)..." +> "\${LOGFILE}" + +for PORT in \$(echo "\${!DEVICES[@]}" | tr ' ' '\n' | sort -n); do + NAME="\${DEVICES[\$PORT]}" + \${SNMPSIM_BIN} \\ + --agent-udpv4-endpoint=\${VM_IP}:\${PORT} \\ + --data-dir="\${BASE_DATA_DIR}/port-\${PORT}" \\ + --process-user=${SNMPSIM_USER} \\ + --process-group=${SNMPSIM_USER} \\ + --pid-file="\${PID_DIR}/\${PORT}.pid" \\ + --daemonize \\ + --logging-method=file:\${LOGFILE} + echo " Started: \${NAME} on \${VM_IP}:\${PORT}" +done + +echo "" +echo "Done. 10 agents running (SNMPv2c, community: public)" +echo "Verify: snmpwalk -v2c -c public \${VM_IP}:10000 1.3.6.1.2.1.1" +EOF + +cat > "${INSTALL_DIR}/stop-sim.sh" << 'EOF' +#!/bin/bash +PID_DIR="/var/run/snmpsim.commands.responder" +echo "Stopping snmpsim agents..." +STOPPED=0 +for pidfile in "${PID_DIR}"/*.pid; do + [[ -f "$pidfile" ]] || continue + PID=$(cat "$pidfile" 2>/dev/null) + PORT=$(basename "$pidfile" .pid) + if kill "$PID" 2>/dev/null; then + echo " Stopped port $PORT (PID $PID)" + STOPPED=$((STOPPED+1)) + fi + rm -f "$pidfile" +done +if [[ $STOPPED -eq 0 ]]; then + pkill -f snmpsim-command-responder 2>/dev/null && echo " Stopped via pkill" || echo " No snmpsim process found." +fi +echo "Done." +EOF + +cat > "${INSTALL_DIR}/restart-sim.sh" << 'EOF' +#!/bin/bash +cd "$(dirname "$0")" +./stop-sim.sh +sleep 2 +rm -rf /tmp/snmpsim/* +./start-sim.sh +EOF + +chmod +x "${INSTALL_DIR}"/{start-sim.sh,stop-sim.sh,restart-sim.sh} +ok "start-sim.sh, stop-sim.sh, restart-sim.sh created" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 6: Create trap generator" +# ───────────────────────────────────────────────────────────────────────────── + +cat > "${INSTALL_DIR}/trap-generator.sh" << EOF +#!/bin/bash +# ============================================================================= +# trap-generator.sh +# Sends one random SNMP trap to the configured destination. +# Run manually or via cron. +# +# Usage: ./trap-generator.sh [--host ] [--port ] +# Defaults to compiled-in values if no args given. +# ============================================================================= + +SEVONE_IP="\${1:-${TRAP_HOST}}" +SEVONE_PORT="\${2:-${TRAP_PORT}}" +COMMUNITY="public" + +declare -A DEVICES +DEVICES["core-sw-01"]="${VM_IP}:10000:1.3.6.1.4.1.9.1.282" +DEVICES["core-sw-02"]="${VM_IP}:10001:1.3.6.1.4.1.9.1.282" +DEVICES["dist-sw-01"]="${VM_IP}:10002:1.3.6.1.4.1.9.1.659" +DEVICES["dist-sw-02"]="${VM_IP}:10003:1.3.6.1.4.1.9.1.659" +DEVICES["access-sw-01"]="${VM_IP}:10004:1.3.6.1.4.1.9.1.516" +DEVICES["access-sw-02"]="${VM_IP}:10005:1.3.6.1.4.1.9.1.516" +DEVICES["access-sw-03"]="${VM_IP}:10006:1.3.6.1.4.1.9.1.516" +DEVICES["rtr-01"]="${VM_IP}:10007:1.3.6.1.4.1.9.1.1861" +DEVICES["rtr-02"]="${VM_IP}:10008:1.3.6.1.4.1.9.1.1861" +DEVICES["fw-01"]="${VM_IP}:10009:1.3.6.1.4.1.9.1.1902" + +SWITCH_IFACES=("GigabitEthernet0/1" "GigabitEthernet0/2" "GigabitEthernet0/3" "GigabitEthernet0/4" "GigabitEthernet0/5" "TenGigabitEthernet1/1" "TenGigabitEthernet1/2") +ROUTER_IFACES=("GigabitEthernet0/0/0" "GigabitEthernet0/0/1" "Serial0/1/0") +FW_IFACES=("GigabitEthernet0/0" "GigabitEthernet0/1" "GigabitEthernet0/2" "Management0/0") + +random_element() { local a=("\$@"); echo "\${a[\$RANDOM % \${#a[@]}]}"; } +random_device() { local k=("\${!DEVICES[@]}"); echo "\${k[\$RANDOM % \${#k[@]}]}"; } + +get_ifaces() { + case "\$1" in + rtr*) echo "\${ROUTER_IFACES[@]}" ;; + fw*) echo "\${FW_IFACES[@]}" ;; + *) echo "\${SWITCH_IFACES[@]}" ;; + esac +} + +send_linkDown() { + local dev="\$1"; local ifaces=(\$(get_ifaces "\$dev")) + local iface=\$(random_element "\${ifaces[@]}"); local idx=\$((RANDOM % \${#ifaces[@]} + 1)) + echo "[\$(date '+%H:%M:%S')] linkDown | \$dev | \$iface" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.6.3.1.1.5.3 \\ + 1.3.6.1.2.1.2.2.1.1.\$idx i \$idx 1.3.6.1.2.1.2.2.1.2.\$idx s "\$iface" \\ + 1.3.6.1.2.1.2.2.1.7.\$idx i 2 1.3.6.1.2.1.2.2.1.8.\$idx i 2 +} + +send_linkUp() { + local dev="\$1"; local ifaces=(\$(get_ifaces "\$dev")) + local iface=\$(random_element "\${ifaces[@]}"); local idx=\$((RANDOM % \${#ifaces[@]} + 1)) + echo "[\$(date '+%H:%M:%S')] linkUp | \$dev | \$iface" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.6.3.1.1.5.4 \\ + 1.3.6.1.2.1.2.2.1.1.\$idx i \$idx 1.3.6.1.2.1.2.2.1.2.\$idx s "\$iface" \\ + 1.3.6.1.2.1.2.2.1.7.\$idx i 1 1.3.6.1.2.1.2.2.1.8.\$idx i 1 +} + +send_coldStart() { + echo "[\$(date '+%H:%M:%S')] coldStart | \$1" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.6.3.1.1.5.1 +} + +send_warmStart() { + echo "[\$(date '+%H:%M:%S')] warmStart | \$1" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.6.3.1.1.5.2 +} + +send_ospfNbrStateChange() { + [[ "\$1" != rtr* ]] && return + local peer="10.\$((RANDOM%255)).\$((RANDOM%255)).\$((RANDOM%255))" + local state=\$((RANDOM%8+1)) + echo "[\$(date '+%H:%M:%S')] ospfNbrStateChange| \$1 | peer \$peer state \$state" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.4.1.9.10.99.0.1 \\ + 1.3.6.1.2.1.14.10.1.1.\$peer a "\$peer" 1.3.6.1.2.1.14.10.1.6.\$peer i \$state \\ + 1.3.6.1.2.1.14.4.1.2.0 s "0.0.0.0" +} + +send_bgpEstablished() { + [[ "\$1" != rtr* ]] && return + local peer="192.168.\$((RANDOM%255)).\$((RANDOM%255))" + local as=\$((RANDOM%65000+1000)) + echo "[\$(date '+%H:%M:%S')] bgpEstablished | \$1 | peer \$peer AS\$as" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.2.1.15.7.1 \\ + 1.3.6.1.2.1.15.3.1.7.\$peer i 6 1.3.6.1.2.1.15.3.1.9.\$peer i \$as +} + +send_bgpBackwardTransition() { + [[ "\$1" != rtr* ]] && return + local peer="192.168.\$((RANDOM%255)).\$((RANDOM%255))" + local as=\$((RANDOM%65000+1000)); local state=\$((RANDOM%5+1)) + echo "[\$(date '+%H:%M:%S')] bgpBackward | \$1 | peer \$peer state \$state" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.2.1.15.7.2 \\ + 1.3.6.1.2.1.15.3.1.7.\$peer i \$state 1.3.6.1.2.1.15.3.1.9.\$peer i \$as +} + +send_cpuThreshold() { + local cpu=\$((RANDOM%40+60)) + echo "[\$(date '+%H:%M:%S')] cpuThreshold | \$1 | CPU \${cpu}%" + snmptrap -v2c -c "\$COMMUNITY" "\$SEVONE_IP:\$SEVONE_PORT" "" 1.3.6.1.4.1.9.9.109.0.1 \\ + 1.3.6.1.4.1.9.9.109.1.1.1.1.3.1 i \$cpu \\ + 1.3.6.1.4.1.9.9.109.1.1.1.1.4.1 i \$((cpu-RANDOM%10)) \\ + 1.3.6.1.4.1.9.9.109.1.1.1.1.5.1 i \$((cpu-RANDOM%15)) +} + +TRAP_TYPES=(linkDown linkDown linkDown linkUp linkUp linkUp coldStart warmStart + ospfNbrStateChange ospfNbrStateChange bgpEstablished + bgpBackwardTransition cpuThreshold) + +trap_type=\$(random_element "\${TRAP_TYPES[@]}") +device=\$(random_device) + +echo "--- \$(date '+%Y-%m-%d %H:%M:%S') | \$trap_type from \$device -> \$SEVONE_IP:\$SEVONE_PORT ---" +case "\$trap_type" in + linkDown) send_linkDown "\$device" ;; + linkUp) send_linkUp "\$device" ;; + coldStart) send_coldStart "\$device" ;; + warmStart) send_warmStart "\$device" ;; + ospfNbrStateChange) send_ospfNbrStateChange "\$device" ;; + bgpEstablished) send_bgpEstablished "\$device" ;; + bgpBackwardTransition) send_bgpBackwardTransition "\$device" ;; + cpuThreshold) send_cpuThreshold "\$device" ;; +esac +EOF + +chmod +x "${INSTALL_DIR}/trap-generator.sh" +ok "trap-generator.sh created" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 7: Build and install nflow-generator" +# ───────────────────────────────────────────────────────────────────────────── + +NFLOW_BIN="${INSTALL_DIR}/nflow-generator" + +if [[ -f "$NFLOW_BIN" ]]; then + ok "nflow-generator already present — skipping build" +else + info "Cloning and building nflow-generator..." + TMPDIR=$(mktemp -d) + git clone --quiet https://github.com/nerdalert/nflow-generator.git "$TMPDIR/nflow-generator" + cd "$TMPDIR/nflow-generator" + go build -o "$NFLOW_BIN" . + cd / + rm -rf "$TMPDIR" + ok "nflow-generator built: $NFLOW_BIN" +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 8: Create systemd services" +# ───────────────────────────────────────────────────────────────────────────── + +# NetFlow systemd service +cat > /etc/systemd/system/snmpsim-netflow.service << EOF +[Unit] +Description=SevOne Lab NetFlow Generator +After=network.target + +[Service] +ExecStart=${INSTALL_DIR}/nflow-generator -t ${NETFLOW_HOST} -p ${NETFLOW_PORT} +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + +# Trap generator systemd timer (every 5 minutes) +cat > /etc/systemd/system/snmpsim-traps.service << EOF +[Unit] +Description=SevOne Lab Trap Generator (single run) + +[Service] +Type=oneshot +ExecStart=${INSTALL_DIR}/trap-generator.sh ${TRAP_HOST} ${TRAP_PORT} +StandardOutput=journal +StandardError=journal +EOF + +cat > /etc/systemd/system/snmpsim-traps.timer << EOF +[Unit] +Description=SevOne Lab Trap Generator — every 5 minutes + +[Timer] +OnBootSec=60 +OnUnitActiveSec=5min +Unit=snmpsim-traps.service + +[Install] +WantedBy=timers.target +EOF + +# SNMP simulator systemd service +# Type=oneshot + RemainAfterExit=yes is correct for a script that launches +# background daemons and exits. HOME must be set so snmpsim can find its +# variation modules path (~/.snmpsim/variation). +cat > /etc/systemd/system/snmpsim-agents.service << EOF +[Unit] +Description=SevOne Lab SNMP Simulator (10 Cisco agents) +After=network.target + +[Service] +Type=oneshot +RemainAfterExit=yes +Environment=HOME=/root +ExecStart=${INSTALL_DIR}/start-sim.sh +ExecStop=${INSTALL_DIR}/stop-sim.sh +ExecReload=${INSTALL_DIR}/restart-sim.sh + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable --now snmpsim-agents.service +systemctl enable --now snmpsim-netflow.service +systemctl enable --now snmpsim-traps.timer + +ok "systemd services created, enabled, and started:" +ok " snmpsim-agents.service (SNMP -> ${VM_IP}:10000-10009, community: public)" +ok " snmpsim-netflow.service (NetFlow -> ${NETFLOW_HOST}:${NETFLOW_PORT})" +ok " snmpsim-traps.timer (Traps -> ${TRAP_HOST}:${TRAP_PORT} every 5 min)" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 9: Fix ownership" +# ───────────────────────────────────────────────────────────────────────────── + +chown -R "${SNMPSIM_USER}:${SNMPSIM_USER}" "${INSTALL_DIR}" +chown root:root "${INSTALL_DIR}/start-sim.sh" "${INSTALL_DIR}/stop-sim.sh" "${INSTALL_DIR}/restart-sim.sh" +chmod +x "${INSTALL_DIR}/start-sim.sh" "${INSTALL_DIR}/stop-sim.sh" "${INSTALL_DIR}/restart-sim.sh" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 10: Verify installation" +# ───────────────────────────────────────────────────────────────────────────── + +sleep 3 +PASS=0; FAIL=0 + +check() { + local desc="$1"; local cmd="$2" + if eval "$cmd" &>/dev/null; then + ok "$desc" + PASS=$((PASS+1)) + else + error "$desc" + FAIL=$((FAIL+1)) + fi +} + +check "snmpsim-agents service active" "systemctl is-active --quiet snmpsim-agents" +check "Port 10000 bound (core-sw-01)" "ss -ulnp | grep -q ':10000'" +check "Port 10007 bound (rtr-01)" "ss -ulnp | grep -q ':10007'" +check "SNMPv2c response (port 10000)" "snmpget -v2c -c public ${VM_IP}:10000 1.3.6.1.2.1.1.5.0 2>/dev/null" +check "SNMPv2c response (port 10005)" "snmpget -v2c -c public ${VM_IP}:10005 1.3.6.1.2.1.1.5.0 2>/dev/null" +check "SNMPv2c response (port 10009)" "snmpget -v2c -c public ${VM_IP}:10009 1.3.6.1.2.1.1.5.0 2>/dev/null" +check "nflow-generator service active" "systemctl is-active --quiet snmpsim-netflow" +check "trap timer active" "systemctl is-active --quiet snmpsim-traps.timer" + +echo "" +echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}" +echo -e " Checks passed: ${GREEN}${PASS}${NC} Checks failed: ${RED}${FAIL}${NC}" +echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}" + +# ───────────────────────────────────────────────────────────────────────────── +# Final summary +# ───────────────────────────────────────────────────────────────────────────── +echo "" +echo -e "${BLUE}╔══════════════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ Installation complete ║${NC}" +echo -e "${BLUE}╚══════════════════════════════════════════════════════════╝${NC}" +echo "" +echo -e " Install directory : ${CYAN}${INSTALL_DIR}${NC}" +echo -e " VM IP : ${CYAN}${VM_IP}${NC}" +echo -e " Trap destination : ${CYAN}${TRAP_HOST}:${TRAP_PORT}${NC}" +echo -e " NetFlow dest : ${CYAN}${NETFLOW_HOST}:${NETFLOW_PORT}${NC}" +echo "" + echo -e " SNMP devices : ${CYAN}${VM_IP}:10000 - ${VM_IP}:10009${NC}" + echo -e " SNMP version : ${CYAN}v2c${NC}" + echo -e " Community string : ${CYAN}public${NC}" +echo "" +echo -e " ${YELLOW}Add each device to SevOne:${NC}" +echo -e " IP: ${VM_IP}, SNMP Version: v2c, Community: public, Port: 10000 through 10009" +echo "" +echo -e " ${YELLOW}Useful commands:${NC}" +echo -e " ${INSTALL_DIR}/start-sim.sh" +echo -e " ${INSTALL_DIR}/stop-sim.sh" +echo -e " ${INSTALL_DIR}/restart-sim.sh" +echo -e " ${INSTALL_DIR}/trap-generator.sh # send one trap now" +echo -e " systemctl status snmpsim-netflow" +echo -e " systemctl status snmpsim-traps.timer" +echo -e " tail -f ${INSTALL_DIR}/logs/snmpsim.log" +echo "" diff --git a/install-snmpsim-lab.sh:Zone.Identifier b/install-snmpsim-lab.sh:Zone.Identifier new file mode 100644 index 0000000000000000000000000000000000000000..d6c1ec682968c796b9f5e9e080cc6f674b57c766 GIT binary patch literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x +# or run continuously: watch -n 30 ./trap-generator.sh +# or via cron: */5 * * * * /opt/snmpsim-lab/trap-generator.sh +# ============================================================================= + +SEVONE_IP="${1:-10.0.0.1}" +SEVONE_PORT="162" +COMMUNITY="public" + +# The 10 simulated devices (hostname -> IP:port -> sysObjectID) +# We spoof the agent address so SevOne sees the trap as coming from each device +declare -A DEVICES +DEVICES["core-sw-01"]="9.60.154.118:10000:1.3.6.1.4.1.9.1.282" +DEVICES["core-sw-02"]="9.60.154.118:10001:1.3.6.1.4.1.9.1.282" +DEVICES["dist-sw-01"]="9.60.154.118:10002:1.3.6.1.4.1.9.1.659" +DEVICES["dist-sw-02"]="9.60.154.118:10003:1.3.6.1.4.1.9.1.659" +DEVICES["access-sw-01"]="9.60.154.118:10004:1.3.6.1.4.1.9.1.516" +DEVICES["access-sw-02"]="9.60.154.118:10005:1.3.6.1.4.1.9.1.516" +DEVICES["access-sw-03"]="9.60.154.118:10006:1.3.6.1.4.1.9.1.516" +DEVICES["rtr-01"]="9.60.154.118:10007:1.3.6.1.4.1.9.1.1861" +DEVICES["rtr-02"]="9.60.154.118:10008:1.3.6.1.4.1.9.1.1861" +DEVICES["fw-01"]="9.60.154.118:10009:1.3.6.1.4.1.9.1.1902" + +# Interface names per device type +SWITCH_IFACES=("GigabitEthernet0/1" "GigabitEthernet0/2" "GigabitEthernet0/3" "GigabitEthernet0/4" "GigabitEthernet0/5" "TenGigabitEthernet1/1" "TenGigabitEthernet1/2") +ROUTER_IFACES=("GigabitEthernet0/0/0" "GigabitEthernet0/0/1" "Serial0/1/0") +FW_IFACES=("GigabitEthernet0/0" "GigabitEthernet0/1" "GigabitEthernet0/2" "Management0/0") + +# Pick a random element from an array +random_element() { + local arr=("$@") + echo "${arr[$RANDOM % ${#arr[@]}]}" +} + +# Pick a random device name +random_device() { + local keys=("${!DEVICES[@]}") + echo "${keys[$RANDOM % ${#keys[@]}]}" +} + +# Get interface list for a device +get_ifaces() { + local device="$1" + if [[ "$device" == rtr* ]]; then + echo "${ROUTER_IFACES[@]}" + elif [[ "$device" == fw* ]]; then + echo "${FW_IFACES[@]}" + else + echo "${SWITCH_IFACES[@]}" + fi +} + +# ------------------------------------------------------- +# TRAP SENDERS +# Each function sends one specific trap type. +# snmptrap syntax: +# snmptrap -v2c -c [varbinds...] +# ------------------------------------------------------- + +send_linkDown() { + local device="$1" + local info="${DEVICES[$device]}" + local agent_ip=$(echo "$info" | cut -d: -f1) + local ifaces=($(get_ifaces "$device")) + local iface=$(random_element "${ifaces[@]}") + local ifindex=$((RANDOM % ${#ifaces[@]} + 1)) + local uptime=$((RANDOM % 9000000 + 100000)) + + echo "[$(date '+%H:%M:%S')] linkDown | $device | $iface (ifIndex $ifindex)" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.6.3.1.1.5.3 \ + 1.3.6.1.2.1.2.2.1.1.$ifindex i $ifindex \ + 1.3.6.1.2.1.2.2.1.2.$ifindex s "$iface" \ + 1.3.6.1.2.1.2.2.1.7.$ifindex i 2 \ + 1.3.6.1.2.1.2.2.1.8.$ifindex i 2 +} + +send_linkUp() { + local device="$1" + local info="${DEVICES[$device]}" + local agent_ip=$(echo "$info" | cut -d: -f1) + local ifaces=($(get_ifaces "$device")) + local iface=$(random_element "${ifaces[@]}") + local ifindex=$((RANDOM % ${#ifaces[@]} + 1)) + + echo "[$(date '+%H:%M:%S')] linkUp | $device | $iface (ifIndex $ifindex)" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.6.3.1.1.5.4 \ + 1.3.6.1.2.1.2.2.1.1.$ifindex i $ifindex \ + 1.3.6.1.2.1.2.2.1.2.$ifindex s "$iface" \ + 1.3.6.1.2.1.2.2.1.7.$ifindex i 1 \ + 1.3.6.1.2.1.2.2.1.8.$ifindex i 1 +} + +send_coldStart() { + local device="$1" + + echo "[$(date '+%H:%M:%S')] coldStart | $device" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.6.3.1.1.5.1 +} + +send_warmStart() { + local device="$1" + + echo "[$(date '+%H:%M:%S')] warmStart | $device" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.6.3.1.1.5.2 +} + +send_ospfNbrStateChange() { + local device="$1" + # Only routers run OSPF + if [[ "$device" != rtr* ]]; then return; fi + + local neighbor="10.$((RANDOM % 255)).$((RANDOM % 255)).$((RANDOM % 255))" + # OSPF states: 1=down 2=attempt 3=init 4=twoWay 5=exchangeStart 6=exchange 7=loading 8=full + local state=$((RANDOM % 8 + 1)) + local state_names=("" "down" "attempt" "init" "twoWay" "exchangeStart" "exchange" "loading" "full") + + echo "[$(date '+%H:%M:%S')] ospfNbrStateChange | $device | neighbor $neighbor -> ${state_names[$state]}" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.4.1.9.10.99.0.1 \ + 1.3.6.1.2.1.14.10.1.1.$neighbor a "$neighbor" \ + 1.3.6.1.2.1.14.10.1.6.$neighbor i $state \ + 1.3.6.1.2.1.14.4.1.2.0 s "0.0.0.0" +} + +send_bgpEstablished() { + local device="$1" + # Only routers run BGP + if [[ "$device" != rtr* ]]; then return; fi + + local peer="192.168.$((RANDOM % 255)).$((RANDOM % 255))" + local remote_as=$((RANDOM % 65000 + 1000)) + + echo "[$(date '+%H:%M:%S')] bgpEstablished | $device | peer $peer AS$remote_as" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.2.1.15.7.1 \ + 1.3.6.1.2.1.15.3.1.7.$peer i 6 \ + 1.3.6.1.2.1.15.3.1.9.$peer i $remote_as +} + +send_bgpBackwardTransition() { + local device="$1" + if [[ "$device" != rtr* ]]; then return; fi + + local peer="192.168.$((RANDOM % 255)).$((RANDOM % 255))" + local remote_as=$((RANDOM % 65000 + 1000)) + # States: 1=idle 2=connect 3=active 4=openSent 5=openConfirm 6=established + local state=$((RANDOM % 5 + 1)) + + echo "[$(date '+%H:%M:%S')] bgpBackwardTransition | $device | peer $peer -> state $state" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.2.1.15.7.2 \ + 1.3.6.1.2.1.15.3.1.7.$peer i $state \ + 1.3.6.1.2.1.15.3.1.9.$peer i $remote_as +} + +send_cpuThreshold() { + local device="$1" + local cpu=$((RANDOM % 40 + 60)) # 60-99% + + echo "[$(date '+%H:%M:%S')] cpuThreshold | $device | CPU ${cpu}%" + + snmptrap -v2c -c "$COMMUNITY" "$SEVONE_IP:$SEVONE_PORT" \ + "" \ + 1.3.6.1.4.1.9.9.109.0.1 \ + 1.3.6.1.4.1.9.9.109.1.1.1.1.3.1 i $cpu \ + 1.3.6.1.4.1.9.9.109.1.1.1.1.4.1 i $((cpu - RANDOM % 10)) \ + 1.3.6.1.4.1.9.9.109.1.1.1.1.5.1 i $((cpu - RANDOM % 15)) +} + +# ------------------------------------------------------- +# MAIN - pick random trap type and random device +# ------------------------------------------------------- + +# Weighted trap type selection +# Link traps are most common in real networks +TRAP_TYPES=( + "linkDown" + "linkDown" + "linkDown" + "linkUp" + "linkUp" + "linkUp" + "coldStart" + "warmStart" + "ospfNbrStateChange" + "ospfNbrStateChange" + "bgpEstablished" + "bgpBackwardTransition" + "cpuThreshold" +) + +# Pick random trap type and device +trap_type=$(random_element "${TRAP_TYPES[@]}") +device=$(random_device) + +echo "--- Sending trap: $trap_type from $device to $SEVONE_IP:$SEVONE_PORT ---" + +case "$trap_type" in + linkDown) send_linkDown "$device" ;; + linkUp) send_linkUp "$device" ;; + coldStart) send_coldStart "$device" ;; + warmStart) send_warmStart "$device" ;; + ospfNbrStateChange) send_ospfNbrStateChange "$device" ;; + bgpEstablished) send_bgpEstablished "$device" ;; + bgpBackwardTransition) send_bgpBackwardTransition "$device" ;; + cpuThreshold) send_cpuThreshold "$device" ;; +esac diff --git a/trap-generator.sh:Zone.Identifier b/trap-generator.sh:Zone.Identifier new file mode 100644 index 0000000000000000000000000000000000000000..d6c1ec682968c796b9f5e9e080cc6f674b57c766 GIT binary patch literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x/dev/null; then + systemctl stop "$svc" + ok "Stopped: $svc" + else + skip "Not running: $svc" + fi + + if systemctl is-enabled --quiet "$svc" 2>/dev/null; then + systemctl disable "$svc" + ok "Disabled: $svc" + else + skip "Not enabled: $svc" + fi +done + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 2: Kill any remaining processes" +# ───────────────────────────────────────────────────────────────────────────── + +if pgrep -f snmpsim-command-responder &>/dev/null; then + pkill -f snmpsim-command-responder + ok "Killed remaining snmpsim processes" +else + skip "No snmpsim processes running" +fi + +if pgrep -f nflow-generator &>/dev/null; then + pkill -f nflow-generator + ok "Killed nflow-generator process" +else + skip "No nflow-generator process running" +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 3: Remove systemd unit files" +# ───────────────────────────────────────────────────────────────────────────── + +for f in "${SERVICE_FILES[@]}"; do + if [[ -f "$f" ]]; then + rm -f "$f" + ok "Removed: $f" + else + skip "Not found: $f" + fi +done + +systemctl daemon-reload +ok "systemd daemon reloaded" + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 4: Remove install directory" +# ───────────────────────────────────────────────────────────────────────────── + +if [[ -d "$INSTALL_DIR" ]]; then + rm -rf "$INSTALL_DIR" + ok "Removed: $INSTALL_DIR" +else + skip "Not found: $INSTALL_DIR" +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 5: Remove PID directory and cache" +# ───────────────────────────────────────────────────────────────────────────── + +if [[ -d "$PID_DIR" ]]; then + rm -rf "$PID_DIR" + ok "Removed: $PID_DIR" +else + skip "Not found: $PID_DIR" +fi + +if [[ -d "$CACHE_DIR" ]]; then + rm -rf "$CACHE_DIR" + ok "Removed: $CACHE_DIR" +else + skip "Not found: $CACHE_DIR" +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 6: snmpsim pip package" +# ───────────────────────────────────────────────────────────────────────────── + +if [[ $KEEP_PIP -eq 1 ]]; then + skip "Keeping snmpsim pip package (--keep-pip)" +else + if pip3 show snmpsim-lextudio &>/dev/null 2>&1; then + pip3 uninstall -y snmpsim-lextudio + ok "Uninstalled: snmpsim-lextudio" + elif pip3 show snmpsim &>/dev/null 2>&1; then + pip3 uninstall -y snmpsim + ok "Uninstalled: snmpsim" + else + skip "snmpsim pip package not found" + fi + + # Remove leftover snmpsim binaries + for bin in snmpsim-command-responder snmpsim-record-mibs; do + path=$(command -v "$bin" 2>/dev/null || true) + if [[ -n "$path" ]]; then + rm -f "$path" + ok "Removed binary: $path" + fi + done +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 7: snmpsim system user" +# ───────────────────────────────────────────────────────────────────────────── + +if [[ $KEEP_USER -eq 1 ]]; then + skip "Keeping snmpsim user (--keep-user)" +else + if id "$SNMPSIM_USER" &>/dev/null; then + userdel "$SNMPSIM_USER" + ok "Removed user: $SNMPSIM_USER" + else + skip "User not found: $SNMPSIM_USER" + fi +fi + +# ───────────────────────────────────────────────────────────────────────────── +section "Step 8: Remove cron entries (if any)" +# ───────────────────────────────────────────────────────────────────────────── + +if crontab -l 2>/dev/null | grep -q "trap-generator"; then + crontab -l 2>/dev/null | grep -v "trap-generator" | crontab - + ok "Removed trap-generator cron entry" +else + skip "No trap-generator cron entry found" +fi + +# ───────────────────────────────────────────────────────────────────────────── +# Done +# ───────────────────────────────────────────────────────────────────────────── +echo "" +echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ Uninstall complete ║${NC}" +echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}" +echo "" + +KEPT=() +[[ $KEEP_PIP -eq 1 ]] && KEPT+=("snmpsim pip package") +[[ $KEEP_USER -eq 1 ]] && KEPT+=("snmpsim system user") + +if [[ ${#KEPT[@]} -gt 0 ]]; then + echo -e " Kept (by request):" + for item in "${KEPT[@]}"; do + echo -e " ${YELLOW}${item}${NC}" + done + echo "" +fi + +echo -e " Not removed (system packages):" +echo -e " Python 3, pip, Go, git, net-snmp-utils" +echo "" +echo -e " Ready for a fresh install:" +echo -e " ${CYAN}sudo ./install-snmpsim-lab.sh --vm-ip --trap-host --netflow-host ${NC}" +echo "" diff --git a/uninstall-snmpsim-lab.sh:Zone.Identifier b/uninstall-snmpsim-lab.sh:Zone.Identifier new file mode 100644 index 0000000000000000000000000000000000000000..d6c1ec682968c796b9f5e9e080cc6f674b57c766 GIT binary patch literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x