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