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.
Nie wiem nic o 'SNMP' ale' Net-SNMP' ma klasę 'Net :: SNMP :: Message'. –