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