2009-07-14 8 views
7

Kilka tygodni temu napisałem przekaźnik SNMP dla naszej grupy operacyjnej. Mają jakieś głupie urządzenia, które mogą wysyłać pułapki tylko do jednego adresu IP, a my mamy system monitorujący, który nasłuchuje na wielu adresach IP pod kątem dostępności. Kodeks nie żyje proste, a przede wszystkim:Jak mogę analizować surową pułapkę SNMP w Perlu?

while (recv($packet)) { 
    foreach $target (@targets) { 
    send($target, $packet); 
    } 
} 

To działało, w zasadzie, ale teraz oczywiste krótki nadchodzi, że nie zawierają inicjator IP jest problem (podobno pierwsza klasa urządzenia obejmowały informacje w postaci varbind i niektóre nowe klasy nie).

Co chciałbym zrobić, to zmienić swój kod, aby coś takiego:

while ($server->recv($packet)) { 
    my $obj = decompile($packet) 
    if (!$obj->{varbind}{snmpTrapAddress}) { 
    $obj->{varbind}{snmpTrapAddress} = inet_ntoa($server->peeraddr()); 
    } 
    $packet = compile($obj); 
    foreach $target (@targets) { 
    send($target, $packet); 
    } 
} 

Innymi słowy, jeśli nadawca nie jest tym snmpTrapAddress, dodaj ją. Problem polega na tym, że każdy pakiet SNMP, który oglądałem dla Perla, wydaje się bardzo mocno skoncentrowany na infrastrukturze odbierania pułapek i wydajności.

A więc: Czy istnieje prosty moduł Perla, który pozwoli mi powiedzieć "tutaj jest blob danych reprezentujących pułapkę snmp, dekodować go w coś, co mogę łatwo manipulować, a następnie przekompilować go z powrotem w obiekt typu blob, który mogę rzucić na sieć"?

Jeśli odpowiedź brzmi "użyj manekina SNMP", czy możesz podać przykłady tego? Być może jestem ślepy, ale z wyjścia perldoc SNMP nie jest dla mnie oczywiste, jak go używać w ten sposób.

EDIT:

Okazuje się rozglądać po kawałku, że "kodowanie SNMP" jest naprawdę ASN.1 BER (Podstawowe zasady kodowania). Na podstawie tego mam zamiar przejść z Convert :: BER. Nadal będę wdzięczny za wszelkie proste wskazówki dotyczące łamania/edycji/przebudowy dla SNMP.

+0

Nie wiem nic o 'SNMP' ale' Net-SNMP' ma klasę 'Net :: SNMP :: Message'. –

Odpowiedz

8

Nigdy nie znalazłem idealnego rozwiązania tego problemu. Net :: SNMP :: Message (część z Net::SNMP) może na to pozwolić, ale nie ma publicznego interfejsu, a żaden z interfejsów Net :: SNMP nie wydaje się szczególnie istotny. NSNMP jest najbliżej ducha tego, czego szukałem, ale jest kruchy i nie działał dla mojego pakietu po wyjęciu z pudełka i jeśli zamierzam wspierać kruchy kod, to będzie to mój własny łamliwy kod =).

Mon::SNMP również zbliżył się do tego, czego szukałem, ale również został wyrwany z pudełka. Wydaje się być porzuconym, z ostatnią wersją w 2001 roku i ostatnią wersją CPAN dewelopera w 2002 roku. Nie zdawałem sobie z tego sprawy w tym momencie, ale teraz myślę, że jest ona zepsuta z powodu zmiany interfejsu Konwertuj :: BER moduł, którego używa.

Mon :: SNMP skierował mnie w stronę Convert::BER. Kilka tysięcy odczytów z Convert :: BER POD, źródła Mon :: SNMP i RFC 1157 (zwłaszcza 4.1.6, "The Trap-PDU") później i wymyśliłem ten kod jako dowód koncepcji do zrobienia czego chciałem. To tylko dowód koncepcji (z powodów, które opiszę po kodzie), więc może nie być idealny, ale pomyślałem, że może to stanowić przydatne odniesienie dla przyszłych pracowników Perla pracujących w tej dziedzinie, więc tutaj jest:

#!/usr/bin/perl 

use Convert::BER; 
use Convert::BER qw(/^(\$|BER_)/); 

my $ber = Convert::BER->new(); 

# OID I want to add to the trap if not already present 
my $snmpTrapAddress = '1.3.6.1.6.3.18.1.3'; 

# this would be from the incoming socket in production 
my $source_ip = '10.137.54.253'; 

# convert the octets into chars to match SNMP standard for IPs 
my $source_ip_str = join('', map { chr($_); } split(/\./, $source_ip)); 

# Read the binary trap data from STDIN or ARGV. Normally this would 
# come from the UDP receiver 
my $d = join('', <>); 

# Stuff my trap data into $ber 
$ber->buffer($d); 

print STDERR "Original packet:\n"; 
$ber->dump(); 

# Just decode the first two fields so we can tell what version we're dealing with 
$ber->decode(
       SEQUENCE => [ 
        INTEGER => \$version, 
        STRING => \$community, 
        BER => \$rest_of_trap, 
       ], 
) || die "Couldn't decode packet: ".$ber->error()."\n"; 

