SNMP-Trap-Tools(at)Work

SNMP-Trap-Tools(at)Work / Teknisk beskrivelse

SNMP Trap Sender

Simulator som sender SNMP-traps og -informs som en nettverksenhet, med web-GUI

Versjon 1.1.0Revidert 2026-10-02

1. Sammendrag

SNMP Trap Sender simulerer en nettverksenhet som sender SNMP-varsler. Den kan sende en periodisk heartbeat, simulere en omstart, ta et nettverksgrensesnitt ned og opp, og sende testmeldinger i fritekst eller salver for lasttesting. Varslene sendes som SNMPv1-traps, SNMPv2c-traps eller -informs, eller SNMPv3-traps eller -informs, til én eller flere trap-mottakere.

Simulatoren har sin egen MIB, TRAPSIM-MIB. Lastet opp til en trap-mottaker gjør den at mottakeren viser hvert simulert varsel med navn. Standardhendelsene bruker SNMPv2-MIB (coldStart, warmStart) og IF-MIB (linkDown, linkUp), som alle mottakere allerede kjenner.

Den kjører som en systemd-tjeneste på Rocky Linux 9, enten i en VM eller i en Proxmox LXC-container. Den styres fra et web-GUI på engelsk eller norsk og installeres fra én fil uten tilgang til internett. Den er motstykket til SNMP Trap Receiver 2.0.

2. Formål og omfang

Simulerer

Én nettverksenhet med navn, SNMPv1-agentadresse, SNMPv3-engine-ID, sysUpTime som starter på nytt ved en simulert omstart, og et løpenummer for heartbeat

Sender

SNMPv1 Trap-PDU, SNMPv2c Trap og InformRequest, SNMPv3 Trap og InformRequest (USM: noAuthNoPriv, authNoPriv, authPriv; MD5, SHA-1, SHA-2; DES, 3DES, AES-128/192/256) over UDP/IPv4 og UDP/IPv6

Hendelser

Heartbeat, omstart (varslet, cold/warm start, enheten oppe), grensesnitt nede/oppe (IF-MIB og TRAPSIM-MIB) og testmelding

Scenarier

Omstart med nedetid, grensesnitt som blafrer (gjentatt nede/oppe) og salve med fast takt

Planer

En hvilken som helst hendelse gjentatt hvert N. sekund, for eksempel en heartbeat hvert 60. sekund

Gjør ikke

Svarer ikke på SNMP GET, er ikke en ekte agent og tar ikke imot traps

3. Arkitektur

                Rocky Linux 9 (VM eller Proxmox LXC)
 ┌───────────────────────────────────────────────────────────────┐
 │  snmp-trap-sender.service  (bruker trapsender)                │
 │                                                               │
 │  ┌──────────────┐  ┌────────────────────────────────────────┐ │
 │  │ Flask-app på │  │ Motor: tråd + asyncio-hendelsesløkke   │ │
 │  │ Waitress     ├─►│  send()     planer        scenarier    │ │
 │  │ (8 tråder)   │  │  én pysnmp SnmpEngine per mottaker     │ │
 │  └──────▲───────┘  └──────────┬───────────────────┬─────────┘ │
 │         │                     │                   │           │
 │  state.json (enhet,           │ historikk (2000)  │           │
 │  mottakere, planer)           │ + sent_traps.jsonl│           │
 └─────────┼─────────────────────┼───────────────────┼───────────┘
           │ HTTP 8080           │ UDP → 162         │ UDP → 162
     ┌─────┴─────┐       ┌───────┴────────┐  ┌───────┴────────┐
     │ nettleser │       │ trap-mottaker 1│  │ trap-mottaker 2│
     └───────────┘       └────────────────┘  └────────────────┘

Alt kjører i én prosess. All SNMP-trafikk går i én asyncio-hendelsesløkke i en egen tråd. Web-forespørsler gir arbeidet til den løkka med run_coroutine_threadsafe og venter på svaret. Planer og scenarier er asyncio-oppgaver i den samme løkka, så de blokkerer aldri GUI-et.

