aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/snmpa_mpd.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/snmp/src/agent/snmpa_mpd.erl
downloadotp-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.erl1386
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).