문제

몇 주 전에 저는 OPS 그룹을위한 SNMP 릴레이를 썼습니다. 여기에는 트랩을 단일 IP로만 보낼 수있는 멍청한 장치가 있으며 가용성을 위해 여러 IP를 듣는 모니터링 시스템이 있습니다. 코드는 단순하고 본질적으로 :

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

기본적으로 작동했지만 이제는 원래의 IP를 포함하지 않는 것이 분명한 짧은 오지 (분명히 첫 번째 클래스의 장치에는 varbind로 정보를 포함시키고 일부 새로운 클래스는 그렇지 않습니다).

내가하고 싶은 것은 내 코드를 다음과 같은 것으로 변경하는 것입니다.

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

다시 말해, 내 발신자가 snmptrapaddress를 포함하지 않으면 추가하십시오. 문제는 내가 Perl을 위해 본 모든 SNMP 패키지가 트랩을 받고 공연을하는 인프라에 매우 집중된 것처럼 보인다는 것입니다.

그래서 : "SNMP 트랩을 나타내는 데이터의 덩어리가 있습니다. 쉽게 조작 할 수있는 무언가로 디코딩 한 다음 네트워크 위로 던질 수있는 블로브로 다시 컴파일 할 수있는 간단한 Perl 모듈이 있습니까?

당신이주는 대답이 "SNMP 더미 사용"이라면 이것의 예를 제공 할 수 있습니까? 나는 맹인 일지 모르지만 출력에서 Perldoc SNMP 이런 식으로 사용하는 방법은 분명하지 않습니다.

편집하다:

"SNMP 인코딩"이 실제로 ASN.1 BER (기본 인코딩 규칙)이라는 점을 둘러 본 후에 밝혀졌습니다. 이것을 바탕으로 나는 Convert :: ber와 함께 가고 있습니다. SNMP에 대한 쉬운 분리/편집/재 구축 팁을 여전히 환영합니다.

도움이 되었습니까?

해결책

나는 이것에 대한 완벽한 해결책을 찾지 못했습니다. net :: snmp :: 메시지 (일부 net :: snmp)이를 허용 할 수는 있지만 공개적으로 정의 된 인터페이스가없는 것 같습니다. Net :: SNMP 인터페이스는 특히 관련이없는 것 같습니다. NSNMP 내가 찾고 있던 것의 정신에 가장 가깝지만 부서지기 쉬우 며 상자 밖으로 내 패킷에 효과가 없었으며 부서지기 쉬운 코드를 지원하려면 내 자신의 부서지기 쉬운 코드가 될 것입니다 =).

월 :: snmp 또한 내가 찾고 있던 것과 가까워졌지만 상자에서 나왔습니다. 2001 년 마지막 릴리스와 2002 년 개발자의 마지막 CPAN 릴리스와 함께 버려진 것으로 보입니다. 당시에는 몰랐지만 이제는 Convert :: BER의 인터페이스의 변화로 인해 깨진 것이라고 생각합니다. 사용하는 모듈.

Mon :: SNMP가 나를 향해 지적했습니다 변환 :: ber. 개종자의 수천 개의 읽기 :: Ber Pod, Mon :: SNMP 소스 및 RFC 1157 (Esp. 4.1.6, "Trap-PDU") 나중에 나는이 코드를 내가 원하는 것을 수행하기위한 개념 증명으로 생각해 냈습니다. 이것은 단지 개념 증명서 (코드 후에 자세하게 설명 할 이유가 있기 때문에) 완벽하지 않을 수도 있지만,이 분야에서 일하는 미래의 Perl 사람들에게 유용한 참조를 제공 할 수 있다고 생각했습니다.

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

그래서 그게 다야. 여기 키커가 있습니다. 나는 그들의 말에 Ops를 덫에 원래 발신자의 IP를 얻지 못했다는 말을 가져 갔다. 이 예제를 통해 작업하는 동안 적어도 그들이 준 예에서 원래 IP는 트랩의 에이전트 ADDR 필드에 있음을 알았습니다. 그들에게 이것을 보여준 후, 도구의 API에서 이것을 사용하는 곳은 노출되었다. 그들은 그들의 끝을 바꾸려고 노력했다. 나는 그들이 실제로 패킷에 뭉쳐야 할 것을 요구할 때에 대해 위의 코드를 정리하고 있지만, 현재 위의 것은 강력하게 테스트되지 않은 개념 코드 증명으로 남아있을 것입니다. 언젠가 누군가가 도움이되기를 바랍니다.

다른 팁

시도해 보았 니 NSNMP?

snmp_session을 확실히 확인하십시오.

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

몇 가지 예제가있는 이전 배포 사이트에 대한 링크를 따라야합니다.

나는 기본적으로 Mon :: SNMP, Convert :: BER, TCP/IP 등을 통해 동일한 경로를 여행했습니다. 작업한다는 것은 UDP 포트 162에서 SNMP 트랩을 수락하고 여러 바퀴를 재창조하지 않고 로깅을 위해 문자열에 해당하는 문자열로 디코딩한다는 의미입니다. 나는 수신 트랩 기능 만 사용하고 있지만 그것이 당신이 원하는 것을 할 수 있다고 생각합니다.

그래도 CPAN이 아닌 Google 코드에 있으므로 찾기가 조금 어렵습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top