Motoren har én pysnmp SnmpEngine per mottaker. Når flere mottakere deler én motor, sender det høynivå-API-et i pysnmp senere varsler også til mottakerne fra tidligere kall. Egne motorer hindrer det, og de lar også en v3-mottaker bruke enhetens egen engine-ID. En motor bygges på nytt når mottakeren eller engine-ID-en endres.

3.1. Moduler

Modul Ansvar

trapsender/events.py

Hendelseskatalogen: OID per hendelse, parametere, validering, og hvordan hver hendelse blir til variabelbindinger

trapsender/engine.py

Engine: sending, planer, scenarier, historikk, sendelogg og statistikk

trapsender/state.py

Simulert enhet, mottakere og planer i state.json; validering; hemmeligheter holdes utenfor GUI-et

trapsender/config.py

/etc/snmp-trap-sender/config.ini (web, grenser, lagring)

trapsender/web.py

Flask-GUI og JSON-API: innlogging, CSRF og sikkerhetshoder

trapsender/i18n.py

GUI-tekster på engelsk og norsk (196 nøkler; begge språkene må ha de samme nøklene)

trapsender/manual.py

Viser brukermanualen (docs/no/Brukermanual.md, docs/en/User-Manual.md) i GUI-et. En liten Markdown-tolker uten avhengigheter: all tekst HTML-escapes først, og lenker kan bare peke til #ankre eller http(s)

trapsender/selftest.py

Ende-til-ende-test med en midlertidig lokal mottaker

trapsender/main.py

Kommandolinje: serve, set-password, add-target, send, selftest, version

trapsender/mib/TRAPSIM-MIB.mib

Simulatorens MIB

4. Hendelser

Hvert varsel starter med sysUpTime.0 (oppetiden til den simulerte enheten) og snmpTrapOID.0. For SNMPv1-mottakere legges snmpTrapAddress.0 til når en agentadresse er satt. pysnmp bruker den som feltet agent-addr i Trap-PDU-en (RFC 3584), og gjør om varslets OID til feltene enterprise, generic-trap og specific-trap i v1.

Hendelse Varsel Variabler

Heartbeat

TRAPSIM-MIB::tsimHeartbeat

tsimDeviceName, tsimSequence (1, 2, 3 … siden forrige omstart), tsimInterval

Omstart varslet

TRAPSIM-MIB::tsimReboot

tsimDeviceName, tsimRebootReason, tsimDowntime, tsimMessage

Enheten oppe

TRAPSIM-MIB::tsimDeviceUp

tsimDeviceName, tsimRebootReason, tsimDowntime

Grensesnitt nede / oppe

TRAPSIM-MIB::tsimInterfaceDown / tsimInterfaceUp

tsimDeviceName, tsimIfIndex, tsimIfName, tsimIfAlias, tsimSeverity, tsimMessage

Testmelding

TRAPSIM-MIB::tsimTestMessage

tsimDeviceName, tsimSeverity, tsimMessage

coldStart / warmStart

SNMPv2-MIB::coldStart / warmStart

–

linkDown / linkUp

IF-MIB::linkDown / linkUp

ifIndex.n, ifAdminStatus.n = up(1), ifOperStatus.n = down(2) / up(1), pluss ifName.n og ifAlias.n, slik mange leverandører gjør

Parameterne valideres på serveren: ifIndex 1…2147483647, nedetid 0…86400 sekunder, alvorlighet og årsak fra oppramsingene i MIB-en, grensesnittnavn og beskrivelse opptil 64 tegn og melding opptil 255.

5. TRAPSIM-MIB

MIB-en er SMIv2 og består smilint -l 6 (libsmis strengeste nivå) uten merknader. Den ligger under enterprise 32473, som IANA har reservert for dokumentasjon og eksempler (RFC 5612). Den kolliderer derfor aldri med OID-ene til en ekte leverandør.

