From c49a434098b715b5d7d47ebcb1c2b9a22d0ee500 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 19 Aug 2014 15:42:49 +0200 Subject: Implement IPv4+IPv6 in MT manager --- lib/snmp/src/manager/snmpm_net_if_mt.erl | 799 ++++++++++++++++++++----------- lib/snmp/test/snmp_to_snmpnet_SUITE.erl | 16 +- 2 files changed, 535 insertions(+), 280 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_net_if_mt.erl b/lib/snmp/src/manager/snmpm_net_if_mt.erl index 2937f5cc87..516a2f444a 100644 --- a/lib/snmp/src/manager/snmpm_net_if_mt.erl +++ b/lib/snmp/src/manager/snmpm_net_if_mt.erl @@ -59,8 +59,9 @@ { server, note_store, - domain, - sock, +%% domain, +%% sock, + transports = [], mpd_state, log, irb = auto, % auto | {user, integer()} @@ -68,6 +69,9 @@ filter }). +-record(transport, + {socket, + domain = snmpUDPDomain}). -define(DEFAULT_FILTER_MODULE, snmpm_net_if_filter). -define(DEFAULT_FILTER_OPTS, [{module, ?DEFAULT_FILTER_MODULE}]). @@ -107,12 +111,12 @@ send_pdu(Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port) -> send_pdu(Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port, ExtraInfo) when is_record(Pdu, pdu) -> ?d("send_pdu -> entry with~n" - " Pid: ~p~n" - " Pdu: ~p~n" - " Vsn: ~p~n" - " MsgData: ~p~n" - " Domain/IP: ~p~n" - " Addr/Port : ~p", + " Pid: ~p~n" + " Pdu: ~p~n" + " Vsn: ~p~n" + " MsgData: ~p~n" + " Domain/IP: ~p~n" + " Addr/Port: ~p", [Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port]), {Domain, Addr} = address(Domain_or_Ip, Addr_or_Port), cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo}). @@ -161,12 +165,17 @@ init([Server, NoteStore]) -> ?d("init -> entry with" "~n Server: ~p" "~n NoteStore: ~p", [Server, NoteStore]), - case (catch do_init(Server, NoteStore)) of + try do_init(Server, NoteStore) + catch {error, Reason} -> - {stop, Reason}; - {ok, State} -> - {ok, State} + {stop, Reason} end. + %% case (catch do_init(Server, NoteStore)) of + %% {error, Reason} -> + %% {stop, Reason}; + %% {ok, State} -> + %% {ok, State} + %% end. do_init(Server, NoteStore) -> process_flag(trap_exit, true), @@ -199,21 +208,6 @@ do_init(Server, NoteStore) -> {ok, IRB} = snmpm_config:system_info(net_if_irb), IrGcRef = irgc_start(IRB), - %% -- Socket -- - SndBuf = get_opt(Opts, sndbuf, default), - RecBuf = get_opt(Opts, recbuf, default), - BindTo = get_opt(Opts, bind_to, false), - NoReuse = get_opt(Opts, no_reuse, false), - {ok, Port} = snmpm_config:system_info(port), - Domain = - case snmpm_config:system_info(domain) of - {ok, D} -> - D; - _ -> - snmpm_config:default_transport_domain() - end, - {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, Domain, BindTo, NoReuse), - %% Flow control -- FilterOpts = get_opt(Opts, filter, []), FilterMod = create_filter(FilterOpts), @@ -224,82 +218,197 @@ do_init(Server, NoteStore) -> Log = do_init_log(ATL), ?vdebug("Log: ~w", [Log]), - %% -- Initiate counters --- - init_counters(), - - %% -- We are done --- - State = #state{server = Server, - note_store = NoteStore, - mpd_state = MpdState, - domain = Domain, - sock = Sock, - log = Log, - irb = IRB, - irgc = IrGcRef, - filter = FilterMod}, - ?vdebug("started", []), - {ok, State}. + {ok, DomainAddresses} = snmpm_config:system_info(transports), + ?vdebug("DomainAddresses: ~w",[DomainAddresses]), + CommonSocketOpts = common_socket_opts(Opts), + BindTo = get_opt(Opts, bind_to, false), + case + [begin + {IpPort, SocketOpts} = + socket_params(Domain, Address, BindTo, CommonSocketOpts), + Socket = socket_open(IpPort, SocketOpts), + #transport{socket = Socket, domain = Domain} + end || {Domain, Address} <- DomainAddresses] + of + [] -> + ?vinfo("No transports configured: ~p", [DomainAddresses]), + throw({error, {no_transports,DomainAddresses}}); + Transports -> + %% -- Initiate counters --- + init_counters(), + + %% -- We are done --- + State = #state{ + server = Server, + note_store = NoteStore, + mpd_state = MpdState, + transports = Transports, + log = Log, + irb = IRB, + irgc = IrGcRef, + filter = FilterMod}, + ?vdebug("started", []), + {ok, State} + end. -%% Open port -do_open_port(Port, SendSz, RecvSz, Domain, BindTo, NoReuse) -> - ?vtrace("do_open_port -> entry with~n" - " Port: ~p~n" - " SendSz: ~p~n" - " RecvSz: ~p~n" - " Domain: ~p~n" - " BindTo: ~p~n" - " NoReuse: ~p", - [Port, SendSz, RecvSz, Domain, BindTo, NoReuse]), - IpOpts1 = bind_to(BindTo), - IpOpts2 = no_reuse(NoReuse), - IpOpts3 = recbuf(RecvSz), - IpOpts4 = sndbuf(SendSz), - IpOpts = - [binary, - snmp_conf:tdomain_to_family(Domain) | - IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4], - OpenRes = - case init:get_argument(snmpm_fd) of - {ok, [[FdStr]]} -> - Fd = list_to_integer(FdStr), - gen_udp:open(0, [{fd, Fd}|IpOpts]); - error -> - gen_udp:open(Port, IpOpts) - end, - case OpenRes of + + %% %% -- Socket -- + %% SndBuf = get_opt(Opts, sndbuf, default), + %% RecBuf = get_opt(Opts, recbuf, default), + %% BindTo = get_opt(Opts, bind_to, false), + %% NoReuse = get_opt(Opts, no_reuse, false), + + %% {ok, Port} = snmpm_config:system_info(port), + %% Domain = + %% case snmpm_config:system_info(domain) of + %% {ok, D} -> + %% D; + %% _ -> + %% snmpm_config:default_transport_domain() + %% end, + %% {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, Domain, BindTo, NoReuse), + + %% %% -- Initiate counters --- + %% init_counters(), + + %% %% -- We are done --- + %% State = #state{server = Server, + %% note_store = NoteStore, + %% mpd_state = MpdState, + %% domain = Domain, + %% sock = Sock, + %% log = Log, + %% irb = IRB, + %% irgc = IrGcRef, + %% filter = FilterMod}, + %% ?vdebug("started", []), + %% {ok, State}. + + +%% %% Open port +%% do_open_port(Port, SendSz, RecvSz, Domain, BindTo, NoReuse) -> +%% ?vtrace("do_open_port -> entry with~n" +%% " Port: ~p~n" +%% " SendSz: ~p~n" +%% " RecvSz: ~p~n" +%% " Domain: ~p~n" +%% " BindTo: ~p~n" +%% " NoReuse: ~p", +%% [Port, SendSz, RecvSz, Domain, BindTo, NoReuse]), +%% IpOpts1 = bind_to(BindTo), +%% IpOpts2 = no_reuse(NoReuse), +%% IpOpts3 = recbuf(RecvSz), +%% IpOpts4 = sndbuf(SendSz), +%% IpOpts = +%% [binary, +%% snmp_conf:tdomain_to_family(Domain) | +%% IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4], +%% OpenRes = +%% case init:get_argument(snmpm_fd) of +%% {ok, [[FdStr]]} -> +%% Fd = list_to_integer(FdStr), +%% gen_udp:open(0, [{fd, Fd}|IpOpts]); +%% error -> +%% gen_udp:open(Port, IpOpts) +%% end, +%% case OpenRes of +%% {error, _} = Error -> +%% throw(Error); +%% OK -> +%% OK +%% end. + +socket_open(IpPort, SocketOpts) -> + ?vtrace("socket_open -> entry with~n" + " IpPort: ~p~n" + " SocketOpts: ~p", [IpPort, SocketOpts]), + case gen_udp:open(IpPort, SocketOpts) of {error, _} = Error -> throw(Error); - OK -> - OK + {ok, Socket} -> + Socket end. -bind_to(true) -> - case snmpm_config:system_info(address) of - {ok, Addr} when is_list(Addr) -> - [{ip, list_to_tuple(Addr)}]; - {ok, Addr} -> - [{ip, Addr}]; +%% bind_to(true) -> +%% case snmpm_config:system_info(address) of +%% {ok, Addr} when is_list(Addr) -> +%% [{ip, list_to_tuple(Addr)}]; +%% {ok, Addr} -> +%% [{ip, Addr}]; +%% _ -> +%% [] +%% end; +%% bind_to(_) -> +%% []. + +socket_params(Domain, {IpAddr, IpPort}, BindTo, CommonSocketOpts) -> + Family = snmp_conf:tdomain_to_family(Domain), + SocketOpts = + case Family of + inet6 -> + [Family, {ipv6_v6only, true} | CommonSocketOpts]; + Family -> + [Family | CommonSocketOpts] + end, + case Family of + inet -> + case init:get_argument(snmp_fd) of + {ok, [[FdStr]]} -> + Fd = list_to_integer(FdStr), + case BindTo of + true -> + {IpPort, [{ip, IpAddr}, {fd, Fd} | SocketOpts]}; + _ -> + {0, [{fd, Fd} | SocketOpts]} + end; + error -> + {IpPort, [{ip, IpAddr} | SocketOpts]} + end; _ -> - [] - end; -bind_to(_) -> - []. - -no_reuse(false) -> - [{reuseaddr, true}]; -no_reuse(_) -> - []. - -recbuf(default) -> - []; -recbuf(Sz) -> - [{recbuf, Sz}]. + case BindTo of + true -> + {IpPort, [{ip, IpAddr} | SocketOpts]}; + _ -> + {IpPort, SocketOpts} + end + end. -sndbuf(default) -> - []; -sndbuf(Sz) -> - [{sndbuf, Sz}]. +common_socket_opts(Opts) -> + [binary + | case get_opt(Opts, sndbuf, default) of + default -> + []; + Sz -> + [{sndbuf, Sz}] + end ++ + case get_opt(Opts, recbuf, default) of + default -> + []; + Sz -> + [{sndbuf, Sz}] + end ++ + case get_opt(Opts, no_reuse, false) of + false -> + [{reuseaddr, true}]; + _ -> + [] + end]. + +%% no_reuse(false) -> +%% [{reuseaddr, true}]; +%% no_reuse(_) -> +%% []. + +%% recbuf(default) -> +%% []; +%% recbuf(Sz) -> +%% [{recbuf, Sz}]. + +%% sndbuf(default) -> +%% []; +%% sndbuf(Sz) -> +%% [{sndbuf, Sz}]. create_filter(Opts) when is_list(Opts) -> @@ -338,7 +447,8 @@ do_init_log(true) -> Function = increment_counter, Args = [atl_seqno, Initial, Max], SeqNoGen = {Module, Function, Args}, - case snmp_log:create(Name, File, SeqNoGen, Size, Repair, true) of + case snmp_log:create( + Name, File, SeqNoGen, Size, Repair, true) of {ok, Log} -> ?vdebug("log created: ~w", [Log]), {Name, Log, Type}; @@ -356,8 +466,6 @@ do_init_log(true) -> end. -%% ---------------------------------------------------------------------- - %%-------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | @@ -449,11 +557,20 @@ handle_cast(Msg, State) -> %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- handle_info( - {udp, Sock, Ip, Port, Bytes}, - #state{sock = Sock, domain = Domain} = State) -> - ?vlog("received ~w bytes from ~p:~p", [size(Bytes), Ip, Port]), - handle_udp(Domain, {Ip, Port}, Bytes, State), - {noreply, State}; + {udp, Socket, IpAddr, IpPort, Bytes}, + #state{transports = Transports} = State) -> + Size = byte_size(Bytes), + case lists:keyfind(Socket, #transport.socket, Transports) of + #transport{socket = Socket, domain = Domain} -> + ?vlog("received ~w bytes from ~p:~p [~w]", + [Size, IpAddr, IpPort, Socket]), + handle_udp(Domain, {IpAddr, IpPort}, Bytes, State), + {noreply, State}; + false -> + warning_msg("Received ~w bytes on unknown port: ~p from ~s", + [Size, Socket, format_address({IpAddr, IpPort})]), + {noreply, State} + end; handle_info(inform_response_gc, State) -> ?vlog("received inform_response_gc message", []), @@ -466,11 +583,18 @@ handle_info({disk_log, _Node, Log, Info}, State) -> State2 = handle_disk_log(Log, Info, State), {noreply, State2}; -handle_info({'DOWN', _MRef, process, Pid, {net_if_worker, ExitStatus}}, +handle_info({'DOWN', _MRef, process, _Pid, {net_if_worker, _Result}}, State) -> - ?vdebug("received DOWN message from net_if-worker: " - "~n ExitStatus: ~p", [ExitStatus]), - handle_worker_exit(Pid, ExitStatus), + ?vdebug("received DOWN message from net_if worker [~w]: " + "~n Result: ~p", [_Pid, _Result]), + {noreply, State}; +handle_info( + {'DOWN', _MRef, process, Pid, + {net_if_worker, Failer, Class, Reason, Stacktrace} = _ExitStatus}, + State) -> + ?vdebug("received DOWN message from net_if worker [~w]: " + "~n ExitStatus: ~p", [Pid, _ExitStatus]), + Failer(Pid, Class, Reason, Stacktrace), {noreply, State}; handle_info(Info, State) -> @@ -512,99 +636,108 @@ code_change(_Vsn, State, _Extra) -> "~n Extra: ~p", [_Vsn, State, _Extra]), {ok, State}. - + %%%------------------------------------------------------------------- %%% Internal functions %%%------------------------------------------------------------------- handle_udp(Domain, Addr, Bytes, State) -> - Verbosity = get(verbosity), - spawn_opt( - fun() -> - Log = worker_init(State, Verbosity), - Res = - (catch maybe_handle_recv_msg( - Domain, Addr, Bytes, - State#state{log = Log})), - worker_exit(udp, {Domain, Addr}, Res) + worker( + fun (S) -> + maybe_handle_recv_msg(Domain, Addr, Bytes, S) end, - [monitor]). + fun (Pid, Class, Reason, Stacktrace) -> + warning_msg( + "Worker process (~p) terminated " + "while processing (incomming) message from %s:~n" + "~w:~w at ~p", + [Pid, snmp_conf:mk_addr_string({Domain, Addr}), + Class, Reason, Stacktrace]) + end, + State). + + %% Verbosity = get(verbosity), + %% spawn_opt( + %% fun() -> + %% Log = worker_init(State, Verbosity), + %% Res = + %% (catch maybe_handle_recv_msg( + %% Domain, Addr, Bytes, + %% State#state{log = Log})), + %% worker_exit(udp, {Domain, Addr}, Res) + %% end, + %% [monitor]). maybe_handle_recv_msg( Domain, Addr, Bytes, - #state{filter = FilterMod, domain = ManagerDomain} = State) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + #state{filter = FilterMod, transports = Transports} = State) -> + {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}), case (catch FilterMod:accept_recv(Arg1, Arg2)) of false -> %% Drop the received packet - inc(netIfMsgInDrops), - ok; + inc(netIfMsgInDrops); _ -> handle_recv_msg(Domain, Addr, Bytes, State) - end. + end, + ok. handle_recv_msg(Domain, Addr, Bytes, #state{server = Pid}) when is_binary(Bytes) andalso (size(Bytes) =:= 0) -> - Pid ! {snmp_error, {empty_message, Domain, Addr}, Domain, Addr}, - ok; - + Pid ! {snmp_error, {empty_message, Domain, Addr}, Domain, Addr}; +%% handle_recv_msg( Domain, Addr, Bytes, - #state{server = Pid, - note_store = NoteStore, - mpd_state = MpdState, - log = Log} = State) -> + #state{ + server = Pid, + note_store = NoteStore, + mpd_state = MpdState, + log = Log} = State) -> Logger = logger(Log, read, Domain, Addr), - case (catch snmpm_mpd:process_msg(Bytes, Domain, Addr, - MpdState, NoteStore, Logger)) of + case (catch snmpm_mpd:process_msg( + Bytes, Domain, Addr, MpdState, NoteStore, Logger)) of {ok, Vsn, Pdu, MS, ACM} -> - maybe_handle_recv_pdu(Domain, Addr, Vsn, Pdu, MS, ACM, - Logger, State); + maybe_handle_recv_pdu( + Domain, Addr, Vsn, Pdu, MS, ACM, Logger, State); {discarded, Reason, Report} -> ?vdebug("discarded: ~p", [Reason]), ErrorInfo = {failed_processing_message, Reason}, Pid ! {snmp_error, ErrorInfo, Domain, Addr}, - maybe_udp_send(Domain, Addr, Report, State), - ok; + maybe_udp_send(Domain, Addr, Report, State); {discarded, Reason} -> ?vdebug("discarded: ~p", [Reason]), ErrorInfo = {failed_processing_message, Reason}, - Pid ! {snmp_error, ErrorInfo, Domain, Addr}, - ok; + Pid ! {snmp_error, ErrorInfo, Domain, Addr}; Error -> error_msg("processing of received message failed: " - "~n ~p", [Error]), - ok + "~n ~p", [Error]) end. maybe_handle_recv_pdu( Domain, Addr, Vsn, #pdu{type = Type} = Pdu, PduMS, ACM, Logger, - #state{filter = FilterMod, domain = ManagerDomain} = State) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + #state{filter = FilterMod, transports = Transports} = State) -> + {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}), case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, Type)) of false -> - inc(netIfPduInDrops), - ok; + inc(netIfPduInDrops); _ -> handle_recv_pdu( Domain, Addr, Vsn, Pdu, PduMS, ACM, Logger, State) end; maybe_handle_recv_pdu( Domain, Addr, Vsn, Trap, PduMS, ACM, Logger, - #state{filter = FilterMod, domain = ManagerDomain} = State) + #state{filter = FilterMod, transports = Transports} = State) when is_record(Trap, trappdu) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}), case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, trappdu)) of false -> - inc(netIfPduInDrops), - ok; + inc(netIfPduInDrops); _ -> handle_recv_pdu( Domain, Addr, Vsn, Trap, PduMS, ACM, Logger, State) @@ -625,40 +758,34 @@ handle_recv_pdu( #pdu{type = report} = Pdu, _PduMS, ok, _Logger, #state{server = Pid} = _State) -> ?vtrace("received report - ok", []), - Pid ! {snmp_report, {ok, Pdu}, Domain, Addr}, - ok; + Pid ! {snmp_report, {ok, Pdu}, Domain, Addr}; handle_recv_pdu( Domain, Addr, _Vsn, #pdu{type = report} = Pdu, _PduMS, {error, ReqId, Reason}, _Logger, #state{server = Pid} = _State) -> ?vtrace("received report - error", []), - Pid ! {snmp_report, {error, ReqId, Reason, Pdu}, Domain, Addr}, - ok; + Pid ! {snmp_report, {error, ReqId, Reason, Pdu}, Domain, Addr}; handle_recv_pdu( Domain, Addr, _Vsn, #pdu{type = 'snmpv2-trap'} = Pdu, _PduMS, _ACM, _Logger, #state{server = Pid} = _State) -> ?vtrace("received snmpv2-trap", []), - Pid ! {snmp_trap, Pdu, Domain, Addr}, - ok; + Pid ! {snmp_trap, Pdu, Domain, Addr}; handle_recv_pdu( Domain, Addr, _Vsn, Trap, _PduMS, _ACM, _Logger, #state{server = Pid} = _State) when is_record(Trap, trappdu) -> ?vtrace("received trappdu", []), - Pid ! {snmp_trap, Trap, Domain, Addr}, - ok; + Pid ! {snmp_trap, Trap, Domain, Addr}; handle_recv_pdu( Domain, Addr, _Vsn, Pdu, _PduMS, _ACM, _Logger, #state{server = Pid} = _State) when is_record(Pdu, pdu) -> ?vtrace("received pdu", []), - Pid ! {snmp_pdu, Pdu, Domain, Addr}, - ok; + Pid ! {snmp_pdu, Pdu, Domain, Addr}; handle_recv_pdu( _Domain, _Addr, _Vsn, Pdu, _PduMS, ACM, _Logger, _State) -> ?vlog("received unexpected pdu: " - "~n Pdu: ~p" - "~n ACM: ~p", [Pdu, ACM]), - ok. + "~n Pdu: ~p" + "~n ACM: ~p", [Pdu, ACM]). handle_inform_request( @@ -693,15 +820,29 @@ handle_inform_request( ok. handle_inform_response(Ref, Domain, Addr, State) -> - Verbosity = get(verbosity), - spawn_opt( - fun() -> - Log = worker_init(State, Verbosity), - Res = (catch do_handle_inform_response( - Ref, Domain, Addr, State#state{log = Log})), - worker_exit(inform_response, {Domain, Addr}, Res) + worker( + fun (S) -> + do_handle_inform_response(Ref, Domain, Addr, S) end, - [monitor]). + fun (Pid, Class, Reason, Stacktrace) -> + warning_msg( + "Worker process (~p) terminated " + "while processing (outgoing) inform response for %s:~n" + "~w:~w at ~p", + [Pid, snmp_conf:mk_addr_string({Domain, Addr}), + Class, Reason, Stacktrace]) + end, + State). + + %% Verbosity = get(verbosity), + %% spawn_opt( + %% fun() -> + %% Log = worker_init(State, Verbosity), + %% Res = (catch do_handle_inform_response( + %% Ref, Domain, Addr, State#state{log = Log})), + %% worker_exit(inform_response, {Domain, Addr}, Res) + %% end, + %% [monitor]). @@ -721,11 +862,11 @@ do_handle_inform_response(Ref, Domain, Addr, State) -> maybe_send_inform_response( RePdu, Vsn, ACM, Domain, Addr, Logger, - #state{server = Pid, - sock = Sock, - domain = ManagerDomain, - filter = FilterMod}) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + #state{ + server = Pid, + filter = FilterMod, + transports = Transports} = State) -> + {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}), case (catch FilterMod:accept_send_pdu( Arg1, Arg2, pdu_type_of(RePdu))) of @@ -735,8 +876,7 @@ maybe_send_inform_response( _ -> case snmpm_mpd:generate_response_msg(Vsn, RePdu, ACM, Logger) of {ok, Msg} -> - maybe_udp_send( - Domain, Addr, Msg, Sock, FilterMod, ManagerDomain); + maybe_udp_send(Domain, Addr, Msg, State); {discarded, Reason} -> ?vlog("failed generating response message:" "~n Reason: ~p", [Reason]), @@ -778,22 +918,36 @@ irgc_stop(Ref) -> handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State) -> - Verbosity = get(verbosity), - spawn_opt( - fun() -> - Log = worker_init(State, Verbosity), - Res = (catch maybe_handle_send_pdu( - Pdu, Vsn, MsgData, - Domain, Addr, - State#state{log = Log})), - worker_exit(send_pdu, {Domain, Addr}, Res) + worker( + fun (S) -> + maybe_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, S) end, - [monitor]). + fun (Pid, Class, Reason, Stacktrace) -> + warning_msg( + "Worker process (~p) terminated " + "while processing (outgoing) pdu for %s:~n" + "~w:~w at ~p", + [Pid, snmp_conf:mk_addr_string({Domain, Addr}), + Class, Reason, Stacktrace]) + end, + State). + + %% Verbosity = get(verbosity), + %% spawn_opt( + %% fun() -> + %% Log = worker_init(State, Verbosity), + %% Res = (catch maybe_handle_send_pdu( + %% Pdu, Vsn, MsgData, + %% Domain, Addr, + %% State#state{log = Log})), + %% worker_exit(send_pdu, {Domain, Addr}, Res) + %% end, + %% [monitor]). maybe_handle_send_pdu( Pdu, Vsn, MsgData, Domain, Addr, - #state{filter = FilterMod, domain = ManagerDomain} = State) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + #state{filter = FilterMod, transports = Transports} = State) -> + {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}), case (catch FilterMod:accept_send_pdu(Arg1, Arg2, pdu_type_of(Pdu))) of false -> inc(netIfPduOutDrops), @@ -804,9 +958,10 @@ maybe_handle_send_pdu( do_handle_send_pdu( Pdu, Vsn, MsgData, Domain, Addr, - #state{server = Pid, - note_store = NoteStore, - log = Log} = State) -> + #state{ + server = Pid, + note_store = NoteStore, + log = Log} = State) -> Logger = logger(Log, write, Domain, Addr), case (catch snmpm_mpd:generate_msg( Vsn, NoteStore, Pdu, MsgData, Logger)) of @@ -821,43 +976,55 @@ do_handle_send_pdu( ok end. + maybe_udp_send( Domain, Addr, Msg, - #state{sock = Sock, filter = FilterMod, domain = ManagerDomain}) -> - maybe_udp_send(Domain, Addr, Msg, Sock, FilterMod, ManagerDomain). - -maybe_udp_send(Domain, Addr, Msg, Sock, FilterMod, ManagerDomain) -> - {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}), + #state{filter = FilterMod, transports = Transports}) -> + To = {Domain, Addr}, + {Arg1, Arg2} = fix_filter_address(Transports, To), case (catch FilterMod:accept_send(Arg1, Arg2)) of false -> inc(netIfMsgOutDrops), ok; _ -> - %% XXX There should be some kind of lookup of socket - %% from transport domain here - {Ip, Port} = Addr, - udp_send(Sock, Ip, Port, Msg) + case select_transport_from_domain(Domain, Transports) of + false -> + error_msg( + "Can not find transport~n" + " size: ~p~n" + " to: ~s", + [sz(Msg), format_address(To)]); + #transport{socket = Socket} -> + udp_send(Socket, Addr, Msg) + end end. - - -udp_send(Sock, Ip, Port, Msg) -> - case (catch gen_udp:send(Sock, Ip, Port, Msg)) of + +udp_send(Sock, To, Msg) -> + {IpAddr, IpPort} = + case To of + {Domain, Addr} when is_atom(Domain) -> + Addr; + {_, P} = Addr when is_integer(P) -> + Addr + end, + try gen_udp:send(Sock, IpAddr, IpPort, Msg) of ok -> ?vdebug("sent ~w bytes to ~w:~w [~w]", - [sz(Msg), Ip, Port, Sock]), + [sz(Msg), IpAddr, IpPort, Sock]), ok; {error, Reason} -> - error_msg("failed sending message to ~p:~p: " - "~n ~p", [Ip, Port, Reason]), - ok; - Error -> - error_msg("failed sending message to ~p:~p: " - "~n ~p", [Ip, Port, Error]), - ok + error_msg("failed sending message to ~p:~p:~n" + " ~p",[IpAddr, IpPort, Reason]) + catch + error:Error -> + error_msg("failed sending message to ~p:~p:~n" + " error:~p~n" + " ~p", + [IpAddr, IpPort, Error, erlang:get_stacktrace()]) end. sz(B) when is_binary(B) -> - size(B); + byte_size(B); sz(L) when is_list(L) -> length(L); sz(_) -> @@ -1047,79 +1214,152 @@ handle_set_log_type(State, _NewType) -> {State, {error, not_enabled}}. -%% ------------------------------------------------------------------- - -worker_init(#state{log = undefined = Log}, Verbosity) -> - worker_init2(Log, Verbosity); -worker_init(#state{log = {Name, Log, Type}}, Verbosity) -> - case snmp_log:open(Name, Log) of - {ok, NewLog} -> - worker_init2({Name, NewLog, Type}, Verbosity); - {error, Reason} -> - warning_msg("NetIf worker ~p failed opening ATL: " - "~n ~p", [self(), Reason]), - NewLog = undefined, - worker_init2({Name, NewLog, Type}, Verbosity) - end; -worker_init(State, Verbosity) -> - ?vinfo("worker_init -> entry with invalid data: " - "~n State: ~p" - "~n Verbosity: ~p", [State, Verbosity]), - exit({worker_init, State, Verbosity}). - -worker_init2(Log, Verbosity) -> - put(sname, mnifw), - put(verbosity, Verbosity), - Log. - - -worker_exit(Tag, Info, Result) -> - exit({net_if_worker, {Tag, Info, Result}}). - -handle_worker_exit(_, {_, _, ok}) -> - ok; -handle_worker_exit(Pid, {udp, {Domain, Addr}, ExitStatus}) -> - warning_msg( - "Worker process (~p) terminated " - "while processing (incomming) message from %s:~n" - "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]), - ok; -handle_worker_exit(Pid, {send_pdu, {Domain, Addr}, ExitStatus}) -> - warning_msg( - "Worker process (~p) terminated " - "while processing (outgoing) pdu for %s:~n" - "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]), - ok; -handle_worker_exit(Pid, {inform_response, {Domain, Addr}, ExitStatus}) -> - warning_msg( - "Worker process (~p) terminated " - "while processing (outgoing) inform response for %s:~n" - "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]), - ok; -handle_worker_exit(_, _) -> - ok. - +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. %% If the manager uses legacy snmpUDPDomain e.g has not set %% {domain, _}, then make sure snmpm_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(_ManagerDomain, {Domain, _} = Address) - when is_atom(Domain) -> - Address; -fix_filter_address(snmpUDPDomain, {_, Port} = Addr) - when is_integer(Port) -> - Addr. +fix_filter_address(Transports, Address) -> + DefaultDomain = snmpm_config:default_transport_domain(), + case Transports of + [#transport{domain = DefaultDomain}, DefaultDomain] -> + case Address of + {Domain, Addr} when is_atom(Domain) -> + Addr; + {_, IpPort} = Addr when is_integer(IpPort) -> + Addr + end; + _ -> + Address + end. address(Domain, Addr) when is_atom(Domain) -> {Domain, Addr}; address(Ip, Port) when is_integer(Port) -> {snmpm_config:default_transport_domain(), {Ip, Port}}. +format_address(Address) -> + iolist_to_binary(snmp_conf:mk_addr_string(Address)). + +%% ------------------------------------------------------------------- + +worker(Worker, Failer, State) -> + Verbosity = get(verbosity), + spawn_opt( + fun () -> + try + put(sname, mnifw), + put(verbosity, Verbosity), + Worker(worker_init(State)) + of + Result -> + %% Winds up in handle_info {'DOWN', ...} + erlang:exit({net_if_worker, Result}) + catch + Class:Reason -> + %% Winds up in handle_info {'DOWN', ...} + erlang:exit( + {net_if_worker, Failer, + Class, Reason, erlang:get_stacktrace()}) + end + end, + [monitor]). + +worker_init(#state{log = undefined} = State) -> + State; +worker_init(#state{log = {Name, Log, Type}} = State) -> + case snmp_log:open(Name, Log) of + {ok, NewLog} -> + State#state{log = {Name, NewLog, Type}}; + {error, Reason} -> + warning_msg("NetIf worker ~p failed opening ATL: " + "~n ~p", [self(), Reason]), + State#state{log = {Name, undefined, Type}} + end; +worker_init(State) -> + ?vinfo("worker_init -> entry with invalid data: " + "~n State: ~p", [State]), + erlang:error({worker_init, State}). + +%% worker_init2(Log, Verbosity) -> +%% put(sname, mnifw), +%% put(verbosity, Verbosity), +%% Log. + + +%% worker_exit(Tag, Info, Result) -> +%% exit({net_if_worker, {Tag, Info, Result}}). + +%% handle_worker_exit(_, {_, _, {Result}}) -> +%% Result; +%% handle_worker_exit( +%% Pid, {udp, {Domain, Addr}, {Class, Reason, Stacktrace}) -> +%% warning_msg( +%% "Worker process (~p) terminated " +%% "while processing (incomming) message from %s:~n" +%% "~w:~w at ~p", +%% [Pid, snmp_conf:mk_addr_string({Domain, Addr}), +%% Class, Reason, Stacktrace]), +%% ok; +%% handle_worker_exit( +%% Pid, {send_pdu, {Domain, Addr}, {Class, Reason, Stacktrace}) -> +%% warning_msg( +%% "Worker process (~p) terminated " +%% "while processing (outgoing) pdu for %s:~n" +%% "~w:~w at ~p", +%% [Pid, snmp_conf:mk_addr_string({Domain, Addr}), +%% Class, Reason, Stacktrace]), +%% ok; +%% handle_worker_exit( +%% Pid, {inform_response, {Domain, Addr}, {Class, Reason, Stacktrace}}) -> +%% warning_msg( +%% "Worker process (~p) terminated " +%% "while processing (outgoing) inform response for %s:~n" +%% "~w:~w at ~p", +%% [Pid, snmp_conf:mk_addr_string({Domain, Addr}), +%% Class, Reason, Stacktrace]), +%% ok; +%% handle_worker_exit(Pid, Term) -> +%% warning_msg( +%% "Worker process (~p) terminated for strange reason: ~p", +%% [Pid, Term]). + + +%% %% If the manager uses legacy snmpUDPDomain e.g has not set +%% %% {domain, _}, then make sure snmpm_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(_ManagerDomain, {Domain, _} = Address) +%% when is_atom(Domain) -> +%% Address; +%% fix_filter_address(snmpUDPDomain, {_, Port} = Addr) +%% when is_integer(Port) -> +%% Addr. + +%% address(Domain, Addr) when is_atom(Domain) -> +%% {Domain, Addr}; +%% address(Ip, Port) when is_integer(Port) -> +%% {snmpm_config:default_transport_domain(), {Ip, Port}}. + +%% format_address(Address) -> +%% iolist_to_binary(snmp_conf:mk_addr_string(Address)). + %% ------------------------------------------------------------------- make_response_pdu(#pdu{request_id = ReqId, varbinds = Vbs}) -> @@ -1170,7 +1410,7 @@ logger({_Name, Log, Types}, Type, Domain, Addr) -> AddrString = iolist_to_binary(snmp_conf:mk_addr_string({Domain, Addr})), fun(Msg) -> - snmp_log:log(Log, Msg, Domain, AddrString) + snmp_log:log(Log, Msg, AddrString) end; false -> fun(_) -> @@ -1205,10 +1445,11 @@ get_opt(Opts, Key, Def) -> %% ------------------------------------------------------------------- -get_info(#state{sock = Id}) -> +get_info(#state{transports = Transports}) -> ProcSize = proc_mem(self()), - PortInfo = get_port_info(Id), - [{process_memory, ProcSize}, {port_info, PortInfo}]. + [{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/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index 5ce9cf9e5c..c514eafd7d 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -60,16 +60,19 @@ groups() -> [{ipv4, [], [{group, snmpget}, {group, snmptrapd}, + {group, snmpd_mt}, {group, snmpd} ]}, {ipv6, [], [{group, snmpget}, {group, snmptrapd}, + {group, snmpd_mt}, {group, snmpd} ]}, {ipv4_ipv6, [], [{group, snmpget}, {group, snmptrapd}, + {group, snmpd_mt}, {group, snmpd} ]}, %% @@ -77,6 +80,8 @@ groups() -> [erlang_agent_netsnmp_get]}, {snmptrapd, [], [erlang_agent_netsnmp_inform]}, + {snmpd_mt, [], + [erlang_manager_netsnmp_get]}, {snmpd, [], [erlang_manager_netsnmp_get]} ]. @@ -100,9 +105,16 @@ init_per_group(snmpget = Exec, Config) -> init_per_group(snmptrapd = Exec, Config) -> %% From Ubuntu package snmpd init_per_group_agent(Exec, Config); +init_per_group(snmpd_mt, Config) -> + %% From Ubuntu package snmp + init_per_group_manager( + snmpd, + [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(snmpd = Exec, Config) -> %% From Ubuntu package snmp - init_per_group_manager(Exec, Config); + init_per_group_manager( + Exec, + [{manager_net_if_module, snmpm_net_if} | Config]); %% init_per_group(_, Config) -> Config. @@ -462,10 +474,12 @@ agent_app_env(Config) -> manager_app_env(Config) -> Dir = ?config(priv_dir, Config), Vsns = ?config(snmp_versions, Config), + NetIfModule = ?config(manager_net_if_module, Config), [{versions, Vsns}, {audit_trail_log, [{type, read_write}, {dir, Dir}, {size, {10240, 10}}]}, + {net_if, [{module, NetIfModule}]}, {config, [{dir, Dir}, {db_dir, Dir}, {verbosity, trace}]} -- cgit v1.2.3