最近 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 一下.
發表留言