1.3.6.1.4.1.32473            example (RFC 5612)
 └─ .1                       trapSimRoot
     └─ .1                   trapSimMIB (MODULE-IDENTITY)
         ├─ .0               trapSimNotifications
         │   ├─ .1  tsimHeartbeat       .4  tsimInterfaceDown
         │   ├─ .2  tsimReboot          .5  tsimInterfaceUp
         │   ├─ .3  tsimDeviceUp        .6  tsimTestMessage
         ├─ .1               trapSimObjects (accessible-for-notify)
         │   ├─ .1 tsimDeviceName   .5 tsimRebootReason   .9  tsimIfAlias
         │   ├─ .2 tsimSequence     .6 tsimDowntime       .10 tsimMessage
         │   ├─ .3 tsimInterval     .7 tsimIfIndex
         │   └─ .4 tsimSeverity     .8 tsimIfName
         └─ .2               trapSimConformance (krav og grupper)

Oppramsinger: TrapSimSeverity = clear(1), info(2), warning(3), minor(4), major(5), critical(6); TrapSimRebootReason = operatorRequest(1), softwareUpgrade(2), powerFailure(3), watchdog(4), crash(5), scheduled(6).

Fila ligger i Documentation/MIB/, i Offline-Installation/ og i det installerte programmet (/opt/snmp-trap-sender/app/trapsender/mib/). Den kan også lastes ned fra fanen Enhet.

6. Scenarier

Omstart

Sender tsimReboot (årsak, nedetid). Så er enheten «nede» i nedetiden: planlagte varsler pauser, og GUI-et viser Enheten starter på nytt. Etterpå starter sysUpTime og heartbeat-nummeret på 0 igjen, og scenariet sender coldStart (eller warmStart) og tsimDeviceUp. Det er den rekkefølgen en ekte enhet gir, så den egner seg godt til å teste regler på mottakersiden.

Grensesnitt som blafrer

Antall ganger: grensesnitt nede, vent nede sekunder, grensesnitt oppe, vent oppe sekunder. Varslene er linkDown/linkUp fra IF-MIB, tsimInterfaceDown/Up fra TRAPSIM, eller begge.

Salve

Antall varsler av én hendelse med takt per sekund. Takten følger en fast tidsplan, så én treg sending strekker ikke ut hele salven. Grenser: max_rate (standard 500/s) og max_burst (standard 100 000).

Scenarier sender til alle aktive mottakere, kjører i bakgrunnen, viser fremdriften i GUI-et og kan avbrytes. Avbrytes en omstart, avsluttes «nede»-tilstanden med en gang.

7. Planer

En plan gjentar én hendelse med faste parametere hvert intervall sekund (1…86 400) til alle aktive mottakere eller til utvalgte mottakere. Første sending skjer når planen startes. En ny installasjon har en Heartbeat-plan som er slått av. Installeren kan slå den på med --heartbeat N. Planer lagres i state.json og starter igjen sammen med tjenesten.

8. Mottakere

En mottaker er én trap-mottaker: vertsnavn eller IP-adresse (IPv4 eller IPv6), UDP-port, SNMP-versjon, trap eller inform, community (v1/v2c) eller USM-bruker med protokoller og nøkler (v3), tidsavbrudd og nye forsøk (brukes av informs), og en bryter for aktiv.

  • Traps sendes uten kvittering. Sendt betyr at pakken forlot senderen, ikke at en mottaker godtok den.

  • Informs kvitteres av mottakeren. Mangler kvitteringen etter tidsavbrudd og nye forsøk, vises det som feil. Informs er måten å sjekke at mottakeren faktisk fikk varselet.

  • SNMPv3-traps sendes med den simulerte enhetens engine-ID som autoritativ motor. Mottakeren må ha den samme brukeren for den engine-ID-en. GUI-et viser linja som skal inn i konfigurasjonen til SNMP Trap Receiver ([v3user:NAVN] … engine_id = …).

Communities og v3-nøkler lagres i state.json (modus 0600, bare lesbar for tjenestebrukeren). De sendes aldri tilbake til nettleseren. Redigeringsskjemaet viser «uendret» og beholder den lagrede verdien når feltet står tomt.

