From dfb5cf6e2406486eeb7fbd89bf9118a6411bd5f2 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 26 Jun 2014 10:50:24 +0200 Subject: Rewrite agent for IPv4 + IPv6 --- lib/snmp/src/agent/snmp_framework_mib.erl | 86 ++- lib/snmp/src/agent/snmp_target_mib.erl | 12 +- lib/snmp/src/agent/snmpa_conf.erl | 93 +-- lib/snmp/src/agent/snmpa_net_if.erl | 1020 +++++++++++++++++++---------- lib/snmp/src/agent/snmpa_trap.erl | 145 ++-- lib/snmp/src/misc/snmp_conf.erl | 8 +- lib/snmp/src/misc/snmp_config.erl | 12 +- lib/snmp/test/klas3.erl | 1 + 8 files changed, 899 insertions(+), 478 deletions(-) diff --git a/lib/snmp/src/agent/snmp_framework_mib.erl b/lib/snmp/src/agent/snmp_framework_mib.erl index 4599f05f47..3b33cae3ec 100644 --- a/lib/snmp/src/agent/snmp_framework_mib.erl +++ b/lib/snmp/src/agent/snmp_framework_mib.erl @@ -41,7 +41,7 @@ -compile({no_auto_import,[error/1]}). -export([init/0, configure/1]). -export([intContextTable/1, intContextTable/3, - intAgentTransportDomain/1, + intAgentTransportDomain/1, intAgentTransports/1, intAgentUDPPort/1, intAgentIpAddress/1, snmpEngineID/1, snmpEngineBoots/1, @@ -137,8 +137,9 @@ read_agent(Dir) -> error({failed_reading_config_file, Dir, FileName, Reason}) end, Mand = - [{intAgentIpAddress, mandatory}, - {intAgentUDPPort, mandatory}, + [{intAgentTransports, mandatory}, +%%% {intAgentIpAddress, mandatory}, +%%% {intAgentUDPPort, mandatory}, {snmpEngineMaxMessageSize, mandatory}, {snmpEngineID, mandatory}], {ok, Conf} = snmp_conf:check_mandatory(Conf0, Mand), @@ -190,27 +191,63 @@ check_context(Context) -> %% Agent %% {Name, Value}. %%----------------------------------------------------------------- -check_agent({intAgentTransportDomain, D}, _Domain) -> - {snmp_conf:check_domain(D), D}; -check_agent({intAgentIpAddress = Tag, Value}, D) -> - Domain = - case D of - undefined -> - snmp_target_mib:default_domain(); - _ -> - D - end, - {case snmp_conf:check_ip(Domain, Value) of +check_agent(Entry, undefined) -> + check_agent(Entry, {snmp_target_mib:default_domain(), undefined}); +check_agent({intAgentTransportDomain, Domain}, {_, Port}) -> + {snmp_conf:check_domain(Domain), {Domain, Port}}; +check_agent({intAgentUDPPort, Port}, {Domain, _}) -> + ok = snmp_conf:check_port(Port), + {ok, {Domain, Port}}; +check_agent({intAgentIpAddress, _}, {_, undefined}) -> + error({missing_mandatory, intAgentUDPPort}); +check_agent({intAgentIpAddress = Tag, Ip} = Entry, {Domain, Port} = State) -> + {case snmp_conf:check_ip(Domain, Ip) of ok -> - ok; + [Entry, {intAgentTransports, [{Domain, {Ip, Port}}]}]; {ok, FixedIp} -> - {ok, {Tag, FixedIp}} - end, Domain}; -check_agent(Entry, Domain) -> - {check_agent(Entry), Domain}. + [{Tag, Ip}, {intAgentTransports, [{Domain, {FixedIp, Port}}]}] + end, State}; +check_agent({intAgentTransports = Tag, Transports}, {_, Port} = State) -> + CheckedTransports = + [case + case Port of + undefined -> + snmp_conf:check_address(Domain, Address); + _ -> + snmp_conf:check_address(Domain, Address, Port) + end + of + ok -> + Transport; + {ok, FixedAddress} -> + {Domain, FixedAddress} + end + || {Domain, Address} = Transport <- Transports], + {{ok, {Tag, CheckedTransports}}, State}; +check_agent(Entry, State) -> + {check_agent(Entry), State}. + +%%% XXX remove +%%% +%%% check_agent({intAgentTransportDomain, D}, _Domain) -> +%%% {snmp_conf:check_domain(D), D}; +%%% check_agent({intAgentIpAddress = Tag, Value}, D) -> +%%% Domain = +%%% case D of +%%% undefined -> +%%% snmp_target_mib:default_domain(); +%%% _ -> +%%% D +%%% end, +%%% {case snmp_conf:check_ip(Domain, Value) of +%%% ok -> +%%% ok; +%%% {ok, FixedIp} -> +%%% {ok, {Tag, FixedIp}} +%%% end, Domain}; +%%% check_agent(Entry, Domain) -> +%%% {check_agent(Entry), Domain}. -check_agent({intAgentUDPPort, Value}) -> - snmp_conf:check_integer(Value); %% This one is kept for backwards compatibility check_agent({intAgentMaxPacketSize, Value}) -> snmp_conf:check_packet_size(Value); @@ -224,7 +261,9 @@ check_agent(X) -> %% Ordering function to sort intAgentTransportDomain first %% hence before intAgentIpAddress. Sort other entries on the key. order_agent(EntryA, EntryB) -> - snmp_conf:keyorder(1, EntryA, EntryB, [intAgentTransportDomain | sort]). + snmp_conf:keyorder( + 1, EntryA, EntryB, + [intAgentTransportDomain, intAgentUDPPort | sort]). @@ -402,6 +441,9 @@ intAgentIpAddress(Op) -> intAgentTransportDomain(Op) -> snmp_generic:variable_func(Op, db(intAgentTransportDomain)). +intAgentTransports(Op) -> + snmp_generic:variable_func(Op, db(intAgentTransports)). + snmpEngineID(print) -> diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index 932829e150..e916f17d6a 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -174,7 +174,8 @@ check_target_addr( EngineId, TMask, MMS); check_target_addr( {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, - EngineId, TMask, MMS}) -> % Arity 10 + EngineId, TMask, MMS}) + when is_integer(Udp) -> % Arity 10 Domain = default_domain(), Address = {Ip, Udp}, check_target_addr( @@ -189,7 +190,8 @@ check_target_addr( EngineId); check_target_addr( {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, - EngineId}) -> % Arity 8 + EngineId}) % Arity 8 + when is_integer(Udp) -> Domain = default_domain(), Address = {Ip, Udp}, check_target_addr( @@ -202,7 +204,8 @@ check_target_addr( check_target_addr( Name, Domain, Address, Timeout, RetryCount, TagList, Params); check_target_addr( - {Name, Ip, Udp, Timeout, RetryCount, TagList, Params}) -> % Arity 7 + {Name, Ip, Udp, Timeout, RetryCount, TagList, Params}) % Arity 7 + when is_integer(Udp) -> Domain = default_domain(), Address = {Ip, Udp}, check_target_addr( @@ -216,7 +219,8 @@ check_target_addr( Name, Domain, Address, Timeout, RetryCount, TagList, Params, TMask, MMS); check_target_addr( {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, - TMask, MMS}) -> % Arity 9 + TMask, MMS}) % Arity 9 + when is_integer(Udp) -> Domain = default_domain(), Address = {Ip, Udp}, check_target_addr( diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index 2569b01b55..b4d32dc928 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -140,6 +140,8 @@ write_agent_conf(Fd, [H|T]) -> do_write_agent_conf(Fd, H), write_agent_conf(Fd, T). +do_write_agent_conf(Fd, {intAgentTransports = Tag, Val}) -> + io:format(Fd, "{~w, ~w}.~n", [Tag, Val]); do_write_agent_conf(Fd, {intAgentTransportDomain = Tag, Val}) -> io:format(Fd, "{~w, ~w}.~n", [Tag, Val]); do_write_agent_conf(Fd, {intAgentIpAddress = Tag, Val}) -> @@ -379,72 +381,35 @@ do_write_standard_conf(_Fd, Tag, Val) -> %% ------ target_addr.conf ------ %% -target_addr_entry(Name, - Ip, - TagList, - ParamsName, - EngineId) -> +target_addr_entry( + Name, Ip, TagList, ParamsName, EngineId) -> target_addr_entry(Name, Ip, TagList, ParamsName, EngineId, []). -target_addr_entry(Name, - Ip, - TagList, - ParamsName, - EngineId, - TMask) -> - target_addr_entry(Name, Ip, 162, TagList, - ParamsName, EngineId, - TMask, 2048). - -target_addr_entry(Name, - Ip, - Udp, - TagList, - ParamsName, - EngineId, - TMask, - MaxMessageSize) -> - target_addr_entry(Name, Ip, Udp, 1500, 3, TagList, - ParamsName, EngineId, - TMask, MaxMessageSize). - -target_addr_entry(Name, - Ip, - Udp, - Timeout, - RetryCount, - TagList, - ParamsName, - EngineId, - TMask, - MaxMessageSize) -> - target_addr_entry(Name, snmp_target_mib:default_domain(), Ip, Udp, - Timeout, RetryCount, TagList, - ParamsName, EngineId, - TMask, MaxMessageSize). - -target_addr_entry(Name, - Domain, - Ip, - Udp, - Timeout, - RetryCount, - TagList, - ParamsName, - EngineId, - TMask, - MaxMessageSize) -> - {Name, - Domain, - Ip, - Udp, - Timeout, - RetryCount, - TagList, - ParamsName, - EngineId, - TMask, - MaxMessageSize}. +target_addr_entry( + Name, Ip, TagList, ParamsName, + EngineId, TMask) -> + target_addr_entry( + Name, Ip, 162, TagList, ParamsName, + EngineId, TMask, 2048). + +target_addr_entry( + Name, Domain_or_Ip, Addr_or_Port, TagList, + ParamsName, EngineId, TMask, MaxMessageSize) -> + target_addr_entry( + Name, Domain_or_Ip, Addr_or_Port, 1500, 3, TagList, + ParamsName, EngineId, TMask, MaxMessageSize). + +target_addr_entry( + Name, Domain_or_Ip, Addr_or_Port, Timeout, RetryCount, TagList, + ParamsName, EngineId, TMask, MaxMessageSize) -> + {Name, Domain_or_Ip, Addr_or_Port, Timeout, RetryCount, TagList, + ParamsName, EngineId, TMask, MaxMessageSize}. + +target_addr_entry( + Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, + ParamsName, EngineId,TMask, MaxMessageSize) -> + {Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, + ParamsName, EngineId, TMask, MaxMessageSize}. write_target_addr_config(Dir, Conf) -> diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl index 875ea19097..f5fd377341 100644 --- a/lib/snmp/src/agent/snmpa_net_if.erl +++ b/lib/snmp/src/agent/snmpa_net_if.erl @@ -35,19 +35,27 @@ -include("snmp_debug.hrl"). -include("snmp_verbosity.hrl"). --record(state, {parent, - note_store, - master_agent, - usock, - usock_opts, - mpd_state, - log, - reqs = [], - debug = false, - limit = infinity, - rcnt = [], - filter, - domain = snmpUDPDomain}). +-record(state, + {parent, + note_store, + master_agent, + transports = [], +%% usock, +%% usock_opts, + mpd_state, + log, + reqs = [], + debug = false, + limit = infinity, +%% rcnt = [], + filter}). +%% domain = snmpUDPDomain}). + +-record(transport, + {socket, + domain = snmpUDPDomain, + opts = [], + req_refs = []}). -ifndef(default_verbosity). -define(default_verbosity,silence). @@ -105,21 +113,27 @@ get_request_limit(Pid) -> set_request_limit(Pid, NewLimit) -> call(Pid, {set_request_limit, NewLimit}). -get_port() -> - {value, UDPPort} = snmp_framework_mib:intAgentUDPPort(get), - UDPPort. +get_transports() -> + {value, Transports} = snmp_framework_mib:intAgentTransports(get), + Transports. -get_address() -> - {value, IPAddress} = snmp_framework_mib:intAgentIpAddress(get), - IPAddress. - -get_domain() -> - case snmp_framework_mib:intAgentTransportDomain(get) of - {value, Domain} -> - Domain; - genErr -> - snmpUDPDomain - end. +%%% XXX remove +%%% +%%% get_ip_port() -> +%%% {value, UDPPort} = snmp_framework_mib:intAgentUDPPort(get), +%%% UDPPort. +%%% +%%% get_address() -> +%%% {value, IPAddress} = snmp_framework_mib:intAgentIpAddress(get), +%%% IPAddress. +%%% +%%% get_domain() -> +%%% case snmp_framework_mib:intAgentTransportDomain(get) of +%%% {value, Domain} -> +%%% Domain; +%%% genErr -> +%%% snmpUDPDomain +%%% end. filter_reset(Pid) -> Pid ! filter_reset. @@ -170,13 +184,15 @@ do_init(Prio, NoteStore, MasterAgent, Parent, Opts) -> put(verbosity,get_verbosity(Opts)), ?vlog("starting",[]), - %% -- Port and address -- - Domain = get_domain(), - ?vdebug("domain: ~w",[Domain]), - UDPPort = get_port(), - ?vdebug("port: ~w",[UDPPort]), - IPAddress = get_address(), - ?vdebug("addr: ~w",[IPAddress]), +%%% XXX remove +%%% +%%% %% -- Port and address -- +%%% Domain = get_domain(), +%%% ?vdebug("domain: ~w",[Domain]), +%%% UDPPort = get_ip_port(), +%%% ?vdebug("port: ~w",[UDPPort]), +%%% IPAddress = get_address(), +%%% ?vdebug("addr: ~w",[IPAddress]), %% -- Versions -- Vsns = get_vsns(Opts), @@ -193,37 +209,73 @@ do_init(Prio, NoteStore, MasterAgent, Parent, Opts) -> Log = create_log(), ?vdebug("Log: ~w",[Log]), - - %% -- Socket -- - IPOpts1 = ip_opt_bind_to_ip_address(Opts, IPAddress), - IPOpts2 = ip_opt_no_reuse_address(Opts), - IPOpts3 = ip_opt_recbuf(Opts), - IPOpts4 = ip_opt_sndbuf(Opts), - IPOpts = - [binary, snmp_conf:tdomain_to_family(Domain) - | IPOpts1 ++ IPOpts2 ++ IPOpts3 ++ IPOpts4], - case gen_udp_open(UDPPort, IPOpts) of - {ok, Sock} -> + DomainAddresses = get_transports(), + ?vdebug("DomainAddresses: ~w",[DomainAddresses]), + try + [begin + SocketOpts = socket_opts(Domain, Address, Opts), + Socket = socket_open(Domain, SocketOpts), + active_once(Socket), + #transport{ + socket = Socket, + domain = Domain, + opts = SocketOpts} + end || {Domain, Address} <- DomainAddresses] + of + [] -> + ?vinfo("No transports configured: ~p", [DomainAddresses]), + {error, {no_transports,DomainAddresses}}; + Transports -> MpdState = snmpa_mpd:init(Vsns), - init_counters(), - active_once(Sock), + init_counters(), S = #state{parent = Parent, note_store = NoteStore, master_agent = MasterAgent, mpd_state = MpdState, - usock = Sock, - usock_opts = IPOpts, + transports = Transports, log = Log, limit = Limit, - filter = FilterMod, - domain = Domain}, + filter = FilterMod}, ?vdebug("started with MpdState: ~p", [MpdState]), - {ok, S}; - {error, Reason} -> - ?vinfo("Failed to open UDP socket: ~p", [Reason]), - {error, {udp_open, UDPPort, Reason}} + {ok, S} + catch + Error -> + ?vinfo("Failed to initialize socket(s): ~p", [Error]), + {error, Error} end. +%%% XXX remove +%%% +%%% %% -- Socket -- +%%% IPOpts1 = ip_opt_bind_to_ip_address(Opts, IPAddress), +%%% IPOpts2 = ip_opt_no_reuse_address(Opts), +%%% IPOpts3 = ip_opt_recbuf(Opts), +%%% IPOpts4 = ip_opt_sndbuf(Opts), +%%% IPOpts = +%%% [binary, snmp_conf:tdomain_to_family(Domain) +%%% | IPOpts1 ++ IPOpts2 ++ IPOpts3 ++ IPOpts4], +%%% case gen_udp_open(UDPPort, IPOpts) of +%%% {ok, Sock} -> +%%% MpdState = snmpa_mpd:init(Vsns), +%%% init_counters(), +%%% active_once(Sock), +%%% S = #state{parent = Parent, +%%% note_store = NoteStore, +%%% master_agent = MasterAgent, +%%% mpd_state = MpdState, +%%% usock = Sock, +%%% usock_opts = IPOpts, +%%% log = Log, +%%% limit = Limit, +%%% filter = FilterMod, +%%% domain = Domain}, +%%% ?vdebug("started with MpdState: ~p", [MpdState]), +%%% {ok, S}; +%%% {error, Reason} -> +%%% ?vinfo("Failed to open UDP socket: ~p", [Reason]), +%%% {error, {udp_open, UDPPort, Reason}} +%%% end. + create_log() -> case ets:lookup(snmp_agent_table, audit_trail_log) of @@ -298,33 +350,63 @@ format_address(Address) -> iolist_to_binary(snmp_conf:mk_addr_string(Address)). - -gen_udp_open(Port, Opts) -> +socket_open(snmpUDPDomain = Domain, [IpPort | Opts]) -> case init:get_argument(snmp_fd) of {ok, [[FdStr]]} -> Fd = list_to_integer(FdStr), - ?vdebug("gen_udp_open(~p, ~p) Fd: ~p",[Port,Opts,Fd]), - gen_udp:open(0, [{fd, Fd}|Opts]); + ?vdebug("socket_open(~p, [~p | ~p]) Fd: ~p", + [Domain, IpPort, Opts, Fd]), + gen_udp_open(IpPort, [{fd, Fd} | Opts]); error -> case init:get_argument(snmpa_fd) of {ok, [[FdStr]]} -> Fd = list_to_integer(FdStr), - ?vdebug("gen_udp_open(~p, ~p) Fd: ~p",[Port,Opts,Fd]), - gen_udp:open(0, [{fd, Fd}|Opts]); + ?vdebug("socket_open(~p, [~p | ~p]) Fd: ~p", + [Domain, IpPort, Opts, Fd]), + gen_udp_open(IpPort, [{fd, Fd} | Opts]); error -> - ?vdebug("gen_udp_open(~p, ~p)",[Port,Opts]), - gen_udp:open(Port, Opts) + ?vdebug("socket_open(~p, [~p | ~p])", + [Domain, IpPort, Opts]), + gen_udp_open(IpPort, Opts) end + end; +socket_open(Domain, [IpPort | Opts]) + when Domain =:= transportDomainUdpIpv4; + Domain =:= transportDomainUdpIpv6 -> + ?vdebug("socket_open(~p, [~p | ~p])", [Domain, IpPort, Opts]), + gen_udp_open(IpPort, Opts); +socket_open(Domain, Opts) -> + throw({socket_open, Domain, Opts}). + +gen_udp_open(IpPort, Opts) -> + case gen_udp:open(IpPort, Opts) of + {ok, Socket} -> + Socket; + {error, Reason} -> + throw({udp_open, IpPort, Reason}) end. -loop(#state{domain = Domain} = S) -> + +loop(#state{transports = Transports, limit = Limit, parent = Parent} = S) -> + ?vdebug("loop(~p)", [S]), receive - {udp, _UdpId, Ip, Port, Packet} -> - ?vlog("got paket from ~w:~w",[Ip,Port]), - From = fix_filter_address(Domain, {Domain, {Ip, Port}}), - NewS = maybe_handle_recv(S, From, Packet), - loop(NewS); + {udp, Socket, IpAddr, IpPort, Packet} = Msg when is_port(Socket) -> + ?vlog("got paket from ~w:~w on ~w", [IpAddr, IpPort, Socket]), + case lists:keyfind(Socket, #transport.socket, Transports) of + #transport{socket = Socket, domain = Domain} = Transport -> + From = + case Domain of + snmpUDPDomain -> + {IpAddr, IpPort}; + _ -> + {Domain, {IpAddr, IpPort}} + end, + loop(maybe_handle_recv(S, Transport, From, Packet)); + false -> + error_msg("Packet on unknown port: ~p", [Msg]), + loop(S) + end; {info, ReplyRef, Pid} -> Info = get_info(S), @@ -332,12 +414,35 @@ loop(#state{domain = Domain} = S) -> loop(S); %% response (to get/get_next/get_bulk/set requests) - {snmp_response, Vsn, RePdu, Type, ACMData, To, []} -> + {snmp_response, Vsn, RePdu, Type, ACMData, To, Extra} -> ?vlog("reply pdu: " "~n ~s", [?vapply(snmp_misc, format, [256, "~w", [RePdu]])]), - NewS = maybe_handle_reply_pdu(S, Vsn, RePdu, Type, ACMData, To), - loop(NewS); + {_, ReqRef} = lists:keyfind(request_ref, 1, Extra), + case + case + (Limit =/= infinity) andalso + select_transport_from_req_ref(ReqRef, Transports) + of + false -> + select_transport_from_domain( + address_to_domain(To), + Transports); + T -> + T + end + of + false -> + error_msg( + "Can not find transport for response PDU to: ~s", + [format_address(To)]), + loop(S); + Transport -> + NewS = update_req_counter_outgoing(S, Transport, ReqRef), + maybe_handle_reply_pdu( + NewS, Transport, Vsn, RePdu, Type, ACMData, To), + loop(NewS) + end; %% Traps/notification {send_pdu, Vsn, Pdu, MsgData, TDomAddrs} -> @@ -406,15 +511,34 @@ loop(#state{domain = Domain} = S) -> NewS = handle_send_discovery(S, Pdu, MsgData, To, From), loop(NewS); - {discarded_pdu, _Vsn, ReqId, _ACMData, Variable, _Extra} -> - ?vdebug("discard PDU: ~p", [Variable]), + {discarded_pdu, _Vsn, ReqId, _ACMData, Variable, Extra} -> + ?vdebug("discard PDU: ~p - ~p - ~p", + [Variable, Extra, Transports]), snmpa_mpd:discarded_pdu(Variable), - NewS = update_req_counter_outgoing(S, ReqId), - loop(NewS); + {_, ReqRef} = lists:keyfind(request_ref, 1, Extra), + if + Limit =:= infinity -> + %% The incoming PDU was not registered + loop(update_req_counter_outgoing(S, false, ReqRef)); + true -> + case + select_transport_from_req_ref(ReqRef, Transports) + of + false -> + error_msg( + "Can not find transport for discarded PDU: ~p", + [ReqId]), + loop(S); + Transport -> + loop( + update_req_counter_outgoing( + S, Transport, ReqRef)) + end + end; {get_log_type, ReplyRef, Pid} -> ?vdebug("get log type: ~p", []), - #state{log = {_, LogType}} = S, + {_, LogType} = S#state.log, Pid ! {ReplyRef, {ok, LogType}, self()}, loop(S); @@ -426,7 +550,6 @@ loop(#state{domain = Domain} = S) -> {get_request_limit, ReplyRef, Pid} -> ?vdebug("get request limit: ~p", []), - #state{limit = Limit} = S, Pid ! {ReplyRef, {ok, Limit}, self()}, loop(S); @@ -450,36 +573,50 @@ loop(#state{domain = Domain} = S) -> reset_counters(), loop(S); - {'EXIT', Parent, Reason} when Parent == S#state.parent -> + {'EXIT', Parent, Reason} -> ?vlog("parent (~p) exited: " "~n ~p", [Parent, Reason]), exit(Reason); - {'EXIT', Port, Reason} when Port == S#state.usock -> - UDPPort = get_port(), - NewS = - case gen_udp_open(UDPPort, S#state.usock_opts) of - {ok, Id} -> - error_msg("Port ~p exited for reason" - "~n ~p" - "~n Re-opened (~p)", [Port, Reason, Id]), - S#state{usock = Id}; - {error, ReopenReason} -> - error_msg("Port ~p exited for reason" - "~n ~p" - "~n Re-open failed with reason" - "~n ~p", - [Port, Reason, ReopenReason]), - ok - end, - loop(NewS); - - {'EXIT', Port, Reason} when is_port(Port) -> - error_msg("Exit message from port ~p for reason ~p~n", - [Port, Reason]), - loop(S); + {'EXIT', Socket, Reason} when is_port(Socket) -> + case lists:keyfind(Socket, #transport.socket, Transports) of + #transport{ + socket = Socket, + domain = Domain, + opts = SocketOpts, + req_refs = ReqRefs} = Transport -> + try socket_open(Domain, SocketOpts) of + NewSocket -> + error_msg( + "Socket ~p exited for reason" + "~n ~p" + "~n Re-opened (~p)", + [Socket, Reason, NewSocket]), + (length(ReqRefs) < Limit) andalso + active_once(NewSocket), + S#state{ + transports = + lists:keyreplace( + Socket, #transport.socket, Transports, + Transport#transport{socket = NewSocket})} + catch + ReopenReason -> + error_msg( + "Socket ~p exited for reason" + "~n ~p" + "~n Re-open failed with reason" + "~n ~p", + [Socket, Reason, ReopenReason]), + exit(ReopenReason) + end; + false -> + error_msg( + "Exit message from port ~p for reason ~p~n", + [Socket, Reason]), + loop(S) + end; - {'EXIT', Pid, Reason} -> + {'EXIT', Pid, Reason} when is_pid(Pid) -> ?vlog("~p exited: " "~n ~p", [Pid, Reason]), NewS = clear_reqs(Pid, S), @@ -487,71 +624,144 @@ loop(#state{domain = Domain} = S) -> {system, From, Msg} -> ?vdebug("system event ~p from ~p", [Msg, From]), - sys:handle_system_msg(Msg, From, S#state.parent, ?MODULE, [], S); + sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], S); _ -> loop(S) end. -update_req_counter_incomming(#state{limit = infinity, usock = Sock} = S, _) -> - active_once(Sock), %% No limit so activate directly +update_req_counter_incoming( + #state{limit = infinity} = S, + #transport{socket = Socket}, + _ReqRef) -> + active_once(Socket), %% No limit so activate directly S; -update_req_counter_incomming(#state{limit = Limit, - rcnt = RCnt, - usock = Sock} = S, Rid) - when length(RCnt) + 1 == Limit -> +update_req_counter_incoming( + #state{limit = Limit} = S, + #transport{socket = Socket, req_refs = ReqRefs} = T, + ReqRef) when length(ReqRefs) + 1 >= Limit -> %% Ok, one more and we are at the limit. %% Just make sure we are not already processing this one... - case lists:member(Rid, RCnt) of + case lists:member(ReqRef, ReqRefs) of false -> %% We are at the limit, do _not_ activate socket - S#state{rcnt = [Rid|RCnt]}; + update_transport_req_refs(S, T, [ReqRef | ReqRefs]); true -> - active_once(Sock), + active_once(Socket), S end; -update_req_counter_incomming(#state{rcnt = RCnt, - usock = Sock} = S, Rid) -> - active_once(Sock), - case lists:member(Rid, RCnt) of +update_req_counter_incoming( + #state{} = S, + #transport{socket = Socket, req_refs = ReqRefs} = T, + ReqRef) -> + active_once(Socket), + case lists:member(ReqRef, ReqRefs) of false -> - S#state{rcnt = [Rid|RCnt]}; + update_transport_req_refs(S, T, [ReqRef | ReqRefs]); true -> S end. - -update_req_counter_outgoing(#state{limit = infinity} = S, _Rid) -> +update_transport_req_refs( + #state{transports = Transports} = S, + #transport{socket = Socket} = T, + ReqRefs) -> + S#state{ + transports = + lists:keyreplace( + Socket, #transport.socket, Transports, + T#transport{req_refs = ReqRefs})}. + +%%% XXX remove +%%% +%%% update_req_counter_incoming(#state{limit = infinity, usock = Sock} = S, _) -> +%%% active_once(Sock), %% No limit so activate directly +%%% S; +%%% update_req_counter_incoming(#state{limit = Limit, +%%% rcnt = RCnt, +%%% usock = Sock} = S, Rid) +%%% when length(RCnt) + 1 >= Limit -> +%%% %% Ok, one more and we are at the limit. +%%% %% Just make sure we are not already processing this one... +%%% case lists:member(Rid, RCnt) of +%%% false -> +%%% %% We are at the limit, do _not_ activate socket +%%% S#state{rcnt = [Rid|RCnt]}; +%%% true -> +%%% active_once(Sock), +%%% S +%%% end; +%%% update_req_counter_incoming(#state{rcnt = RCnt, +%%% usock = Sock} = S, Rid) -> +%%% active_once(Sock), +%%% case lists:member(Rid, RCnt) of +%%% false -> +%%% S#state{rcnt = [Rid|RCnt]}; +%%% true -> +%%% S +%%% end. + + +update_req_counter_outgoing( + #state{limit = infinity} = S, + _Transport, _ReqRef) -> %% Already activated (in the incoming function) S; -update_req_counter_outgoing(#state{limit = Limit, - rcnt = RCnt, - usock = Sock} = S, Rid) - when length(RCnt) == Limit -> - ?vtrace("handle_req_counter_outgoing(~w) -> entry with" - "~n Rid: ~w" - "~n length(RCnt): ~w", [Limit, Rid, length(RCnt)]), - case lists:delete(Rid, RCnt) of - NewRCnt when length(NewRCnt) < Limit -> +update_req_counter_outgoing( + #state{limit = Limit, transports = Transports} = S, + #transport{socket = Socket, req_refs = ReqRefs} = Transport, + ReqRef) -> + LengthReqRefs = length(ReqRefs), + ?vtrace("update_req_counter_outgoing() -> entry with~n" + " Limit: ~w~n" + " ReqRef: ~w~n" + " length(ReqRefs): ~w", [Limit, ReqRef, LengthReqRefs]), + NewReqRefs = lists:delete(ReqRef, ReqRefs), + (LengthReqRefs >= Limit) andalso (length(NewReqRefs) < Limit) andalso + begin ?vtrace("update_req_counter_outgoing -> " - "passed below limit: activate", []), - active_once(Sock), - S#state{rcnt = NewRCnt}; - _ -> - S - end; -update_req_counter_outgoing(#state{limit = Limit, rcnt = RCnt} = S, - Rid) -> - ?vtrace("handle_req_counter_outgoing(~w) -> entry with" - "~n Rid: ~w" - "~n length(RCnt): ~w", [Limit, Rid, length(RCnt)]), - NewRCnt = lists:delete(Rid, RCnt), - S#state{rcnt = NewRCnt}. + "passed below limit: activate", []), + active_once(Socket) + end, + S#state{ + transports = + snmp_misc:keyreplace( + Socket, #transport.socket, Transports, + Transport#transport{req_refs = NewReqRefs})}. + +%%% XXX remove +%%% +%%% update_req_counter_outgoing( +%%% #state{limit = Limit, +%%% rcnt = RCnt, +%%% usock = Sock} = S, Rid) +%%% when length(RCnt) >= Limit -> +%%% ?vtrace("handle_req_counter_outgoing(~w) -> entry with" +%%% "~n Rid: ~w" +%%% "~n length(RCnt): ~w", [Limit, Rid, length(RCnt)]), +%%% case lists:delete(Rid, RCnt) of +%%% NewRCnt when length(NewRCnt) < Limit -> +%%% ?vtrace("update_req_counter_outgoing -> " +%%% "passed below limit: activate", []), +%%% active_once(Sock), +%%% S#state{rcnt = NewRCnt}; +%%% _ -> +%%% S +%%% end; +%%% update_req_counter_outgoing(#state{limit = Limit, rcnt = RCnt} = S, +%%% Rid) -> +%%% ?vtrace("handle_req_counter_outgoing(~w) -> entry with" +%%% "~n Rid: ~w" +%%% "~n length(RCnt): ~w", [Limit, Rid, length(RCnt)]), +%%% NewRCnt = lists:delete(Rid, RCnt), +%%% S#state{rcnt = NewRCnt}. maybe_handle_recv( - #state{usock = Sock, filter = FilterMod} = S, From, Packet) -> + #state{filter = FilterMod} = S, + #transport{socket = Socket} = Transport, + From, Packet) -> {From_1, From_2} = From, case try FilterMod:accept_recv(From_1, From_2) @@ -566,7 +776,7 @@ maybe_handle_recv( false -> %% Drop the received packet inc(netIfMsgInDrops), - active_once(Sock), + active_once(Socket), S; Other -> case Other of @@ -577,28 +787,13 @@ maybe_handle_recv( "FilterMod:accept_recv(~p, ~p) returned: ~p", [From_1,From_2,Other]) end, - handle_recv(S, From, Packet) - end. - -handle_discovery_response(_From, #pdu{request_id = ReqId} = Pdu, - ManagerEngineId, - #state{usock = Sock, reqs = Reqs} = S) -> - case lists:keysearch(ReqId, 1, S#state.reqs) of - {value, {_, Pid}} -> - active_once(Sock), - Pid ! {snmp_discovery_response_received, Pdu, ManagerEngineId}, - NReqs = lists:keydelete(ReqId, 1, Reqs), - S#state{reqs = NReqs}; - _ -> - %% Ouch, timeout? resend? - S + handle_recv(S, Transport, From, Packet) end. handle_recv( - #state{usock = Sock, - mpd_state = MpdState, - note_store = NS, - log = Log} = S, From, Packet) -> + #state{mpd_state = MpdState, note_store = NS, log = Log} = S, + #transport{socket = Socket} = Transport, + From, Packet) -> put(n1, erlang:now()), LogF = fun(Type, Data) -> @@ -607,48 +802,72 @@ handle_recv( case (catch snmpa_mpd:process_packet( Packet, From, MpdState, NS, LogF)) of {ok, _Vsn, Pdu, _PduMS, {discovery, ManagerEngineId}} -> - handle_discovery_response(From, Pdu, ManagerEngineId, S); + handle_discovery_response( + S, Transport, From, Pdu, ManagerEngineId); {ok, _Vsn, Pdu, _PduMS, discovery} -> - handle_discovery_response(From, Pdu, undefined, S); + handle_discovery_response( + S, Transport, From, Pdu, undefined); {ok, Vsn, Pdu, PduMS, ACMData} -> ?vlog("got pdu ~s", [?vapply(snmp_misc, format, [256, "~w", [Pdu]])]), - %% handle_recv_pdu(From, Vsn, Pdu, PduMS, ACMData, S); - maybe_handle_recv_pdu(From, Vsn, Pdu, PduMS, ACMData, S); + %% handle_recv_pdu(S, Transport, From, Vsn, Pdu, PduMS, ACMData); + maybe_handle_recv_pdu( + S, Transport, From, Vsn, Pdu, PduMS, ACMData); {discarded, Reason} -> ?vlog("packet discarded for reason: ~s", [?vapply(snmp_misc, format, [256, "~w", [Reason]])]), - active_once(Sock), + active_once(Socket), S; {discarded, Reason, ReportPacket} -> ?vlog("sending report for reason: " "~n ~s", [?vapply(snmp_misc, format, [256, "~w", [Reason]])]), - (catch udp_send(S#state.usock, From, ReportPacket)), - active_once(Sock), + (catch udp_send(Socket, From, ReportPacket)), + active_once(Socket), S; {discovery, ReportPacket} -> ?vlog("sending discovery report", []), - (catch udp_send(S#state.usock, From, ReportPacket)), - active_once(Sock), + (catch udp_send(Socket, From, ReportPacket)), + active_once(Socket), S; Error -> error_msg("processing of received message failed: " "~n ~p", [Error]), - active_once(Sock), + active_once(Socket), + S + end. + +handle_discovery_response( + #state{reqs = Reqs} = S, + #transport{socket = Socket}, + _From, + #pdu{request_id = ReqId} = Pdu, + ManagerEngineId) -> + case lists:keyfind(ReqId, 1, S#state.reqs) of + {ReqId, Pid} -> + active_once(Socket), + Pid ! {snmp_discovery_response_received, Pdu, ManagerEngineId}, + %% XXX Strange... Reqs from this Pid should be reaped + %% at process exit by clear_reqs/2 so the following + %% should be redundant. + NReqs = lists:keydelete(ReqId, 1, Reqs), + S#state{reqs = NReqs}; + false -> + %% Ouch, timeout? resend? S end. maybe_handle_recv_pdu( + #state{filter = FilterMod} = S, + #transport{socket = Socket} = Transport, From, Vsn, - #pdu{type = Type} = Pdu, PduMS, ACMData, - #state{usock = Sock, filter = FilterMod} = S) -> + #pdu{type = Type} = Pdu, PduMS, ACMData) -> {From_1, From_2} = From, case try FilterMod:accept_recv_pdu(From_1, From_2, Type) @@ -664,8 +883,8 @@ maybe_handle_recv_pdu( of false -> inc(netIfPduInDrops), - active_once(Sock), - ok; + active_once(Socket), + S; Other -> case Other of true -> @@ -675,39 +894,72 @@ maybe_handle_recv_pdu( "FilterMod:accept_recv_pdu(~p, ~p, ~p) returned: ~p", [From_1,From_2,Type,Other]) end, - handle_recv_pdu(From, Vsn, Pdu, PduMS, ACMData, S) + handle_recv_pdu(S, Transport, From, Vsn, Pdu, PduMS, ACMData) end. handle_recv_pdu( + #state{reqs = Reqs} = S, + #transport{socket = Socket}, From, Vsn, - #pdu{type = 'get-response'} = Pdu, _PduMS, _ACMData, - #state{usock = Sock} = S) -> - active_once(Sock), - handle_response(Vsn, Pdu, From, S), + #pdu{type = 'get-response', request_id = ReqId} = Pdu, + _PduMS, _ACMData) -> + active_once(Socket), + case lists:keyfind(ReqId, 1, Reqs) of + {ReqId, Pid} -> + ?vdebug("handle_recv_pdu -> " + "~n send response to receiver ~p", [Pid]), + Pid ! {snmp_response_received, Vsn, Pdu, From}; + false -> + ?vdebug("handle_recv_pdu -> " + "~n No receiver available for response pdu", []) + end, S; -handle_recv_pdu(From, Vsn, #pdu{request_id = Rid, type = Type} = Pdu, - PduMS, ACMData, #state{master_agent = Pid} = S) - when ((Type =:= 'get-request') orelse - (Type =:= 'get-next-request') orelse - (Type =:= 'get-bulk-request')) -> - ?vtrace("handle_recv_pdu -> received get (~w)", [Type]), - Pid ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, []}, - update_req_counter_incomming(S, Rid); -handle_recv_pdu(From, Vsn, Pdu, PduMS, ACMData, - #state{usock = Sock, master_agent = Pid} = S) -> +%%% XXX remove +%%% +%%% handle_recv_pdu( +%%% #state{} = S, +%%% #transport{socket = Socket} = Transport, +%%% From, Vsn, +%%% #pdu{type = 'get-response'} = Pdu, +%%% _PduMS, _ACMData) -> +%%% active_once(Socket), +%%% handle_response(S, Pdu, From, Vsn), +%%% S; +handle_recv_pdu( + #state{master_agent = Pid} = S, + #transport{} = Transport, + From, Vsn, + #pdu{type = Type} = Pdu, + PduMS, ACMData) + when Type =:= 'set-request'; + Type =:= 'get-request'; + Type =:= 'get-next-request'; + Type =:= 'get-bulk-request' -> + ?vtrace("handle_recv_pdu -> received request (~w)", [Type]), + ReqRef = make_ref(), + Extra = [{request_ref, ReqRef}], + Pid ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra}, + NewS = update_req_counter_incoming(S, Transport, ReqRef), + ?vdebug("handle_recv_pdu -> ~p", [NewS]), + NewS; +handle_recv_pdu( + #state{master_agent = Pid} = S, + #transport{socket = Socket}, + From, Vsn, Pdu, PduMS, ACMData) -> ?vtrace("handle_recv_pdu -> received other request", []), - active_once(Sock), + active_once(Socket), Pid ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, []}, S. maybe_handle_reply_pdu( - #state{filter = FilterMod, domain = Domain} = S, Vsn, - #pdu{request_id = Rid} = Pdu, + #state{filter = FilterMod, transports = Transports} = S, + #transport{} = Transport, + Vsn, + #pdu{} = Pdu, Type, ACMData, To) -> - - S1 = update_req_counter_outgoing(S, Rid), - Addresses = [fix_filter_address(Domain, To)], + %% + Addresses = [fix_filter_address(Transports, To)], case try FilterMod:accept_send_pdu(Addresses, Type) @@ -715,7 +967,8 @@ maybe_handle_reply_pdu( Class:Exception -> error_msg( "FilterMod:accept_send_pdu(~p, ~p) crashed: ~w:~w~n ~p", - [Addresses,Type,Class,Exception,erlang:get_stacktrace()]), + [Addresses, Type, Class, Exception, + erlang:get_stacktrace()]), true end of @@ -731,11 +984,16 @@ maybe_handle_reply_pdu( "FilterMod:accept_send_pdu(~p, ~p) returned: ~p", [Addresses,Type,Other]) end, - handle_reply_pdu(S1, Vsn, Pdu, Type, ACMData, To) - end, - S1. + handle_reply_pdu(S, Transport, Vsn, Pdu, Type, ACMData, To) + end. -handle_reply_pdu(#state{log = Log} = S, Vsn, Pdu, Type, ACMData, To) -> +handle_reply_pdu( + #state{log = Log} = S, + #transport{} = Transport, + Vsn, + #pdu{} = Pdu, + Type, ACMData, To) -> + %% LogF = fun(Type2, Data) -> log(Log, Type2, Data, To) @@ -744,7 +1002,15 @@ handle_reply_pdu(#state{log = Log} = S, Vsn, Pdu, Type, ACMData, To) -> ACMData, LogF)) of {ok, Packet} -> ?vinfo("time in agent: ~w mysec", [time_in_agent()]), - maybe_udp_send(S, To, Packet); + try maybe_udp_send(S, Transport, To, Packet) + catch + {Reason, Sz} -> + error_msg("Cannot send message " + "~n size: ~p" + "~n reason: ~p" + "~n pdu: ~p", + [Sz, Reason, Pdu]) + end; {discarded, Reason} -> ?vlog("handle_reply_pdu -> " "~n reply discarded for reason: ~s", @@ -758,7 +1024,7 @@ handle_reply_pdu(#state{log = Log} = S, Vsn, Pdu, Type, ACMData, To) -> maybe_handle_send_pdu( - #state{filter = FilterMod, domain = Domain} = S, + #state{filter = FilterMod, transports = Transports} = S, Vsn, Pdu, MsgData, TDomAddrSecs, From) -> ?vtrace("maybe_handle_send_pdu -> entry with~n" @@ -767,26 +1033,15 @@ maybe_handle_send_pdu( DomAddrSecs = snmpa_mpd:process_taddrs(TDomAddrSecs), AddressesToFilter = - [case Domain of - snmpUDPDomain -> - case DAS of - {{Dom, Addr}, _SecData} - when is_atom(Dom) -> % v3 - Addr; - {Dom, Addr} - when is_atom(Dom) -> % v1 & v2 - Addr - end; - _ -> - case DAS of - {{Dom, _Addr} = DomAddr, _SecData} - when is_atom(Dom) -> % v3 - DomAddr; - {Dom, _Addr} = DomAddr - when is_atom(Dom) -> % v1 & v2 - DomAddr - end - end || DAS <- DomAddrSecs], + case is_legacy_transports(Transports) of + true -> + [fix_filter_legacy_mpd_address(DAS) + || DAS <- DomAddrSecs]; + false -> + [fix_filter_mpd_address(DAS) + || DAS <- DomAddrSecs] + end, + Type = pdu_type_of(Pdu), case @@ -806,33 +1061,24 @@ maybe_handle_send_pdu( true -> handle_send_pdu(S, Vsn, Pdu, MsgData, DomAddrSecs, From); FilteredAddresses when is_list(FilteredAddresses) -> - MergedDomAddrSecs = - [DAS || - DAS <- DomAddrSecs, - lists:member( - case Domain of - snmpUDPDomain -> - case DAS of - {{Dom, Addr}, _SData} - when is_atom(Dom) -> % v3 - Addr; - {Dom, Addr} - when is_atom(Dom) -> % v1 & v2 - Addr - end; - true -> - case DAS of - {{Dom, _Addr} = DomAddr, _SData} - when is_atom(Dom) -> % v3 - DomAddr; - {Dom, _Addr} = DomAddr - when is_atom(Dom) -> % v1 & v2 - DomAddr - end - end, FilteredAddresses)], - ?vtrace("maybe_handle_send_pdu -> MergedDomAddrSecs:~n" - " ~p", [MergedDomAddrSecs]), - handle_send_pdu(S, Vsn, Pdu, MsgData, MergedDomAddrSecs, From); + FilteredDomAddrSecs = + case is_legacy_transports(Transports) of + true -> + [DAS || + DAS <- DomAddrSecs, + lists:member( + fix_filter_legacy_mpd_address(DAS), + FilteredAddresses)]; + false -> + [DAS || + DAS <- DomAddrSecs, + lists:member( + fix_filter_mpd_address(DAS), + FilteredAddresses)] + end, + ?vtrace("maybe_handle_send_pdu -> FilteredDomAddrSecs:~n" + " ~p", [FilteredDomAddrSecs]), + handle_send_pdu(S, Vsn, Pdu, MsgData, FilteredDomAddrSecs, From); Other -> error_msg( "FilterMod:accept_send_pdu(~p, ~p) returned: ~p", @@ -841,8 +1087,9 @@ maybe_handle_send_pdu( end. handle_send_pdu( - #state{note_store = NS} = S, Vsn, Pdu, MsgData, DomAddrSecs, From) -> - + #state{note_store = NS} = S, + Vsn, Pdu, MsgData, DomAddrSecs, From) -> + %% ?vtrace("handle_send_pdu -> entry with~n" " Pdu: ~p~n" " DomAddrSecs: ~p", [Pdu, DomAddrSecs]), @@ -863,23 +1110,24 @@ handle_send_pdu( case From of undefined -> S; - Pid -> + Pid when is_pid(Pid) -> ?vtrace("link to ~p and add to request list", [Pid]), link(Pid), - NReqs = snmp_misc:keyreplaceadd( - Pid, 2, S#state.reqs, {Pdu#pdu.request_id, From}), + NReqs = + snmp_misc:keyreplaceadd( + Pid, 2, S#state.reqs, {Pdu#pdu.request_id, From}), S#state{reqs = NReqs} end. handle_send_discovery( - #state{note_store = NS, - log = Log, - usock = Sock, - reqs = Reqs} = S, - #pdu{type = Type, - request_id = ReqId} = Pdu, - MsgData, To, From) -> + #state{ + note_store = NS, + log = Log, + reqs = Reqs, + transports = Transports} = S, + #pdu{type = Type, request_id = ReqId} = Pdu, + MsgData, To, From) when is_pid(From) -> ?vtrace("handle_send_discovery -> entry with" "~n Pdu: ~p" @@ -889,20 +1137,28 @@ handle_send_discovery( case (catch snmpa_mpd:generate_discovery_msg(NS, Pdu, MsgData, To)) of {ok, {Domain, Address, Packet}} -> - log(Log, Type, Packet, {Domain, Address}), - udp_send(Sock, {Domain, Address}, Packet), - ?vtrace("handle_send_discovery -> sent (~w)", [ReqId]), - NReqs = snmp_misc:keyreplaceadd(From, 2, Reqs, {ReqId, From}), - S#state{reqs = NReqs}; + case select_transport_from_domain(Domain, Transports) of + false -> + error_msg( + "Can not find transport to: ~s", + [format_address(To)]), + S; + #transport{socket = Socket} -> + log(Log, Type, Packet, {Domain, Address}), + udp_send(Socket, {Domain, Address}, Packet), + ?vtrace("handle_send_discovery -> sent (~w)", [ReqId]), + NReqs = snmp_misc:keyreplaceadd(From, 2, Reqs, {ReqId, From}), + S#state{reqs = NReqs} + end; {discarded, Reason} -> ?vlog("handle_send_discovery -> " "~n Discovery PDU ~p not sent due to ~p", [Pdu, Reason]), - ok; + S; {'EXIT', Reason} -> user_err("failed generating discovery message: " "~n PDU: ~p" "~n Reason: ~p", [Pdu, Reason]), - ok + S end. @@ -912,18 +1168,20 @@ do_handle_send_pdu(S, Trap, Addresses) -> do_handle_send_pdu(S, trappdu, Trap, Addresses). do_handle_send_pdu(S, Type, Pdu, Addresses) -> - case (catch do_handle_send_pdu1(S, Type, Addresses)) of + try do_handle_send_pdu1(S, Type, Addresses) + catch {Reason, Sz} -> - error_msg("Cannot send message " - "~n size: ~p" - "~n reason: ~p" - "~n pdu: ~p", - [Sz, Reason, Pdu]); - _ -> - ok + error_msg( + "Can not send message~n" + " size: ~p~n" + " reason: ~p~n" + " pdu: ~p", + [Sz, Reason, Pdu]) end. -do_handle_send_pdu1(S, Type, Addresses) -> +do_handle_send_pdu1( + #state{transports = Transports} = S, + Type, Addresses) -> lists:foreach( fun ({Domain, Address, Packet}) when is_binary(Packet) -> ?vdebug( @@ -931,32 +1189,55 @@ do_handle_send_pdu1(S, Type, Addresses) -> " size: ~p~n" " to: ~p", [Domain, sz(Packet), Address]), To = {Domain, Address}, - maybe_udp_send(S, To, Packet); + case select_transport_from_domain(Domain, Transports) of + false -> + error_msg( + "Can not find transport~n" + " size: ~p~n" + " to: ~s", + [sz(Packet), To]); + Transport -> + maybe_udp_send(S, Transport, To, Packet) + end; ({Domain, Address, {Packet, LogData}}) when is_binary(Packet) -> ?vdebug( "[~w] sending encrypted packet:~n" " size: ~p~n" " to: ~p", [Domain, sz(Packet), Address]), To = {Domain, Address}, - maybe_udp_send(S, To, Packet, Type, LogData) + case select_transport_from_domain(Domain, Transports) of + false -> + error_msg( + "Can not find transport~n" + " size: ~p~n" + " to: ~s", + [sz(Packet), To]); + Transport -> + maybe_udp_send(S, Transport, To, Packet, Type, LogData) + end end, Addresses). -handle_response(Vsn, Pdu, From, S) -> - case lists:keysearch(Pdu#pdu.request_id, 1, S#state.reqs) of - {value, {_, Pid}} -> - ?vdebug("handle_response -> " - "~n send response to receiver ~p", [Pid]), - Pid ! {snmp_response_received, Vsn, Pdu, From}; - _ -> - ?vdebug("handle_response -> " - "~n No receiver available for response pdu", []) - end. +%%% XXX remove +%%% +%%% handle_response( +%%% #state{reqs = Reqs} = S, Pdu#pdu{request_id = ReqId}, From, Vsn) +%%% when is_pid(From) -> +%%% case lists:keyfind(ReqId, 1, S#state.reqs) of +%%% {ReqId, Pid} -> +%%% ?vdebug("handle_response -> " +%%% "~n send response to receiver ~p", [Pid]), +%%% Pid ! {snmp_response_received, Vsn, Pdu, From}; +%%% false -> +%%% ?vdebug("handle_response -> " +%%% "~n No receiver available for response pdu", []) +%%% end. maybe_udp_send( - #state{usock = Sock, filter = FilterMod, domain = Domain}, + #state{filter = FilterMod, transports = Transports}, + #transport{socket = Socket}, To, Packet) -> - {To_1, To_2} = fix_filter_address(Domain, To), + {To_1, To_2} = fix_filter_address(Transports, To), case try FilterMod:accept_send(To_1, To_2) catch @@ -979,25 +1260,21 @@ maybe_udp_send( "FilterMod:accept_send(~p, ~p) returned: ~p", [To_1,To_2,Other]) end, - %% XXX should be some kind of lookup of domain to socket - (catch udp_send(Sock, To, Packet)) + udp_send(Socket, To, Packet) end. maybe_udp_send( - #state{ - log = Log, - usock = Sock, - filter = FilterMod, - domain = Domain}, + #state{log = Log, filter = FilterMod, transports = Transports}, + #transport{socket = Socket}, To, Packet, Type, _LogData) -> - {To_1, To_2} = fix_filter_address(Domain, To), + {To_1, To_2} = fix_filter_address(Transports, To), case try FilterMod:accept_send(To_1, To_2) catch Class:Exception -> error_msg( "FilterMod:accept_send(~p, ~p) crashed for: ~w:~w~n ~p", - [To_1,To_2,Class,Exception,erlang:get_stacktrace()]), + [To_1, To_2, Class, Exception, erlang:get_stacktrace()]), true end of @@ -1014,32 +1291,33 @@ maybe_udp_send( [To_1,To_2,Other]) end, log(Log, Type, Packet, To), - (catch udp_send(Sock, To, Packet)) + udp_send(Socket, To, Packet) end. -udp_send(UdpId, To, B) -> - %% XXX should be some kind of lookup of domain to socket - {Ip, Port} = +udp_send(Socket, To, B) -> + {IpAddr, IpPort} = case To of {Domain, Addr} when is_atom(Domain) -> Addr; {_, P} = Addr when is_integer(P) -> Addr end, - case (catch gen_udp:send(UdpId, Ip, Port, B)) of + try gen_udp:send(Socket, IpAddr, IpPort, B) of {error, emsgsize} -> %% From this message we cannot recover, so exit sending loop throw({emsgsize, sz(B)}); {error, ErrorReason} -> error_msg("[error] cannot send message " "(destination: ~p:~p, size: ~p, reason: ~p)", - [Ip, Port, sz(B), ErrorReason]); - {'EXIT', ExitReason} -> - error_msg("[exit] cannot send message " - "(destination: ~p:~p, size: ~p, reason: ~p)", - [Ip, Port, sz(B), ExitReason]); - _ -> + [IpAddr, IpPort, sz(B), ErrorReason]); + ok -> ok + catch + error:ExitReason -> + error_msg("[exit] cannot send message " + "(destination: ~p:~p, size: ~p, reason: ~p, at: ~p)", + [IpAddr, IpPort, sz(B), ExitReason, + erlang:get_stacktrace()]) end. sz(L) when is_list(L) -> length(L); @@ -1089,21 +1367,74 @@ active_once(Sock) -> inet:setopts(Sock, [{active, once}]). +select_transport_from_req_ref(_, []) -> + false; +select_transport_from_req_ref( + ReqRef, + [#transport{req_refs = ReqRefs} = Transport | Transports]) -> + case lists:member(ReqRef, ReqRefs) of + true -> + Transport; + false -> + select_transport_from_req_ref(ReqRef, Transports) + end. + +select_transport_from_domain(Domain, Transports) when is_atom(Domain) -> + Pos = #transport.domain, + case lists:keyfind(Domain, Pos, Transports) of + #transport{domain = Domain} = Transport -> + Transport; + false when Domain == snmpUDPDomain -> + lists:keyfind(transportDomainUdpIpv4, Pos, Transports); + false when Domain == transportDomainUdpIpv4 -> + lists:keyfind(snmpUDPDomain, Pos, Transports); + false -> + false + end. + +address_to_domain({Domain, _Addr}) when is_atom(Domain) -> + Domain; +address_to_domain({_Ip, Port}) when is_integer(Port) -> + snmpUDPDomain. + %% If the agent uses legacy snmpUDPDomain e.g has not set %% intAgentTransportDomain, then make sure %% snmpa_network_interface_filter gets legacy arguments %% to not break backwards compatibility. %% -fix_filter_address(snmpUDPDomain, {Domain, Addr}) - when Domain =:= snmpUDPDomain; - Domain =:= transportDomainUdpIpv4 -> - Addr; -fix_filter_address(_AgentDomain, {Domain, _} = Address) - when is_atom(Domain) -> - Address; -fix_filter_address(snmpUDPDomain, {_, Port} = Addr) - when is_integer(Port) -> - Addr. +fix_filter_address(Transports, Address) -> + case is_legacy_transports(Transports) of + true -> + case Address of + {Domain, Addr} when is_atom(Domain) -> + Addr; + {_, IpPort} = Addr when is_integer(IpPort) -> + Addr + end; + false -> + Address + end. + +is_legacy_transports([#transport{domain = snmpUDPDomain}]) -> + true; +is_legacy_transports([#transport{} | _]) -> + false. + +fix_filter_legacy_mpd_address(Domain_Address_SecData) -> + case Domain_Address_SecData of + {{Domain, Addr}, _SecData} when is_atom(Domain) -> % v3 + Addr; + {Domain, Addr} when is_atom(Domain) -> % v1 & v2 + Addr + end. + +fix_filter_mpd_address(Domain_Address_SecData) -> + case Domain_Address_SecData of + {{Domain, _Addr} = Address, _SecData} when is_atom(Domain) -> % v3 + Address; + {Domain, _Addr} = Address when is_atom(Domain) -> % v1 & v2 + Address + end. %%%----------------------------------------------------------------- @@ -1261,6 +1592,35 @@ get_counters([Counter|Counters], Acc) -> %% ---------------------------------------------------------------- +socket_opts(Domain, {IpAddr, IpPort}, Opts) -> + [IpPort, % Picked off at socket open, separate argument + binary, + snmp_conf:tdomain_to_family(Domain) + | case get_bind_to_ip_address(Opts) of + true -> + [{ip, IpAddr}]; + _ -> + [] + end ++ + case get_no_reuse_address(Opts) of + false -> + [{reuseaddr, true}]; + _ -> + [] + end ++ + case get_recbuf(Opts) of + use_default -> + []; + Sz -> + [{recbuf, Sz}] + end ++ + case get_sndbuf(Opts) of + use_default -> + []; + Sz -> + [{sndbuf, Sz}] + end]. + ip_opt_bind_to_ip_address(Opts, Ip) -> case get_bind_to_ip_address(Opts) of true -> @@ -1378,14 +1738,14 @@ call(Pid, Req) -> %% ---------------------------------------------------------------- -get_info(#state{usock = Id, reqs = Reqs}) -> +get_info(#state{transports = Transports, reqs = Reqs}) -> ProcSize = proc_mem(self()), - PortInfo = get_port_info(Id), Counters = get_counters(), - [{reqs, Reqs}, - {counters, Counters}, - {process_memory, ProcSize}, - {port_info, PortInfo}]. + [{reqs, Reqs}, + {counters, Counters}, + {process_memory, ProcSize} + | [{port_info, get_port_info(Socket)} + || #transport{socket = Socket} <- Transports]]. proc_mem(P) when is_pid(P) -> case (catch erlang:process_info(P, memory)) of diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index ac0739860b..40956c40fb 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -790,42 +790,45 @@ send_trap_pdus([], ContextName, {TrapRec, Vbs}, send_v1_trap(_TrapRec, [], _Vbs, _ExtraInfo, _NetIf, _SysUpTime) -> ok; -send_v1_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, - V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> +send_v1_trap( + #trap{enterpriseoid = Enter, specificcode = Spec}, + V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> ?vdebug("prepare to send v1 trap " "~n '~p'" "~n with" "~n ~p" "~n to" "~n ~p", [Enter, Spec, V1Res]), - AgentDomain = - case snmp_framework_mib:intAgentTransportDomain(get) of - {value, AD} -> - AD; - genErr -> - snmp_target_mib:default_domain() - end, - case AgentDomain of - snmpUDPDomain -> - {value, AgentIp} = snmp_framework_mib:intAgentIpAddress(get), - TrapPdu = make_v1_trap_pdu(Enter, Spec, Vbs, SysUpTime, AgentIp), - AddrCommunities = mk_addr_communities(V1Res), - lists:foreach( - fun ({Community, Addrs}) -> - ?vtrace("send v1 trap pdu to ~p",[Addrs]), - NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs, ExtraInfo} - end, AddrCommunities); - _ -> - ?vtrace( - "snmpa_trap: can not send v1 trap with domain: ~w", - [AgentDomain]), - user_err( - "snmpa_trap: can not send v1 trap with domain: ~w", - [AgentDomain]) - end; -send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, ExtraInfo, NetIf, - SysUpTime) -> + do_send_v1_trap(Enter, Spec, V1Res, Vbs, ExtraInfo, NetIf, SysUpTime); +%%% AgentDomain = +%%% case snmp_framework_mib:intAgentTransportDomain(get) of +%%% {value, AD} -> +%%% AD; +%%% genErr -> +%%% snmp_target_mib:default_domain() +%%% end, +%%% case AgentDomain of +%%% snmpUDPDomain -> +%%% {value, AgentIp} = snmp_framework_mib:intAgentIpAddress(get), +%%% TrapPdu = make_v1_trap_pdu(Enter, Spec, Vbs, SysUpTime, AgentIp), +%%% AddrCommunities = mk_addr_communities(V1Res), +%%% lists:foreach( +%%% fun ({Community, Addrs}) -> +%%% ?vtrace("send v1 trap pdu to ~p",[Addrs]), +%%% NetIf ! {send_pdu, 'version-1', TrapPdu, +%%% {community, Community}, Addrs, ExtraInfo} +%%% end, AddrCommunities); +%%% _ -> +%%% ?vtrace( +%%% "snmpa_trap: can not send v1 trap with domain: ~w", +%%% [AgentDomain]), +%%% user_err( +%%% "snmpa_trap: can not send v1 trap with domain: ~w", +%%% [AgentDomain]) +%%% end; +send_v1_trap( + #notification{oid = Oid}, + V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> %% Use alg. in rfc2089 to map a v2 trap to a v1 trap % delete Counter64 objects from vbs ?vdebug("prepare to send v1 trap '~p'",[Oid]), @@ -842,32 +845,64 @@ send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, ExtraInfo, NetIf, {lists:reverse(First),Last} end end, - AgentDomain = - case snmp_framework_mib:intAgentTransportDomain(get) of - {value, AD} -> - AD; - genErr -> - snmp_target_mib:default_domain() + do_send_v1_trap(Enter, Spec, V1Res, NVbs, ExtraInfo, NetIf, SysUpTime). + +do_send_v1_trap(Enter, Spec, V1Res, NVbs, ExtraInfo, NetIf, SysUpTime) -> + {value, Transports} = snmp_framework_mib:intAgentTransports(get), + {_Domain, {AgentIp, _AgentPort}} = + case lists:keyfind(snmpUDPDomain, 1, Transports) of + false -> + case lists:keyfind(transportDomainUdpIpv4, 1, Transports) of + false -> + ?vtrace( + "snmpa_trap: can not send v1 trap " + "without IPv4 domain: ~p", + [Transports]), + user_err( + "snmpa_trap: can not send v1 trap " + "without IPv4 domain: ~p", + [Transports]); + DomainAddr -> + DomainAddr + end; + DomainAddr -> + DomainAddr end, - case AgentDomain of - snmpUDPDomain -> - {value, AgentIp} = snmp_framework_mib:intAgentIpAddress(get), - TrapPdu = make_v1_trap_pdu(Enter, Spec, NVbs, SysUpTime, AgentIp), - AddrCommunities = mk_addr_communities(V1Res), - lists:foreach( - fun ({Community, Addrs}) -> - ?vtrace("send v1 trap to ~p",[Addrs]), - NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs, ExtraInfo} - end, AddrCommunities); - _ -> - ?vtrace( - "snmpa_trap: can not send v1 trap with domain: ~w", - [AgentDomain]), - user_err( - "snmpa_trap: can not send v1 trap with domain: ~w", - [AgentDomain]) - end. + TrapPdu = make_v1_trap_pdu(Enter, Spec, NVbs, SysUpTime, AgentIp), + AddrCommunities = mk_addr_communities(V1Res), + lists:foreach( + fun ({Community, Addrs}) -> + ?vtrace("send v1 trap to ~p",[Addrs]), + NetIf ! {send_pdu, 'version-1', TrapPdu, + {community, Community}, Addrs, ExtraInfo} + end, AddrCommunities). + +%%% AgentDomain = +%%% case snmp_framework_mib:intAgentTransportDomain(get) of +%%% {value, AD} -> +%%% AD; +%%% genErr -> +%%% snmp_target_mib:default_domain() +%%% end, +%%% case AgentDomain of +%%% snmpUDPDomain -> +%%% {value, AgentIp} = snmp_framework_mib:intAgentIpAddress(get), +%%% TrapPdu = make_v1_trap_pdu(Enter, Spec, NVbs, SysUpTime, AgentIp), +%%% AddrCommunities = mk_addr_communities(V1Res), +%%% lists:foreach( +%%% fun ({Community, Addrs}) -> +%%% ?vtrace("send v1 trap to ~p",[Addrs]), +%%% NetIf ! {send_pdu, 'version-1', TrapPdu, +%%% {community, Community}, Addrs, ExtraInfo} +%%% end, AddrCommunities); +%%% _ -> +%%% ?vtrace( +%%% "snmpa_trap: can not send v1 trap with domain: ~w", +%%% [AgentDomain]), +%%% user_err( +%%% "snmpa_trap: can not send v1 trap with domain: ~w", +%%% [AgentDomain]) +%%% end. send_v2_trap(_TrapRec, [], _Vbs, _Recv, _ExtraInfo, _NetIf, _SysUpTime) -> ok; diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 82cbf42e35..f4483995cb 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -223,13 +223,17 @@ read_check(_, _, [], _, Res) -> lists:reverse(Res); read_check(File, Check, [{StartLine, Row, EndLine}|Lines], State, Res) -> try Check(Row, State) of + {Rows, NewState} when is_list(Rows) -> + ?vtrace("read_check -> ok:~n" + " Rows: ~p~n", [Rows]), + read_check(File, Check, Lines, NewState, Rows ++ Res); {ok, NewState} -> ?vtrace("read_check -> ok", []), - read_check(File, Check, Lines, NewState, [Row|Res]); + read_check(File, Check, Lines, NewState, [Row | Res]); {{ok, NewRow}, NewState} -> ?vtrace("read_check -> ok:~n" " NewRow: ~p~n", [NewRow]), - read_check(File, Check, Lines, NewState, [NewRow|Res]) + read_check(File, Check, Lines, NewState, [NewRow | Res]) catch {error, Reason} -> ?vtrace("read_check -> error:~n" diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 38e248c326..91240082f1 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -33,7 +33,7 @@ -export([write_agent_snmp_files/7, write_agent_snmp_files/12, write_agent_snmp_files/6, write_agent_snmp_files/11, - write_agent_snmp_conf/5, + write_agent_snmp_conf/4, write_agent_snmp_conf/5, write_agent_snmp_context_conf/1, write_agent_snmp_community_conf/1, write_agent_snmp_standard_conf/2, @@ -1650,6 +1650,12 @@ write_agent_snmp_files( %% ------ [agent] agent.conf ------ %% +write_agent_snmp_conf(Dir, Transports, EngineID, MMS) -> + Conf = + [{intAgentTransports, Transports}, + {snmpEngineID, EngineID}, + {snmpEngineMaxMessageSize, MMS}], + do_write_agent_snmp_conf(Dir, Conf). write_agent_snmp_conf(Dir, Domain, AgentAddr, EngineID, MMS) when is_atom(Domain) -> @@ -2513,6 +2519,8 @@ write_config_file(Dir, FileName, Order, Check, Write, Entries) lists:foldl( fun (Entry, State) -> case Check(Entry, State) of + {Ok, NewState} when is_list(Ok) -> + NewState; {ok, NewState} -> NewState; {{ok, _}, NewState} -> @@ -2709,6 +2717,8 @@ verify_lines([], _, _, Acc) -> verify_lines( [{StartLine, Term, EndLine}|Lines], Check, State, Acc) -> try Check(Term, State) of + {Terms, NewState} when is_list(Terms) -> + verify_lines(Lines, Check, NewState, Terms ++ Acc); {ok, NewState} -> verify_lines(Lines, Check, NewState, [Term|Acc]); {{ok, NewTerm}, NewState} -> diff --git a/lib/snmp/test/klas3.erl b/lib/snmp/test/klas3.erl index 4c7c03e2ca..4cbd852b2d 100644 --- a/lib/snmp/test/klas3.erl +++ b/lib/snmp/test/klas3.erl @@ -75,6 +75,7 @@ fname(get) -> end, case snmpa:current_net_if_data() of {value, []} -> ok; + {value, [{request_ref, R}]} when is_reference(R) -> ok; {value, _} -> throw("bad_nil"); _ -> throw("bad_nid") end, -- cgit v1.2.3