Pregunta

Hace

Unas semanas escribí un relayer SNMP para nuestro grupo de operaciones. Ellos tienen algunos dispositivos tontas que sólo pueden enviar capturas a una sola dirección IP, y tenemos un sistema de monitoreo que escucha en varias direcciones IP para la disponibilidad. muerto simple del código, y esencialmente:

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

Ha funcionado, básicamente, pero ahora lo obvio corta venir que no incluye el iniciador IP es un problema (al parecer la primera clase de dispositivo que se incluye la información como un varbind y algunos de clase nueva, no lo hace).

Lo que me gustaría hacer es cambiar el código para algo como esto:

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

En otras palabras, si mi remitente no está incluyendo snmpTrapAddress, añadirlo. El problema es que cada paquete SNMP que he mirado para Perl parece muy fuertemente centrado en la infraestructura de recepción de capturas y la realización sufran.

Así que: ¿Hay un módulo de Perl simple que permitirá que diga "aquí está una masa de datos que representan una trampa SNMP decodificarlo en algo que pueda manipular fácilmente, a continuación, volver a compilar de nuevo en una burbuja puedo tirar por la red. "?

Si la respuesta que dan es "el uso de SNMP de prueba", se puede proporcionar ejemplos de esto? Puede que acaba de ser ciego, pero a partir de la salida del perldoc SNMP que no es obvio para mí cómo usarlo de esta manera.

EDIT:

Resulta que después de mirar un poco más que "SNMP codificación" es realmente ASN.1 BER (Basic Encoding Rules). Con base en esta estoy teniendo un ir con Convert :: BER. Me gustaría volver a acoger cualquier ruptura fácil abajo / editar / reconstruir consejos para SNMP.

¿Fue útil?

Solución

Nunca he encontrado una solución perfecta para esto. Net :: SNMP :: Mensaje (parte de Net :: SNMP ) podría permitir esto, pero no parecen tener una interfaz definida públicamente, y ninguno de la interfaz de Net :: SNMP parecía especialmente relevante. NSNMP se acerca más al espíritu de lo que estaba buscando, pero es frágil y no funcionó para mi paquete fuera de la caja y si voy a apoyar código frágil, que va a ser mi propio código quebradiza =).

Lu :: SNMP también se acercó a lo que estaba buscando, pero también estaba roto fuera de la caja. Parece que ser abandonado, con la última versión en 2001 y la última versión de CPAN del desarrollador en 2002. No me di cuenta en ese momento, pero ahora creo que se ha roto debido a un cambio en la interfaz con el Convert :: BER módulo que utiliza.

Lu :: SNMP consiguió me señaló hacia Convertir :: BER . Unos pocos miles de lecturas de la Convert :: BER POD, la fuente Lu :: SNMP y (esp. 4.1.6, "La Trampa-PDU") más tarde y se me ocurrió este código como una prueba de concepto para hacer lo que quería. Esto es sólo una prueba de concepto (por razones que detalle después del código) por lo que puede que no sea perfecto, pero pensé que podría proporcionar una referencia útil para las personas que trabajan en Perl futuros en esta área, así que aquí está:

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

Por lo tanto, eso es todo. He aquí el problema. Tomé ops en su palabra de que no estaban recibiendo la IP del remitente original en la trampa. Mientras se trabaja a través de este ejemplo, he encontrado que, al menos en el ejemplo que me dieron, el IP original estaba en el campo agente-addr en la trampa. Después de mostrarles esto y en qué parte del API de la herramienta de su uso de este se expone que se iban a tratar de hacer el cambio en su extremo. Estoy daving el código anterior en contra de la vez que me piden algo realmente necesito lodo en el paquete de, pero por ahora el anteriormente, seguirán estando prueba del no-probado rigurosamente-de concepto. Espero que ayuda a alguien algún día.

Otros consejos

¿Usted intentó NSNMP ?

Sin duda la salida SNMP_Session.

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

Asegúrese de seguir los enlaces al sitio de distribución de edad que cuenta con varios ejemplos.

He viajado básicamente el mismo camino a través lun :: SNMP, convertir :: BER, TCP / IP Illustrated, etc .. SNMP_Session es la única cosa que he sido capaz de hacer el trabajo. Por el trabajo, me refiero a aceptar una trampa SNMP en el puerto UDP 162 y decodificarlo en equivalentes de cadena para la tala sin reinventar varias ruedas. Sólo estoy usando la funcionalidad trampa reciben pero creo que puede hacer lo que quiera también.

Es en Google Code, no CPAN sin embargo, por lo que es un poco difícil de encontrar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top