9. Simulert enhet

Navn

Sendes som tsimDeviceName. Installeren setter det til vertsnavnet.

Agentadresse

Valgfri IPv4-adresse som legges i SNMPv1-traps som agent-addr, slik at trapen ser ut til å komme fra en annen enhet

sysUpTime

Teller fra forrige (simulerte) omstart i hundredels sekunder og starter på nytt ved 232

Heartbeat-nummer

+1 per heartbeat (samme nummer til alle mottakere), tilbake til 0 ved en omstart. En mottaker kan se tapte heartbeats av hull i rekka.

Engine-ID

Format etter RFC 3411: 80007ed905 (enterprise 32473, oktetter) + 6 tilfeldige byte. Lages ved første oppstart. Ny engine-ID i GUI-et lager en ny.

10. Web-GUI og API

10.1. Sider

Send

Hurtigknapper (Heartbeat, Omstart, Grensesnitt nede, Grensesnitt oppe), et skjema for en hvilken som helst hendelse med parametere og valgte mottakere, scenarier, og pågående eller siste scenarier med Avbryt

Planer

Tabell med av/på-bryter per plan; opprett, endre og slett

Mottakere

Tabell; opprett, endre, slett og Test (sender en testmelding til den mottakeren og viser resultatet)

Historikk

De siste 2000 varslene med tid, varsel, mottaker, versjon, kilde (manuell, plan, scenario, CLI) og resultat; filter; nedlasting av loggen

Enhet

Simulert enhet, oppetid, heartbeat-nummer, omstarter, engine-ID og tellere; nedlasting av TRAPSIM-MIB

Topplinje

Enheten er oppe / Enheten starter på nytt (n s), enhetsnavn og oppetid, EN/NO og Logg ut

Bunnlinje

Versjon, Lisenser, Lisensrapport og Third-party notices

10.2. Endepunkter

Sti Metode Formål

/login, /logout, /lang/<en|no>

Sesjon og språk

/healthz

GET

{"ok": true, "version": "1.1.0"} uten innlogging

/manual

GET

Brukermanualen på valgt språk, med innholdsfortegnelse og søk. Brukermanual-knappen lenker til /manual#<fane>

/api/meta

GET

Hendelseskatalog, oppramsinger og grenser

/api/status

GET

Enhet, oppetid, nede-tilstand, tellere og scenarier

/api/send

POST

{"event", "params", "targets"} → resultat per mottaker

/api/scenario

POST

{"kind": "reboot|flap|burst", "params"}

/api/scenario/<id>/cancel

POST

Avbryt

/api/targets

GET/POST

Liste (uten hemmeligheter) / opprett eller oppdater

/api/targets/<id>/test, …/delete

POST

Test / slett

/api/jobs

GET/POST

Planer

/api/jobs/<id>/enable, …/delete

POST

På/av / slett

/api/history?since=N

GET

Sendte varsler med id > N

/api/device, /api/device/engine-id

POST

Enhetsnavn og agentadresse / ny engine-ID

/download/mib, /download/log

GET

TRAPSIM-MIB / sent_traps.jsonl

/api/legal, /api/legal/<report|notices>

GET

Lisensdata

10.3. Sikkerhet

Samme modell som SNMP Trap Receiver: ett passord (scrypt-hash), sesjonskapsel med HttpOnly og SameSite=Strict, forsinkelse etter 5 feilede innlogginger, CSRF-token på hver POST, Content-Security-Policy: default-src 'self' uten innebygd skript, X-Frame-Options: DENY og nosniff. Alle verdier escapes før de vises, og forespørsler er begrenset til 256 KB.

Senderen kan brukes til å oversvømme en mottaker (salve). Tilgangen til GUI-et bør derfor begrenses til dem som kjører tester.

11. Konfigurasjon

/etc/snmp-trap-sender/config.ini (root:trapsender, 0640). Start tjenesten på nytt etter en endring.

