2009-07-14 27 views
7

Một vài tuần trước, tôi đã viết một trình phát lại SNMP cho nhóm ops của chúng tôi. Họ có một số thiết bị câm mà chỉ có thể gửi bẫy đến một IP duy nhất, và chúng tôi có một hệ thống giám sát lắng nghe trên nhiều IP sẵn có. Mã của đơn giản chết, và về cơ bản:Làm cách nào để phân tích cú pháp bẫy SNMP thô trong Perl?

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

Nó làm việc, về cơ bản, nhưng bây giờ rõ ràng ngắn sắp tới rằng nó không bao gồm người khởi tạo IP là một vấn đề (hình như lớp đầu tiên của thiết bị bao gồm các thông tin như một varbind và một số lớp mới không).

Những gì tôi muốn làm là thay đổi mã của tôi để một cái gì đó như thế này:

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

Nói cách khác, nếu người gửi của tôi là không bao gồm snmpTrapAddress, thêm nó. Vấn đề là mỗi gói SNMP tôi đã xem xét cho Perl có vẻ rất tập trung vào cơ sở hạ tầng của việc nhận bẫy và thực hiện được.

Vì vậy: Có một mô-đun Perl đơn giản cho phép tôi nói "đây là một khối dữ liệu đại diện cho một cái bẫy snmp hay không. Giải mã nó mạng "?

Nếu câu trả lời bạn đưa ra là "sử dụng giả SNMP", bạn có thể cung cấp ví dụ về điều này không? Tôi chỉ có thể bị mù, nhưng từ đầu ra của perldoc SNMP nó không rõ ràng với tôi làm thế nào để sử dụng nó theo cách này.

EDIT:

Hóa ra sau khi nhìn xung quanh một chút rằng "SNMP mã hóa" thực sự là ASN.1 BER (Basic Rules Encoding). Dựa trên điều này tôi đang có một đi với Convert :: BER. Tôi vẫn sẽ hoan nghênh bất kỳ mẹo chia nhỏ/chỉnh sửa/xây dựng lại dễ dàng nào cho SNMP.

+0

Tôi không biết gì về 'SNMP' nhưng' Net-SNMP' có lớp 'Net :: SNMP :: Message'. –

Trả lời

8

Tôi chưa bao giờ tìm được giải pháp hoàn hảo cho điều này. Net :: SNMP :: Message (một phần của Net::SNMP) có thể cho phép điều này nhưng dường như không có giao diện được xác định công khai và không giao diện Net :: SNMP nào có vẻ đặc biệt phù hợp. NSNMP là gần với tinh thần của những gì tôi đang tìm kiếm, nhưng nó giòn và không làm việc cho gói của tôi ra khỏi hộp và nếu tôi sẽ hỗ trợ mã giòn, nó sẽ là mã giòn của riêng tôi =).

Mon::SNMP cũng gần với những gì tôi đang tìm kiếm, nhưng nó cũng bị phá vỡ khỏi hộp. Nó dường như bị bỏ rơi, với bản phát hành cuối cùng vào năm 2001 và bản phát hành CPAN cuối cùng của nhà phát triển vào năm 2002. Tôi đã không nhận ra nó vào lúc đó nhưng bây giờ tôi nghĩ rằng nó bị hỏng do thay đổi giao diện cho Convert :: BER mô-đun mà nó sử dụng.

Thứ hai :: SNMP cho tôi chỉ về phía Convert::BER. Một vài nghìn lần đọc của Convert :: BER POD, Mon :: SNMP source, và RFC 1157 (đặc biệt là 4.1.6, "The Trap-PDU") sau đó và tôi đã đưa ra mã này như là một bằng chứng về khái niệm để làm những gì tôi muốn. Đây chỉ là bằng chứng của khái niệm (vì lý do tôi sẽ chi tiết sau mã) vì vậy nó có thể không hoàn hảo, nhưng tôi nghĩ nó có thể cung cấp tài liệu tham khảo hữu ích cho những người Perl trong tương lai làm việc trong lĩnh vực này, vì vậy đây là:

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

Vì vậy, đó là nó. Đây là kicker. Tôi đã sử dụng từ khóa của họ rằng họ không nhận được IP của người gửi ban đầu trong cái bẫy. Trong khi làm việc thông qua ví dụ này, tôi thấy rằng, ít nhất là trong ví dụ họ đã cho tôi, IP ban đầu là trong lĩnh vực đại lý-addr trong cái bẫy.Sau khi cho họ thấy điều này và ở đâu trong API của công cụ mà họ sử dụng, chúng sẽ bị lộ ra để cố gắng thay đổi kết thúc. Tôi đang giải mã đoạn mã trên chống lại thời gian họ hỏi tôi về một thứ mà tôi thực sự cần muck trong gói, nhưng bây giờ ở trên sẽ vẫn không được kiểm tra nghiêm ngặt bằng chứng về mã khái niệm. Hy vọng nó sẽ giúp ai đó một ngày nào đó.

2

Bạn đã thử NSNMP?

+0

Argh! Tôi nghĩ rằng tôi đã có, nhưng có vẻ như những gì tôi thực sự vấp ngã là NSNMP :: Đơn giản, đó không phải là những gì tôi muốn. NSNMP trông giống như một giải pháp có khả năng. Poking tại nó một số bây giờ ... – jj33

+0

Khái niệm về NSNMP nên làm việc, nhưng nó là một mô-đun rất mong manh. Nó chỉ hỗ trợ một phiên bản giới hạn và nó không phù hợp với những gì tôi cần ngay bây giờ, quay trở lại xây dựng của riêng tôi mà tôi có thể mở rộng cho phù hợp với tất cả các phiên bản sau này mà tôi hiểu rõ hơn cách BER hoạt động. 1 cho các gợi ý tốt mặc dù. – jj33

2

Chắc chắn kiểm tra SNMP_Session.

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

Đảm bảo tuân theo các liên kết đến các trang web phân phối cũ trong đó có một số ví dụ.

Về cơ bản, tôi đã di chuyển cùng một đường dẫn qua Mon :: SNMP, Chuyển đổi :: BER, TCP/IP Illustrated, vv .. SNMP_Session là điều duy nhất tôi có thể thực hiện công việc. Theo công việc, tôi có nghĩa là chấp nhận một bẫy SNMP trên cổng UDP 162 và giải mã nó thành chuỗi tương đương để ghi nhật ký mà không cần phát minh lại một số bánh xe. Tôi chỉ sử dụng chức năng nhận bẫy nhưng tôi nghĩ rằng nó có thể làm những gì bạn muốn quá.

Đó là trên Google Code, chứ không phải CPAN, do đó, có một chút khó tìm.

Các vấn đề liên quan