Domanda

Un paio di settimane fa ho scritto un relayer SNMP per il nostro gruppo ops. Hanno alcuni dispositivi stupidi che possono inviare solo trappole ad un singolo IP, e abbiamo un sistema di monitoraggio che ascolta su IP multipli per la disponibilità. morto semplice del codice, ed essenzialmente:

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

E 'funzionato, in fondo, ma ora l'ovvio breve venuta che non include il mittente IP è un problema (a quanto pare la prima classe del dispositivo incluso informazioni come varbind e qualche nuova classe non lo fa).

Quello che vorrei fare è cambiare il mio codice per qualcosa di simile:

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);
  }
}

In altre parole, se il mio mittente non è compreso snmpTrapAddress, aggiungerlo. Il problema è che ogni pacchetto SNMP Ho guardato per Perl sembra fortemente focalizzato sulle infrastrutture di ricevere trappole ed eseguendo ottiene.

Quindi: C'è un semplice modulo Perl che mi permetterà di dire "qui c'è un blob di dati che rappresentano un trap SNMP decodificarlo in qualcosa che posso facilmente manipolare, quindi ricompilare di nuovo in un blob posso buttare in rete. "?

Se la risposta è si dà "l'uso SNMP fittizio", si può fornire esempi di questo? Posso solo essere cieco, ma dall'uscita del perldoc SNMP non è ovvio per me come utilizzarlo in questo modo.

EDIT:

Risulta dopo aver guardato un po 'intorno che "codifica SNMP" è davvero ASN.1 BER (Basic Encoding Rules). Sulla base di questo che sto avendo un andare con Convert :: BER. Vorrei ancora favorevole a qualsiasi rottura facile giù / modificare / ricostruire suggerimenti per SNMP.

È stato utile?

Soluzione

Non ho mai trovato una soluzione perfetta per questo. Net :: SNMP :: Messaggio (parte di Net :: SNMP ) potrebbe consentire questo, ma non sembra avere un'interfaccia definita pubblicamente, e nessuno dell'interfaccia Net :: SNMP sembrava particolarmente rilevante. NSNMP è più vicino allo spirito di quello che stavo cercando, ma è fragile e non ha funzionato per il mio pacchetto fuori dalla scatola e se ho intenzione di supportare il codice fragile, che sta per essere il mio proprio codice fragili =).

Mon :: SNMP anche avuto vicino a quello che cercavo, ma troppo era rotto fuori dalla scatola. Sembra essere abbandonato, con l'ultima release nel 2001 e ultima release CPAN dello sviluppatore nel 2002. Non mi rendevo conto che al momento, ma ora penso che è rotto a causa di un cambiamento nell'interfaccia per Converti :: BER modulo che utilizza.

Mon :: SNMP mi ha puntato verso Convertire :: BER . A poche migliaia legge della Convert :: BER POD, la fonte Mon :: SNMP, e 1157 (esp. 4.1.6, "La trappola-PDU") più tardi e mi si avvicinò con questo codice come una prova di concetto di fare quello che volevo. Questo è solo prova di concetto (per ragioni io dettaglio dopo il codice) in modo che potrebbe non essere perfetto, ma ho pensato che potrebbe fornire riferimenti utili per i futuri persone Perl che lavorano in questo settore, così qui è:

#!/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;  
}

Quindi, questo è tutto. Ecco il kicker. Ho preso ops in parola che non stavano ottenendo l'indirizzo IP del mittente originale nella trappola. Mentre si lavora con questo esempio, ho scoperto che, almeno nell'esempio che mi hanno dato, l'originale IP era nel campo agente-addr nella trappola. Dopo aver mostrato loro questo e dove nelle API dello strumento loro di utilizzare questo è esposto andarono a cercare di fare il cambiamento sul loro fine. Sto daving il codice precedente contro il tempo mi chiedono per qualcosa che in realtà bisogno di letame nel pacchetto per, ma per ora la sopra rimarrà a prova non rigorosamente testato di codice di concetto. Speranza che aiuta qualcuno un giorno.

Altri suggerimenti

Hai provato NSNMP ?

Sicuramente il check-out SNMP_Session.

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

Assicurati di seguire i collegamenti al vecchio sito di distribuzione che ha diversi esempi.

Ho praticamente viaggiato lo stesso percorso attraverso Mon :: SNMP, convertire :: BER, TCP / IP Illustrated, ecc .. SNMP_Session è l'unica cosa che ho potuto fare il lavoro. Con il lavoro, intendo accettare un trap SNMP sulla porta UDP 162 e decodificarlo in equivalenti di stringa per la registrazione senza reinventare diverse ruote. Sono solo utilizzando la funzionalità di trappola di ricezione, ma penso che possa fare ciò che si vuole troppo.

E 'su Google Code, non CPAN però, quindi è un po' difficile da trovare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top