1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2004-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(snmpm_mpd). 22 23-export([init/1, 24 25 process_msg/7, process_msg/6, 26 generate_msg/5, generate_response_msg/4, 27 28 next_msg_id/0, 29 next_req_id/0, 30 31 reset/1, 32 inc/1]). 33 34-define(SNMP_USE_V3, true). 35-include("snmp_types.hrl"). 36-include("snmpm_internal.hrl"). 37-include("SNMP-MPD-MIB.hrl"). 38-include("SNMPv2-TM.hrl"). 39 40-define(VMODULE,"MPD"). 41-include("snmp_verbosity.hrl"). 42 43-define(empty_msg_size, 24). 44 45-record(state, {v1 = false, v2c = false, v3 = false}). 46 47 48%%%----------------------------------------------------------------- 49%%% This module implemets the Message Processing and Dispatch part of 50%%% the multi-lingual SNMP agent. 51%%% 52%%% The MPD is responsible for: 53%%% *) call the security module (auth/priv). 54%%% *) decoding the message into a PDU. 55%%% *) decide a suitable Access Control Model, and provide it with 56%%% the data it needs. 57%%% *) maintaining SNMP counters. 58%%% 59%%% In order to take care of the different versions of counters, it 60%%% implements and maintains the union of all SNMP counters (i.e. from 61%%% rfc1213 and from rfc1907). It is up to the administrator of the 62%%% agent to load the correct MIB. Note that this module implements 63%%% the counters only, it does not provide instrumentation functions 64%%% for the counters. 65%%% 66%%% With the terms defined in rfc2271, this module implememts part 67%%% of the Dispatcher and the Message Processing functionality. 68%%%----------------------------------------------------------------- 69init(Vsns) -> 70 ?vdebug("init -> entry with ~p", [Vsns]), 71 ?SNMP_RAND_SEED(), 72 %% rand:seed(exrop, 73 %% {erlang:phash2([node()]), 74 %% erlang:monotonic_time(), 75 %% erlang:unique_integer()}), 76 snmpm_config:cre_counter(msg_id, rand:uniform(2147483647)), 77 snmpm_config:cre_counter(req_id, rand:uniform(2147483647)), 78 init_counters(), 79 State = init_versions(Vsns, #state{}), 80 init_usm(State#state.v3), 81 ?vtrace("init -> done when ~p", [State]), 82 State. 83 84reset(#state{v3 = V3}) -> 85 reset_counters(), 86 reset_usm(V3). 87 88 89%%----------------------------------------------------------------- 90%% Func: process_msg(Packet, TDomain, TAddress, State) -> 91%% {ok, SnmpVsn, Pdu, PduMS, ACMData} | {discarded, Reason} 92%% Types: Packet = binary() 93%% TDomain = snmpUDPDomain | atom() 94%% TAddress = {Ip, Udp} 95%% State = #state 96%% Purpose: This is the main Message Dispatching function. (see 97%% section 4.2.1 in rfc2272) 98%%----------------------------------------------------------------- 99process_msg(Msg, Domain, Ip, Port, State, NoteStore, Logger) -> 100 process_msg(Msg, Domain, {Ip, Port}, State, NoteStore, Logger). 101 102process_msg(Msg, Domain, Addr, State, NoteStore, Logger) -> 103 inc(snmpInPkts), 104 105 case (catch snmp_pdus:dec_message_only(binary_to_list(Msg))) of 106 107 %% Version 1 108 #message{version = 'version-1', vsn_hdr = Community, data = Data} 109 when State#state.v1 =:= true -> 110 HS = ?empty_msg_size + length(Community), 111 process_v1_v2c_msg( 112 'version-1', NoteStore, Msg, Domain, Addr, 113 Community, Data, HS, Logger); 114 115 %% Version 2 116 #message{version = 'version-2', vsn_hdr = Community, data = Data} 117 when State#state.v2c =:= true -> 118 HS = ?empty_msg_size + length(Community), 119 process_v1_v2c_msg( 120 'version-2', NoteStore, Msg, Domain, Addr, 121 Community, Data, HS, Logger); 122 123 %% Version 3 124 #message{version = 'version-3', vsn_hdr = H, data = Data} 125 when State#state.v3 =:= true -> 126 ?vlog("v3:" 127 "~n msgID: ~p" 128 "~n msgFlags: ~p" 129 "~n msgSecModel: ~p", 130 [H#v3_hdr.msgID,H#v3_hdr.msgFlags,H#v3_hdr.msgSecurityModel]), 131 process_v3_msg(NoteStore, Msg, H, Data, Addr, Logger); 132 133 %% Crap 134 {'EXIT', {bad_version, Vsn}} -> 135 ?vinfo("exit: bad version: ~p",[Vsn]), 136 inc(snmpInBadVersions), 137 {discarded, snmpInBadVersions}; 138 139 %% More crap 140 {'EXIT', Reason} -> 141 ?vinfo("exit: ~p",[Reason]), 142 inc(snmpInASNParseErrs), 143 {discarded, Reason}; 144 145 %% Really crap 146 Crap -> 147 ?vinfo("unknown message: " 148 "~n ~p",[Crap]), 149 inc(snmpInBadVersions), 150 {discarded, snmpInBadVersions} 151 end. 152 153 154%%----------------------------------------------------------------- 155%% Handles a Community based message (v1 or v2c). 156%%----------------------------------------------------------------- 157process_v1_v2c_msg( 158 Vsn, _NoteStore, Msg, Domain, Addr, Community, Data, HS, Log) -> 159 160 ?vdebug("process_v1_v2c_msg -> entry with" 161 "~n Vsn: ~p" 162 "~n Domain: ~p" 163 "~n Addr: ~p" 164 "~n Community: ~p" 165 "~n HS: ~p", [Vsn, Domain, Addr, Community, HS]), 166 167 {TDomain, TAddress} = 168 try 169 {snmp_conf:mk_tdomain(Domain), 170 snmp_conf:mk_taddress(Domain, Addr)} 171 catch 172 throw:{error, TReason} -> 173 throw({discarded, {badarg, Domain, TReason}}) 174 end, 175 176 Max = get_max_message_size(), 177 AgentMax = get_agent_max_message_size(Domain, Addr), 178 PduMS = pdu_ms(Max, AgentMax, HS), 179 180 ?vtrace("process_v1_v2c_msg -> PduMS: ~p", [PduMS]), 181 182 case (catch snmp_pdus:dec_pdu(Data)) of 183 Pdu when is_record(Pdu, pdu) -> 184 ?vtrace("process_v1_v2c_msg -> was a pdu", []), 185 Log(Msg), 186 inc_snmp_in(Pdu), 187 MsgData = {Community, sec_model(Vsn), TDomain, TAddress}, 188 {ok, Vsn, Pdu, PduMS, MsgData}; 189 190 Trap when is_record(Trap, trappdu) -> 191 ?vtrace("process_v1_v2c_msg -> was a trap", []), 192 Log(Msg), 193 inc_snmp_in(Trap), 194 MsgData = {Community, sec_model(Vsn), TDomain, TAddress}, 195 {ok, Vsn, Trap, PduMS, MsgData}; 196 197 {'EXIT', Reason} -> 198 ?vlog("process_v1_v2c_msg -> failed decoding PDU: " 199 "~n Reason: ~p", [Reason]), 200 inc(snmpInASNParseErrs), 201 {discarded, Reason} 202 end. 203 204pdu_ms(MgrMMS, AgentMMS, HS) when AgentMMS < MgrMMS -> 205 AgentMMS - HS; 206pdu_ms(MgrMMS, _AgentMMS, HS) -> 207 MgrMMS - HS. 208 209sec_model('version-1') -> ?SEC_V1; 210sec_model('version-2') -> ?SEC_V2C. 211 212 213%%----------------------------------------------------------------- 214%% Handles a SNMPv3 Message, following the procedures in rfc2272, 215%% section 4.2 and 7.2 216%%----------------------------------------------------------------- 217process_v3_msg(NoteStore, Msg, Hdr, Data, Address, Log) -> 218 ?vdebug( 219 "process_v3_msg -> entry with~n" 220 " Hdr: ~p~n" 221 " Address: ~p", [Hdr, Address]), 222 223 %% 7.2.3 224 #v3_hdr{msgID = MsgID, 225 msgMaxSize = MMS, 226 msgFlags = MsgFlags, 227 msgSecurityModel = MsgSecModel, 228 msgSecurityParameters = SecParams, 229 hdr_size = HdrSize} = Hdr, 230 231 %% 7.2.4 232 SecModule = get_security_module(MsgSecModel), 233 ?vtrace("process_v3_msg -> 7.2.4: " 234 "~n SecModule: ~p", [SecModule]), 235 236 %% 7.2.5 237 SecLevel = check_sec_level(MsgFlags), 238 IsReportable = is_reportable(MsgFlags), 239 ?vtrace("process_v3_msg -> 7.2.5: " 240 "~n SecLevel: ~p" 241 "~n IsReportable: ~p", [SecLevel, IsReportable]), 242 243 %% 7.2.6 244 SecRes = (catch SecModule:process_incoming_msg(Msg, Data, 245 SecParams, SecLevel)), 246 ?vtrace("process_v3_msg -> 7.2.6 - message processing result: " 247 "~n ~p",[SecRes]), 248 {SecEngineID, SecName, ScopedPDUBytes, SecData} = 249 check_sec_module_result(SecRes, Hdr, Data, IsReportable, Log), 250 ?vtrace("process_v3_msg -> 7.2.6 - checked module result: " 251 "~n SecEngineID: ~p" 252 "~n SecName: ~p",[SecEngineID, SecName]), 253 254 %% 7.2.7 255 #scopedPdu{contextEngineID = CtxEngineID, 256 contextName = CtxName, 257 data = PDU} = 258 case (catch snmp_pdus:dec_scoped_pdu(ScopedPDUBytes)) of 259 ScopedPDU when is_record(ScopedPDU, scopedPdu) -> 260 ScopedPDU; 261 {'EXIT', Reason} -> 262 ?vlog("failed decoding scoped pdu: " 263 "~n ~p",[Reason]), 264 inc(snmpInASNParseErrs), 265 discard(Reason) 266 end, 267 268 ?vlog("7.2.7" 269 "~n ContextEngineID: ~p " 270 "~n context: \"~s\" ", 271 [CtxEngineID, CtxName]), 272 if 273 SecLevel =:= 3 -> % encrypted message - log decrypted pdu 274 Log({Hdr, ScopedPDUBytes}); 275 true -> % otherwise, log binary 276 Log(Msg) 277 end, 278 279 %% Make sure a get_bulk doesn't get too big. 280 MgrMMS = get_max_message_size(), 281 %% PduMMS is supposed to be the maximum total length of the response 282 %% PDU we can send. From the MMS, we need to subtract everything before 283 %% the PDU, i.e. Message and ScopedPDU. 284 %% Message: [48, TotalLen, Vsn, [Tag, LH, Hdr], [Tag, LM, MsgSec], Data] 285 %% 1 3 <----------- HdrSize -----------> 286 %% HdrSize = everything up to and including msgSecurityParameters. 287 %% ScopedPduData follows. This is 288 %% [Tag, Len, [Tag, L1, CtxName], [Tag, L2, CtxEID]] 289 %% i.e. 6 + length(CtxName) + length(CtxEID) 290 %% 291 %% Total: 1 + TotalLenOctets + 3 + ScopedPduDataLen 292 TotMMS = tot_mms(MgrMMS, MMS), 293 TotalLenOctets = snmp_pdus:get_encoded_length(TotMMS - 1), 294 PduMMS = TotMMS - TotalLenOctets - 10 - HdrSize - 295 length(CtxName) - length(CtxEngineID), 296 ?vtrace("process_v3_msg -> PduMMS = ~p", [PduMMS]), 297 Type = PDU#pdu.type, 298 ?vdebug("process_v3_msg -> PDU type: ~p",[Type]), 299 case Type of 300 report -> 301 %% 7.2.10 & 11 302 %% BMK BMK BMK: discovery? 303 Note = snmp_note_store:get_note(NoteStore, MsgID), 304 case Note of 305 {SecEngineID, MsgSecModel, SecName, SecLevel, 306 CtxEngineID, CtxName, _ReqId} -> 307 ?vtrace("process_v3_msg -> 7.2.11b: ok", []), 308 %% BMK BMK: Should we discard the cached info 309 %% BMK BMK: or do we let the gc deal with it? 310 {ok, 'version-3', PDU, PduMMS, ok}; 311 _ when is_tuple(Note) -> 312 ?vlog("process_v3_msg -> 7.2.11b: error" 313 "~n Note: ~p", [Note]), 314 Recv = {SecEngineID, MsgSecModel, SecName, SecLevel, 315 CtxEngineID, CtxName, PDU#pdu.request_id}, 316 Err = sec_error(Note, Recv), 317 ACM = {invalid_sec_info, Err}, 318 ReqId = element(size(Note), Note), 319 {ok, 'version-3', PDU, PduMMS, {error, ReqId, ACM}}; 320 _NoFound -> 321 ?vtrace("process_v3_msg -> _NoFound: " 322 "~p", [_NoFound]), 323 inc(snmpUnknownPDUHandlers), 324 discard({no_outstanding_req, MsgID}) 325 end; 326 327 'get-response' -> 328 %% 7.2.10 & 12 329 case snmp_note_store:get_note(NoteStore, MsgID) of 330 {SecEngineID, MsgSecModel, SecName, SecLevel, 331 CtxEngineID, CtxName, _} -> 332 %% 7.2.12.d 333 {ok, 'version-3', PDU, PduMMS, undefined}; 334 _ -> 335 %% 7.2.12.b 336 %% BMK BMK: Should we not discard the cached info?? 337 inc(snmpUnknownPDUHandlers), 338 discard({no_outstanding_req, MsgID}) 339 end; 340 341 'snmpv2-trap' -> 342 %% 7.2.14 343 {ok, 'version-3', PDU, PduMMS, undefined}; 344 345 'inform-request' -> 346 %% 7.2.13 347 SnmpEngineID = get_engine_id(), 348 case SecEngineID of 349 SnmpEngineID -> % 7.2.13.b 350 ?vtrace("7.2.13d - valid securityEngineID: ~p", 351 [SecEngineID]), 352 %% 4.2.2.1.1 - we don't handle proxys yet => we only 353 %% handle CtxEngineID to ourselves 354 %% Check that we actually know of an agent with this 355 %% CtxEngineID and Address 356 case is_known_engine_id(CtxEngineID, Address) of 357 true -> 358 ?vtrace("and the agent EngineID (~p) " 359 "is know to us", [CtxEngineID]), 360 %% Uses ACMData that snmpm_acm knows of. 361 %% BUGBUG BUGBUG 362 ACMData = 363 {MsgID, MsgSecModel, SecName, SecLevel, 364 CtxEngineID, CtxName, SecData}, 365 {ok, 'version-3', PDU, PduMMS, ACMData}; 366 UnknownEngineID -> 367 ?vtrace("4.2.2.1.2 - UnknownEngineId: ~p", 368 [UnknownEngineID]), 369 %% 4.2.2.1.2 370 NIsReportable = snmp_misc:is_reportable_pdu(Type), 371 Val = inc(snmpUnknownPDUHandlers), 372 ErrorInfo = 373 {#varbind{oid = ?snmpUnknownPDUHandlers, 374 variabletype = 'Counter32', 375 value = Val}, 376 SecName, 377 [{securityLevel, SecLevel}, 378 {contextEngineID, CtxEngineID}, 379 {contextName, CtxName}]}, 380 case generate_v3_report_msg(MsgID, 381 MsgSecModel, 382 Data, 383 ErrorInfo, 384 Log) of 385 {ok, Report} when NIsReportable =:= true -> 386 discard(snmpUnknownPDUHandlers, Report); 387 _ -> 388 discard(snmpUnknownPDUHandlers) 389 end 390 end; 391 _ -> % 7.2.13.a 392 ?vinfo("7.2.13a - invalid securityEngineID: ~p", 393 [SecEngineID]), 394 discard({badSecurityEngineID, SecEngineID}) 395 end; 396 397 _ -> 398 %% 7.2.13 - This would be the requests which we should not 399 %% receive since we are a manager, barring possible 400 %% proxy... 401 discard(Type) 402 end. 403 404 405sec_error(T1, T2) 406 when is_tuple(T1) andalso is_tuple(T2) andalso (size(T1) =:= size(T2)) -> 407 Tags = {sec_engine_id, msg_sec_model, sec_name, sec_level, 408 ctx_engine_id, ctx_name, request_id}, 409 sec_error(size(T1), T1, T2, Tags, []); 410sec_error(T1, T2) -> 411 [{internal_error, T1, T2}]. 412 413sec_error(0, _T1, _T2, _Tags, Acc) -> 414 Acc; 415sec_error(Idx, T1, T2, Tags, Acc) -> 416 case element(Idx, T1) =:= element(Idx, T2) of 417 true -> 418 sec_error(Idx - 1, T1, T2, Tags, Acc); 419 false -> 420 Elem = {element(Idx, Tags), element(Idx, T1), element(Idx, T2)}, 421 sec_error(Idx - 1, T1, T2, Tags, [Elem|Acc]) 422 end. 423 424tot_mms(MgrMMS, AgentMMS) when MgrMMS > AgentMMS -> AgentMMS; 425tot_mms(MgrMMS, _AgentMMS) -> MgrMMS. 426 427get_security_module(?SEC_USM) -> 428 snmpm_usm; 429get_security_module(_) -> 430 inc(snmpUnknownSecurityModels), 431 discard(snmpUnknownSecurityModels). 432 433check_sec_level([MsgFlag]) -> 434 SecLevel = MsgFlag band 3, 435 if 436 SecLevel == 2 -> 437 inc(snmpInvalidMsgs), 438 discard(snmpInvalidMsgs); 439 true -> 440 SecLevel 441 end; 442check_sec_level(_Unknown) -> 443 inc(snmpInvalidMsgs), 444 discard(snmpInvalidMsgs). 445 446is_reportable([MsgFlag]) -> 447 4 == (MsgFlag band 4). 448 449 450check_sec_module_result({ok, X}, _, _, _, _) -> 451 X; 452check_sec_module_result({error, Reason, Info}, _, _, _, _) 453 when is_list(Info) -> 454 %% case 7.2.6 b 455 discard({securityError, Reason, Info}); 456check_sec_module_result({error, Reason, ErrorInfo}, V3Hdr, Data, true, Log) -> 457 %% case 7.2.6 a 458 ?vtrace("security module result:" 459 "~n Reason: ~p" 460 "~n ErrorInfo: ~p", [Reason, ErrorInfo]), 461 #v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr, 462 Pdu = get_scoped_pdu(Data), 463 case generate_v3_report_msg(MsgID, MsgSecModel, Pdu, ErrorInfo, Log) of 464 {ok, Report} -> 465 discard({securityError, Reason}, Report); 466 {discarded, _SomeOtherReason} -> 467 discard({securityError, Reason}) 468 end; 469check_sec_module_result({error, Reason, _ErrorInfo}, _, _, _, _) -> 470 ?vtrace("security module result:" 471 "~n Reason: ~p" 472 "~n _ErrorInfo: ~p", [Reason, _ErrorInfo]), 473 discard({securityError, Reason}); 474check_sec_module_result(Res, _, _, _, _) -> 475 ?vtrace("security module result:" 476 "~n Res: ~p", [Res]), 477 discard({securityError, Res}). 478 479get_scoped_pdu(D) when is_list(D) -> 480 (catch snmp_pdus:dec_scoped_pdu(D)); 481get_scoped_pdu(D) -> 482 D. 483 484 485%%----------------------------------------------------------------- 486%% Generate a message 487%%----------------------------------------------------------------- 488generate_msg('version-3', NoteStore, Pdu, 489 {SecModel, SecName, SecLevel, CtxEngineID, CtxName, 490 TargetName}, Log) -> 491 generate_v3_msg(NoteStore, Pdu, 492 SecModel, SecName, SecLevel, CtxEngineID, CtxName, 493 TargetName, Log); 494generate_msg(Vsn, _NoteStore, Pdu, {Comm, _SecModel}, Log) -> 495 generate_v1_v2c_msg(Vsn, Pdu, Comm, Log). 496 497 498generate_v3_msg(NoteStore, Pdu, 499 SecModel, SecName, SecLevel, CtxEngineID, CtxName, 500 TargetName, Log) -> 501 %% rfc2272: 7.1.6 502 ?vdebug("generate_v3_msg -> 7.1.6", []), 503 ScopedPDU = #scopedPdu{contextEngineID = CtxEngineID, 504 contextName = CtxName, 505 data = Pdu}, 506 case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of 507 {'EXIT', Reason} -> 508 user_err("failed encoding scoped pdu " 509 "~n pdu: ~w" 510 "~n contextName: ~w" 511 "~n reason: ~w", [Pdu, CtxName, Reason]), 512 {discarded, Reason}; 513 ScopedPDUBytes -> 514 {ok, generate_v3_msg(NoteStore, Pdu, ScopedPDUBytes, 515 SecModel, SecName, SecLevel, 516 CtxEngineID, CtxName, TargetName, Log)} 517 end. 518 519generate_v3_msg(NoteStore, 520 #pdu{type = Type} = Pdu, ScopedPduBytes, 521 SecModel, SecName, SecLevel, CtxEngineID, CtxName, 522 TargetName, Log) -> 523 %% 7.1.7 524 ?vdebug("generate_v3_msg -> 7.1.7", []), 525 MsgID = next_msg_id(), 526 MsgFlags = snmp_misc:mk_msg_flags(Type, SecLevel), 527 V3Hdr = #v3_hdr{msgID = MsgID, 528 msgMaxSize = get_max_message_size(), 529 msgFlags = MsgFlags, 530 msgSecurityModel = SecModel}, 531 Message = #message{version = 'version-3', 532 vsn_hdr = V3Hdr, 533 data = ScopedPduBytes}, 534 SecModule = sec_module(SecModel), 535 536 %% 7.1.9a 537 ?vdebug("generate_v3_msg -> 7.1.9a", []), 538 SecEngineID = sec_engine_id(TargetName), 539 ?vtrace("SecEngineID: ~p", [SecEngineID]), 540 %% 7.1.9b 541 ?vdebug("generate_v3_msg -> 7.1.9b", []), 542 case generate_v3_outgoing_msg(Message, SecModule, SecEngineID, 543 SecName, [], SecLevel) of 544 {ok, Packet} -> 545 %% 7.1.9c 546 %% Store in cache for 150 sec. 547 ?vdebug("generate_v3_msg -> 7.1.9c", []), 548 %% The request id is just in the case when we receive a 549 %% report with incorrect securityModel and/or securityLevel 550 CacheVal = {SecEngineID, SecModel, SecName, SecLevel, 551 CtxEngineID, CtxName, Pdu#pdu.request_id}, 552 snmp_note_store:set_note(NoteStore, 1500, MsgID, CacheVal), 553 Log(Packet), 554 inc_snmp_out(Pdu), 555 ?vdebug("generate_v3_msg -> done", []), 556 Packet; 557 558 Error -> 559 throw(Error) 560 end. 561 562 563sec_module(?SEC_USM) -> 564 snmpm_usm. 565 566%% 9) If the PDU is a GetRequest-PDU, GetNextRequest-PDU, 567%% GetBulkRequest-PDU, SetRequest-PDU, InformRequest-PDU, or or 568%% SNMPv2-Trap-PDU, then 569%% 570%% a) If the PDU is an SNMPv2-Trap-PDU, then securityEngineID is set 571%% to the value of this entity's snmpEngineID. 572%% 573%% Otherwise, the snmpEngineID of the target entity is determined, 574%% in an implementation-dependent manner, possibly using 575%% transportDomain and transportAddress. The value of 576%% securityEngineID is set to the value of the target entity's 577%% snmpEngineID. 578%% 579%% As we never send traps, the SecEngineID is allways the 580%% snmpEngineID of the target entity! 581sec_engine_id(TargetName) -> 582 case get_agent_engine_id(TargetName) of 583 {ok, EngineId} -> 584 EngineId; 585 _ -> 586 config_err("Can't find engineID for " 587 "snmpTargetAddrName ~p", [TargetName]), 588 %% this will trigger error in secmodule 589 "" 590 end. 591 592 593%% BMK BMK BMK 594%% This one looks very similar to lik generate_v1_v2c_response_msg! 595%% Common/shared? Should there be differences? 596%% 597generate_v1_v2c_msg(Vsn, Pdu, Community, Log) -> 598 ?vdebug("generate_v1_v2c_msg -> encode pdu", []), 599 case (catch snmp_pdus:enc_pdu(Pdu)) of 600 {'EXIT', Reason} -> 601 user_err("failed encoding pdu: " 602 "(pdu: ~w, community: ~w): ~n~w", 603 [Pdu, Community, Reason]), 604 {discarded, Reason}; 605 PduBytes -> 606 MMS = get_max_message_size(), 607 Message = #message{version = Vsn, 608 vsn_hdr = Community, 609 data = PduBytes}, 610 case generate_v1_v2c_outgoing_msg(Message) of 611 {error, Reason} -> 612 user_err("failed encoding message " 613 "(pdu: ~w, community: ~w): ~n~w", 614 [Pdu, Community, Reason]), 615 {discarded, Reason}; 616 {ok, Packet} when size(Packet) =< MMS -> 617 Log(Packet), 618 inc_snmp_out(Pdu), 619 {ok, Packet}; 620 {ok, Packet} -> 621 ?vlog("packet max size exceeded: " 622 "~n MMS: ~p" 623 "~n Len: ~p", 624 [MMS, size(Packet)]), 625 {discarded, tooBig} 626 end 627 end. 628 629 630 631%% ----------------------------------------------------------------------- 632 633generate_response_msg('version-3', Pdu, 634 {MsgID, SecModel, SecName, SecLevel, 635 CtxEngineID, CtxName, SecData}, Log) -> 636 generate_v3_response_msg(Pdu, MsgID, SecModel, SecName, SecLevel, 637 CtxEngineID, CtxName, SecData, Log); 638generate_response_msg(Vsn, Pdu, {Comm, _SecModel}, Log) -> 639 generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log); 640generate_response_msg(Vsn, Pdu, {Comm, _SecModel, _TDomain, _TAddress}, Log) -> 641 generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log). 642 643 644generate_v3_response_msg(#pdu{type = Type} = Pdu, MsgID, 645 SecModel, SecName, SecLevel, 646 CtxEngineID, CtxName, SecData, Log) -> 647 %% rfc2272: 7.1 steps 6-8 648 ScopedPdu = #scopedPdu{contextEngineID = CtxEngineID, 649 contextName = CtxName, 650 data = Pdu}, 651 case (catch snmp_pdus:enc_scoped_pdu(ScopedPdu)) of 652 {'EXIT', Reason} -> 653 user_err("failed encoded scoped pdu " 654 "(pdu: ~w, contextName: ~w): ~n~w", 655 [Pdu, CtxName, Reason]), 656 {discarded, Reason}; 657 ScopedPduBytes -> 658 MMS = get_max_message_size(), 659 MsgFlags = snmp_misc:mk_msg_flags(Type, SecLevel), 660 V3Hdr = #v3_hdr{msgID = MsgID, 661 msgMaxSize = MMS, 662 msgFlags = MsgFlags, 663 msgSecurityModel = SecModel}, 664 Message = #message{version = 'version-3', 665 vsn_hdr = V3Hdr, 666 data = ScopedPduBytes}, 667 %% We know that the security model is valid when we 668 %% generate a response. 669 SecModule = sec_module(SecModel), 670 SecEngineID = get_engine_id(), 671 case generate_v3_outgoing_msg(Message, SecModule, SecEngineID, 672 SecName, SecData, SecLevel) of 673 %% Check the packet size. Send the msg even 674 %% if it's larger than the agent can handle - 675 %% it will be dropped. Just check against the 676 %% internal size. 677 {ok, Packet} when size(Packet) =< MMS -> 678 if 679 SecLevel == 3 -> 680 %% encrypted - log decrypted pdu 681 Log({V3Hdr, ScopedPduBytes}); 682 true -> 683 %% otherwise log the entire msg 684 Log(Packet) 685 end, 686 inc_snmp_out(Pdu), 687 {ok, Packet}; 688 689 {ok, _Packet} when Pdu#pdu.error_status =:= tooBig -> 690 ?vlog("packet max size exceeded (tooBog): " 691 "~n MMS: ~p", [MMS]), 692 inc(snmpSilentDrops), 693 {discarded, tooBig}; 694 {ok, _Packet} -> 695 ?vlog("packet max size exceeded: " 696 "~n MMS: ~p", [MMS]), 697 TooBigPdu = Pdu#pdu{error_status = tooBig, 698 error_index = 0, 699 varbinds = []}, 700 generate_v3_response_msg(TooBigPdu, MsgID, 701 SecModel, SecName, SecLevel, 702 CtxEngineID, 703 CtxName, 704 SecData, Log); 705 Error -> 706 Error 707 end 708 end. 709 710 711generate_v3_outgoing_msg(Message, 712 SecModule, SecEngineID, SecName, SecData, SecLevel) -> 713 case (catch SecModule:generate_outgoing_msg(Message, 714 SecEngineID, 715 SecName, SecData, 716 SecLevel)) of 717 {'EXIT', Reason} -> 718 config_err("~p (message: ~p)", [Reason, Message]), 719 {discarded, Reason}; 720 {error, Reason} -> 721 config_err("~p (message: ~p)", [Reason, Message]), 722 {discarded, Reason}; 723 Bin when is_binary(Bin) -> 724 {ok, Bin}; 725 OutMsg when is_list(OutMsg) -> 726 case (catch list_to_binary(OutMsg)) of 727 Bin when is_binary(Bin) -> 728 {ok, Bin}; 729 {'EXIT', Reason} -> 730 {error, Reason} 731 end 732 end. 733 734 735generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log) -> 736 case (catch snmp_pdus:enc_pdu(Pdu)) of 737 {'EXIT', Reason} -> 738 user_err("failed encoding pdu: " 739 "(pdu: ~w, community: ~w): ~n~w", 740 [Pdu, Comm, Reason]), 741 {discarded, Reason}; 742 PduBytes -> 743 MMS = get_max_message_size(), 744 Message = #message{version = Vsn, 745 vsn_hdr = Comm, 746 data = PduBytes}, 747 case generate_v1_v2c_outgoing_msg(Message) of 748 {error, Reason} -> 749 user_err("failed encoding message only " 750 "(pdu: ~w, community: ~w): ~n~w", 751 [Pdu, Comm, Reason]), 752 {discarded, Reason}; 753 754 {ok, Packet} when size(Packet) =< MMS -> 755 Log(Packet), 756 inc_snmp_out(Pdu), 757 {ok, Packet}; 758 759 {ok, Packet} -> %% Too big 760 too_big(Vsn, Pdu, Comm, MMS, size(Packet), Log) 761 end 762 end. 763 764 765too_big('version-1' = Vsn, #pdu{type = 'get-response'} = Pdu, 766 Comm, _MMS, _Len, Log) -> 767 %% In v1, the varbinds should be identical to the incoming 768 %% request. It isn't identical now! Make acceptable (?) 769 %% approximation. 770 V = set_vb_null(Pdu#pdu.varbinds), 771 TooBigPdu = Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = V}, 772 too_big(Vsn, TooBigPdu, Comm, Log); 773too_big('version-2' = Vsn, #pdu{type = 'get-response'} = Pdu, 774 Comm, _MMS, _Len, Log) -> 775 %% In v2, varbinds should be empty (reasonable!) 776 TooBigPdu = Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = []}, 777 too_big(Vsn, TooBigPdu, Comm, Log); 778too_big(_Vsn, Pdu, _Comm, _Log, MMS, Len) -> 779 user_err("encoded pdu, ~p bytes, exceeded " 780 "max message size of ~p bytes. Pdu: ~n~w", 781 [Len, MMS, Pdu]), 782 {discarded, tooBig}. 783 784 785too_big(Vsn, Pdu, Comm, Log) -> 786 case (catch snmp_pdus:enc_pdu(Pdu)) of 787 {'EXIT', Reason} -> 788 user_err("failed encoding pdu " 789 "(pdu: ~w, community: ~w): ~n~w", 790 [Pdu, Comm, Reason]), 791 {discarded, Reason}; 792 PduBytes -> 793 Message = #message{version = Vsn, 794 vsn_hdr = Comm, 795 data = PduBytes}, 796 case generate_v1_v2c_outgoing_msg(Message) of 797 {error, Reason} -> 798 user_err("failed encoding message only" 799 "(pdu: ~w, community: ~w): ~n~w", 800 [Pdu, Comm, Reason]), 801 {discarded, Reason}; 802 {ok, Bin} -> 803 Log(Bin), 804 inc_snmp_out(Pdu), 805 {ok, Bin} 806 end 807 end. 808 809set_vb_null(Vbs) -> 810 [Vb#varbind{variabletype = 'NULL', value = 'NULL'} || Vb <- Vbs]. 811 812 813generate_v1_v2c_outgoing_msg(Message) -> 814 ?vdebug("generate_v1_v2c_outgoing_msg -> encode message", []), 815 case (catch snmp_pdus:enc_message_only(Message)) of 816 {'EXIT', Reason} -> 817 {error, Reason}; 818 Bin when is_binary(Bin) -> 819 {ok, Bin}; 820 Packet when is_list(Packet) -> 821 case (catch list_to_binary(Packet)) of 822 Bin when is_binary(Bin) -> 823 {ok, Bin}; 824 {'EXIT', Reason} -> 825 {error, Reason} 826 end 827 end. 828 829 830 831generate_v3_report_msg(MsgID, SecModel, ScopedPdu, ErrInfo, Log) 832 when is_record(ScopedPdu, scopedPdu) -> 833 ReqID = (ScopedPdu#scopedPdu.data)#pdu.request_id, 834 generate_v3_report_msg2(MsgID, ReqID, SecModel, ErrInfo, Log); 835generate_v3_report_msg(MsgID, SecModel, _, ErrInfo, Log) -> 836 %% RFC2572, 7.1.3.c.4 837 generate_v3_report_msg2(MsgID, 0, SecModel, ErrInfo, Log). 838 839 840generate_v3_report_msg2(MsgID, ReqID, SecModel, ErrInfo, Log) -> 841 {Varbind, SecName, Opts} = ErrInfo, 842 Pdu = #pdu{type = report, 843 request_id = ReqID, 844 error_status = noError, 845 error_index = 0, 846 varbinds = [Varbind]}, 847 SecLevel = snmp_misc:get_option(securityLevel, Opts, 0), 848 CtxEngineID = snmp_misc:get_option(contextEngineID, Opts, get_engine_id()), 849 CtxName = snmp_misc:get_option(contextName, Opts, ""), 850 SecData = snmp_misc:get_option(sec_data, Opts, []), 851 generate_v3_response_msg(Pdu, 852 MsgID, SecModel, SecName, SecLevel, 853 CtxEngineID, CtxName, SecData, Log). 854 855 856%%----------------------------------------------------------------- 857 858%% Get "our" (manager) MMS 859get_max_message_size() -> 860 case snmpm_config:get_engine_max_message_size() of 861 {ok, MMS} -> 862 MMS; 863 E -> 864 user_err("failed retreiving engine max message size: ~w", [E]), 865 484 866 end. 867 868%% The the MMS of the agent 869get_agent_max_message_size(Domain, Addr) -> 870 case snmpm_config:get_agent_engine_max_message_size(Domain, Addr) of 871 {ok, MMS} -> 872 MMS; 873 _Error -> 874 TAddr = fun(TN) -> 875 case snmpm_config:agent_info(TN, taddress) of 876 {ok, TA} -> 877 TA; 878 {error, _} -> 879 undefined 880 end 881 end, 882 KnownAgents = 883 [{TargetName, TAddr(TargetName)} || 884 TargetName <- snmpm_config:which_agents()], 885 ?vlog("[agent engine max msg size lookup] unknown agent: ~s" 886 "~n Known Agents: ~p", 887 [snmp_conf:mk_addr_string({Domain, Addr}), KnownAgents]), 888 get_max_message_size() 889 end. 890%% get_agent_max_message_size(Addr, Port) -> 891%% case snmpm_config:get_agent_engine_max_message_size(Addr, Port) of 892%% {ok, MMS} -> 893%% MMS; 894%% _Error -> 895%% ?vlog("unknown agent: ~w:~w", [Addr, Port]), 896%% get_max_message_size() 897%% end. 898 899%% Get "our" (manager) engine id 900get_engine_id() -> 901 case snmpm_config:get_engine_id() of 902 {ok, Id} -> 903 Id; 904 _Error -> 905 "" 906 end. 907 908%% The engine id of the agent 909get_agent_engine_id(Name) -> 910 snmpm_config:get_agent_engine_id(Name). 911 912is_known_engine_id(EngineID, {Addr, Port}) -> 913 snmpm_config:is_known_engine_id(EngineID, Addr, Port). 914 915 916%%----------------------------------------------------------------- 917%% Sequence number (msg-id & req-id) functions 918%%----------------------------------------------------------------- 919next_msg_id() -> 920 next_id(msg_id). 921 922next_req_id() -> 923 next_id(req_id). 924 925next_id(Id) -> 926 snmpm_config:incr_counter(Id, 1). 927 928 929%%----------------------------------------------------------------- 930%% Version(s) functions 931%%----------------------------------------------------------------- 932init_versions([], S) -> 933 S; 934init_versions([v1|Vsns], S) -> 935 init_versions(Vsns, S#state{v1 = true}); 936init_versions([v2|Vsns], S) -> 937 init_versions(Vsns, S#state{v2c = true}); 938init_versions([v3|Vsns], S) -> 939 init_versions(Vsns, S#state{v3 = true}). 940 941init_usm(true) -> 942 snmpm_usm:init(); 943init_usm(_) -> 944 ok. 945 946 947%%----------------------------------------------------------------- 948%% Counter functions 949%%----------------------------------------------------------------- 950init_counters() -> 951 F = fun(Counter) -> maybe_create_counter(Counter) end, 952 lists:map(F, counters()). 953 954reset_counters() -> 955 F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end, 956 lists:map(F, counters()). 957 958reset_usm(true) -> 959 snmpm_usm:reset(); 960reset_usm(_) -> 961 ok. 962 963maybe_create_counter(Counter) -> 964 snmpm_config:maybe_cre_stats_counter(Counter, 0). 965 966counters() -> 967 [snmpInPkts, 968 snmpOutPkts, 969 snmpInBadVersions, 970 snmpInBadCommunityNames, 971 snmpInBadCommunityUses, 972 snmpInASNParseErrs, 973 snmpInTooBigs, 974 snmpInNoSuchNames, 975 snmpInBadValues, 976 snmpInReadOnlys, 977 snmpInGenErrs, 978 snmpInTotalReqVars, 979 snmpInTotalSetVars, 980 snmpInGetRequests, 981 snmpInGetNexts, 982 snmpInSetRequests, 983 snmpInGetResponses, 984 snmpInTraps, 985 snmpOutTooBigs, 986 snmpOutNoSuchNames, 987 snmpOutBadValues, 988 snmpOutGenErrs, 989 snmpOutGetRequests, 990 snmpOutGetNexts, 991 snmpOutSetRequests, 992 snmpOutGetResponses, 993 snmpOutTraps, 994 snmpSilentDrops, 995 snmpProxyDrops, 996 %% From SNMP-MPD-MIB 997 snmpUnknownSecurityModels, 998 snmpInvalidMsgs, 999 snmpUnknownPDUHandlers 1000 ]. 1001 1002 1003%%----------------------------------------------------------------- 1004%% inc(VariableName) increments the variable (Counter) in 1005%% the local mib. (e.g. snmpInPkts) 1006%%----------------------------------------------------------------- 1007inc(Name) -> inc(Name, 1). 1008inc(Name, N) -> snmpm_config:incr_stats_counter(Name, N). 1009 1010inc_snmp_in(#pdu{type = Type}) -> 1011 inc_in_type(Type); 1012inc_snmp_in(TrapPdu) when is_record(TrapPdu, trappdu) -> 1013 inc(snmpInPkts), 1014 inc(snmpInTraps). 1015 1016inc_snmp_out(#pdu{type = Type, 1017 error_status = ErrorStatus}) -> 1018 inc(snmpOutPkts), 1019 inc_out_err(ErrorStatus), 1020 inc_out_type(Type). 1021 1022inc_out_type('get-request') -> inc(snmpOutGetRequests); 1023inc_out_type('get-next-request') -> inc(snmpOutGetNexts); 1024inc_out_type('set-request') -> inc(snmpOutSetRequests); 1025inc_out_type(_) -> ok. 1026 1027inc_out_err(genErr) -> inc(snmpOutGenErrs); 1028inc_out_err(tooBig) -> inc(snmpOutTooBigs); 1029inc_out_err(noSuchName) -> inc(snmpOutNoSuchNames); 1030inc_out_err(badValue) -> inc(snmpOutBadValues); 1031inc_out_err(_) -> ok. 1032 1033inc_in_type('get-response') -> inc(snmpInGetResponses); 1034inc_in_type(_) -> ok. 1035 1036 1037%%----------------------------------------------------------------- 1038 1039discard(Reason) -> 1040 throw({discarded, Reason}). 1041 1042discard(Reason, Report) -> 1043 throw({discarded, Reason, Report}). 1044 1045user_err(F, A) -> 1046 error_msg("USER ERROR: " ++ F ++ "~n", A). 1047 1048config_err(F, A) -> 1049 error_msg("CONFIG ERROR: " ++ F ++ "~n", A). 1050 1051error_msg(F, A) -> 1052 ?snmpm_error("MPD: " ++ F, A). 1053