Seksjon Nøkkel Standard Betydning

[web]

host, port

0.0.0.0, 8080

Adressen til web-GUI-et

auth, password_hash, secret_key

true, settes av installeren

Innlogging

default_language

en

en eller no

session_hours

12

[sender]

max_rate

500

Øvre grense for salver, per sekund

max_burst

100000

Største salve

history_size

2000

Varsler som holdes for fanen Historikk

[storage]

data_dir

/var/lib/snmp-trap-sender

state.json

log_dir

/var/log/snmp-trap-sender

sent_traps.jsonl

log_max_mb, log_backups

20, 5

Loggrotasjon

Mottakere, planer og enheten endres i GUI-et og lagres i state.json.

12. Loggformat

Hver linje i sent_traps.jsonl er ett sendt varsel:

{"id": 42, "time": "2026-10-02 12:20:53", "ts": 1790936453.2,
 "event": "sim_if_down", "trap": "TRAPSIM-MIB::tsimInterfaceDown",
 "origin": "manual", "target": "Receiver", "target_id": "1cce00181e",
 "dest": "172.16.101.83:162", "version": "v2c", "notify": "trap",
 "params": {"if_index": 1, "if_name": "GigabitEthernet0/1",
            "if_alias": "Uplink", "severity": "major",
            "message": "Fibre cut on uplink"},
 "result": "sent", "error": "", "ms": 1.4}

result er sent, acknowledged (inform besvart) eller error.

13. Installasjon og drift

13.1. Filer

Sti

Eier, modus

Innhold

/opt/snmp-trap-sender/

root

Program, Python-miljø og dokumentasjon; .previous/ etter en oppgradering

/etc/snmp-trap-sender/config.ini

root:trapsender, 0640

Konfigurasjon

/var/lib/snmp-trap-sender/state.json

trapsender, 0600

Enhet, mottakere (med hemmeligheter) og planer

/var/log/snmp-trap-sender/

trapsender, 0750

sent_traps.jsonl og rotasjoner

/usr/local/bin/snmp-trap-sender

root, 0755

Kommandolinje

13.2. systemd og LXC

Tjenesten kjører som systembrukeren trapsender uten kapabiliteter. Sending til port 162 krever ingen rettigheter, fordi kildeporten er en tilfeldig høy port. Selve enheten setter bare NoNewPrivileges, et tomt CapabilityBoundingSet og UMask=0027, og det virker i alle containere.

Mer sandkasse ligger i en drop-in-fil, snmp-trap-sender.service.d/hardening.conf (ProtectSystem=strict, PrivateTmp, PrivateDevices, ProtectKernel*, RestrictNamespaces, RestrictAddressFamilies, …). Den krever mount-navnerom. De virker i VM-er og i Proxmox LXC-containere med nesting slått på, som er standard for nye uprivilegerte containere. Uten nesting stopper systemd tjenesten med status=226/NAMESPACE. Installeren oppdager dette, fjerner drop-in-fila, starter på nytt og sier ifra. --no-hardening hopper over drop-in-fila fra starten.

Tjenesten lytter ikke på noen SNMP-port, så bare web-porten må være åpen.

13.3. Kommandolinje

snmp-trap-sender serve

Kjør (brukes av systemd)

snmp-trap-sender set-password [--password-stdin | --generate]

Web-passord (root)

snmp-trap-sender add-target VERT [--port N] [--version v1|v2c] [--community C] [--heartbeat N]

Legg til en mottaker, og start tjenesten på nytt etterpå

snmp-trap-sender send HENDELSE [-p navn=verdi …] [-t MOTTAKER_ID]

Send én hendelse nå, f.eks. send link_down -p if_index=3 -p if_name=Gi0/3. Hendelser: heartbeat, sim_reboot, sim_device_up, sim_if_down, sim_if_up, test_message, cold_start, warm_start, link_down, link_up

snmp-trap-sender selftest

Ende-til-ende-test (se Verifisering)

