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