1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-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-module(snmpa_trap). 21 22%%%----------------------------------------------------------------- 23%%% This module takes care of all trap(notification handling. 24%%%----------------------------------------------------------------- 25%% External exports 26-export([construct_trap/2, 27 try_initialise_vars/2, 28 send_trap/6, send_trap/7, send_trap/8]). 29-export([send_discovery/6]). 30 31%% Internal exports 32-export([init_v2_inform/9, init_v2_inform/10, 33 init_v3_inform/9, init_v3_inform/10, init_v3_inform/11, 34 send_inform/6]). 35-export([init_discovery_inform/13, send_discovery_inform/5]). 36 37%% <BACKWARD-COMPAT> 38-export([send_discovery/5, 39 init_discovery_inform/12]). 40%% </BACKWARD-COMPAT> 41 42-include_lib("snmp/include/snmp_types.hrl"). 43-include_lib("snmp/src/agent/snmpa_internal.hrl"). 44-include_lib("snmp/include/SNMPv2-MIB.hrl"). 45-include_lib("snmp/include/SNMPv2-TM.hrl"). 46-include_lib("snmp/include/SNMPv2-TC.hrl"). 47-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). 48-include_lib("snmp/include/SNMP-TARGET-MIB.hrl"). 49-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). 50-define(enterpriseSpecific, 6). 51 52 53-define(VMODULE,"TRAP"). 54-include("snmp_verbosity.hrl"). 55-include("snmpa_internal.hrl"). 56 57 58%%----------------------------------------------------------------- 59%% Trap mechanism 60%% ============== 61%% Distributed subagent (dSA) case 62%% The MIB with the TRAP-TYPE macro is loaded in dSA. This means 63%% that dSA has info on all variables defined in the TRAP-TYPE, 64%% even though some variables may be located in other SA:s (or 65%% in the MA). Other variables that may be sent in the trap, 66%% must be known by either the dSA, or some of its parent agents 67%% (e.g. the master agent), if the variable should be referred 68%% to by symbolic name. It is however possible to send other 69%% variables as well, but then the entire OID must be provided. 70%% The dSA locates the asn1 type, oid and value for as many 71%% variables as possible. This information, together with the 72%% variables for which the type, value or oid isn't known, is 73%% sent to the dSA's parent. This agent performs the same 74%% operation, and so on, until eventually the MA will receive the 75%% info. The MA then fills in the gaps, and at this point all 76%% oids and types must be known, otherwise an error is signalled, 77%% and the opertaion is aborted. For the unknown values for some 78%% oids, a get-operation is performed by the MA. This will 79%% retreive the missing values. 80%% At this point, all oid, types and values are known, so the MA 81%% can distribute the traps according to the information in the 82%% internal tables. 83%% 84%% Local subagent (lSA) case 85%% This case is similar to the case above. 86%% 87%% Master agent (MA) case 88%% This case is similar to the case above. 89%% 90%% NOTE: All trap forwarding between agents is made asynchronously. 91%% 92%% dSA: Distributed SA (the #trap is loaded here) 93%% nSA: [many] SAs between dSA and MA 94%% MA: Master Agent. (all trap info (destiniations is here)) 95%% 1) application decides to send a trap. 96%% 2) dSA calls send_trap which initialises vars 97%% 3) dSA sends all to nSA 98%% 4) nSA tries to map symbolic names to oids and find the types 99%% of all variableoids with a value (and no type). 100%% 5) nSA sends all to (n-1)SA 101%% 6) MA tries to initialise vars 102%% 7) MA makes a trappdu, and sends it to all destination. 103%% 104%% Problems with this implementation 105%% ================================= 106%% It's ok to send {Oid, Value} but not just Oid. (it should be for 107%% any Oid) 108%% It's ok to send {Name, Value} but not just Name. (it should be 109%% for Names in the hierarchy) 110%% This approach might be too flexible; will people use it? 111%% *NOTE* 112%% Therefore, in this version we *do not* allow extra variables 113%% in traps. 114%% *YES* In _this_ version we do. 115%%----------------------------------------------------------------- 116 117%%----------------------------------------------------------------- 118%% Func: construct_trap/2 119%% Args: Trap is an atom 120%% Varbinds is a list of 121%% {Variable, Value} | {SymbolicTableCol, RowIndex, Value} 122%% where Variable is an atom or an OID, 123%% where RowIndex is the indexes for the row. 124%% We don't check the RowIndex. 125%% Purpose: This is the initially-called function. It is called 126%% by the agent that found out that a trap should be 127%% sent. 128%% Initialize as many variables as possible. 129%% Returns: {ok, TrapRecord, <list of Var>} | error 130%% where Var is returned from initiate_vars. 131%% NOTE: Executed at the inital SA 132%%----------------------------------------------------------------- 133construct_trap(Trap, Varbinds) -> 134 ?vdebug("construct_trap -> entry with" 135 "~n Trap: ~p", [Trap]), 136 case snmpa_symbolic_store:get_notification(Trap) of 137 undefined -> 138 user_err("construct_trap got undef Trap: ~w" , [Trap]), 139 error; 140 141 {value, #trap{oidobjects = ListOfVars} = TRec} -> 142 ?vdebug("construct_trap -> trap" 143 "~n ~p", [TRec]), 144 OidVbs = [alias_to_oid(Vb) || Vb <- Varbinds], 145 LV = initiate_vars(ListOfVars, OidVbs), 146 InitiatedVars = try_initialise_vars(get(mibserver), LV), 147 {ok, TRec, InitiatedVars}; 148 149 {value, #notification{oidobjects = ListOfVars} = NRec} -> 150 ?vdebug("construct_trap -> notification" 151 "~n ~p", [NRec]), 152 OidVbs = [alias_to_oid(Vb) || Vb <- Varbinds], 153 LV = initiate_vars(ListOfVars, OidVbs), 154 InitiatedVars = try_initialise_vars(get(mibserver), LV), 155 {ok, NRec, InitiatedVars} 156 end. 157 158%% Variable value (without oid processing) 159alias_to_oid({Alias, Val}) when is_atom(Alias) -> 160 case alias2oid(Alias) of 161 Alias -> 162 {{keep, Alias}, {value, Val}}; 163 Oid -> 164 {{keep, Oid}, {value, Val}} 165 end; 166alias_to_oid({Oid, Val}) when is_list(Oid) -> 167 {{keep, Oid}, {value, Val}}; 168 169%% Variable value (with oid processing) 170alias_to_oid({{Process, Alias}, Val}) when is_atom(Alias) -> 171 case alias2oid(Alias) of 172 Alias -> 173 {{Process, Alias}, {value, Val}}; 174 Oid -> 175 {{Process, Oid}, {value, Val}} 176 end; 177alias_to_oid({{Process, Oid}, Val}) when is_list(Oid) -> 178 {{Process, Oid}, {value, Val}}; 179 180%% Table Column value 181alias_to_oid({Alias, RowIndex, Val}) when is_atom(Alias) -> 182 case alias2oid(Alias, RowIndex) of 183 Alias -> 184 {Alias, RowIndex, {value, Val}}; 185 Oid -> 186 {{keep, Oid}, {value, Val}} 187 188 end. 189 190alias2oid(Alias) -> 191 alias2oid(Alias, [0]). 192 193alias2oid(Alias, Append) -> 194 case snmpa_symbolic_store:aliasname_to_oid(Alias) of 195 {value, Oid} -> 196 lists:append(Oid, Append); 197 _ -> 198 Alias 199 end. 200 201 202%%----------------------------------------------------------------- 203%% Func: initiate_vars/2 204%% Args: ListOfVars is a list of {Oid, #asn1_type} 205%% Varbinds is a list of 206%% {{Process, VariableOid}, Value} | 207%% {{Process, VariableAtom}, Value} | 208%% {TableColAtom, RowIndex, Value} 209%% Purpose: For each variable specified in the TRAP-TYPE macro 210%% (each in ListOfVars), check if it's got a value given 211%% in the Varbinds list. 212%% For each Oid: 213%% 1) It has corresponding VariableOid. Use Value. 214%% 2) No corresponding VariableOid. No value. 215%% Returns: A list of 216%% {{Process, VariableOid}, #asn1_type, Value} | 217%% {{Process, VariableOid}, #asn1_type} | 218%% {{Process, VariableOid}, Value} | 219%% {{Process, VariableAtom}, Value} | 220%% {TableColAtom, RowIndex, Value} 221%% NOTE: Executed at the inital SA 222%%----------------------------------------------------------------- 223initiate_vars([{Oid, Asn1Type} | T], Varbinds) -> 224 case delete_oid_from_varbinds(Oid, Varbinds) of 225 {undefined, _, _} -> 226 [{{keep, Oid}, Asn1Type} | initiate_vars(T, Varbinds)]; 227 %% Skip this oid! 228 {{value, ?NOTIFICATION_IGNORE_VB_VALUE}, _VarOid, RestOfVarbinds} -> 229 initiate_vars(T, RestOfVarbinds); 230 {Value, VarOid, RestOfVarbinds} -> 231 [{VarOid, Asn1Type, Value} | initiate_vars(T, RestOfVarbinds)] 232 end; 233initiate_vars([], Varbinds) -> 234 Varbinds. 235 236delete_oid_from_varbinds(Oid, [{{_Process, VarOid} = VOid, Value} | T]) -> 237 case lists:prefix(Oid, VarOid) of 238 true -> 239 {Value, VOid, T}; 240 _ -> 241 {Value2, VOid2, T2} = delete_oid_from_varbinds(Oid, T), 242 {Value2, VOid2, [{VOid, Value} | T2]} 243 end; 244delete_oid_from_varbinds(Oid, [H | T]) -> 245 {Value, VOid, T2} = delete_oid_from_varbinds(Oid, T), 246 {Value, VOid, [H | T2]}; 247delete_oid_from_varbinds(_Oid, []) -> 248 {undefined, undefined, []}. 249 250 251%%----------------------------------------------------------------- 252%% Func: try_initialise_vars(Mib, Varbinds) 253%% Args: Mib is the local mib process 254%% Varbinds is a list returned from initiate_vars. 255%% Purpose: Try to initialise uninitialised vars. 256%% Returns: see initiate_vars 257%% NOTE: Executed at the intermediate SAs 258%%----------------------------------------------------------------- 259try_initialise_vars(Mib, Varbinds) -> 260 V = try_map_symbolic(Varbinds), 261 try_find_type(V, Mib). 262 263 264%%----------------------------------------------------------------- 265%% Func: try_map_symbolic/1 266%% Args: Varbinds is a list returned from initiate_vars. 267%% Purpose: Try to map symbolic name to oid for the 268%% symbolic names left in the Varbinds list. 269%% Returns: see initiate_vars. 270%% NOTE: Executed at the intermediate SAs 271%%----------------------------------------------------------------- 272try_map_symbolic([Varbind | Varbinds]) -> 273 [localise_oid(Varbind) | try_map_symbolic(Varbinds)]; 274try_map_symbolic([]) -> []. 275 276localise_oid({{_Process, Alias}, _Value} = VB) when is_atom(Alias) -> 277 alias_to_oid(VB); 278localise_oid({Alias, _RowIndex, _Value} = VB) when is_atom(Alias) -> 279 alias_to_oid(VB); 280localise_oid(X) -> 281 X. 282 283 284%%----------------------------------------------------------------- 285%% Func: try_find_type/2 286%% Args: Varbinds is a list returned from initiate_vars. 287%% Mib is a ref to the Mib process corresponding to 288%% this agent. 289%% Purpose: Try to find the type for each variableoid with a value 290%% but no type. 291%% Returns: see initiate_vars. 292%% NOTE: Executed at the intermediate SAs 293%%----------------------------------------------------------------- 294try_find_type(Varbinds, Mib) -> 295 [localise_type(Varbind, Mib) || Varbind <- Varbinds]. 296 297%% We add the 'process oid' value of 'keep' for all variables 298%% that does not already have a (process) value (and tables 299%% although those will never have any other value). 300localise_type({VariableOid, Type}, _Mib) 301 when is_list(VariableOid) andalso is_record(Type, asn1_type) -> 302 {{keep, VariableOid}, Type}; 303localise_type({{_Process, VariableOid} = VOid, Type}, _Mib) 304 when is_list(VariableOid) andalso is_record(Type, asn1_type) -> 305 {VOid, Type}; 306localise_type({VariableOid, Value}, Mib) when is_list(VariableOid) -> 307 case snmpa_mib:lookup(Mib, VariableOid) of 308 {variable, ME} -> 309 {{keep, VariableOid}, ME#me.asn1_type, Value}; 310 {table_column, ME, _} -> 311 {{keep, VariableOid}, ME#me.asn1_type, Value}; 312 _ -> 313 {{keep, VariableOid}, Value} 314 end; 315localise_type({{_Process, VariableOid} = VOid, Value}, Mib) 316 when is_list(VariableOid) -> 317 case snmpa_mib:lookup(Mib, VariableOid) of 318 {variable, ME} -> 319 {VOid, ME#me.asn1_type, Value}; 320 {table_column, ME, _} -> 321 {VOid, ME#me.asn1_type, Value}; 322 _ -> 323 {VOid, Value} 324 end; 325localise_type(X, _) -> 326 X. 327 328 329%%----------------------------------------------------------------- 330%% Func: make_v1_trap_pdu/5 331%% Args: Enterprise = oid() 332%% Specific = integer() 333%% Varbinds is as returned from initiate_vars 334%% (but only {Oid, Type[, Value} permitted) 335%% SysUpTime = integer() 336%% AgentIp = {A, B, C, D} 337%% Purpose: Make a #trappdu 338%% Checks the Varbinds to see that no symbolic names are 339%% present, and that each var has a type. Performs a get 340%% to find any missing value. 341%% Returns: {#trappdu, [byte()] | error 342%% Fails: yes 343%% NOTE: Executed at the MA 344%%----------------------------------------------------------------- 345make_v1_trap_pdu(Enterprise, Specific, VarbindList, SysUpTime, AgentIp) -> 346 {Enterp,Generic,Spec} = 347 case Enterprise of 348 ?snmp -> 349 {sys_object_id(),Specific,0}; 350 _ -> 351 {Enterprise,?enterpriseSpecific,Specific} 352 end, 353 #trappdu{enterprise = Enterp, 354 agent_addr = AgentIp, 355 generic_trap = Generic, 356 specific_trap = Spec, 357 time_stamp = SysUpTime, 358 varbinds = VarbindList}. 359 360make_discovery_pdu(Vbs) -> 361 #pdu{type = 'inform-request', 362 request_id = snmpa_mpd:generate_req_id(), 363 error_status = noError, 364 error_index = 0, 365 varbinds = Vbs}. 366 367make_v2_notif_pdu(Vbs, Type) -> 368 #pdu{type = Type, 369 request_id = snmpa_mpd:generate_req_id(), 370 error_status = noError, 371 error_index = 0, 372 varbinds = Vbs}. 373 374make_varbind_list(Varbinds) -> 375 OVarbinds = order(Varbinds), 376 {VariablesWithValueAndType, VariablesWithType} = split_variables(OVarbinds), 377 V = get_values(VariablesWithType), 378 Vars = lists:append([V, VariablesWithValueAndType]), 379 [make_varbind(Var) || Var <- unorder(lists:keysort(1, Vars))]. 380 381 382%%----------------------------------------------------------------- 383%% Func: send_trap/6 384%% Args: TrapRec = #trap | #notification 385%% NotifyName = string() 386%% ContextName = string() 387%% Recv = no_receiver | {Ref, Receiver} 388%% Receiver = pid() | atom() | {M,F,A} 389%% Vbs = [varbind()] 390%% NetIf = pid() 391%% Purpose: Default trap sending function. 392%% Sends the trap to the targets pointed out by NotifyName. 393%% If NotifyName is ""; the normal procedure defined in 394%% SNMP-NOTIFICATION-MIB is used, i.e. the trap is sent to 395%% all managers. 396%% Otherwise, the NotifyName is used to find an entry in the 397%% SnmpNotifyTable which define how to send the notification 398%% (as an Inform or a Trap), and to select targets from 399%% SnmpTargetAddrTable (using the Tag). 400%%----------------------------------------------------------------- 401send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf) -> 402 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 403 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 404 send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 405 LocalEngineID, ExtraInfo, NetIf). 406 407send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, ExtraInfo, NetIf) -> 408 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 409 send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 410 LocalEngineID, ExtraInfo, NetIf). 411 412%% The agent normally does not care about the result, 413%% but since it can be usefull when debugging, add 414%% some info when we fail to send the trap(s). 415send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, 416 ExtraInfo, NetIf) -> 417 try 418 begin 419 do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 420 LocalEngineID, ExtraInfo, NetIf) 421 end 422 catch 423 C:E:S -> 424 Info = [{args, [TrapRec, NotifyName, ContextName, 425 Recv, Vbs, LocalEngineID, ExtraInfo, NetIf]}, 426 {class, C}, 427 {err, E}, 428 {stacktrace, S}], 429 ?vlog("snmpa_trap:send_trap exception: " 430 "~n ~p", [Info]), 431 {error, {failed_sending_trap, Info}} 432 end. 433 434do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, 435 LocalEngineID, ExtraInfo, NetIf) -> 436 VarbindList = make_varbind_list(Vbs), 437 Dests = find_dests(NotifyName), 438 send_trap_pdus(Dests, ContextName, {TrapRec, VarbindList}, [], [], [], 439 Recv, LocalEngineID, ExtraInfo, NetIf). 440 441send_discovery(TargetName, Record, ContextName, Vbs, NetIf) -> 442 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 443 send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo). 444send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo) -> 445 case find_dest(TargetName) of 446 {ok, Dest} -> 447 Vbs2 = make_varbind_list(Vbs), % OTP-16207 448 send_discovery_pdu(Dest, Record, ContextName, Vbs2, NetIf, 449 ExtraInfo); 450 Error -> 451 Error 452 end. 453 454 455get_values(VariablesWithType) -> 456 {Order, Varbinds} = extract_order(VariablesWithType, 1), 457 ?vtrace("get_values -> " 458 "~n Order: ~p" 459 "~n Varbinds: ~p", [Order, Varbinds]), 460 case snmpa_agent:do_get(snmpa_acm:get_root_mib_view(), Varbinds, true) of 461 {noError, _, NewVarbinds} -> 462 ?vtrace("get_values -> values retrieved" 463 "~n NewVarbinds: ~p", [NewVarbinds]), 464 %% NewVarbinds is the result of: 465 %% first a reverse, then a sort on the oid field and finally 466 %% a reverse during the get-processing so we need to re-sort 467 %% on the org_index field again before contract-order 468 NewVarbinds1 = lists:keysort(#varbind.org_index, NewVarbinds), 469 contract_order(Order, NewVarbinds1); 470 {ErrorStatus, ErrorIndex, _} -> 471 user_err("snmpa_trap: get operation failed: ~w" 472 "~n at ~w" 473 "~n in ~w", 474 [ErrorStatus, ErrorIndex, Varbinds]), 475 throw(error) 476 end. 477 478make_varbind({Process, #varbind{oid = Oid} = VB}) -> 479 VB#varbind{oid = process_oid(Process, Oid)}; 480make_varbind({Process, {VarOid, ASN1Type, Value}}) -> 481 case snmpa_agent:make_value_a_correct_value(Value, ASN1Type, undef) of 482 {value, Type, Val} -> 483 #varbind{oid = process_oid(Process, VarOid), 484 variabletype = Type, 485 value = Val}; 486 {error, Reason} -> 487 user_err("snmpa_trap: Invalid value: ~w" 488 "~n Oid: ~w" 489 "~n Val: ~w" 490 "~n Type: ~w", 491 [Reason, VarOid, Value, ASN1Type]), 492 throw(error) 493 end. 494 495process_oid(truncate, Oid) -> 496 case lists:reverse(Oid) of 497 [0 | RevRestOid] -> 498 lists:reverse(RevRestOid); 499 _ -> 500 Oid 501 end; 502process_oid(_, Oid) -> 503 Oid. 504 505 506 507%% Order does two things: 508%% 1) Add an index to each element indicating where in the 509%% list it was found. 510%% 2) Extract the 'process oid' information (and add it to the index => ID) 511%% We can add whatever we want to the second element since the first, 512%% the integer (No) is unique. 513 514order(Varbinds) -> 515 order(Varbinds, 1). 516 517order([{{Process, OidOrAlias}, Type, Value} | T], No) -> 518 VB = {OidOrAlias, Type, Value}, 519 ID = {No, Process}, 520 [{ID, VB} | order(T, No + 1)]; 521order([{{Process, OidOrAlias}, Type} | T], No) -> 522 VB = {OidOrAlias, Type}, 523 ID = {No, Process}, 524 [{ID, VB} | order(T, No + 1)]; 525order([H | T], No) -> 526 ID = {No, keep}, 527 [{ID, H} | order(T, No + 1)]; 528order([], _) -> 529 []. 530 531 532unorder(OVbs) -> 533 [{Process, VB} || {{_No, Process}, VB} <- OVbs]. 534 535 536extract_order([{No, {VarOid, _Type}} | T], Index) -> 537 {Order, V} = extract_order(T, Index+1), 538 {[No | Order], [#varbind{oid = VarOid, org_index = Index} | V]}; 539extract_order([], _) -> 540 {[], []}. 541 542contract_order([No | Order], [Varbind | T]) -> 543 [{No, Varbind} | contract_order(Order, T)]; 544contract_order([], []) -> 545 []. 546 547 548split_variables([{No, {VarOid, Type, Val}} | T]) when is_list(VarOid) -> 549 {A, B} = split_variables(T), 550 {[{No, {VarOid, Type, Val}} | A], B}; 551split_variables([{No, {VarOid, Type}} | T]) 552 when is_list(VarOid) andalso is_record(Type, asn1_type) -> 553 {A, B} = split_variables(T), 554 {A, [{No, {VarOid, Type}} | B]}; 555 556split_variables([{_No, {VarName, Value}} | _T]) -> 557 user_err("snmpa_trap: Undefined variable ~w (~w)", [VarName, Value]), 558 throw(error); 559split_variables([{_No, {VarName, RowIndex, Value}} | _T]) -> 560 user_err("snmpa_trap: Undefined variable ~w ~w (~w)", 561 [VarName, RowIndex, Value]), 562 throw(error); 563 564split_variables([]) -> 565 {[], []}. 566 567 568%%----------------------------------------------------------------- 569%% Func: find_dests(NotifyName) -> 570%% [{DestAddr, TargetName, TargetParams, NotifyType}] 571%% Types: NotifyType = string() 572%% DestAddr = {TDomain, TAddr} 573%% TargetName = string() 574%% TargetParams = {MpModel, SecModel, SecName, SecLevel} 575%% NotifyType = trap | {inform, Timeout, Retry} 576%% Returns: A list of all Destination addresses for this community. 577%% NOTE: This function is executed in the master agent's context 578%%----------------------------------------------------------------- 579find_dests("") -> 580 ?vtrace("find destinations", []), 581 snmp_notification_mib:get_targets(); 582find_dests(NotifyName) -> 583 ?vtrace("find destinations for ~p", [NotifyName]), 584 case snmp_notification_mib:get_targets(NotifyName) of 585 [] -> 586 ?vlog("No dests found for NotifyName: ~p", [NotifyName]), 587 []; 588 Dests -> 589 Dests 590 end. 591 592find_dest(TargetName) -> 593 AddrCols = [?snmpTargetAddrTDomain, 594 ?snmpTargetAddrTAddress, 595 ?snmpTargetAddrTimeout, 596 ?snmpTargetAddrRetryCount, 597 ?snmpTargetAddrParams, 598 ?snmpTargetAddrRowStatus], 599 case snmp_target_mib:snmpTargetAddrTable(get, TargetName, AddrCols) of 600 [{value, TDomain}, 601 {value, TAddress}, 602 {value, Timeout}, 603 {value, RetryCount}, 604 {value, Params}, 605 {value, ?'RowStatus_active'}] -> 606 ?vtrace("find_dest -> found snmpTargetAddrTable info:" 607 "~n TDomain: ~p" 608 "~n TAddress: ~p" 609 "~n Timeout: ~p" 610 "~n RetryCount: ~p" 611 "~n Params: ~p", 612 [TDomain, TAddress, Timeout, RetryCount, Params]), 613 ParmCols = [?snmpTargetParamsMPModel, 614 ?snmpTargetParamsSecurityModel, 615 ?snmpTargetParamsSecurityName, 616 ?snmpTargetParamsSecurityLevel, 617 ?snmpTargetParamsRowStatus], 618 case snmp_target_mib:snmpTargetParamsTable(get, Params, ParmCols) of 619 [{value, ?MP_V3}, 620 {value, SecModel}, 621 {value, SecName}, 622 {value, SecLevel}, 623 {value, ?'RowStatus_active'}] -> 624 ?vtrace("find_dest -> found snmpTargetParamsTable info:" 625 "~n SecModel: ~p" 626 "~n SecName: ~p" 627 "~n SecLevel: ~p", 628 [SecModel, SecName, SecLevel]), 629 DestAddr = {TDomain, TAddress}, 630 TargetParams = {SecModel, SecName, SecLevel}, 631 Val = {DestAddr, TargetName, TargetParams, Timeout, RetryCount}, 632 {ok, Val}; 633 [{value, ?MP_V3}, 634 {value, _SecModel}, 635 {value, _SecName}, 636 {value, _SecLevel}, 637 {value, RowStatus}] -> 638 {error, {invalid_RowStatus, RowStatus, snmpTargetParamsTable}}; 639 [{value, MpModel}, 640 {value, _SecModel}, 641 {value, _SecName}, 642 {value, _SecLevel}, 643 {value, ?'RowStatus_active'}] -> 644 {error, {invalid_MpModel, MpModel, snmpTargetParamsTable}}; 645 [{value, _MpModel}, 646 {value, _SecModel}, 647 {value, _SecName}, 648 {value, _SecLevel}, 649 {value, RowStatus}] -> 650 {error, {invalid_RowStatus, RowStatus, snmpTargetParamsTable}}; 651 Bad -> 652 ?vlog("find_dest -> " 653 "could not find snmpTargetParamsTable info: " 654 "~n Bad: ~p", [Bad]), 655 {error, {not_found, snmpTargetParamsTable}} 656 end; 657 658 [{value, _TDomain}, 659 {value, _TAddress}, 660 {value, _Timeout}, 661 {value, _RetryCount}, 662 {value, _Params}, 663 {value, RowStatus}] -> 664 {error, {invalid_RowStatus, RowStatus, snmpTargetAddrTable}}; 665 _ -> 666 {error, {not_found, snmpTargetAddrTable}} 667 end. 668 669send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, 670 Timeout, Retry}, 671 Record, ContextName, Vbs, NetIf, 672 ExtraInfo) -> 673 ?vdebug("send_discovery_pdu -> entry with " 674 "~n Destination address: ~p" 675 "~n Target name: ~p" 676 "~n Sec model: ~p" 677 "~n Sec name: ~p" 678 "~n Sec level: ~p" 679 "~n Timeout: ~p" 680 "~n Retry: ~p" 681 "~n Record: ~p" 682 "~n ContextName: ~p" 683 "~n ExtraInfo: ~p", 684 [Dest, TargetName, SecModel, SecName, SecLevel, 685 Timeout, Retry, Record, ContextName, ExtraInfo]), 686 case get_mib_view(SecModel, SecName, SecLevel, ContextName) of 687 {ok, MibView} -> 688 case check_all_varbinds(Record, Vbs, MibView) of 689 true -> 690 SysUpTime = snmp_standard_mib:sys_up_time(), 691 send_discovery_pdu(Record, Dest, Vbs, 692 SecModel, SecName, SecLevel, 693 TargetName, ContextName, 694 Timeout, Retry, 695 SysUpTime, NetIf, ExtraInfo); 696 false -> 697 {error, {mibview_validation_failed, Vbs, MibView}} 698 end; 699 {discarded, Reason} -> 700 {error, {failed_get_mibview, Reason}} 701 end. 702 703send_discovery_pdu(Record, Dest, Vbs, 704 SecModel, SecName, SecLevel, TargetName, 705 ContextName, Timeout, Retry, SysUpTime, NetIf, ExtraInfo) -> 706 {_Oid, IVbs} = mk_v2_trap(Record, Vbs, SysUpTime), % v2 refers to SMIv2; 707 Sender = proc_lib:spawn_link(?MODULE, init_discovery_inform, 708 [self(), 709 Dest, 710 SecModel, SecName, SecLevel, TargetName, 711 ContextName, 712 Timeout, Retry, 713 IVbs, NetIf, 714 get(verbosity), 715 ExtraInfo]), 716 {ok, Sender, SecLevel}. 717 718init_discovery_inform(Parent, 719 Dest, 720 SecModel, SecName, SecLevel, TargetName, 721 ContextName, Timeout, Retry, Vbs, NetIf, Verbosity) -> 722 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 723 init_discovery_inform(Parent, 724 Dest, 725 SecModel, SecName, SecLevel, TargetName, 726 ContextName, Timeout, Retry, Vbs, NetIf, 727 Verbosity, ExtraInfo). 728init_discovery_inform(Parent, 729 Dest, 730 SecModel, SecName, SecLevel, TargetName, 731 ContextName, Timeout, Retry, Vbs, NetIf, 732 Verbosity, ExtraInfo) -> 733 put(verbosity, Verbosity), 734 put(sname, madis), 735 Pdu = make_discovery_pdu(Vbs), 736 ContextEngineId = snmp_framework_mib:get_engine_id(), 737 SecLevelFlag = mk_flag(SecLevel), 738 SecData = {SecModel, SecName, SecLevelFlag, TargetName}, 739 MsgData = {SecData, ContextEngineId, ContextName}, 740 Msg = {send_discovery, Pdu, MsgData, Dest, self(), ExtraInfo}, 741 ?MODULE:send_discovery_inform(Parent, Timeout*10, Retry, Msg, NetIf). 742 743%% note_timeout(Timeout, Retry) 744%% when ((is_integer(Timeout) andalso (Timeout > 0)) andalso 745%% (is_integer(Retry) andalso (Retry > 0))) 746%% note_timeout(Timeout*10, Retry, 0); 747%% note_timeout(Timeout, Retry) 748%% when (is_integer(Timeout) andalso (Timeout > 0)) -> 749%% Timeout*10. 750 751%% note_timeout(_Timeout, -1, NoteTimeout) -> 752%% NoteTimeout; 753%% note_timeout(Timeout, Retry, NoteTimeout) when -> 754%% note_timeout(Timeout*2, Retry-1, NoteTimeout+Timeout). 755 756send_discovery_inform(Parent, _Timeout, -1, _Msg, _NetIf) -> 757 Parent ! {discovery_response, {error, timeout}}; 758send_discovery_inform(Parent, Timeout, Retry, Msg, NetIf) -> 759 NetIf ! Msg, 760 receive 761 {snmp_discovery_response_received, Pdu, undefined} -> 762 ?vtrace("received stage 2 discovery response: " 763 "~n Pdu: ~p", [Pdu]), 764 Parent ! {discovery_response, {ok, Pdu}}; 765 {snmp_discovery_response_received, Pdu, ManagerEngineId} -> 766 ?vtrace("received stage 1 discovery response: " 767 "~n Pdu: ~p" 768 "~n ManagerEngineId: ~p", [Pdu, ManagerEngineId]), 769 Parent ! {discovery_response, {ok, Pdu, ManagerEngineId}} 770 after 771 Timeout -> 772 ?MODULE:send_discovery_inform(Parent, 773 Timeout*2, Retry-1, Msg, NetIf) 774 end. 775 776 777%%----------------------------------------------------------------- 778%% NOTE: This function is executed in the master agent's context 779%% For each target, check if it has access to the objects in the 780%% notification, determine which message version (v1, v2c or v3) 781%% should be used for the target, and determine the message 782%% specific parameters to be used. 783%%----------------------------------------------------------------- 784send_trap_pdus([{DestAddr, TargetName, 785 {MpModel, SecModel, SecName, SecLevel}, Type} | T], 786 ContextName, 787 {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, 788 LocalEngineID, ExtraInfo, NetIf) -> 789 ?vdebug("send trap pdus: " 790 "~n Destination address: ~p" 791 "~n Target name: ~p" 792 "~n MP model: ~p" 793 "~n Type: ~p" 794 "~n V1Res: ~p" 795 "~n V2Res: ~p" 796 "~n V3Res: ~p", 797 [DestAddr, TargetName, MpModel, Type, V1Res, V2Res, V3Res]), 798 case get_mib_view(SecModel, SecName, SecLevel, ContextName) of 799 {ok, MibView} -> 800 case check_all_varbinds(TrapRec, Vbs, MibView) of 801 true when MpModel =:= ?MP_V1 -> 802 ?vtrace("send_trap_pdus -> v1 mp model",[]), 803 ContextEngineId = LocalEngineID, 804 case snmp_community_mib:vacm2community({SecName, 805 ContextEngineId, 806 ContextName}, 807 DestAddr) of 808 {ok, Community} -> 809 ?vdebug("community found for v1 dest: ~p", 810 [element(2, DestAddr)]), 811 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 812 [{DestAddr, Community} | V1Res], 813 V2Res, V3Res, Recv, 814 LocalEngineID, ExtraInfo, NetIf); 815 undefined -> 816 ?vdebug("No community found for v1 dest: ~p", 817 [element(2, DestAddr)]), 818 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 819 V1Res, V2Res, V3Res, Recv, 820 LocalEngineID, ExtraInfo, NetIf) 821 end; 822 true when MpModel =:= ?MP_V2C -> 823 ?vtrace("send_trap_pdus -> v2c mp model",[]), 824 ContextEngineId = LocalEngineID, 825 case snmp_community_mib:vacm2community({SecName, 826 ContextEngineId, 827 ContextName}, 828 DestAddr) of 829 {ok, Community} -> 830 ?vdebug("community found for v2c dest: ~p", 831 [element(2, DestAddr)]), 832 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 833 V1Res, 834 [{DestAddr, Community, Type}|V2Res], 835 V3Res, Recv, 836 LocalEngineID, ExtraInfo, NetIf); 837 undefined -> 838 ?vdebug("No community found for v2c dest: ~p", 839 [element(2, DestAddr)]), 840 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 841 V1Res, V2Res, V3Res, Recv, 842 LocalEngineID, ExtraInfo, NetIf) 843 end; 844 true when MpModel =:= ?MP_V3 -> 845 ?vtrace("send_trap_pdus -> v3 mp model",[]), 846 SecLevelF = mk_flag(SecLevel), 847 MsgData = {SecModel, SecName, SecLevelF, TargetName}, 848 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 849 V1Res, V2Res, 850 [{DestAddr, MsgData, Type} | V3Res], 851 Recv, LocalEngineID, ExtraInfo, NetIf); 852 true -> 853 ?vlog("bad MpModel ~p for dest ~p", 854 [MpModel, element(2, DestAddr)]), 855 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 856 V1Res, V2Res, V3Res, Recv, 857 LocalEngineID, ExtraInfo, NetIf); 858 _ -> 859 ?vlog("no access for dest: " 860 "~n ~p in target ~p", 861 [element(2, DestAddr), TargetName]), 862 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 863 V1Res, V2Res, V3Res, Recv, 864 LocalEngineID, ExtraInfo, NetIf) 865 end; 866 {discarded, Reason} -> 867 ?vlog("mib view error ~p for" 868 "~n dest: ~p" 869 "~n SecName: ~w", 870 [Reason, element(2, DestAddr), SecName]), 871 send_trap_pdus(T, ContextName, {TrapRec, Vbs}, 872 V1Res, V2Res, V3Res, Recv, 873 LocalEngineID, ExtraInfo, NetIf) 874 end; 875send_trap_pdus([], ContextName, {TrapRec, Vbs}, 876 V1Res, V2Res, V3Res, Recv, 877 LocalEngineID, ExtraInfo, 878 NetIf) -> 879 SysUpTime = snmp_standard_mib:sys_up_time(), 880 ?vdebug("send trap pdus with sysUpTime ~p", [SysUpTime]), 881 InformRecvs = get_inform_recvs(V2Res ++ V3Res), 882 InformTargets = [Addr || {Addr, _, _, _} <- InformRecvs], 883 deliver_recv(Recv, snmp_targets, InformTargets), 884 send_v1_trap(TrapRec, V1Res, Vbs, ExtraInfo, NetIf, SysUpTime), 885 send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime), 886 send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, 887 SysUpTime, ContextName). 888 889send_v1_trap(_TrapRec, [], _Vbs, _ExtraInfo, _NetIf, _SysUpTime) -> 890 ok; 891send_v1_trap( 892 #trap{enterpriseoid = Enter, specificcode = Spec}, 893 V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> 894 ?vdebug("prepare to send v1 trap " 895 "~n '~p'" 896 "~n with" 897 "~n ~p" 898 "~n to" 899 "~n ~p", [Enter, Spec, V1Res]), 900 do_send_v1_trap(Enter, Spec, V1Res, Vbs, ExtraInfo, NetIf, SysUpTime); 901send_v1_trap( 902 #notification{oid = Oid}, 903 V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> 904 %% Use alg. in rfc2089 to map a v2 trap to a v1 trap 905 % delete Counter64 objects from vbs 906 ?vdebug("prepare to send v1 trap '~p'",[Oid]), 907 NVbs = [Vb || Vb <- Vbs, Vb#varbind.variabletype =/= 'Counter64'], 908 {Enter,Spec} = 909 case Oid of 910 [1,3,6,1,6,3,1,1,5,Specific] -> 911 {?snmp,Specific - 1}; 912 _ -> 913 case lists:reverse(Oid) of 914 [Last, 0 | First] -> 915 {lists:reverse(First),Last}; 916 [Last | First] -> 917 {lists:reverse(First),Last} 918 end 919 end, 920 do_send_v1_trap(Enter, Spec, V1Res, NVbs, ExtraInfo, NetIf, SysUpTime). 921 922do_send_v1_trap(Enter, Spec, V1Res, NVbs, ExtraInfo, NetIf, SysUpTime) -> 923 {value, Transports} = snmp_framework_mib:intAgentTransports(get), 924 {_Domain, {AgentIp, _AgentPort}} = 925 case lists:keyfind(snmpUDPDomain, 1, Transports) of 926 false -> 927 case lists:keyfind(transportDomainUdpIpv4, 1, Transports) of 928 false -> 929 ?vtrace( 930 "snmpa_trap: cannot send v1 trap " 931 "without IPv4 domain: ~p", 932 [Transports]), 933 user_err( 934 "snmpa_trap: cannot send v1 trap " 935 "without IPv4 domain: ~p", 936 [Transports]); 937 DomainAddr -> 938 DomainAddr 939 end; 940 DomainAddr -> 941 DomainAddr 942 end, 943 TrapPdu = make_v1_trap_pdu(Enter, Spec, NVbs, SysUpTime, AgentIp), 944 AddrCommunities = mk_addr_communities(V1Res), 945 lists:foreach( 946 fun ({Community, Addrs}) -> 947 ?vtrace("send v1 trap to ~p",[Addrs]), 948 NetIf ! {send_pdu, 'version-1', TrapPdu, 949 {community, Community}, Addrs, ExtraInfo} 950 end, AddrCommunities). 951 952send_v2_trap(_TrapRec, [], _Vbs, _Recv, _ExtraInfo, _NetIf, _SysUpTime) -> 953 ok; 954send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime) -> 955 ?vdebug("prepare to send v2 trap",[]), 956 {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), 957 TrapRecvs = get_trap_recvs(V2Res), 958 InformRecvs = get_inform_recvs(V2Res), 959 do_send_v2_trap(TrapRecvs, IVbs, ExtraInfo, NetIf), 960 do_send_v2_inform(InformRecvs, IVbs, Recv, ExtraInfo, NetIf). 961 962send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID, _ExtraInfo, 963 _NetIf, _SysUpTime, _ContextName) -> 964 ok; 965send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, 966 NetIf, SysUpTime, ContextName) -> 967 ?vdebug("prepare to send v3 trap",[]), 968 {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), % v2 refers to SMIv2; 969 TrapRecvs = get_trap_recvs(V3Res), % same SMI for v3 970 InformRecvs = get_inform_recvs(V3Res), 971 do_send_v3_trap(TrapRecvs, ContextName, IVbs, ExtraInfo, NetIf), 972 do_send_v3_inform(InformRecvs, ContextName, IVbs, Recv, 973 LocalEngineID, ExtraInfo, NetIf). 974 975 976mk_v2_trap(#notification{oid = Oid}, Vbs, SysUpTime) -> 977 ?vtrace("make v2 notification '~p'",[Oid]), 978 mk_v2_notif(Oid, Vbs, SysUpTime); 979mk_v2_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, 980 Vbs, SysUpTime) -> 981 %% Use alg. in rfc1908 to map a v1 trap to a v2 trap 982 ?vtrace("make v2 trap for '~p' with ~p",[Enter,Spec]), 983 {Oid,Enterp} = 984 case Enter of 985 ?snmp -> 986 {?snmpTraps ++ [Spec + 1],sys_object_id()}; 987 _ -> 988 {Enter ++ [0, Spec],Enter} 989 end, 990 ExtraVb = #varbind{oid = ?snmpTrapEnterprise_instance, 991 variabletype = 'OBJECT IDENTIFIER', 992 value = Enterp}, 993 mk_v2_notif(Oid, Vbs ++ [ExtraVb], SysUpTime). 994 995mk_v2_notif(Oid, Vbs, SysUpTime) -> 996 IVbs = [#varbind{oid = ?sysUpTime_instance, 997 variabletype = 'TimeTicks', 998 value = SysUpTime}, 999 #varbind{oid = ?snmpTrapOID_instance, 1000 variabletype = 'OBJECT IDENTIFIER', 1001 value = Oid} | Vbs], 1002 {Oid, IVbs}. 1003 1004get_trap_recvs(TrapRecvs) -> 1005 [{Addr, MsgData} || {Addr, MsgData, trap} <- TrapRecvs]. 1006 1007get_inform_recvs(InformRecvs) -> 1008 [{Addr, MsgData, Timeout, Retry} || 1009 {Addr, MsgData, {inform, Timeout, Retry}} <- InformRecvs]. 1010 1011do_send_v2_trap([], _Vbs, _ExtraInfo, _NetIf) -> 1012 ok; 1013do_send_v2_trap(Recvs, Vbs, ExtraInfo, NetIf) -> 1014 TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), 1015 AddrCommunities = mk_addr_communities(Recvs), 1016 lists:foreach(fun({Community, Addrs}) -> 1017 ?vtrace("send v2 trap to ~p",[Addrs]), 1018 NetIf ! {send_pdu, 'version-2', TrapPdu, 1019 {community, Community}, Addrs, ExtraInfo} 1020 end, AddrCommunities), 1021 ok. 1022 1023do_send_v2_inform([], _Vbs, _Recv, _ExtraInfo, _NetIf) -> 1024 ok; 1025do_send_v2_inform(Recvs, Vbs, Recv, ExtraInfo, NetIf) -> 1026 lists:foreach( 1027 fun({Addr, Community, Timeout, Retry}) -> 1028 ?vtrace("~n start inform sender to send v2 inform to ~p", 1029 [Addr]), 1030 proc_lib:spawn_link(?MODULE, init_v2_inform, 1031 [Addr, Timeout, Retry, Vbs, 1032 Recv, ExtraInfo, NetIf, Community, 1033 get(verbosity), get(sname)]) 1034 end, 1035 Recvs). 1036 1037do_send_v3_trap([], _ContextName, _Vbs, _ExtraInfo, _NetIf) -> 1038 ok; 1039do_send_v3_trap(Recvs, ContextName, Vbs, ExtraInfo, NetIf) -> 1040 TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), % Yes, v2 1041 ContextEngineId = snmp_framework_mib:get_engine_id(), 1042 lists:foreach(fun(Recv) -> 1043 ?vtrace("~n send v3 notif to ~p",[Recv]), 1044 NetIf ! {send_pdu, 'version-3', TrapPdu, 1045 {v3, ContextEngineId, ContextName}, 1046 [Recv], ExtraInfo} 1047 end, Recvs), 1048 ok. 1049 1050do_send_v3_inform([], _ContextName, _Vbs, _Recv, 1051 _LocalEngineID, _ExtraInfo, _NetIf) -> 1052 ok; 1053do_send_v3_inform(Recvs, ContextName, Vbs, Recv, 1054 LocalEngineID, ExtraInfo, NetIf) -> 1055 lists:foreach( 1056 fun({Addr, MsgData, Timeout, Retry}) -> 1057 ?vtrace("~n start inform sender to send v3 inform to ~p", 1058 [Addr]), 1059 proc_lib:spawn_link(?MODULE, init_v3_inform, 1060 [{Addr, MsgData}, Timeout, Retry, Vbs, 1061 Recv, LocalEngineID, ExtraInfo, 1062 NetIf, ContextName, 1063 get(verbosity), get(sname)]) 1064 end, 1065 Recvs). 1066 1067%% New process 1068init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community, V, S) -> 1069 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1070 init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, 1071 Community, V, S). 1072 1073init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, 1074 Community, V, S) -> 1075 %% Make a new Inform for each recipient; they need unique 1076 %% request-ids! 1077 put(verbosity,V), 1078 put(sname,inform_sender_short_name(S)), 1079 ?vdebug("~n starting with timeout = ~p and retry = ~p", 1080 [Timeout,Retry]), 1081 InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), 1082 Msg = {send_pdu_req, 'version-2', InformPdu, {community, Community}, 1083 [Addr], self(), ExtraInfo}, 1084 ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). 1085 1086 1087%% New process 1088init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName, V, S) -> 1089 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1090 LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, 1091 init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, 1092 LocalEngineID, ExtraInfo, 1093 NetIf, ContextName, V, S). 1094 1095init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, NetIf, 1096 ContextName, V, S) -> 1097 ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, 1098 init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, 1099 LocalEngineID, ExtraInfo, 1100 NetIf, ContextName, V, S). 1101 1102init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, ExtraInfo, 1103 NetIf, ContextName, V, S) -> 1104 %% Make a new Inform for each recipient; they need unique 1105 %% request-ids! 1106 put(verbosity,V), 1107 put(sname,inform_sender_short_name(S)), 1108 ?vdebug("~n starting with timeout = ~p and retry = ~p", 1109 [Timeout,Retry]), 1110 InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), % Yes, v2 1111 ContextEngineId = LocalEngineID, 1112 Msg = {send_pdu_req, 'version-3', InformPdu, 1113 {v3, ContextEngineId, ContextName}, [Addr], self(), ExtraInfo}, 1114 ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). 1115 1116send_inform(Addr, _Timeout, -1, _Msg, Recv, _NetIf) -> 1117 ?vinfo("~n Delivery of send-pdu-request to net-if failed: reply timeout", 1118 []), 1119 deliver_recv(Recv, snmp_notification, {no_response, Addr}); 1120send_inform(Addr, Timeout, Retry, Msg, Recv, NetIf) -> 1121 ?vtrace("deliver send-pdu-request to net-if when" 1122 "~n Timeout: ~p" 1123 "~n Retry: ~p",[Timeout, Retry]), 1124 NetIf ! Msg, 1125 receive 1126 {snmp_response_received, _Vsn, _Pdu, _From} -> 1127 ?vtrace("received response for ~p (when Retry = ~p)", 1128 [Recv, Retry]), 1129 deliver_recv(Recv, snmp_notification, {got_response, Addr}) 1130 after 1131 Timeout -> 1132 ?MODULE:send_inform(Addr, Timeout*2, Retry-1, Msg, Recv, NetIf) 1133 end. 1134 1135% A nasty bit of verbosity setup... 1136inform_sender_short_name(ma) -> mais; 1137inform_sender_short_name(maw) -> mais; 1138inform_sender_short_name(mats) -> mais; 1139inform_sender_short_name(_) -> sais. 1140 1141deliver_recv(no_receiver, _MsgId, _Result) -> 1142 ?vtrace("deliver_recv -> no receiver", []), 1143 ok; 1144deliver_recv(#snmpa_notification_delivery_info{tag = Tag, 1145 mod = Mod, 1146 extra = Extra}, 1147 snmp_targets, TAddrs) when is_list(TAddrs) -> 1148 ?vtrace("deliver_recv(snmp_targets) -> entry with" 1149 "~n Tag: ~p" 1150 "~n Mod: ~p" 1151 "~n Extra: ~p" 1152 "~n TAddrs: ~p" 1153 "", [Tag, Mod, Extra, TAddrs]), 1154 Addrs = transform_taddrs(TAddrs), 1155 (catch Mod:delivery_targets(Tag, Addrs, Extra)); 1156deliver_recv(#snmpa_notification_delivery_info{tag = Tag, 1157 mod = Mod, 1158 extra = Extra}, 1159 snmp_notification, {DeliveryResult, TAddr}) -> 1160 ?vtrace("deliver_recv -> entry with" 1161 "~n Tag: ~p" 1162 "~n Mod: ~p" 1163 "~n Extra: ~p" 1164 "~n DeliveryResult: ~p" 1165 "~n TAddr: ~p" 1166 "", [Tag, Mod, Extra, DeliveryResult, TAddr]), 1167 [Addr] = transform_taddrs([TAddr]), 1168 (catch Mod:delivery_info(Tag, Addr, DeliveryResult, Extra)); 1169deliver_recv({Tag, Receiver}, MsgId, Result) -> 1170 ?vtrace("deliver_recv -> entry with" 1171 "~n Tag: ~p" 1172 "~n Receiver: ~p" 1173 "~n MsgId: ~p" 1174 "~n Result: ~p" 1175 "", [Tag, Receiver, MsgId, Result]), 1176 Msg = {MsgId, Tag, Result}, 1177 case Receiver of 1178 Pid when is_pid(Pid) -> 1179 Pid ! Msg; 1180 Name when is_atom(Name) -> 1181 catch Name ! Msg; 1182 {M, F, A} -> 1183 catch M:F([Msg | A]); 1184 Else -> 1185 ?vinfo("~n Cannot deliver acknowledgment: bad receiver = '~p'", 1186 [Else]), 1187 user_err("snmpa: bad receiver, ~w\n", [Else]) 1188 end; 1189deliver_recv(Else, _MsgId, _Result) -> 1190 ?vinfo("~n Cannot deliver acknowledgment: bad receiver = '~p'", 1191 [Else]), 1192 user_err("snmpa: bad receiver, ~w\n", [Else]). 1193 1194transform_taddrs(TAddrs) -> 1195 UseTDomain = 1196 case snmp_framework_mib:intAgentTransportDomain(get) of 1197 {value,snmpUDPDomain} -> 1198 false; 1199 {value,_} -> 1200 true; 1201 genErr -> 1202 false 1203 end, 1204 DomAddrs = [transform_taddr(TAddr) || TAddr <- TAddrs], 1205 case UseTDomain of 1206 true -> 1207 DomAddrs; 1208 false -> 1209 [Addr || {_Domain, Addr} <- DomAddrs] 1210 end. 1211 1212%% v2 1213transform_taddr({?snmpUDPDomain, Addr}) -> 1214 transform_taddr(transportDomainUdpIpv4, Addr); 1215transform_taddr({?transportDomainUdpIpv4, Addr}) -> 1216 transform_taddr(transportDomainUdpIpv4, Addr); 1217transform_taddr({?transportDomainUdpIpv6, Addr}) -> 1218 transform_taddr(transportDomainUdpIpv6, Addr); 1219%% v3 1220transform_taddr({{?snmpUDPDomain, Addr}, _MsgData}) -> 1221 transform_taddr(transportDomainUdpIpv4, Addr); 1222transform_taddr({{?transportDomainUdpIpv4, Addr}, _MsgData}) -> 1223 transform_taddr(transportDomainUdpIpv4, Addr); 1224transform_taddr({{?transportDomainUdpIpv6, Addr}, _MsgData}) -> 1225 transform_taddr(transportDomainUdpIpv6, Addr). 1226 1227transform_taddr( 1228 transportDomainUdpIpv4 = Domain, 1229 [A1,A2,A3,A4,P1,P2]) -> 1230 Ip = {A1, A2, A3, A4}, 1231 Port = P1 bsl 8 + P2, 1232 {Domain, {Ip, Port}}; 1233transform_taddr( 1234 transportDomainUdpIpv6 = Domain, 1235 [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]) -> 1236 Ip = {A1, A2, A3, A4, A5, A6, A7, A8}, 1237 Port = P1 bsl 8 + P2, 1238 {Domain, {Ip, Port}}; 1239transform_taddr( 1240 transportDomainUdpIpv6 = Domain, 1241 [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, 1242 P1, P2]) -> 1243 Ip = 1244 {(A1 bsl 8) bor A2, (A3 bsl 8) bor A4, 1245 (A5 bsl 8) bor A6, (A7 bsl 8) bor A8, 1246 (A9 bsl 8) bor A10, (A11 bsl 8) bor A12, 1247 (A13 bsl 8) bor A14, (A15 bsl 8) bor A16}, 1248 Port = P1 bsl 8 + P2, 1249 {Domain, {Ip, Port}}. 1250 1251%% transform_taddr({?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}) -> % v2 1252%% Addr = {A1, A2, A3, A4}, 1253%% Port = P1 bsl 8 + P2, 1254%% {Addr, Port}; 1255%% transform_taddr({?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}) -> % v2 1256%% Addr = {A1, A2, A3, A4}, 1257%% Port = P1 bsl 8 + P2, 1258%% {Addr, Port}; 1259%% transform_taddr({?transportDomainUdpIpv6, 1260%% [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}) -> % v2 1261%% Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, 1262%% Port = P1 bsl 8 + P2, 1263%% {Addr, Port}; 1264%% transform_taddr({{?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 1265%% Addr = {A1, A2, A3, A4}, 1266%% Port = P1 bsl 8 + P2, 1267%% {Addr, Port}; 1268%% transform_taddr({{?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 1269%% Addr = {A1, A2, A3, A4}, 1270%% Port = P1 bsl 8 + P2, 1271%% {Addr, Port}; 1272%% transform_taddr({{?transportDomainUdpIpv6, 1273%% [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}, _MsgData}) -> % v3 1274%% Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, 1275%% Port = P1 bsl 8 + P2, 1276%% {Addr, Port}. 1277 1278 1279 1280check_all_varbinds(#notification{oid = Oid}, Vbs, MibView) -> 1281 case snmpa_acm:validate_mib_view(Oid, MibView) of 1282 true -> check_all_varbinds(Vbs, MibView); 1283 false -> false 1284 end; 1285check_all_varbinds(#trap{enterpriseoid = Enter, specificcode = Spec}, 1286 Vbs, MibView) -> 1287 %% Use alg. in rfc1908 to map a v1 trap to a v2 trap 1288 Oid = case Enter of 1289 ?snmp -> ?snmpTraps ++ [Spec + 1]; 1290 _ -> Enter ++ [0, Spec] 1291 end, 1292 case snmpa_acm:validate_mib_view(Oid, MibView) of 1293 true -> check_all_varbinds(Vbs, MibView); 1294 false -> false 1295 end. 1296 1297check_all_varbinds([#varbind{oid = Oid} | Vbs], MibView) -> 1298 case snmpa_acm:validate_mib_view(Oid, MibView) of 1299 true -> check_all_varbinds(Vbs, MibView); 1300 false -> false 1301 end; 1302check_all_varbinds([], _MibView) -> 1303 true. 1304 1305 1306%%-------------------------------------------------- 1307%% Functions to access the local mib. 1308%%-------------------------------------------------- 1309sys_object_id() -> 1310 case snmpa_agent:do_get(snmpa_acm:get_root_mib_view(), 1311 [#varbind{oid = ?sysObjectID_instance}], 1312 true) of 1313 {noError, _, [#varbind{value = Value}]} -> 1314 Value; 1315 X -> 1316 user_err("sysObjectID bad return value ~w", [X]) 1317 end. 1318 1319%% Collect all ADDRs for each community together. 1320%% In: [{Addr, Community}] 1321%% Out: [{Community, [Addr]}] 1322mk_addr_communities(Recvs) -> 1323 [{Addr, Comm} | T] = lists:keysort(2, Recvs), 1324 mic(T, Comm, [Addr], []). 1325 1326mic([{Addr, Comm} | T], CurComm, AddrList, Res) when Comm =:= CurComm -> 1327 mic(T, CurComm, [Addr | AddrList], Res); 1328mic([{Addr, Comm} | T], CurComm, AddrList, Res) -> 1329 mic(T, Comm, [Addr], [{CurComm, AddrList} | Res]); 1330mic([], CurComm, AddrList, Res) -> 1331 [{CurComm, AddrList} | Res]. 1332 1333 1334%%----------------------------------------------------------------- 1335%% Convert the SecurityLevel into a flag value used by snmpa_mpd 1336%%----------------------------------------------------------------- 1337mk_flag(?'SnmpSecurityLevel_noAuthNoPriv') -> 0; 1338mk_flag(?'SnmpSecurityLevel_authNoPriv') -> 1; 1339mk_flag(?'SnmpSecurityLevel_authPriv') -> 3. 1340 1341 1342%%-------------------------------------------------- 1343%% Mib view wrapper 1344%%-------------------------------------------------- 1345get_mib_view(SecModel, SecName, SecLevel, ContextName) -> 1346 snmpa_vacm:get_mib_view(notify, 1347 SecModel, SecName, SecLevel, ContextName). 1348 1349 1350user_err(F, A) -> 1351 snmpa_error:user_err(F, A). 1352