1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2020. 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-module(snmpa_mpd). 21 22-export([init/1, reset/0, inc/1, counters/0, 23 discarded_pdu/1, 24 process_packet/5, process_packet/6, process_packet/7, 25 generate_response_msg/5, generate_response_msg/6, 26 generate_msg/5, generate_msg/6, 27 generate_discovery_msg/4, 28 process_taddrs/1, 29 generate_req_id/0]). 30 31-define(SNMP_USE_V3, true). 32-include("snmp_types.hrl"). 33-include("SNMP-MPD-MIB.hrl"). 34-include("SNMPv2-TM.hrl"). 35-include("SNMP-FRAMEWORK-MIB.hrl"). 36-include("TRANSPORT-ADDRESS-MIB.hrl"). 37 38-define(VMODULE,"MPD"). 39-include("snmp_verbosity.hrl"). 40-include("snmpa_internal.hrl"). 41 42-define(empty_msg_size, 24). 43 44-record(state, {v1 = false, v2c = false, v3 = false}). 45-record(note, {sec_engine_id, 46 sec_model, 47 sec_name, 48 sec_level, 49 ctx_engine_id, 50 ctx_name, 51 disco = false, 52 req_id}). 53 54 55%%%----------------------------------------------------------------- 56%%% This module implemets the Message Processing and Dispatch part of 57%%% the multi-lingual SNMP agent. 58%%% 59%%% The MPD is responsible for: 60%%% *) call the security module (auth/priv). 61%%% *) decoding the message into a PDU. 62%%% *) decide a suitable Access Control Model, and provide it with 63%%% the data it needs. 64%%% *) maintaining SNMP counters. 65%%% 66%%% In order to take care of the different versions of counters, it 67%%% implements and maintains the union of all SNMP counters (i.e. from 68%%% rfc1213 and from rfc1907). It is up to the administrator of the 69%%% agent to load the correct MIB. Note that this module implements 70%%% the counters only, it does not provide instrumentation functions 71%%% for the counters. 72%%% 73%%% With the terms defined in rfc2271, this module implememts part 74%%% of the Dispatcher and the Message Processing functionality. 75%%%----------------------------------------------------------------- 76init(Vsns) -> 77 ?vlog("init -> entry with" 78 "~n Vsns: ~p", [Vsns]), 79 ?SNMP_RAND_SEED(), 80 ets:insert(snmp_agent_table, {msg_id, rand:uniform(2147483647)}), 81 ets:insert(snmp_agent_table, {req_id, rand:uniform(2147483647)}), 82 init_counters(), 83 init_versions(Vsns, #state{}). 84 85 86reset() -> 87 reset_counters(), 88 ok. 89 90 91%%----------------------------------------------------------------- 92%% Purpose: We must calculate the length of a 93%% message with an empty Pdu, and zero-length community 94%% string. This length is used to calculate the max 95%% pdu size allowed for each request. This size is 96%% dependent on two dynamic fields, the community string 97%% and the pdu (varbinds actually). It is calculated 98%% as EmptySize + length(CommunityString) + 4. 99%% We assume that the length of the CommunityString is 100%% less than 128 (thus requiring just one octet for the 101%% length field (the same as the zero-length community 102%% string)). 4 comes from the fact that the maximum pdu 103%% size needs 31 bits which needs 5 * 7 bits to be 104%% expressed. One 7bit octet is already present in the 105%% empty msg, leaving 4 more 7bit octets. 106%% Actually, this function is not used, we use a constant instead. 107%%----------------------------------------------------------------- 108%% Ret: 24 109%empty_msg() -> 110% M = #message{version = 'version-1', community = "", data = 111% #pdu{type = 'get-response', request_id = 1, 112% error_status = noError, error_index = 0, varbinds = []}}, 113% length(snmp_pdus:enc_message(M)) + 4. 114 115%%----------------------------------------------------------------- 116%% Func: process_packet(Packet, Domain, Address, State, Log) -> 117%% {ok, SnmpVsn, Pdu, PduMS, ACMData} | {discarded, Reason} 118%% Types: Packet = binary() 119%% Domain = snmpUDPDomain | transportDomain() 120%% Address = {Ip, Udp} (*but* depends on Domain) 121%% State = #state 122%% Purpose: This is the main Message Dispatching function. (see 123%% section 4.2.1 in rfc2272) 124%%----------------------------------------------------------------- 125process_packet(Packet, From, State, NoteStore, Log) -> 126 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 127 process_packet(Packet, From, LocalEngineID, State, NoteStore, Log). 128 129process_packet( 130 Packet, Domain, Address, LocalEngineID, State, NoteStore, Log) -> 131 From = {Domain, Address}, 132 process_packet(Packet, From, LocalEngineID, State, NoteStore, Log). 133 134process_packet(Packet, Domain, Address, State, NoteStore, Log) 135 when is_atom(Domain) -> 136 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 137 From = {Domain, Address}, 138 process_packet(Packet, From, LocalEngineID, State, NoteStore, Log); 139process_packet(Packet, From, LocalEngineID, State, NoteStore, Log) -> 140 inc(snmpInPkts), 141 case catch snmp_pdus:dec_message_only(binary_to_list(Packet)) of 142 143 #message{version = 'version-1', vsn_hdr = Community, data = Data} 144 when State#state.v1 =:= true -> 145 ?vlog("v1, community: ~s", [Community]), 146 HS = ?empty_msg_size + length(Community), 147 v1_v2c_proc( 148 'version-1', NoteStore, Community, From, 149 LocalEngineID, Data, HS, Log, Packet); 150 151 #message{version = 'version-2', vsn_hdr = Community, data = Data} 152 when State#state.v2c =:= true -> 153 ?vlog("v2c, community: ~s", [Community]), 154 HS = ?empty_msg_size + length(Community), 155 v1_v2c_proc( 156 'version-2', NoteStore, Community, From, 157 LocalEngineID, Data, HS, Log, Packet); 158 159 #message{version = 'version-3', vsn_hdr = V3Hdr, data = Data} 160 when State#state.v3 =:= true -> 161 ?vlog("v3, msgID: ~p, msgFlags: ~p, msgSecModel: ~p", 162 [V3Hdr#v3_hdr.msgID, 163 V3Hdr#v3_hdr.msgFlags, 164 V3Hdr#v3_hdr.msgSecurityModel]), 165 v3_proc( 166 NoteStore, Packet, From, 167 LocalEngineID, V3Hdr, Data, Log); 168 169 #message{version = MsgVersion} -> 170 ?vlog("Invalid Version: " 171 "~n Message Version: ~p" 172 "~nwhen" 173 "~n Versions:" 174 "~n v1: ~w" 175 "~n v2c: ~w" 176 "~n v3: ~w", 177 [MsgVersion, 178 State#state.v1, State#state.v2c, State#state.v3]), 179 inc(snmpInBadVersions), 180 {discarded, snmpInBadVersions}; 181 182 {'EXIT', {bad_version, Vsn}} -> 183 ?vtrace("exit: bad version: ~p",[Vsn]), 184 inc(snmpInBadVersions), 185 {discarded, snmpInBadVersions}; 186 187 {'EXIT', Reason} -> 188 ?vtrace("exit: ~p", [Reason]), 189 inc(snmpInASNParseErrs), 190 {discarded, Reason}; 191 192 UnknownMessage -> 193 ?vdebug("Unknown message: " 194 "~n ~p" 195 "~nwhen" 196 "~n State: " 197 "~n ~p", [UnknownMessage, State]), 198 inc(snmpInBadVersions), 199 {discarded, snmpInBadVersions} 200 end. 201 202discarded_pdu(false) -> ok; 203discarded_pdu(Variable) -> inc(Variable). 204 205 206%%----------------------------------------------------------------- 207%% Handles a Community based message (v1 or v2c). 208%%----------------------------------------------------------------- 209v1_v2c_proc( 210 Vsn, NoteStore, Community, From, 211 LocalEngineID, Data, HS, Log, Packet) -> 212 try 213 case From of 214 {D, A} when is_atom(D) -> 215 {snmp_conf:mk_tdomain(D), 216 snmp_conf:mk_taddress(D, A)}; 217 {_, P} = A when is_integer(P) -> 218 {snmp_conf:mk_tdomain(), 219 snmp_conf:mk_taddress(A)} 220 end 221 of 222 {TDomain, TAddress} -> 223 v1_v2c_proc_dec( 224 Vsn, NoteStore, Community, TDomain, TAddress, 225 LocalEngineID, Data, HS, Log, Packet) 226 catch 227 _ -> 228 {discarded, {badarg, From}} 229 end. 230 231 232v1_v2c_proc_dec( 233 Vsn, NoteStore, Community, TDomain, TAddress, 234 LocalEngineID, Data, HS, Log, Packet) -> 235 AgentMS = get_engine_max_message_size(LocalEngineID), 236 MgrMS = snmp_community_mib:get_target_addr_ext_mms(TDomain, TAddress), 237 PduMS = case MgrMS of 238 {ok, MMS} when MMS < AgentMS -> MMS - HS; 239 _ -> AgentMS - HS 240 end, 241 case (catch snmp_pdus:dec_pdu(Data)) of 242 Pdu when is_record(Pdu, pdu) -> 243 Log(Pdu#pdu.type, Packet), 244 inc_snmp_in_vars(Pdu), 245 #pdu{request_id = ReqId} = Pdu, 246 247 %% <TDomain> 248 %% We have added TDomain, what are the consequences? 249 ACMData = 250 {community, sec_model(Vsn), Community, TDomain, TAddress}, 251 OkRes = {ok, Vsn, Pdu, PduMS, ACMData}, 252 %% </TDomain> 253 254 %% Make sure that we don't process duplicate SET request 255 %% twice. We don't know what could happen in that case. 256 %% The mgr does, so he has to generate a new SET request. 257 ?vdebug("PDU type: ~p", [Pdu#pdu.type]), 258 case Pdu#pdu.type of 259 'set-request' -> 260 %% Check if this message has already been processed 261 Key = {agent, {TDomain, TAddress}, ReqId}, 262 case snmp_note_store:get_note(NoteStore, Key) of 263 undefined -> 264 %% Set the processed note _after_ pdu processing. 265 %% This makes duplicated requests be ignored even 266 %% if pdu processing took long time. 267 snmp_note_store:set_note(NoteStore, 268 100, Key, true), 269 %% Uses ACMData that snmpa_acm knows of. 270 OkRes; 271 true -> 272 {discarded, duplicate_pdu} 273 end; 274 _ -> 275 OkRes 276 end; 277 {'EXIT', Reason} -> 278 ?vtrace("PDU decode exit: ~p",[Reason]), 279 inc(snmpInASNParseErrs), 280 {discarded, Reason}; 281 _TrapPdu -> 282 {discarded, trap_pdu} 283 end. 284 285sec_model('version-1') -> ?SEC_V1; 286sec_model('version-2') -> ?SEC_V2C. 287 288 289%%----------------------------------------------------------------- 290%% Handles a SNMPv3 Message, following the procedures in rfc2272, 291%% section 4.2 and 7.2 292%%----------------------------------------------------------------- 293v3_proc(NoteStore, Packet, _From, LocalEngineID, V3Hdr, Data, Log) -> 294 case (catch v3_proc(NoteStore, Packet, LocalEngineID, V3Hdr, Data, Log)) of 295 {'EXIT', Reason} -> 296 exit(Reason); 297 Result -> 298 Result 299 end. 300 301v3_proc(NoteStore, Packet, LocalEngineID, V3Hdr, Data, Log) -> 302 ?vtrace("v3_proc -> entry with" 303 "~n LocalEngineID: ~p", 304 [LocalEngineID]), 305 %% 7.2.3 306 #v3_hdr{msgID = MsgID, 307 msgMaxSize = MMS, 308 msgFlags = MsgFlags, 309 msgSecurityModel = MsgSecurityModel, 310 msgSecurityParameters = SecParams, 311 hdr_size = HdrSize} = V3Hdr, 312 ?vdebug("v3_proc -> version 3 message header [7.2.3]:" 313 "~n msgID = ~p" 314 "~n msgMaxSize = ~p" 315 "~n msgFlags = ~p" 316 "~n msgSecurityModel = ~p" 317 "~n msgSecurityParameters = ~w", 318 [MsgID, MMS, MsgFlags, MsgSecurityModel, SecParams]), 319 %% 7.2.4 320 SecModule = get_security_module(MsgSecurityModel), 321 %% 7.2.5 322 SecLevel = check_sec_level(MsgFlags), 323 IsReportable = snmp_misc:is_reportable(MsgFlags), 324 %% 7.2.6 325 ?vtrace("v3_proc -> [7.2.4-7.2.6]" 326 "~n SecModule = ~p" 327 "~n SecLevel = ~p" 328 "~n IsReportable = ~p", 329 [SecModule, SecLevel, IsReportable]), 330 SecRes = (catch SecModule:process_incoming_msg(Packet, Data, 331 SecParams, SecLevel, 332 LocalEngineID)), 333 ?vtrace("v3_proc -> message processing result: " 334 "~n SecRes: ~p", [SecRes]), 335 {SecEngineID, SecName, ScopedPDUBytes, SecData, DiscoOrPlain} = 336 check_sec_module_result(SecRes, V3Hdr, Data, 337 LocalEngineID, IsReportable, Log), 338 ?vtrace("v3_proc -> " 339 "~n DiscoOrPlain: ~w" 340 "~n SecEngineID: ~w" 341 "~n SecName: ~p", [DiscoOrPlain, SecEngineID, SecName]), 342 %% 7.2.7 343 #scopedPdu{contextEngineID = ContextEngineID, 344 contextName = ContextName, 345 data = PDU} = 346 case (catch snmp_pdus:dec_scoped_pdu(ScopedPDUBytes)) of 347 ScopedPDU when is_record(ScopedPDU, scopedPdu) -> 348 ?vtrace("v3_proc -> message processing result: " 349 "~n ScopedPDU: ~p", [ScopedPDU]), 350 ScopedPDU; 351 {'EXIT', Reason} -> 352 inc(snmpInASNParseErrs), 353 throw({discarded, Reason}) 354 end, 355 %% We'll have to take care of the unlikely case that we receive an 356 %% v1 trappdu in a v3 message explicitly... 357 if 358 is_record(PDU, trappdu) -> 359 inc(snmpUnknownPDUHandlers), 360 throw({discarded, received_v1_trap}); 361 true -> 362 ok 363 end, 364 ?vlog("7.2.7 result: " 365 "~n contextEngineID: ~w" 366 "~n ContextName: \"~s\"", [ContextEngineID, ContextName]), 367 if 368 SecLevel =:= ?'SnmpSecurityLevel_authPriv' -> 369 %% encrypted message - log decrypted pdu 370 Log(PDU#pdu.type, {V3Hdr, ScopedPDUBytes}); 371 true -> % otherwise, log binary 372 Log(PDU#pdu.type, Packet) 373 end, 374 %% Make sure a get_bulk doesn't get too big. 375 AgentMS = get_engine_max_message_size(LocalEngineID), 376 %% PduMMS is supposed to be the maximum total length of the response 377 %% PDU we can send. From the MMS, we need to subtract everything before 378 %% the PDU, i.e. Message and ScopedPDU. 379 %% Message: [48, TotalLen, Vsn, [Tag, LH, Hdr], [Tag, LM, MsgSec], Data] 380 %% 1 3 <----------- HdrSize -----------> 381 %% HdrSize = everything up to and including msgSecurityParameters. 382 %% ScopedPduData follows. This is 383 %% [Tag, Len, [Tag, L1, CtxName], [Tag, L2, CtxEID]] 384 %% i.e. 6 + length(CtxName) + length(CtxEID) 385 %% 386 %% Total: 1 + TotalLenOctets + 3 + ScopedPduDataLen 387 TotMMS = if AgentMS > MMS -> MMS; 388 true -> AgentMS 389 end, 390 TotalLenOctets = snmp_pdus:get_encoded_length(TotMMS - 1), 391 PduMMS = TotMMS - TotalLenOctets - 10 - HdrSize - 392 length(ContextName) - length(ContextEngineID), 393 ?vdebug("v3_proc -> PDU type: ~p", [PDU#pdu.type]), 394 case PDU#pdu.type of 395 report when DiscoOrPlain =:= discovery -> 396 %% Discovery stage 1 response 397 Key = {agent, MsgID}, 398 Note = snmp_note_store:get_note(NoteStore, Key), 399 case Note of 400 #note{sec_engine_id = "", 401 sec_model = _MsgSecModel, 402 sec_name = "", 403 sec_level = _SecLevel, 404 ctx_engine_id = _CtxEngineID, 405 ctx_name = _CtxName, 406 disco = true, 407 req_id = _ReqId} -> 408 %% This is part of the discovery process initiated by us. 409 %% Response to the discovery stage 1 request 410 ?vdebug("v3_proc -> discovery stage 1 response", []), 411 {ok, 'version-3', PDU, PduMMS, {discovery, SecEngineID}}; 412 #note{sec_engine_id = SecEngineID, 413 sec_model = _MsgSecModel, 414 sec_name = SecName, 415 sec_level = _SecLevel, % OTP-16207 416 ctx_engine_id = _CtxEngineID, 417 ctx_name = _CtxName, 418 disco = true, 419 req_id = _ReqId} -> 420 %% This is part of the discovery process initiated by us. 421 %% Response to the discovery stage 2 request 422 ?vdebug("v3_proc -> discovery stage 2 response", []), 423 {ok, 'version-3', PDU, PduMMS, discovery}; 424 _ -> 425 %% 7.2.11 426 DiscardReason = {bad_disco_note, Key, Note}, 427 throw({discarded, DiscardReason}) 428 end; 429 report -> 430 %% 7.2.11 431 throw({discarded, report}); 432 'get-response' -> %% As a result of a sent inform-request? 433 %% 7.2.12 434 Key = {agent, MsgID}, 435 Note = snmp_note_store:get_note(NoteStore, Key), 436 case Note of 437 #note{sec_engine_id = "", 438 sec_model = _MsgSecModel, 439 sec_name = "", 440 sec_level = _SecLevel, 441 ctx_engine_id = _CtxEngineID, 442 ctx_name = _CtxName, 443 disco = true, 444 req_id = _ReqId} -> 445 %% This is part of the discovery process initiated by us. 446 %% Response to the discovery stage 1 request 447 ?vdebug("v3_proc -> discovery stage 1 response", []), 448 {ok, 'version-3', PDU, PduMMS, {discovery, SecEngineID}}; 449 #note{sec_engine_id = SecEngineID, 450 sec_model = _MsgSecModel, 451 sec_name = SecName, 452 sec_level = SecLevel, 453 ctx_engine_id = _CtxEngineID, 454 ctx_name = _CtxName, 455 disco = true, 456 req_id = _ReqId} -> 457 %% This is part of the discovery process initiated by us. 458 %% Response to the discovery stage 2 request 459 ?vdebug("v3_proc -> discovery stage 2 response", []), 460 {ok, 'version-3', PDU, PduMMS, discovery}; 461 #note{sec_engine_id = SecEngineID, 462 sec_model = MsgSecurityModel, 463 sec_name = SecName, 464 sec_level = SecLevel, 465 ctx_engine_id = ContextEngineID, 466 ctx_name = ContextName, 467 disco = false, 468 req_id = _ReqId} -> 469 {ok, 'version-3', PDU, PduMMS, undefined}; 470 _ -> 471 inc(snmpUnknownPDUHandlers), 472 throw({discarded, {no_outstanding_req, MsgID}}) 473 end; 474 'snmpv2-trap' -> 475 inc(snmpUnknownPDUHandlers), 476 throw({discarded, received_v2_trap}); 477 Type -> 478 %% 7.2.13 479 SnmpEngineID = LocalEngineID, 480 ?vtrace("v3_proc -> 7.2.13", []), 481 case SecEngineID of 482 SnmpEngineID when (DiscoOrPlain =:= discovery) -> 483 %% This is a discovery step 2 message! 484 ?vtrace("v3_proc -> discovery stage 2", []), 485 generate_discovery2_report_msg(MsgID, 486 MsgSecurityModel, 487 SecName, 488 SecLevel, 489 ContextEngineID, 490 ContextName, 491 SecData, 492 PDU, 493 LocalEngineID, 494 Log); 495 496 SnmpEngineID when (DiscoOrPlain =:= plain) -> 497 %% 4.2.2.1.1 - we don't handle proxys yet => we only 498 %% handle ContextEngineID to ourselves 499 case ContextEngineID of 500 SnmpEngineID -> 501 %% Uses ACMData that snmpa_acm knows of. 502 {ok, 'version-3', PDU, PduMMS, 503 {v3, MsgID, MsgSecurityModel, SecName, SecLevel, 504 ContextEngineID, ContextName, SecData}}; 505 _ -> 506 %% 4.2.2.1.2 507 NIsReportable = snmp_misc:is_reportable_pdu(Type), 508 ErrorInfo = 509 snmpUnknownPDUHandlers_ei(SecName, SecLevel, 510 ContextEngineID, 511 ContextName), 512 case generate_v3_report_msg(MsgID, 513 MsgSecurityModel, 514 Data, LocalEngineID, 515 ErrorInfo, Log) of 516 {ok, Report} when NIsReportable =:= true -> 517 {discarded, snmpUnknownPDUHandlers, Report}; 518 _ -> 519 {discarded, snmpUnknownPDUHandlers} 520 end 521 end; 522 523 "" -> 524 %% This is a discovery step 1 message!! 525 ?vtrace("v3_proc -> discovery step 1", []), 526 generate_discovery1_report_msg(MsgID, 527 MsgSecurityModel, 528 SecName, 529 SecLevel, 530 ContextEngineID, 531 ContextName, 532 SecData, 533 PDU, 534 LocalEngineID, 535 Log); 536 537 _ -> 538 {discarded, {badSecurityEngineID, SecEngineID}} 539 end 540 end. 541 542make_error_info(Variable, Oid, SecName, Opts) -> 543 Val = inc(Variable), 544 VB = #varbind{oid = Oid, 545 variabletype = 'Counter32', 546 value = Val}, 547 {VB, SecName, Opts}. 548 549snmpUnknownPDUHandlers_ei(SecName, SecLevel, 550 ContextEngineID, ContextName) -> 551 Opts = [{securityLevel, SecLevel}, 552 {contextEngineID, ContextEngineID}, 553 {contextName, ContextName}], 554 make_error_info(snmpUnknownPDUHandlers, 555 ?snmpUnknownPDUHandlers_instance, 556 SecName, Opts). 557 558get_security_module(?SEC_USM) -> 559 snmpa_usm; 560get_security_module(_) -> 561 inc(snmpUnknownSecurityModels), 562 throw({discarded, snmpUnknownSecurityModels}). 563 564check_sec_level([MsgFlag]) -> 565 SecLevel = MsgFlag band 3, 566 if 567 SecLevel == 2 -> 568 inc(snmpInvalidMsgs), 569 throw({discarded, snmpInvalidMsgs}); 570 true -> 571 SecLevel 572 end; 573check_sec_level(Unknown) -> 574 ?vlog("invalid msgFlags: ~p",[Unknown]), 575 inc(snmpInvalidMsgs), 576 throw({discarded, snmpInvalidMsgs}). 577 578check_sec_module_result(Res, V3Hdr, Data, LocalEngineID, IsReportable, Log) -> 579 case Res of 580 {ok, X} -> 581 X; 582 {error, Reason, []} -> % case 7.2.6 b 583 ?vdebug("security module result [7.2.6-b]:" 584 "~n Reason: ~p", [Reason]), 585 throw({discarded, {securityError, Reason}}); 586 {error, Reason, ErrorInfo} when IsReportable =:= true -> % case 7.2.6 a 587 ?vdebug("security module result when reportable [7.2.6-a]:" 588 "~n Reason: ~p" 589 "~n ErrorInfo: ~p", [Reason, ErrorInfo]), 590 #v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr, 591 Pdu = get_scoped_pdu(Data), 592 case generate_v3_report_msg(MsgID, MsgSecModel, Pdu, 593 LocalEngineID, ErrorInfo, Log) of 594 {ok, Report} -> 595 throw({discarded, {securityError, Reason}, Report}); 596 {discarded, _SomeOtherReason} -> 597 throw({discarded, {securityError, Reason}}) 598 end; 599 {error, Reason, ErrorInfo} -> 600 ?vdebug("security module result when not reportable:" 601 "~n Reason: ~p" 602 "~n ErrorInfo: ~p", [Reason, ErrorInfo]), 603 throw({discarded, {securityError, Reason}}); 604 Else -> 605 ?vdebug("security module result:" 606 "~n Else: ~p", [Else]), 607 throw({discarded, {securityError, Else}}) 608 end. 609 610get_scoped_pdu(D) when is_list(D) -> 611 (catch snmp_pdus:dec_scoped_pdu(D)); 612get_scoped_pdu(D) -> 613 D. 614 615 616%%----------------------------------------------------------------- 617%% Executed when a response or report message is generated. 618%%----------------------------------------------------------------- 619generate_response_msg(Vsn, RePdu, Type, ACMData, Log) -> 620 generate_response_msg(Vsn, RePdu, Type, ACMData, Log, 1). 621 622generate_response_msg(Vsn, RePdu, Type, ACMData, Log, N) when is_integer(N) -> 623 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 624 generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log, N); 625generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) -> 626 generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log, 1). 627 628generate_response_msg(Vsn, RePdu, Type, 629 {community, _SecModel, Community, _TDomain, _TAddress}, 630 LocalEngineID, 631 Log, _) -> 632 case catch snmp_pdus:enc_pdu(RePdu) of 633 {'EXIT', Reason} -> 634 user_err("failed encoding pdu: " 635 "(pdu: ~w, community: ~w): ~n~w", 636 [RePdu, Community, Reason]), 637 {discarded, Reason}; 638 PduBytes -> 639 Message = #message{version = Vsn, 640 vsn_hdr = Community, 641 data = PduBytes}, 642 case catch list_to_binary( 643 snmp_pdus:enc_message_only(Message)) of 644 {'EXIT', Reason} -> 645 user_err("failed encoding message only " 646 "(pdu: ~w, community: ~w): ~n~w", 647 [RePdu, Community, Reason]), 648 {discarded, Reason}; 649 Packet -> 650 MMS = get_engine_max_message_size(LocalEngineID), 651 case size(Packet) of 652 Len when Len =< MMS -> 653 Log(Type, Packet), 654 inc_snmp_cnt_vars(Type, RePdu), 655 inc_snmp_out_vars(RePdu), 656 {ok, Packet}; 657 Len -> 658 ?vlog("pdu to big:" 659 "~n Max message size: ~p" 660 "~n Encoded message size: ~p", 661 [MMS,Len]), 662 too_big(Vsn, RePdu, Community, Log, MMS, Len) 663 end 664 end 665 end; 666generate_response_msg(Vsn, RePdu, Type, 667 {v3, MsgID, MsgSecurityModel, SecName, SecLevel, 668 ContextEngineID, ContextName, SecData}, 669 LocalEngineID, 670 Log, N) -> 671 %% rfc2272: 7.1 steps 6-8 672 ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, 673 contextName = ContextName, 674 data = RePdu}, 675 case catch snmp_pdus:enc_scoped_pdu(ScopedPDU) of 676 {'EXIT', Reason} -> 677 user_err("failed encoded scoped pdu " 678 "(pdu: ~w, contextName: ~w): ~n~w", 679 [RePdu, ContextName, Reason]), 680 {discarded, Reason}; 681 ScopedPDUBytes -> 682 AgentMS = get_engine_max_message_size(LocalEngineID), 683 V3Hdr = #v3_hdr{msgID = MsgID, 684 msgMaxSize = AgentMS, 685 msgFlags = snmp_misc:mk_msg_flags(Type, SecLevel), 686 msgSecurityModel = MsgSecurityModel}, 687 Message = #message{version = Vsn, 688 vsn_hdr = V3Hdr, 689 data = ScopedPDUBytes}, 690 %% We know that the security model is valid when we 691 %% generate a response. 692 SecModule = 693 case MsgSecurityModel of 694 ?SEC_USM -> 695 snmpa_usm 696 end, 697 SecEngineID = LocalEngineID, % 3.1.1a 698 ?vtrace("generate_response_msg -> SecEngineID: ~w", [SecEngineID]), 699 case (catch SecModule:generate_outgoing_msg(Message, 700 SecEngineID, 701 SecName, 702 SecData, 703 SecLevel, 704 LocalEngineID)) of 705 {'EXIT', Reason} -> 706 config_err("~p (message: ~p)", [Reason, Message]), 707 {discarded, Reason}; 708 {error, Reason} -> 709 config_err("~p (message: ~p)", [Reason, Message]), 710 {discarded, Reason}; 711 OutMsg when is_list(OutMsg) -> 712 %% Check the packet size. Send the msg even 713 %% if it's larger than the mgr can handle - it 714 %% will be dropped. Just check against the 715 %% internal size. For GET-BULk responses: we 716 %% *know* that we're within the right limits, 717 %% because of the calculation we do when we 718 %% receive the bulk-request. 719 Packet = list_to_binary(OutMsg), 720 case size(Packet) of 721 Len when Len =< AgentMS -> 722 if 723 SecLevel =:= 3 -> 724 %% encrypted - log decrypted pdu 725 Log(Type, {V3Hdr, ScopedPDUBytes}); 726 true -> 727 %% otherwise log the entire msg 728 Log(Type, Packet) 729 end, 730 inc_snmp_cnt_vars(Type, RePdu), 731 inc_snmp_out_vars(RePdu), 732 {ok, Packet}; 733 Len when N =:= 2 -> 734 ?vlog("packet max size exceeded: " 735 "~n Max: ~p" 736 "~n Len: ~p", 737 [AgentMS,Len]), 738 inc(snmpSilentDrops), 739 {discarded, tooBig}; 740 Len -> 741 ?vlog("packet max size exceeded: " 742 "~n N: ~p" 743 "~n Max: ~p" 744 "~n Len: ~p", 745 [N, AgentMS, Len]), 746 TooBigPdu = RePdu#pdu{error_status = tooBig, 747 error_index = 0, 748 varbinds = []}, 749 generate_response_msg(Vsn, TooBigPdu, Type, 750 {v3, MsgID, 751 MsgSecurityModel, 752 SecName, SecLevel, 753 ContextEngineID, 754 ContextName, 755 SecData}, 756 LocalEngineID, Log, N+1) 757 end 758 end 759 end. 760 761generate_v3_report_msg(MsgID, MsgSecurityModel, Data, LocalEngineID, 762 ErrorInfo, Log) -> 763 {Varbind, SecName, Opts} = ErrorInfo, 764 ReqId = 765 if 766 is_record(Data, scopedPdu) -> 767 (Data#scopedPdu.data)#pdu.request_id; 768 true -> 769 0 %% RFC2572, 7.1.3.c.4 770 end, 771 ?vtrace("Report ReqId: ~p",[ReqId]), 772 Pdu = #pdu{type = report, 773 request_id = ReqId, 774 error_status = noError, 775 error_index = 0, 776 varbinds = [Varbind]}, 777 SecLevel = snmp_misc:get_option(securityLevel, Opts, 0), 778 SnmpEngineID = LocalEngineID, 779 ContextEngineID = 780 snmp_misc:get_option(contextEngineID, Opts, SnmpEngineID), 781 ContextName = snmp_misc:get_option(contextName, Opts, ""), 782 SecData = snmp_misc:get_option(sec_data, Opts, []), 783 784 generate_response_msg('version-3', Pdu, report, 785 {v3, MsgID, MsgSecurityModel, SecName, SecLevel, 786 ContextEngineID, ContextName, SecData}, 787 LocalEngineID, Log). 788 789 790%% Response to stage 1 discovery message (terminating, i.e. from the manager) 791generate_discovery1_report_msg(MsgID, MsgSecurityModel, 792 SecName, SecLevel, 793 ContextEngineID, ContextName, 794 {SecData, Oid, Value}, 795 #pdu{request_id = ReqId}, 796 LocalEngineID, Log) -> 797 ?vtrace("generate_discovery1_report_msg -> entry with" 798 "~n ReqId: ~p" 799 "~n Value: ~p", [ReqId, Value]), 800 Varbind = #varbind{oid = Oid, 801 variabletype = 'Counter32', 802 value = Value, 803 org_index = 1}, 804 PduOut = #pdu{type = report, 805 request_id = ReqId, 806 error_status = noError, 807 error_index = 0, 808 varbinds = [Varbind]}, 809 case generate_response_msg('version-3', PduOut, report, 810 {v3, MsgID, MsgSecurityModel, SecName, SecLevel, 811 ContextEngineID, ContextName, SecData}, 812 LocalEngineID, Log) of 813 {ok, Packet} -> 814 {discovery, Packet}; 815 Error -> 816 Error 817 end. 818 819%% Response to stage 2 discovery message (terminating, i.e. from the manager) 820generate_discovery2_report_msg(MsgID, MsgSecurityModel, 821 SecName, SecLevel, 822 ContextEngineID, ContextName, 823 SecData, #pdu{request_id = ReqId}, 824 LocalEngineID, Log) -> 825 ?vtrace("generate_discovery2_report_msg -> entry with" 826 "~n ReqId: ~p", [ReqId]), 827 SecModule = get_security_module(MsgSecurityModel), 828 Vb = SecModule:current_statsNotInTimeWindows_vb(), 829 PduOut = #pdu{type = report, 830 request_id = ReqId, 831 error_status = noError, 832 error_index = 0, 833 varbinds = [Vb]}, 834 case generate_response_msg('version-3', PduOut, report, 835 {v3, MsgID, MsgSecurityModel, SecName, SecLevel, 836 ContextEngineID, ContextName, SecData}, 837 LocalEngineID, Log) of 838 {ok, Packet} -> 839 {discovery, Packet}; 840 Error -> 841 Error 842 end. 843 844 845too_big(Vsn, Pdu, Community, Log, _MMS, _Len) 846 when Pdu#pdu.type =:= 'get-response' -> 847 ErrPdu = 848 if 849 Vsn =:= 'version-1' -> 850 %% In v1, the varbinds should be identical to the incoming 851 %% request. It isn't identical now! 852 %% Make acceptable (?) approximation. 853 V = set_vb_null(Pdu#pdu.varbinds), 854 Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = V}; 855 true -> 856 %% In v2, varbinds should be empty (reasonable!) 857 Pdu#pdu{error_status = tooBig, error_index = 0, varbinds = []} 858 end, 859 860 case catch snmp_pdus:enc_pdu(ErrPdu) of 861 {'EXIT', Reason} -> 862 user_err("failed encoding pdu (pdu: ~w, community: ~w): ~n~w", 863 [ErrPdu, Community, Reason]), 864 {discarded, Reason}; 865 PduBytes -> 866 Message = #message{version = Vsn, vsn_hdr = Community, 867 data = PduBytes}, 868 case catch snmp_pdus:enc_message_only(Message) of 869 {'EXIT', Reason} -> 870 user_err("failed encoding message only" 871 "(pdu: ~w, community: ~w): ~n~w", 872 [ErrPdu, Community, Reason]), 873 {discarded, Reason}; 874 Packet -> 875 Bin = list_to_binary(Packet), 876 Log(Pdu#pdu.type, Bin), 877 inc_snmp_out_vars(ErrPdu), 878 {ok, Bin} 879 end 880 end; 881too_big(_Vsn, Pdu, _Community, _Log, MMS, Len) -> 882 user_err("encoded pdu, ~p bytes, exceeded " 883 "max message size of ~p bytes. Pdu: ~n~w", 884 [Len, MMS, Pdu]), 885 {discarded, tooBig}. 886 887set_vb_null([Vb | Vbs]) -> 888 [Vb#varbind{variabletype = 'NULL', value = 'NULL'} | set_vb_null(Vbs)]; 889set_vb_null([]) -> 890 []. 891 892%%----------------------------------------------------------------- 893%% Executed when a message that isn't a response is generated, i.e. 894%% a trap or an inform. 895%%----------------------------------------------------------------- 896generate_msg(Vsn, NoteStore, Pdu, ACMData, To) -> 897 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 898 generate_msg(Vsn, NoteStore, Pdu, ACMData, LocalEngineID, To). 899 900generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, LocalEngineID, To) -> 901 Message = #message{version = Vsn, vsn_hdr = Community, data = Pdu}, 902 case catch list_to_binary(snmp_pdus:enc_message(Message)) of 903 {'EXIT', Reason} -> 904 user_err("failed encoding message " 905 "(pdu: ~w, community: ~w): ~n~w", 906 [Pdu, Community, Reason]), 907 {discarded, Reason}; 908 Packet -> 909 AgentMax = get_engine_max_message_size(LocalEngineID), 910 case size(Packet) of 911 Len when Len =< AgentMax -> 912 {ok, mk_v1_v2_packet_list(To, Packet, Len, Pdu)}; 913 Len -> 914 ?vlog("packet max size exceeded: " 915 "~n Max: ~p" 916 "~n Len: ~p", 917 [AgentMax, Len]), 918 {discarded, tooBig} 919 end 920 end; 921generate_msg('version-3', NoteStore, Pdu, 922 {v3, ContextEngineID, ContextName}, LocalEngineID, To) -> 923 %% rfc2272: 7.1 step 6 924 ScopedPDU = #scopedPdu{contextEngineID = LocalEngineID, 925 contextName = ContextName, 926 data = Pdu}, 927 case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of 928 {'EXIT', Reason} -> 929 user_err("failed encoding scoped pdu " 930 "(pdu: ~w, contextName: ~w): ~n~w", 931 [Pdu, ContextName, Reason]), 932 {discarded, Reason}; 933 ScopedPDUBytes -> 934 {ok, mk_v3_packet_list(NoteStore, To, ScopedPDUBytes, Pdu, 935 ContextEngineID, ContextName, 936 LocalEngineID)} 937 end. 938 939 940generate_discovery_msg(NoteStore, Pdu, MsgData, To) -> 941 Timeout = 1500, 942 generate_discovery_msg(NoteStore, Pdu, MsgData, Timeout, To). 943 944generate_discovery_msg(NoteStore, Pdu, MsgData, Timeout, To) -> 945 {SecData, ContextEngineID, ContextName} = MsgData, 946 {SecModel, SecName, SecLevelFlag, TargetName} = SecData, 947 {ManagerEngineId, InitialUserName} = 948 case get_target_engine_id(TargetName) of 949 {ok, discovery} -> 950 {"", ""}; % Discovery stage 1 951 {ok, {discovery, IUN}} -> 952 {"", IUN}; % Discovery stage 1 953 {ok, TargetEngineId} -> 954 {TargetEngineId, ""} % Discovery stage 2 955 end, 956 generate_discovery_msg(NoteStore, Pdu, 957 ContextEngineID, ContextName, 958 SecModel, SecName, SecLevelFlag, 959 ManagerEngineId, 960 InitialUserName, 961 Timeout, To). 962 963generate_discovery_msg(NoteStore, Pdu, 964 ContextEngineID, ContextName, 965 SecModel, _SecName, _SecLevelFlag, 966 "" = ManagerEngineID, 967 InitialUserName, 968 Timeout, To) -> 969 %% Discovery step 1 uses SecLevel = noAuthNoPriv 970 SecName = "", 971 SecLevelFlag = 0, % ?'SnmpSecurityLevel_noAuthNoPriv', 972 generate_discovery_msg2(NoteStore, Pdu, 973 ContextEngineID, ManagerEngineID, 974 SecModel, SecName, SecLevelFlag, 975 InitialUserName, 976 ContextName, Timeout, To); 977generate_discovery_msg(NoteStore, Pdu, 978 ContextEngineID, ContextName, 979 SecModel, SecName, SecLevelFlag, 980 ManagerEngineID, 981 InitialUserName, 982 Timeout, To) -> 983 %% SecLevelFlag = 1, % ?'SnmpSecurityLevel_authNoPriv', 984 generate_discovery_msg2(NoteStore, Pdu, 985 ContextEngineID, ManagerEngineID, 986 SecModel, SecName, SecLevelFlag, 987 InitialUserName, 988 ContextName, Timeout, To). 989 990generate_discovery_msg2(NoteStore, Pdu, 991 ContextEngineID, ManagerEngineID, 992 SecModel, SecName, SecLevelFlag, 993 InitialUserName, 994 ContextName, Timeout, To) -> 995 %% rfc2272: 7.1.6 996 ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID, 997 contextName = ContextName, 998 data = Pdu}, 999 case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of 1000 {'EXIT', Reason} -> 1001 user_err("failed encoding scoped pdu " 1002 "(pdu: ~w, contextName: ~w): ~n~w", 1003 [Pdu, ContextName, Reason]), 1004 {discarded, Reason}; 1005 ScopedPDUBytes -> 1006 {ok, generate_discovery_msg(NoteStore, To, 1007 Pdu, ScopedPDUBytes, 1008 ContextEngineID, ManagerEngineID, 1009 SecModel, SecName, SecLevelFlag, 1010 InitialUserName, 1011 ContextName, Timeout)} 1012 end. 1013 1014%% Timeout is in msec but note timeout is in 1/10 seconds 1015discovery_note_timeout(Timeout) -> 1016 (Timeout div 100) + 1. 1017 1018generate_discovery_msg(NoteStore, {TDomain, TAddress}, 1019 Pdu, ScopedPduBytes, 1020 ContextEngineID, ManagerEngineID, 1021 SecModel, SecName, SecLevelFlag, 1022 InitialUserName, 1023 ContextName, Timeout) -> 1024 1025 {ok, {Domain, Address}} = transform_taddr(TDomain, TAddress), 1026 1027 %% 7.1.7 1028 ?vdebug("generate_discovery_msg -> 7.1.7 (~w)", [ManagerEngineID]), 1029 MsgID = generate_msg_id(), 1030 PduType = Pdu#pdu.type, 1031 MsgFlags = mk_msg_flags(PduType, SecLevelFlag), 1032 V3Hdr = #v3_hdr{msgID = MsgID, 1033 msgMaxSize = get_max_message_size(), 1034 msgFlags = MsgFlags, 1035 msgSecurityModel = SecModel}, 1036 Message = #message{version = 'version-3', 1037 vsn_hdr = V3Hdr, 1038 data = ScopedPduBytes}, 1039 SecModule = sec_module(SecModel), 1040 1041 %% 7.1.9b 1042 ?vdebug("generate_discovery_msg -> 7.1.9b", []), 1043 case generate_sec_discovery_msg(Message, SecModule, 1044 ManagerEngineID, 1045 SecName, SecLevelFlag, 1046 InitialUserName) of 1047 {ok, Packet} -> 1048 %% 7.1.9c 1049 %% Store in cache for Timeout msec. 1050 NoteTimeout = discovery_note_timeout(Timeout), 1051 ?vdebug("generate_discovery_msg -> 7.1.9c [~w]", [NoteTimeout]), 1052 %% The request id is just in case when we receive a 1053 %% report with incorrect securityModel and/or securityLevel 1054 Key = {agent, MsgID}, 1055 Note = #note{sec_engine_id = ManagerEngineID, 1056 sec_model = SecModel, 1057 sec_name = SecName, 1058 sec_level = SecLevelFlag, 1059 ctx_engine_id = ContextEngineID, 1060 ctx_name = ContextName, 1061 disco = true, 1062 req_id = Pdu#pdu.request_id}, 1063 snmp_note_store:set_note(NoteStore, Timeout, Key, Note), 1064 %% Log(Packet), 1065 inc_snmp_out_vars(Pdu), 1066 ?vdebug("generate_discovery_msg -> done", []), 1067 {Domain, Address, Packet}; 1068 1069 Error -> 1070 throw(Error) 1071 end. 1072 1073generate_sec_discovery_msg(Message, SecModule, 1074 SecEngineID, SecName, SecLevelFlag, 1075 InitialUserName) -> 1076 case (catch SecModule:generate_discovery_msg(Message, SecEngineID, 1077 SecName, SecLevelFlag, 1078 InitialUserName)) of 1079 {'EXIT', Reason} -> 1080 config_err("~p (message: ~p)", [Reason, Message]), 1081 {discarded, Reason}; 1082 {error, Reason} -> 1083 config_err("~p (message: ~p)", [Reason, Message]), 1084 {discarded, Reason}; 1085 Bin when is_binary(Bin) -> 1086 {ok, Bin}; 1087 OutMsg when is_list(OutMsg) -> 1088 case (catch list_to_binary(OutMsg)) of 1089 Bin when is_binary(Bin) -> 1090 {ok, Bin}; 1091 {'EXIT', Reason} -> 1092 {error, Reason} 1093 end 1094 end. 1095 1096 1097transform_taddr(?snmpUDPDomain, TAddress) -> 1098 transform_taddr(?transportDomainUdpIpv4, TAddress); 1099transform_taddr(?transportDomainUdpIpv4, [A, B, C, D, P1, P2]) -> 1100 Domain = transportDomainUdpIpv4, 1101 Addr = {A,B,C,D}, 1102 Port = P1 bsl 8 + P2, 1103 Address = {Addr, Port}, 1104 {ok, {Domain, Address}}; 1105transform_taddr(?transportDomainUdpIpv4, BadAddr) -> 1106 {error, {bad_transportDomainUdpIpv4_address, BadAddr}}; 1107transform_taddr( 1108 ?transportDomainUdpIpv6, 1109 [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, 1110 P1, P2]) -> 1111 Domain = transportDomainUdpIpv6, 1112 Addr = 1113 {(A1 bsl 8) bor A2, (A3 bsl 8) bor A4, 1114 (A5 bsl 8) bor A6, (A7 bsl 8) bor A8, 1115 (A9 bsl 8) bor A10, (A11 bsl 8) bor A12, 1116 (A13 bsl 8) bor A14, (A15 bsl 8) bor A16}, 1117 Port = P1 bsl 8 + P2, 1118 Address = {Addr, Port}, 1119 {ok, {Domain, Address}}; 1120transform_taddr( 1121 ?transportDomainUdpIpv6, 1122 [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]) -> 1123 Domain = transportDomainUdpIpv6, 1124 Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, 1125 Port = P1 bsl 8 + P2, 1126 Address = {Addr, Port}, 1127 {ok, {Domain, Address}}; 1128transform_taddr(?transportDomainUdpIpv6, BadAddr) -> 1129 {error, {bad_transportDomainUdpIpv6_address, BadAddr}}; 1130transform_taddr(BadTDomain, TAddress) -> 1131 case lists:member(BadTDomain, snmp_conf:all_tdomains()) of 1132 true -> 1133 {error, {unsupported_tdomain, BadTDomain, TAddress}}; 1134 false -> 1135 {error, {unknown_tdomain, BadTDomain, TAddress}} 1136 end. 1137 1138 1139process_taddrs(Dests) -> 1140 ?vtrace("process_taddrs -> entry with" 1141 "~n Dests: ~p", [Dests]), 1142 process_taddrs(Dests, []). 1143 1144process_taddrs([], Acc) -> 1145 ?vtrace("process_taddrs -> entry when done with" 1146 "~n Acc: ~p", [Acc]), 1147 lists:reverse(Acc); 1148 1149%% v3 1150process_taddrs([{{TDomain, TAddress}, SecData} | T], Acc) -> 1151 ?vtrace("process_taddrs -> entry when v3 with" 1152 "~n TDomain: ~p" 1153 "~n TAddress: ~p" 1154 "~n SecData: ~p", [TDomain, TAddress, SecData]), 1155 case transform_taddr(TDomain, TAddress) of 1156 {ok, DestAddr} -> 1157 ?vtrace("process_taddrs -> transformed: " 1158 "~n DestAddr: ~p", [DestAddr]), 1159 Entry = {DestAddr, SecData}, 1160 process_taddrs(T, [Entry | Acc]); 1161 {error, Reason} -> 1162 ?vinfo("Failed transforming v3 domain and address" 1163 "~n Reason: ~p", [Reason]), 1164 user_err("Bad TDomain/TAddress: ~w/~w", [TDomain, TAddress]), 1165 process_taddrs(T, Acc) 1166 end; 1167%% v1 & v2 1168process_taddrs([{TDomain, TAddress} | T], Acc) -> 1169 ?vtrace("process_taddrs -> entry when v1/v2 with" 1170 "~n TDomain: ~p" 1171 "~n TAddress: ~p", [TDomain, TAddress]), 1172 case transform_taddr(TDomain, TAddress) of 1173 {ok, DestAddr} -> 1174 ?vtrace("process_taddrs -> transformed: " 1175 "~n DestAddr: ~p", [DestAddr]), 1176 Entry = DestAddr, 1177 process_taddrs(T, [Entry | Acc]); 1178 {error, Reason} -> 1179 ?vinfo("Failed transforming v1/v2 domain and address: " 1180 "~n Reason: ~p", [Reason]), 1181 user_err("Bad TDomain/TAddress: ~w/~w", [TDomain, TAddress]), 1182 process_taddrs(T, Acc) 1183 end; 1184process_taddrs(Crap, Acc) -> 1185 throw({error, {bad_taddrs, Crap, Acc}}). 1186 1187 1188mk_v1_v2_packet_list(To, Packet, Len, Pdu) -> 1189 mk_v1_v2_packet_list(To, Packet, Len, Pdu, []). 1190 1191mk_v1_v2_packet_list([], _Packet, _Len, _Pdu, Acc) -> 1192 lists:reverse(Acc); 1193 1194%% This (old) clause is for backward compatibillity reasons 1195%% If this is called, then the filter function is not used 1196mk_v1_v2_packet_list([{?snmpUDPDomain, [A,B,C,D,U1,U2]} | T], 1197 Packet, Len, Pdu, Acc) -> 1198 %% Sending from default UDP port 1199 inc_snmp_out_vars(Pdu), 1200 Entry = {snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}, Packet}, 1201 mk_v1_v2_packet_list(T, Packet, Len, Pdu, [Entry | Acc]); 1202 1203%% This is the new clause 1204%% This is only called if the actual target was accepted 1205%% (by the filter module) 1206mk_v1_v2_packet_list([{Domain, Addr} | T], 1207 Packet, Len, Pdu, Acc) -> 1208 %% Sending from default UDP port 1209 inc_snmp_out_vars(Pdu), 1210 Entry = {Domain, Addr, Packet}, 1211 %% It would be cleaner to return {To, Packet} to not 1212 %% break the abstraction for an address on the 1213 %% {Domain, Address} format. 1214 mk_v1_v2_packet_list(T, Packet, Len, Pdu, [Entry | Acc]). 1215 1216 1217get_max_message_size() -> 1218 snmp_framework_mib:get_engine_max_message_size(). 1219 1220mk_msg_flags(PduType, SecLevel) -> 1221 snmp_misc:mk_msg_flags(PduType, SecLevel). 1222 1223mk_v3_packet_entry(NoteStore, Domain, Addr, 1224 {SecModel, SecName, SecLevel, TargetAddrName}, 1225 ScopedPDUBytes, Pdu, _ContextEngineID, ContextName, 1226 LocalEngineID) -> 1227 %% rfc2272 7.1 step 77 1228 ?vtrace("mk_v3_packet_entry -> entry - RFC2272-7.1:7", []), 1229 MsgVersion = 'version-3', % 7.1:7a 1230 MsgID = generate_msg_id(), % 7.1:7b 1231 MaxMsgSz = get_max_message_size(), % 7.1:7c 1232 PduType = Pdu#pdu.type, 1233 MsgFlags = mk_msg_flags(PduType, SecLevel), % 7.1:7d 1234 MsgSecModel = SecModel, % 7.1:7e 1235 V3Hdr = #v3_hdr{msgID = MsgID, 1236 msgMaxSize = MaxMsgSz, 1237 msgFlags = MsgFlags, 1238 msgSecurityModel = MsgSecModel}, 1239 Message = #message{version = MsgVersion, 1240 vsn_hdr = V3Hdr, 1241 data = ScopedPDUBytes}, 1242 SecModule = 1243 case SecModel of 1244 ?SEC_USM -> 1245 snmpa_usm 1246 end, 1247 1248 %% 1249 %% 7.1:8 - If the PDU is from the Response Class or the Internal Class 1250 %% securityEngineID = snmpEngineID (local/source) 1251 %% 7.1:9 - If the PDU is from the Unconfirmed Class 1252 %% securityEngineID = snmpEngineID (local/source) 1253 %% else 1254 %% securityEngineID = targetEngineID (remote/destination) 1255 %% 1256 1257 %% 7.1.9a 1258 ?vtrace("mk_v3_packet_entry -> sec engine id - 7.1.9a", []), 1259 SecEngineID = 1260 case PduType of 1261 'snmpv2-trap' -> 1262 LocalEngineID; 1263 _ -> 1264 %% This is the implementation dependent target engine id 1265 %% procedure. 1266 case get_target_engine_id(TargetAddrName) of 1267 {ok, discovery} -> 1268 config_err("Discovery has not yet been performed for " 1269 "snmpTargetAddrName ~p~n", 1270 [TargetAddrName]), 1271 throw({discarded, {discovery, TargetAddrName}}); 1272 {ok, TargetEngineId} -> 1273 ?vtrace("TargetEngineId: ~p", [TargetEngineId]), 1274 TargetEngineId; 1275 undefined -> 1276 config_err("Can't find engineID for " 1277 "snmpTargetAddrName ~p~n", 1278 [TargetAddrName]), 1279 "" % this will trigger error in secmodule 1280 end 1281 end, 1282 1283 ?vdebug("mk_v3_packet_entry -> secEngineID: ~p", [SecEngineID]), 1284 %% 7.1.9b 1285 case (catch SecModule:generate_outgoing_msg(Message, SecEngineID, 1286 SecName, [], SecLevel, 1287 LocalEngineID)) of 1288 {'EXIT', Reason} -> 1289 config_err("~p (message: ~p)", [Reason, Message]), 1290 skip; 1291 {error, Reason} -> 1292 ?vlog("~n ~w error ~p\n", [SecModule, Reason]), 1293 skip; 1294 OutMsg when is_list(OutMsg) -> 1295 %% 7.1.9c 1296 %% Store in cache for 150 sec. 1297 Packet = list_to_binary(OutMsg), 1298 ?vdebug("mk_v3_packet_entry -> generated: ~w bytes", 1299 [size(Packet)]), 1300 Data = 1301 if 1302 SecLevel =:= 3 -> 1303 %% encrypted - log decrypted pdu 1304 {Packet, {V3Hdr, ScopedPDUBytes}}; 1305 true -> 1306 %% otherwise log the entire msg 1307 Packet 1308 end, 1309 CacheKey = {agent, MsgID}, 1310 CacheVal = #note{sec_engine_id = SecEngineID, 1311 sec_model = SecModel, 1312 sec_name = SecName, 1313 sec_level = SecLevel, 1314 ctx_engine_id = LocalEngineID, 1315 ctx_name = ContextName, 1316 disco = false, 1317 req_id = Pdu#pdu.request_id}, 1318 snmp_note_store:set_note(NoteStore, 1500, CacheKey, CacheVal), 1319 inc_snmp_out_vars(Pdu), 1320 %% It would be cleaner to return {To, Packet} to not 1321 %% break the abstraction for an address on the 1322 %% {Domain, Address} format. 1323 {ok, {Domain, Addr, Data}} 1324 end. 1325 1326 1327mk_v3_packet_list(NoteStore, To, 1328 ScopedPDUBytes, Pdu, ContextEngineID, ContextName, 1329 LocalEngineID) -> 1330 mk_v3_packet_list(NoteStore, To, 1331 ScopedPDUBytes, Pdu, 1332 ContextEngineID, ContextName, LocalEngineID, []). 1333 1334mk_v3_packet_list(_, [], 1335 _ScopedPDUBytes, _Pdu, 1336 _ContextEngineID, _ContextName, 1337 _LocalEngineID, Acc) -> 1338 lists:reverse(Acc); 1339 1340%% This clause is for backward compatibillity reasons 1341%% If this is called the filter function is not used 1342mk_v3_packet_list(NoteStore, 1343 [{{?snmpUDPDomain, [A,B,C,D,U1,U2]}, SecData} | T], 1344 ScopedPDUBytes, Pdu, ContextEngineID, ContextName, 1345 LocalEngineID, Acc) -> 1346 case mk_v3_packet_entry(NoteStore, 1347 snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}, SecData, 1348 ScopedPDUBytes, Pdu, 1349 ContextEngineID, ContextName, LocalEngineID) of 1350 skip -> 1351 mk_v3_packet_list(NoteStore, T, 1352 ScopedPDUBytes, Pdu, 1353 ContextEngineID, ContextName, LocalEngineID, 1354 Acc); 1355 {ok, Entry} -> 1356 mk_v3_packet_list(NoteStore, T, 1357 ScopedPDUBytes, Pdu, 1358 ContextEngineID, ContextName, LocalEngineID, 1359 [Entry | Acc]) 1360 end; 1361 1362%% This is the new clause 1363%% This is only called if the actual target was accepted 1364%% (by the filter module) 1365mk_v3_packet_list(NoteStore, 1366 [{{Domain, Addr}, SecData} | T], 1367 ScopedPDUBytes, Pdu, ContextEngineID, ContextName, 1368 LocalEngineID, Acc) -> 1369 case mk_v3_packet_entry(NoteStore, 1370 Domain, Addr, SecData, 1371 ScopedPDUBytes, Pdu, 1372 ContextEngineID, ContextName, LocalEngineID) of 1373 skip -> 1374 mk_v3_packet_list(NoteStore, T, 1375 ScopedPDUBytes, Pdu, 1376 ContextEngineID, ContextName, Acc); 1377 {ok, Entry} -> 1378 mk_v3_packet_list(NoteStore, T, 1379 ScopedPDUBytes, Pdu, 1380 ContextEngineID, ContextName, 1381 LocalEngineID, [Entry | Acc]) 1382 end. 1383 1384 1385generate_msg_id() -> 1386 gen(msg_id). 1387 1388generate_req_id() -> 1389 gen(req_id). 1390 1391gen(Id) -> 1392 case ets:update_counter(snmp_agent_table, Id, 1) of 1393 N when N =< 2147483647 -> 1394 N; 1395 _N -> 1396 ets:insert(snmp_agent_table, {Id, 0}), 1397 0 1398 end. 1399 1400 1401get_target_engine_id(TargetAddrName) -> 1402 snmp_target_mib:get_target_engine_id(TargetAddrName). 1403 1404get_engine_max_message_size(_LocalEngineID) -> 1405 snmp_framework_mib:get_engine_max_message_size(). 1406 1407sec_module(?SEC_USM) -> 1408 snmpa_usm. 1409 1410 1411%%----------------------------------------------------------------- 1412%% Version(s) functions 1413%%----------------------------------------------------------------- 1414init_versions([], S) -> 1415 S; 1416init_versions([v1|Vsns], S) -> 1417 init_versions(Vsns, S#state{v1 = true}); 1418init_versions([v2|Vsns], S) -> 1419 init_versions(Vsns, S#state{v2c = true}); 1420init_versions([v3|Vsns], S) -> 1421 init_versions(Vsns, S#state{v3 = true}). 1422 1423 1424%%----------------------------------------------------------------- 1425%% Counter functions 1426%%----------------------------------------------------------------- 1427init_counters() -> 1428 F = fun(Counter) -> maybe_create_counter(Counter) end, 1429 lists:map(F, counters()). 1430 1431reset_counters() -> 1432 F = fun(Counter) -> init_counter(Counter) end, 1433 lists:map(F, counters()). 1434 1435maybe_create_counter(Counter) -> 1436 case ets:lookup(snmp_agent_table, Counter) of 1437 [_] -> ok; 1438 _ -> init_counter(Counter) 1439 end. 1440 1441init_counter(Counter) -> 1442 ets:insert(snmp_agent_table, {Counter, 0}). 1443 1444counters() -> 1445 [ 1446 snmpInPkts, 1447 snmpOutPkts, 1448 snmpInBadVersions, 1449 snmpInBadCommunityNames, 1450 snmpInBadCommunityUses, 1451 snmpInASNParseErrs, 1452 snmpInTooBigs, 1453 snmpInNoSuchNames, 1454 snmpInBadValues, 1455 snmpInReadOnlys, 1456 snmpInGenErrs, 1457 snmpInTotalReqVars, 1458 snmpInTotalSetVars, 1459 snmpInGetRequests, 1460 snmpInGetNexts, 1461 snmpInSetRequests, 1462 snmpInGetResponses, 1463 snmpInTraps, 1464 snmpOutTooBigs, 1465 snmpOutNoSuchNames, 1466 snmpOutBadValues, 1467 snmpOutGenErrs, 1468 snmpOutGetRequests, 1469 snmpOutGetNexts, 1470 snmpOutSetRequests, 1471 snmpOutGetResponses, 1472 snmpOutTraps, 1473 snmpSilentDrops, 1474 snmpProxyDrops, 1475 %% From SNMP-MPD-MIB 1476 snmpUnknownSecurityModels, 1477 snmpInvalidMsgs, 1478 snmpUnknownPDUHandlers 1479 ]. 1480 1481 1482 1483%%----------------------------------------------------------------- 1484%% inc(VariableName) increments the variable (Counter) in 1485%% the local mib. (e.g. snmpInPkts) 1486%%----------------------------------------------------------------- 1487inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). 1488inc(Name, N) -> ets:update_counter(snmp_agent_table, Name, N). 1489 1490inc_snmp_in_vars(#pdu{type = Type}) -> 1491 inc_in_type(Type). 1492 1493inc_snmp_cnt_vars(_, #pdu{error_status = ErrStat}) when ErrStat =/= noError -> 1494 ok; 1495inc_snmp_cnt_vars('get-request', #pdu{varbinds = Vbs}) -> 1496 inc(snmpInTotalReqVars, length(Vbs)); 1497inc_snmp_cnt_vars('get-next-request', #pdu{varbinds = Vbs}) -> 1498 inc(snmpInTotalReqVars, length(Vbs)); 1499inc_snmp_cnt_vars('set-request', #pdu{varbinds = Vbs}) -> 1500 inc(snmpInTotalSetVars, length(Vbs)); 1501inc_snmp_cnt_vars(_, _) -> 1502 ok. 1503 1504inc_snmp_out_vars(#pdu{type = Type, 1505 error_status = ErrorStatus}) -> 1506 inc(snmpOutPkts), 1507 inc_out_err(ErrorStatus), 1508 inc_out_vars_2(Type); 1509inc_snmp_out_vars(TrapPdu) when is_record(TrapPdu, trappdu) -> 1510 inc(snmpOutPkts), 1511 inc(snmpOutTraps). 1512 1513inc_out_vars_2('get-response') -> inc(snmpOutGetResponses); 1514inc_out_vars_2('get-request') -> inc(snmpOutGetRequests); 1515inc_out_vars_2('get-next-request') -> inc(snmpOutGetNexts); 1516inc_out_vars_2('set-request') -> inc(snmpOutSetRequests); 1517inc_out_vars_2(_) -> ok. 1518 1519inc_out_err(genErr) -> inc(snmpOutGenErrs); 1520inc_out_err(tooBig) -> inc(snmpOutTooBigs); 1521inc_out_err(noSuchName) -> inc(snmpOutNoSuchNames); 1522inc_out_err(badValue) -> inc(snmpOutBadValues); 1523% snmpOutReadOnlys is not used any more (rfc1213) 1524%inc_out_err(readOnly) -> inc(snmpOutReadOnlys); 1525inc_out_err(_) -> ok. 1526 1527inc_in_type('get-request') -> inc(snmpInGetRequests); 1528inc_in_type('get-next-request') -> inc(snmpInGetNexts); 1529inc_in_type('set-request') -> inc(snmpInSetRequests); 1530inc_in_type(_) -> ok. 1531 1532 1533user_err(F, A) -> 1534 snmpa_error:user_err(F, A). 1535 1536config_err(F, A) -> 1537 snmpa_error:config_err(F, A). 1538