1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2004-2021. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(snmpa_net_if).
21
22-behaviour(snmpa_network_interface).
23
24-export([start_link/4,
25	 info/1,
26	 verbosity/2]).
27-export([get_log_type/1,  set_log_type/2]).
28-export([get_request_limit/1, set_request_limit/2]).
29-export([system_continue/3, system_terminate/4, system_code_change/4]).
30-export([init/5]).
31-export([filter_reset/1]).
32
33-include("snmp_types.hrl").
34-include("snmpa_internal.hrl").
35-include("snmpa_atl.hrl").
36-include("snmp_debug.hrl").
37-include("snmp_verbosity.hrl").
38
39
40%% Regarding Trap/Notification transport(s),
41%% it *should* be possible to specify either:
42%% 1) A fixed set of transport(s) used for sending.
43%%    For instance, one for IPv4 and one for IPv6.
44%% 2) A single one-shot ephemeral port.
45%%    That is, a port is created, used once, and then closed.
46%% 3) A pool of ephemeral ports, used for "a time"
47%%    (a configurable number of sends, a set number of bytes
48%%     or time based) thar is cycled through.
49%% Though, we do *not* currently implement ephemeral sockets,
50%% so case 2 and 3 are not possible at the moment.
51
52%% Also, the request-reponder and trap-sender transport(s),
53%% has different needs.
54%% The trap-sender transport will send more data then it will receive.
55%% Therefor, we should be able to specify;
56%% bind_to, no_reuse_address, recbuf and sndbuf individually:
57%% {intAgentTransports,
58%%  [{transportDomainUdpIpv4, {{141,213,11,24},   PortInfo},
59%%    req_responder, Opts},
60%%   {transportDomainUdpIpv4, {{141,213,11,24},   PortInfo},
61%%    trap_sender,   Opts},
62%%   {transportDomainUdpIpv6, {{0,0,0,0,0,0,0,1}, Portinfo},
63%%    req_responder, Opts},
64%%   {transportDomainUdpIpv6, {{0,0,0,0,0,0,0,1}, Portinfo},
65%%    trap_sender,   Opts}]}.
66%% Opts is basically "socket options" for this transport (the same
67%% options as for net-if).
68%% Also, always give the option to provide a port range:
69%%    Port     :: pos_integer() | system | range() || ranges()
70%%                system => Let the system choose
71%%                (0 is unfortunately already used as 'default',
72%%                 so we can't use that for 'system').
73%%    range()  :: {Min :: pos_integer(), Max :: pos_integer()} when Min < Max
74%%    ranges() :: [pos_integer() | range()]
75%%                Examples:
76%%                      [{2000, 2004}]
77%%                      [2000, 2001, 2002, 2003, 2004]
78%%                      [2000, 2001, {2002, 2004}]
79%%                      [{5000, 5100}, {6000, 6100}]
80
81%% <EPHEMERAL-FOR-FUTUR-USE>
82%% , *but*
83%% may also contain the tuple, {ephemeral, EphmOpts}.
84%% Ephm sockets are created on the fly, used for the specified
85%% time (number of sends, number of bytes sent, used time, ...).
86%% </EPHEMERAL-FOR-FUTUR-USE>
87
88-record(state,
89	{parent,
90	 note_store,
91	 master_agent,
92	 transports  = [],
93	 mpd_state,
94	 log,
95	 reqs  = [],
96	 debug = false,
97	 limit = infinity,
98	 filter}).
99
100-type transport_kind() :: req_responder | trap_sender.
101-type port_info() :: non_neg_integer() |
102                     system |
103                     {pos_integer(), pos_integer()}.
104
105%% <EPHEMERAL-FOR-FUTUR-USE>
106%% How would 'ephemeral' effect this?
107%% What kind of usage would we have for emphemeral ports?
108%%    once (send and maybe receive reply (inform)) |
109%%    {sends, pos_integer()} (number of sends) |
110%%    {data,  pos_integer()} (number of bytes sent) |
111%%    {alive_time, pos_integer()} (once used for the first time, alive time)
112%% You can't combine a ephemeral info with a fixed port (pos_integer())
113%% The port_info() is used when creating a port, even an ephemeral port.
114%% But it must either be 'system' or a range in that (ephemeral) case.
115%% Also, ephemeral transports are only allowed if kind = trap_sender
116%% -type ephemeral() :: none |
117%%                      once |
118%%                      {sends,      pos_integer()} |
119%%                      {data,       pos_integer()} |
120%%                      {alive_time, pos_integer()}.
121
122%% Note that since informs require confirmation,
123%% an ephemeral socket cannot be removed immediately
124%% when it has been "used up".
125%% We need to keep it for some time to receive responces
126%% and in case a resend is needed!.
127%% </EPHEMERAL-FOR-FUTUR-USE>
128
129-record(transport,
130	{socket,
131         mref,
132         kind         = all :: all | transport_kind(),
133	 domain       = snmpUDPDomain,
134         address   :: inet:ip_address(),
135         port_no   :: pos_integer(),
136         port_info :: port_info(),
137         %% <EPHEMERAL-FOR-FUTUR-USE>
138         ephm         = none, %%  :: ephemeral(),
139         ephm_info    = undefined, % Only used if ephm =/= none and once
140         %% </EPHEMERAL-FOR-FUTUR-USE>
141         inet_backend = [],
142	 opts         = [],
143	 req_refs     = [] % Not used for trap/notification transports
144        }).
145
146-ifndef(default_verbosity).
147-define(default_verbosity,silence).
148-endif.
149
150-define(DEFAULT_FILTER_MODULE, snmpa_net_if_filter).
151-define(DEFAULT_FILTER_OPTS,   [{module, ?DEFAULT_FILTER_MODULE}]).
152
153-define(ATL_SEQNO_INITIAL, 1).
154-define(ATL_SEQNO_MAX,     2147483647).
155
156
157%%%-----------------------------------------------------------------
158%%% This module implements the default Network Interface part
159%%% of the SNMP agent. It uses UDP, and read the agent.conf to find
160%%% the UDP port.
161%%%
162%%% Opts = [Opt]
163%%% Opt  = {verbosity,silence | log | debug} |
164%%%        {bind_to, bool()} |
165%%%        {recbuf,integer()} |
166%%%        {no_reuse, bool()} |
167%%%        {req_limit, integer() | infinity}
168%%%-----------------------------------------------------------------
169start_link(Prio, NoteStore, MasterAgent, Opts) ->
170    ?d("start_link -> entry with"
171	"~n   Prio:        ~p"
172	"~n   NoteStore:   ~p"
173	"~n   MasterAgent: ~p"
174	"~n   Opts:        ~p", [Prio, NoteStore, MasterAgent, Opts]),
175    Args = [Prio, NoteStore, MasterAgent, self(), Opts],
176    proc_lib:start_link(?MODULE, init, Args).
177
178
179info(Pid) ->
180    case call(Pid, info) of
181	Info when is_list(Info) ->
182	    Info;
183	_ ->
184	    []
185    end.
186
187verbosity(Pid, Verbosity) ->
188    Pid ! {verbosity, Verbosity}.
189
190get_log_type(Pid) ->
191    call(Pid, get_log_type).
192
193set_log_type(Pid, NewType) ->
194    call(Pid, {set_log_type, NewType}).
195
196get_request_limit(Pid) ->
197    call(Pid, get_request_limit).
198
199set_request_limit(Pid, NewLimit) ->
200    call(Pid, {set_request_limit, NewLimit}).
201
202get_transports() ->
203    {value, Transports} = snmp_framework_mib:intAgentTransports(get),
204    Transports.
205
206filter_reset(Pid) ->
207    Pid ! filter_reset.
208
209
210%%-----------------------------------------------------------------
211%%-----------------------------------------------------------------
212
213init(Prio, NoteStore, MasterAgent, Parent, Opts) ->
214    ?d("init -> entry with"
215      "~n   Prio:        ~p"
216      "~n   NoteStore:   ~p"
217      "~n   MasterAgent: ~p"
218      "~n   Parent:      ~p"
219      "~n   Opts:        ~p", [Prio, NoteStore, MasterAgent, Parent, Opts]),
220    case (catch do_init(Prio, NoteStore, MasterAgent, Parent, Opts)) of
221	{ok, State} ->
222	    proc_lib:init_ack({ok, self()}),
223	    try loop(State)
224	    catch
225		C:E:S when C =/= exit, E =/= shutdown ->
226		    Fmt =
227			"loop/1 EXCEPTION ~w:~w~n"
228			"   ~p",
229		    case C of
230			exit ->
231			    %% Externally killed, root cause is elsewhere
232			    info_msg(Fmt, [C, E, S]);
233			_ ->
234			    error_msg(Fmt, [C, E, S])
235		    end,
236		    erlang:raise(C, E, S)
237	    end;
238	{error, Reason} ->
239	    config_err("failed starting net-if: ~n~p", [Reason]),
240	    proc_lib:init_ack({error, Reason});
241	Error ->
242	    config_err("failed starting net-if: ~n~p", [Error]),
243	    proc_lib:init_ack({error, Error})
244    end.
245
246do_init(Prio, NoteStore, MasterAgent, Parent, Opts) ->
247    process_flag(trap_exit, true),
248    process_flag(priority,  Prio),
249
250    %% -- Verbosity --
251    put(sname,     nif),
252    put(verbosity, get_verbosity(Opts)),
253    ?vlog("starting",[]),
254
255    %% -- Versions --
256    Vsns = get_vsns(Opts),
257    ?vdebug("vsns: ~w",[Vsns]),
258
259    %% Flow control --
260    Limit      = get_req_limit(Opts),
261    ?vdebug("Limit: ~w", [Limit]),
262    FilterOpts = get_filter_opts(Opts),
263    FilterMod  = create_filter(FilterOpts),
264    ?vdebug("FilterMod: ~w FilterOpts: ~p", [FilterMod,FilterOpts]),
265
266    %% -- Audit trail log
267    Log = create_log(),
268    ?vdebug("Log: ~w",[Log]),
269
270    RawTransports = get_transports(),
271    ?vdebug("Raw Transports: "
272            "~n   ~p", [RawTransports]),
273    try
274	[begin
275             %% Any socket option not explicitly configured for the transport
276             %% will be taken from the "global" socket options (which serve as
277             %% default values).
278             %% Also, note that Ephm are not actually used at this time.
279	     {Ephm, IpAddr, PortInfo, InetBackend, SocketOpts} =
280                 socket_opts(Domain, Address, RawSocketOpts, Opts),
281             ?vtrace("socket opts processed:"
282                     "~n      Ephm:         ~p"
283                     "~n      Port Info:    ~p"
284                     "~n      Inet Backend: ~p"
285                     "~n      Socket Opts:  ~p",
286                     [Ephm, PortInfo, InetBackend, SocketOpts]),
287	     {Socket, IpPort}             = socket_open(Domain, PortInfo,
288                                                        InetBackend,
289                                                        SocketOpts),
290             SockMRef = inet:monitor(Socket),
291             ?vtrace("socket opened:"
292                     "~n      Socket:   ~p"
293                     "~n      Port No:  ~p"
294                     "~n      Info:     ~p",
295                     [Socket, IpPort, inet:info(Socket)]),
296
297             %% Should we really do this here?
298             %% If Kind =:= trap_sender, we only need to receive after
299             %% we have sent an inform!
300	     active_once(Socket),
301             ?vtrace("socket activated:"
302                     "~n      Info: ~p", [inet:info(Socket)]),
303	     #transport{
304                socket       = Socket,
305                mref         = SockMRef,
306                kind         = Kind,
307                domain       = Domain,
308                %% We may not have explicitly specified the port ('system'
309                %% or a range), so it could have been "generated".
310                %% Also, shall we push this into the transport (handled by the
311                %% FRAMEWORK MIB)? Would not work for ephemeral sockets.
312                address      = IpAddr,
313                port_no      = IpPort,
314                port_info    = PortInfo,
315                ephm         = Ephm,
316                inet_backend = InetBackend,
317                opts         = SocketOpts}
318             %% We need to fix this also
319	 end || {Domain, Address, Kind, RawSocketOpts} <- RawTransports]
320    of
321	[] ->
322	    ?vinfo("No transports configured: ~p", [RawTransports]),
323	    {error, {no_transports, RawTransports}};
324	Transports ->
325	    MpdState = snmpa_mpd:init(Vsns),
326	    init_counters(),
327	    S = #state{parent       = Parent,
328		       note_store   = NoteStore,
329		       master_agent = MasterAgent,
330		       mpd_state    = MpdState,
331		       transports   = Transports,
332		       log          = Log,
333		       limit        = Limit,
334		       filter       = FilterMod},
335	    ?vdebug("started with MpdState: ~p", [MpdState]),
336	    {ok, S}
337    catch
338	Error ->
339	    ?vinfo("Failed to initialize socket(s): ~p", [Error]),
340	    {error, Error}
341    end.
342
343
344create_log() ->
345    case ets:lookup(snmp_agent_table, audit_trail_log) of
346	[] ->
347	    {undefined, []};
348	[{audit_trail_log, AtlOpts}] ->
349	    ?vtrace("AtlOpts: ~p", [AtlOpts]),
350	    Type   = get_atl_type(AtlOpts),
351	    Dir    = get_atl_dir(AtlOpts),
352	    Size   = get_atl_size(AtlOpts),
353	    Repair = get_atl_repair(AtlOpts),
354	    Name   = ?audit_trail_log_name,
355	    File   = filename:absname(?audit_trail_log_file, Dir),
356	    case get_atl_seqno(AtlOpts) of
357		true ->
358		    Initial  = ?ATL_SEQNO_INITIAL,
359		    Max      = ?ATL_SEQNO_MAX,
360		    Module   = snmpa_agent,
361		    Function = increment_counter,
362		    Args     = [atl_seqno, Initial, Max],
363		    SeqNoGen = {Module, Function, Args},
364		    case snmp_log:create(Name, File,
365					 SeqNoGen, Size, Repair, true) of
366			{ok, Log} ->
367			    ?vdebug("log created: ~w", [Log]),
368			    {Log, Type};
369			{error, Reason} ->
370			    throw({error, {create_log, Reason}})
371		    end;
372		_ ->
373		    case snmp_log:create(Name, File, Size, Repair, true) of
374			{ok, Log} ->
375			    ?vdebug("log created: ~w", [Log]),
376			    {Log, Type};
377			{error, Reason} ->
378			    throw({error, {create_log, Reason}})
379		    end
380	    end
381    end.
382
383
384create_filter(Opts) when is_list(Opts) ->
385    case get_filter_module(Opts) of
386	?DEFAULT_FILTER_MODULE = Mod ->
387	    Mod;
388	Module ->
389	    snmpa_network_interface_filter:verify(Module),
390	    Module
391    end;
392create_filter(BadOpts) ->
393    throw({error, {bad_filter_opts, BadOpts}}).
394
395
396log({_, []}, _, _, _) ->
397    ok;
398log({Log, Types}, 'set-request', Packet, Address) ->
399    case lists:member(write, Types) of
400	true ->
401	    snmp_log:log(Log, Packet, format_address(Address));
402	false ->
403	    ok
404    end;
405log({Log, Types}, _, Packet, Address) ->
406     case lists:member(read, Types) of
407	true ->
408	    snmp_log:log(Log, Packet, format_address(Address));
409	false ->
410	    ok
411    end.
412
413format_address(Address) ->
414    iolist_to_binary(snmp_conf:mk_addr_string(Address)).
415
416
417socket_open(snmpUDPDomain = Domain, IpPort, InetBackend, Opts) ->
418    ?vdebug("socket_open(~p) -> entry with"
419            "~n   Port: ~p"
420            "~n   Opts: ~p", [Domain, IpPort, Opts]),
421    case init:get_argument(snmp_fd) of
422	{ok, [[FdStr]]} ->
423	    FD = list_to_integer(FdStr),
424	    ?vdebug("socket_open(~p) -> open with fd - "
425                    "(old) snmp_fd command line argument provided: "
426                    "~n   FD:   ~p"
427                    "~n   Port: ~p"
428                    "~n   Opts: ~p", [Domain, FD, IpPort, Opts]),
429	    gen_udp_open(0, InetBackend ++ [{fd, FD} | Opts]);
430	error ->
431	    case init:get_argument(snmpa_fd) of
432		{ok, [[FdStr]]} ->
433		    FD = list_to_integer(FdStr),
434                    ?vdebug("socket_open(~p) -> open with fd - "
435                            "snmpa_fd command line argument provided: "
436                            "~n   FD:   ~p"
437                            "~n   Port: ~p"
438                            "~n   Opts: ~p", [Domain, FD, IpPort, Opts]),
439		    gen_udp_open(0, InetBackend ++ [{fd, FD} | Opts]);
440		error ->
441		    ?vdebug("socket_open(~p) -> plain open"
442                            "~n   Port: ~p"
443                            "~n   Opts: ~p", [Domain, IpPort, Opts]),
444		    gen_udp_open(IpPort, InetBackend ++ Opts)
445	    end
446    end;
447socket_open(Domain, PortInfo, InetBackend, Opts)
448  when (Domain =:= transportDomainUdpIpv4) orelse
449       (Domain =:= transportDomainUdpIpv6) ->
450    ?vdebug("socket_open(~p) -> entry with"
451            "~n      PortInfo:    ~p"
452            "~n      InetBackend: ~p"
453            "~n      Opts:        ~p", [Domain, PortInfo, InetBackend, Opts]),
454    gen_udp_open(PortInfo, InetBackend ++ Opts);
455socket_open(Domain, PortInfo, InetBackend, Opts) ->
456    ?vinfo("socket_open(~p) -> entry when invalid with"
457           "~n   PortInfo:    ~p"
458           "~n   InetBackend: ~p"
459           "~n   Opts:        ~p", [Domain, PortInfo, InetBackend, Opts]),
460    throw({socket_open, Domain, InetBackend, Opts}).
461
462
463%% Make the system choose!
464gen_udp_open(system, Opts) ->
465    ?vtrace("gen_udp_open(system) -> entry"),
466    case gen_udp:open(0, Opts) of
467	{ok, Socket} ->
468            case inet:port(Socket) of
469                {ok, PortNo} ->
470                    ?vtrace("gen_udp_open(system) -> created: "
471                            "~n      ~p (~w)", [Socket, PortNo]),
472                    {Socket, PortNo};
473                {error, PReason} ->
474                    (catch gen_udp:close(Socket)),
475                    throw({udp_open, {port, PReason}})
476            end;
477	{error, OReason} ->
478            throw({udp_open, {open, OReason}})
479    end;
480%% This is for "future compat" since we cannot actually config '0'...
481gen_udp_open(IpPort, Opts) when (IpPort =:= 0) ->
482    ?vtrace("gen_udp_open(0) -> entry with"
483            "~n   Opts: ~p", [Opts]),
484    case gen_udp:open(IpPort, Opts) of
485	{ok, Socket} ->
486            case inet:port(Socket) of
487                {ok, PortNo} ->
488                    ?vtrace("gen_udp_open(0) -> created: "
489                            "~n      ~p (~w)", [Socket, PortNo]),
490                    {Socket, PortNo};
491                {error, PReason} ->
492                    (catch gen_udp:close(Socket)),
493                    throw({udp_open, {port, PReason}})
494            end;
495	{error, Reason} ->
496	    throw({udp_open, {open, IpPort, Reason}})
497    end;
498gen_udp_open(IpPort, Opts) when is_integer(IpPort) ->
499    ?vtrace("gen_udp_open(~w) -> entry with"
500            "~n   Opts: ~p", [IpPort, Opts]),
501    case gen_udp:open(IpPort, Opts) of
502	{ok, Socket} ->
503            ?vtrace("gen_udp_open(~w) -> created: "
504                    "~n      ~p", [Socket]),
505	    {Socket, IpPort};
506	{error, Reason} ->
507	    throw({udp_open, {open, IpPort, Reason}})
508    end;
509%% A range is "pointless" if we allow reuseaddr...
510%% ...but we leave that to the user...
511gen_udp_open({Min, Max}, Opts) ->
512    ?vtrace("gen_udp_open(~w,~w) -> entry", [Min, Max]),
513    gen_udp_range_open(Min, Max, Opts);
514%% A range is "pointless" if we allow reuseaddr...
515%% ...but we leave that to the user...
516gen_udp_open(Ranges, Opts) when is_list(Ranges) ->
517    gen_udp_ranges_open(Ranges, Opts).
518
519gen_udp_range_open(Min, Max, _Opts) when (Min > Max) ->
520    ?vinfo("gen_udp_range_open -> entry when no available ports"),
521    throw({udp_open, no_available_ports});
522gen_udp_range_open(Min, Max, Opts) ->
523    ?vtrace("gen_udp_range_open -> entry with"
524            "~n      Min: ~w"
525            "~n      Max: ~w", [Min, Max]),
526    try gen_udp:open(Min, Opts) of
527        {ok, Socket} ->
528            ?vtrace("gen_udp_range_open(~w,~w) -> created: "
529                    "~n      ~p", [Min, Max, Socket]),
530            {Socket, Min};
531        {error, eaddrinuse} ->
532            ?vdebug("gen_udp_range_open(~w,~w) -> eaddrinuse"),
533            gen_udp_range_open(Min+1, Max, Opts);
534        {error, Reason} ->
535            ?vdebug("gen_udp_range_open(~w,~w) -> ~w", [Reason]),
536            throw({udp_open, {open, Reason}})
537    catch
538        C:E:S ->
539            ?vinfo("gen_udp_range_open(~w,~w) -> failed open socket: "
540                   "~n      C: ~p"
541                   "~n      E: ~p"
542                   "~n      S: ~p", [Min, Max, C, E, S]),
543            erlang:raise(C, E, S)
544    end.
545
546gen_udp_ranges_open([], _Opts) ->
547    ?vinfo("gen_udp_ranges_open -> entry when no available ports"),
548    throw({udp_open, no_available_ports});
549gen_udp_ranges_open([PortNo|Ranges], Opts) when is_integer(PortNo) andalso
550                                                (PortNo > 0) ->
551    ?vtrace("gen_udp_ranges_open(~w) -> entry", [PortNo]),
552    try gen_udp_open(PortNo, Opts) of
553        {_Sock, PortNo} = SUCCESS when is_integer(PortNo) ->
554            SUCCESS
555    catch
556        throw:{udp_open, _} ->
557            gen_udp_ranges_open(Ranges, Opts)
558    end;
559gen_udp_ranges_open([{Min, Max}|Ranges], Opts) ->
560    ?vtrace("gen_udp_ranges_open(~w,~w) -> entry", [Min, Max]),
561    try gen_udp_range_open(Min, Max, Opts) of
562        {_Sock, PortNo} = SUCCESS when is_integer(PortNo) ->
563            SUCCESS
564    catch
565        throw:{udp_open, _} ->
566            gen_udp_ranges_open(Ranges, Opts)
567    end.
568
569
570loop(#state{transports = Transports,
571            limit      = Limit,
572            parent     = Parent} = S) ->
573    ?vdebug("loop(~p)", [S]),
574    receive
575        {udp, Socket, IpAddr, IpPort, Packet} = Msg ->
576	    ?vlog("got paket from ~w:~w on ~w", [IpAddr, IpPort, Socket]),
577	    case lists:keyfind(Socket, #transport.socket, Transports) of
578		#transport{socket = Socket, domain = Domain} = Transport ->
579		    From =
580			case Domain of
581			    snmpUDPDomain ->
582				{IpAddr, IpPort};
583			    _ ->
584				{Domain, {IpAddr, IpPort}}
585			end,
586		    loop(maybe_handle_recv(S, Transport, From, Packet));
587		false ->
588		    error_msg("Packet on unknown socket: "
589                              "~n   ~p", [Msg]),
590		    loop(S)
591	    end;
592
593	{udp_error, Socket, Error} when is_port(Socket) ->
594	    ?vinfo("got udp-error on ~p: ~w", [Socket, Error]),
595	    case lists:keyfind(Socket, #transport.socket, Transports) of
596		#transport{socket = Socket} = Transport ->
597		    loop(handle_udp_error(S, Transport, Error));
598		false ->
599		    loop(handle_udp_error_unknown(S, Socket, Error))
600	    end;
601
602	{info, ReplyRef, Pid} ->
603	    Info = get_info(S),
604	    Pid ! {ReplyRef, Info, self()},
605	    loop(S);
606
607	%% response (to get/get_next/get_bulk/set requests)
608	{snmp_response, Vsn, RePdu, Type, ACMData, To, Extra} ->
609	    ?vlog("reply pdu: "
610		  "~n   ~s",
611		  [?vapply(snmp_misc, format, [256, "~w", [RePdu]])]),
612	    {_, ReqRef} = lists:keyfind(request_ref, 1, Extra),
613	    case
614		case
615		    (Limit =/= infinity) andalso
616		    select_transport(ReqRef, Transports)
617		of
618		    false ->
619			select_transport(address_to_domain(To), Type,
620                                         Transports);
621		    T ->
622			T
623		end
624	    of
625		false ->
626		    error_msg(
627		      "Can not find transport for response PDU to: ~s",
628		      [format_address(To)]),
629		    loop(S);
630		Transport ->
631		    NewS = update_req_counter_outgoing(S, Transport, ReqRef),
632		    maybe_handle_reply_pdu(
633		      NewS, Transport, Vsn, RePdu, Type, ACMData, To),
634		    loop(NewS)
635	    end;
636
637	%% Traps/notification
638	{send_pdu, Vsn, Pdu, MsgData, TDomAddrs} ->
639	    ?vdebug("send pdu:~n"
640		    "   Pdu:       ~p~n"
641		    "   TDomAddrs: ~p", [Pdu, TDomAddrs]),
642	    NewS =
643		maybe_handle_send_pdu(
644		  S, Vsn, Pdu, MsgData, TDomAddrs, undefined),
645	    loop(NewS);
646
647	%% We dont use the extra-info at this time, ...
648	{send_pdu, Vsn, Pdu, MsgData, TDomAddrs, _ExtraInfo} ->
649	    ?vdebug("send pdu:~n"
650		    "   Pdu:        ~p~n"
651		    "   TDomAddrs: ~p", [Pdu, TDomAddrs]),
652	    NewS =
653		maybe_handle_send_pdu(
654		  S, Vsn, Pdu, MsgData, TDomAddrs, undefined),
655	    loop(NewS);
656
657	%% Informs
658	{send_pdu_req, Vsn, Pdu, MsgData, TDomAddrs, From} ->
659	    ?vdebug("send pdu request:~n"
660		    "   Pdu:       ~p~n"
661		    "   TDomAddrs: ~p~n"
662		    "   From:      ~p",
663		    [Pdu, TDomAddrs, toname(From)]),
664	    NewS =
665		maybe_handle_send_pdu(
666		  S, Vsn, Pdu, MsgData, TDomAddrs, From),
667	    loop(NewS);
668
669	%% We dont use the extra-info at this time, ...
670	{send_pdu_req, Vsn, Pdu, MsgData, TDomAddrs, From, _ExtraInfo} ->
671	    ?vdebug("send pdu request:~n"
672		    "   Pdu:       ~p~n"
673		    "   TDomAddrs: ~p~n"
674		    "   From:      ~p",
675		    [Pdu, TDomAddrs, toname(From)]),
676	    NewS =
677		maybe_handle_send_pdu(
678		  S, Vsn, Pdu, MsgData, TDomAddrs, From),
679	    loop(NewS);
680
681	%% Discovery Inform
682	%% <BACKWARD-COMPAT>
683	{send_discovery, Pdu, MsgData, To, From} ->
684	    ?vdebug("received send discovery request: "
685		    "~n   Pdu:  ~p"
686		    "~n   To:   ~p"
687		    "~n   From: ~p",
688		    [Pdu, To, toname(From)]),
689	    NewS = handle_send_discovery(S, Pdu, MsgData, To, From),
690	    loop(NewS);
691	%% </BACKWARD-COMPAT>
692
693	%% Discovery Inform
694	{send_discovery, Pdu, MsgData, To, From, ExtraInfo} ->
695	    ?vdebug("received send discovery request: "
696		    "~n   Pdu:       ~p"
697		    "~n   To:        ~p"
698		    "~n   From:      ~p"
699		    "~n   ExtraInfo: ~p",
700		    [Pdu, To, toname(From), ExtraInfo]),
701	    NewS = handle_send_discovery(S, Pdu, MsgData, To, From),
702	    loop(NewS);
703
704	{discarded_pdu, _Vsn, ReqId, _ACMData, Variable, Extra} ->
705	    ?vdebug("discard PDU: ~p - ~p - ~p",
706		    [Variable, Extra, Transports]),
707	    snmpa_mpd:discarded_pdu(Variable),
708	    {_, ReqRef} = lists:keyfind(request_ref, 1, Extra),
709	    if
710		Limit =:= infinity ->
711		    %% The incoming PDU was not registered
712		    loop(update_req_counter_outgoing(S, false, ReqRef));
713		true ->
714		    case
715			select_transport(ReqRef, Transports)
716		    of
717			false ->
718			    error_msg(
719			      "Can not find transport for discarded PDU: ~p",
720			      [ReqId]),
721			    loop(S);
722			Transport ->
723			    loop(
724			      update_req_counter_outgoing(
725				S, Transport, ReqRef))
726		    end
727	    end;
728
729	{get_log_type, ReplyRef, Pid} ->
730	    ?vdebug("get log type: ~p", []),
731	    {_, LogType} = S#state.log,
732	    Pid ! {ReplyRef, {ok, LogType}, self()},
733	    loop(S);
734
735	{{set_log_type, NewType}, ReplyRef, Pid} ->
736	    ?vdebug("set log type: ~p", [NewType]),
737	    {NewState, Reply} = (catch handle_set_log_type(S, NewType)),
738	    Pid ! {ReplyRef, Reply, self()},
739	    loop(NewState);
740
741	{get_request_limit, ReplyRef, Pid} ->
742	    ?vdebug("get request limit: ~p", []),
743	    Pid ! {ReplyRef, {ok, Limit}, self()},
744	    loop(S);
745
746	{{set_request_limit, NewLimit}, ReplyRef, Pid} ->
747	    ?vdebug("set request limit: ~p", [NewLimit]),
748	    {NewState, Reply} = (catch handle_set_request_limit(S, NewLimit)),
749	    Pid ! {ReplyRef, Reply, self()},
750	    loop(NewState);
751
752	{disk_log, _Node, Log, Info} ->
753	    ?vdebug("disk log event: ~p, ~p", [Log, Info]),
754	    NewS = handle_disk_log(Log, Info, S),
755	    loop(NewS);
756
757	{verbosity, Verbosity} ->
758	    ?vlog("verbosity: ~p -> ~p", [get(verbosity), Verbosity]),
759	    put(verbosity, snmp_verbosity:validate(Verbosity)),
760	    loop(S);
761
762	filter_reset ->
763	    reset_counters(),
764	    loop(S);
765
766	{'EXIT', Parent, Reason} ->
767	    ?vlog("parent (~p) exited: "
768		  "~n   ~p", [Parent, Reason]),
769	    exit(Reason);
770
771        {'DOWN', _SockMRef, Type, Socket, Reason} when (Type =:= port) orelse
772                                                       (Type =:= socket) ->
773	    case lists:keyfind(Socket, #transport.socket, Transports) of
774		#transport{
775                   socket       = Socket,
776                   domain       = Domain,
777                   port_info    = PortInfo,
778                   inet_backend = InetBackend,
779                   opts         = SocketOpts,
780                   req_refs     = ReqRefs} = Transport ->
781		    try socket_open(Domain, PortInfo, InetBackend, SocketOpts) of
782			{NewSocket, PortNo} ->
783			    error_msg(
784			      "Socket ~p exited for reason"
785			      "~n     ~p"
786			      "~n     Re-opened (~p, ~w)",
787			      [Socket, Reason, NewSocket, PortNo]),
788			    (length(ReqRefs) < Limit) andalso
789				active_once(NewSocket),
790                            NewSockMRef = inet:monitor(NewSocket),
791			    S#state{
792			      transports =
793				  lists:keyreplace(
794				    Socket, #transport.socket, Transports,
795				    Transport#transport{socket  = NewSocket,
796                                                        mref    = NewSockMRef,
797                                                        port_no = PortNo})}
798		    catch
799			ReopenReason ->
800			    error_msg(
801			      "Socket ~p exited for reason"
802			      "~n     ~p"
803			      "~n     Re-open failed with reason"
804			      "~n     ~p",
805			      [Socket, Reason, ReopenReason]),
806			    exit(ReopenReason)
807		    end;
808		false ->
809		    error_msg(
810		      "Exit message from socket ~p for reason ~p~n",
811		      [Socket, Reason]),
812		    loop(S)
813	    end;
814
815	{'EXIT', Pid, Reason} when is_pid(Pid) ->
816	    ?vlog("~p exited: "
817		  "~n   ~p", [Pid, Reason]),
818	    NewS = clear_reqs(Pid, S),
819	    loop(NewS);
820
821	{system, From, Msg} ->
822	    ?vdebug("system event ~p from ~p", [Msg, From]),
823	    sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], S);
824
825	_ ->
826	    loop(S)
827    end.
828
829handle_udp_error(S, #transport{socket = Socket,
830                               kind   = Kind}, Error) ->
831    try inet:sockname(Socket) of
832        {ok, {IP, Port}} ->
833            error_msg("UDP Error for transport: "
834                      "~n      Socket: ~p (~p, ~p)"
835                      "~n      Kind:   ~p"
836                      "~n      Error:  ~p", [Socket, IP, Port, Kind, Error]);
837        {error, _} ->
838            error_msg("UDP Error for transport: "
839                      "~n      Socket: ~p"
840                      "~n      Kind:   ~p"
841                      "~n      Error:  ~p", [Socket, Kind, Error])
842    catch
843        _:_:_ ->
844            error_msg("UDP Error for transport: "
845                      "~n      Socket: ~p"
846                      "~n      Kind:   ~p"
847                      "~n      Error:  ~p", [Socket, Kind, Error])
848    end,
849    S.
850
851handle_udp_error_unknown(S, Socket, Error) ->
852    try inet:sockname(Socket) of
853        {ok, {IP, Port}} ->
854            warning_msg("UDP Error for unknown transport: "
855                        "~n      Socket: ~p (~p, ~p)"
856                        "~n      Error:  ~p", [Socket, IP, Port, Error]);
857        {error, _} ->
858            warning_msg("UDP Error for unknown transport: "
859                        "~n      Socket: ~p"
860                        "~n      Error:  ~p", [Socket, Error])
861    catch
862        _:_:_ ->
863            warning_msg("UDP Error for transport: "
864                        "~n      Socket: ~p"
865                        "~n      Error:  ~p", [Socket, Error])
866    end,
867    S.
868
869
870update_req_counter_incoming(
871  #state{limit = infinity} = S,
872  #transport{socket = Socket},
873  _ReqRef) ->
874    active_once(Socket), %% No limit so activate directly
875    S;
876update_req_counter_incoming(
877  #state{limit = Limit} = S,
878  #transport{socket = Socket, req_refs = ReqRefs} = T,
879  ReqRef) when length(ReqRefs) + 1 >= Limit ->
880    %% Ok, one more and we are at the limit.
881    %% Just make sure we are not already processing this one...
882    case lists:member(ReqRef, ReqRefs) of
883	false ->
884	    %% We are at the limit, do _not_ activate socket
885	    update_transport_req_refs(S, T, [ReqRef | ReqRefs]);
886	true ->
887	    active_once(Socket),
888	    S
889    end;
890update_req_counter_incoming(
891  #state{} = S,
892  #transport{socket = Socket, req_refs = ReqRefs} = T,
893  ReqRef) ->
894    active_once(Socket),
895    case lists:member(ReqRef, ReqRefs) of
896	false ->
897	    update_transport_req_refs(S, T, [ReqRef | ReqRefs]);
898	true ->
899	    S
900    end.
901
902update_req_counter_outgoing(
903  #state{limit = infinity} = S,
904  _Transport, _ReqRef) ->
905    %% Already activated (in the incoming function)
906    S;
907update_req_counter_outgoing(
908  #state{limit = Limit} = S,
909  #transport{socket = Socket, req_refs = ReqRefs} = Transport,
910  ReqRef) ->
911    LengthReqRefs = length(ReqRefs),
912    ?vtrace("update_req_counter_outgoing() -> entry with~n"
913	    "   Limit:           ~w~n"
914	    "   ReqRef:          ~w~n"
915	    "   length(ReqRefs): ~w", [Limit, ReqRef, LengthReqRefs]),
916    NewReqRefs = lists:delete(ReqRef, ReqRefs),
917    (LengthReqRefs >= Limit) andalso (length(NewReqRefs) < Limit) andalso
918	begin
919	    ?vtrace("update_req_counter_outgoing -> "
920		    "passed below limit: activate", []),
921	    active_once(Socket)
922	end,
923    update_transport_req_refs(S, Transport, NewReqRefs).
924
925update_transport_req_refs(
926  #state{transports = Transports} = S,
927  #transport{socket = Socket} = T,
928  ReqRefs) ->
929    S#state{
930      transports =
931	  lists:keyreplace(
932	    Socket, #transport.socket, Transports,
933	    T#transport{req_refs = ReqRefs})}.
934
935
936maybe_handle_recv(
937  #state{filter = FilterMod} = S,
938  #transport{socket = Socket} = Transport,
939  From, Packet) ->
940    {From_1, From_2} = From,
941    case
942	try FilterMod:accept_recv(From_1, From_2)
943	catch
944	    Class:Exception:StackTrace ->
945		error_msg(
946		  "FilterMod:accept_recv(~p, ~p) crashed: ~w:~w~n    ~p",
947		  [From_1, From_2, Class, Exception, StackTrace]),
948		true
949	end
950    of
951	false ->
952	    %% Drop the received packet
953            %% What if this is an expected (inform) reply
954            %% on an ephemeral socket?
955	    inc(netIfMsgInDrops),
956	    active_once(Socket),
957	    S;
958	Other ->
959	    case Other of
960		true ->
961		    ok;
962		_ ->
963		    error_msg(
964		      "FilterMod:accept_recv(~p, ~p) returned: ~p",
965		      [From_1,From_2,Other])
966	    end,
967	    handle_recv(S, Transport, From, Packet)
968    end.
969
970handle_recv(
971  #state{mpd_state  = MpdState, note_store = NS, log = Log} = S,
972  #transport{socket = Socket} = Transport,
973  From, Packet) ->
974    put(n1, erlang:monotonic_time(micro_seconds)),
975    LogF =
976	fun(Type, Data) ->
977		log(Log, Type, Data, From)
978	end,
979    case (catch snmpa_mpd:process_packet(
980		  Packet, From, MpdState, NS, LogF)) of
981	{ok, _Vsn, Pdu, _PduMS, {discovery, ManagerEngineId}} ->
982	    handle_discovery_response(
983	      S, Transport, From, Pdu, ManagerEngineId);
984
985	{ok, _Vsn, Pdu, _PduMS, discovery} ->
986	    handle_discovery_response(
987	      S, Transport, From, Pdu, undefined);
988
989	{ok, Vsn, Pdu, PduMS, ACMData} ->
990	    ?vlog("got pdu ~s",
991		  [?vapply(snmp_misc, format, [256, "~w", [Pdu]])]),
992	    %% handle_recv_pdu(S, Transport, From, Vsn, Pdu, PduMS, ACMData);
993	    maybe_handle_recv_pdu(
994	      S, Transport, From, Vsn, Pdu, PduMS, ACMData);
995
996	{discarded, Reason} ->
997	    ?vlog("packet discarded for reason: ~s",
998		  [?vapply(snmp_misc, format, [256, "~w", [Reason]])]),
999	    active_once(Socket),
1000	    S;
1001
1002	{discarded, Reason, ReportPacket} ->
1003	    ?vlog("sending report for reason: "
1004		"~n   ~s",
1005		[?vapply(snmp_misc, format, [256, "~w", [Reason]])]),
1006	    (catch udp_send(Socket, From, ReportPacket)),
1007	    active_once(Socket),
1008	    S;
1009
1010	{discovery, ReportPacket} ->
1011	    ?vlog("sending discovery report", []),
1012	    (catch udp_send(Socket, From, ReportPacket)),
1013	    active_once(Socket),
1014	    S;
1015
1016	Error ->
1017	    error_msg("processing of received message failed: "
1018                      "~n   ~p", [Error]),
1019	    active_once(Socket),
1020	    S
1021    end.
1022
1023handle_discovery_response(
1024  #state{reqs = Reqs} = S,
1025  #transport{socket = Socket},
1026  _From,
1027  #pdu{request_id = ReqId} = Pdu,
1028  ManagerEngineId) ->
1029    active_once(Socket),
1030    case lists:keyfind(ReqId, 1, S#state.reqs) of
1031	{ReqId, Pid} ->
1032	    Pid ! {snmp_discovery_response_received, Pdu, ManagerEngineId},
1033	    %% XXX Strange... Reqs from this Pid should be reaped
1034	    %% at process exit by clear_reqs/2 so the following
1035	    %% should be redundant.
1036            NReqs = lists:keydelete(ReqId, 1, Reqs -- [{0, Pid}]), % ERIERL-427
1037	    S#state{reqs = NReqs};
1038
1039        %% <OTP-16207>
1040        %% For some reason 'snmptrapd' response in stage 2 with request-id
1041        %% of zero.
1042        false when (ReqId =:= 0) ->
1043            DiscoReqs = [X|| {0, From1}     <- S#state.reqs,
1044                             {_, From2} = X <- S#state.reqs, From1 =:= From2],
1045            case (length(DiscoReqs) =:= 2) of
1046                true ->
1047                    [{_, Pid}, _] = DiscoReqs,
1048                    Pid ! {snmp_discovery_response_received, Pdu,
1049                           ManagerEngineId},
1050                    NReqs = S#state.reqs -- DiscoReqs,
1051                    S#state{reqs = NReqs};
1052                false ->
1053                    S
1054            end;
1055        %% </OTP-16207>
1056
1057	false ->
1058	    %% Ouch, timeout? resend?
1059	    S
1060    end.
1061
1062maybe_handle_recv_pdu(
1063  #state{filter = FilterMod} = S,
1064  #transport{socket = Socket} = Transport,
1065  From, Vsn,
1066  #pdu{type = Type} = Pdu, PduMS, ACMData) ->
1067    {From_1, From_2} = From,
1068    case
1069	try FilterMod:accept_recv_pdu(From_1, From_2, Type)
1070	catch
1071	    Class:Exception:StackTrace ->
1072		error_msg(
1073		  "FilterMod:accept_recv_pdu(~p, ~p, ~p) crashed: ~w:~w~n"
1074		  "    ~p",
1075		  [From_1, From_2, Type, Class, Exception, StackTrace]),
1076		true
1077	end
1078    of
1079	false ->
1080	    inc(netIfPduInDrops),
1081	    active_once(Socket),
1082	    S;
1083	Other ->
1084	    case Other of
1085		true ->
1086		    ok;
1087		_ ->
1088		    error_msg(
1089		      "FilterMod:accept_recv_pdu(~p, ~p, ~p) returned: ~p",
1090		      [From_1,From_2,Type,Other])
1091	    end,
1092	    handle_recv_pdu(S, Transport, From, Vsn, Pdu, PduMS, ACMData)
1093    end.
1094
1095handle_recv_pdu(
1096  #state{reqs = Reqs} = S,
1097  #transport{socket = Socket},
1098  From, Vsn,
1099  #pdu{type = 'get-response', request_id = ReqId} = Pdu,
1100  _PduMS, _ACMData) ->
1101    active_once(Socket),
1102    case lists:keyfind(ReqId, 1, Reqs) of
1103	{ReqId, Pid} ->
1104	    ?vdebug("handle_recv_pdu -> "
1105		    "~n   send response to receiver ~p", [Pid]),
1106	    Pid ! {snmp_response_received, Vsn, Pdu, From};
1107	false ->
1108	    ?vdebug("handle_recv_pdu -> "
1109		    "~n   No receiver available for response pdu", [])
1110    end,
1111    S;
1112handle_recv_pdu(
1113  #state{master_agent = Pid} = S,
1114  #transport{} = Transport,
1115  From, Vsn,
1116  #pdu{type = Type} = Pdu,
1117  PduMS, ACMData)
1118  when Type =:= 'set-request';
1119       Type =:= 'get-request';
1120       Type =:= 'get-next-request';
1121       Type =:= 'get-bulk-request' ->
1122    ?vtrace("handle_recv_pdu -> received request (~w)", [Type]),
1123    ReqRef = make_ref(),
1124    Extra = [{request_ref, ReqRef}],
1125    Pid ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra},
1126    NewS = update_req_counter_incoming(S, Transport, ReqRef),
1127    ?vdebug("handle_recv_pdu -> ~p", [NewS]),
1128    NewS;
1129handle_recv_pdu(
1130  #state{master_agent = Pid} = S,
1131  #transport{socket = Socket},
1132  From, Vsn, Pdu, PduMS, ACMData) ->
1133    ?vtrace("handle_recv_pdu -> received other request", []),
1134    active_once(Socket),
1135    Pid ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, []},
1136    S.
1137
1138
1139maybe_handle_reply_pdu(
1140  #state{filter = FilterMod, transports = Transports} = S,
1141  #transport{} = Transport,
1142  Vsn,
1143  #pdu{} = Pdu,
1144  Type, ACMData, To) ->
1145    %%
1146    Addresses = [fix_filter_address(Transports, To)],
1147    case
1148	try
1149	    FilterMod:accept_send_pdu(Addresses, Type)
1150	catch
1151	    Class:Exception:StackTrace ->
1152		error_msg(
1153		  "FilterMod:accept_send_pdu(~p, ~p) crashed: ~w:~w~n    ~p",
1154		  [Addresses, Type, Class, Exception, StackTrace]),
1155		true
1156	end
1157    of
1158	false ->
1159	    inc(netIfPduOutDrops),
1160	    ok;
1161	Other ->
1162	    case Other of
1163		true ->
1164		    ok;
1165		_ ->
1166		    error_msg(
1167		      "FilterMod:accept_send_pdu(~p, ~p) returned: ~p",
1168		      [Addresses,Type,Other])
1169	    end,
1170	    handle_reply_pdu(S, Transport, Vsn, Pdu, Type, ACMData, To)
1171    end.
1172
1173handle_reply_pdu(
1174  #state{log = Log} = S,
1175  #transport{} = Transport,
1176  Vsn,
1177  #pdu{} = Pdu,
1178  Type, ACMData, To) ->
1179    %%
1180    LogF =
1181	fun(Type2, Data) ->
1182		log(Log, Type2, Data, To)
1183	end,
1184    case (catch snmpa_mpd:generate_response_msg(Vsn, Pdu, Type,
1185						ACMData, LogF)) of
1186	{ok, Packet} ->
1187	    ?vinfo("time in agent: ~w mysec", [time_in_agent()]),
1188	    try maybe_udp_send_wo_log(S, Transport, To, Packet)
1189	    catch
1190		{Reason, Sz} ->
1191		    error_msg("Cannot send message "
1192			      "~n   size:   ~p"
1193			      "~n   reason: ~p"
1194			      "~n   pdu:    ~p",
1195			      [Sz, Reason, Pdu])
1196	    end;
1197	{discarded, Reason} ->
1198	    ?vlog("handle_reply_pdu -> "
1199		  "~n   reply discarded for reason: ~s",
1200		[?vapply(snmp_misc, format, [256, "~w", [Reason]])]),
1201	    ok;
1202	{'EXIT', Reason} ->
1203	    user_err("failed generating response message: "
1204		     "~nPDU: ~p~n~p", [Pdu, Reason])
1205    end.
1206
1207
1208
1209maybe_handle_send_pdu(#state{filter     = FilterMod,
1210                             transports = Transports} = S,
1211                       Vsn, Pdu, MsgData, TDomAddrSecs, From) ->
1212
1213    ?vtrace("maybe_handle_send_pdu -> entry with"
1214	    "~n   FilterMod:    ~p"
1215	    "~n   TDomAddrSecs: ~p", [FilterMod, TDomAddrSecs]),
1216
1217    DomAddrSecs = snmpa_mpd:process_taddrs(TDomAddrSecs),
1218    AddressesToFilter =
1219	case is_legacy_transports(Transports) of
1220	    true ->
1221		[fix_filter_legacy_mpd_address(DAS)
1222		 || DAS <- DomAddrSecs];
1223	    false ->
1224		[fix_filter_mpd_address(DAS)
1225		 || DAS <- DomAddrSecs]
1226	end,
1227
1228    Type = pdu_type_of(Pdu),
1229
1230    case
1231	try FilterMod:accept_send_pdu(AddressesToFilter, Type)
1232	catch
1233	    Class:Exception:StackTrace ->
1234		error_msg(
1235		  "FilterMod:accept_send_pdu(~p, ~p) crashed: ~w:~w~n    ~p",
1236		  [AddressesToFilter, Type, Class, Exception, StackTrace]),
1237		true
1238	end
1239    of
1240	false ->
1241	    inc(netIfPduOutDrops),
1242	    ok;
1243	true ->
1244	    handle_send_pdu(S, Vsn, Pdu, MsgData, DomAddrSecs, From);
1245	FilteredAddresses when is_list(FilteredAddresses) ->
1246	    FilteredDomAddrSecs =
1247		case is_legacy_transports(Transports) of
1248		    true ->
1249			[DAS ||
1250			    DAS <- DomAddrSecs,
1251			    lists:member(
1252				fix_filter_legacy_mpd_address(DAS),
1253				FilteredAddresses)];
1254		    false ->
1255			[DAS ||
1256			    DAS <- DomAddrSecs,
1257			    lists:member(
1258				fix_filter_mpd_address(DAS),
1259				FilteredAddresses)]
1260		end,
1261	    ?vtrace("maybe_handle_send_pdu -> FilteredDomAddrSecs:~n"
1262		    "   ~p", [FilteredDomAddrSecs]),
1263	    handle_send_pdu(S, Vsn, Pdu, MsgData, FilteredDomAddrSecs, From);
1264	Other ->
1265	    error_msg(
1266	      "FilterMod:accept_send_pdu(~p, ~p) returned: ~p",
1267	      [AddressesToFilter,Type,Other]),
1268	    handle_send_pdu(S, Vsn, Pdu, MsgData, DomAddrSecs, From)
1269    end.
1270
1271handle_send_pdu(
1272  #state{note_store = NS} = S,
1273  Vsn, Pdu, MsgData, DomAddrSecs, From) ->
1274    %%
1275    ?vtrace("handle_send_pdu -> entry with~n"
1276	    "   Pdu:         ~p~n"
1277	    "   DomAddrSecs: ~p", [Pdu, DomAddrSecs]),
1278
1279    case (catch snmpa_mpd:generate_msg(
1280		  Vsn, NS, Pdu, MsgData, DomAddrSecs)) of
1281	{ok, Addresses} ->
1282	    do_handle_send_pdu(S, Pdu, Addresses);
1283	{discarded, Reason} ->
1284	    ?vlog("handle_send_pdu -> "
1285		  "~n   PDU ~p not sent due to ~p", [Pdu, Reason]),
1286	    ok;
1287        {'EXIT', Reason} ->
1288            user_err("failed generating message: "
1289                     "~nPDU: ~p~n~p", [Pdu, Reason]),
1290            ok
1291    end,
1292    case From of
1293	undefined ->
1294	    S;
1295	Pid when is_pid(Pid) ->
1296	    ?vtrace("link to ~p and add to request list", [Pid]),
1297	    link(Pid),
1298	    NReqs =
1299		snmp_misc:keyreplaceadd(
1300		  Pid, 2, S#state.reqs, {Pdu#pdu.request_id, From}),
1301	    S#state{reqs = NReqs}
1302    end.
1303
1304
1305handle_send_discovery(
1306  #state{
1307     note_store = NS,
1308     log        = Log,
1309     reqs       = Reqs,
1310     transports = Transports} = S,
1311  #pdu{type = Type, request_id = ReqId} = Pdu,
1312  MsgData, To, From) when is_pid(From) ->
1313
1314    ?vtrace("handle_send_discovery -> entry with"
1315	    "~n   Pdu:     ~p"
1316	    "~n   MsgData: ~p"
1317	    "~n   To:      ~p"
1318	    "~n   From:    ~p", [Pdu, MsgData, To, From]),
1319
1320    case (catch snmpa_mpd:generate_discovery_msg(NS, Pdu, MsgData, To)) of
1321	{ok, {Domain, Address, Packet}} ->
1322	    case select_transport(Domain, Type, Transports) of
1323		false ->
1324		    error_msg(
1325		      "Can not find transport to: ~s",
1326		      [format_address(To)]),
1327		    S;
1328		#transport{socket = Socket} ->
1329		    log(Log, Type, Packet, {Domain, Address}),
1330		    udp_send(Socket, {Domain, Address}, Packet),
1331		    ?vtrace("handle_send_discovery -> sent (~w)", [ReqId]),
1332                    link(From),
1333		    NReqs  = snmp_misc:keyreplaceadd(From, 2, Reqs, {ReqId, From}),
1334                    NReqs2 = (NReqs -- [{0, From}]) ++ [{0, From}], % OTP-16207
1335		    S#state{reqs = NReqs2}
1336	    end;
1337	{discarded, Reason} ->
1338	    ?vlog("handle_send_discovery -> "
1339		  "~n   Discovery PDU ~p not sent due to ~p", [Pdu, Reason]),
1340	    S;
1341        {'EXIT', Reason} ->
1342            user_err("failed generating discovery message: "
1343                     "~n   PDU:    ~p"
1344		     "~n   Reason: ~p", [Pdu, Reason]),
1345            S
1346    end.
1347
1348
1349do_handle_send_pdu(S, #pdu{type = Type} = Pdu, Addresses) ->
1350    do_handle_send_pdu(S, Type, Pdu, Addresses);
1351do_handle_send_pdu(S, Trap, Addresses) ->
1352    do_handle_send_pdu(S, trappdu, Trap, Addresses).
1353
1354do_handle_send_pdu(S, Type, Pdu, Addresses) ->
1355    try do_handle_send_pdu1(S, Type, Addresses)
1356    catch
1357	{Reason, Sz} ->
1358	    error_msg(
1359	      "Can not send message~n"
1360	      "   size:   ~p~n"
1361	      "   reason: ~p~n"
1362	      "   pdu:    ~p",
1363	      [Sz, Reason, Pdu])
1364    end.
1365
1366%% Because of the ephemeral sockets used by some transports,
1367%% the list of transports may be update for each send...
1368do_handle_send_pdu1(S, _Type, []) ->
1369    S;
1370do_handle_send_pdu1(S, Type, [{Domain, Address, Pkg}|Addresses])
1371  when is_binary(Pkg) ->
1372    NewS = do_handle_send_pdu2(S, Type, Domain, Address,
1373                               Pkg, Pkg, ""),
1374    do_handle_send_pdu1(NewS, Type, Addresses);
1375do_handle_send_pdu1(S, Type, [{Domain, Address, {Pkg, LogPkg}}|Addresses])
1376  when is_binary(Pkg) ->
1377    NewS = do_handle_send_pdu2(S, Type, Domain, Address,
1378                               Pkg, LogPkg, " encrypted"),
1379    do_handle_send_pdu1(NewS, Type, Addresses).
1380
1381
1382do_handle_send_pdu2(#state{transports = Transports} = S,
1383                    Type, Domain, Address, Pkg, LogPkg, EncrStr) ->
1384    ?vdebug("[~w] sending~s packet:"
1385            "~n   size: ~p"
1386            "~n   to:   ~p", [Domain, EncrStr, sz(Pkg), Address]),
1387    To = {Domain, Address},
1388    case select_transport(Domain, Type, Transports) of
1389	false ->
1390	    error_msg("Transport not found: "
1391                      "~n   size: ~p"
1392                      "~n   to:   ~s",
1393                      [sz(Pkg), format_address(To)]),
1394            S;
1395	#transport{ephm = none} = Transport ->
1396            ?vtrace("do_handle_send_pdu2 -> transport(ephm = none) selected: "
1397                    "~n      ~p", [Transport]),
1398	    maybe_udp_send_w_log(S, Transport, To, Pkg, LogPkg, Type),
1399            S%;
1400
1401            %% <EPHEMERAL-FOR-FUTUR-USE>
1402            %% #transport{} = Transport ->
1403            %% ?vtrace("do_handle_send_pdu2 -> transport selected: "
1404            %%         "~n      ~p", [Transport]),
1405	    %% case maybe_udp_send_w_log(S, Transport, To, Pkg, LogPkg, Type) of
1406            %%     {ok, Sz} -> % we actually sent something
1407            %%        maybe_update_ephm_transport(S, Transport, Type, Sz);
1408            %%     _ -> % Non-fatal error -> Nothing sent
1409            %%         S
1410            %% end
1411            %% </EPHEMERAL-FOR-FUTUR-USE>
1412    end.
1413
1414
1415%% <EPHEMERAL-FOR-FUTUR-USE>
1416
1417%% For inform:
1418%% This will have a reply, so we cannot close it directly!
1419%% Also, we will resend (a couple of times), if we don't
1420%% get a reply in time.
1421%% So, how do we handle this transport in this case?
1422%% Shall we create a list of used sockets? Tagged with a key
1423%% so we can reuse the correct socket for a resend?
1424
1425%%
1426%% Or shall we used the same socket for all the sends for this
1427%% trap/inform? That is, we send the trap/inform to a number of
1428%% targets (the Addresses list), and *that* is considered *one*
1429%% use?
1430%% That would mean that we would potentially need to wait for
1431%% replies from a large number of targets?
1432%% But it may be better in the long term, because we will not
1433%% use of so many sockets.
1434%%
1435
1436%% maybe_update_ephm_transport(S, #transport{ephm = once} = _Transport,
1437%%                             'inform-request' = _Type, _Sz) ->
1438%%     S; % Figure out the above first!
1439
1440%% %% Before we close the current socket, create the new.
1441%% %% This is done in case we fail to create a new socket
1442%% %% (if we first close the current and then fail to create
1443%% %%  the new, we are stuck).
1444%% %% If we fail to create the new socket, we keep the current.
1445%% %% Better then nothing!
1446%% maybe_update_ephm_transport(S, #transport{socket    = OldSocket,
1447%%                                           ephm      = once,
1448%%                                           port_info = PortInfo,
1449%%                                           opts      = Opts} = Transport,
1450%%                             _Type, _Sz) ->
1451%%     try
1452%%         begin
1453%%             {Socket, PortNo} = gen_udp_open(PortInfo, Opts),
1454%%             (catch gen_udp:close(OldSocket)),
1455%%             T2 = Transport#transport{socket  = Socket,
1456%%                                      port_no = PortNo},
1457%%             TS  = S#state.transports,
1458%%             TS2 = lists:keyreplace(OldSocket, #transport.socket, TS, T2),
1459%%             S#state{transports = TS2}
1460%%         end
1461%%     catch
1462%%         _:_:_ ->
1463%%             %% We need to identify which transport!
1464%%             error_msg("Failed creating new ephemeral socket for transport"),
1465%%             S
1466%%     end;
1467
1468%% %% Note that we do not currently handle inform:s, as that adds a whole
1469%% %% set of issues. See above for more info.
1470%% maybe_update_ephm_transport(S, #transport{socket    = Socket,
1471%%                                           ephm      = {sends, MaxSends},
1472%%                                           ephm_info = NumSends,
1473%%                                           port_info = _PortInfo,
1474%%                                           opts      = _Opts} = Transport,
1475%%                             _Type, _Sz) when (MaxSends > NumSends) ->
1476%%     T2  = Transport#transport{ephm_info = NumSends + 1},
1477%%     TS  = S#state.transports,
1478%%     TS2 = lists:keyreplace(Socket, #transport.socket, TS, T2),
1479%%     S#state{transports = TS2};
1480%% maybe_update_ephm_transport(S, #transport{socket    = OldSocket,
1481%%                                           ephm      = {sends, _MaxSends},
1482%%                                           ephm_info = _NumSends,
1483%%                                           port_info = PortInfo,
1484%%                                           opts      = Opts} = Transport,
1485%%                             _Type, _Sz) ->
1486%%     try
1487%%         begin
1488%%             {Socket, PortNo} = gen_udp_open(PortInfo, Opts),
1489%%             (catch gen_udp:close(OldSocket)),
1490%%             T2 = Transport#transport{socket    = Socket,
1491%%                                      ephm_info = 0,
1492%%                                      port_no   = PortNo},
1493%%             TS  = S#state.transports,
1494%%             TS2 = lists:keyreplace(OldSocket, #transport.socket, TS, T2),
1495%%             S#state{transports = TS2}
1496%%         end
1497%%     catch
1498%%         _:_:_ ->
1499%%             %% We need to identify which transport!
1500%%             error_msg("Failed creating new ephemeral socket for transport"),
1501%%             S
1502%%     end;
1503
1504%% %% Note that we do not currently handle inform:s, as that adds a whole
1505%% %% set of issues. See above for more info.
1506%% maybe_update_ephm_transport(S, #transport{socket    = Socket,
1507%%                                           ephm      = {data, MaxData},
1508%%                                           ephm_info = AccSent,
1509%%                                           port_info = _PortInfo,
1510%%                                           opts      = _Opts} = Transport,
1511%%                             _Type, Sz) when (MaxData > (AccSent + Sz)) ->
1512%%     T2 = Transport#transport{ephm_info = AccSent + Sz},
1513%%     TS  = S#state.transports,
1514%%     TS2 = lists:keyreplace(Socket, #transport.socket, TS, T2),
1515%%     S#state{transports = TS2};
1516%% maybe_update_ephm_transport(S, #transport{socket    = OldSocket,
1517%%                                           ephm      = {data, _MaxData},
1518%%                                           ephm_info = _AccSent,
1519%%                                           port_info = PortInfo,
1520%%                                           opts      = Opts} = Transport,
1521%%                             _Type, _Sz) ->
1522%%     try
1523%%         begin
1524%%             {Socket, PortNo} = gen_udp_open(PortInfo, Opts),
1525%%             (catch gen_udp:close(OldSocket)),
1526%%             T2 = Transport#transport{socket    = Socket,
1527%%                                      ephm_info = 0,
1528%%                                      port_no   = PortNo},
1529%%             TS  = S#state.transports,
1530%%             TS2 = lists:keyreplace(OldSocket, #transport.socket, TS, T2),
1531%%             S#state{transports = TS2}
1532%%         end
1533%%     catch
1534%%         _:_:_ ->
1535%%             %% We need to identify which transport!
1536%%             error_msg("Failed creating new ephemeral socket for transport"),
1537%%             S
1538%%     end;
1539
1540%% %% Note that we do not currently handle inform:s, as that adds a whole
1541%% %% set of issues. See above for more info.
1542%% maybe_update_ephm_transport(S, #transport{socket    = Socket,
1543%%                                           ephm      = {alive_time, AliveTime},
1544%%                                           ephm_info = undefined} = Transport,
1545%%                             _Type, _Sz) ->
1546%%     AliveEnd = erlang:monotonic_time(micro_seconds) + AliveTime,
1547%%     T2 = Transport#transport{ephm_info = AliveEnd},
1548%%     TS  = S#state.transports,
1549%%     TS2 = lists:keyreplace(Socket, #transport.socket, TS, T2),
1550%%     S#state{transports = TS2};
1551%% maybe_update_ephm_transport(S, #transport{socket    = OldSocket,
1552%%                                           ephm      = {alive_time, _AliveTime},
1553%%                                           ephm_info = AliveEnd,
1554%%                                           port_info = PortInfo,
1555%%                                           opts      = Opts} = Transport,
1556%%                             _Type, _Sz) ->
1557%%     TS = erlang:monotonic_time(micro_seconds),
1558%%     if
1559%%         (TS > AliveEnd) ->
1560%%             try
1561%%                 begin
1562%%                     {Socket, PortNo} = gen_udp_open(PortInfo, Opts),
1563%%                     (catch gen_udp:close(OldSocket)),
1564%%                     T2 = Transport#transport{socket    = Socket,
1565%%                                              %% This will be set when the transport
1566%%                                              %% is first used
1567%%                                              ephm_info = undefined,
1568%%                                              port_no   = PortNo},
1569%%                     TS  = S#state.transports,
1570%%                     TS2 = lists:keyreplace(OldSocket, #transport.socket, TS, T2),
1571%%                     S#state{transports = TS2}
1572%%                 end
1573%%             catch
1574%%                 _:_:_ ->
1575%%                     %% We need to identify which transport!
1576%%                     error_msg("Failed creating new ephemeral socket for transport"),
1577%%                     S
1578%%             end;
1579%%         true ->
1580%%             S
1581%%     end;
1582
1583%% maybe_update_ephm_transport(S, _Transport, _Type, _Sz) ->
1584%%     S.
1585
1586%% </EPHEMERAL-FOR-FUTUR-USE>
1587
1588
1589%% This function is used when logging has already been done!
1590maybe_udp_send_wo_log(
1591  #state{filter = FilterMod, transports = Transports},
1592  #transport{socket = Socket},
1593  To, Packet) ->
1594    {To_1, To_2} = fix_filter_address(Transports, To),
1595    case
1596	try FilterMod:accept_send(To_1, To_2)
1597	catch
1598	    Class:Exception:StackTrace ->
1599		error_msg(
1600		  "FilterMod:accept_send(~p, ~p) crashed: ~w:~w~n    ~p",
1601		  [To_1, To_2, Class, Exception, StackTrace]),
1602		true
1603	end
1604    of
1605	false ->
1606	    inc(netIfMsgOutDrops),
1607	    ok;
1608	Other ->
1609	    case Other of
1610		true ->
1611		    ok;
1612		_ ->
1613		    error_msg(
1614		      "FilterMod:accept_send(~p, ~p) returned: ~p",
1615		      [To_1,To_2,Other])
1616	    end,
1617	    udp_send(Socket, To, Packet)
1618    end.
1619
1620maybe_udp_send_w_log(
1621  #state{log = Log, filter = FilterMod, transports = Transports},
1622  #transport{socket = Socket},
1623  To, Pkg, LogPkg, Type) ->
1624    {To_1, To_2} = fix_filter_address(Transports, To),
1625    case
1626	try FilterMod:accept_send(To_1, To_2)
1627	catch
1628	    Class:Exception:StackTrace ->
1629		error_msg(
1630		  "FilterMod:accept_send(~p, ~p) crashed for: ~w:~w~n    ~p",
1631		  [To_1, To_2, Class, Exception, StackTrace]),
1632		true
1633	end
1634    of
1635	false ->
1636	    inc(netIfMsgOutDrops),
1637	    ok;
1638	Other ->
1639	    case Other of
1640		true ->
1641		    ok;
1642		_ ->
1643		    error_msg(
1644		      "FilterMod:accept_send(~p, ~p) returned: ~p",
1645		      [To_1, To_2, Other])
1646	    end,
1647	    log(Log, Type, LogPkg, To),
1648	    udp_send(Socket, To, Pkg)
1649    end.
1650
1651
1652udp_send(Socket, To, B) ->
1653    {IpAddr, IpPort} =
1654	case To of
1655	    {Domain, Addr} when is_atom(Domain) ->
1656		Addr;
1657	    {_, P} = Addr when is_integer(P) ->
1658		Addr
1659	end,
1660    try gen_udp:send(Socket, IpAddr, IpPort, B) of
1661	{error, emsgsize} ->
1662	    %% From this message we cannot recover, so exit sending loop
1663	    throw({emsgsize, sz(B)});
1664	{error, ErrorReason} ->
1665	    error_msg("[error] cannot send message "
1666		      "(destination: ~p:~p, size: ~p, reason: ~p)",
1667		      [IpAddr, IpPort, sz(B), ErrorReason]),
1668            ok;
1669	ok ->
1670            %% For future use! Ephemeral ports!
1671	    {ok, size(B)}
1672    catch
1673	error:ExitReason:StackTrace ->
1674	    error_msg("[exit] cannot send message "
1675		      "(destination: ~p:~p, size: ~p, reason: ~p, at: ~p)",
1676		      [IpAddr, IpPort, sz(B), ExitReason, StackTrace])
1677    end.
1678
1679sz(L) when is_list(L) -> length(L);
1680sz(B) when is_binary(B) -> size(B);
1681sz(_) -> undefined.
1682
1683
1684handle_disk_log(_Log, {wrap, NoLostItems}, State) ->
1685    ?vlog("Audit Trail Log - wrapped: ~w previously logged items where lost",
1686	  [NoLostItems]),
1687    State;
1688handle_disk_log(_Log, {truncated, NoLostItems}, State) ->
1689    ?vlog("Audit Trail Log - truncated: ~w items where lost when truncating",
1690	  [NoLostItems]),
1691    State;
1692handle_disk_log(_Log, full, State) ->
1693    error_msg("Failure to write to Audit Trail Log (full)", []),
1694    State;
1695handle_disk_log(_Log, {error_status, ok}, State) ->
1696    State;
1697handle_disk_log(_Log, {error_status, {error, Reason}}, State) ->
1698    error_msg("Error status received from Audit Trail Log: "
1699	      "~n~p", [Reason]),
1700    State;
1701handle_disk_log(_Log, _Info, State) ->
1702    State.
1703
1704
1705clear_reqs(Pid, S) ->
1706    NReqs  = lists:keydelete(Pid, 2, S#state.reqs),
1707    NReqs2 = NReqs -- [{0, Pid}], % ERIERL-427
1708    S#state{reqs = NReqs2}.
1709
1710
1711toname(P) when is_pid(P) ->
1712    case process_info(P, registered_name) of
1713	{registered_name, Name} ->
1714	    Name;
1715	_ ->
1716	    P
1717    end;
1718toname(Else) ->
1719    Else.
1720
1721
1722active_once(Sock) ->
1723    ?vtrace("activate once", []),
1724    ok = inet:setopts(Sock, [{active, once}]).
1725
1726
1727select_transport(_, []) ->
1728    false;
1729select_transport(ReqRef,
1730                 [#transport{req_refs = ReqRefs} = Transport | Transports]) ->
1731    case lists:member(ReqRef, ReqRefs) of
1732	true ->
1733	    Transport;
1734	false ->
1735	    select_transport(ReqRef, Transports)
1736    end.
1737
1738select_transport(snmpUDPDomain = Domain, Type, Transports) ->
1739    ?vtrace("select_transport -> entry with"
1740            "~n      Domain:     ~p"
1741            "~n      Type:       ~p"
1742            "~n      Transports: ~p",
1743            [Domain, Type, Transports]),
1744    case select_transport2(Domain, Type, Transports) of
1745	#transport{} = Transport ->
1746            ?vtrace("select_transport -> selected: "
1747                    "~n      ~p",
1748                    [Transport]),
1749 	    Transport;
1750 	false ->
1751 	    select_transport2(transportDomainUdpIpv4, Type, Transports)
1752    end;
1753select_transport(Domain, Type, Transports)
1754  when is_atom(Domain) ->
1755    ?vtrace("select_transport -> entry with"
1756            "~n      Domain:     ~p"
1757            "~n      Type:       ~p"
1758            "~n      Transports: ~p",
1759            [Domain, Type, Transports]),
1760    case select_transport2(Domain, Type, Transports) of
1761 	#transport{} = Transport ->
1762            ?vtrace("select_transport -> selected: "
1763                    "~n      ~p",
1764                    [Transport]),
1765 	    Transport;
1766 	false when Domain =:= transportDomainUdpIpv4 ->
1767            ?vdebug("select_transport -> (~p) not found: "
1768                    "~n      try ~p", [Domain, snmpUDPDomain]),
1769	    select_transport2(snmpUDPDomain, Type, Transports);
1770 	false ->
1771 	    false
1772    end.
1773
1774
1775%% Two kinds of (pdu) type that we (the agent) will ever attempt to send:
1776%%   req-responder: 'get-response'
1777%%   trap-sender:   'inform-request'   |
1778%%                  'snmpv2-trap'      |
1779%%                  report             |
1780%%                  trap (which is a 'fake' type (#trappdu{}))
1781select_transport2(_Domain, _Type,
1782                  []) ->
1783    false;
1784select_transport2(snmpUDPDomain = Domain, _Type,
1785                  [#transport{domain = Domain} = Transport|_Transports]) ->
1786    Transport;
1787select_transport2(snmpUDPDomain = Domain, Type,
1788                  [_|Transports]) ->
1789    select_transport2(Domain, Type, Transports);
1790select_transport2(Domain, Type,
1791                  [#transport{domain = Domain,
1792                              kind   = Kind} = Transport|_Transports])
1793  when ((Type =:= 'inform-request') orelse
1794        (Type =:= 'snmpv2-trap') orelse
1795        (Type =:= report) orelse
1796        (Type =:= trap) orelse
1797        (Type =:= trappdu)) andalso
1798       ((Kind =:= all) orelse (Kind =:= trap_sender)) ->
1799    Transport;
1800select_transport2(Domain, Type,
1801                  [#transport{domain = Domain,
1802                              kind   = Kind} = Transport|_Transports])
1803  when (Type =:= 'get-response') andalso
1804       ((Kind =:= all) orelse (Kind =:= req_responder)) ->
1805    Transport;
1806select_transport2(Domain, Type, [_|Transports]) ->
1807    select_transport2(Domain, Type, Transports).
1808
1809
1810
1811address_to_domain({Domain, _Addr}) when is_atom(Domain) ->
1812    Domain;
1813address_to_domain({_Ip, Port}) when is_integer(Port) ->
1814    snmpUDPDomain.
1815
1816%% If the agent uses legacy snmpUDPDomain e.g has not set
1817%% intAgentTransportDomain, then make sure
1818%% snmpa_network_interface_filter gets legacy arguments
1819%% to not break backwards compatibility.
1820%%
1821fix_filter_address(Transports, Address) ->
1822    case is_legacy_transports(Transports) of
1823	true ->
1824	    case Address of
1825		{Domain, Addr} when is_atom(Domain) ->
1826		    Addr;
1827		{_, IpPort} = Addr when is_integer(IpPort) ->
1828		    Addr
1829	    end;
1830	false ->
1831	    Address
1832    end.
1833
1834is_legacy_transports([#transport{domain = snmpUDPDomain}]) ->
1835    true;
1836is_legacy_transports([#transport{} | _]) ->
1837    false.
1838
1839fix_filter_legacy_mpd_address(Domain_Address_SecData) ->
1840    case Domain_Address_SecData of
1841	{{Domain, Addr}, _SecData} when is_atom(Domain) -> % v3
1842	    Addr;
1843	{Domain, Addr} when is_atom(Domain) -> % v1 & v2
1844	    Addr
1845    end.
1846
1847fix_filter_mpd_address(Domain_Address_SecData) ->
1848    case Domain_Address_SecData of
1849	{{Domain, _Addr} = Address, _SecData} when is_atom(Domain) -> % v3
1850	    Address;
1851	{Domain, _Addr} = Address when is_atom(Domain) -> % v1 & v2
1852	    Address
1853    end.
1854
1855%%%-----------------------------------------------------------------
1856
1857handle_set_log_type(#state{log = {Log, OldValue}} = State, NewType)
1858  when (Log /= undefined) ->
1859    NewValue =
1860	case NewType of
1861	    read ->
1862		[read];
1863	    write ->
1864		[write];
1865	    read_write ->
1866		[read,write];
1867	    _ ->
1868		throw({State, {error, {bad_atl_type, NewType}}})
1869	end,
1870    NewState = State#state{log = {Log, NewValue}},
1871    OldType =
1872	case {lists:member(read, OldValue),
1873	      lists:member(write, OldValue)} of
1874	    {true, true} ->
1875		read_write;
1876	    {true, false} ->
1877		read;
1878	    {false, true} ->
1879		write;
1880	    {false, false} ->
1881		throw({State, {error, {bad_atl_type, OldValue}}})
1882	end,
1883    {NewState, {ok, OldType}};
1884handle_set_log_type(State, _NewType) ->
1885    {State, {error, not_enabled}}.
1886
1887
1888handle_set_request_limit(#state{limit = OldLimit} = State, NewLimit)
1889  when ((is_integer(NewLimit) andalso (NewLimit >= 0)) orelse
1890	(NewLimit == infinity)) ->
1891    NewState = State#state{limit = NewLimit},
1892    {NewState, {ok, OldLimit}};
1893handle_set_request_limit(State, BadLimit) ->
1894    {State, {error, {bad_request_limit, BadLimit}}}.
1895
1896
1897%%%-----------------------------------------------------------------
1898%%% System messages
1899%%%-----------------------------------------------------------------
1900system_continue(_Parent, _Dbg, S) ->
1901    loop(S).
1902
1903system_terminate(Reason, _Parent, _Dbg, #state{log = Log}) ->
1904    ?vlog("system-terminate -> entry with"
1905	  "~n   Reason: ~p", [Reason]),
1906    do_close_log(Log),
1907    exit(Reason).
1908
1909system_code_change(OldState, _Module, _OldVsn, downgrade_to_pre_4_16) ->
1910    {OldLog, Type} = OldState#state.log,
1911    NewLog   = snmp_log:downgrade(OldLog),
1912    NewState = OldState#state{log = {NewLog, Type}},
1913    {ok, NewState};
1914
1915system_code_change(OldState, _Module, _OldVsn, upgrade_from_pre_4_16) ->
1916    Initial  = ?ATL_SEQNO_INITIAL,
1917    Max      = ?ATL_SEQNO_MAX,
1918    Module   = snmpa_agent,
1919    Function = increment_counter,
1920    Args     = [atl_seqno, Initial, Max],
1921    SeqNoGen = {Module, Function, Args},
1922    {OldLog, Type} = OldState#state.log,
1923    NewLog   = snmp_log:upgrade(OldLog, SeqNoGen),
1924    NewState = OldState#state{log = {NewLog, Type}},
1925    {ok, NewState};
1926
1927system_code_change(S, _Module, _OldVsn, _Extra) ->
1928    {ok, S}.
1929
1930do_close_log({undefined, []}) ->
1931    ok;
1932do_close_log({Log, _}) ->
1933    (catch snmp_log:sync(Log)),
1934    (catch snmp_log:close(Log)),
1935    ok;
1936do_close_log(_) ->
1937    ok.
1938
1939
1940%%%-----------------------------------------------------------------
1941%%% DEBUG FUNCTIONS
1942%%%-----------------------------------------------------------------
1943time_in_agent() ->
1944    erlang:monotonic_time(micro_seconds) - get(n1).
1945
1946%% ----------------------------------------------------------------
1947
1948pdu_type_of(#pdu{type = Type}) ->
1949    Type;
1950pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) ->
1951    trap.
1952
1953
1954%%-----------------------------------------------------------------
1955%% Counter functions
1956%%-----------------------------------------------------------------
1957
1958init_counters() ->
1959    F = fun(Counter) -> maybe_create_counter(Counter) end,
1960    lists:map(F, counters()).
1961
1962reset_counters() ->
1963    F = fun(Counter) -> init_counter(Counter) end,
1964    lists:map(F, counters()).
1965
1966maybe_create_counter(Counter) ->
1967    case ets:lookup(snmp_agent_table, Counter) of
1968	[_] -> ok;
1969	_ -> init_counter(Counter)
1970    end.
1971
1972init_counter(Counter) ->
1973    ets:insert(snmp_agent_table, {Counter, 0}).
1974
1975counters() ->
1976    [
1977     netIfMsgOutDrops,
1978     netIfMsgInDrops,
1979     netIfPduOutDrops,
1980     netIfPduInDrops
1981    ].
1982
1983inc(Name) ->
1984    ets:update_counter(snmp_agent_table, Name, 1).
1985
1986get_counters() ->
1987    Counters = counters(),
1988    get_counters(Counters, []).
1989
1990get_counters([], Acc) ->
1991    lists:reverse(Acc);
1992get_counters([Counter|Counters], Acc) ->
1993    case ets:lookup(snmp_agent_table, Counter) of
1994	[CounterVal] ->
1995	    get_counters(Counters, [CounterVal|Acc]);
1996	_ ->
1997	    get_counters(Counters, Acc)
1998    end.
1999
2000
2001%% ----------------------------------------------------------------
2002
2003%% This extracts the socket options from what is specified for
2004%% the transport, or if not, from the "global" socket options.
2005socket_opts(Domain, {IpAddr, PortInfo}, SocketOpts, DefaultOpts) ->
2006    ?vtrace("socket_opts -> entry with"
2007            "~n      Domain:      ~p"
2008            "~n      IpAddr:      ~p"
2009            "~n      PortInfo:    ~p"
2010            "~n      SocketOpts:  ~p"
2011            "~n      DefaultOpts: ~p",
2012            [Domain, IpAddr, PortInfo, SocketOpts, DefaultOpts]),
2013    Opts =
2014        [binary |
2015         case snmp_conf:tdomain_to_family(Domain) of
2016             inet6 = Family ->
2017                 [Family, {ipv6_v6only, true}];
2018             Family ->
2019                 [Family]
2020         end ++
2021         case get_bind_to_ip_address(SocketOpts, DefaultOpts) of
2022             true ->
2023                 [{ip, IpAddr}];
2024             _ ->
2025                 []
2026         end ++
2027         case get_no_reuse_address(SocketOpts, DefaultOpts) of
2028             false ->
2029                 [{reuseaddr, true}];
2030             _ ->
2031                 []
2032         end ++
2033         case get_recbuf(SocketOpts, DefaultOpts) of
2034             use_default ->
2035                 [];
2036             Sz ->
2037                 [{recbuf, Sz}]
2038         end ++
2039             case get_sndbuf(SocketOpts, DefaultOpts) of
2040                 use_default ->
2041                     [];
2042                 Sz ->
2043                     [{sndbuf, Sz}]
2044             end
2045        ] ++
2046        case get_extra_sock_opts(SocketOpts, DefaultOpts) of
2047            ESO when is_list(ESO) ->
2048                ESO;
2049            BadESO ->
2050                error_msg("Invalid 'extra socket options' (=> ignored):"
2051                          "~n   ~p", [BadESO]),
2052                []
2053        end,
2054    InetBackend =
2055        case get_inet_backend(SocketOpts, DefaultOpts) of
2056            use_default ->
2057                [];
2058            Backend when (Backend =:= inet) orelse (Backend =:= socket) ->
2059                [{inet_backend, Backend}]
2060        end,
2061    %% <EPHEMERAL-FOR-FUTUR-USE>
2062    %% Ephm = get_ephemeral(SocketOpts),
2063    %% {Ephm, PortInfo, Opts}.
2064    %% </EPHEMERAL-FOR-FUTUR-USE>
2065    {none, IpAddr, PortInfo, InetBackend, Opts}.
2066
2067
2068%% ----------------------------------------------------------------
2069
2070get_atl_type(Opts) ->
2071    case snmp_misc:get_option(type, Opts, read_write) of
2072	read_write ->
2073	    [read,write];
2074	write ->
2075	    [write];
2076	read ->
2077	    [read]
2078    end.
2079
2080get_atl_dir(Opts) ->
2081    snmp_misc:get_option(dir, Opts).
2082
2083get_atl_size(Opts) ->
2084    snmp_misc:get_option(size, Opts).
2085
2086get_atl_repair(Opts) ->
2087    snmp_misc:get_option(repair, Opts, true).
2088
2089get_atl_seqno(Opts) ->
2090    snmp_misc:get_option(seqno, Opts, false).
2091
2092get_verbosity(Opts) ->
2093    snmp_misc:get_option(verbosity, Opts, ?default_verbosity).
2094
2095get_vsns(Opts) ->
2096    snmp_misc:get_option(versions, Opts, [v1, v2, v3]).
2097
2098get_req_limit(O) ->
2099    snmp_misc:get_option(req_limit, O, infinity).
2100
2101get_filter_opts(O) ->
2102    snmp_misc:get_option(filter, O, []).
2103
2104get_filter_module(O) ->
2105    snmp_misc:get_option(module, O, ?DEFAULT_FILTER_MODULE).
2106
2107get_recbuf(Opts, DefaultOpts) ->
2108    get_socket_opt(recbuf, Opts, DefaultOpts, use_default).
2109
2110get_sndbuf(Opts, DefaultOpts) ->
2111    get_socket_opt(sndbuf, Opts, DefaultOpts, use_default).
2112
2113get_bind_to_ip_address(Opts, DefaultOpts) ->
2114    get_socket_opt(bind_to, Opts, DefaultOpts, false).
2115
2116get_no_reuse_address(Opts, DefaultOpts) ->
2117    get_socket_opt(no_reuse, Opts, DefaultOpts, false).
2118
2119get_extra_sock_opts(Opts, DefaultOpts) ->
2120    get_socket_opt(extra_sock_opts, Opts, DefaultOpts, []).
2121
2122get_inet_backend(Opts, DefaultOpts) ->
2123    get_socket_opt(inet_backend, Opts, DefaultOpts, use_default).
2124
2125%% <EPHEMERAL-FOR-FUTUR-USE>
2126%% This is not realy a socket option, but rather socket 'meta'
2127%% information. Its still put together with the actual socket
2128%% options.
2129%% get_ephemeral(SocketOpts) ->
2130%%     snmp_misc:get_option(ephemeral, SocketOpts, none).
2131%% </EPHEMERAL-FOR-FUTUR-USE>
2132
2133
2134get_socket_opt(Opt, Opts, DefaultOpts, DefaultVal) ->
2135    snmp_misc:get_option(Opt, Opts,
2136                         snmp_misc:get_option(Opt, DefaultOpts, DefaultVal)).
2137
2138
2139
2140%% ----------------------------------------------------------------
2141
2142%% error_msg(F) ->
2143%%     error_msg(F, []).
2144
2145error_msg(F, A) ->
2146    ?snmpa_error("NET-IF server: " ++ F, A).
2147
2148warning_msg(F, A) ->
2149    ?snmpa_warning("NET-IF server: " ++ F, A).
2150
2151info_msg(F,A) ->
2152    ?snmpa_info("NET-IF server: " ++ F, A).
2153
2154%% ---
2155
2156user_err(F, A) ->
2157    snmpa_error:user_err(F, A).
2158
2159config_err(F, A) ->
2160    snmpa_error:config_err(F, A).
2161
2162
2163%% ----------------------------------------------------------------
2164
2165call(Pid, Req) ->
2166    ReplyRef = make_ref(),
2167    Pid ! {Req, ReplyRef, self()},
2168    receive
2169	{ReplyRef, Reply, Pid} ->
2170	    Reply
2171    after 5000 ->
2172	    {error, timeout}
2173    end.
2174
2175
2176%% ----------------------------------------------------------------
2177
2178get_info(#state{transports = Transports, reqs = Reqs}) ->
2179    ProcSize = proc_mem(self()),
2180    Counters = get_counters(),
2181    [{reqs,           Reqs},
2182     {counters,       Counters},
2183     {process_memory, ProcSize},
2184     {transport_info, [#{tdomain        => Domain,
2185                         taddress       => {Address, PortNo},
2186                         transport_kind => Kind,
2187                         port_info      => PortInfo,
2188                         opts           => Opts,
2189                         socket_info    => get_socket_info(Socket),
2190                         num_reqs       => length(Refs)} ||
2191                          #transport{socket    = Socket,
2192                                     domain    = Domain,
2193                                     address   = Address,
2194                                     port_no   = PortNo,
2195                                     port_info = PortInfo,
2196                                     opts      = Opts,
2197                                     kind      = Kind,
2198                                     req_refs  = Refs} <- Transports]}].
2199
2200proc_mem(P) when is_pid(P) ->
2201    case (catch erlang:process_info(P, memory)) of
2202	{memory, Sz} when is_integer(Sz) ->
2203	    Sz;
2204	_ ->
2205	    undefined
2206    end.
2207%% proc_mem(_) ->
2208%%     undefined.
2209
2210get_socket_info(Id) when is_port(Id) ->
2211    Info = inet:info(Id),
2212    IfList =
2213	case (catch inet:getif(Id)) of
2214	    {ok, IFs} ->
2215		[{interfaces, IFs}];
2216	    _ ->
2217		[]
2218	end,
2219    BufSz =
2220	case (catch inet:getopts(Id, [recbuf, sndbuf])) of
2221	    {ok, Sz} ->
2222		[{buffer_size, Sz}];
2223	    _ ->
2224		[]
2225	end,
2226    [{socket, Id}, {info, Info}] ++ IfList ++ BufSz;
2227get_socket_info(Id) ->
2228    Info = inet:info(Id),
2229
2230    %% Does not exist for 'socket'
2231    IfList = [],
2232	%% case (catch inet:getif(Id)) of
2233	%%     {ok, IFs} ->
2234	%% 	[{interfaces, IFs}];
2235	%%     _ ->
2236	%% 	[]
2237	%% end,
2238
2239    BufSz =
2240	case (catch inet:getopts(Id, [recbuf, sndbuf])) of
2241	    {ok, Sz} ->
2242		[{buffer_size, Sz}];
2243	    _ ->
2244		[]
2245	end,
2246
2247    [{socket, Id}, {info, Info}] ++ IfList ++ BufSz.
2248
2249
2250
2251%% ---------------------------------------------------------------
2252