if ($version == 0) { 
    #print STDERR "This is a version 1 trap, proceeding\n"; 

    # decode the PDU up to but not including the VARBINDS 
    $rest_of_trap->decode(
    [ SEQUENCE => BER_CONTEXT | BER_CONSTRUCTOR | 0x04 ] => 
     [ 
     OBJECT_ID => \$enterprise_oid, 
     [ STRING => BER_APPLICATION | 0x00 ] => \$agentaddr, 
     INTEGER => \$generic, 
     INTEGER => \$specific, 
     [ INTEGER => BER_APPLICATION | 0x03 ] => \$timeticks, 
     SEQUENCE => [ BER => \$varbind_ber, ], 
     ], 
) || die "Couldn't decode packet: ".$extra->error()."\n";; 

    # now decode the actual VARBINDS (just the OIDs really, to decode the values 
    # We'd have to go to the MIBs, which I neither want nor need to do 
    my($r, $t_oid, $t_val, %varbinds); 
    while ($r = $varbind_ber->decode(
    SEQUENCE => [ 
     OBJECT_ID => \$t_oid, 
     ANY  => \$t_val, 
    ],)) 
    { 
    if (!$r) { 
     die "Couldn't decode SEQUENCE: ".$extra->error()."\n"; 
    } 
    $varbinds{$t_oid} = $t_val; 
    } 

    if ($varbinds{$snmpTrapAddress} || $varbinds{"$snmpTrapAddress.0"}) { 
    # the original trap already had the data, just print it back out 
    print $d; 
    } else { 
    # snmpTrapAddress isn't present, create a new object and rebuild the packet 
    my $new_trap = new Convert::BER; 
    $new_trap->encode(
     SEQUENCE => [ 
     INTEGER => $version, 
     STRING => $community, 
     [ SEQUENCE => BER_CONTEXT | BER_CONSTRUCTOR | 0x04 ] => 
      [ 
      OBJECT_ID => $enterprise_oid, 
      [ STRING => BER_APPLICATION | 0x00 ] => $agentaddr, 
      INTEGER => $generic, 
      INTEGER => $specific, 
      [ INTEGER => BER_APPLICATION | 0x03 ] => $timeticks, 
      SEQUENCE => [ 
       BER => $varbind_ber, 
       # this next oid/val is the only mod we should be making 
       SEQUENCE => [ 
       OBJECT_ID => "$snmpTrapAddress.0", 
       [ STRING => BER_APPLICATION | 0x00 ] => $source_ip_str, 
       ], 
      ], 
      ], 
     ], 
    ); 
    print STDERR "New packet:\n"; 
    $new_trap->dump(); 
    print $new_trap->buffer; 
    } 
} else { 
    print STDERR "I don't know how to decode non-v1 packets yet\n"; 
    # send back the original packet 
    print $d; 
} 

To wszystko. Oto kicker. Wziąłem ops na ich słowo, że oni nie dostają IP pierwotnego nadawcy w pułapce. Pracując nad tym przykładem, stwierdziłem, że przynajmniej w podanym przykładzie, oryginalne IP znajdowało się w polu agent-adres w pułapce.Po pokazaniu im tego i gdzie w interfejsie API narzędzia, z którego korzystali, odsunęli się, próbując dokonać zmiany po ich zakończeniu. Nadpisuję powyższy kod w momencie, gdy pytają mnie o coś, co właściwie muszę zgrupować, ale na razie powyższe pozostanie nietestownie przetestowanym kodem proof of concept. Mam nadzieję, że pewnego dnia komuś pomoże.

2

Czy próbowałeś NSNMP?

+0

Argh! Myślałem, że tak, ale wygląda na to, że natknąłem się na NSNMP :: Simple, co nie jest tym, czego chcę. NSNMP wygląda na prawdopodobne rozwiązanie. Wkuwanie go teraz ... – jj33

+0

Koncepcja NSNMP powinna działać, ale jest to bardzo delikatny moduł. Obsługuje tylko jedną ograniczoną wersję i nie pasuje do tego, czego teraz potrzebuję, wracając do tworzenia własnych, które mogę rozbudować, aby pasowały do ​​wszystkich wersji później, teraz, gdy rozumiem lepiej, jak działa BER. +1 za dobrą sugestię. – jj33

2

Zdecydowanie sprawdź SNMP_Session.

http://code.google.com/p/snmp-session/

Upewnij się linki do starej witryny dystrybucyjnej, która ma kilka przykładów.

Zasadniczo przeszedłem tą samą ścieżką przez Mon :: SNMP, Convert :: BER, TCP/IP Illustrated, itd .. SNMP_Session to jedyna rzecz, którą udało mi się wykonać. Przez pracę rozumiem akceptację pułapki SNMP na porcie UDP 162 i dekodowanie go na ekwiwalenty ciągów w celu rejestrowania bez ponownego wynajdywania kilku kół. Używam tylko funkcji pułapki odbiorczej, ale myślę, że może też zrobić to, co chcesz.

Jest w Google Code, nie CPAN, więc jest to trochę trudne do znalezienia.