發文作者:dieabsorb | 5 四月, 2011

SNMP 小記

最近 SNMP 的需求很大 >///<, 也學了不少東西, 該來筆記一下..

* get, get-next, get-bulk

以前在學校幾乎是 snmpwalk 一個指令搞定, 沒真的搞清楚在幹麼, 其實在 snmp v1 只有 get, get-next 兩種 request, snmpwalk 是透過不斷地丟出 get-next request 來要求下一個 oid, 實際上當你拉 get-next 1.2.3 的時候, 會丟回下一個有值的 oid(有點廢話 XD), 所以有可能是 1.2.3.1 或是 1.2.4, 都有可能, 所以 protocol 層並不會管你要拉的 1.2.3 而很溫馨的停在 1.2.3.*, application 會自己處理, 另外一個 get-next 要注意的是他會傳 *下一個* 值(又來了廢話 XD) 所以其實如果你想要拿就是某個 1.2.3.5566 的值, snmpwalk 的作法其實長這樣

1 22:27:41.951538 IP nms.host.14633 > snmp.agent.snmp:  C=test123 GetNextRequest(33)  25.4.2.1.5.77836    
2 22:27:41.952936 IP snmp.agent.snmp > nms.host.14633:  C=test123 GetResponse(32)  25.4.2.1.6.0=2    
3 22:27:41.953039 IP nms.host.14633 > snmp.agent.snmp:  C=test123 GetRequest(33)  25.4.2.1.5.77836    
4 22:27:41.953308 IP snmp.agent.snmp > nms.host.14633:  C=test123 GetResponse(45)  25.4.2.1.5.77836=[|snmp]

先丟個 get-next, 發現回傳的 base 已經跳過我們要的部份, 重新下一個 get 來把原本的值拿回來. get, get-next 其實很好, 也可以在 PDU 裡面塞很多個 oid 一次跟 snmp agent 要求值, 但是這樣對想要拿一整個 table 的時候 (像是想要查某個 switch 上的 mac table) 很沒效率,  尤其是遇到大型 router 的時候 , 上面的 agent 很趣味, 時常心情好要回不回的 (而且越貴的 router 反而越不喜歡回 😦 ), 所以減少 snmp request 會讓我們的 code 節省很多時間. 在 v2 加入了新的 get-bulk, 其實 get-bulk 也很好懂, Wiki 上就寫了 “Optimized version of GetNextRequest", 丟一個 get-bulk request, agent 會盡量塞值到 response 裡面, 之後就跟 get-next 一樣, NMS 有需要就在自己丟下一個 get-bulk 出去.

* Perl <-> SNMP

Net::SNMP 當然是用 Perl 抓 SNMP 的最好選擇 !

Net::SNMP 基本上滿完整的, 只是用起來實在是有點麻煩,  而且因為我被 snmpwalk 養壞了 XD, 所以剛開始寫的時候覺得 get_bulk_request(), get_next_request(), 會幫我東西都處理好, 實在是太天真的阿~~

剛剛其實就有提到 application 必須自己處理你想要哪些 oid, agent 只會乖乖的把下一個 oid 丟給你, 所以 Net::SNMP 的 example 中使用 non-blocking get-bulk 的 callback function 就有這段

1      while (@names) {
2         $next = shift @names;
3         if (!oid_base_match($OID_ifTable, $next)) {
4            return; # Table is done.
5         }
6         $table->{$next} = $list->{$next};
7      }

根據回傳的一堆 oid, 跟我們想要的 baseoid 做比較, 如果是我們要得 oid, 可能後面還有值, 要一直拿到我們要得 base 結束為止, 這個例子也就是麻煩在這, 其實你常常會想要 1.2.3, 1.4.6, 1.7.8 三個 oid 開頭的值一次拉, 所以你本來就可以這樣下..

1   my $result = $session->get_bulk_request(
2      -varbindlist    => \@oids,
3      -callback       => [ \&table_callback, \%table ],
4      -maxrepetitions => 10,
5   );

但是 callback 就不能像上面這樣寫了, 要改成大概像這樣..

 1  my @nextoids = ();
 2  while (@names) {
 3      $next = shift @names;
 4      for (my $i=0; $i < $#oids; $i++ ) {
 5          if (oid_base_match($oids[$i], $next)) {
 6              $nextoids[$i] = $next;
 7          }
 8      }
 9      $table->{$next} = $list->{$next};
10  }

然後在根據 @nextoids 有沒有 element 來知道是不是每個 oid 都抓完了.

不過老實說這樣實在是很麻煩, 後來找到 Net::SNMP::Util 感覺更好用, 直接把 oid 丟給他, 就會幫你處理好, 而且還可以把難看的 oid 取個 alias, 回傳的 $result  hash reference 就根據你的 alias access, example 像這樣

%oids  = (
        'ifType'  =>   '1.3.6.1.2.1.2.2.1.3',
        'ifXData'  => [ '1.3.6.1.2.1.31.1.1.1.1',   # ifName
                       '1.3.6.1.2.1.31.1.1.1.15' ], # ifHighSpeed
        'someMib' =>   '1.3.6.1.4.1.99999.12.3'
);
foreach $host ( @hosts ){
    foreach $index ( sort keys %{$result->{$host}{ifType}} ){
        printf "$host - $index - type:%d - %s (%d kbps)\n",
            $result->{$host}{ifType}{$index},
            $result->{$host}{ifXData}[0]{$index},   # ifName
            $result->{$host}{ifXData}[1]{$index};   # ifHighSpeed
    }
}

不過改用 Net::SNMP::Util 之後還是有幾個小問題自己會犯錯:

1. 用 non-blocking mode 的時候自己要記得加 -maxrepetition 的參數, 這部份有文字敘述, 但是範例居然沒有寫, 如果傻傻的 copy 範例會發現抓不到東西…

2. get-bulk/get-next 抓某個特定 oid (不是一整個 oid table) 會抓不到值. 就是正常行為, 其實你下 get-next 是拿到下一個 oid, 所以這時候要改用 get (在 Net::SNMP::Util 裡是 snmpget(), snmpparaget() ) 才行.

3. example 有點 typo XD

除此之外, 實在是滿好用的, code 可以精簡很多,  看 FreeBSD ports 還沒有, 我也順便丟了 pr 一下.


發表留言

分類