diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/agent/snmpa_mpd.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/snmp/src/agent/snmpa_mpd.erl')
-rw-r--r-- | lib/snmp/src/agent/snmpa_mpd.erl | 1386 |
1 files changed, 1386 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl new file mode 100644 index 0000000000..2e09286b87 --- /dev/null +++ b/lib/snmp/src/agent/snmpa_mpd.erl @@ -0,0 +1,1386 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(snmpa_mpd). + +-export([init/1, reset/0, inc/1, counters/0, + discarded_pdu/1, + process_packet/6, + generate_response_msg/5, generate_msg/5, + generate_discovery_msg/4, + process_taddrs/1, + generate_req_id/0]). + +-define(SNMP_USE_V3, true). +-include("snmp_types.hrl"). +-include("SNMP-MPD-MIB.hrl"). +-include("SNMPv2-TM.hrl"). +-include("SNMP-FRAMEWORK-MIB.hrl"). + +-define(VMODULE,"MPD"). +-include("snmp_verbosity.hrl"). + +-define(empty_msg_size, 24). + +-record(state, {v1 = false, v2c = false, v3 = false}). +-record(note, {sec_engine_id, + sec_model, + sec_name, + sec_level, + ctx_engine_id, + ctx_name, + disco = false, + req_id}). + + +%%%----------------------------------------------------------------- +%%% This module implemets the Message Processing and Dispatch part of +%%% the multi-lingual SNMP agent. +%%% +%%% The MPD is responsible for: +%%% *) call the security module (auth/priv). +%%% *) decoding the message into a PDU. +%%% *) decide a suitable Access Control Model, and provide it with +%%% the data it needs. +%%% *) maintaining SNMP counters. +%%% +%%% In order to take care of the different versions of counters, it +%%% implements and maintains the union of all SNMP counters (i.e. from +%%% rfc1213 and from rfc1907). It is up to the administrator of the +%%% agent to load the correct MIB. Note that this module implements +%%% the counters only, it does not provide instrumentation functions +%%% for the counters. +%%% +%%% With the terms defined in rfc2271, this module implememts part +%%% of the Dispatcher and the Message Processing functionality. +%%%----------------------------------------------------------------- +init(Vsns) -> + ?vlog("init -> entry with" + "~n Vsns: ~p", [Vsns]), + {A,B,C} = erlang:now(), + random:seed(A,B,C), + ets:insert(snmp_agent_table, {msg_id, random:uniform(2147483647)}), + ets:insert(snmp_agent_table, {req_id, random:uniform(2147483647)}), + init_counters(), + init_versions(Vsns, #state{}). + + +reset() -> + reset_counters(), + ok. + + +%%----------------------------------------------------------------- +%% Purpose: We must calculate the length of a +%% message with an empty Pdu, and zero-length community +%% string. This length is used to calculate the max +%% pdu size allowed for each request. This size is +%% dependent on two dynamic fields, the community string +%% and the pdu (varbinds actually). It is calculated +%% as EmptySize + length(CommunityString) + 4. +%% We assume that the length of the CommunityString is +%% less than 128 (thus requiring just one octet for the +%% length field (the same as the zero-length community +%% string)). 4 comes from the fact that the maximum pdu +%% size needs 31 bits which needs 5 * 7 bits to be +%% expressed. One 7bit octet is already present in the +%% empty msg, leaving 4 more 7bit octets. +%% Actually, this function is not used, we use a constant instead. +%%----------------------------------------------------------------- +%% Ret: 24 +%empty_msg() -> +% M = #message{version = 'version-1', community = "", data = +% #pdu{type = 'get-response', request_id = 1, +% error_status = noError, error_index = 0, varbinds = []}}, +% length(snmp_pdus:enc_message(M)) + 4. + +%%----------------------------------------------------------------- +%% Func: process_packet(Packet, TDomain, TAddress, State, Log) -> +%% {ok, SnmpVsn, Pdu, PduMS, ACMData} | {discarded, Reason} +%% Types: Packet = binary() +%% TDomain = snmpUDPDomain | atom() +%% TAddress = {Ip, Udp} +%% State = #state +%% Purpose: This is the main Message Dispatching function. (see +%% section 4.2.1 in rfc2272) +%%----------------------------------------------------------------- +process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) -> + inc(snmpInPkts), + case catch snmp_pdus:dec_message_only(binary_to_list(Packet)) of + + #message{version = 'version-1', vsn_hdr = Community, data = Data} + when State#state.v1 =:= true -> + ?vlog("v1, community: ~s", [Community]), + HS = ?empty_msg_size + length(Community), + v1_v2c_proc('version-1', NoteStore, Community, TDomain, TAddress, + Data, HS, Log, Packet); + + #message{version = 'version-2', vsn_hdr = Community, data = Data} + when State#state.v2c =:= true -> + ?vlog("v2c, community: ~s", [Community]), + HS = ?empty_msg_size + length(Community), + v1_v2c_proc('version-2', NoteStore, Community, TDomain, TAddress, + Data, HS, Log, Packet); + + #message{version = 'version-3', vsn_hdr = V3Hdr, data = Data} + when State#state.v3 =:= true -> + ?vlog("v3, msgID: ~p, msgFlags: ~p, msgSecModel: ~p", + [V3Hdr#v3_hdr.msgID, + V3Hdr#v3_hdr.msgFlags, + V3Hdr#v3_hdr.msgSecurityModel]), + v3_proc(NoteStore, Packet, TDomain, TAddress, V3Hdr, Data, Log); + + {'EXIT', {bad_version, Vsn}} -> + ?vtrace("exit: bad version: ~p",[Vsn]), + inc(snmpInBadVersions), + {discarded, snmpInBadVersions}; + + {'EXIT', Reason} -> + ?vtrace("exit: ~p", [Reason]), + inc(snmpInASNParseErrs), + {discarded, Reason}; + + UnknownMessage -> + ?vtrace("Unknown message: ~n ~p" + "~nwhen" + "~n State: ~p", [UnknownMessage, State]), + inc(snmpInBadVersions), + {discarded, snmpInBadVersions} + end. + +discarded_pdu(false) -> ok; +discarded_pdu(Variable) -> inc(Variable). + + +%%----------------------------------------------------------------- +%% Handles a Community based message (v1 or v2c). +%%----------------------------------------------------------------- +v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain, {Ip, Udp}, + Data, HS, Log, Packet) -> + TAddress = tuple_to_list(Ip) ++ [Udp div 256, Udp rem 256], + AgentMS = snmp_framework_mib:get_engine_max_message_size(), + MgrMS = snmp_community_mib:get_target_addr_ext_mms(?snmpUDPDomain, + TAddress), + PduMS = case MgrMS of + {ok, MMS} when MMS < AgentMS -> MMS - HS; + _ -> AgentMS - HS + end, + case (catch snmp_pdus:dec_pdu(Data)) of + Pdu when is_record(Pdu, pdu) -> + Log(Pdu#pdu.type, Packet), + inc_snmp_in_vars(Pdu), + #pdu{request_id = ReqId} = Pdu, + OkRes = {ok, Vsn, Pdu, PduMS, + {community, sec_model(Vsn), Community, TAddress}}, + %% Make sure that we don't process duplicate SET request + %% twice. We don't know what could happen in that case. + %% The mgr does, so he has to generate a new SET request. + ?vdebug("PDU type: ~p", [Pdu#pdu.type]), + case Pdu#pdu.type of + 'set-request' -> + %% Check if this message has already been processed + Key = {agent, Ip, ReqId}, + case snmp_note_store:get_note(NoteStore, Key) of + undefined -> + %% Set the processed note _after_ pdu processing. + %% This makes duplicated requests be ignored even + %% if pdu processing took long time. + snmp_note_store:set_note(NoteStore, + 100, Key, true), + %% Uses ACMData that snmpa_acm knows of. + %% snmpUDPDomain is implicit, since that's the only + %% one we handle. + OkRes; + true -> + {discarded, duplicate_pdu} + end; + _ -> + OkRes + end; + {'EXIT', Reason} -> + ?vtrace("PDU decode exit: ~p",[Reason]), + inc(snmpInASNParseErrs), + {discarded, Reason}; + _TrapPdu -> + {discarded, trap_pdu} + end; +v1_v2c_proc(_Vsn, _NoteStore, _Community, snmpUDPDomain, TAddress, + _Data, _HS, _Log, _Packet) -> + {discarded, {badarg, TAddress}}; +v1_v2c_proc(_Vsn, _NoteStore, _Community, TDomain, _TAddress, + _Data, _HS, _Log, _Packet) -> + {discarded, {badarg, TDomain}}. + +sec_model('version-1') -> ?SEC_V1; +sec_model('version-2') -> ?SEC_V2C. + + +%%----------------------------------------------------------------- +%% Handles a SNMPv3 Message, following the procedures in rfc2272, +%% section 4.2 and 7.2 +%%----------------------------------------------------------------- +v3_proc(NoteStore, Packet, _TDomain, _TAddress, V3Hdr, Data, Log) -> + case (catch v3_proc(NoteStore, Packet, V3Hdr, Data, Log)) of + {'EXIT', Reason} -> + exit(Reason); + Result -> + Result + end. + +v3_proc(NoteStore, Packet, V3Hdr, Data, Log) -> + %% 7.2.3 + #v3_hdr{msgID = MsgID, + msgMaxSize = MMS, + msgFlags = MsgFlags, + msgSecurityModel = MsgSecurityModel, + msgSecurityParameters = SecParams, + hdr_size = HdrSize} = V3Hdr, + ?vdebug("v3_proc -> version 3 message header:" + "~n msgID = ~p" + "~n msgMaxSize = ~p" + "~n msgFlags = ~p" + "~n msgSecurityModel = ~p" + "~n msgSecurityParameters = ~w", + [MsgID, MMS, MsgFlags, MsgSecurityModel, SecParams]), + %% 7.2.4 + SecModule = get_security_module(MsgSecurityModel), + %% 7.2.5 + SecLevel = check_sec_level(MsgFlags), + IsReportable = snmp_misc:is_reportable(MsgFlags), + %% 7.2.6 + ?vtrace("v3_proc -> " + "~n SecModule = ~p" + "~n SecLevel = ~p" + "~n IsReportable = ~p", + [SecModule,SecLevel,IsReportable]), + SecRes = (catch SecModule:process_incoming_msg(Packet, Data, + SecParams, SecLevel)), + ?vtrace("v3_proc -> message processing result: " + "~n SecRes: ~p", [SecRes]), + {SecEngineID, SecName, ScopedPDUBytes, SecData, DiscoOrPlain} = + check_sec_module_result(SecRes, V3Hdr, Data, IsReportable, Log), + ?vtrace("v3_proc -> " + "~n DiscoOrPlain: ~w" + "~n SecEngineID: ~w" + "~n SecName: ~p", [DiscoOrPlain, SecEngineID, SecName]), + %% 7.2.7 + #scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = PDU} = + case (catch snmp_pdus:dec_scoped_pdu(ScopedPDUBytes)) of + ScopedPDU when is_record(ScopedPDU, scopedPdu) -> + ?vtrace("v3_proc -> message processing result: " + "~n ScopedPDU: ~p", [ScopedPDU]), + ScopedPDU; + {'EXIT', Reason} -> + inc(snmpInASNParseErrs), + throw({discarded, Reason}) + end, + %% We'll have to take care of the unlikely case that we receive an + %% v1 trappdu in a v3 message explicitly... + if + is_record(PDU, trappdu) -> + inc(snmpUnknownPDUHandlers), + throw({discarded, received_v1_trap}); + true -> + ok + end, + ?vlog("7.2.7 result: " + "~n contextEngineID: ~w" + "~n ContextName: \"~s\"", [ContextEngineID, ContextName]), + if + SecLevel =:= ?'SnmpSecurityLevel_authPriv' -> + %% encrypted message - log decrypted pdu + Log(PDU#pdu.type, {V3Hdr, ScopedPDUBytes}); + true -> % otherwise, log binary + Log(PDU#pdu.type, Packet) + end, + %% Make sure a get_bulk doesn't get too big. + AgentMS = snmp_framework_mib:get_engine_max_message_size(), + %% PduMMS is supposed to be the maximum total length of the response + %% PDU we can send. From the MMS, we need to subtract everything before + %% the PDU, i.e. Message and ScopedPDU. + %% Message: [48, TotalLen, Vsn, [Tag, LH, Hdr], [Tag, LM, MsgSec], Data] + %% 1 3 <----------- HdrSize -----------> + %% HdrSize = everything up to and including msgSecurityParameters. + %% ScopedPduData follows. This is + %% [Tag, Len, [Tag, L1, CtxName], [Tag, L2, CtxEID]] + %% i.e. 6 + length(CtxName) + length(CtxEID) + %% + %% Total: 1 + TotalLenOctets + 3 + ScopedPduDataLen + TotMMS = if AgentMS > MMS -> MMS; + true -> AgentMS + end, + TotalLenOctets = snmp_pdus:get_encoded_length(TotMMS - 1), + PduMMS = TotMMS - TotalLenOctets - 10 - HdrSize - + length(ContextName) - length(ContextEngineID), + ?vdebug("v3_proc -> PDU type: ~p", [PDU#pdu.type]), + case PDU#pdu.type of + report when DiscoOrPlain =:= discovery -> + %% Discovery stage 1 response + Key = {agent, MsgID}, + Note = snmp_note_store:get_note(NoteStore, Key), + case Note of + #note{sec_engine_id = "", + sec_model = _MsgSecModel, + sec_name = "", + sec_level = _SecLevel, + ctx_engine_id = _CtxEngineID, + ctx_name = _CtxName, + disco = true, + req_id = _ReqId} -> + %% This is part of the discovery process initiated by us. + %% Response to the discovery stage 1 request + ?vdebug("v3_proc -> discovery stage 1 response", []), + {ok, 'version-3', PDU, PduMMS, {discovery, SecEngineID}}; + #note{sec_engine_id = SecEngineID, + sec_model = _MsgSecModel, + sec_name = SecName, + sec_level = SecLevel, + ctx_engine_id = _CtxEngineID, + ctx_name = _CtxName, + disco = true, + req_id = _ReqId} -> + %% This is part of the discovery process initiated by us. + %% Response to the discovery stage 2 request + ?vdebug("v3_proc -> discovery stage 2 response", []), + {ok, 'version-3', PDU, PduMMS, discovery}; + _ -> + %% 7.2.11 + DiscardReason = {bad_disco_note, Key, Note}, + throw({discarded, DiscardReason}) + end; + report -> + %% 7.2.11 + throw({discarded, report}); + 'get-response' -> %% As a result of a sent inform-request? + %% 7.2.12 + Key = {agent, MsgID}, + Note = snmp_note_store:get_note(NoteStore, Key), + case Note of + #note{sec_engine_id = "", + sec_model = _MsgSecModel, + sec_name = "", + sec_level = _SecLevel, + ctx_engine_id = _CtxEngineID, + ctx_name = _CtxName, + disco = true, + req_id = _ReqId} -> + %% This is part of the discovery process initiated by us. + %% Response to the discovery stage 1 request + ?vdebug("v3_proc -> discovery stage 1 response", []), + {ok, 'version-3', PDU, PduMMS, {discovery, SecEngineID}}; + #note{sec_engine_id = SecEngineID, + sec_model = _MsgSecModel, + sec_name = SecName, + sec_level = SecLevel, + ctx_engine_id = _CtxEngineID, + ctx_name = _CtxName, + disco = true, + req_id = _ReqId} -> + %% This is part of the discovery process initiated by us. + %% Response to the discovery stage 2 request + ?vdebug("v3_proc -> discovery stage 2 response", []), + {ok, 'version-3', PDU, PduMMS, discovery}; + #note{sec_engine_id = SecEngineID, + sec_model = MsgSecurityModel, + sec_name = SecName, + sec_level = SecLevel, + ctx_engine_id = ContextEngineID, + ctx_name = ContextName, + disco = false, + req_id = _ReqId} -> + {ok, 'version-3', PDU, PduMMS, undefined}; + _ -> + inc(snmpUnknownPDUHandlers), + throw({discarded, {no_outstanding_req, MsgID}}) + end; + 'snmpv2-trap' -> + inc(snmpUnknownPDUHandlers), + throw({discarded, received_v2_trap}); + Type -> + %% 7.2.13 + SnmpEngineID = snmp_framework_mib:get_engine_id(), + ?vtrace("v3_proc -> SnmpEngineID = ~w", [SnmpEngineID]), + case SecEngineID of + SnmpEngineID when (DiscoOrPlain =:= discovery) -> + %% This is a discovery step 2 message! + ?vtrace("v3_proc -> discovery stage 2", []), + generate_discovery2_report_msg(MsgID, + MsgSecurityModel, + SecName, + SecLevel, + ContextEngineID, + ContextName, + SecData, + PDU, + Log); + + SnmpEngineID when (DiscoOrPlain =:= plain) -> + %% 4.2.2.1.1 - we don't handle proxys yet => we only + %% handle ContextEngineID to ourselves + case ContextEngineID of + SnmpEngineID -> + %% Uses ACMData that snmpa_acm knows of. + {ok, 'version-3', PDU, PduMMS, + {v3, MsgID, MsgSecurityModel, SecName, SecLevel, + ContextEngineID, ContextName, SecData}}; + _ -> + %% 4.2.2.1.2 + NIsReportable = snmp_misc:is_reportable_pdu(Type), + Val = inc(snmpUnknownPDUHandlers), + ErrorInfo = {#varbind{oid = ?snmpUnknownPDUHandlers, + variabletype = 'Counter32', + value = Val}, + SecName, + [{securityLevel, SecLevel}, + {contextEngineID, ContextEngineID}, + {contextName, ContextName}]}, + case generate_v3_report_msg(MsgID, + MsgSecurityModel, + Data, ErrorInfo, + Log) of + {ok, Report} when NIsReportable =:= true -> + {discarded, snmpUnknownPDUHandlers, Report}; + _ -> + {discarded, snmpUnknownPDUHandlers} + end + end; + + "" -> + %% This is a discovery step 1 message!! + ?vtrace("v3_proc -> discovery step 1", []), + generate_discovery1_report_msg(MsgID, + MsgSecurityModel, + SecName, + SecLevel, + ContextEngineID, + ContextName, + SecData, + PDU, + Log); + + _ -> + {discarded, {badSecurityEngineID, SecEngineID}} + end + end. + + +get_security_module(?SEC_USM) -> + snmpa_usm; +get_security_module(_) -> + inc(snmpUnknownSecurityModels), + throw({discarded, snmpUnknownSecurityModels}). + +check_sec_level([MsgFlag]) -> + SecLevel = MsgFlag band 3, + if + SecLevel == 2 -> + inc(snmpInvalidMsgs), + throw({discarded, snmpInvalidMsgs}); + true -> + SecLevel + end; +check_sec_level(Unknown) -> + ?vlog("invalid msgFlags: ~p",[Unknown]), + inc(snmpInvalidMsgs), + throw({discarded, snmpInvalidMsgs}). + +check_sec_module_result(Res, V3Hdr, Data, IsReportable, Log) -> + case Res of + {ok, X} -> + X; + {error, Reason, []} -> % case 7.2.6 b + ?vdebug("security module result [7.2.6-b]:" + "~n Reason: ~p", [Reason]), + throw({discarded, {securityError, Reason}}); + {error, Reason, ErrorInfo} when IsReportable == true -> % case 7.2.6 a + ?vdebug("security module result when reportable [7.2.6-a]:" + "~n Reason: ~p" + "~n ErrorInfo: ~p", [Reason, ErrorInfo]), + #v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr, + Pdu = get_scoped_pdu(Data), + case generate_v3_report_msg(MsgID, MsgSecModel, Pdu, + ErrorInfo, Log) of + {ok, Report} -> + throw({discarded, {securityError, Reason}, Report}); + {discarded, _SomeOtherReason} -> + throw({discarded, {securityError, Reason}}) + end; + {error, Reason, ErrorInfo} -> + ?vdebug("security module result when not reportable:" + "~n Reason: ~p" + "~n ErrorInfo: ~p", [Reason, ErrorInfo]), + throw({discarded, {securityError, Reason}}); + Else -> + ?vdebug("security module result:" + "~n Else: ~p", [Else]), + throw({discarded, {securityError, Else}}) + end. + +get_scoped_pdu(D) when is_list(D) -> + (catch snmp_pdus:dec_scoped_pdu(D)); +get_scoped_pdu(D) -> + D. + + +%%----------------------------------------------------------------- +%% Executed when a response or report message is generated. +%%----------------------------------------------------------------- +generate_response_msg(Vsn, RePdu, Type, ACMData, Log) -> + generate_response_msg(Vsn, RePdu, Type, ACMData, Log, 1). + +generate_response_msg(Vsn, RePdu, Type, + {community, _SecModel, Community, _IpUdp}, + Log, _) -> + case catch snmp_pdus:enc_pdu(RePdu) of + {'EXIT', Reason} -> + user_err("failed encoding pdu: " + "(pdu: ~w, community: ~w): ~n~w", + [RePdu, Community, Reason]), + {discarded, Reason}; + PduBytes -> + Message = #message{version = Vsn, vsn_hdr = Community, + data = PduBytes}, + case catch list_to_binary( + snmp_pdus:enc_message_only(Message)) of + {'EXIT', Reason} -> + user_err("failed encoding message only " + "(pdu: ~w, community: ~w): ~n~w", + [RePdu, Community, Reason]), + {discarded, Reason}; + Packet -> + MMS = snmp_framework_mib:get_engine_max_message_size(), + case size(Packet) of + Len when Len =< MMS -> + Log(Type, Packet), + inc_snmp_cnt_vars(Type, RePdu), + inc_snmp_out_vars(RePdu), + {ok, Packet}; + Len -> + ?vlog("pdu to big:" + "~n Max message size: ~p" + "~n Encoded message size: ~p", + [MMS,Len]), + too_big(Vsn, RePdu, Community, Log, MMS, Len) + end + end + end; +generate_response_msg(Vsn, RePdu, Type, + {v3, MsgID, MsgSecurityModel, SecName, SecLevel, + ContextEngineID, ContextName, SecData}, + Log, N) -> + %% rfc2272: 7.1 steps 6-8 + ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = RePdu}, + case catch snmp_pdus:enc_scoped_pdu(ScopedPDU) of + {'EXIT', Reason} -> + user_err("failed encoded scoped pdu " + "(pdu: ~w, contextName: ~w): ~n~w", + [RePdu, ContextName, Reason]), + {discarded, Reason}; + ScopedPDUBytes -> + AgentMS = snmp_framework_mib:get_engine_max_message_size(), + V3Hdr = #v3_hdr{msgID = MsgID, + msgMaxSize = AgentMS, + msgFlags = snmp_misc:mk_msg_flags(Type, SecLevel), + msgSecurityModel = MsgSecurityModel}, + Message = #message{version = Vsn, + vsn_hdr = V3Hdr, + data = ScopedPDUBytes}, + %% We know that the security model is valid when we + %% generate a response. + SecModule = + case MsgSecurityModel of + ?SEC_USM -> + snmpa_usm + end, + SecEngineID = snmp_framework_mib:get_engine_id(), + ?vtrace("generate_response_msg -> SecEngineID: ~w", [SecEngineID]), + case (catch SecModule:generate_outgoing_msg(Message, + SecEngineID, + SecName, + SecData, + SecLevel)) of + {'EXIT', Reason} -> + config_err("~p (message: ~p)", [Reason, Message]), + {discarded, Reason}; + {error, Reason} -> + config_err("~p (message: ~p)", [Reason, Message]), + {discarded, Reason}; + OutMsg when is_list(OutMsg) -> + %% Check the packet size. Send the msg even + %% if it's larger than the mgr can handle - it + %% will be dropped. Just check against the + %% internal size. For GET-BULk responses: we + %% *know* that we're within the right limits, + %% because of the calculation we do when we + %% receive the bulk-request. + Packet = list_to_binary(OutMsg), + case size(Packet) of + Len when Len =< AgentMS -> + if + SecLevel =:= 3 -> + %% encrypted - log decrypted pdu + Log(Type, {V3Hdr, ScopedPDUBytes}); + true -> + %% otherwise log the entire msg + Log(Type, Packet) + end, + inc_snmp_cnt_vars(Type, RePdu), + inc_snmp_out_vars(RePdu), + {ok, Packet}; + Len when N =:= 2 -> + ?vlog("packet max size exceeded: " + "~n Max: ~p" + "~n Len: ~p", + [AgentMS,Len]), + inc(snmpSilentDrops), + {discarded, tooBig}; + Len -> + ?vlog("packet max size exceeded: " + "~n N: ~p" + "~n Max: ~p" + "~n Len: ~p", + [N, AgentMS, Len]), + TooBigPdu = RePdu#pdu{error_status = tooBig, + error_index = 0, + varbinds = []}, + generate_response_msg(Vsn, TooBigPdu, Type, + {v3, MsgID, + MsgSecurityModel, + SecName, SecLevel, + ContextEngineID, + ContextName, + SecData}, Log, N+1) + end + end + end. + +generate_v3_report_msg(MsgID, MsgSecurityModel, Data, ErrorInfo, Log) -> + {Varbind, SecName, Opts} = ErrorInfo, + ReqId = + if + is_record(Data, scopedPdu) -> + (Data#scopedPdu.data)#pdu.request_id; + true -> + 0 %% RFC2572, 7.1.3.c.4 + end, + ?vtrace("Report ReqId: ~p",[ReqId]), + Pdu = #pdu{type = report, + request_id = ReqId, + error_status = noError, + error_index = 0, + varbinds = [Varbind]}, + SecLevel = snmp_misc:get_option(securityLevel, Opts, 0), + SnmpEngineID = snmp_framework_mib:get_engine_id(), + ContextEngineID = + snmp_misc:get_option(contextEngineID, Opts, SnmpEngineID), + ContextName = snmp_misc:get_option(contextName, Opts, ""), + SecData = snmp_misc:get_option(sec_data, Opts, []), + + generate_response_msg('version-3', Pdu, report, + {v3, MsgID, MsgSecurityModel, SecName, SecLevel, + ContextEngineID, ContextName, SecData}, Log). + +%% req_id(#scopedPdu{data = #pdu{request_id = ReqId}}) -> +%% ?vtrace("Report ReqId: ~p",[ReqId]), +%% ReqId; +%% req_id(_) -> +%% 0. % RFC2572, 7.1.3.c.4 + + +%% maybe_generate_discovery1_report_msg() -> +%% case (catch DiscoveryHandler:handle_discovery1(Ip, Udp, EngineId)) of +%% {ok, Entry} when is_record(Entry, snmp_discovery_data1) -> +%% ok; +%% ignore -> +%% ok; +%% {error, Reason} -> + +%% Response to stage 1 discovery message (terminating, i.e. from the manager) +generate_discovery1_report_msg(MsgID, MsgSecurityModel, + SecName, SecLevel, + ContextEngineID, ContextName, + {SecData, Oid, Value}, + #pdu{request_id = ReqId}, Log) -> + ?vtrace("generate_discovery1_report_msg -> entry with" + "~n ReqId: ~p" + "~n Value: ~p", [ReqId, Value]), + Varbind = #varbind{oid = Oid, + variabletype = 'Counter32', + value = Value, + org_index = 1}, + PduOut = #pdu{type = report, + request_id = ReqId, + error_status = noError, + error_index = 0, + varbinds = [Varbind]}, + case generate_response_msg('version-3', PduOut, report, + {v3, MsgID, MsgSecurityModel, SecName, SecLevel, + ContextEngineID, ContextName, SecData}, Log) of + {ok, Packet} -> + {discovery, Packet}; + Error -> + Error + end. + +%% Response to stage 2 discovery message (terminating, i.e. from the manager) +generate_discovery2_report_msg(MsgID, MsgSecurityModel, + SecName, SecLevel, + ContextEngineID, ContextName, + SecData, #pdu{request_id = ReqId}, Log) -> + ?vtrace("generate_discovery2_report_msg -> entry with" + "~n ReqId: ~p", [ReqId]), + SecModule = get_security_module(MsgSecurityModel), + Vb = SecModule:current_statsNotInTimeWindows_vb(), + PduOut = #pdu{type = report, + request_id = ReqId, + error_status = noError, + error_index = 0, + varbinds = [Vb]}, + case generate_response_msg('version-3', PduOut, report, + {v3, MsgID, MsgSecurityModel, SecName, SecLevel, + ContextEngineID, ContextName, SecData}, Log) of + {ok, Packet} -> + {discovery, Packet}; + Error -> + Error + end. + + +too_big(Vsn, Pdu, Community, Log, _MMS, _Len) + when Pdu#pdu.type =:= 'get-response' -> + ErrPdu = + if + Vsn =:= 'version-1' -> + %% In v1, the varbinds should be identical to the incoming + %% request. It isn't identical now! + %% Make acceptable (?) approximation. + V = set_vb_null(Pdu#pdu.varbinds), + Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = V}; + true -> + %% In v2, varbinds should be empty (reasonable!) + Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = []} + end, + + case catch snmp_pdus:enc_pdu(ErrPdu) of + {'EXIT', Reason} -> + user_err("failed encoding pdu (pdu: ~w, community: ~w): ~n~w", + [ErrPdu, Community, Reason]), + {discarded, Reason}; + PduBytes -> + Message = #message{version = Vsn, vsn_hdr = Community, + data = PduBytes}, + case catch snmp_pdus:enc_message_only(Message) of + {'EXIT', Reason} -> + user_err("failed encoding message only" + "(pdu: ~w, community: ~w): ~n~w", + [ErrPdu, Community, Reason]), + {discarded, Reason}; + Packet -> + Bin = list_to_binary(Packet), + Log(Pdu#pdu.type, Bin), + inc_snmp_out_vars(ErrPdu), + {ok, Bin} + end + end; +too_big(_Vsn, Pdu, _Community, _Log, MMS, Len) -> + user_err("encoded pdu, ~p bytes, exceeded " + "max message size of ~p bytes. Pdu: ~n~w", + [Len, MMS, Pdu]), + {discarded, tooBig}. + +set_vb_null([Vb | Vbs]) -> + [Vb#varbind{variabletype = 'NULL', value = 'NULL'} | set_vb_null(Vbs)]; +set_vb_null([]) -> + []. + +%%----------------------------------------------------------------- +%% Executed when a message that isn't a response is generated, i.e. +%% a trap or an inform. +%%----------------------------------------------------------------- +generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, To) -> + Message = #message{version = Vsn, vsn_hdr = Community, data = Pdu}, + case catch list_to_binary(snmp_pdus:enc_message(Message)) of + {'EXIT', Reason} -> + user_err("failed encoding message " + "(pdu: ~w, community: ~w): ~n~w", + [Pdu, Community, Reason]), + {discarded, Reason}; + Packet -> + AgentMax = snmp_framework_mib:get_engine_max_message_size(), + case size(Packet) of + Len when Len =< AgentMax -> + {ok, mk_v1_v2_packet_list(To, Packet, Len, Pdu)}; + Len -> + ?vlog("packet max size exceeded: " + "~n Max: ~p" + "~n Len: ~p", + [AgentMax, Len]), + {discarded, tooBig} + end + end; +generate_msg('version-3', NoteStore, Pdu, + {v3, ContextEngineID, ContextName}, To) -> + %% rfc2272: 7.1.6 + ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = Pdu}, + case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of + {'EXIT', Reason} -> + user_err("failed encoding scoped pdu " + "(pdu: ~w, contextName: ~w): ~n~w", + [Pdu, ContextName, Reason]), + {discarded, Reason}; + ScopedPDUBytes -> + {ok, mk_v3_packet_list(NoteStore, To, ScopedPDUBytes, Pdu, + ContextEngineID, ContextName)} + end. + + +generate_discovery_msg(NoteStore, Pdu, MsgData, To) -> + Timeout = 1500, + generate_discovery_msg(NoteStore, Pdu, MsgData, Timeout, To). + +generate_discovery_msg(NoteStore, Pdu, MsgData, Timeout, To) -> + {SecData, ContextEngineID, ContextName} = MsgData, + {SecModel, SecName, SecLevelFlag, TargetName} = SecData, + {ManagerEngineId, InitialUserName} = + case get_target_engine_id(TargetName) of + {ok, discovery} -> + {"", ""}; % Discovery stage 1 + {ok, {discovery, IUN}} -> + {"", IUN}; % Discovery stage 1 + {ok, TargetEngineId} -> + {TargetEngineId, ""} % Discovery stage 2 + end, + generate_discovery_msg(NoteStore, Pdu, + ContextEngineID, ContextName, + SecModel, SecName, SecLevelFlag, + ManagerEngineId, + InitialUserName, + Timeout, To). + +generate_discovery_msg(NoteStore, Pdu, + ContextEngineID, ContextName, + SecModel, _SecName, _SecLevelFlag, + "" = ManagerEngineID, + InitialUserName, + Timeout, To) -> + %% Discovery step 1 uses SecLevel = noAuthNoPriv + SecName = "", + SecLevelFlag = 0, % ?'SnmpSecurityLevel_noAuthNoPriv', + generate_discovery_msg2(NoteStore, Pdu, + ContextEngineID, ManagerEngineID, + SecModel, SecName, SecLevelFlag, + InitialUserName, + ContextName, Timeout, To); +generate_discovery_msg(NoteStore, Pdu, + ContextEngineID, ContextName, + SecModel, SecName, SecLevelFlag, + ManagerEngineID, + InitialUserName, + Timeout, To) -> + %% SecLevelFlag = 1, % ?'SnmpSecurityLevel_authNoPriv', + generate_discovery_msg2(NoteStore, Pdu, + ContextEngineID, ManagerEngineID, + SecModel, SecName, SecLevelFlag, + InitialUserName, + ContextName, Timeout, To). + +generate_discovery_msg2(NoteStore, Pdu, + ContextEngineID, ManagerEngineID, + SecModel, SecName, SecLevelFlag, + InitialUserName, + ContextName, Timeout, To) -> + %% rfc2272: 7.1.6 + ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, + contextName = ContextName, + data = Pdu}, + case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of + {'EXIT', Reason} -> + user_err("failed encoding scoped pdu " + "(pdu: ~w, contextName: ~w): ~n~w", + [Pdu, ContextName, Reason]), + {discarded, Reason}; + ScopedPDUBytes -> + {ok, generate_discovery_msg(NoteStore, To, + Pdu, ScopedPDUBytes, + ContextEngineID, ManagerEngineID, + SecModel, SecName, SecLevelFlag, + InitialUserName, + ContextName, Timeout)} + end. + +%% Timeout is in msec but note timeout is in 1/10 seconds +discovery_note_timeout(Timeout) -> + (Timeout div 100) + 1. + +generate_discovery_msg(NoteStore, {?snmpUDPDomain, [A,B,C,D,U1,U2]}, + Pdu, ScopedPduBytes, + ContextEngineID, ManagerEngineID, + SecModel, SecName, SecLevelFlag, + InitialUserName, + ContextName, Timeout) -> + %% 7.1.7 + ?vdebug("generate_discovery_msg -> 7.1.7 (~w)", [ManagerEngineID]), + MsgID = generate_msg_id(), + PduType = Pdu#pdu.type, + MsgFlags = mk_msg_flags(PduType, SecLevelFlag), + V3Hdr = #v3_hdr{msgID = MsgID, + msgMaxSize = get_max_message_size(), + msgFlags = MsgFlags, + msgSecurityModel = SecModel}, + Message = #message{version = 'version-3', + vsn_hdr = V3Hdr, + data = ScopedPduBytes}, + SecModule = sec_module(SecModel), + + %% 7.1.9b + ?vdebug("generate_discovery_msg -> 7.1.9b", []), + case generate_sec_discovery_msg(Message, SecModule, + ManagerEngineID, + SecName, SecLevelFlag, + InitialUserName) of + {ok, Packet} -> + %% 7.1.9c + %% Store in cache for Timeout msec. + NoteTimeout = discovery_note_timeout(Timeout), + ?vdebug("generate_discovery_msg -> 7.1.9c [~w]", [NoteTimeout]), + %% The request id is just in case when we receive a + %% report with incorrect securityModel and/or securityLevel + Key = {agent, MsgID}, + Note = #note{sec_engine_id = ManagerEngineID, + sec_model = SecModel, + sec_name = SecName, + sec_level = SecLevelFlag, + ctx_engine_id = ContextEngineID, + ctx_name = ContextName, + disco = true, + req_id = Pdu#pdu.request_id}, + snmp_note_store:set_note(NoteStore, Timeout, Key, Note), + %% Log(Packet), + inc_snmp_out_vars(Pdu), + ?vdebug("generate_discovery_msg -> done", []), + {Packet, {{A,B,C,D}, U1 bsl 8 + U2}}; + + Error -> + throw(Error) + end. + +generate_sec_discovery_msg(Message, SecModule, + SecEngineID, SecName, SecLevelFlag, + InitialUserName) -> + case (catch SecModule:generate_discovery_msg(Message, SecEngineID, + SecName, SecLevelFlag, + InitialUserName)) of + {'EXIT', Reason} -> + config_err("~p (message: ~p)", [Reason, Message]), + {discarded, Reason}; + {error, Reason} -> + config_err("~p (message: ~p)", [Reason, Message]), + {discarded, Reason}; + Bin when is_binary(Bin) -> + {ok, Bin}; + OutMsg when is_list(OutMsg) -> + case (catch list_to_binary(OutMsg)) of + Bin when is_binary(Bin) -> + {ok, Bin}; + {'EXIT', Reason} -> + {error, Reason} + end + end. + + +process_taddrs(Dests) -> + ?vtrace("process_taddrs -> entry with" + "~n Dests: ~p", [Dests]), + process_taddrs(Dests, []). + +process_taddrs([], Acc) -> + ?vtrace("process_taddrs -> entry when done with" + "~n Acc: ~p", [Acc]), + lists:reverse(Acc); + +%% v3 +process_taddrs([{{?snmpUDPDomain, [A,B,C,D,U1,U2]}, SecData} | T], Acc) -> + ?vtrace("process_taddrs -> entry when v3 with" + "~n A: ~p" + "~n B: ~p" + "~n C: ~p" + "~n D: ~p" + "~n U1: ~p" + "~n U2: ~p" + "~n SecData: ~p", [A, B, C, D, U1, U2, SecData]), + Entry = {{snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}}, SecData}, + process_taddrs(T, [Entry | Acc]); +%% Bad v3 +process_taddrs([{{TDomain, TAddr}, _SecData} | T], Acc) -> + ?vtrace("process_taddrs -> entry when bad v3 with" + "~n TDomain: ~p" + "~n TAddr: ~p", [TDomain, TAddr]), + user_err("Bad TDomain/TAddr: ~w/~w", [TDomain, TAddr]), + process_taddrs(T, Acc); +%% v1 & v2 +process_taddrs([{?snmpUDPDomain, [A,B,C,D,U1,U2]} | T], Acc) -> + ?vtrace("process_taddrs -> entry when v1/v2 with" + "~n A: ~p" + "~n B: ~p" + "~n C: ~p" + "~n D: ~p" + "~n U1: ~p" + "~n U2: ~p", [A, B, C, D, U1, U2]), + Entry = {snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}}, + process_taddrs(T, [Entry | Acc]); +%% Bad v1 or v2 +process_taddrs([{TDomain, TAddr} | T], Acc) -> + ?vtrace("process_taddrs -> entry when bad v1/v2 with" + "~n TDomain: ~p" + "~n TAddr: ~p", [TDomain, TAddr]), + user_err("Bad TDomain/TAddr: ~w/~w", [TDomain, TAddr]), + process_taddrs(T, Acc); +process_taddrs(Crap, Acc) -> + throw({error, {taddrs_crap, Crap, Acc}}). + + +mk_v1_v2_packet_list(To, Packet, Len, Pdu) -> + mk_v1_v2_packet_list(To, Packet, Len, Pdu, []). + +mk_v1_v2_packet_list([], _Packet, _Len, _Pdu, Acc) -> + lists:reverse(Acc); + +%% This (old) clause is for backward compatibillity reasons +%% If this is called, then the filter function is not used +mk_v1_v2_packet_list([{?snmpUDPDomain, [A,B,C,D,U1,U2]} | T], + Packet, Len, Pdu, Acc) -> + %% Sending from default UDP port + inc_snmp_out_vars(Pdu), + Entry = {snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}, Packet}, + mk_v1_v2_packet_list(T, Packet, Len, Pdu, [Entry | Acc]); + +%% This is the new clause +%% This is only called if the actual target was accepted +%% (by the filter module) +mk_v1_v2_packet_list([{Domain, Addr} | T], + Packet, Len, Pdu, Acc) -> + %% Sending from default UDP port + inc_snmp_out_vars(Pdu), + Entry = {Domain, Addr, Packet}, + mk_v1_v2_packet_list(T, Packet, Len, Pdu, [Entry | Acc]). + + +get_max_message_size() -> + snmp_framework_mib:get_engine_max_message_size(). + +mk_msg_flags(PduType, SecLevel) -> + snmp_misc:mk_msg_flags(PduType, SecLevel). + +mk_v3_packet_entry(NoteStore, Domain, Addr, + {SecModel, SecName, SecLevel, TargetAddrName}, + ScopedPDUBytes, Pdu, ContextEngineID, ContextName) -> + %% 7.1.7 + ?vtrace("mk_v3_packet_entry -> entry - 7.1.7", []), + MsgID = generate_msg_id(), + PduType = Pdu#pdu.type, + MsgFlags = mk_msg_flags(PduType, SecLevel), + V3Hdr = #v3_hdr{msgID = MsgID, + msgMaxSize = get_max_message_size(), + msgFlags = MsgFlags, + msgSecurityModel = SecModel}, + Message = #message{version = 'version-3', + vsn_hdr = V3Hdr, + data = ScopedPDUBytes}, + SecModule = + case SecModel of + ?SEC_USM -> + snmpa_usm + end, + + %% 7.1.9a + ?vtrace("mk_v3_packet_entry -> sec engine id - 7.1.9a", []), + SecEngineID = + case PduType of + 'snmpv2-trap' -> + snmp_framework_mib:get_engine_id(); + _ -> + %% This is the implementation dependent target engine id + %% procedure. + case get_target_engine_id(TargetAddrName) of + {ok, discovery} -> + config_err("Discovery has not yet been performed for " + "snmpTargetAddrName ~p~n", + [TargetAddrName]), + throw({discarded, {discovery, TargetAddrName}}); + {ok, TargetEngineId} -> + ?vtrace("TargetEngineId: ~p", [TargetEngineId]), + TargetEngineId; + undefined -> + config_err("Can't find engineID for " + "snmpTargetAddrName ~p~n", + [TargetAddrName]), + "" % this will trigger error in secmodule + end + end, + + ?vdebug("mk_v3_packet_entry -> secEngineID: ~p", [SecEngineID]), + %% 7.1.9b + case catch SecModule:generate_outgoing_msg(Message, SecEngineID, + SecName, [], SecLevel) of + {'EXIT', Reason} -> + config_err("~p (message: ~p)", [Reason, Message]), + skip; + {error, Reason} -> + ?vlog("~n ~w error ~p\n", [SecModule, Reason]), + skip; + OutMsg when is_list(OutMsg) -> + %% 7.1.9c + %% Store in cache for 150 sec. + Packet = list_to_binary(OutMsg), + ?vdebug("mk_v3_packet_entry -> generated: ~w bytes", + [size(Packet)]), + Data = + if + SecLevel =:= 3 -> + %% encrypted - log decrypted pdu + {Packet, {V3Hdr, ScopedPDUBytes}}; + true -> + %% otherwise log the entire msg + Packet + end, + CacheKey = {agent, MsgID}, + CacheVal = #note{sec_engine_id = SecEngineID, + sec_model = SecModel, + sec_name = SecName, + sec_level = SecLevel, + ctx_engine_id = ContextEngineID, + ctx_name = ContextName, + disco = false, + req_id = Pdu#pdu.request_id}, + snmp_note_store:set_note(NoteStore, 1500, CacheKey, CacheVal), + inc_snmp_out_vars(Pdu), + {ok, {Domain, Addr, Data}} + end. + + +mk_v3_packet_list(NoteStore, To, + ScopedPDUBytes, Pdu, ContextEngineID, ContextName) -> + mk_v3_packet_list(NoteStore, To, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName, []). + +mk_v3_packet_list(_, [], + _ScopedPDUBytes, _Pdu, + _ContextEngineID, _ContextName, + Acc) -> + lists:reverse(Acc); + +%% This clause is for backward compatibillity reasons +%% If this is called the filter function is not used +mk_v3_packet_list(NoteStore, + [{{?snmpUDPDomain, [A,B,C,D,U1,U2]}, SecData} | T], + ScopedPDUBytes, Pdu, ContextEngineID, ContextName, + Acc) -> + case mk_v3_packet_entry(NoteStore, + snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}, SecData, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName) of + skip -> + mk_v3_packet_list(NoteStore, T, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName, + Acc); + {ok, Entry} -> + mk_v3_packet_list(NoteStore, T, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName, [Entry | Acc]) + end; + +%% This is the new clause +%% This is only called if the actual target was accepted +%% (by the filter module) +mk_v3_packet_list(NoteStore, + [{{Domain, Addr}, SecData} | T], + ScopedPDUBytes, Pdu, ContextEngineID, ContextName, + Acc) -> + case mk_v3_packet_entry(NoteStore, + Domain, Addr, SecData, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName) of + skip -> + mk_v3_packet_list(NoteStore, T, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName, Acc); + {ok, Entry} -> + mk_v3_packet_list(NoteStore, T, + ScopedPDUBytes, Pdu, + ContextEngineID, ContextName, [Entry | Acc]) + end. + + +generate_msg_id() -> + gen(msg_id). + +generate_req_id() -> + gen(req_id). + +gen(Id) -> + case ets:update_counter(snmp_agent_table, Id, 1) of + N when N =< 2147483647 -> + N; + _N -> + ets:insert(snmp_agent_table, {Id, 0}), + 0 + end. + + +get_target_engine_id(TargetAddrName) -> + snmp_target_mib:get_target_engine_id(TargetAddrName). + +sec_module(?SEC_USM) -> + snmpa_usm. + + +%%----------------------------------------------------------------- +%% Version(s) functions +%%----------------------------------------------------------------- +init_versions([], S) -> + S; +init_versions([v1|Vsns], S) -> + init_versions(Vsns, S#state{v1 = true}); +init_versions([v2|Vsns], S) -> + init_versions(Vsns, S#state{v2c = true}); +init_versions([v3|Vsns], S) -> + init_versions(Vsns, S#state{v3 = true}). + + +%%----------------------------------------------------------------- +%% Counter functions +%%----------------------------------------------------------------- +init_counters() -> + F = fun(Counter) -> maybe_create_counter(Counter) end, + lists:map(F, counters()). + +reset_counters() -> + F = fun(Counter) -> init_counter(Counter) end, + lists:map(F, counters()). + +maybe_create_counter(Counter) -> + case ets:lookup(snmp_agent_table, Counter) of + [_] -> ok; + _ -> init_counter(Counter) + end. + +init_counter(Counter) -> + ets:insert(snmp_agent_table, {Counter, 0}). + +counters() -> + [ + snmpInPkts, + snmpOutPkts, + snmpInBadVersions, + snmpInBadCommunityNames, + snmpInBadCommunityUses, + snmpInASNParseErrs, + snmpInTooBigs, + snmpInNoSuchNames, + snmpInBadValues, + snmpInReadOnlys, + snmpInGenErrs, + snmpInTotalReqVars, + snmpInTotalSetVars, + snmpInGetRequests, + snmpInGetNexts, + snmpInSetRequests, + snmpInGetResponses, + snmpInTraps, + snmpOutTooBigs, + snmpOutNoSuchNames, + snmpOutBadValues, + snmpOutGenErrs, + snmpOutGetRequests, + snmpOutGetNexts, + snmpOutSetRequests, + snmpOutGetResponses, + snmpOutTraps, + snmpSilentDrops, + snmpProxyDrops, + %% From SNMP-MPD-MIB + snmpUnknownSecurityModels, + snmpInvalidMsgs, + snmpUnknownPDUHandlers + ]. + + + +%%----------------------------------------------------------------- +%% inc(VariableName) increments the variable (Counter) in +%% the local mib. (e.g. snmpInPkts) +%%----------------------------------------------------------------- +inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). +inc(Name, N) -> ets:update_counter(snmp_agent_table, Name, N). + +inc_snmp_in_vars(#pdu{type = Type}) -> + inc_in_type(Type). + +inc_snmp_cnt_vars(_, #pdu{error_status = ErrStat}) when ErrStat =/= noError -> + ok; +inc_snmp_cnt_vars('get-request', #pdu{varbinds = Vbs}) -> + inc(snmpInTotalReqVars, length(Vbs)); +inc_snmp_cnt_vars('get-next-request', #pdu{varbinds = Vbs}) -> + inc(snmpInTotalReqVars, length(Vbs)); +inc_snmp_cnt_vars('set-request', #pdu{varbinds = Vbs}) -> + inc(snmpInTotalSetVars, length(Vbs)); +inc_snmp_cnt_vars(_, _) -> + ok. + +inc_snmp_out_vars(#pdu{type = Type, + error_status = ErrorStatus}) -> + inc(snmpOutPkts), + inc_out_err(ErrorStatus), + inc_out_vars_2(Type); +inc_snmp_out_vars(TrapPdu) when is_record(TrapPdu, trappdu) -> + inc(snmpOutPkts), + inc(snmpOutTraps). + +inc_out_vars_2('get-response') -> inc(snmpOutGetResponses); +inc_out_vars_2('get-request') -> inc(snmpOutGetRequests); +inc_out_vars_2('get-next-request') -> inc(snmpOutGetNexts); +inc_out_vars_2('set-request') -> inc(snmpOutSetRequests); +inc_out_vars_2(_) -> ok. + +inc_out_err(genErr) -> inc(snmpOutGenErrs); +inc_out_err(tooBig) -> inc(snmpOutTooBigs); +inc_out_err(noSuchName) -> inc(snmpOutNoSuchNames); +inc_out_err(badValue) -> inc(snmpOutBadValues); +% snmpOutReadOnlys is not used any more (rfc1213) +%inc_out_err(readOnly) -> inc(snmpOutReadOnlys); +inc_out_err(_) -> ok. + +inc_in_type('get-request') -> inc(snmpInGetRequests); +inc_in_type('get-next-request') -> inc(snmpInGetNexts); +inc_in_type('set-request') -> inc(snmpInSetRequests); +inc_in_type(_) -> ok. + + +user_err(F, A) -> + snmpa_error:user_err(F, A). + +config_err(F, A) -> + snmpa_error:config_err(F, A). |