Jaroslav Ištvan
← Zpět na projekty

ISA — snmp2otel

C++17SNMPv2cBEROTLP/HTTP

Co to dělá

snmp2otel pravidelně posílá SNMPv2c GET dotazy na zadaného agenta a hodnoty, které dostane zpátky, přeposílá dál jako OpenTelemetry metriky — most mezi starým světem síťového monitoringu (SNMP) a moderním observability stackem (OTEL).

Zajímá ho jen skalární OID (ty, co končí .0) — typicky čítače a měřidla z MIB tabulky agenta, ne celé podstromy.

Co je SNMP

Simple Network Management Protocol je standard ze začátku devadesátých let, kterým se dodnes vyčítají údaje z routerů, switchů, tiskáren nebo záložních zdrojů. Když někde visí graf vytížení firemní linky, je docela pravděpodobné, že data pod ním tekla přes SNMP.

  • Na spravovaném zařízení běží agent. Ten drží stromovou databázi hodnot (MIB) a každou hodnotu adresuje číselné OID — třeba 1.3.6.1.2.1.1.3.0 je doba běhu zařízení od zapnutí.
  • Manažer (v našem případě tenhle program) pošle přes UDP dotaz GetRequest se seznamem OID a agent odpoví toutéž strukturou doplněnou o hodnoty.
  • Verze 2c se autentizuje takzvaným community stringem, což je v podstatě heslo posílané v otevřené podobě. Do veřejné sítě se to nehodí, uvnitř spravované infrastruktury se to používá dodnes.
  • Pakety nejsou text ani JSON, ale binární ASN.1/BER— proto si projekt nese vlastní kodér a dekodér.

Koncovka .0 u OID znamená skalární hodnotu, tedy jedno konkrétní číslo, ne tabulku. Program schválně zpracovává jen tyhle — u tabulek by musel řešit procházení podstromu, což je jiná úloha.

Co je OpenTelemetry

OpenTelemetry je otevřený standard pro sběr telemetrie — metrik, trasování a logů — nezávislý na dodavateli. Řeší stav, kdy měl každý monitorovací nástroj vlastní formát i vlastního agenta a přechod jinam znamenal předělat instrumentaci celé aplikace.

  • Data se posílají protokolem OTLP; program používá jeho variantu přes HTTP s JSON tělem.
  • Na druhé straně obvykle stojí OTEL Collector, který data přijme a přepošle do libovolného backendu — Prometheus, Grafana, cokoliv. Odesílatel nemusí vědět, kam data nakonec doputují, a v tom je celý smysl.
  • Gauge je metrika, která má v daném okamžiku prostě nějakou hodnotu — teplota, počet spojení. Liší se tím od čítače, který jen monotónně roste.

Proč tedy stavět most: zařízení mluvící SNMP jsou všude a hned tak nezmizí, ale moderní observability stack mluví OTLP. Exportér je překladatel mezi těmi dvěma světy.

Vstup a výstup