snmp-trap-sender version

13.4. Offline-installeren

Samme oppbygging som for SNMP Trap Receiver. Et bash-hode kontrollerer SHA-256 til arkivet som ligger bakerst med python3, pakker det ut med det trygge datafilteret i tarfile og starter install.sh. Det skriptet kontrollerer operativsystem, Python 3.9, arkitektur, glibc 2.34 eller nyere og sjekksummene til filene. Så bygger det et virtuelt miljø bare fra wheel-filene som følger med (uten nett), og installerer program, bruker, konfigurasjon og systemd-enhet. Eventuelt legger det til en mottaker og en heartbeat. Til slutt starter det tjenesten, venter på /healthz og kjører selvtesten. En oppgradering beholder konfigurasjon, state.json og logger, og flytter forrige program til .previous/.

build-offline-installer.sh bygger installeren på en hvilken som helst maskin med internett. Den laster ned wheel-filene i Application/requirements.lock for CPython 3.9 på x86_64 og aarch64, genererer lisensfilene og GPL-skanningen, kontrollerer en offline-installasjon med Python 3.9 hvis byggemaskinen har det, og kjører pip-audit hvis det er installert.

14. Verifisering

snmp-trap-sender selftest starter en pysnmp-mottaker på en ledig port på 127.0.0.1 (community selftest og én v3-bruker med SHA-256/AES-128 for senderens engine-ID) og bruker midlertidige mapper. Den kontrollerer:

  • at state.json har modus 0600, at fire mottakere (v1, v2c trap, v2c inform, v3 authPriv) lagres, og at en ugyldig vert avvises;

  • at hver av de 10 hendelsene kommer fram via alle fire mottakerne med riktig snmpTrapOID, at versjonene v1, v2c og v3 kommer inn, at informs kvitteres, at agentadressen i v1 er den simulerte, at heartbeat har enhetsnavn og nummer 1, og at linkDown har ifIndex, ifAdminStatus = up og ifOperStatus = down;

  • at omstartsscenariet gir tsimReboot → coldStart → tsimDeviceUp og nullstiller sysUpTime, heartbeat-nummer og omstartstelleren; at blafring ×2 med begge varseltyper gir 8 varsler; og at en salve på 40 med 100/s tar mellom 0,3 og 3 sekunder og leverer alle 40;

  • at en heartbeat-plan hvert sekund gir stigende nummer, at historikk og logg skrives, og at en inform til en port uten mottaker gir feil;

  • web: innlogging, feil passord, engelsk som standard, CSRF, sending via API-et, avvisning av ugyldige parametere, ingen hemmeligheter i mottakerlista, at nøklene beholdes når en mottaker endres, start av scenario, nedlasting av MIB, brukermanualen på engelsk og norsk, norsk side, sikkerhetshoder, og lisensinventar og -dokumenter på begge språk.

Installeren kjører selvtesten etter hver installasjon (46 sjekker).

15. Begrensninger

  • Bare HTTP. Sett en omvendt proxy med TLS foran eller bruk SSH-tunnel over nett man ikke stoler på.

  • Én simulert enhet per installasjon. For flere enheter kjører du flere containere.

  • Simulatoren svarer ikke på SNMP GET, så en mottaker som poller enheten etter en trap, får ikke svar.

  • Målt på én kjerne: rundt 1000 v2c-traps i sekundet i en salve. pysnmp-motoren er begrensningen.

16. Lisenser

Alle tredjepartskomponentene har tillatende lisenser. Se Lisensrapport.md, THIRD_PARTY_NOTICES.txt og third_party_inventory.json. TRAPSIM-MIB er en del av dette prosjektet og kan brukes, kopieres og endres fritt.

Vedlegg A: Porter

Port

Protokoll

Retning

Formål

8080

TCP

inn

Web-GUI (kan endres)

162 (kan endres per mottaker)

UDP

ut

Traps og informs til mottakerne

Motstykket som tar imot og viser varslene: Teknisk beskrivelse for SNMP Trap Receiver