1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-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 21%% 22%% ts:run(snmp, snmp_agent_test, [batch]). 23%% 24-module(snmp_test_mgr_misc). 25 26%% API 27-export([start_link_packet/8, start_link_packet/9, start_link_packet/10, 28 stop/1, 29 send_discovery_pdu/2, 30 send_pdu/2, send_msg/4, send_bytes/2, 31 error/2, 32 get_pdu/1, set_pdu/2, format_hdr/1]). 33 34%% internal exports 35-export([init_packet/11]). 36 37-compile({no_auto_import, [error/2]}). 38 39-define(SNMP_USE_V3, true). 40-include_lib("snmp/include/snmp_types.hrl"). 41-include_lib("snmp/src/misc/snmp_verbosity.hrl"). 42-include("snmp_test_lib.hrl"). 43 44 45%%---------------------------------------------------------------------- 46%% The InHandler process will receive messages on the form {snmp_pdu, Pdu}. 47%%---------------------------------------------------------------------- 48 49start_link_packet( 50 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz) -> 51 start_link_packet( 52 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 53 false). 54 55start_link_packet( 56 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 57 Dbg) -> 58 start_link_packet( 59 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 60 Dbg, inet). 61 62start_link_packet( 63 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 64 Dbg, IpFamily) when is_integer(UdpPort) -> 65 Args = 66 [self(), 67 InHandler, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 68 Dbg, IpFamily], 69 proc_lib:start_link(?MODULE, init_packet, Args). 70 71stop(Pid) -> 72 Pid ! {stop, self()}, 73 receive 74 {Pid, stopped} -> ok 75 end. 76 77 78send_discovery_pdu(Pdu, PacketPid) when is_record(Pdu, pdu) -> 79 PacketPid ! {send_discovery_pdu, self(), Pdu}, 80 await_discovery_response_pdu(). 81 82await_discovery_response_pdu() -> 83 receive 84 {discovery_response, Reply} -> 85 Reply 86 end. 87 88 89send_pdu(Pdu, PacketPid) when is_record(Pdu, pdu) -> 90 PacketPid ! {send_pdu, Pdu}. 91 92send_msg(Msg, PacketPid, Ip, Udp) when is_record(Msg, message) -> 93 PacketPid ! {send_msg, Msg, Ip, Udp}. 94 95send_bytes(Bytes, PacketPid) -> 96 PacketPid ! {send_bytes, Bytes}. 97 98%%-------------------------------------------------- 99%% The SNMP encode/decode process 100%%-------------------------------------------------- 101init_packet( 102 Parent, 103 SnmpMgr, AgentIp, UdpPort, TrapUdp, VsnHdr, Version, Dir, BufSz, 104 DbgOptions, IpFamily) -> 105 %% This causes "verbosity printouts" to print (from the 106 %% specified level) in the modules we "borrow" from the 107 %% agent code (burr,,,). 108 %% With "our" name (mgr_misc). 109 put(sname, mgr_misc), 110 init_debug(DbgOptions), 111 %% Make use of the "test name" print "feature" 112 put(tname, "MGR-MISC"), 113 ?IPRINT("starting"), 114 UdpOpts = [{recbuf,BufSz}, {reuseaddr, true}, IpFamily], 115 {ok, UdpId} = gen_udp:open(TrapUdp, UdpOpts), 116 put(msg_id, 1), 117 init_usm(Version, Dir), 118 proc_lib:init_ack(Parent, self()), 119 ?IPRINT("started"), 120 packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []). 121 122init_debug(Dbg) when is_atom(Dbg) -> 123 put(debug,Dbg), 124 %% put(verbosity, silence); 125 put(verbosity, trace); 126init_debug(DbgOptions) when is_list(DbgOptions) -> 127 case lists:keysearch(debug, 1, DbgOptions) of 128 {value, {_, Dbg}} when is_atom(Dbg) -> 129 put(debug, Dbg); 130 _ -> 131 put(debug, false) 132 end, 133 case lists:keysearch(verbosity, 1, DbgOptions) of 134 {value, {_, Ver}} when is_atom(Ver) -> 135 put(verbosity, Ver); 136 _ -> 137 put(verbosity, silence) 138 end, 139 ok. 140 141 142packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, MsgData) -> 143 receive 144 {send_discovery_pdu, From, Pdu} -> 145 d("packet_loop -> received send_discovery_pdu with" 146 "~n From: ~p" 147 "~n Pdu: ~p", [From, Pdu]), 148 case mk_discovery_msg(Version, Pdu, VsnHdr, "") of 149 error -> 150 ok; 151 {M, B} when is_list(B) -> 152 put(discovery, {M, From}), 153 display_outgoing_message(M), 154 udp_send(UdpId, AgentIp, UdpPort, B) 155 end, 156 packet_loop(SnmpMgr, UdpId, AgentIp, UdpPort, VsnHdr, Version, []); 157 158 {send_pdu, Pdu} -> 159 d("packet_loop -> received send_pdu with" 160 "~n Pdu: ~p", [Pdu]), 161 case mk_msg(Version, Pdu, VsnHdr, MsgData) of 162 error -> 163 ok; 164 B when is_list(B) -> 165 udp_send(UdpId, AgentIp, UdpPort, B) 166 end, 167 packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]); 168 169 {send_msg, Msg, Ip, Udp} -> 170 d("packet_loop -> received send_msg with" 171 "~n Msg: ~p" 172 "~n Ip: ~p" 173 "~n Udp: ~p", [Msg,Ip,Udp]), 174 case catch snmp_pdus:enc_message(Msg) of 175 {'EXIT', Reason} -> 176 error("Encoding error:" 177 "~n Msg: ~w" 178 "~n Reason: ~w",[Msg, Reason]); 179 B when is_list(B) -> 180 udp_send(UdpId, Ip, Udp, B) 181 end, 182 packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]); 183 {udp, UdpId, Ip, UdpPort, Bytes} -> 184 d("packet_loop -> received udp with" 185 "~n UdpId: ~p" 186 "~n Ip: ~p" 187 "~n UdpPort: ~p" 188 "~n sz(Bytes): ~p", [UdpId, Ip, UdpPort, sz(Bytes)]), 189 MsgData3 = handle_udp_packet(Version, erase(discovery), 190 UdpId, Ip, UdpPort, Bytes, 191 SnmpMgr, AgentIp), 192 packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version, 193 MsgData3); 194 {send_bytes, B} -> 195 d("packet_loop -> received send_bytes with" 196 "~n sz(B): ~p", [sz(B)]), 197 udp_send(UdpId, AgentIp, UdpPort, B), 198 packet_loop(SnmpMgr,UdpId,AgentIp,UdpPort,VsnHdr,Version,[]); 199 {stop, Pid} -> 200 d("packet_loop -> received stop from ~p", [Pid]), 201 gen_udp:close(UdpId), 202 Pid ! {self(), stopped}, 203 exit(normal); 204 Other -> 205 d("packet_loop -> received unknown" 206 "~n ~p", [Other]), 207 exit({snmp_packet_got_other, Other}) 208 end. 209 210 211handle_udp_packet(_V, undefined, 212 UdpId, Ip, UdpPort, 213 Bytes, SnmpMgr, AgentIp) -> 214 try snmp_pdus:dec_message_only(Bytes) of 215 Message when Message#message.version =:= 'version-3' -> 216 d("handle_udp_packet -> version 3"), 217 handle_v3_message(SnmpMgr, UdpId, Ip, UdpPort, AgentIp, 218 Bytes, Message); 219 220 Message when is_record(Message, message) -> 221 d("handle_udp_packet -> version 1 or 2"), 222 handle_v1_or_v2_message(SnmpMgr, UdpId, Ip, UdpPort, AgentIp, 223 Bytes, Message) 224 225 catch 226 Class:Error:_ -> 227 error("Decoding error (~w). Bytes: ~w ~n Error: ~w " 228 "(UDPport: ~w, Ip: ~w)", 229 [Class, Bytes, Error, UdpPort, Ip]), 230 [] 231 end; 232handle_udp_packet(V, {DiscoReqMsg, From}, _UdpId, _Ip, _UdpPort, 233 Bytes, _, _AgentIp) -> 234 DiscoRspMsg = (catch snmp_pdus:dec_message(Bytes)), 235 display_incomming_message(DiscoRspMsg), 236 _Reply = (catch check_discovery_result(V, DiscoReqMsg, DiscoRspMsg)), 237 case (catch check_discovery_result(V, DiscoReqMsg, DiscoRspMsg)) of 238 {ok, AgentEngineID} when is_list(AgentEngineID) -> 239 %% Ok, step 1 complete, now for step 2 240 %% Which we skip for now 241 OK = {ok, AgentEngineID}, 242 From ! {discovery_response, OK}, 243 []; 244 Error -> 245 From ! {discovery_response, Error}, 246 [] 247 end. 248 249handle_v3_message(Mgr, UdpId, Ip, UdpPort, AgentIp, 250 Bytes, Message) -> 251 try handle_v3_msg(Bytes, Message) of 252 {ok, NewData, MsgData} -> 253 Msg = Message#message{data = NewData}, 254 case Mgr of 255 {pdu, Pid} -> 256 Pdu = get_pdu(Msg), 257 d("handle_v3_message -> send pdu to manager (~p): " 258 "~n ~p", [Pid, Pdu]), 259 Pid ! {snmp_pdu, Pdu}; 260 {msg, Pid} -> 261 d("handle_v3_message -> send msg to manager (~p): " 262 "~n ~p", [Pid, Msg]), 263 Pid ! {snmp_msg, Msg, Ip, UdpPort} 264 end, 265 MsgData 266 267 catch 268 throw:{error, Reason, B}:_ -> 269 udp_send(UdpId, AgentIp, UdpPort, B), 270 error("Decoding (v3) error. Auto-sending Report.\n" 271 "~n Reason: ~w " 272 "(UDPport: ~w, Ip: ~w)", 273 [Reason, UdpPort, Ip]), 274 []; 275 276 throw:{error, Reason}:_ -> 277 error("Decoding (v3) error. " 278 "~n Bytes: ~w" 279 "~n Reason: ~w " 280 "(UDPport: ~w, Ip: ~w)", 281 [Bytes, Reason, UdpPort, Ip]), 282 []; 283 284 Class:Error:_ -> 285 error("Decoding (v3) error (~w). " 286 "~n Bytes: ~w" 287 "~n Error: ~w " 288 "(UDPport: ~w, Ip: ~w)", 289 [Class, Bytes, Error, UdpPort, Ip]), 290 [] 291 292 end. 293 294handle_v1_or_v2_message(Mgr, _UdpId, Ip, UdpPort, _AgentIp, 295 Bytes, Message) -> 296 try snmp_pdus:dec_pdu(Message#message.data) of 297 Pdu when is_record(Pdu, pdu) -> 298 case Mgr of 299 {pdu, Pid} -> 300 d("handle_v1_or_v2_message -> send pdu to manager (~p): " 301 "~n ~p", [Pid, Pdu]), 302 Pid ! {snmp_pdu, Pdu}; 303 {msg, Pid} -> 304 d("handle_v1_or_v2_message -> send msg to manager (~p): " 305 "~n ~p", [Pid, Pdu]), 306 Msg = Message#message{data = Pdu}, 307 Pid ! {snmp_msg, Msg, Ip, UdpPort} 308 end; 309 Pdu when is_record(Pdu, trappdu) -> 310 case Mgr of 311 {pdu, Pid} -> 312 d("handle_v1_or_v2_message -> send trap-pdu to manager (~p): " 313 "~n ~p", [Pid, Pdu]), 314 Pid ! {snmp_pdu, Pdu}; 315 {msg, Pid} -> 316 d("handle_v1_or_v2_message -> send trap-msg to manager (~p): " 317 "~n ~p", [Pid, Pdu]), 318 Msg = Message#message{data = Pdu}, 319 Pid ! {snmp_msg, Msg, Ip, UdpPort} 320 end 321 322 catch 323 Class:Error:_ -> 324 error("Decoding (v1 or v2) error (~w): " 325 "~n Bytes: ~w" 326 "~n Error: ~w " 327 "(UDPport: ~w, Ip: ~w)", 328 [Class, Bytes, Error, UdpPort, Ip]) 329 end. 330 331 332%% This function assumes that the agent and the manager (thats us) 333%% has the same version. 334check_discovery_result('version-3', DiscoReqMsg, DiscoRspMsg) -> 335 ReqMsgID = getMsgID(DiscoReqMsg), 336 RspMsgID = getMsgID(DiscoRspMsg), 337 check_msgID(ReqMsgID, RspMsgID), 338 ReqRequestId = getRequestId('version-3', DiscoReqMsg), 339 RspRequestId = getRequestId('version-3', DiscoRspMsg), 340 check_requestId(ReqRequestId, RspRequestId), 341 {ok, getMsgAuthEngineID(DiscoRspMsg)}; 342check_discovery_result(Version, DiscoReqMsg, DiscoRspMsg) -> 343 ReqRequestId = getRequestId(Version, DiscoReqMsg), 344 RspRequestId = getRequestId(Version, DiscoRspMsg), 345 check_requestId(ReqRequestId, RspRequestId), 346 {ok, getSysDescr(DiscoRspMsg)}. 347 348check_msgID(ID, ID) -> 349 ok; 350check_msgID(ReqMsgID, RspMsgID) -> 351 throw({error, {invalid_msgID, ReqMsgID, RspMsgID}}). 352 353check_requestId(Id,Id) -> 354 ok; 355check_requestId(ReqRequestId, RspRequestId) -> 356 throw({error, {invalid_requestId, ReqRequestId, RspRequestId}}). 357 358getMsgID(M) when is_record(M, message) -> 359 (M#message.vsn_hdr)#v3_hdr.msgID. 360 361getRequestId('version-3',M) when is_record(M, message) -> 362 ((M#message.data)#scopedPdu.data)#pdu.request_id; 363getRequestId(_Version,M) when is_record(M, message) -> 364 (M#message.data)#pdu.request_id; 365getRequestId(Version,M) -> 366 io:format("************* ERROR ****************" 367 "~n Version: ~w" 368 "~n M: ~w~n", [Version,M]), 369 throw({error, {unknown_request_id, Version, M}}). 370 371getMsgAuthEngineID(M) when is_record(M, message) -> 372 SecParams1 = (M#message.vsn_hdr)#v3_hdr.msgSecurityParameters, 373 SecParams2 = snmp_pdus:dec_usm_security_parameters(SecParams1), 374 SecParams2#usmSecurityParameters.msgAuthoritativeEngineID. 375 376getSysDescr(M) when is_record(M, message) -> 377 getSysDescr((M#message.data)#pdu.varbinds); 378getSysDescr([]) -> 379 not_found; 380getSysDescr([#varbind{oid = [1,3,6,1,2,1,1,1], value = Value}|_]) -> 381 Value; 382getSysDescr([#varbind{oid = [1,3,6,1,2,1,1,1,0], value = Value}|_]) -> 383 Value; 384getSysDescr([_|T]) -> 385 getSysDescr(T). 386 387handle_v3_msg(Packet, #message{vsn_hdr = V3Hdr, data = Data}) -> 388 d("handle_v3_msg -> entry"), 389 %% Code copied from snmp_mpd.erl 390 #v3_hdr{msgID = MsgId, msgFlags = MsgFlags, 391 msgSecurityModel = MsgSecurityModel, 392 msgSecurityParameters = SecParams} = V3Hdr, 393 SecModule = get_security_module(MsgSecurityModel), 394 d("handle_v3_msg -> SecModule: ~p", [SecModule]), 395 SecLevel = hd(MsgFlags) band 3, 396 d("handle_v3_msg -> SecLevel: ~p", [SecLevel]), 397 IsReportable = snmp_misc:is_reportable(MsgFlags), 398 SecRes = (catch SecModule:process_incoming_msg(list_to_binary(Packet), 399 Data,SecParams,SecLevel)), 400 {_SecEngineID, SecName, ScopedPDUBytes, SecData, _} = 401 check_sec_module_result(SecRes, V3Hdr, Data, IsReportable), 402 case (catch snmp_pdus:dec_scoped_pdu(ScopedPDUBytes)) of 403 ScopedPDU when is_record(ScopedPDU, scopedPdu) -> 404 {ok, ScopedPDU, {MsgId, SecName, SecData}}; 405 {'EXIT', Reason} -> 406 throw({error, Reason}); 407 Error -> 408 throw({error, {scoped_pdu_decode_failed, Error}}) 409 end; 410handle_v3_msg(_Packet, BadMessage) -> 411 throw({error, bad_message, BadMessage}). 412 413get_security_module(?SEC_USM) -> 414 snmpa_usm; 415get_security_module(SecModel) -> 416 throw({error, {unknown_sec_model, SecModel}}). 417 418check_sec_module_result(Res, V3Hdr, Data, IsReportable) -> 419 d("check_sec_module_result -> entry with" 420 "~n Res: ~p", [Res]), 421 case Res of 422 {ok, X} -> 423 X; 424 {error, Reason, []} -> 425 throw({error, {securityError, Reason}}); 426 {error, Reason, ErrorInfo} when IsReportable == true -> 427 #v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr, 428 case generate_v3_report_msg(MsgID, MsgSecModel, Data, ErrorInfo) of 429 error -> 430 throw({error, {securityError, Reason}}); 431 Packet -> 432 throw({error, {securityError, Reason}, Packet}) 433 end; 434 {error, Reason, _} -> 435 throw({error, {securityError, Reason}}); 436 Else -> 437 throw({error, {securityError, Else}}) 438 end. 439 440generate_v3_report_msg(_MsgID, _MsgSecurityModel, Data, ErrorInfo) -> 441 d("generate_v3_report_msg -> entry with" 442 "~n ErrorInfo: ~p", [ErrorInfo]), 443 {Varbind, SecName, Opts} = ErrorInfo, 444 ReqId = 445 if is_record(Data, scopedPdu) -> (Data#scopedPdu.data)#pdu.request_id; 446 true -> 0 447 end, 448 Pdu = #pdu{type = report, request_id = ReqId, 449 error_status = noError, error_index = 0, 450 varbinds = [Varbind]}, 451 SecLevel = snmp_misc:get_option(securityLevel, Opts, 0), 452 SnmpEngineID = snmp_framework_mib:get_engine_id(), 453 ContextEngineID = 454 snmp_misc:get_option(contextEngineID, Opts, SnmpEngineID), 455 ContextName = snmp_misc:get_option(contextName, Opts, ""), 456 mk_msg('version-3', Pdu, {ContextName, SecName, SnmpEngineID, 457 ContextEngineID, SecLevel}, 458 undefined). 459 460 461error(Format, Data) -> 462 io:format("*** Error ***~n"), 463 ok = io:format(Format, Data), 464 io:format("~n"). 465 466 467mk_discovery_msg('version-3', Pdu, _VsnHdr, UserName) -> 468 ScopedPDU = #scopedPdu{contextEngineID = "", 469 contextName = "", 470 data = Pdu}, 471 Bytes = snmp_pdus:enc_scoped_pdu(ScopedPDU), 472 MsgID = get(msg_id), 473 put(msg_id, MsgID+1), 474 UsmSecParams = 475 #usmSecurityParameters{msgAuthoritativeEngineID = "", 476 msgAuthoritativeEngineBoots = 0, 477 msgAuthoritativeEngineTime = 0, 478 msgUserName = UserName, 479 msgPrivacyParameters = "", 480 msgAuthenticationParameters = ""}, 481 SecBytes = snmp_pdus:enc_usm_security_parameters(UsmSecParams), 482 PduType = Pdu#pdu.type, 483 Hdr = #v3_hdr{msgID = MsgID, 484 msgMaxSize = 1000, 485 msgFlags = snmp_misc:mk_msg_flags(PduType, 0), 486 msgSecurityModel = ?SEC_USM, 487 msgSecurityParameters = SecBytes}, 488 Msg = #message{version = 'version-3', vsn_hdr = Hdr, data = Bytes}, 489 case (catch snmp_pdus:enc_message_only(Msg)) of 490 {'EXIT', Reason} -> 491 error("Discovery encoding error: " 492 "~n Pdu: ~w" 493 "~n Reason: ~w",[Pdu, Reason]), 494 error; 495 L when is_list(L) -> 496 {Msg#message{data = ScopedPDU}, L} 497 end; 498mk_discovery_msg(Version, Pdu, {Com, _, _, _, _}, _UserName) -> 499 Msg = #message{version = Version, vsn_hdr = Com, data = Pdu}, 500 case catch snmp_pdus:enc_message(Msg) of 501 {'EXIT', Reason} -> 502 error("Discovery encoding error:" 503 "~n Pdu: ~w" 504 "~n Reason: ~w",[Pdu, Reason]), 505 error; 506 L when is_list(L) -> 507 {Msg, L} 508 end. 509 510 511mk_msg('version-3', Pdu, {Context, User, EngineID, CtxEngineId, SecLevel}, 512 MsgData) -> 513 d("mk_msg(version-3) -> entry with" 514 "~n Pdu: ~p" 515 "~n Context: ~p" 516 "~n User: ~p" 517 "~n EngineID: ~p" 518 "~n CtxEngineID: ~p" 519 "~n SecLevel: ~p", 520 [Pdu, Context, User, EngineID, CtxEngineId, SecLevel]), 521 %% Code copied from snmp_mpd.erl 522 {MsgId, SecName, SecData} = 523 if 524 is_tuple(MsgData) andalso (Pdu#pdu.type =:= 'get-response') -> 525 MsgData; 526 true -> 527 Md = get(msg_id), 528 put(msg_id, Md + 1), 529 {Md, User, []} 530 end, 531 ScopedPDU = #scopedPdu{contextEngineID = CtxEngineId, 532 contextName = Context, 533 data = Pdu}, 534 ScopedPDUBytes = snmp_pdus:enc_scoped_pdu(ScopedPDU), 535 536 PduType = Pdu#pdu.type, 537 V3Hdr = #v3_hdr{msgID = MsgId, 538 msgMaxSize = 1000, 539 msgFlags = snmp_misc:mk_msg_flags(PduType, SecLevel), 540 msgSecurityModel = ?SEC_USM}, 541 Message = #message{version = 'version-3', vsn_hdr = V3Hdr, 542 data = ScopedPDUBytes}, 543 SecEngineID = case PduType of 544 'get-response' -> snmp_framework_mib:get_engine_id(); 545 _ -> EngineID 546 end, 547 case catch snmpa_usm:generate_outgoing_msg(Message, SecEngineID, 548 SecName, SecData, SecLevel) of 549 {'EXIT', Reason} -> 550 error("version-3 message encoding exit" 551 "~n Pdu: ~w" 552 "~n Reason: ~w",[Pdu, Reason]), 553 error; 554 {error, Reason} -> 555 error("version-3 message encoding error" 556 "~n Pdu: ~w" 557 "~n Reason: ~w",[Pdu, Reason]), 558 error; 559 Packet -> 560 Packet 561 end; 562mk_msg(Version, Pdu, {Com, _User, _EngineID, _Ctx, _SecLevel}, _SecData) -> 563 Msg = #message{version = Version, vsn_hdr = Com, data = Pdu}, 564 case catch snmp_pdus:enc_message(Msg) of 565 {'EXIT', Reason} -> 566 error("~w encoding error" 567 "~n Pdu: ~w" 568 "~n Reason: ~w",[Version, Pdu, Reason]), 569 error; 570 B when is_list(B) -> 571 B 572 end. 573 574format_hdr(#message{version = 'version-3', 575 vsn_hdr = #v3_hdr{msgID = MsgId}, 576 data = #scopedPdu{contextName = CName}}) -> 577 io_lib:format("v3, ContextName = \"~s\" Message ID = ~w\n", 578 [CName, MsgId]); 579format_hdr(#message{version = Vsn, vsn_hdr = Com}) -> 580 io_lib:format("~w, CommunityName = \"~s\"\n", [vsn(Vsn), Com]). 581 582vsn('version-1') -> v1; 583vsn('version-2') -> v2c. 584 585 586udp_send(UdpId, AgentIp, UdpPort, B) -> 587 ?vlog("attempt send message (~w bytes) to ~p", [sz(B), {AgentIp, UdpPort}]), 588 case (catch gen_udp:send(UdpId, AgentIp, UdpPort, B)) of 589 {error,ErrorReason} -> 590 error("failed (error) sending message to ~p:~p: " 591 "~n ~p",[AgentIp, UdpPort, ErrorReason]); 592 {'EXIT',ExitReason} -> 593 error("failed (exit) sending message to ~p:~p:" 594 "~n ~p",[AgentIp, UdpPort, ExitReason]); 595 _ -> 596 ok 597 end. 598 599 600get_pdu(#message{version = 'version-3', data = #scopedPdu{data = Pdu}}) -> 601 Pdu; 602get_pdu(#message{data = Pdu}) -> 603 Pdu. 604 605set_pdu(Msg, RePdu) when Msg#message.version == 'version-3' -> 606 SP = (Msg#message.data)#scopedPdu{data = RePdu}, 607 Msg#message{data = SP}; 608set_pdu(Msg, RePdu) -> 609 Msg#message{data = RePdu}. 610 611 612%% Disgustingly, we borrow stuff from the agent, including the 613%% local-db. Also, disgustingly, the local-db may actually not 614%% have died yet. But since we actually *need* a clean local-db, 615%% we must make sure its dead before we try to start the new 616%% instance... 617init_usm('version-3', Dir) -> 618 ?IPRINT("init_usm -> create (and init) fake \"agent\" table", []), 619 ets:new(snmp_agent_table, [set, public, named_table]), 620 ets:insert(snmp_agent_table, {agent_mib_storage, persistent}), 621 %% The local-db process may *still* be running (from a previous 622 %% test case), on the way down, but not yet dead. 623 %% Either way, before we try to start it, make sure its old instance 624 %% dead and *gone*! 625 %% How do we do that without getting hung up? Calling the stop 626 %% function, will not do since it uses Timeout=infinity. 627 ?IPRINT("init_usm -> ensure (old) fake local-db is dead", []), 628 ensure_local_db_dead(), 629 ?IPRINT("init_usm -> try start fake local-db", []), 630 case snmpa_local_db:start_link(normal, Dir, 631 [{sname, "MGR-LOCAL-DB"}, 632 {verbosity, trace}]) of 633 {ok, Pid} -> 634 ?IPRINT("init_usm -> local-db started: ~p" 635 "~n ~p", [Pid, process_info(Pid)]); 636 {error, {already_started, Pid}} -> 637 LDBInfo = process_info(Pid), 638 ?EPRINT("init_usm -> local-db already started: ~p" 639 "~n ~p", [Pid, LDBInfo]), 640 ?FAIL({still_running, snmpa_local_db, LDBInfo}); 641 {error, Reason} -> 642 ?EPRINT("init_usm -> failed start local-db: " 643 "~n ~p", [Reason]), 644 ?FAIL({failed_starting, snmpa_local_db, Reason}) 645 end, 646 NameDb = snmpa_agent:db(snmpEngineID), 647 ?IPRINT("init_usm -> try set manager engine-id"), 648 R = snmp_generic:variable_set(NameDb, "mgrEngine"), 649 snmp_verbosity:print(info, info, "init_usm -> engine-id set result: ~p", [R]), 650 ?IPRINT("init_usm -> try set engine boots (framework-mib)"), 651 snmp_framework_mib:set_engine_boots(1), 652 ?IPRINT("init_usm -> try set engine time (framework-mib)"), 653 snmp_framework_mib:set_engine_time(1), 654 ?IPRINT("init_usm -> try usm (mib) reconfigure"), 655 snmp_user_based_sm_mib:reconfigure(Dir), 656 ?IPRINT("init_usm -> done"), 657 ok; 658init_usm(_Vsn, _Dir) -> 659 ok. 660 661ensure_local_db_dead() -> 662 ensure_dead(whereis(snmpa_local_db), 2000). 663 664ensure_dead(Pid, Timeout) when is_pid(Pid) -> 665 MRef = erlang:monitor(process, Pid), 666 try 667 begin 668 ensure_dead_wait(Pid, MRef, Timeout), 669 ensure_dead_stop(Pid, MRef, Timeout), 670 ensure_dead_kill(Pid, MRef, Timeout), 671 exit(failed_stop_local_db) 672 end 673 catch 674 throw:ok -> 675 ok 676 end; 677ensure_dead(_, _) -> 678 ?IPRINT("ensure_dead -> already dead", []), 679 ok. 680 681ensure_dead_wait(Pid, MRef, Timeout) -> 682 receive 683 {'DOWN', MRef, process, Pid, _Info} -> 684 ?IPRINT("ensure_dead_wait -> died peacefully"), 685 throw(ok) 686 after Timeout -> 687 ?WPRINT("ensure_dead_wait -> giving up"), 688 ok 689 end. 690 691ensure_dead_stop(Pid, MRef, Timeout) -> 692 %% Spawn a stop'er process 693 StopPid = spawn(fun() -> snmpa_local_db:stop() end), 694 receive 695 {'DOWN', MRef, process, Pid, _Info} -> 696 ?NPRINT("ensure_dead -> dead (stopped)"), 697 throw(ok) 698 after Timeout -> 699 ?WPRINT("ensure_dead_stop -> giving up"), 700 exit(StopPid, kill), 701 ok 702 end. 703 704ensure_dead_kill(Pid, MRef, Timeout) -> 705 exit(Pid, kill), 706 receive 707 {'DOWN', MRef, process, Pid, _Info} -> 708 ?NPRINT("ensure_dead -> dead (killed)"), 709 throw(ok) 710 after Timeout -> 711 ?WPRINT("ensure_dead_kill -> giving up"), 712 ok 713 end. 714 715 716 717display_incomming_message(M) -> 718 display_message("Incomming",M). 719 720display_outgoing_message(M) -> 721 display_message("Outgoing", M). 722 723display_message(Direction, M) when is_record(M, message) -> 724 io:format("~s SNMP message:~n", [Direction]), 725 V = M#message.version, 726 display_version(V), 727 display_hdr(V, M#message.vsn_hdr), 728 display_msg_data(V, Direction, M#message.data); 729display_message(Direction, M) -> 730 io:format("~s message unknown: ~n~p", [Direction, M]). 731 732display_version('version-3') -> 733 display_prop("Version",'SNMPv3'); 734display_version(V) -> 735 display_prop("Version",V). 736 737display_hdr('version-3',H) -> 738 display_msgID(H#v3_hdr.msgID), 739 display_msgMaxSize(H#v3_hdr.msgMaxSize), 740 display_msgFlags(H#v3_hdr.msgFlags), 741 SecModel = H#v3_hdr.msgSecurityModel, 742 display_msgSecurityModel(SecModel), 743 display_msgSecurityParameters(SecModel,H#v3_hdr.msgSecurityParameters); 744display_hdr(_V,Community) -> 745 display_community(Community). 746 747display_community(Community) -> 748 display_prop("Community",Community). 749 750display_msgID(Id) -> 751 display_prop("msgID",Id). 752 753display_msgMaxSize(Size) -> 754 display_prop("msgMaxSize",Size). 755 756display_msgFlags([Flags]) -> 757 display_prop("msgFlags",Flags); 758display_msgFlags([]) -> 759 display_prop("msgFlags",no_value_to_display); 760display_msgFlags(Flags) -> 761 display_prop("msgFlags",Flags). 762 763display_msgSecurityModel(?SEC_USM) -> 764 display_prop("msgSecurityModel",'USM'); 765display_msgSecurityModel(Model) -> 766 display_prop("msgSecurityModel",Model). 767 768display_msgSecurityParameters(?SEC_USM,Params) -> 769 display_usmSecurityParameters(Params); 770display_msgSecurityParameters(_Model,Params) -> 771 display_prop("msgSecurityParameters",Params). 772 773display_usmSecurityParameters(P) when is_list(P) -> 774 P1 = lists:flatten(P), 775 display_usmSecurityParameters(snmp_pdus:dec_usm_security_parameters(P1)); 776display_usmSecurityParameters(P) when is_record(P,usmSecurityParameters) -> 777 ID = P#usmSecurityParameters.msgAuthoritativeEngineID, 778 display_msgAuthoritativeEngineID(ID), 779 Boots = P#usmSecurityParameters.msgAuthoritativeEngineBoots, 780 display_msgAuthoritativeEngineBoots(Boots), 781 Time = P#usmSecurityParameters.msgAuthoritativeEngineTime, 782 display_msgAuthoritativeEngineTime(Time), 783 Name = P#usmSecurityParameters.msgUserName, 784 display_msgUserName(Name), 785 SecParams = P#usmSecurityParameters.msgAuthenticationParameters, 786 display_msgAuthenticationParameters(SecParams), 787 PrivParams = P#usmSecurityParameters.msgPrivacyParameters, 788 display_msgPrivacyParameters(PrivParams); 789display_usmSecurityParameters(P) -> 790 display_prop("unknown USM sec paraams",P). 791 792display_msgAuthoritativeEngineID(ID) -> 793 display_prop("msgAuthoritativeEngineID",ID). 794 795display_msgAuthoritativeEngineBoots(V) -> 796 display_prop("msgAuthoritativeEngineBoots",V). 797 798display_msgAuthoritativeEngineTime(V) -> 799 display_prop("msgAuthoritativeEngineTime",V). 800 801display_msgUserName(V) -> 802 display_prop("msgUserName",V). 803 804display_msgAuthenticationParameters(V) -> 805 display_prop("msgAuthenticationParameters",V). 806 807display_msgPrivacyParameters(V) -> 808 display_prop("msgPrivacyParameters",V). 809 810display_msg_data('version-3',Direction,D) when is_record(D,scopedPdu) -> 811 display_scoped_pdu(Direction,D); 812display_msg_data(_Version,Direction,D) when is_record(D,pdu) -> 813 display_pdu(Direction,D); 814display_msg_data(_Version,_Direction,D) -> 815 display_prop("Unknown message data",D). 816 817display_scoped_pdu(Direction,P) -> 818 display_contextEngineID(P#scopedPdu.contextEngineID), 819 display_contextName(P#scopedPdu.contextName), 820 display_scoped_pdu_data(Direction,P#scopedPdu.data). 821 822display_contextEngineID(Id) -> 823 display_prop("contextEngineID",Id). 824 825display_contextName(Name) -> 826 display_prop("contextName",Name). 827 828display_scoped_pdu_data(Direction,D) when is_record(D,pdu) -> 829 display_pdu(Direction,D); 830display_scoped_pdu_data(Direction,D) when is_record(D,trappdu) -> 831 display_trappdu(Direction,D); 832display_scoped_pdu_data(_Direction,D) -> 833 display_prop("Unknown scoped pdu data",D). 834 835display_pdu(Direction, P) -> 836 io:format("~s PDU:~n", [Direction]), 837 display_type(P#pdu.type), 838 display_request_id(P#pdu.request_id), 839 display_error_status(P#pdu.error_status), 840 display_error_index(P#pdu.error_index), 841 display_varbinds(P#pdu.varbinds). 842 843display_type(T) -> 844 display_prop("Type",T). 845 846display_request_id(Id) -> 847 display_prop("Request id",Id). 848 849display_error_status(S) -> 850 display_prop("Error status",S). 851 852display_error_index(I) -> 853 display_prop("Error index",I). 854 855display_varbinds([H|T]) -> 856 display_prop_hdr("Varbinds"), 857 display_varbind(H), 858 display_varbinds(T); 859display_varbinds([]) -> 860 ok. 861 862display_varbind(V) when is_record(V,varbind) -> 863 display_oid(V#varbind.oid), 864 display_vtype(V#varbind.variabletype), 865 display_value(V#varbind.value), 866 display_org_index(V#varbind.org_index); 867display_varbind(V) -> 868 display_prop("\tVarbind",V). 869 870display_oid(V) -> 871 display_prop("\tOid",V). 872 873display_vtype(V) -> 874 display_prop("\t\tVariable type",V). 875 876display_value(V) -> 877 display_prop("\t\tValue",V). 878 879display_org_index(V) -> 880 display_prop("\t\tOrg index",V). 881 882display_trappdu(Direction,P) -> 883 io:format("~s TRAP-PDU:~n",[Direction]), 884 display_prop("TRAP-PDU",P). 885 886display_prop(S,no_value_to_display) -> 887 io:format("\t~s: ~n",[S]); 888display_prop(S,V) -> 889 io:format("\t~s: ~p~n",[S,V]). 890 891 892display_prop_hdr(S) -> 893 io:format("\t~s:~n",[S]). 894 895 896%%---------------------------------------------------------------------- 897%% Debug 898%%---------------------------------------------------------------------- 899 900sz(L) when is_list(L) -> 901 iolist_size(L); 902sz(B) when is_binary(B) -> 903 size(B); 904sz(O) -> 905 {unknown_size, O}. 906 907d(F) -> d(F, []). 908d(F,A) -> d(get(debug), F, A). 909 910d(true, F, A) -> 911 ?IPRINT(F, A); 912d(_,_F,_A) -> 913 ok. 914 915