Na vstupu je adresa agenta a soubor se seznamem OID (jedno na řádek, komentáře přes #), na výstupu JSON tělo požadavku, které program pošle na OTLP/HTTP endpoint kolektoru:

Spuštění + oids.txt
./snmp2otel -t 127.0.0.1 -o ./oids.txt \
  -e http://127.0.0.1:4318/v1/metrics -p 1161 -v

# oids.txt:
1.3.6.1.2.1.1.3.0
1.3.6.1.4.1.9999.1.1.0
1.3.6.1.4.1.9999.1.2.0
1.3.6.1.4.1.9999.1.3.0
Odeslané OTLP metriky (zkráceno)
{
  "resourceMetrics": [{
    "resource": { "attributes": [
      { "key": "service.name",
        "value": { "stringValue": "snmp2otel" } }
    ]},
    "scopeMetrics": [{
      "scope": { "name": "snmp2otel" },
      "metrics": [{
        "name": "snmp.1.3.6.1.2.1.1.3.0",
        "unit": "",
        "gauge": { "dataPoints": [{
          "timeUnixNano": "1732820000000000000",
          "asDouble": 483920
        }]}
      }]
    }]
  }]
}

Jak to funguje uvnitř

  1. CLI parser (cli.hpp) načte cíl, community string, port, timeout a interval.
  2. Soubor s OID se načte jednou a přeskočí prázdné i komentářové řádky a neskalární OID.
  3. Ručně napsaný BER encoder (ber.cpp) složí SNMPv2c GetRequest paket — žádná externí SNMP knihovna.
  4. UDP round-trip na agenta, s konfigurovatelným počtem opakování a timeoutem.
  5. BER decoder rozparsuje odpověď do typovaných VarBind hodnot (Integer, OctetString, OID, Counter32, Gauge32, TimeTicks, Counter64…).
  6. Číselné typy se převedou na OTLP Gauge metriky a pošlou přes cpp-httplib POST požadavkem.
  7. Celé se to opakuje každých -i sekund, dokud program běží.

Moduly

Projekt drží hlavičky v include/ a implementaci v src/. Každá dvojice odpovídá jedné vrstvě cesty od příkazové řádky až k odeslanému HTTP požadavku:

ModulZodpovědnost
berKódování a dekódování ASN.1/BER — délky, integery, octet stringy, OID, čtení TLV prvků
cliZpracování přepínačů do struktury Options, výpis usage
snmpSestavení GetRequest, parsování GetResponse, UDP round-trip s opakováním
otelRozebrání URL endpointu, sestavení OTLP JSON, odeslání přes HTTP
vlogVerbose logování přes jeden globální přepínač
mainNačtení souboru s OID a hlavní dotazovací smyčka
tests/Testy nad ber, snmp a otel (GoogleTest)
third_party/cpp-httplib a googletest

Řazení není náhodné: ber nezná SNMP, snmp nezná OTEL a otel neví, odkud se hodnoty vzaly. Závislosti vedou jedním směrem, takže každou vrstvu jde testovat zvlášť.

Datové struktury

StrukturaRole
ber::BytesAlias pro vektor bajtů — syrový obsah paketu
ber::TLVJeden přečtený prvek [tag][délka][hodnota] i s rozsahem hodnoty v bufferu
ber::ber_errorVýjimka při poškozeném nebo neočekávaném kódování
snmp::VarBindValueTypovaná hodnota — Integer, OctetString, OID, Counter32, Gauge32, TimeTicks, Counter64
snmp::VarBindDvojice OID a jeho hodnota
snmp::PDUTělo odpovědi: request-id, error-status, error-index a seznam VarBind
OptionsKonfigurace poskládaná z argumentů příkazové řádky
ParsedUrlRozebraný endpoint — schéma, host, port a cesta

Stojí za zmínku, že tříd tu skoro nenajdete — jsou to prosté struktury a volné funkce v namespacech. Je to záměr: program nemá víc implementací téhož rozhraní, mezi kterými by za běhu vybíral. Je to přímá roura z jednoho konce na druhý, a na tu je hierarchie tříd zbytečná váha. U IPK, kde se čtyři protokoly opravdu střídají za společným rozhraním, to vyšlo přesně opačně.

Poznámky k návrhu

  • Vlastní BER (de)kódování — SNMP pakety jsou ASN.1/BER, takže parsování je ruční práce s bajty, ne jen volání JSON knihovny.
  • Cíleně jednoduchý scope — SNMPv2c GET, skalární OID, jeden exportní formát.
  • Odolnost proti chybám — chybějící odpověď, neplatné OID nebo nedostupný kolektor program nepoloží, jen zaloguje a pokračuje dalším cyklem. Exportér, který spadne při prvním výpadku sítě, je k ničemu.

Testování

Testy (GoogleTest, složka tests/) pokrývají BER kódování i dekódování, sestavení a parsování SNMP PDU a parsování URL endpointu pro export.

GitHub