aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/misc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/misc')
-rw-r--r--lib/snmp/src/misc/Makefile122
-rw-r--r--lib/snmp/src/misc/depend.mk58
-rw-r--r--lib/snmp/src/misc/modules.mk33
-rw-r--r--lib/snmp/src/misc/snmp_conf.erl421
-rw-r--r--lib/snmp/src/misc/snmp_config.erl2348
-rw-r--r--lib/snmp/src/misc/snmp_debug.hrl38
-rw-r--r--lib/snmp/src/misc/snmp_log.erl584
-rw-r--r--lib/snmp/src/misc/snmp_mini_mib.erl151
-rw-r--r--lib/snmp/src/misc/snmp_misc.erl465
-rw-r--r--lib/snmp/src/misc/snmp_note_store.erl450
-rw-r--r--lib/snmp/src/misc/snmp_pdus.erl770
-rw-r--r--lib/snmp/src/misc/snmp_usm.erl367
-rw-r--r--lib/snmp/src/misc/snmp_verbosity.erl161
-rw-r--r--lib/snmp/src/misc/snmp_verbosity.hrl64
14 files changed, 6032 insertions, 0 deletions
diff --git a/lib/snmp/src/misc/Makefile b/lib/snmp/src/misc/Makefile
new file mode 100644
index 0000000000..48d76bdbed
--- /dev/null
+++ b/lib/snmp/src/misc/Makefile
@@ -0,0 +1,122 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+
+EBIN = ../../ebin
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN = $(SNMP_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN)
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+HRL_FILES = $(HRLS:%=%.hrl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+
+# ----------------------------------------------------
+# SNMP FLAGS
+# ----------------------------------------------------
+ifeq ($(SNMP_DEFAULT_VERBOSITY),)
+ SNMP_FLAGS = -Ddefault_verbosity=silence
+else
+ SNMP_FLAGS = -Ddefault_verbosity=$(SNMP_DEFAULT_VERBOSITY)
+endif
+
+# SNMP_DEBUG=d
+ifeq ($(SNMP_DEBUG),d)
+ SNMP_FLAGS += -Dsnmp_debug
+endif
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/snmp/ebin
+
+ifeq ($(WARN_UNUSED_VARS),true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+ERL_COMPILE_FLAGS += -I../../include \
+ -I../compile \
+ -Dversion=\"$(VSN)$(PRE_VSN)\" \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}' \
+ -I$(ERL_TOP)/lib/stdlib \
+ $(SNMP_FLAGS)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug:
+ @$(MAKE) TYPE=debug opt
+
+opt: $(TARGET_FILES)
+
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+info:
+ @echo "TARGET_FILES: $(TARGET_FILES)"
+ @echo ""
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/misc
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/misc
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+# $(INSTALL_DIR) $(RELSYSDIR)/include
+# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
+include depend.mk
diff --git a/lib/snmp/src/misc/depend.mk b/lib/snmp/src/misc/depend.mk
new file mode 100644
index 0000000000..dea0f75048
--- /dev/null
+++ b/lib/snmp/src/misc/depend.mk
@@ -0,0 +1,58 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+$(EBIN)/snmp_conf.$(EMULATOR): \
+ snmp_conf.erl \
+ snmp_verbosity.hrl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_config.$(EMULATOR): \
+ snmp_config.erl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_log.$(EMULATOR): \
+ snmp_log.erl \
+ snmp_verbosity.hrl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_misc.$(EMULATOR): \
+ snmp_misc.erl \
+ snmp_verbosity.hrl \
+ ../compile/snmpc_misc.hrl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_note_store.$(EMULATOR): \
+ snmp_note_store.erl \
+ ../misc/snmp_debug.hrl \
+ ../misc/snmp_verbosity.hrl
+
+$(EBIN)/snmp_pdu.$(EMULATOR): \
+ snmp_pdu.erl \
+ snmp_verbosity.hrl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_usm.$(EMULATOR): \
+ snmp_usm.erl \
+ snmp_verbosity.hrl \
+ ../../include/snmp_types.hrl
+
+$(EBIN)/snmp_verbosity.$(EMULATOR): \
+ snmp_verbosity.erl
+
+
diff --git a/lib/snmp/src/misc/modules.mk b/lib/snmp/src/misc/modules.mk
new file mode 100644
index 0000000000..32092aaae2
--- /dev/null
+++ b/lib/snmp/src/misc/modules.mk
@@ -0,0 +1,33 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+MODULES = \
+ snmp_conf \
+ snmp_config \
+ snmp_log \
+ snmp_mini_mib \
+ snmp_misc \
+ snmp_note_store \
+ snmp_pdus \
+ snmp_usm \
+ snmp_verbosity
+
+HRLS = snmp_verbosity snmp_debug
+
+
diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl
new file mode 100644
index 0000000000..63762ac17b
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_conf.erl
@@ -0,0 +1,421 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Basic module for reading and verifying config files
+%%----------------------------------------------------------------------
+-module(snmp_conf).
+
+
+%% External exports
+-export([read_files/2, read/2]).
+
+%% Basic (type) check functions
+-export([check_mandatory/2,
+ check_integer/1, check_integer/2,
+
+ check_string/1, check_string/2,
+
+ check_atom/2,
+
+ check_timer/1,
+
+ check_ip/1, check_taddress/1,
+
+ check_packet_size/1,
+
+ check_oid/1,
+
+ check_mp_model/1,
+ check_sec_model/1, check_sec_model/2, check_sec_model/3,
+ check_sec_level/1,
+
+ all_integer/1
+ ]).
+
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+-include("SNMP-FRAMEWORK-MIB.hrl").
+
+-define(VMODULE,"CONF").
+-include("snmp_verbosity.hrl").
+
+
+%%-----------------------------------------------------------------
+
+read_files(Dir, Files) when is_list(Dir) andalso is_list(Files) ->
+ read_files(Dir, Files, []).
+
+read_files(_Dir, [], Res) ->
+ lists:reverse(Res);
+read_files(Dir, [{Gen, Filter, Check, FileName}|Files], Res)
+ when is_function(Filter) andalso
+ is_function(Check) andalso
+ is_list(FileName) ->
+ ?vdebug("read_files -> entry with"
+ "~n FileName: ~p", [FileName]),
+ File = filename:join(Dir, FileName),
+ case file:read_file_info(File) of
+ {ok, _} ->
+ Confs = read(File, Check),
+ read_files(Dir, Files, [Filter(Confs)|Res]);
+ {error, R} ->
+ ?vlog("failed reading file info for ~s: "
+ "~n ~p", [FileName, R]),
+ Gen(Dir),
+ read_files(Dir, Files, [Filter([])|Res])
+ end.
+
+
+%% Ret. Res | exit(Reason)
+read(File, Check) when is_function(Check) ->
+ ?vdebug("read -> entry with"
+ "~n File: ~p", [File]),
+ Fd = open_file(File),
+
+ case loop(Fd, [], Check, 1, File) of
+ {error, Reason} ->
+ file:close(Fd),
+ error(Reason);
+ {ok, Res} ->
+ file:close(Fd),
+ Res
+ end.
+
+open_file(File) ->
+ case file:open(File, [read]) of
+ {ok, Fd} ->
+ Fd;
+ {error, Reason} ->
+ error({failed_open, File, Reason})
+ end.
+
+loop(Fd, Res, Check, StartLine, File) ->
+ case do_read(Fd, "", StartLine) of
+ {ok, Row, EndLine} ->
+ ?vtrace("loop -> "
+ "~n Row: ~p"
+ "~n EndLine: ~p", [Row, EndLine]),
+ case (catch Check(Row)) of
+ ok ->
+ ?vtrace("loop -> ok", []),
+ loop(Fd, [Row | Res], Check, EndLine, File);
+ {ok, NewRow} ->
+ ?vtrace("loop -> ok: "
+ "~n NewRow: ~p", [NewRow]),
+ loop(Fd, [NewRow | Res], Check, EndLine, File);
+ {error, Reason} ->
+ ?vtrace("loop -> check error: "
+ "~n Reason: ~p", [Reason]),
+ {error, {failed_check, File, StartLine, EndLine, Reason}};
+ Error ->
+ ?vtrace("loop -> check failure: "
+ "~n Error: ~p", [Error]),
+ {error, {failed_check, File, StartLine, EndLine, Error}}
+ end;
+ {error, EndLine, Error} ->
+ ?vtrace("loop -> read failure: "
+ "~n Error: ~p", [Error]),
+ {error, {failed_reading, File, StartLine, EndLine, Error}};
+ eof ->
+ {ok, Res}
+ end.
+
+
+do_read(Io, Prompt, StartLine) ->
+ case io:request(Io, {get_until,Prompt,erl_scan,tokens,[StartLine]}) of
+ {ok, Toks, EndLine} ->
+ case erl_parse:parse_term(Toks) of
+ {ok, Term} ->
+ {ok, Term, EndLine};
+ {error, {Line, erl_parse, Error}} ->
+ {error, Line, {parse_error, Error}}
+ end;
+ {error,E,EndLine} ->
+ {error, EndLine, E};
+ {eof, _EndLine} ->
+ eof;
+ Other ->
+ Other
+ end.
+
+
+%%-----------------------------------------------------------------
+
+
+check_mandatory(L, [{Key, Value}|T]) ->
+ case lists:keymember(Key, 1, L) of
+ true ->
+ check_mandatory(L, T);
+ false when Value == mandatory ->
+ error({missing_mandatory, Key});
+ false ->
+ {value, V} = Value,
+ check_mandatory([{Key, V} | L], T)
+ end;
+check_mandatory(L, []) ->
+ {ok, L}.
+
+
+%% ---------
+
+check_integer(I) -> check_integer(I, any).
+
+check_integer(I, any) when is_integer(I) -> ok;
+check_integer(I, pos) when is_integer(I), I > 0 -> ok;
+check_integer(I, neg) when is_integer(I), I < 0 -> ok;
+check_integer(I1, {gt, I2})
+ when is_integer(I1) andalso is_integer(I2) andalso (I1 > I2) -> ok;
+check_integer(I1, {gte, I2})
+ when is_integer(I1) andalso is_integer(I2) andalso (I1 >= I2) -> ok;
+check_integer(I1, {lt, I2})
+ when is_integer(I1) andalso is_integer(I2) andalso (I1 < I2) -> ok;
+check_integer(I1, {lte, I2})
+ when is_integer(I1) andalso is_integer(I2) andalso (I1 =< I2) -> ok;
+check_integer(I1, {eq, I1})
+ when is_integer(I1) -> ok;
+check_integer(I, {range, L, U})
+ when (is_integer(I) andalso
+ is_integer(L) andalso
+ is_integer(U) andalso
+ (I >= L) andalso (I =< U)) -> ok;
+check_integer(I, _) -> error({invalid_integer, I}).
+
+check_packet_size(S) ->
+ case (catch check_integer(S, {range, 484, 2147483647})) of
+ ok ->
+ ok;
+ {error, _} ->
+ error({invalid_packet_size, S})
+ end.
+
+%% ---------
+
+check_string(X) when is_list(X) -> ok;
+check_string(X) -> error({invalid_string, X}).
+
+check_string(X, any)
+ when is_list(X) -> ok;
+check_string(X, {gt, Len})
+ when is_list(X) andalso (length(X) > Len) -> ok;
+check_string(X, {gt, _Len})
+ when is_list(X) -> error({invalid_length, X});
+check_string(X, {gte, Len})
+ when is_list(X) andalso (length(X) >= Len) -> ok;
+check_string(X, {gte, _Len})
+ when is_list(X) -> error({invalid_length, X});
+check_string(X, {lt, Len})
+ when is_list(X) andalso (length(X) < Len) -> ok;
+check_string(X, {lt, _Len})
+ when is_list(X) -> error({invalid_length, X});
+check_string(X, {lte, Len})
+ when is_list(X) andalso (length(X) =< Len) -> ok;
+check_string(X, {lte, _Len})
+ when is_list(X) -> error({invalid_length, X});
+check_string(X, Len)
+ when is_list(X) andalso is_integer(Len) andalso (length(X) =:= Len) -> ok;
+check_string(X, _Len) when is_list(X) -> error({invalid_length, X});
+check_string(X, _Len) -> error({invalid_string, X}).
+
+
+check_atom(X, Atoms) ->
+ case lists:keysearch(X, 1, Atoms) of
+ {value, {X, Val}} ->
+ {ok, Val};
+ _ ->
+ error({invalid_atom, X, Atoms})
+ end.
+
+
+%% ---------
+
+check_mp_model(MPModel) when is_atom(MPModel) ->
+ All = [{v1, ?MP_V1}, {v2c, ?MP_V2C}, {v3, ?MP_V3}],
+ check_atom(MPModel, All);
+check_mp_model(?MP_V1) ->
+ {ok, ?MP_V1};
+check_mp_model(?MP_V2C) ->
+ {ok, ?MP_V2C};
+check_mp_model(?MP_V3) ->
+ {ok, ?MP_V3};
+check_mp_model(BadMpModel) ->
+ error({invalid_mp_model, BadMpModel}).
+
+
+%% ---------
+
+check_sec_model(SecModel) when is_atom(SecModel) ->
+ check_sec_model(SecModel, []);
+check_sec_model(?SEC_ANY) ->
+ {ok, ?SEC_ANY};
+check_sec_model(?SEC_V1) ->
+ {ok, ?SEC_V1};
+check_sec_model(?SEC_V2C) ->
+ {ok, ?SEC_V2C};
+check_sec_model(?SEC_USM) ->
+ {ok, ?SEC_USM};
+check_sec_model(BadSecModel) ->
+ error({invalid_sec_model, BadSecModel}).
+
+check_sec_model(SecModel, Exclude) when is_atom(SecModel) ->
+ All = [{any, ?SEC_ANY},
+ {v1, ?SEC_V1},
+ {v2c, ?SEC_V2C},
+ {usm, ?SEC_USM}],
+ Alt = [{X, Y} || {X, Y} <- All, not lists:member(X, Exclude)],
+ case (catch check_atom(SecModel, Alt) ) of
+ {error, _} ->
+ error({invalid_sec_model, SecModel});
+ OK ->
+ OK
+ end;
+check_sec_model(BadSecModel, _Exclude) ->
+ error({invalid_sec_model, BadSecModel}).
+
+check_sec_model(v1, v1, Exclude) ->
+ check_sec_model2(v1, ?SEC_V1, Exclude);
+check_sec_model(v1, SecModel, _Exclude) ->
+ error({invalid_sec_model, v1, SecModel});
+check_sec_model(v2c, v2c, Exclude) ->
+ check_sec_model2(v2c, ?SEC_V2C, Exclude);
+check_sec_model(v2c, SecModel, _Exclude) ->
+ error({invalid_sec_model, v2c, SecModel});
+check_sec_model(v3, usm, Exclude) ->
+ check_sec_model2(v3, ?SEC_USM, Exclude);
+check_sec_model(v3, SecModel, _Exclude) ->
+ error({invalid_sec_model, v3, SecModel});
+check_sec_model(M1, M2, _Exclude) ->
+ error({invalid_sec_model, M1, M2}).
+
+check_sec_model2(SecModel, SM, Exclude) ->
+ case lists:member(SecModel, Exclude) of
+ false ->
+ {ok, SM};
+ true ->
+ error({invalid_sec_model, SecModel})
+ end.
+
+
+%% ---------
+
+check_sec_level(SecLevel) when is_atom(SecLevel) ->
+ All = [{noAuthNoPriv, ?'SnmpSecurityLevel_noAuthNoPriv'},
+ {authNoPriv, ?'SnmpSecurityLevel_authNoPriv'},
+ {authPriv, ?'SnmpSecurityLevel_authPriv'}],
+ case (catch check_atom(SecLevel, All)) of
+ {error, _} ->
+ error({invalid_sec_level, SecLevel});
+ OK ->
+ OK
+ end;
+check_sec_level(?'SnmpSecurityLevel_noAuthNoPriv' = SL) ->
+ {ok, SL};
+check_sec_level(?'SnmpSecurityLevel_authNoPriv' = SL) ->
+ {ok, SL};
+check_sec_level(?'SnmpSecurityLevel_authPriv' = SL) ->
+ {ok, SL};
+check_sec_level(BadSecLevel) ->
+ error({invalid_sec_level, BadSecLevel}).
+
+
+%% ---------
+
+check_taddress(X) when is_list(X) andalso (length(X) =:= 6) ->
+ case (catch all_integer(X)) of
+ true ->
+ ok;
+ false ->
+ error({invalid_taddress, X})
+ end;
+check_taddress(X) ->
+ error({invalid_taddress, X}).
+
+
+%% ---------
+
+check_timer(infinity) ->
+ {ok, infinity};
+check_timer(T) when is_record(T, snmp_incr_timer) ->
+ {ok, T};
+check_timer({WaitFor, Factor, Incr, Retry} = T) ->
+ case (catch do_check_timer(WaitFor, Factor, Incr, Retry)) of
+ ok ->
+ {ok, #snmp_incr_timer{wait_for = WaitFor,
+ factor = Factor,
+ incr = Incr,
+ max_retries = Retry}};
+ _Err ->
+ error({invalid_timer, T})
+ end;
+check_timer(Timeout) ->
+ case (catch check_integer(Timeout, {gt, 0})) of
+ ok ->
+ {ok, #snmp_incr_timer{wait_for = Timeout,
+ factor = 1,
+ incr = 0,
+ max_retries = 0}};
+ _Err ->
+ error({invalid_timer, Timeout})
+ end.
+
+do_check_timer(WaitFor, Factor, Incr, Retry) ->
+ check_integer(WaitFor, {gt, 0}),
+ check_integer(Factor, {gt, 0}),
+ check_integer(Incr, {gte, 0}),
+ check_integer(Retry, {gte, 0}),
+ ok.
+
+%% ---------
+
+check_ip(X) when is_list(X) andalso (length(X) =:= 4) ->
+ case (catch all_integer(X)) of
+ true ->
+ ok;
+ false ->
+ error({invalid_ip_address, X})
+ end;
+check_ip(X) ->
+ error({invalid_ip_address, X}).
+
+
+%% ---------
+
+check_oid([E1,E2|_] = X) when E1 * 40 + E2 =< 255 ->
+ case all_integer(X) of
+ true ->
+ ok;
+ _ ->
+ error({invalid_object_identifier, X})
+ end;
+check_oid(X) ->
+ error({invalid_object_identifier, X}).
+
+
+%% ---------
+
+all_integer([H|T]) when is_integer(H) -> all_integer(T);
+all_integer([_H|_T]) -> false;
+all_integer([]) -> true.
+
+
+%% ---------
+
+error(Reason) ->
+ throw({error, Reason}).
+
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
new file mode 100644
index 0000000000..ad41eaf160
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -0,0 +1,2348 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_config).
+
+-include_lib("kernel/include/file.hrl").
+-include("snmp_types.hrl").
+
+-export([config/0]).
+
+-export([write_config_file/4, append_config_file/4, read_config_file/3]).
+
+-export([write_agent_snmp_files/7, write_agent_snmp_files/12,
+
+ write_agent_snmp_conf/5,
+ write_agent_snmp_context_conf/1,
+ write_agent_snmp_community_conf/1,
+ write_agent_snmp_standard_conf/2,
+ write_agent_snmp_target_addr_conf/4,
+ write_agent_snmp_target_addr_conf/6,
+ write_agent_snmp_target_params_conf/2,
+ write_agent_snmp_notify_conf/2,
+ write_agent_snmp_usm_conf/5,
+ write_agent_snmp_vacm_conf/3,
+
+ write_manager_snmp_files/8,
+ write_manager_snmp_conf/5,
+ write_manager_snmp_users_conf/2,
+ write_manager_snmp_agents_conf/2,
+ write_manager_snmp_usm_conf/2
+
+ ]).
+
+-export([write_agent_config/3,
+ update_agent_config/2,
+
+ write_agent_context_config/3,
+ update_agent_context_config/2,
+
+ write_agent_community_config/3,
+ update_agent_community_config/2,
+
+ write_agent_standard_config/3,
+ update_agent_standard_config/2,
+
+ write_agent_target_addr_config/3,
+ update_agent_target_addr_config/2,
+
+ write_agent_target_params_config/3,
+ update_agent_target_params_config/2,
+
+ write_agent_notify_config/3,
+ update_agent_notify_config/2,
+
+ write_agent_vacm_config/3,
+ update_agent_vacm_config/2,
+
+ write_agent_usm_config/3,
+ update_agent_usm_config/2,
+
+ write_manager_config/3,
+ update_manager_config/2,
+
+ write_manager_users_config/3,
+ update_manager_users_config/2,
+
+ write_manager_agents_config/3,
+ update_manager_agents_config/2,
+
+ write_manager_usm_config/3,
+ update_manager_usm_config/2
+ ]).
+
+
+%%----------------------------------------------------------------------
+%% Handy SNMP configuration
+%%----------------------------------------------------------------------
+
+config() ->
+ case (catch config2()) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason};
+ E ->
+ {error, {failed, E}}
+ end.
+
+
+config2() ->
+ intro(),
+ SysAgentConfig =
+ case config_agent() of
+ [] ->
+ [];
+ SAC ->
+ [{agent, SAC}]
+ end,
+ SysMgrConfig =
+ case config_manager() of
+ [] ->
+ [];
+ SMC ->
+ [{manager, SMC}]
+ end,
+ config_sys(SysAgentConfig ++ SysMgrConfig),
+ ok.
+
+
+intro() ->
+ i("~nSimple SNMP configuration tool (version ~s)", [?version]),
+ i("------------------------------------------------"),
+ i("Note: Non-trivial configurations still has to be"),
+ i(" done manually. IP addresses may be entered "),
+ i(" as dront.ericsson.se (UNIX only) or"),
+ i(" 123.12.13.23"),
+ i("------------------------------------------------"),
+ ok.
+
+
+config_agent() ->
+ case (catch snmp_agent2()) of
+ ok ->
+ [];
+ {ok, SysConf} ->
+ SysConf;
+ {error, Reason} ->
+ error(Reason);
+ {'EXIT', Reason} ->
+ error(Reason);
+ E ->
+ error({agent_config_failed, E})
+ end.
+
+snmp_agent2() ->
+ case ask("~nConfigure an agent (y/n)?", "y", fun verify_yes_or_no/1) of
+ yes ->
+ {Vsns, ConfigDir, SysConf} = config_agent_sys(),
+ config_agent_snmp(ConfigDir, Vsns),
+ {ok, SysConf};
+ no ->
+ ok
+ end.
+
+
+config_manager() ->
+ case (catch config_manager2()) of
+ ok ->
+ [];
+ {ok, SysConf} ->
+ SysConf;
+ {error, Reason} ->
+ error(Reason);
+ {'EXIT', Reason} ->
+ error(Reason);
+ E ->
+ error({manager_config_failed, E})
+ end.
+
+config_manager2() ->
+ case ask("~nConfigure a manager (y/n)?", "y", fun verify_yes_or_no/1) of
+ yes ->
+ {Vsns, ConfigDir, SysConf} = config_manager_sys(),
+ config_manager_snmp(ConfigDir, Vsns),
+ {ok, SysConf};
+ no ->
+ ok
+ end.
+
+
+config_sys(SysConfig) ->
+ i("~n--------------------"),
+ {ok, DefDir} = file:get_cwd(),
+ ConfigDir = ask("Configuration directory for system file (absolute path)?",
+ DefDir, fun verify_dir/1),
+ write_sys_config_file(ConfigDir, SysConfig).
+
+
+%% -------------------
+
+config_agent_sys() ->
+ i("~nAgent system config: "
+ "~n--------------------"),
+ Prio = ask("1. Agent process priority (low/normal/high)",
+ "normal", fun verify_prio/1),
+ Vsns = ask("2. What SNMP version(s) should be used "
+ "(1,2,3,1&2,1&2&3,2&3)?", "3", fun verify_versions/1),
+ %% d("Vsns: ~p", [Vsns]),
+ {ok, DefDir} = file:get_cwd(),
+ {DefConfDir, Warning} = default_agent_dir(DefDir),
+ ConfQ =
+ if
+ Warning == "" ->
+ "3. Configuration directory (absolute path)?";
+ true ->
+ lists:flatten(
+ io_lib:format("3. Configuration directory (absolute path)?"
+ "~n ~s", [Warning]))
+ end,
+ ConfigDir = ask(ConfQ, DefConfDir, fun verify_dir/1),
+ ConfigVerb = ask("4. Config verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ DbDir = ask("5. Database directory (absolute path)?", DefDir,
+ fun verify_dir/1),
+ MibStorageType = ask("6. Mib storage type (ets/dets/mnesia)?", "ets",
+ fun verify_mib_storage_type/1),
+ MibStorage =
+ case MibStorageType of
+ ets ->
+ ets;
+ dets ->
+ DetsDir = ask("6b. Mib storage directory (absolute path)?",
+ DbDir, fun verify_dir/1),
+ DetsAction = ask("6c. Mib storage [dets] database start "
+ "action "
+ "(default/clear/keep)?",
+ "default", fun verify_mib_storage_action/1),
+ case DetsAction of
+ default ->
+ {dets, DetsDir};
+ _ ->
+ {dets, DetsDir, DetsAction}
+ end;
+ mnesia ->
+% Nodes = ask("Mib storage nodes?", "none",
+% fun verify_mib_storage_nodes/1),
+ Nodes = [],
+ MnesiaAction = ask("6b. Mib storage [mnesia] database start "
+ "action "
+ "(default/clear/keep)?",
+ "default", fun verify_mib_storage_action/1),
+ case MnesiaAction of
+ default ->
+ {mnesia, Nodes};
+ _ ->
+ {mnesia, Nodes, MnesiaAction}
+ end
+ end,
+ TargetCacheVerb = ask("7. Target cache verbosity "
+ "(silence/info/log/debug/trace)?", "silence",
+ fun verify_verbosity/1),
+ SymStoreVerb = ask("8. Symbolic store verbosity "
+ "(silence/info/log/debug/trace)?", "silence",
+ fun verify_verbosity/1),
+ LocalDbVerb = ask("9. Local DB verbosity "
+ "(silence/info/log/debug/trace)?", "silence",
+ fun verify_verbosity/1),
+ LocalDbRepair = ask("10. Local DB repair (true/false/force)?", "true",
+ fun verify_dets_repair/1),
+ LocalDbAutoSave = ask("11. Local DB auto save (infinity/milli seconds)?",
+ "5000", fun verify_dets_auto_save/1),
+ ErrorMod = ask("12. Error report module?", "snmpa_error_logger", fun verify_module/1),
+ Type = ask("13. Agent type (master/sub)?", "master",
+ fun verify_agent_type/1),
+ AgentConfig =
+ case Type of
+ master ->
+ MasterAgentVerb = ask("14. Master-agent verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ ForceLoad = ask("15. Shall the agent re-read the "
+ "configuration files during startup ~n"
+ " (and ignore the configuration "
+ "database) (true/false)?", "true",
+ fun verify_bool/1),
+ MultiThreaded = ask("16. Multi threaded agent (true/false)?",
+ "false",
+ fun verify_bool/1),
+ MeOverride = ask("17. Check for duplicate mib entries when "
+ "installing a mib (true/false)?", "false",
+ fun verify_bool/1),
+ TrapOverride = ask("18. Check for duplicate trap names when "
+ "installing a mib (true/false)?", "false",
+ fun verify_bool/1),
+ MibServerVerb = ask("19. Mib server verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ MibServerCache = ask("20. Mib server cache "
+ "(true/false)?",
+ "true",
+ fun verify_bool/1),
+ NoteStoreVerb = ask("21. Note store verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ NoteStoreTimeout = ask("22. Note store GC timeout?", "30000",
+ fun verify_timeout/1),
+ ATL =
+ case ask("23. Shall the agent use an audit trail log "
+ "(y/n)?",
+ "n", fun verify_yes_or_no/1) of
+ yes ->
+ ATLType = ask("23b. Audit trail log type "
+ "(write/read_write)?",
+ "read_write", fun verify_atl_type/1),
+ ATLDir = ask("23c. Where to store the "
+ "audit trail log?",
+ DefDir, fun verify_dir/1),
+ ATLMaxFiles = ask("23d. Max number of files?",
+ "10",
+ fun verify_pos_integer/1),
+ ATLMaxBytes = ask("23e. Max size (in bytes) "
+ "of each file?",
+ "10240",
+ fun verify_pos_integer/1),
+ ATLSize = {ATLMaxBytes, ATLMaxFiles},
+ ATLRepair = ask("23f. Audit trail log repair "
+ "(true/false/truncate/snmp_repair)?", "true",
+ fun verify_atl_repair/1),
+ [{audit_trail_log, [{type, ATLType},
+ {dir, ATLDir},
+ {size, ATLSize},
+ {repair, ATLRepair}]}];
+ no ->
+ []
+ end,
+ NetIfVerb = ask("24. Network interface verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ NetIfMod = ask("25. Which network interface module shall be used?",
+ "snmpa_net_if", fun verify_module/1),
+ NetIfOpts =
+ case NetIfMod of
+ snmpa_net_if ->
+ NetIfBindTo =
+ ask("25a. Bind the agent IP address "
+ "(true/false)?",
+ "false", fun verify_bool/1),
+ NetIfNoReuse =
+ ask("25b. Shall the agents "
+ "IP address "
+ "and port be not reusable "
+ "(true/false)?",
+ "false", fun verify_bool/1),
+ NetIfReqLimit =
+ ask("25c. Agent request limit "
+ "(used for flow control) "
+ "(infinity/pos integer)?",
+ "infinity",
+ fun verify_netif_req_limit/1),
+ NetIfRecbuf =
+ case ask("25d. Receive buffer size of the "
+ "agent (in bytes) "
+ "(default/pos integer)?",
+ "default",
+ fun verify_netif_recbuf/1) of
+ default ->
+ [];
+ RecBufSz ->
+ [{recbuf, RecBufSz}]
+ end,
+ NetIfSndbuf =
+ case ask("25e. Send buffer size of the agent "
+ "(in bytes) (default/pos integer)?",
+ "default",
+ fun verify_netif_sndbuf/1) of
+ default ->
+ [];
+ SndBufSz ->
+ [{sndbuf, SndBufSz}]
+ end,
+ NetIfFilter =
+ case ask("25f. Do you wish to specify a "
+ "network interface filter module "
+ "(or use default)",
+ "default", fun verify_module/1) of
+ default ->
+ [];
+ NetIfFilterMod ->
+ [{filter, [{module, NetIfFilterMod}]}]
+ end,
+ [{bind_to, NetIfBindTo},
+ {no_reuse, NetIfNoReuse},
+ {req_limit, NetIfReqLimit}] ++
+ NetIfRecbuf ++ NetIfSndbuf ++ NetIfFilter;
+ _ ->
+ []
+ end,
+ NetIf = [{module, NetIfMod},
+ {verbosity, NetIfVerb},
+ {options, NetIfOpts}],
+ [{agent_type, master},
+ {agent_verbosity, MasterAgentVerb},
+ {config, [{dir, ConfigDir},
+ {force_load, ForceLoad},
+ {verbosity, ConfigVerb}]},
+ {multi_threaded, MultiThreaded},
+ {mib_server, [{mibentry_override, MeOverride},
+ {trapentry_override, TrapOverride},
+ {verbosity, MibServerVerb},
+ {cache, MibServerCache}]},
+ {note_store, [{timeout, NoteStoreTimeout},
+ {verbosity, NoteStoreVerb}]},
+ {net_if, NetIf}] ++ ATL;
+ sub ->
+ SubAgentVerb = ask("14. Sub-agent verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ [{agent_type, sub},
+ {agent_verbosity, SubAgentVerb},
+ {config, [{dir, ConfigDir}]}]
+ end,
+ SysConfig =
+ [{priority, Prio},
+ {versions, Vsns},
+ {db_dir, DbDir},
+ {mib_storage, MibStorage},
+ {target_cache, [{verbosity, TargetCacheVerb}]},
+ {symbolic_store, [{verbosity, SymStoreVerb}]},
+ {local_db, [{repair, LocalDbRepair},
+ {auto_save, LocalDbAutoSave},
+ {verbosity, LocalDbVerb}]},
+ {error_report_module, ErrorMod}] ++ AgentConfig,
+ {Vsns, ConfigDir, SysConfig}.
+
+
+config_agent_snmp(Dir, Vsns) ->
+ i("~nAgent snmp config: "
+ "~n------------------"),
+ AgentName = guess_agent_name(),
+ EngineName = guess_engine_name(),
+ SysName = ask("1. System name (sysName standard variable)",
+ AgentName, fun verify_system_name/1),
+ EngineID = ask("2. Engine ID (snmpEngineID standard variable)",
+ EngineName, fun verify_engine_id/1),
+ MMS = ask("3. Max message size?", "484",
+ fun verify_max_message_size/1),
+ AgentUDP = ask("4. The UDP port the agent listens to. "
+ "(standard 161)",
+ "4000", fun verify_port_number/1),
+ Host = host(),
+ AgentIP = ask("5. IP address for the agent (only used as id ~n"
+ " when sending traps)", Host, fun verify_address/1),
+ ManagerIP = ask("6. IP address for the manager (only this manager ~n"
+ " will have access to the agent, traps are sent ~n"
+ " to this one)", Host, fun verify_address/1),
+ TrapUdp = ask("7. To what UDP port at the manager should traps ~n"
+ " be sent (standard 162)?", "5000",
+ fun verify_port_number/1),
+ SecType = ask("8. Do you want a none- minimum- or semi-secure"
+ " configuration? ~n"
+ " Note that if you chose v1 or v2, you won't get any"
+ " security for these~n"
+ " requests (none, minimum, semi_des, semi_aes)",
+ "minimum",
+ fun verify_sec_type/1),
+ Passwd =
+ case lists:member(v3, Vsns) and (SecType /= none) of
+ true ->
+ ensure_crypto_started(),
+ ask("8b. Give a password of at least length 8. It is used to "
+ "generate ~n"
+ " private keys for the configuration: ",
+ mandatory, fun verify_passwd/1);
+ false ->
+ ""
+ end,
+ NotifType =
+ case lists:member(v1, Vsns) of
+ true ->
+ Overwrite = ask("9. Current configuration files will "
+ "now be overwritten. "
+ "Ok (y/n)?", "y", fun verify_yes_or_no/1),
+ case Overwrite of
+ no ->
+ error(overwrite_not_allowed);
+ yes ->
+ ok
+ end,
+ trap;
+ false ->
+ NT = ask("9. Should notifications be sent as traps or informs "
+ "(trap/inform)?", "trap", fun verify_notif_type/1),
+ Overwrite = ask("10. Current configuration files will "
+ "now be overwritten. "
+ "Ok (y/n)?", "y", fun verify_yes_or_no/1),
+ case Overwrite of
+ no ->
+ error(overwrite_not_allowed);
+ yes ->
+ ok
+ end,
+ NT
+ end,
+ case (catch write_agent_snmp_files(Dir,
+ Vsns, ManagerIP, TrapUdp,
+ AgentIP, AgentUDP,
+ SysName, NotifType, SecType,
+ Passwd, EngineID, MMS)) of
+ ok ->
+ i("~n- - - - - - - - - - - - -"),
+ i("Info: 1. SecurityName \"initial\" has noAuthNoPriv read access~n"
+ " and authenticated write access to the \"restricted\"~n"
+ " subtree."),
+ i(" 2. SecurityName \"all-rights\" has noAuthNoPriv "
+ "read/write~n"
+ " access to the \"internet\" subtree."),
+ i(" 3. Standard traps are sent to the manager."),
+ case lists:member(v1, Vsns) or lists:member(v2, Vsns) of
+ true ->
+ i(" 4. Community \"public\" is mapped to security name"
+ " \"initial\"."),
+ i(" 5. Community \"all-rights\" is mapped to security"
+ " name \"all-rights\".");
+ false ->
+ ok
+ end,
+ i("The following agent files were written: agent.conf, "
+ "community.conf,~n"
+ "standard.conf, target_addr.conf, "
+ "target_params.conf, ~n"
+ "notify.conf" ++
+ case lists:member(v3, Vsns) of
+ true -> ", vacm.conf and usm.conf";
+ false -> " and vacm.conf"
+ end),
+ i("- - - - - - - - - - - - -"),
+ ok;
+ E ->
+ error({failed_writing_files, E})
+ end.
+
+
+%% -------------------
+
+config_manager_sys() ->
+ i("~nManager system config: "
+ "~n----------------------"),
+ Prio = ask("1. Manager process priority (low/normal/high)",
+ "normal", fun verify_prio/1),
+ Vsns = ask("2. What SNMP version(s) should be used "
+ "(1,2,3,1&2,1&2&3,2&3)?", "3", fun verify_versions/1),
+ {ok, DefDir} = file:get_cwd(),
+ {DefConfDir, Warning} = default_manager_dir(DefDir),
+ ConfQ =
+ if
+ Warning == "" ->
+ "3. Configuration directory (absolute path)?";
+ true ->
+ lists:flatten(
+ io_lib:format("3. Configuration directory (absolute path)?"
+ "~n ~s", [Warning]))
+ end,
+ ConfigDir = ask(ConfQ, DefConfDir, fun verify_dir/1),
+ ConfigVerb = ask("4. Config verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ ConfigDbDir = ask("5. Database directory (absolute path)?",
+ DefDir, fun verify_dir/1),
+ ConfigDbRepair = ask("6. Database repair "
+ "(true/false/force)?", "true",
+ fun verify_dets_repair/1),
+ ConfigDbAutoSave = ask("7. Database auto save "
+ "(infinity/milli seconds)?",
+ "5000", fun verify_dets_auto_save/1),
+ IRB =
+ case ask("8. Inform request behaviour (auto/user)?",
+ "auto", fun verify_irb/1) of
+ auto ->
+ auto;
+ user ->
+ case ask("8b. Use default GC timeout"
+ "(default/seconds)?",
+ "default", fun verify_irb_user/1) of
+ default ->
+ user;
+ IrbGcTo ->
+ {user, IrbGcTo}
+ end
+ end,
+ ServerVerb = ask("9. Server verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ ServerTimeout = ask("10. Server GC timeout?", "30000",
+ fun verify_timeout/1),
+ NoteStoreVerb = ask("11. Note store verbosity "
+ "(silence/info/log/debug/trace)?",
+ "silence",
+ fun verify_verbosity/1),
+ NoteStoreTimeout = ask("12. Note store GC timeout?", "30000",
+ fun verify_timeout/1),
+ NetIfMod = ask("13. Which network interface module shall be used?",
+ "snmpm_net_if", fun verify_module/1),
+ NetIfVerb = ask("14. Network interface verbosity "
+ "(silence/info/log/debug/trace)?", "silence",
+ fun verify_verbosity/1),
+ NetIfBindTo = ask("15. Bind the manager IP address "
+ "(true/false)?",
+ "false", fun verify_bool/1),
+ NetIfNoReuse = ask("16. Shall the manager IP address and port "
+ "be not reusable (true/false)?",
+ "false", fun verify_bool/1),
+ NetIfRecbuf =
+ case ask("17. Receive buffer size of the manager (in bytes) "
+ "(default/pos integer)?", "default",
+ fun verify_netif_recbuf/1) of
+ default ->
+ [];
+ RecBufSz ->
+ [{recbuf, RecBufSz}]
+ end,
+ NetIfSndbuf =
+ case ask("18. Send buffer size of the manager (in bytes) "
+ "(default/pos integer)?", "default",
+ fun verify_netif_sndbuf/1) of
+ default ->
+ [];
+ SndBufSz ->
+ [{sndbuf, SndBufSz}]
+ end,
+ NetIfOpts =
+ [{bind_to, NetIfBindTo},
+ {no_reuse, NetIfNoReuse}] ++ NetIfRecbuf ++ NetIfSndbuf,
+ NetIf =
+ [{module, NetIfMod},
+ {verbosity, NetIfVerb},
+ {options, NetIfOpts}],
+ ATL =
+ case ask("19. Shall the manager use an audit trail log "
+ "(y/n)?",
+ "n", fun verify_yes_or_no/1) of
+ yes ->
+ ATLDir = ask("19b. Where to store the "
+ "audit trail log?",
+ DefDir, fun verify_dir/1),
+ ATLMaxFiles = ask("19c. Max number of files?",
+ "10",
+ fun verify_pos_integer/1),
+ ATLMaxBytes = ask("19d. Max size (in bytes) "
+ "of each file?",
+ "10240",
+ fun verify_pos_integer/1),
+ ATLSize = {ATLMaxBytes, ATLMaxFiles},
+ ATLRepair = ask("19e. Audit trail log repair "
+ "(true/false/truncate/snmp_repair)?", "true",
+ fun verify_atl_repair/1),
+ [{audit_trail_log, [{dir, ATLDir},
+ {size, ATLSize},
+ {repair, ATLRepair}]}];
+ no ->
+ []
+ end,
+ DefUser =
+ case ask("20. Do you wish to assign a default user [yes] or use~n"
+ " the default settings [no] (y/n)?", "n",
+ fun verify_yes_or_no/1) of
+ yes ->
+ DefUserMod = ask("20b. Default user module?",
+ "snmpm_user_default",
+ fun verify_module/1),
+ DefUserData = ask("20c. Default user data?", "undefined",
+ fun verify_user_data/1),
+ [{def_user_mod, DefUserMod},
+ {def_user_data, DefUserData}];
+ no ->
+ []
+ end,
+ SysConfig =
+ [{priority, Prio},
+ {versions, Vsns},
+ {config, [{dir, ConfigDir},
+ {verbosity, ConfigVerb},
+ {db_dir, ConfigDbDir},
+ {repair, ConfigDbRepair},
+ {auto_save, ConfigDbAutoSave}]},
+ {inform_request_behaviour, IRB},
+ {mibs, []},
+ {server, [{timeout, ServerTimeout},
+ {verbosity, ServerVerb}]},
+ {note_store, [{timeout, NoteStoreTimeout},
+ {verbosity, NoteStoreVerb}]},
+ {net_if, NetIf}] ++ ATL ++ DefUser,
+ {Vsns, ConfigDir, SysConfig}.
+
+
+config_manager_snmp(Dir, Vsns) ->
+ i("~nManager snmp config: "
+ "~n--------------------"),
+ EngineName = guess_engine_name(),
+ EngineID = ask("1. Engine ID (snmpEngineID standard variable)",
+ EngineName, fun verify_engine_id/1),
+ MMS = ask("2. Max message size?", "484",
+ fun verify_max_message_size/1),
+ Host = host(),
+ IP = ask("3. IP address for the manager (only used as id ~n"
+ " when sending requests)",
+ Host, fun verify_address/1),
+ Port = ask("4. Port number (standard 162)?", "5000",
+ fun verify_port_number/1),
+ Users = config_manager_snmp_users([]),
+ Agents = config_manager_snmp_agents([]),
+ Usms = config_manager_snmp_usms([]),
+ Overwrite = ask("8. Current configuration files will now be overwritten. "
+ "Ok (y/n)?", "y", fun verify_yes_or_no/1),
+ case Overwrite of
+ no ->
+ error(overwrite_not_allowed);
+ yes ->
+ ok
+ end,
+ case (catch write_manager_snmp_files(Dir,
+ IP, Port, MMS, EngineID,
+ Users, Agents, Usms)) of
+ ok ->
+ i("~n- - - - - - - - - - - - -"),
+ i("The following manager files were written: "
+ "manager.conf, agents.conf " ++
+ case lists:member(v3, Vsns) of
+ true ->
+ ", users.conf and usm.conf";
+ false ->
+ " and users.conf"
+ end),
+ i("- - - - - - - - - - - - -"),
+ ok;
+ E ->
+ error({failed_writing_files, E})
+ end.
+
+
+config_manager_snmp_users(Users) ->
+ case ask("5. Configure a user of this manager (y/n)?",
+ "y", fun verify_yes_or_no/1) of
+ yes ->
+ User = config_manager_snmp_user(),
+ config_manager_snmp_users([User|Users]);
+ no ->
+ lists:reverse(Users)
+ end.
+
+config_manager_snmp_user() ->
+ UserId = ask("5b. User id?", mandatory,
+ fun verify_user_id/1),
+ UserMod = ask("5c. User callback module?", mandatory,
+ fun verify_module/1),
+ UserData = ask("5d. User (callback) data?", "undefined",
+ fun verify_user_data/1),
+ {UserId, UserMod, UserData}.
+
+
+config_manager_snmp_agents(Agents) ->
+ case ask("6. Configure an agent handled by this manager (y/n)?",
+ "y", fun verify_yes_or_no/1) of
+ yes ->
+ Agent = config_manager_snmp_agent(),
+ config_manager_snmp_agents([Agent|Agents]);
+ no ->
+ lists:reverse(Agents)
+ end.
+
+config_manager_snmp_agent() ->
+ UserId = ask("6b. User id?", mandatory,
+ fun verify_user_id/1),
+ TargetName = ask("6c. Target name?", guess_agent_name(),
+ fun verify_system_name/1),
+ Version = ask("6d. Version (1/2/3)?", "1",
+ fun verify_version/1),
+ Comm = ask("6e. Community string ?", "public",
+ fun verify_community/1),
+ EngineID = ask("6f. Engine ID (snmpEngineID standard variable)",
+ guess_engine_name(), fun verify_engine_id/1),
+ IP = ask("6g. IP address for the agent", host(),
+ fun verify_address/1),
+ Port = ask("6h. The UDP port the agent listens to. "
+ "(standard 161)", "4000", fun verify_port_number/1),
+ Timeout = ask("6i. Retransmission timeout (infinity/pos integer)?",
+ "infinity", fun verify_retransmission_timeout/1),
+ MMS = ask("6j. Max message size?", "484",
+ fun verify_max_message_size/1),
+ SecModel = ask("6k. Security model (any/v1/v2c/usm)?", "any",
+ fun verify_sec_model/1),
+ SecName = ask("6l. Security name?", "\"initial\"",
+ fun verify_sec_name/1),
+ SecLevel = ask("6m. Security level (noAuthNoPriv/authNoPriv/authPriv)?",
+ "noAuthNoPriv", fun verify_sec_level/1),
+ {UserId,
+ TargetName, Comm, IP, Port, EngineID, Timeout, MMS,
+ Version, SecModel, SecName, SecLevel}.
+
+
+config_manager_snmp_usms(Usms) ->
+ case ask("7. Configure an usm user handled by this manager (y/n)?",
+ "y", fun verify_yes_or_no/1) of
+ yes ->
+ Usm = config_manager_snmp_usm(),
+ config_manager_snmp_usms([Usm|Usms]);
+ no ->
+ lists:reverse(Usms)
+ end.
+
+config_manager_snmp_usm() ->
+ EngineID = ask("7a. Engine ID", guess_engine_name(),
+ fun verify_engine_id/1),
+ UserName = ask("7b. User name?", mandatory, fun verify_usm_name/1),
+ SecName = ask("7c. Security name?", UserName,
+ fun verify_usm_sec_name/1),
+ AuthP = ask("7d. Authentication protocol (no/sha/md5)?", "no",
+ fun verify_usm_auth_protocol/1),
+ AuthKey = ask_auth_key("7e", AuthP),
+ PrivP = ask("7e. Priv protocol (no/des/aes)?", "no",
+ fun verify_usm_priv_protocol/1),
+ PrivKey = ask_priv_key("7f", PrivP),
+ {EngineID, UserName,
+ SecName, AuthP, AuthKey, PrivP, PrivKey}.
+
+
+%% ------------------------------------------------------------------
+
+is_members([], _Files) ->
+ true;
+is_members([H|T], Files) ->
+ lists:member(H, Files) andalso is_members(T, Files).
+
+default_agent_dir(DefDir) ->
+ default_dir("agent", DefDir).
+
+default_manager_dir(DefDir) ->
+ default_dir("manager", DefDir).
+
+default_dir(Component, DefDir) ->
+ %% Look for the component dir, if found use that as default
+ {ok, Files} = file:list_dir(DefDir),
+ case lists:member(Component, Files) of
+ true ->
+ {filename:join(DefDir, Component), ""};
+ false ->
+ %% No luck,
+ %% so check if cwd contains either agent and/or
+ %% manager config files. If it does, issue a warning
+
+ %% Check for presence of agent config files
+ %% If all the agent config files are present,
+ %% issue a warning
+ AgentConfs =
+ [
+ "agent.conf",
+ "context.conf",
+ "community.conf",
+ "notify.conf",
+ "standard.conf",
+ "target_params.conf",
+ "target_addr.conf",
+ "usm.conf",
+ "vacm.conf"
+ ],
+ IsAgentDir = is_members(AgentConfs, Files),
+
+ %% Check for presence of manager config files
+ %% If all the manager config files are present,
+ %% issue a warning
+ ManagerConfs =
+ [
+ "agents.conf",
+ "manager.conf",
+ "users.conf",
+ "usm.conf"
+ ],
+ IsManagerDir = is_members(ManagerConfs, Files),
+ Warning =
+ if
+ IsAgentDir and IsManagerDir ->
+ "Note that the default directory contains both agent and manager config files";
+ IsAgentDir ->
+ "Note that the default directory contains agent config files";
+ IsManagerDir ->
+ "Note that the default directory contains manager config files";
+ true ->
+ ""
+ end,
+ {DefDir, Warning}
+ end.
+
+
+%% ------------------------------------------------------------------
+
+ask_auth_key(_Prefix, usmNoAuthProtocol) ->
+ "";
+ask_auth_key(Prefix, usmHMACSHAAuthProtocol) ->
+ ask(Prefix ++ " Authentication [sha] key (length 0 or 20)?", "\"\"",
+ fun verify_usm_auth_sha_key/1);
+ask_auth_key(Prefix, usmHMACMD5AuthProtocol) ->
+ ask(Prefix ++ " Authentication [md5] key (length 0 or 16)?", "\"\"",
+ fun verify_usm_auth_md5_key/1).
+
+ask_priv_key(_Prefix, usmNoPrivProtocol) ->
+ "";
+ask_priv_key(Prefix, usmDESPrivProtocol) ->
+ ask(Prefix ++ " Priv [des] key (length 0 or 16)?", "\"\"",
+ fun verify_usm_priv_des_key/1);
+ask_priv_key(Prefix, usmAesCfb128Protocol) ->
+ ask(Prefix ++ " Priv [aes] key (length 0 or 16)?", "\"\"",
+ fun verify_usm_priv_aes_key/1).
+
+
+%% ------------------------------------------------------------------
+
+verify_yes_or_no("y") ->
+ {ok, yes};
+verify_yes_or_no("yes") ->
+ {ok, yes};
+verify_yes_or_no("n") ->
+ {ok, no};
+verify_yes_or_no("no") ->
+ {ok, no};
+verify_yes_or_no(YON) ->
+ {error, "invalid yes or no: " ++ YON}.
+
+
+verify_prio("low") ->
+ {ok, low};
+verify_prio("normal") ->
+ {ok, normal};
+verify_prio("high") ->
+ {ok, high};
+verify_prio(Prio) ->
+ {error, "invalid process priority: " ++ Prio}.
+
+
+verify_system_name(Name) -> {ok, Name}.
+
+
+verify_engine_id(Name) -> {ok, Name}.
+
+
+verify_max_message_size(MMS) ->
+ case (catch list_to_integer(MMS)) of
+ I when is_integer(I) andalso (I >= 484) ->
+ {ok, I};
+ I when is_integer(I) ->
+ {error, "invalid max message size (must be atleast 484): " ++ MMS};
+ _ ->
+ {error, "invalid max message size: " ++ MMS}
+ end.
+
+
+verify_port_number(P) ->
+ case (catch list_to_integer(P)) of
+ N when is_integer(N) andalso (N > 0) ->
+ {ok, N};
+ _ ->
+ {error, "invalid port number: " ++ P}
+ end.
+
+
+verify_versions("1") -> {ok, [v1]};
+verify_versions("2") -> {ok, [v2]};
+verify_versions("3") -> {ok, [v3]};
+verify_versions("1&2") -> {ok, [v1,v2]};
+verify_versions("1&3") -> {ok, [v1,v3]};
+verify_versions("2&3") -> {ok, [v2,v3]};
+verify_versions("1&2&3") -> {ok, [v1,v2,v3]};
+verify_versions(V) -> {error, "incorrect version(s): " ++ V}.
+
+verify_version("1") -> {ok, v1};
+verify_version("2") -> {ok, v2};
+verify_version("3") -> {ok, v3};
+verify_version(V) -> {error, "incorrect version: " ++ V}.
+
+
+verify_passwd(Passwd) when length(Passwd) >= 8 ->
+ {ok, Passwd};
+verify_passwd(_P) ->
+ {error, "invalid password"}.
+
+
+verify_dir(Dir) ->
+ case filename:pathtype(Dir) of
+ absolute ->
+ case file:read_file_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok, snmp_misc:ensure_trailing_dir_delimiter(Dir)};
+ {ok, _FileInfo} ->
+ {error, Dir ++ " is not a directory"};
+ _ ->
+ {error, "invalid directory: " ++ Dir}
+ end;
+ _E ->
+ {error, "invalid directory (not absolute): " ++ Dir}
+ end.
+
+
+verify_notif_type("trap") -> {ok, trap};
+verify_notif_type("inform") -> {ok, inform};
+verify_notif_type(NT) -> {error, "invalid notifcation type: " ++ NT}.
+
+
+verify_sec_type("none") -> {ok, none};
+verify_sec_type("minimum") -> {ok, minimum};
+verify_sec_type("semi_des") -> {ok, {semi, des}};
+verify_sec_type("semi_aes") -> {ok, {semi, aes}};
+verify_sec_type(ST) -> {error, "invalid security type: " ++ ST}.
+
+
+verify_address(A) ->
+ case (catch snmp_misc:ip(A)) of
+ {ok, IP} ->
+ {ok, tuple_to_list(IP)};
+ {error, _} ->
+ {error, "invalid address: " ++ A};
+ _E ->
+ {error, "invalid address: " ++ A}
+ end.
+
+
+verify_mib_storage_type("m") ->
+ {ok, mnesia};
+verify_mib_storage_type("mnesia") ->
+ {ok, mnesia};
+verify_mib_storage_type("d") ->
+ {ok, dets};
+verify_mib_storage_type("dets") ->
+ {ok, dets};
+verify_mib_storage_type("e") ->
+ {ok, ets};
+verify_mib_storage_type("ets") ->
+ {ok, ets};
+verify_mib_storage_type(T) ->
+ {error, "invalid mib storage type: " ++ T}.
+
+verify_mib_storage_action("default") ->
+ {ok, default};
+verify_mib_storage_action("clear") ->
+ {ok, clear};
+verify_mib_storage_action("keep") ->
+ {ok, keep};
+verify_mib_storage_action(A) ->
+ {error, "invalid mib storage action: " ++ A}.
+
+
+verify_verbosity("silence") ->
+ {ok, silence};
+verify_verbosity("info") ->
+ {ok, info};
+verify_verbosity("log") ->
+ {ok, log};
+verify_verbosity("debug") ->
+ {ok, debug};
+verify_verbosity("trace") ->
+ {ok, trace};
+verify_verbosity(V) ->
+ {error, "invalid verbosity: " ++ V}.
+
+
+verify_dets_repair("true") ->
+ {ok, true};
+verify_dets_repair("false") ->
+ {ok, false};
+verify_dets_repair("force") ->
+ {ok, force};
+verify_dets_repair(R) ->
+ {error, "invalid repair: " ++ R}.
+
+verify_dets_auto_save("infinity") ->
+ {ok, infinity};
+verify_dets_auto_save(I0) ->
+ case (catch list_to_integer(I0)) of
+ I when is_integer(I) andalso (I > 0) ->
+ {ok, I};
+ _ ->
+ {error, "invalid auto save timeout time: " ++ I0}
+ end.
+
+
+%% I know that this is a little of the edge, but...
+verify_module(M0) ->
+ case (catch list_to_atom(M0)) of
+ M when is_atom(M) ->
+ {ok, M};
+ _ ->
+ {error, "invalid module: " ++ M0}
+ end.
+
+
+verify_agent_type("master") ->
+ {ok, master};
+verify_agent_type("sub") ->
+ {ok, sub};
+verify_agent_type(AT) ->
+ {error, "invalid agent type: " ++ AT}.
+
+
+verify_bool("true") ->
+ {ok, true};
+verify_bool("false") ->
+ {ok, false};
+verify_bool(B) ->
+ {error, "invalid boolean: " ++ B}.
+
+
+verify_timeout(T0) ->
+ case (catch list_to_integer(T0)) of
+ T when is_integer(T) andalso (T > 0) ->
+ {ok, T};
+ _ ->
+ {error, "invalid timeout time: '" ++ T0 ++ "'"}
+ end.
+
+
+verify_retransmission_timeout("infinity") ->
+ {ok, infinity};
+verify_retransmission_timeout([${|R] = Timer) ->
+ case lists:reverse(R) of
+ [$}|R2] ->
+ case string:tokens(lists:reverse(R2), ", ") of
+ [WaitForStr, FactorStr, IncrStr, RetryStr] ->
+ WaitFor = incr_timer_value(WaitForStr, 1),
+ Factor = incr_timer_value(FactorStr, 1),
+ Incr = incr_timer_value(IncrStr, 0),
+ Retry = incr_timer_value(RetryStr, 0),
+ {ok, {WaitFor, Factor, Incr, Retry}};
+ _ ->
+ {error, "invalid retransmission timer: '" ++ Timer ++ "'"}
+ end;
+ _ ->
+ {error, "invalid retransmission timer: '" ++ Timer ++ "'"}
+ end;
+verify_retransmission_timeout(T0) ->
+ case (catch list_to_integer(T0)) of
+ T when is_integer(T) andalso (T > 0) ->
+ {ok, T};
+ _ ->
+ {error, "invalid timeout time: '" ++ T0 ++ "'"}
+ end.
+
+incr_timer_value(Str, Min) ->
+ case (catch list_to_integer(Str)) of
+ I when is_integer(I) andalso (I >= Min) ->
+ I;
+ I when is_integer(I) ->
+ E = lists:flatten(io_lib:format("invalid incremental timer value "
+ "(min value is ~w): " ++ Str,
+ [Min])),
+ error(E);
+ _ ->
+ error("invalid incremental timer value: " ++ Str)
+ end.
+
+
+%% verify_atl_type("read") ->
+%% {ok, read};
+verify_atl_type("write") ->
+ {ok, write};
+verify_atl_type("read_write") ->
+ {ok, read_write};
+verify_atl_type(T) ->
+ {error, "invalid log type: " ++ T}.
+
+verify_atl_repair("true") ->
+ {ok, true};
+verify_atl_repair("false") ->
+ {ok, false};
+verify_atl_repair("truncate") ->
+ {ok, truncate};
+verify_atl_repair("snmp_repair") ->
+ {ok, snmp_repair};
+verify_atl_repair(R) ->
+ {error, "invalid audit trail log repair: " ++ R}.
+
+
+verify_pos_integer(I0) ->
+ case (catch list_to_integer(I0)) of
+ I when is_integer(I) andalso (I > 0) ->
+ {ok, I};
+ _ ->
+ {error, "invalid integer value: " ++ I0}
+ end.
+
+
+verify_netif_req_limit("infinity") ->
+ {ok, infinity};
+verify_netif_req_limit(I0) ->
+ case (catch list_to_integer(I0)) of
+ I when is_integer(I) andalso (I > 0) ->
+ {ok, I};
+ _ ->
+ {error, "invalid network interface request limit: " ++ I0}
+ end.
+
+verify_netif_recbuf(Val) ->
+ verify_netif_recbuf_or_sndbuf(Val, "recbuf").
+
+verify_netif_sndbuf(Val) ->
+ verify_netif_recbuf_or_sndbuf(Val, "sndbuf").
+
+verify_netif_recbuf_or_sndbuf("default", _) ->
+ {ok, default};
+verify_netif_recbuf_or_sndbuf(I0, Buf) ->
+ case (catch list_to_integer(I0)) of
+ I when is_integer(I) andalso (I > 0) ->
+ {ok, I};
+ _ ->
+ {error, "invalid network interface " ++ Buf ++ " size: " ++ I0}
+ end.
+
+
+verify_irb("auto") ->
+ {ok, auto};
+verify_irb("user") ->
+ {ok, user};
+verify_irb(IRB) ->
+ E = lists:flatten(io_lib:format("invalid irb: ~p", [IRB])),
+ {error, E}.
+
+
+verify_irb_user("default") ->
+ {ok, default};
+verify_irb_user(TO) ->
+ case (catch list_to_integer(TO)) of
+ I when is_integer(I) andalso (I > 0) ->
+ {ok, I*1000}; % Time is given in seconds
+ _ ->
+ {error, "invalid IRB GC time: " ++ TO}
+ end.
+
+
+
+verify_user_id(UserId) when is_list(UserId) ->
+ case (catch list_to_atom(UserId)) of
+ A when is_atom(A) ->
+ {ok, A};
+ _ ->
+ {error, "invalid user id: " ++ UserId}
+ end;
+verify_user_id(UserId) when is_atom(UserId) ->
+ {ok, UserId};
+verify_user_id(UserId) ->
+ E = lists:flatten(io_lib:format("invalid user id: ~p", [UserId])),
+ {error, E}.
+
+verify_user_data("undefined") ->
+ {ok, undefined};
+verify_user_data(UserData) ->
+ {ok, UserData}.
+
+
+verify_community("\"\"") ->
+ {ok, ""};
+verify_community(Comm) ->
+ {ok, Comm}.
+
+
+% verify_context_name("\"\"") ->
+% {ok, ""};
+% verify_context_name(Ctx) ->
+% {ok, Ctx}.
+
+
+% verify_mp_model("v1") ->
+% {ok, v1};
+% verify_mp_model("v2c") ->
+% {ok, v2c};
+% verify_mp_model("v3") ->
+% {ok, v3};
+% verify_mp_model(M) ->
+% {error, "invalid mp model: " ++ M}.
+
+
+verify_sec_model("any") ->
+ {ok, any};
+verify_sec_model("v1") ->
+ {ok, v1};
+verify_sec_model("v2c") ->
+ {ok, v2c};
+verify_sec_model("usm") ->
+ {ok, usm};
+verify_sec_model(M) ->
+ {error, "invalid sec model: " ++ M}.
+
+verify_sec_name("\"initial\"") ->
+ {ok, "initial"};
+verify_sec_name(N) ->
+ {ok, N}.
+
+
+verify_sec_level("noAuthNoPriv") ->
+ {ok, noAuthNoPriv};
+verify_sec_level("authNoPriv") ->
+ {ok, authNoPriv};
+verify_sec_level("authPriv") ->
+ {ok, authPriv};
+verify_sec_level(L) ->
+ {error, "invalid sec level: " ++ L}.
+
+
+verify_usm_name(Name) ->
+ {ok, Name}.
+
+verify_usm_sec_name(Name) ->
+ {ok, Name}.
+
+
+verify_usm_auth_protocol("no") ->
+ {ok, usmNoAuthProtocol};
+verify_usm_auth_protocol("sha") ->
+ {ok, usmHMACSHAAuthProtocol};
+verify_usm_auth_protocol("md5") ->
+ {ok, usmHMACMD5AuthProtocol};
+verify_usm_auth_protocol(AuthP) ->
+ {error, "invalid auth protocol: " ++ AuthP}.
+
+verify_usm_auth_sha_key(Key) ->
+ verify_usm_key("auth sha", Key, 20).
+
+verify_usm_auth_md5_key(Key) ->
+ verify_usm_key("auth md5", Key, 16).
+
+verify_usm_priv_protocol("no") ->
+ {ok, usmNoPrivProtocol};
+verify_usm_priv_protocol("des") ->
+ {ok, usmDESPrivProtocol};
+verify_usm_priv_protocol("aes") ->
+ {ok, usmAesCfb128Protocol};
+verify_usm_priv_protocol(AuthP) ->
+ {error, "invalid priv protocol: " ++ AuthP}.
+
+verify_usm_priv_des_key(Key) ->
+ verify_usm_key("priv des", Key, 16).
+
+verify_usm_priv_aes_key(Key) ->
+ verify_usm_key("priv aes", Key, 16).
+
+verify_usm_key(_What, "\"\"", _ExpectLength) ->
+ {ok, ""};
+verify_usm_key(_What, Key, ExpectLength) when length(Key) =:= ExpectLength ->
+ {ok, Key};
+verify_usm_key(What, [$[|RestKey] = Key0, ExpectLength) ->
+ case lists:reverse(RestKey) of
+ [$]|RevRestKey] ->
+ Key1 = lists:reverse(RevRestKey),
+ verify_usm_key2(What, Key1, ExpectLength);
+ _ ->
+ %% Its not a list ([...]) and its not the correct length, ...
+ {error, "invalid " ++ What ++ " key length: " ++ Key0}
+ end;
+verify_usm_key(What, Key, ExpectLength) ->
+ verify_usm_key2(What, Key, ExpectLength).
+
+verify_usm_key2(What, Key0, ExpectLength) ->
+ case string:tokens(Key0, [$,]) of
+ Key when length(Key) =:= ExpectLength ->
+ convert_usm_key(Key, []);
+ _ ->
+ {error, "invalid " ++ What ++ " key length: " ++ Key0}
+ end.
+
+convert_usm_key([], Acc) ->
+ {ok, lists:reverse(Acc)};
+convert_usm_key([I|Is], Acc) ->
+ case (catch list_to_integer(I)) of
+ Int when is_integer(Int) ->
+ convert_usm_key(Is, [Int|Acc]);
+ _Err ->
+ {error, "invalid key number: " ++ I}
+ end.
+
+
+% ip(Host) ->
+% case catch snmp_misc:ip(Host) of
+% {ok, IPtuple} -> tuple_to_list(IPtuple);
+% {error, Reason} -> throw({error, Reason});
+% _Q -> throw({error, {"ip conversion failed", Host}})
+% end.
+
+% make_ip(Str) ->
+% case catch snmp_misc:ip(Str) of
+% {ok, IPtuple} -> tuple_to_list(IPtuple);
+% _Q -> ip(Str)
+% end.
+
+
+print_q(Q, mandatory) ->
+ io:format(Q ++ " ",[]);
+print_q(Q, Default) when is_list(Default) ->
+ io:format(Q ++ " [~s] ",[Default]).
+
+%% Defval = string() | mandatory
+ask(Q, Default, Verify) when is_list(Q) andalso is_function(Verify) ->
+ print_q(Q, Default),
+ PrelAnsw = io:get_line(''),
+ Answer =
+ case remove_newline(PrelAnsw) of
+ "" when Default =/= mandatory -> Default;
+ "" -> ask(Q, Default, Verify);
+ A -> A
+ end,
+ case (catch Verify(Answer)) of
+ {ok, Answer2} ->
+ Answer2;
+ {error, ReasonStr} ->
+ i("ERROR: " ++ ReasonStr),
+ ask(Q, Default, Verify)
+ end.
+
+
+host() ->
+ case (catch inet:gethostname()) of
+ {ok, Name} ->
+ case (catch inet:getaddr(Name, inet)) of
+ {ok, Addr} when is_tuple(Addr) ->
+ lists:flatten(
+ io_lib:format("~w.~w.~w.~w", tuple_to_list(Addr)));
+ _ ->
+ "127.0.0.1"
+ end;
+ _ ->
+ "127.0.0.1"
+ end.
+
+guess_agent_name() ->
+ case os:type() of
+ {unix, _} ->
+ lists:append(remove_newline(os:cmd("echo $USER")), "'s agent");
+ {_,_} -> "my agent"
+ end.
+
+guess_engine_name() ->
+ case os:type() of
+ {unix, _} ->
+ lists:append(remove_newline(os:cmd("echo $USER")), "'s engine");
+ {_,_} -> "my engine"
+ end.
+
+% guess_user_id() ->
+% case os:type() of
+% {unix, _} ->
+% lists:append(remove_newline(os:cmd("echo $USER")), "'s user");
+% {_,_} -> "user_id"
+% end.
+
+
+remove_newline(Str) ->
+ lists:delete($\n, Str).
+
+
+%%======================================================================
+%% File generation
+%%======================================================================
+
+%%----------------------------------------------------------------------
+%% Dir: string() (ex: "../conf/")
+%% ManagerIP, AgentIP: [int(),int(),int(),int()]
+%% TrapUdp, AgentUDP: integer()
+%% SysName: string()
+%%----------------------------------------------------------------------
+write_agent_snmp_files(Dir, Vsns, ManagerIP, TrapUdp,
+ AgentIP, AgentUDP, SysName)
+ when is_list(Dir) andalso
+ is_list(Vsns) andalso
+ is_list(ManagerIP) andalso
+ is_integer(TrapUdp) andalso
+ is_list(AgentIP) andalso
+ is_integer(AgentUDP) andalso
+ is_list(SysName) ->
+ write_agent_snmp_files(Dir, Vsns, ManagerIP, TrapUdp, AgentIP, AgentUDP,
+ SysName, "trap", none, "", "agentEngine", 484).
+
+%%
+%% ----- Agent config files generator functions -----
+%%
+
+write_agent_snmp_files(Dir, Vsns, ManagerIP, TrapUdp, AgentIP, AgentUDP,
+ SysName, NotifType, SecType, Passwd, EngineID, MMS) ->
+ write_agent_snmp_conf(Dir, AgentIP, AgentUDP, EngineID, MMS),
+ write_agent_snmp_context_conf(Dir),
+ write_agent_snmp_community_conf(Dir),
+ write_agent_snmp_standard_conf(Dir, SysName),
+ write_agent_snmp_target_addr_conf(Dir, ManagerIP, TrapUdp, Vsns),
+ write_agent_snmp_target_params_conf(Dir, Vsns),
+ write_agent_snmp_notify_conf(Dir, NotifType),
+ write_agent_snmp_usm_conf(Dir, Vsns, EngineID, SecType, Passwd),
+ write_agent_snmp_vacm_conf(Dir, Vsns, SecType),
+ ok.
+
+
+%%
+%% ------ [agent] agent.conf ------
+%%
+
+write_agent_snmp_conf(Dir, AgentIP, AgentUDP, EngineID, MMS) ->
+ Comment =
+"%% This file defines the Agent local configuration info\n"
+"%% The data is inserted into the snmpEngine* variables defined\n"
+"%% in SNMP-FRAMEWORK-MIB, and the intAgent* variables defined\n"
+"%% in OTP-SNMPEA-MIB.\n"
+"%% Each row is a 2-tuple:\n"
+"%% {AgentVariable, Value}.\n"
+"%% For example\n"
+"%% {intAgentUDPPort, 4000}.\n"
+"%% The ip address for the agent is sent as id in traps.\n"
+"%% {intAgentIpAddress, [127,42,17,5]}.\n"
+"%% {snmpEngineID, \"agentEngine\"}.\n"
+"%% {snmpEngineMaxMessageSize, 484}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [{intAgentUDPPort, AgentUDP},
+ {intAgentIpAddress, AgentIP},
+ {snmpEngineID, EngineID},
+ {snmpEngineMaxMessageSize, MMS}],
+ write_agent_config(Dir, Hdr, Conf).
+
+write_agent_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_agent_config(Dir, Hdr, Conf).
+
+update_agent_config(Dir, Conf) ->
+ snmpa_conf:append_agent_config(Dir, Conf).
+
+
+%%
+%% ------ [agent] context.conf ------
+%%
+
+write_agent_snmp_context_conf(Dir) ->
+ Comment =
+"%% This file defines the contexts known to the agent.\n"
+"%% The data is inserted into the vacmContextTable defined\n"
+"%% in SNMP-VIEW-BASED-ACM-MIB.\n"
+"%% Each row is a string:\n"
+"%% ContextName.\n"
+"%%\n"
+"%% The empty string is the default context.\n"
+"%% For example\n"
+"%% \"bridge1\".\n"
+"%% \"bridge2\".\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [""],
+ write_agent_context_config(Dir, Hdr, Conf).
+
+write_agent_context_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_context_config(Dir, Hdr, Conf).
+
+update_agent_context_config(Dir, Conf) ->
+ snmpa_conf:append_context_config(Dir, Conf).
+
+
+%%
+%% ------ community.conf ------
+%%
+
+write_agent_snmp_community_conf(Dir) ->
+ Comment =
+"%% This file defines the community info which maps to VACM parameters.\n"
+"%% The data is inserted into the snmpCommunityTable defined\n"
+"%% in SNMP-COMMUNITY-MIB.\n"
+"%% Each row is a 5-tuple:\n"
+"%% {CommunityIndex, CommunityName, SecurityName, ContextName, TransportTag}.\n"
+"%% For example\n"
+"%% {\"1\", \"public\", \"initial\", \"\", \"\"}.\n"
+"%% {\"2\", \"secret\", \"secret_name\", \"\", \"tag\"}.\n"
+"%% {\"3\", \"bridge1\", \"initial\", \"bridge1\", \"\"}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [{"public", "public", "initial", "", ""},
+ {"all-rights", "all-rights", "all-rights", "", ""},
+ {"standard trap", "standard trap", "initial", "", ""}],
+ write_agent_community_config(Dir, Hdr, Conf).
+
+write_agent_community_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_community_config(Dir, Hdr, Conf).
+
+update_agent_community_config(Dir, Conf) ->
+ snmpa_conf:append_community_config(Dir, Conf).
+
+
+%%
+%% ------ standard.conf ------
+%%
+
+write_agent_snmp_standard_conf(Dir, SysName) ->
+ Comment =
+"%% This file defines the STANDARD-MIB info.\n"
+"%% Each row is a 2-tuple:\n"
+"%% {StandardVariable, Value}.\n"
+"%% For example\n"
+"%% {sysDescr, \"Erlang SNMP agent\"}.\n"
+"%% {sysObjectID, [1,2,3]}.\n"
+"%% {sysContact, \"{mbj,eklas}@erlang.ericsson.se\"}.\n"
+"%% {sysName, \"test\"}.\n"
+"%% {sysLocation, \"erlang\"}.\n"
+"%% {sysServices, 72}.\n"
+"%% {snmpEnableAuthenTraps, enabled}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [{sysDescr, "Erlang SNMP agent"},
+ {sysObjectID, [1,2,3]},
+ {sysContact, "{mbj,eklas}@erlang.ericsson.se"},
+ {sysLocation, "erlang"},
+ {sysServices, 72},
+ {snmpEnableAuthenTraps, enabled},
+ {sysName, SysName}],
+ write_agent_standard_config(Dir, Hdr, Conf).
+
+write_agent_standard_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_standard_config(Dir, Hdr, Conf).
+
+update_agent_standard_config(Dir, Conf) ->
+ snmpa_conf:append_standard_config(Dir, Conf).
+
+
+%%
+%% ------ target_addr.conf ------
+%%
+
+write_agent_snmp_target_addr_conf(Dir, ManagerIp, UDP, Vsns) ->
+ Timeout = 1500,
+ RetryCount = 3,
+ write_agent_snmp_target_addr_conf(Dir, ManagerIp, UDP,
+ Timeout, RetryCount,
+ Vsns).
+
+write_agent_snmp_target_addr_conf(Dir, ManagerIp, UDP,
+ Timeout, RetryCount,
+ Vsns) ->
+ Comment =
+"%% This file defines the target address parameters.\n"
+"%% The data is inserted into the snmpTargetAddrTable defined\n"
+"%% in SNMP-TARGET-MIB, and in the snmpTargetAddrExtTable defined\n"
+"%% in SNMP-COMMUNITY-MIB.\n"
+"%% Each row is a 10-tuple:\n"
+"%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId,\n"
+"%% TMask, MaxMessageSize}.\n"
+"%% The EngineId value is only used if Inform-Requests are sent to this\n"
+"%% target. If Informs are not sent, this value is ignored, and can be\n"
+"%% e.g. an empty string. However, if Informs are sent, it is essential\n"
+"%% that the value of EngineId matches the value of the target's\n"
+"%% actual snmpEngineID.\n"
+"%% For example\n"
+"%% {\"1.2.3.4 v1\", [1,2,3,4], 162, \n"
+"%% 1500, 3, \"std_inform\", \"otp_v2\", \"\",\n"
+"%% [127,0,0,0], 2048}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ F = fun(v1 = Vsn, Acc) ->
+ [{mk_ip(ManagerIp, Vsn),
+ ManagerIp, UDP, Timeout, RetryCount,
+ "std_trap", mk_param(Vsn), "", [], 2048}| Acc];
+ (v2 = Vsn, Acc) ->
+ [{mk_ip(ManagerIp, Vsn),
+ ManagerIp, UDP, Timeout, RetryCount,
+ "std_trap", mk_param(Vsn), "", [], 2048},
+ {lists:flatten(io_lib:format("~s.2",[mk_ip(ManagerIp, Vsn)])),
+ ManagerIp, UDP, Timeout, RetryCount,
+ "std_inform", mk_param(Vsn), "", [], 2048}| Acc];
+ (v3 = Vsn, Acc) ->
+ [{mk_ip(ManagerIp, Vsn),
+ ManagerIp, UDP, Timeout, RetryCount,
+ "std_trap", mk_param(Vsn), "", [], 2048},
+ {lists:flatten(io_lib:format("~s.3",[mk_ip(ManagerIp, Vsn)])),
+ ManagerIp, UDP, Timeout, RetryCount,
+ "std_inform", mk_param(Vsn), "mgrEngine", [], 2048}| Acc]
+ end,
+ Conf = lists:foldl(F, [], Vsns),
+ write_agent_target_addr_config(Dir, Hdr, Conf).
+
+mk_param(Vsn) ->
+ lists:flatten(io_lib:format("target_~w", [Vsn])).
+
+mk_ip([A,B,C,D], Vsn) ->
+ lists:flatten(io_lib:format("~w.~w.~w.~w ~w", [A,B,C,D,Vsn])).
+
+write_agent_target_addr_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_target_addr_config(Dir, Hdr, Conf).
+
+update_agent_target_addr_config(Dir, Conf) ->
+ snmpa_conf:append_target_addr_config(Dir, Conf).
+
+
+%%
+%% ------ target_params.conf ------
+%%
+
+write_agent_snmp_target_params_conf(Dir, Vsns) ->
+ Comment =
+"%% This file defines the target parameters.\n"
+"%% The data is inserted into the snmpTargetParamsTable defined\n"
+"%% in SNMP-TARGET-MIB.\n"
+"%% Each row is a 5-tuple:\n"
+"%% {Name, MPModel, SecurityModel, SecurityName, SecurityLevel}.\n"
+"%% For example\n"
+"%% {\"target_v3\", v3, usm, \"\", noAuthNoPriv}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [fun(V) ->
+ MP = if V == v1 -> v1;
+ V == v2 -> v2c;
+ V == v3 -> v3
+ end,
+ SM = if V == v1 -> v1;
+ V == v2 -> v2c;
+ V == v3 -> usm
+ end,
+ Name = lists:flatten(
+ io_lib:format("target_~w", [V])),
+ {Name, MP, SM, "initial", noAuthNoPriv}
+ end(Vsn) || Vsn <- Vsns],
+ write_agent_target_params_config(Dir, Hdr, Conf).
+
+write_agent_target_params_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_target_params_config(Dir, Hdr, Conf).
+
+update_agent_target_params_config(Dir, Conf) ->
+ snmpa_conf:append_target_params_config(Dir, Conf).
+
+
+%%
+%% ------ notify.conf ------
+%%
+
+write_agent_snmp_notify_conf(Dir, NotifyType) ->
+ Comment =
+"%% This file defines the notification parameters.\n"
+"%% The data is inserted into the snmpNotifyTable defined\n"
+"%% in SNMP-NOTIFICATION-MIB.\n"
+"%% The Name is used as CommunityString for v1 and v2c.\n"
+"%% Each row is a 3-tuple:\n"
+"%% {Name, Tag, Type}.\n"
+"%% For example\n"
+"%% {\"standard trap\", \"std_trap\", trap}.\n"
+"%% {\"standard inform\", \"std_inform\", inform}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [{"stadard_trap", "std_trap", NotifyType}],
+ write_agent_notify_config(Dir, Hdr, Conf).
+
+write_agent_notify_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_notify_config(Dir, Hdr, Conf).
+
+update_agent_notify_config(Dir, Conf) ->
+ snmpa_conf:append_notify_config(Dir, Conf).
+
+
+%%
+%% ------ usm.conf ------
+%%
+
+write_agent_snmp_usm_conf(Dir, Vsns, EngineID, SecType, Passwd) ->
+ case lists:member(v3, Vsns) of
+ false -> ok;
+ true -> write_agent_snmp_usm_conf(Dir, EngineID, SecType, Passwd)
+ end.
+
+write_agent_snmp_usm_conf(Dir, EngineID, SecType, Passwd) ->
+ Comment =
+"%% This file defines the security parameters for the user-based\n"
+"%% security model.\n"
+"%% The data is inserted into the usmUserTable defined\n"
+"%% in SNMP-USER-BASED-SM-MIB.\n"
+"%% Each row is a 14-tuple:\n"
+"%% {EngineID, UserName, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC,\n"
+"%% PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}.\n"
+"%% For example\n"
+"%% {\"agentEngine\", \"initial\", \"initial\", zeroDotZero,\n"
+"%% usmNoAuthProtocol, \"\", \"\", usmNoPrivProtocol, \"\", \"\", \"\",\n"
+"%% \"\", \"\"}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = write_agent_snmp_usm_conf2(EngineID, SecType, Passwd),
+ write_agent_usm_config(Dir, Hdr, Conf).
+
+write_agent_snmp_usm_conf2(EngineID, none, _Passwd) ->
+ [{EngineID, "initial", "initial", zeroDotZero,
+ usmNoAuthProtocol, "", "",
+ usmNoPrivProtocol, "", "",
+ "", "", ""}];
+write_agent_snmp_usm_conf2(EngineID, SecType, Passwd) ->
+ Secret16 = agent_snmp_mk_secret(md5, Passwd, EngineID),
+ Secret20 = agent_snmp_mk_secret(sha, Passwd, EngineID),
+ {PrivProt, PrivSecret} =
+ case SecType of
+ minimum ->
+ {usmNoPrivProtocol, ""};
+ {semi, des} ->
+ {usmDESPrivProtocol, Secret16};
+ {semi, aes} ->
+ {usmAesCfb128Protocol, Secret16}
+ end,
+ [{EngineID, "initial", "initial", zeroDotZero,
+ usmHMACMD5AuthProtocol, "", "",
+ PrivProt, "", "",
+ "", Secret16, PrivSecret},
+
+ {EngineID, "templateMD5", "templateMD5", zeroDotZero,
+ usmHMACMD5AuthProtocol, "", "",
+ PrivProt, "", "",
+ "", Secret16, PrivSecret},
+
+ {EngineID, "templateSHA", "templateSHA", zeroDotZero,
+ usmHMACSHAAuthProtocol, "", "",
+ PrivProt, "", "",
+ "", Secret20, PrivSecret}].
+
+write_agent_usm_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_usm_config(Dir, Hdr, Conf).
+
+update_agent_usm_config(Dir, Conf) ->
+ snmpa_conf:append_usm_config(Dir, Conf).
+
+
+%%
+%% ------ vacm.conf ------
+%%
+
+write_agent_snmp_vacm_conf(Dir, Vsns, SecType) ->
+ Comment =
+"%% This file defines the Mib Views.\n"
+"%% The data is inserted into the vacm* tables defined\n"
+"%% in SNMP-VIEW-BASED-ACM-MIB.\n"
+"%% Each row is one of 3 tuples; one for each table in the MIB:\n"
+"%% {vacmSecurityToGroup, SecModel, SecName, GroupName}.\n"
+"%% {vacmAccess, GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}.\n"
+"%% {vacmViewTreeFamily, ViewIndex, ViewSubtree, ViewStatus, ViewMask}.\n"
+"%% For example\n"
+"%% {vacmSecurityToGroup, v2c, \"initial\", \"initial\"}.\n"
+"%% {vacmSecurityToGroup, usm, \"initial\", \"initial\"}.\n"
+"%% read/notify access to system\n"
+"%% {vacmAccess, \"initial\", \"\", any, noAuthNoPriv, exact,\n"
+"%% \"system\", \"\", \"system\"}.\n"
+"%% {vacmViewTreeFamily, \"system\", [1,3,6,1,2,1,1], included, null}.\n"
+"%% {vacmViewTreeFamily, \"exmib\", [1,3,6,1,3], included, null}."
+" % for EX1-MIB\n"
+"%% {vacmViewTreeFamily, \"internet\", [1,3,6,1], included, null}.\n"
+"%%\n\n",
+ Hdr = lists:flatten(header()) ++ Comment,
+ Groups =
+ lists:foldl(
+ fun(V, Acc) ->
+ [{vacmSecurityToGroup, vacm_ver(V),
+ "initial", "initial"},
+ {vacmSecurityToGroup, vacm_ver(V),
+ "all-rights", "all-rights"}|
+ Acc]
+ end, [], Vsns),
+ Acc =
+ [{vacmAccess, "initial", "", any, noAuthNoPriv, exact,
+ "restricted", "", "restricted"},
+ {vacmAccess, "initial", "", usm, authNoPriv, exact,
+ "internet", "internet", "internet"},
+ {vacmAccess, "initial", "", usm, authPriv, exact,
+ "internet", "internet", "internet"},
+ {vacmAccess, "all-rights", "", any, noAuthNoPriv, exact,
+ "internet", "internet", "internet"}],
+ VTF0 =
+ case SecType of
+ none ->
+ [{vacmViewTreeFamily,
+ "restricted", [1,3,6,1], included, null}];
+ minimum ->
+ [{vacmViewTreeFamily,
+ "restricted", [1,3,6,1], included, null}];
+ {semi, _} ->
+ [{vacmViewTreeFamily,
+ "restricted", [1,3,6,1,2,1,1], included, null},
+ {vacmViewTreeFamily,
+ "restricted", [1,3,6,1,2,1,11], included, null},
+ {vacmViewTreeFamily,
+ "restricted", [1,3,6,1,6,3,10,2,1], included, null},
+ {vacmViewTreeFamily,
+ "restricted", [1,3,6,1,6,3,11,2,1], included, null},
+ {vacmViewTreeFamily,
+ "restricted", [1,3,6,1,6,3,15,1,1], included, null}]
+ end,
+ VTF = VTF0 ++ [{vacmViewTreeFamily,"internet",[1,3,6,1],included,null}],
+ write_agent_vacm_config(Dir, Hdr, Groups ++ Acc ++ VTF).
+
+vacm_ver(v1) -> v1;
+vacm_ver(v2) -> v2c;
+vacm_ver(v3) -> usm.
+
+write_agent_vacm_config(Dir, Hdr, Conf) ->
+ snmpa_conf:write_vacm_config(Dir, Hdr, Conf).
+
+update_agent_vacm_config(Dir, Conf) ->
+ snmpa_conf:append_vacm_config(Dir, Conf).
+
+
+%%
+%% ----- Manager config files generator functions -----
+%%
+
+write_manager_snmp_files(Dir, IP, Port, MMS, EngineID,
+ Users, Agents, Usms) ->
+ write_manager_snmp_conf(Dir, IP, Port, MMS, EngineID),
+ write_manager_snmp_users_conf(Dir, Users),
+ write_manager_snmp_agents_conf(Dir, Agents),
+ write_manager_snmp_usm_conf(Dir, Usms),
+ ok.
+
+
+%%
+%% ------ manager.conf ------
+%%
+
+write_manager_snmp_conf(Dir, IP, Port, MMS, EngineID) ->
+ Comment =
+"%% This file defines the Manager local configuration info\n"
+"%% Each row is a 2-tuple:\n"
+"%% {Variable, Value}.\n"
+"%% For example\n"
+"%% {port, 5000}.\n"
+"%% {address, [127,42,17,5]}.\n"
+"%% {engine_id, \"managerEngine\"}.\n"
+"%% {max_message_size, 484}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf = [{port, Port},
+ {address, IP},
+ {engine_id, EngineID},
+ {max_message_size, MMS}],
+ write_manager_config(Dir, Hdr, Conf).
+
+write_manager_config(Dir, Hdr, Conf) ->
+ snmpm_conf:write_manager_config(Dir, Hdr, Conf).
+
+update_manager_config(Dir, Conf) ->
+ snmpm_conf:append_manager_config(Dir, Conf).
+
+
+%%
+%% ------ users.conf ------
+%%
+
+write_manager_snmp_users_conf(Dir, Users) ->
+ Comment =
+"%% This file defines the users the manager handles\n"
+"%% Each row is a 3-tuple:\n"
+"%% {UserId, UserMod, UserData}.\n"
+"%% For example\n"
+"%% {kalle, kalle_callback_user_mod, \"dummy\"}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_manager_users_config(Dir, Hdr, Users).
+
+write_manager_users_config(Dir, Hdr, Users) ->
+ snmpm_conf:write_users_config(Dir, Hdr, Users).
+
+update_manager_users_config(Dir, Users) ->
+ snmpm_conf:append_users_config(Dir, Users).
+
+
+%%
+%% ------ agents.conf ------
+%%
+
+write_manager_snmp_agents_conf(Dir, Agents) ->
+ Comment =
+"%% This file defines the agents the manager handles\n"
+"%% Each row is a 12-tuple:\n"
+"%% {UserId, \n"
+"%% TargetName, Comm, Ip, Port, EngineID, Timeout, \n"
+"%% MaxMessageSize, Version, SecModel, SecName, SecLevel}\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_manager_agents_config(Dir, Hdr, Agents).
+
+write_manager_agents_config(Dir, Hdr, Agents) ->
+ snmpm_conf:write_agents_config(Dir, Hdr, Agents).
+
+update_manager_agents_config(Dir, Agents) ->
+ snmpm_conf:append_agents_config(Dir, Agents).
+
+
+%%
+%% ------ usm.conf -----
+%%
+
+write_manager_snmp_usm_conf(Dir, Usms) ->
+ Comment =
+"%% This file defines the usm users the manager handles\n"
+"%% Each row is a 6 or 7-tuple:\n"
+"%% {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}\n"
+"%% {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ write_manager_usm_config(Dir, Hdr, Usms).
+
+write_manager_usm_config(Dir, Hdr, Usms) ->
+ snmpm_conf:write_usm_config(Dir, Hdr, Usms).
+
+update_manager_usm_config(Dir, Usms) ->
+ snmpm_conf:append_usm_config(Dir, Usms).
+
+
+%%
+%% -------------------------------------------------------------------------
+%%
+
+write_sys_config_file(Dir, Services) ->
+ {ok, Fid} = file:open(filename:join(Dir,"sys.config"), [write]),
+ ok = io:format(Fid, "~s", [header()]),
+ ok = io:format(Fid, "[{snmp, ~n", []),
+ ok = io:format(Fid, " [~n", []),
+ write_sys_config_file_services(Fid, Services),
+ ok = io:format(Fid, " ]~n", []),
+ ok = io:format(Fid, " }~n", []),
+ ok = io:format(Fid, "].~n", []),
+ ok.
+
+write_sys_config_file_services(Fid, [Service]) ->
+ write_sys_config_file_service(Fid, Service),
+ ok = io:format(Fid, "~n", []),
+ ok;
+write_sys_config_file_services(Fid, [Service|Services]) ->
+ write_sys_config_file_service(Fid, Service),
+ ok = io:format(Fid, ", ~n", []),
+ write_sys_config_file_services(Fid, Services).
+
+write_sys_config_file_service(Fid, {Service, Opts}) ->
+ ok = io:format(Fid, " {~w,~n", [Service]),
+ ok = io:format(Fid, " [~n", []),
+ write_sys_config_file_service_opts(Fid, Service, Opts),
+ ok = io:format(Fid, " ]~n", []),
+ ok = io:format(Fid, " }", []),
+ true.
+
+write_sys_config_file_service_opts(Fid, agent, Opts) ->
+ write_sys_config_file_agent_opts(Fid, Opts);
+write_sys_config_file_service_opts(Fid, manager, Opts) ->
+ write_sys_config_file_manager_opts(Fid, Opts).
+
+
+write_sys_config_file_agent_opts(Fid, [Opt]) ->
+ write_sys_config_file_agent_opt(Fid, Opt),
+ ok = io:format(Fid, "~n", []),
+ ok;
+write_sys_config_file_agent_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_agent_opt(Fid, Opt),
+ ok = io:format(Fid, ", ~n", []),
+ write_sys_config_file_agent_opts(Fid, Opts).
+
+
+write_sys_config_file_agent_opt(Fid, {mibs, []}) ->
+ ok = io:format(Fid, " {mibs, []}", []);
+write_sys_config_file_agent_opt(Fid, {priority, Prio}) ->
+ ok = io:format(Fid, " {priority, ~w}", [Prio]);
+write_sys_config_file_agent_opt(Fid, {error_report_mod, Mod}) ->
+ ok = io:format(Fid, " {error_report_mod, ~w}", [Mod]);
+write_sys_config_file_agent_opt(Fid, {versions, Vsns}) ->
+ ok = io:format(Fid, " {versions, ~w}", [Vsns]);
+write_sys_config_file_agent_opt(Fid, {multi_threaded, B}) ->
+ ok = io:format(Fid, " {multi_threaded, ~w}", [B]);
+write_sys_config_file_agent_opt(Fid, {config, Opts}) ->
+ ok = io:format(Fid, " {config, [", []),
+ write_sys_config_file_agent_config_opts(Fid, Opts),
+ ok = io:format(Fid, "}", []);
+write_sys_config_file_agent_opt(Fid, {db_dir, Dir}) ->
+ ok = io:format(Fid, " {db_dir, \"~s\"}", [Dir]);
+write_sys_config_file_agent_opt(Fid, {mib_storage, ets}) ->
+ ok = io:format(Fid, " {mib_storage, ets}", []);
+write_sys_config_file_agent_opt(Fid, {mib_storage, {dets, Dir}}) ->
+ ok = io:format(Fid, " {mib_storage, {dets, \"~s\"}}", [Dir]);
+write_sys_config_file_agent_opt(Fid, {mib_storage, {dets, Dir, Act}}) ->
+ ok = io:format(Fid, " {mib_storage, {dets, \"~s\", ~w}}",
+ [Dir, Act]);
+write_sys_config_file_agent_opt(Fid, {mib_storage, {mnesia, Nodes}}) ->
+ ok = io:format(Fid, " {mib_storage, {mnesia, ~w}}", [Nodes]);
+write_sys_config_file_agent_opt(Fid, {mib_storage, {mnesia, Nodes, Act}}) ->
+ ok = io:format(Fid, " {mib_storage, {mnesia, ~w, ~w}}",
+ [Nodes, Act]);
+write_sys_config_file_agent_opt(Fid, {target_cache, Opts}) ->
+ ok = io:format(Fid, " {target_cache, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {local_db, Opts}) ->
+ ok = io:format(Fid, " {local_db, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {note_store, Opts}) ->
+ ok = io:format(Fid, " {note_store, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {symbolic_store, Opts}) ->
+ ok = io:format(Fid, " {symbolic_store, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {agent_type, Type}) ->
+ ok = io:format(Fid, " {agent_type, ~w}", [Type]);
+write_sys_config_file_agent_opt(Fid, {agent_verbosity, Verb}) ->
+ ok = io:format(Fid, " {agent_verbosity, ~w}", [Verb]);
+write_sys_config_file_agent_opt(Fid, {audit_trail_log, Opts}) ->
+ ok = io:format(Fid, " {audit_trail_log, [", []),
+ write_sys_config_file_agent_atl_opts(Fid, Opts),
+ ok = io:format(Fid, "}", []);
+write_sys_config_file_agent_opt(Fid, {net_if, Opts}) ->
+ ok = io:format(Fid, " {net_if, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {mib_server, Opts}) ->
+ ok = io:format(Fid, " {mib_server, ~w}", [Opts]);
+write_sys_config_file_agent_opt(Fid, {Key, Val}) ->
+ ok = io:format(Fid, " {~w, ~w}", [Key, Val]).
+
+
+%% Mandatory option dir, means that this is never empty:
+write_sys_config_file_agent_config_opts(Fid, [Opt]) ->
+ write_sys_config_file_agent_config_opt(Fid, Opt),
+ ok = io:format(Fid, "]", []),
+ ok;
+write_sys_config_file_agent_config_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_agent_config_opt(Fid, Opt),
+ ok = io:format(Fid, ", ", []),
+ write_sys_config_file_agent_config_opts(Fid, Opts).
+
+write_sys_config_file_agent_config_opt(Fid, {dir, Dir}) ->
+ ok = io:format(Fid, "{dir, \"~s\"}", [Dir]);
+write_sys_config_file_agent_config_opt(Fid, {force_load, Bool}) ->
+ ok = io:format(Fid, "{force_load, ~w}", [Bool]);
+write_sys_config_file_agent_config_opt(Fid, {verbosity, Verb}) ->
+ ok = io:format(Fid, "{verbosity, ~w}", [Verb]).
+
+
+%% This is only present if there is atleast one option
+write_sys_config_file_agent_atl_opts(Fid, [Opt]) ->
+ write_sys_config_file_agent_atl_opt(Fid, Opt),
+ ok = io:format(Fid, "]", []),
+ ok;
+write_sys_config_file_agent_atl_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_agent_atl_opt(Fid, Opt),
+ ok = io:format(Fid, ", ", []),
+ write_sys_config_file_agent_atl_opts(Fid, Opts).
+
+write_sys_config_file_agent_atl_opt(Fid, {dir, Dir}) ->
+ ok = io:format(Fid, "{dir, \"~s\"}", [Dir]);
+write_sys_config_file_agent_atl_opt(Fid, {type, Type}) ->
+ ok = io:format(Fid, "{type, ~w}", [Type]);
+write_sys_config_file_agent_atl_opt(Fid, {size, Size}) ->
+ ok = io:format(Fid, "{size, ~w}", [Size]);
+write_sys_config_file_agent_atl_opt(Fid, {repair, Rep}) ->
+ ok = io:format(Fid, "{repair, ~w}", [Rep]).
+
+
+write_sys_config_file_manager_opts(Fid, [Opt]) ->
+ write_sys_config_file_manager_opt(Fid, Opt),
+ ok = io:format(Fid, "~n", []),
+ ok;
+write_sys_config_file_manager_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_manager_opt(Fid, Opt),
+ ok = io:format(Fid, ", ~n", []),
+ write_sys_config_file_manager_opts(Fid, Opts).
+
+
+write_sys_config_file_manager_opt(Fid, {mibs, []}) ->
+ ok = io:format(Fid, " {mibs, []}", []);
+write_sys_config_file_manager_opt(Fid, {priority, Prio}) ->
+ ok = io:format(Fid, " {priority, ~w}", [Prio]);
+write_sys_config_file_manager_opt(Fid, {versions, Vsns}) ->
+ ok = io:format(Fid, " {versions, ~w}", [Vsns]);
+write_sys_config_file_manager_opt(Fid, {config, Opts}) ->
+ ok = io:format(Fid, " {config, [", []),
+ write_sys_config_file_manager_config_opts(Fid, Opts),
+ ok = io:format(Fid, "}", []);
+write_sys_config_file_manager_opt(Fid, {server, Opts}) ->
+ ok = io:format(Fid, " {server, ~w}", [Opts]);
+write_sys_config_file_manager_opt(Fid, {note_store, Opts}) ->
+ ok = io:format(Fid, " {note_store, ~w}", [Opts]);
+write_sys_config_file_manager_opt(Fid, {audit_trail_log, Opts}) ->
+ ok = io:format(Fid, " {audit_trail_log, [", []),
+ write_sys_config_file_manager_atl_opts(Fid, Opts),
+ ok = io:format(Fid, "}", []);
+write_sys_config_file_manager_opt(Fid, {net_if, Opts}) ->
+ ok = io:format(Fid, " {net_if, ~w}", [Opts]);
+write_sys_config_file_manager_opt(Fid, {Key, Val}) ->
+ ok = io:format(Fid, " {~w, ~w}", [Key, Val]).
+
+%% Mandatory option dir, means that this is never empty:
+write_sys_config_file_manager_config_opts(Fid, [Opt]) ->
+ write_sys_config_file_manager_config_opt(Fid, Opt),
+ ok = io:format(Fid, "]", []),
+ ok;
+write_sys_config_file_manager_config_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_manager_config_opt(Fid, Opt),
+ ok = io:format(Fid, ", ", []),
+ write_sys_config_file_manager_config_opts(Fid, Opts).
+
+write_sys_config_file_manager_config_opt(Fid, {dir, Dir}) ->
+ ok = io:format(Fid, "{dir, \"~s\"}", [Dir]);
+write_sys_config_file_manager_config_opt(Fid, {db_dir, Dir}) ->
+ ok = io:format(Fid, "{db_dir, \"~s\"}", [Dir]);
+write_sys_config_file_manager_config_opt(Fid, {repair, Rep}) ->
+ ok = io:format(Fid, "{repair, ~w}", [Rep]);
+write_sys_config_file_manager_config_opt(Fid, {auto_save, As}) ->
+ ok = io:format(Fid, "{auto_save, ~w}", [As]);
+write_sys_config_file_manager_config_opt(Fid, {verbosity, Verb}) ->
+ ok = io:format(Fid, "{verbosity, ~w}", [Verb]).
+
+
+%% This is only present if there is atleast one option
+write_sys_config_file_manager_atl_opts(Fid, [Opt]) ->
+ write_sys_config_file_manager_atl_opt(Fid, Opt),
+ ok = io:format(Fid, "]", []),
+ ok;
+write_sys_config_file_manager_atl_opts(Fid, [Opt|Opts]) ->
+ write_sys_config_file_manager_atl_opt(Fid, Opt),
+ ok = io:format(Fid, ", ", []),
+ write_sys_config_file_manager_atl_opts(Fid, Opts).
+
+write_sys_config_file_manager_atl_opt(Fid, {dir, Dir}) ->
+ ok = io:format(Fid, "{dir, \"~s\"}", [Dir]);
+write_sys_config_file_manager_atl_opt(Fid, {type, Type}) ->
+ ok = io:format(Fid, "{type, ~w}", [Type]);
+write_sys_config_file_manager_atl_opt(Fid, {size, Size}) ->
+ ok = io:format(Fid, "{size, ~w}", [Size]);
+write_sys_config_file_manager_atl_opt(Fid, {repair, Rep}) ->
+ ok = io:format(Fid, "{repair, ~w}", [Rep]).
+
+
+header() ->
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ io_lib:format("%% This file was generated by "
+ "snmp_config (version-~s) ~w-~2.2.0w-~2.2.0w "
+ "~2.2.0w:~2.2.0w:~2.2.0w\n",
+ [?version,Y,Mo,D,H,Mi,S]).
+
+
+write_config_file(Dir, FileName, Verify, Write)
+ when (is_list(Dir) andalso
+ is_list(FileName) andalso
+ is_function(Verify) andalso
+ is_function(Write)) ->
+ (catch do_write_config_file(Dir, FileName, Verify, Write)).
+
+do_write_config_file(Dir, FileName, Verify, Write) ->
+ Verify(),
+ case file:open(filename:join(Dir, FileName), [write]) of
+ {ok, Fd} ->
+ (catch Write(Fd)),
+ file:close(Fd),
+ ok;
+ Error ->
+ Error
+ end.
+
+
+append_config_file(Dir, FileName, Verify, Write)
+ when (is_list(Dir) andalso
+ is_list(FileName) andalso
+ is_function(Verify) andalso
+ is_function(Write)) ->
+ (catch do_append_config_file(Dir, FileName, Verify, Write)).
+
+do_append_config_file(Dir, FileName, Verify, Write) ->
+ Verify(),
+ case file:open(filename:join(Dir, FileName), [read, write]) of
+ {ok, Fd} ->
+ file:position(Fd, eof),
+ (catch Write(Fd)),
+ file:close(Fd),
+ ok;
+ Error ->
+ Error
+ end.
+
+
+read_config_file(Dir, FileName, Verify)
+ when is_list(Dir) andalso is_list(FileName) andalso is_function(Verify) ->
+ (catch do_read_config_file(Dir, FileName, Verify)).
+
+do_read_config_file(Dir, FileName, Verify) ->
+ case file:open(filename:join(Dir, FileName), [read]) of
+ {ok, Fd} ->
+ Result = read_loop(Fd, [], Verify, 1),
+ file:close(Fd),
+ Result;
+ {error, Reason} ->
+ {error, {Reason, FileName}}
+ end.
+
+read_loop(Fd, Acc, Check, StartLine) ->
+ case read_term(Fd, StartLine) of
+ {ok, Term, EndLine} ->
+ case (catch Check(Term)) of
+ ok ->
+ read_loop(Fd, [Term | Acc], Check, EndLine);
+ {error, Reason} ->
+ {error, {failed_check, StartLine, EndLine, Reason}};
+ Error ->
+ {error, {failed_check, StartLine, EndLine, Error}}
+ end;
+ {error, EndLine, Error} ->
+ {error, {failed_reading, StartLine, EndLine, Error}};
+ eof ->
+ {ok, lists:reverse(Acc)}
+ end.
+
+read_term(Fd, StartLine) ->
+ case io:request(Fd, {get_until, "", erl_scan, tokens, [StartLine]}) of
+ {ok, Tokens, EndLine} ->
+ case erl_parse:parse_term(Tokens) of
+ {ok, Term} ->
+ {ok, Term, EndLine};
+ {error, {Line, erl_parse, Error}} ->
+ {error, Line, {parse_error, Error}}
+ end;
+ {error, E, EndLine} ->
+ {error, EndLine, E};
+ {eof, _EndLine} ->
+ eof;
+ Other ->
+ Other
+ end.
+
+
+agent_snmp_mk_secret(Alg, Passwd, EngineID) ->
+ snmp_usm:passwd2localized_key(Alg, Passwd, EngineID).
+
+
+ensure_crypto_started() ->
+ i("making sure crypto server is started..."),
+ ensure_started(crypto).
+
+ensure_started(App) ->
+ case (catch App:start()) of
+ ok ->
+ ok;
+ {error, {already_started, App}} ->
+ ok;
+ E ->
+ error({failed_starting, App, E})
+ end.
+
+
+%% -------------------------------------------------------------------------
+
+% d(F, A) ->
+% i("DBG: " ++ F, A).
+
+i(F) ->
+ i(F, []).
+
+i(F, A) ->
+ io:format(F ++ "~n", A).
+
+error(R) ->
+ throw({error, R}).
diff --git a/lib/snmp/src/misc/snmp_debug.hrl b/lib/snmp/src/misc/snmp_debug.hrl
new file mode 100644
index 0000000000..dc916ac96a
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_debug.hrl
@@ -0,0 +1,38 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifdef(snmp_debug).
+-define(d(F,A),
+ io:format("~p:~p:~p:" ++ F ++ "~n",[self(),?MODULE,?LINE]++A)).
+
+%% Same as 'd' but without the ending newline ('~n').
+-define(d_b(F,A),
+ io:format("~p:~p:~p:" ++ F,[self(),?MODULE,?LINE]++A)).
+%% To be used together with 'd_b'. Note: NO ending newline ('~n')..
+-define(d_e(F,A),
+ io:format(F,A)).
+-else.
+-define(d(F,A),ok).
+-define(d_b(F,A),ok).
+-define(d_e(F,A),ok).
+-endif.
+
+
+
+
diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl
new file mode 100644
index 0000000000..c3932ccc08
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_log.erl
@@ -0,0 +1,584 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_log).
+
+
+-export([
+ create/4, create/5,
+ change_size/2, close/1, sync/1, info/1,
+ log/4,
+ log_to_txt/5, log_to_txt/6, log_to_txt/7,
+ log_to_io/4, log_to_io/5, log_to_io/6
+ ]).
+
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+
+-define(VMODULE,"LOG").
+-include("snmp_verbosity.hrl").
+
+-define(LOG_FORMAT, internal).
+-define(LOG_TYPE, wrap).
+
+
+%% --------------------------------------------------------------------
+%% Exported functions
+%% --------------------------------------------------------------------
+
+
+%% -- create ---
+
+create(Name, File, Size, Repair) ->
+ create(Name, File, Size, Repair, false).
+
+create(Name, File, Size, Repair, Notify) ->
+ ?vtrace("create -> entry with"
+ "~n Name: ~p"
+ "~n File: ~p"
+ "~n Size: ~p"
+ "~n Repair: ~p"
+ "~n Notify: ~p", [Name, File, Size, Repair, Notify]),
+ log_open(Name, File, Size, Repair, Notify).
+
+
+%% -- close ---
+
+close(Log) ->
+ ?vtrace("close -> entry with"
+ "~n Log: ~p", [Log]),
+ disk_log:close(Log).
+
+
+%% -- close ---
+
+sync(Log) ->
+ ?vtrace("sync -> entry with"
+ "~n Log: ~p", [Log]),
+ disk_log:sync(Log).
+
+%% -- info ---
+
+info(Log) ->
+ case disk_log:info(Log) of
+ Info when is_list(Info) ->
+ Items = [no_current_bytes, no_current_items,
+ current_file, no_overflows],
+ info_filter(Items, Info, []);
+ Else ->
+ Else
+ end.
+
+info_filter([], _Info, Acc) ->
+ {ok, Acc};
+info_filter([Item|Items], Info, Acc) ->
+ case lists:keysearch(Item, 1, Info) of
+ {value, New} ->
+ info_filter(Items, Info, [New|Acc]);
+ false ->
+ info_filter(Items, Info, Acc)
+ end.
+
+
+%% -- log ---
+
+%%-----------------------------------------------------------------
+%% For efficiency reasons, we want to log the packet as a binary.
+%% This is only possible for messages that are not encrypted.
+%% Therefore, Packet can be either a binary (encoded message), or
+%% a tuple {V3Hdr, ScopedPduBytes}
+%%
+%% log(Log, Packet, Addr, Port)
+%%-----------------------------------------------------------------
+
+
+log(Log, Packet, Addr, Port) ->
+ ?vtrace("log -> entry with"
+ "~n Log: ~p"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Log, Addr, Port]),
+ Entry = {timestamp(), Packet, Addr, Port},
+ disk_log:alog(Log, Entry).
+
+
+
+%% -- change_size ---
+
+change_size(Log, NewSize) ->
+ ?vtrace("change_size -> entry with"
+ "~n Log: ~p"
+ "~n NewSize: ~p", [Log, NewSize]),
+ disk_log:change_size(Log, NewSize).
+
+
+%% -- log_to_txt ---
+
+log_to_txt(Log, FileName, Dir, Mibs, TextFile) ->
+ log_to_txt(Log, FileName, Dir, Mibs, TextFile, null, null).
+
+log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start) ->
+ log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start, null).
+
+log_to_txt(Log, FileName, Dir, Mibs, TextFile, Start, Stop)
+ when is_list(Mibs) andalso is_list(TextFile) ->
+ ?vtrace("log_to_txt -> entry with"
+ "~n Log: ~p"
+ "~n FileName: ~p"
+ "~n Dir: ~p"
+ "~n Mibs: ~p"
+ "~n TextFile: ~p"
+ "~n Start: ~p"
+ "~n Stop: ~p",
+ [Log, FileName, Dir, Mibs, TextFile, Start, Stop]),
+ File = filename:join(Dir, FileName),
+ Converter = fun(L) ->
+ do_log_to_file(L, TextFile, Mibs, Start, Stop)
+ end,
+ log_convert(Log, File, Converter).
+
+
+%% -- log_to_io ---
+
+log_to_io(Log, FileName, Dir, Mibs) ->
+ log_to_io(Log, FileName, Dir, Mibs, null, null).
+
+log_to_io(Log, FileName, Dir, Mibs, Start) ->
+ log_to_io(Log, FileName, Dir, Mibs, Start, null).
+
+log_to_io(Log, FileName, Dir, Mibs, Start, Stop)
+ when is_list(Mibs) ->
+ File = filename:join(Dir, FileName),
+ Converter = fun(L) ->
+ do_log_to_io(L, Mibs, Start, Stop)
+ end,
+ log_convert(Log, File, Converter).
+
+
+%% --------------------------------------------------------------------
+%% Internal functions
+%% --------------------------------------------------------------------
+
+
+%% -- log_convert ---
+
+log_convert(Log, File, Converter) ->
+ %% First check if the caller process has already opened the
+ %% log, because if we close an already open log we will cause
+ %% a runtime error.
+ case is_owner(Log) of
+ true ->
+ Converter(Log);
+ false ->
+ %% Not yet member of the ruling party, apply for membership...
+ %% If a log is opened as read_write it is not possible to
+ %% open it as read_only. So, to get around this we open
+ %% it under a different name...
+ Log2 = convert_name(Log),
+ case log_open(Log2, File) of
+ {ok, _} ->
+ Res = Converter(Log2),
+ disk_log:close(Log2),
+ Res;
+ {error, {name_already_open, _}} ->
+ Converter(Log2);
+ {error, Reason} ->
+ {error, {Log, Reason}}
+ end
+ end.
+
+convert_name(Name) when is_list(Name) ->
+ Name ++ "_tmp";
+convert_name(Name) when is_atom(Name) ->
+ list_to_atom(atom_to_list(Name) ++ "_tmp");
+convert_name(Name) ->
+ lists:flatten(io_lib:format("~w_tmp", [Name])).
+
+
+%% -- do_log_to_text ---
+
+do_log_to_file(Log, TextFile, Mibs, Start, Stop) ->
+ case file:open(TextFile, [write]) of
+ {ok, Fd} ->
+ MiniMib = snmp_mini_mib:create(Mibs),
+ Write = fun(X) ->
+ case format_msg(X, MiniMib, Start, Stop) of
+ {ok, S} ->
+ io:format(Fd, "~s", [S]);
+ _ ->
+ ok
+ end
+ end,
+ Res = (catch loop(disk_log:chunk(Log, start), Log, Write)),
+ snmp_mini_mib:delete(MiniMib),
+ file:close(Fd),
+ Res;
+ {error, Reason} ->
+ {error, {TextFile, Reason}}
+ end.
+
+
+do_log_to_io(Log, Mibs, Start, Stop) ->
+ MiniMib = snmp_mini_mib:create(Mibs),
+ Write = fun(X) ->
+ case format_msg(X, MiniMib, Start, Stop) of
+ {ok, S} ->
+ io:format("~s", [S]);
+ _ ->
+ ok
+ end
+ end,
+ (catch loop(disk_log:chunk(Log, start), Log, Write)),
+ snmp_mini_mib:delete(MiniMib),
+ ok.
+
+
+loop(eof, _Log, _Write) ->
+ ok;
+loop({error, _} = Error, _Log, _Write) ->
+ Error;
+loop({corrupt_log_file, _} = Reason, _Log, _Write) ->
+ {error, Reason};
+loop({Cont, Terms}, Log, Write) ->
+ case (catch lists:foreach(Write, Terms)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ _ ->
+ loop(disk_log:chunk(Log, Cont), Log, Write)
+ end;
+loop({Cont, Terms, BadBytes}, Log, Write) ->
+ error_logger:error_msg("Skipping ~w bytes while converting ~p~n~n",
+ [BadBytes, Log]),
+ case (catch lists:foreach(Write, Terms)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ _ ->
+ loop(disk_log:chunk(Log, Cont), Log, Write)
+ end;
+loop(Error, _Log, _Write) ->
+ Error.
+
+format_msg({TimeStamp, {V3Hdr, ScopedPdu}, {Addr, Port}},
+ Mib, Start, Stop) ->
+ format_msg({TimeStamp, {V3Hdr, ScopedPdu}, Addr, Port},
+ Mib, Start, Stop);
+format_msg({TimeStamp, {V3Hdr, ScopedPdu}, Addr, Port},
+ Mib, Start, Stop) ->
+% io:format("format_msg -> entry with"
+% "~n TimeStamp: ~p"
+% "~n Start: ~p"
+% "~n Stop: ~p", [TimeStamp, Start, Stop]),
+ case timestamp_filter(TimeStamp, Start, Stop) of
+ true ->
+ case (catch snmp_pdus:dec_scoped_pdu(ScopedPdu)) of
+ ScopedPDU when is_record(ScopedPDU, scopedPdu) ->
+ Msg = #message{version = 'version-3',
+ vsn_hdr = V3Hdr,
+ data = ScopedPDU},
+ f(ts2str(TimeStamp), Msg, Addr, Port, Mib);
+ {'EXIT', Reason} ->
+ format_tab("** error in log file at ~s from ~p:~w ~p\n\n",
+ [ts2str(TimeStamp), ip(Addr), Port, Reason])
+ end;
+ false ->
+ ignore
+ end;
+format_msg({TimeStamp, Packet, {Addr, Port}}, Mib, Start, Stop) ->
+ format_msg({TimeStamp, Packet, Addr, Port}, Mib, Start, Stop);
+format_msg({TimeStamp, Packet, Addr, Port}, Mib, Start, Stop) ->
+ case timestamp_filter(TimeStamp, Start, Stop) of
+ true ->
+ case (catch snmp_pdus:dec_message(binary_to_list(Packet))) of
+ Msg when is_record(Msg, message) ->
+ f(ts2str(TimeStamp), Msg, Addr, Port, Mib);
+ {'EXIT', Reason} ->
+ format_tab("** error in log file ~p\n\n", [Reason])
+ end;
+ false ->
+ ignore
+ end;
+format_msg(_, _Mib, _Start, _Stop) ->
+ format_tab("** unknown entry in log file\n\n", []).
+
+f(TimeStamp, #message{version = Vsn, vsn_hdr = VsnHdr, data = Data},
+ Addr, Port, Mib) ->
+ Str = format_pdu(Data, Mib),
+ HdrStr = format_header(Vsn, VsnHdr),
+ case get_type(Data) of
+ trappdu ->
+ f_trap(TimeStamp, Vsn, HdrStr, Str, Addr, Port);
+ 'snmpv2-trap' ->
+ f_trap(TimeStamp, Vsn, HdrStr, Str, Addr, Port);
+ 'inform-request' ->
+ f_inform(TimeStamp, Vsn, HdrStr, Str, Addr, Port);
+ 'get-response' ->
+ f_response(TimeStamp, Vsn, HdrStr, Str, Addr, Port);
+ report ->
+ f_report(TimeStamp, Vsn, HdrStr, Str, Addr, Port);
+ _ ->
+ f_request(TimeStamp, Vsn, HdrStr, Str, Addr, Port)
+ end.
+
+f_request(TimeStamp, Vsn, HdrStr, Str, Addr, Port) ->
+ format_tab("request ~s:~w - ~s [~s] ~w\n~s",
+ [ip(Addr), Port, HdrStr, TimeStamp, Vsn, Str]).
+
+f_response(TimeStamp, Vsn, HdrStr, Str, Addr, Port) ->
+ format_tab("response ~s:~w - ~s [~s] ~w\n~s",
+ [ip(Addr), Port, HdrStr, TimeStamp, Vsn, Str]).
+
+f_report(TimeStamp, Vsn, HdrStr, Str, Addr, Port) ->
+ format_tab("report ~s:~w - ~s [~s] ~w\n~s",
+ [ip(Addr), Port, HdrStr, TimeStamp, Vsn, Str]).
+
+f_trap(TimeStamp, Vsn, HdrStr, Str, Addr, Port) ->
+ format_tab("trap ~s:~w - ~s [~s] ~w\n~s",
+ [ip(Addr), Port, HdrStr, TimeStamp, Vsn, Str]).
+
+f_inform(TimeStamp, Vsn, HdrStr, Str, Addr, Port) ->
+ format_tab("inform ~s:~w - ~s [~s] ~w\n~s",
+ [ip(Addr), Port, HdrStr, TimeStamp, Vsn, Str]).
+
+
+%% Convert a timestamp 2-tupple to a printable string
+%%
+ts2str({Local,Universal}) ->
+ dat2str(Local) ++ " , " ++ dat2str(Universal);
+ts2str(_) ->
+ "".
+
+%% Convert a datetime 2-tupple to a printable string
+%%
+dat2str({{Y,M,D},{H,Min,S}}) ->
+ io_lib:format("~w-~w-~w,~w:~w:~w",[Y,M,D,H,Min,S]).
+
+
+timestamp_filter({Local,Universal},Start,Stop) ->
+ tsf_ge(Local,Universal,Start) and tsf_le(Local,Universal,Stop);
+timestamp_filter(_,_Start,_Stop) ->
+ true.
+
+tsf_ge(_Local,_Universal,null) ->
+ true;
+tsf_ge(Local,_Universal,{local_time,DateTime}) ->
+ tsf_ge(Local,DateTime);
+tsf_ge(_Local,Universal,{universal_time,DateTime}) ->
+ tsf_ge(Universal,DateTime);
+tsf_ge(Local,_Universal,DateTime) ->
+ tsf_ge(Local,DateTime).
+
+tsf_ge(TimeStamp,DateTime) ->
+ T1 = calendar:datetime_to_gregorian_seconds(TimeStamp),
+ T2 = calendar:datetime_to_gregorian_seconds(DateTime),
+ T1 >= T2.
+
+tsf_le(_Local,_Universal,null) ->
+ true;
+tsf_le(Local,_Universal,{local_time,DateTime}) ->
+ tsf_le(Local,DateTime);
+tsf_le(_Local,Universal,{universal_time,DateTime}) ->
+ tsf_le(Universal,DateTime);
+tsf_le(Local,_Universal,DateTime) ->
+ tsf_le(Local,DateTime).
+
+tsf_le(TimeStamp,DateTime) ->
+ T1 = calendar:datetime_to_gregorian_seconds(TimeStamp),
+ T2 = calendar:datetime_to_gregorian_seconds(DateTime),
+ T1 =< T2.
+
+
+%% In the output replace TAB by ESC TAB, and add a single trailing TAB.
+%%
+format_tab(Format, Args) ->
+ Str = lists:flatten(io_lib:format(Format, Args)),
+ DStr = lists:map(fun($\t) -> "\e\t"; (C) -> C end, Str),
+ {ok, io_lib:format("~s\t", [DStr])}.
+
+
+format_header('version-1', CommunityStr) ->
+ CommunityStr;
+format_header('version-2', CommunityStr) ->
+ CommunityStr;
+format_header('version-3', #v3_hdr{msgFlags = MsgFlags,
+ msgSecurityModel = SecModel,
+ msgSecurityParameters = SecParams}) ->
+ SecLevel = snmp_misc:get_sec_level(MsgFlags),
+ case SecModel of
+ ?SEC_USM ->
+ case catch snmp_pdus:dec_usm_security_parameters(SecParams) of
+ #usmSecurityParameters{msgAuthoritativeEngineID = AuthEngineID,
+ msgUserName = UserName} ->
+ io_lib:format("~w:\"~s\":\"~s\"",
+ [SecLevel, AuthEngineID, UserName]);
+ _ ->
+ "-"
+ end;
+ _ ->
+ "\"unknown security model\""
+ end.
+
+
+format_pdu(#scopedPdu{contextName = Context, data = Pdu}, Mib) ->
+ io_lib:format("Context: \"~s\"\n~s",
+ [Context, snmp_misc:format_pdu(Pdu, Mib)]);
+format_pdu(Pdu, Mib) ->
+ snmp_misc:format_pdu(Pdu, Mib).
+
+get_type(#scopedPdu{data = Pdu}) ->
+ get_type(Pdu);
+get_type(Pdu) when is_record(Pdu, trappdu) ->
+ trappdu;
+get_type(#pdu{type = Type}) ->
+ Type.
+
+
+ip({A,B,C,D}) ->
+ io_lib:format("~w.~w.~w.~w", [A,B,C,D]).
+
+
+
+%% -------------------------------------------------------------------
+%% Various utility functions
+%% -------------------------------------------------------------------
+
+log_open(Name, File, Size, Repair, Notify) ->
+ case do_log_open(Name, File, Size, Repair, Notify) of
+ {ok, Log} ->
+ {ok, Log};
+ {repaired, Log, Rec, Bad} ->
+ ?vlog("log_open -> repaired: "
+ "~n Rec: ~p"
+ "~n Bad: ~p", [Rec, Bad]),
+ {ok, Log};
+ Error ->
+ Error
+ end.
+
+%% We need to make sure we do not end up in an infinit loop
+%% Take the number of files of the wrap log and add 2 (for
+%% the index and size files).
+do_log_open(Name, File, {_, N} = Size, snmp_repair = _Repair, Notify) ->
+ do_snmp_log_open(Name, File, Size, N+2, Notify);
+
+do_log_open(Name, File, Size, snmp_repair = _Repair, Notify) ->
+ do_snmp_log_open(Name, File, Size, 1, Notify);
+
+do_log_open(Name, File, Size, Repair, Notify) ->
+ do_std_log_open(Name, File, Size, Repair, Notify).
+
+
+do_snmp_log_open(Name, File, Size, N, Notify) when N =< 0 ->
+ do_std_log_open(Name, File, Size, true, Notify);
+do_snmp_log_open(Name, File, Size, N, Notify) ->
+ case do_std_log_open(Name, File, Size, true, Notify) of
+ {error, {not_a_log_file, XFile}} ->
+ case file:rename(XFile, lists:append([XFile, ".MOVED"])) of
+ ok ->
+ ?vinfo("Failed open log file (even with repair) - "
+ "not a logfile:"
+ "~n Attempting to move file aside (.MOVED)"
+ "~n ~s", [XFile]),
+ do_snmp_log_open(Name, File, Size, N-1, Notify);
+ Error ->
+ {error, {rename_failed, Error}}
+ end;
+ {error, Reason} ->
+ ?vinfo("Failed open log file (even with repair) - "
+ "~n Attempting to move old log file aside (.MOVED)"
+ "~n~p", [Reason]),
+ move_log(File),
+ do_std_log_open(Name, File, Size, true, Notify);
+ Else ->
+ Else
+ end.
+
+
+%% First try to open the log without the size-spec. This will
+%% succeed if the log has already been created. In that case,
+%% we'll use whatever size the log had at the time it was closed.
+do_std_log_open(Name, File, Size, Repair, Notify) ->
+ Opts = [{name, Name},
+ {file, File},
+ {type, ?LOG_TYPE},
+ {format, ?LOG_FORMAT},
+ {mode, read_write},
+ {notify, Notify},
+ {repair, Repair}],
+ case disk_log:open(Opts) of
+ {error, {badarg, size}} ->
+ %% The log didn't exist, try with the size-spec
+ disk_log:open([{size, Size} | Opts]);
+ Else ->
+ Else
+ end.
+
+
+log_open(Name, File) ->
+ Opts = [{name, Name},
+ {file, File},
+ {type, ?LOG_TYPE},
+ {format, ?LOG_FORMAT},
+ {mode, read_only}],
+ case disk_log:open(Opts) of
+ {error, {badarg, size}} ->
+ {error, no_such_log};
+ Else ->
+ Else
+ end.
+
+
+move_log(File) ->
+ Dir = filename:dirname(File),
+ FileName = filename:basename(File),
+ case file:list_dir(Dir) of
+ {ok, Files0} ->
+ Files = [F || F <- Files0, lists:prefix(FileName, F)],
+ F = fun(XFile) ->
+ file:rename(XFile, lists:append([XFile, ".MOVED"]))
+ end,
+ lists:foreach(F, Files);
+ _ ->
+ ok
+ end.
+
+
+is_owner(Log) ->
+ lists:member(self(), log_owners(Log)).
+
+log_owners(Log) ->
+ Info = log_info(Log),
+ case lists:keysearch(owners, 1, Info) of
+ {value, {_, Pids}} ->
+ [P || {P, _} <- Pids];
+ _ ->
+ []
+ end.
+
+log_info(Log) ->
+ case disk_log:info(Log) of
+ Info when is_list(Info) ->
+ Info;
+ _ ->
+ []
+ end.
+
+
+timestamp() ->
+ {calendar:local_time(), calendar:universal_time()}.
+
diff --git a/lib/snmp/src/misc/snmp_mini_mib.erl b/lib/snmp/src/misc/snmp_mini_mib.erl
new file mode 100644
index 0000000000..d80270d5c2
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_mini_mib.erl
@@ -0,0 +1,151 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_mini_mib).
+
+%% need definition of mib record
+-include("snmp_types.hrl").
+
+-export([
+ create/1,
+ delete/1,
+ aliasname/2,
+ oid/2,
+ type/2
+ ]).
+
+
+-record(mini_mib, {cache, db = []}).
+
+
+%%%--------------------------------------------------
+%%% The Mini MIB representation
+%%%--------------------------------------------------
+
+%% Returns a Mini MIB
+create(Mibs) ->
+ Loaded = lists:append([load_mib(Mib) || Mib <- Mibs]),
+ Sorted = lists:keysort(1, Loaded),
+ Db = remove_dubbletts(Sorted),
+ Cache = ets:new(snmp_mini_mib_cache, [set, {keypos, 1}]),
+ #mini_mib{cache = Cache,
+ db = Db}.
+
+delete(#mini_mib{cache = Cache}) ->
+ ets:delete(Cache),
+ ok.
+
+
+%%----------------------------------------------------------------------
+%% Returns: A list of {Oid, Aliasname, Type}
+%%----------------------------------------------------------------------
+load_mib(MIB) ->
+ F1 = snmp_misc:strip_extension_from_filename(MIB, ".bin"),
+ ActualFileName = lists:append(F1, ".bin"),
+ case snmp_misc:read_mib(ActualFileName) of
+ {ok, #mib{mes = MEs, traps = Traps}} ->
+ make_mini_mib_elem(MEs++Traps);
+ {error, Reason} ->
+ exit({error, {MIB, Reason}})
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Pre: List is sorted (dublettes are list neighbours)
+%%----------------------------------------------------------------------
+remove_dubbletts([]) -> [];
+remove_dubbletts([X]) -> [X];
+remove_dubbletts([X,X|T]) -> remove_dubbletts([X|T]);
+remove_dubbletts([X|T]) -> [X|remove_dubbletts(T)].
+
+
+%%----------------------------------------------------------------------
+%% Args: A list if Mes
+%% Returns: a list of {Oid, Aliasname, Type}
+%%----------------------------------------------------------------------
+make_mini_mib_elem([]) -> [];
+make_mini_mib_elem([#me{aliasname = N,
+ oid = Oid,
+ entrytype = variable,
+ asn1_type = #asn1_type{bertype = Type}} | T]) ->
+ [{Oid, N, Type} | make_mini_mib_elem(T)];
+make_mini_mib_elem([#me{aliasname = N,
+ oid = Oid,
+ entrytype = table_column,
+ asn1_type = ASN1}|T])
+ when is_record(ASN1, asn1_type)->
+ [{Oid, N, ASN1#asn1_type.bertype} | make_mini_mib_elem(T)];
+make_mini_mib_elem([#me{aliasname = N,
+ oid = Oid,
+ asn1_type = undefined}|T]) ->
+ [{Oid, N, undefined} | make_mini_mib_elem(T)];
+make_mini_mib_elem([#notification{trapname = N,
+ oid = Oid}|T]) ->
+ [{Oid, N, undefined} | make_mini_mib_elem(T)];
+make_mini_mib_elem([_|T]) ->
+ make_mini_mib_elem(T).
+
+
+%%----------------------------------------------------------------------
+%% Returns: false | {Oid, Aliasname, Type}
+%%----------------------------------------------------------------------
+
+aliasname(#mini_mib{cache = Cache, db = Db}, Oid) ->
+ Key = {Oid, aliasname},
+ case ets:lookup(Cache, Key) of
+ [{_, Value}] ->
+ Value;
+ _ ->
+ Value = aliasname(Db, Oid, false),
+ ets:insert(Cache, {Key, Value}),
+ Value
+ end.
+
+aliasname([], _Oid, Res) ->
+ Res;
+aliasname([{Oid, _Aliasname, _Type} = OidData|T], OidX, Res)
+ when Oid =< OidX ->
+ case lists:prefix(Oid, OidX) of
+ true ->
+ aliasname(T, OidX, OidData);
+ false ->
+ aliasname(T, OidX, Res)
+ end;
+aliasname([{_Oid, _Aliasname, _Type}|_T], _OidX, Res) ->
+ Res.
+
+
+oid(#mini_mib{db = Db}, AliasName) ->
+ case lists:keysearch(AliasName, 2, Db) of
+ {value, {Oid, _Aliasname, _Type}} ->
+ Oid;
+ false ->
+ false
+ end.
+
+
+type(MiniMIB, Oid) ->
+ case aliasname(MiniMIB, Oid) of
+ {_Oid, _AliasName, Type} ->
+ Type;
+ Else ->
+ Else
+ end.
+
+
diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl
new file mode 100644
index 0000000000..1b535743a4
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_misc.erl
@@ -0,0 +1,465 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_misc).
+
+%% need definition of mib record
+-include("snmp_types.hrl").
+-include("snmpc_misc.hrl").
+
+-define(VMODULE,"MISC").
+-include("snmp_verbosity.hrl").
+
+
+-export([assq/2,
+ bits_to_int/2,
+ diff/2,
+ ensure_trailing_dir_delimiter/1,
+ foreach/3,
+ format_pdu/2,
+ format_val/4,
+ format_vb/2,
+ format_vbs/2,
+ format/3,
+ get_option/2,
+ get_option/3,
+ get_sec_level/1,
+ ip/1,
+ is_auth/1,
+ is_BitString/1,
+ is_oid/1,
+ is_priv/1,
+ is_reportable/1,
+ is_reportable_pdu/1,
+ is_string/1,
+ is_tag_member/2,
+ is_tmask_match/3,
+ keyreplaceadd/4,
+ mem_size/1,
+ mk_msg_flags/2,
+ multi_map/2,
+ %% now/0,
+ now/1,
+ read_mib/1,
+ set_option/3,
+ sleep/1,
+ strip_extension_from_filename/2,
+ str_xor/2,
+ time/3,
+
+ verify_behaviour/2
+ ]).
+
+
+verify_behaviour(Behaviour, UserMod)
+ when is_atom(Behaviour) andalso is_atom(UserMod) ->
+ case (catch UserMod:module_info(exports)) of
+ Exps when is_list(Exps) ->
+ Callbacks = Behaviour:behaviour_info(callbacks),
+ (catch verify_behaviour2(Callbacks, Exps));
+ _ ->
+ {error, {bad_module, UserMod}}
+ end;
+verify_behaviour(_, BadModule) ->
+ {error, {bad_module, BadModule}}.
+
+verify_behaviour2([], _) ->
+ ok;
+verify_behaviour2([{Func, Arity} = FuncArity|Callbacks], Exps) ->
+ case lists:member(FuncArity, Exps) of
+ true ->
+ verify_behaviour2(Callbacks, Exps);
+ false ->
+ throw({error, {bad_module, {function, Func, Arity}}})
+ end.
+
+
+sleep(Time) ->
+ receive
+ after Time ->
+ true
+ end.
+
+
+%% Returns time in ms = sec/1000
+% now() -> now(ms).
+now(ms) ->
+ Now = erlang:now(),
+ element(1,Now)*1000000000+
+ element(2,Now)*1000+
+ (element(3,Now) div 1000);
+%% Returns time in cs = sec/100
+now(cs) ->
+ Now = erlang:now(),
+ element(1,Now)*100000000+
+ element(2,Now)*100+
+ (element(3,Now) div 10000);
+now(sec) ->
+ Now = erlang:now(),
+ element(1,Now)*1000000+
+ element(2,Now)+
+ (element(3,Now) div 1000000).
+
+
+is_string([]) -> true;
+is_string([Tkn | Str])
+ when is_integer(Tkn) andalso (Tkn >= 0) andalso (Tkn =< 255) ->
+ is_string(Str);
+is_string(_) -> false.
+
+
+is_oid([E1, E2| Rest])
+ when (length(Rest) =< 126) andalso (E1 *40 + E2 =< 255) ->
+ is_oid2(Rest);
+is_oid([E1]) when E1 =< 2 ->
+ true;
+is_oid(_) -> false.
+
+is_oid2([]) -> true;
+is_oid2([Nbr | RestOid])
+ when is_integer(Nbr) andalso (0 =< Nbr) andalso (Nbr =< 2147483647) ->
+ is_oid2(RestOid);
+is_oid2(_) -> false.
+
+is_BitString([]) -> true;
+is_BitString([Nbr | RestBitstring])
+ when is_integer(Nbr) andalso (Nbr >= 0) andalso (Nbr =< 1) ->
+ is_BitString(RestBitstring);
+is_BitString(_) -> false.
+
+
+%% Check if a Tag is a member in a TagList. Tags and TagLists are defined
+%% in SNMP-TARGET-MIB
+is_tag_member(Tag, TagList) ->
+ check_tag_list(TagList, [], lists:reverse(Tag)).
+
+check_tag_list([32 | T], Res, Gat) ->
+ tag_delimiter_found(Res, Gat, T);
+check_tag_list([9 | T], Res, Gat) ->
+ tag_delimiter_found(Res, Gat, T);
+check_tag_list([13 | T], Res, Gat) ->
+ tag_delimiter_found(Res, Gat, T);
+check_tag_list([11 | T], Res, Gat) ->
+ tag_delimiter_found(Res, Gat, T);
+check_tag_list([Char | T], Res, Gat) ->
+ check_tag_list(T, [Char | Res], Gat);
+check_tag_list([], Res, Gat) ->
+ tag_delimiter_found(Res, Gat, []).
+
+tag_delimiter_found(Gat, Gat, _T) ->
+ true;
+tag_delimiter_found(_Res, _Gat, []) ->
+ false;
+tag_delimiter_found(_Res, Gat, T) ->
+ check_tag_list(T, [], Gat).
+
+
+%% Pre: length(TAddr1) == length(TAddr2)
+%% length(TMask) == 0 | length(TAddr1)
+is_tmask_match(_TAddr1, _TAddr2, []) ->
+ true;
+is_tmask_match([H1 | T1], [H2 | T2], [M1 | M2]) ->
+ if
+ (H1 band M1) == (H2 band M1) ->
+ is_tmask_match(T1, T2, M2);
+ true ->
+ false
+ end.
+
+
+%%--------------------------------------------------
+%% Not a real assq, but what the heck, it's useful.
+%%--------------------------------------------------
+assq(Key, List) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key, Val}} -> {value, Val};
+ _ -> false
+ end.
+
+get_option(Key, Options) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} ->
+ Value;
+ _ ->
+ throw({error, {not_found, Key}})
+ end.
+
+get_option(Key, Options, Default) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} ->
+ Value;
+ _ ->
+ Default
+ end.
+
+set_option(Key, Val, Opts) ->
+ keyreplaceadd(Key, 1, Opts, {Key, Val}).
+
+keyreplaceadd(Key, Pos, List, New) ->
+ case lists:keysearch(Key, Pos, List) of
+ {value, _} -> lists:keyreplace(Key, Pos, List, New);
+ _ -> [New | List]
+ end.
+
+is_auth(SecLevel) ->
+ 1 == (SecLevel band 1).
+
+is_priv(SecLevel) ->
+ 2 == (SecLevel band 2).
+
+is_reportable([MsgFlag]) ->
+ 4 == (MsgFlag band 4).
+
+%% [OTP-3416]
+%% [RFC 2571] Confirmed Class: GetRequest-PDU, GetNextRequest-PDU,
+%% GetBulkRequest-PDU, SetRequest-PDU, and InformRequest-PDU.
+%% Unconfirmed Class: Report-PDU, Trapv2-PDU, and GetResponse-PDU.
+%% [RFC 2572] The reportableFlag MUST always be zero when the message
+%% contains a PDU from the Unconfirmed Class; it MUST always be one
+%% for a PDU from the Confirmed Class,
+%%
+is_reportable_pdu('get-request') -> true;
+is_reportable_pdu('get-next-request') -> true;
+is_reportable_pdu('get-bulk-request') -> true;
+is_reportable_pdu('set-request') -> true;
+is_reportable_pdu('inform-request') -> true;
+is_reportable_pdu(_) -> false.
+
+mk_msg_flags(PduType, SecLevel) ->
+ Flags1 = case is_reportable_pdu(PduType) of
+ true -> 4;
+ false -> 0
+ end,
+ [Flags1 bor SecLevel].
+
+get_sec_level([Flag]) ->
+ SecLevel = Flag band 3,
+ case {is_auth(SecLevel), is_priv(SecLevel)} of
+ {false, false} -> noAuthNoPriv;
+ {true, false} -> authNoPriv;
+ {true, true} -> authPriv
+ end.
+
+
+%% diff(L1, L2) -> L1 - L2.
+%% Ex. [1, 2, 3, 4] - [1, 3, 4] = [2, 3, 4]
+diff(L1, []) -> L1;
+diff([H | T1], [H | T2]) -> diff(T1, T2);
+diff(L1, _) -> L1.
+
+foreach(Function, ExtraArgs, [H | T]) ->
+ apply(Function, [H | ExtraArgs]),
+ foreach(Function, ExtraArgs, T);
+foreach(_Function, _ExtraArgs, []) -> true.
+
+str_xor([H1|T1], [H2|T2]) ->
+ [H1 bxor H2 | str_xor(T1, T2)];
+str_xor([], []) ->
+ [].
+
+
+%%-----------------------------------------------------------------
+%% Pre: ListOfLists is a list of N lists, each of length M.
+%% Func is a function of arity N.
+%% Returns: A list of length M where element Y is the result of
+%% applying Func on [Elem(Y, List1), ..., Elem(Y, ListN)].
+%%-----------------------------------------------------------------
+multi_map(_Func, [[] | _ListOfLists]) ->
+ [];
+multi_map(Func, ListOfLists) ->
+ HD = [hd(L) || L <- ListOfLists],
+ TL = [tl(L) || L <- ListOfLists],
+%% io:format("multi_map -> "
+%% "~n HD: ~p"
+%% "~n TL: ~p", [HD, TL]),
+ [
+ apply(Func, HD) | multi_map(Func, TL)
+ ].
+
+%% Primitive performance analysis.
+time(M,F,A) ->
+ statistics(runtime),
+ R = apply(M, F, A),
+ {R, statistics(runtime)}.
+
+%% How much memory is allocated for X? At least some kind of upper estimation...
+mem_size(X) ->
+ E = ets:new(tmp, [set, protected]),
+ M1 = ets:info(E, memory),
+ ets:insert(E, {make_ref(), X}),
+ M2 = ets:info(E, memory),
+ ets:delete(E),
+ M2 - M1.
+
+
+strip_extension_from_filename(FileName, Ext) when is_atom(FileName) ->
+ strip_extension_from_filename(atom_to_list(FileName), Ext);
+
+strip_extension_from_filename(FileName, Ext) when is_list(FileName) ->
+ case lists:suffix(Ext, FileName) of
+ true -> lists:sublist(FileName, 1, length(FileName) - length(Ext));
+ false -> FileName
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Returns: {ok, Mib}|{error, Reason}
+%%
+%%----------------------------------------------------------------------
+read_mib(FileName) ->
+ (catch do_read_mib(FileName)).
+
+do_read_mib(FileName) ->
+ ?read_mib(FileName).
+
+
+%%----------------------------------------------------------------------
+%% Converts a list of named bits to the integer value.
+%% Returns: integer()|error
+%%----------------------------------------------------------------------
+bits_to_int(Val,Kibbles) ->
+ bits_to_int(Val,Kibbles,0).
+
+bits_to_int([],_Kibbles,Res) -> Res;
+bits_to_int([Kibble|Ks],Kibbles,Res) ->
+ case snmp_misc:assq(Kibble,Kibbles) of
+ {value,V} ->
+ bits_to_int(Ks,Kibbles,Res + round(math:pow(2,V)));
+ _ ->
+ error
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Returns: {ok, {int(),int(),int(),int()}} | {error, Reason}
+%%----------------------------------------------------------------------
+ip(Host) ->
+ inet:getaddr(Host, inet).
+
+ensure_trailing_dir_delimiter([]) -> "/";
+ensure_trailing_dir_delimiter(DirSuggestion) ->
+ case lists:last(DirSuggestion) of
+ $/ -> DirSuggestion;
+ _ -> lists:append(DirSuggestion,"/")
+ end.
+
+
+format_pdu(PDU, MiniMib) when is_record(PDU, pdu) ->
+ #pdu{type = T,
+ error_status = ES,
+ error_index = EI,
+ request_id = RID,
+ varbinds = VBs} = PDU,
+ Txt1 = if
+ (ES =:= noError) andalso (EI =:= 0) ->
+ "";
+ (T =:= 'get-bulk-request') ->
+ "";
+ true ->
+ io_lib:format("*!*!* An error occured. *!*!* ~n"
+ "Error status = ~w, index = ~w.~n",
+ [ES, EI])
+ end,
+ Txt2 = if T =:= 'snmpv2-trap' ->
+ io_lib:format("v2 Trap, Request Id:~w~n", [RID]);
+ T =:= 'get-request' ->
+ io_lib:format("Get request, Request Id:~w~n", [RID]);
+ T =:= 'get-next-request' ->
+ io_lib:format("Get-Next request, Request Id:~w~n", [RID]);
+ T =:= 'get-bulk-request' ->
+ io_lib:format("Get-Bulk request, Request Id:~w~n"
+ " Non-repeaters = ~w~n"
+ " Max-repetitions = ~w~n", [RID, ES, EI]);
+ T =:= 'set-request' ->
+ io_lib:format("Set request, Request Id:~w~n", [RID]);
+ T =:= 'get-response' ->
+ io_lib:format("Response, Request Id:~w~n", [RID]);
+ T =:= 'inform-request' ->
+ io_lib:format("Inform Request Request Id:~w~n", [RID]);
+ T =:= report ->
+ io_lib:format("Report Request Id:~w~n", [RID]);
+ true ->
+ ""
+ end,
+ [Txt1, Txt2, format_vbs(VBs, MiniMib)|"\n"];
+
+format_pdu(#trappdu{enterprise = Enterprise,
+ agent_addr = AgentAddr,
+ generic_trap = GenericTrap,
+ specific_trap = SpecificTrap,
+ time_stamp = TimeStamp,
+ varbinds = VBs}, MiniMib) ->
+ [io_lib:format("v1 Trap~n"
+ " Generic: ~w~n"
+ " Enterprise: ~w~n"
+ " Specific: ~w~n"
+ " Agent addr: ~w~n"
+ " TimeStamp: ~w~n",
+ [GenericTrap,
+ element(1,symbolify_oid(MiniMib,Enterprise)),SpecificTrap,
+ AgentAddr, TimeStamp]),
+ format_vbs(VBs, MiniMib) | "\n"].
+
+format_vbs(Vbs, MiniMib) ->
+ [format_vb(VB, MiniMib) || VB <- Vbs].
+
+format_vb(#varbind{oid = Oid,
+ variabletype = Type,
+ value = Value}, MiniMib) ->
+ {Soid, Mtype} = symbolify_oid(MiniMib, Oid),
+ [io_lib:format(" ~w = ", [Soid]),
+ format_val(Type, Mtype, Value, MiniMib) | "\n"].
+
+format(Max, F, A) when is_integer(Max) ->
+ case lists:flatten(io_lib:format(F,A)) of
+ S when length(S) > Max ->
+ case lists:suffix("\n", S) of
+ true ->
+ lists:concat([lists:sublist(S,Max), "...\n"]);
+ false ->
+ lists:concat([lists:sublist(S,Max), "..."])
+ end;
+ S ->
+ S
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Returns: (a nested) symbolified oid.
+%%----------------------------------------------------------------------
+symbolify_oid(MiniMib, Oid) ->
+ case snmp_mini_mib:aliasname(MiniMib, Oid) of
+ false ->
+ {Oid, unknown};
+ {FoundOid, Aliasname, Type} ->
+ Rest = snmp_misc:diff(Oid, FoundOid),
+ {[Aliasname| Rest], Type}
+ end.
+
+format_val('OCTET STRING', 'BITS', Val, _MiniMib) ->
+ io_lib:format("~w", [snmp_pdus:octet_str_to_bits(Val)]);
+format_val('OBJECT IDENTIFIER', _, Val, MiniMib) ->
+ {NVal, _} = symbolify_oid(MiniMib, Val),
+ io_lib:format("~w", [NVal]);
+format_val(_, _, Val, _MiniMib) ->
+ io_lib:format("~p", [Val]).
+
+
+
+
diff --git a/lib/snmp/src/misc/snmp_note_store.erl b/lib/snmp/src/misc/snmp_note_store.erl
new file mode 100644
index 0000000000..a21a6209f1
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_note_store.erl
@@ -0,0 +1,450 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_note_store).
+
+-behaviour(gen_server).
+
+-include_lib("snmp/src/app/snmp_internal.hrl").
+-include("snmp_debug.hrl").
+-include("snmp_verbosity.hrl").
+
+%% External exports
+-export([start_link/3, stop/1,
+ get_note/2,
+ set_note/3, set_note/4,
+ info/1, verbosity/2]).
+
+%% Internal exports
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-export([timer/3]).
+
+-define(timeout, 30000). % Perform gc twice in a minute.
+
+-ifndef(default_verbosity).
+-define(default_verbosity,silence).
+-endif.
+
+-ifdef(snmp_debug).
+-define(GS_START_LINK(Args),
+ gen_server:start_link(?MODULE, Args, [{debug,[trace]}])).
+-else.
+-define(GS_START_LINK(Args),
+ gen_server:start_link(?MODULE, Args, [])).
+-endif.
+
+
+-record(state, {mod, notes, timer, timeout, active = false}).
+
+
+%%%-----------------------------------------------------------------
+%%% Implements a database for notes with a lifetime. Once in a
+%%% while, the database will be gc:ed, to get rid of old notes.
+%%% This database will not contain much data.
+%%% Options is a list of Option, where Option is
+%%% {verbosity, silence|log|debug|trace} % undocumented feature
+%%%-----------------------------------------------------------------
+start_link(Prio, Mod, Opts) ->
+ ?d("start_link -> entry with"
+ "~n Prio: ~p"
+ "~n Mod: ~p"
+ "~n Opts: ~p", [Prio, Mod, Opts]),
+ ?GS_START_LINK([Prio, Mod, Opts]).
+
+
+%%-----------------------------------------------------------------
+%% Interface functions.
+%%-----------------------------------------------------------------
+
+stop(Pid) ->
+ call(Pid, stop).
+
+get_note(Pid, Key) ->
+ call(Pid, {get_note, Key}).
+
+%% Lifetime is in 1/10 sec or infinity
+set_note(Pid, Key, Value) ->
+ set_note(Pid, infinity, Key, Value).
+set_note(Pid, Lifetime, Key, Value) ->
+ call(Pid, {set_note, Lifetime, Key, Value}).
+
+info(Pid) ->
+ call(Pid, info).
+
+verbosity(Pid, Verbosity) ->
+ cast(Pid, {verbosity, Verbosity}).
+
+
+init([Prio, Mod, Opts]) ->
+ ?d("init -> entry with"
+ "~n Prio: ~p"
+ "~n Mod: ~p"
+ "~n Opts: ~p", [Prio, Mod, Opts]),
+ case (catch do_init(Prio, Mod, Opts)) of
+ {ok, State} ->
+ {ok, State};
+ E ->
+ error_msg("failed starting note-store: ~n~p", [E]),
+ {stop, E}
+ end.
+
+do_init(Prio, Mod, Opts) ->
+ process_flag(trap_exit, true),
+ process_flag(priority, Prio),
+ put(sname, get_sname(Opts)),
+ put(verbosity, get_verbosity(Opts)),
+ put(snmp_component, get_component(Mod)),
+ ?vlog("starting",[]),
+ Notes = ets:new(snmp_note_store, [set, protected]),
+ Timeout = get_timeout(Opts),
+ State = #state{mod = Mod,
+ notes = Notes,
+ timeout = Timeout,
+ timer = start_timer(Timeout)},
+ ?vdebug("started",[]),
+ {ok, State}.
+
+
+%%-----------------------------------------------------------------
+%% A note is any internal information that has to be
+%% stored for some time (the Lifetime).
+%% A note is stored in ets as {Key, {BestBefore, Value}},
+%% where BestBefore is currentTime + Lifetime.
+%% A GC-op can destroy any notes with CurTime > BestBore.
+%% Lifetime is in centiseconds or infinity, in which case
+%% the note is eternal.
+%%-----------------------------------------------------------------
+handle_call({set_note, Lifetime, Key, Value}, _From,
+ #state{mod = Mod, notes = Notes} = State)
+ when is_integer(Lifetime) ->
+ ?vlog("set note <~p,~p> with life time ~p", [Key,Value,Lifetime]),
+ case (catch Mod:system_start_time()) of
+ SysStartTime when is_integer(SysStartTime) ->
+ ?vtrace("handle_call(set_note) -> SysStartTime: ~p",
+ [SysStartTime]),
+ Now = snmp_misc:now(cs),
+ ?vtrace("handle_call(set_note) -> Now: ~p", [Now]),
+ RealUpTime = Now - SysStartTime,
+ ?vtrace("handle_call(set_note) -> RealUpTime: ~p", [RealUpTime]),
+ BestBefore = RealUpTime + Lifetime,
+ ?vtrace("handle_call(set_note) -> BestBefore: ~p", [BestBefore]),
+ Val = ets:insert(Notes, {Key, {BestBefore, Value}}),
+ NState = activate_timer(State),
+ {reply, Val, NState};
+ _Crap ->
+ ?vinfo("handle_call(set_note) -> "
+ "failed retreiving system start time from ~w: "
+ "~n ~p", [Mod, _Crap]),
+ {reply, {error, failed_retreive_system_start_time}, State}
+ end;
+
+handle_call({set_note, infinity, Key, Value}, _From,
+ #state{notes = Notes} = State) ->
+ ?vlog("set note <~p,~p>",[Key,Value]),
+ Val = ets:insert(Notes, {Key, {infinity, Value}}),
+ ?vdebug("set note; old value: ~p",[Val]),
+ {reply, Val, State};
+
+handle_call({get_note, Key}, _From,
+ #state{mod = Mod, notes = Notes} = State) ->
+ ?vlog("get note ~p",[Key]),
+ Val = handle_get_note(Notes, Mod, Key),
+ ?vdebug("get note: ~p",[Val]),
+ {reply, Val, State};
+
+handle_call(info, _From, #state{timer = Pid, notes = Notes} = State) ->
+ ?vlog("info",[]),
+ Info = get_info(Pid, Notes),
+ {reply, Info, State};
+
+handle_call(stop, _From, State) ->
+ ?vlog("stop",[]),
+ {stop, normal, ok, State};
+
+handle_call(Req, From, State) ->
+ warning_msg("received unexpected request from ~p: ~n~p",[From, Req]),
+ {reply, {error, {unknown_request, Req}}, State}.
+
+
+handle_cast({verbosity,Verbosity}, State) ->
+ ?vlog("verbosity: ~p -> ~p",[get(verbosity),Verbosity]),
+ put(verbosity,snmp_verbosity:validate(Verbosity)),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ warning_msg("received unexpected message: ~n~p",[Msg]),
+ {noreply, State}.
+
+
+%%-----------------------------------------------------------------
+%% If there are no possible garbage left, we don't
+%% have to wait for timeout, and perform another
+%% gc, because we won't do anything. So
+%% we switch the timeout off in that case.
+%% It will be switched on as soon as we get some
+%% other message.
+%%-----------------------------------------------------------------
+handle_info(timeout, State) ->
+ ?vdebug("timeout",[]),
+ case gc(State) of
+ nothing_left ->
+ NState = deactivate_timer(State),
+ {noreply, NState};
+ work_to_do ->
+ NState = activate_timer(State),
+ {noreply, NState}
+ end;
+
+handle_info({'EXIT', Pid, Reason},
+ #state{timer = Pid, timeout = Timeout} = State) ->
+ ?vinfo("exit message from the timer process ~p for reason ~p",
+ [Pid, Reason]),
+ set_state(State#state{timer = start_timer(Timeout)});
+
+handle_info({'EXIT',Pid,Reason}, State) ->
+ ?vlog("exit message from ~p for reason ~p",[Pid,Reason]),
+ {noreply, State};
+
+handle_info(Info, State) ->
+ warning_msg("received unexpected info: ~n~p",[Info]),
+ {noreply, State}.
+
+
+set_state(S) ->
+ case gc(S) of
+ nothing_left ->
+ NState = deactivate_timer(S),
+ {noreply, NState};
+ work_to_do ->
+ NState = activate_timer(S),
+ {noreply, NState}
+ end.
+
+
+terminate(Reason, _State) ->
+ ?vdebug("terminate: ~p",[Reason]),
+ ok.
+
+
+%%----------------------------------------------------------
+%% Code change
+%%----------------------------------------------------------
+
+% downgrade
+code_change({down, _Vsn}, State, _Extra) ->
+ NState = activate_timer(deactivate_timer(State)),
+ {ok, NState};
+
+% upgrade
+code_change(_Vsn, State, _Extra) ->
+ process_flag(trap_exit, true),
+ NState = restart_timer(State),
+ {ok, NState}.
+
+
+%%----------------------------------------------------------
+%% Timer
+%%----------------------------------------------------------
+
+activate_timer(#state{timer = Pid, active = false} = State) ->
+ Pid ! activate,
+ receive
+ activated -> ok
+ end,
+ State#state{active = true};
+activate_timer(State) ->
+ State.
+
+deactivate_timer(#state{timer = Pid, active = true} = State) ->
+ Pid ! deactivate,
+ receive
+ deactivated -> ok
+ end,
+ State#state{timeout = false};
+deactivate_timer(State) ->
+ State.
+
+start_timer(Timeout) ->
+ spawn_link(?MODULE, timer, [self(), passive, Timeout]).
+
+%% Kill, restart and activate timer.
+restart_timer(#state{timer = Pid, timeout = Timeout} = State) ->
+ ?d("restart_timer -> kill current timer process ~p",[Pid]),
+ exit(Pid, kill),
+ ?d("restart_timer -> await acknowledgement",[]),
+ receive
+ {'EXIT', Pid, _Reason} ->
+ ok
+ end,
+ ?d("restart_timer -> start a new timer process",[]),
+ activate_timer(State#state{timer = start_timer(Timeout), active = false}).
+
+timer(Pid, passive, Timeout) ->
+ receive
+ deactivate ->
+ ?d("timer(passive) -> deactivate request, just send ack",[]),
+ Pid ! deactivated,
+ ?MODULE:timer(Pid, passive, Timeout);
+
+ activate ->
+ ?d("timer(deactive) -> activate request, send ack",[]),
+ Pid ! activated,
+ ?d("timer(deactive) -> activate",[]),
+ ?MODULE:timer(Pid, active, Timeout) % code replacement
+ after
+ Timeout ->
+ ?d("timer(deactive) -> timeout",[]),
+ ?MODULE:timer(Pid, passive, Timeout)
+ end;
+timer(Pid, active, Timeout) ->
+ receive
+ activate ->
+ ?d("timer(active) -> activate request, just send ack",[]),
+ Pid ! activated,
+ ?MODULE:timer(Pid, active, Timeout);
+
+ deactivate ->
+ ?d("timer(active) -> deactivate request, send ack",[]),
+ Pid ! deactivated,
+ ?d("timer(active) -> deactivate",[]),
+ ?MODULE:timer(Pid, passive, Timeout)
+ after
+ Timeout ->
+ ?d("timer(active) -> timeout",[]),
+ Pid ! timeout,
+ ?MODULE:timer(Pid, active, Timeout)
+ end.
+
+
+handle_get_note(Notes, Mod, Key) ->
+ case ets:lookup(Notes, Key) of
+ [{Key, {infinity, Val}}] ->
+ Val;
+ [{Key, {BestBefore, Val}}] ->
+ ?vtrace("get note -> BestBefore: ~w", [BestBefore]),
+ StartTime = Mod:system_start_time(),
+ ?vtrace("get note -> StartTime: ~w", [StartTime]),
+ Now = snmp_misc:now(cs),
+ ?vtrace("get note -> Now: ~w", [Now]),
+ case (Now - StartTime) of
+ Diff when BestBefore >= Diff ->
+ ?vtrace("get note -> Diff: ~w", [Diff]),
+ Val;
+ OldDiff ->
+ ?vtrace("get note -> note to old [~w] - delete", [OldDiff]),
+ ets:delete(Notes, Key),
+ undefined
+ end;
+ [] ->
+ undefined
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Clean up all old notes in the database.
+%%-----------------------------------------------------------------
+gc(#state{mod = Mod, notes = Notes}) ->
+ RealUpTime = snmp_misc:now(cs) - Mod:system_start_time(),
+ gc(nothing_left, ets:tab2list(Notes), Notes, RealUpTime).
+
+gc(Flag, [{_Key, {infinity, _}} | T], Tab, Now) -> gc(Flag, T, Tab, Now);
+gc(Flag, [{Key, {BestBefore, _}} | T], Tab, Now)
+ when is_integer(BestBefore) andalso (BestBefore < Now) ->
+ ets:delete(Tab, Key),
+ gc(Flag, T, Tab, Now);
+gc(_Flag, [_ | T], Tab, Now) -> gc(work_to_do, T, Tab, Now);
+gc(Flag, [], _Tab, _Now) -> Flag.
+
+
+%%-----------------------------------------------------------------
+
+get_info(Tmr, Notes) ->
+ ProcSize = proc_mem(self()),
+ TMRSz = proc_mem(Tmr),
+ NotesSz = tab_size(Notes),
+ [{process_memory, [{notes, ProcSize}, {timer, TMRSz}]},
+ {db_memory, [{notes, NotesSz}]}].
+
+proc_mem(P) when is_pid(P) ->
+ case (catch erlang:process_info(P, memory)) of
+ {memory, Sz} ->
+ Sz;
+ _ ->
+ undefined
+ end;
+proc_mem(_) ->
+ undefined.
+
+tab_size(T) ->
+ case (catch ets:info(T, memory)) of
+ Sz when is_integer(Sz) ->
+ Sz;
+ _ ->
+ undefined
+ end.
+
+
+%%-----------------------------------------------------------------
+
+call(Pid, Req) ->
+ call(Pid, Req, infinity).
+
+call(Pid, Req, Timeout) ->
+ gen_server:call(Pid, Req, Timeout).
+
+cast(Pid, Msg) ->
+ gen_server:cast(Pid, Msg).
+
+
+%%-----------------------------------------------------------------
+
+%% info_msg(F, A) ->
+%% ?snmp_info(get(snmp_component), "Note store server " ++ F, A).
+
+warning_msg(F, A) ->
+ ?snmp_warning(get(snmp_component), "Note store server " ++ F, A).
+
+error_msg(F, A) ->
+ ?snmp_error(get(snmp_component), "Note store server " ++ F, A).
+
+
+%%-----------------------------------------------------------------
+
+get_verbosity(Opts) ->
+ snmp_misc:get_option(verbosity, Opts, ?default_verbosity).
+
+get_sname(Opts) ->
+ snmp_misc:get_option(sname, Opts, ns).
+
+get_timeout(Opts) ->
+ snmp_misc:get_option(timeout, Opts, ?timeout).
+
+get_component(snmpm) ->
+ "manager";
+get_component(snmpa) ->
+ "agent";
+get_component(_) ->
+ "".
+
diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl
new file mode 100644
index 0000000000..6c80fc3876
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_pdus.erl
@@ -0,0 +1,770 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_pdus).
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+
+-define(VMODULE,"PDUS").
+-include("snmp_verbosity.hrl").
+
+%% See RFC1155, RFC1157, RFC1901, RFC1902, RFC1905, RFC2272
+
+
+%% API
+-export([enc_message/1, enc_message_only/1, enc_pdu/1,
+ enc_varbind/1,
+ enc_oct_str_tag/1, enc_scoped_pdu/1,
+ enc_usm_security_parameters/1,
+ dec_message/1, dec_message_only/1, dec_pdu/1,
+ dec_scoped_pdu_data/1, dec_scoped_pdu/1,
+ dec_usm_security_parameters/1,
+ strip_encrypted_scoped_pdu_data/1,
+ octet_str_to_bits/1, bits_to_str/1,
+ get_encoded_length/1]).
+
+%% Returns the number of octets required to encode Length.
+get_encoded_length(Length) ->
+ length(elength(Length)).
+
+dec_message([48 | Bytes]) ->
+ Bytes2 = get_data_bytes(Bytes),
+ case dec_snmp_version(Bytes2) of
+ {'version-3', Rest} ->
+ dec_rest_v3_msg(Rest);
+ {Vsn, Rest} -> % 1 or 2
+ dec_rest_v1_v2_msg(Vsn, Rest)
+ end.
+
+dec_message_only([48 | Bytes]) ->
+ Bytes2 = get_data_bytes(Bytes),
+ case dec_snmp_version(Bytes2) of
+ {'version-3', Rest} ->
+ dec_rest_v3_msg_only(Rest);
+ {Vsn, Rest} -> % 1 or 2
+ dec_rest_v1_v2_msg_only(Vsn, Rest)
+ end.
+
+dec_snmp_version(Bytes) ->
+ case (catch dec_int_tag(Bytes, 10)) of
+ {error, {bad_integer, BadInt}} ->
+ exit({bad_version, BadInt});
+ {SNMPversion, Rest} when is_integer(SNMPversion) andalso is_list(Rest) ->
+ {dec_snmp_ver(SNMPversion), Rest};
+ {'EXIT', Reason} ->
+ exit(Reason)
+ end.
+
+
+dec_snmp_ver(0) ->
+ 'version-1';
+dec_snmp_ver(1) ->
+ 'version-2';
+dec_snmp_ver(3) ->
+ 'version-3';
+dec_snmp_ver(Vsn) ->
+ exit({bad_version, Vsn}).
+
+dec_rest_v1_v2_msg(Vsn, Rest1) ->
+ {Community, Rest2} = dec_oct_str_tag(Rest1),
+ PDU = dec_pdu(Rest2),
+ #message{version = Vsn, vsn_hdr = Community, data = PDU}.
+
+dec_rest_v1_v2_msg_only(Vsn, Rest1) ->
+ {Community, Rest2} = dec_oct_str_tag(Rest1),
+ #message{version = Vsn, vsn_hdr = Community, data = Rest2}.
+
+dec_rest_v3_msg_only([48 | Bytes]) -> % starts with header data sequence
+ {Size, Tail} = dec_len(Bytes),
+ {HBytes, Bytes1} = split_at(Tail, Size, []),
+ %% Decode HeaderData
+ {MsgID, HBytes1} = dec_int_tag(HBytes),
+ chk_msg_id(MsgID),
+ {MsgMaxSize, HBytes2} = dec_int_tag(HBytes1),
+ chk_msg_max_size(MsgMaxSize),
+ {MsgFlags, HBytes3} = dec_oct_str_tag(HBytes2),
+ {MsgSecurityModel, []} = dec_int_tag(HBytes3),
+ chk_msg_sec_model(MsgSecurityModel),
+ %% Continue with Message
+% {MsgSecurityParameters, Bytes2} = dec_oct_str_tag(Bytes1),
+
+ [4 | Bytes1a] = Bytes1,
+ {Size1a, Tail1a} = dec_len(Bytes1a),
+ {MsgSecurityParameters, Bytes2} = split_at(Tail1a, Size1a, []),
+
+ %% [48 , HdrDataLen, HdrData, 4, MsgSecLen, MsgSec, ...]
+ %% NOTE: HdrDataLen is always so small that one octet is enough to
+ %% encode its length.
+ %% MsgSecLen is worse... but for USM, it is small enough for
+ %% one octet. USM is currently the only secmodel.
+ %% 1 + 1 + Size + 1 + 1 + Size1a
+ HdrSize = Size + Size1a + 4,
+ V3Hdr = #v3_hdr{msgID = MsgID,
+ msgMaxSize = MsgMaxSize,
+ msgFlags = MsgFlags, %dec_msg_flags(MsgFlags),
+ msgSecurityModel = MsgSecurityModel,
+ msgSecurityParameters = MsgSecurityParameters,
+ hdr_size = HdrSize},
+ #message{version = 'version-3', vsn_hdr = V3Hdr, data = Bytes2}.
+
+dec_rest_v3_msg(Bytes) ->
+ Message = dec_rest_v3_msg_only(Bytes),
+ Data = Message#message.data,
+ Message#message{data = dec_scoped_pdu_data(Data)}.
+
+dec_scoped_pdu_data([48 | Bytes]) -> % plaintext
+ {ScopedPdu, []} = dec_scoped_pdu_notag(Bytes),
+ ScopedPdu;
+dec_scoped_pdu_data([4 | Bytes]) -> % encryptedPDU
+ {EncryptedPDU, []} = dec_oct_str_notag(Bytes),
+ EncryptedPDU.
+
+
+dec_scoped_pdu([48 | Bytes]) ->
+ element(1, dec_scoped_pdu_notag(Bytes)).
+
+dec_scoped_pdu_notag(Bytes) ->
+ Bytes1 = get_data_bytes(Bytes),
+ {ContextEngineID, Bytes2} = dec_oct_str_tag(Bytes1),
+ {ContextName, Bytes3} = dec_oct_str_tag(Bytes2),
+ Pdu = dec_pdu(Bytes3),
+ {#scopedPdu{contextEngineID = ContextEngineID,
+ contextName = ContextName,
+ data = Pdu},
+ []}.
+
+dec_pdu_tag(160) ->
+ 'get-request';
+dec_pdu_tag(161) ->
+ 'get-next-request';
+dec_pdu_tag(162) ->
+ 'get-response';
+dec_pdu_tag(163) ->
+ 'set-request';
+%% 164 SNMPv1 Trap
+%% 165 Bulk
+dec_pdu_tag(166) ->
+ 'inform-request';
+dec_pdu_tag(167) ->
+ 'snmpv2-trap';
+dec_pdu_tag(168) ->
+ report.
+
+
+dec_pdu([164 | Bytes]) -> % It's a trap
+ Bytes2 = get_data_bytes(Bytes),
+ {Enterprise, Rest1} = dec_oid_tag(Bytes2),
+ {{'IpAddress', AgentAddr}, Rest2} = dec_value(Rest1),
+ {GenericTrap, Rest3} = dec_int_tag(Rest2),
+ {SpecificTrap, Rest4} = dec_int_tag(Rest3),
+ {{'TimeTicks', TimeStamp}, VBBytes} = dec_value(Rest4),
+ VBs = dec_VBs(VBBytes),
+ #trappdu{enterprise = Enterprise, agent_addr = AgentAddr,
+ generic_trap = GenericTrap, specific_trap = SpecificTrap,
+ time_stamp = TimeStamp, varbinds = VBs};
+
+dec_pdu([165 | Bytes]) -> % Bulk
+ Bytes2 = get_data_bytes(Bytes),
+ {RequestID, Rest1} = dec_int_tag(Bytes2),
+ {NonRepeaters, Rest2} = dec_int_tag(Rest1),
+ {MaxRepetitions,VBbytes} = dec_int_tag(Rest2),
+ VBs = dec_VBs(VBbytes),
+ #pdu{type = 'get-bulk-request', request_id = RequestID,
+ error_status = NonRepeaters, error_index = MaxRepetitions,
+ varbinds = VBs};
+
+dec_pdu([PduTag | Bytes]) ->
+ Type = dec_pdu_tag(PduTag),
+ Bytes2 = get_data_bytes(Bytes),
+ {RequestID, Rest1} = dec_int_tag(Bytes2),
+ {ErrStat, Rest2} = dec_int_tag(Rest1),
+ ErrStatus = case lists:keysearch(ErrStat, 2, errMsgs()) of
+ {value, {ErrStatName, _ErrStat}} ->
+ ErrStatName;
+ false ->
+ ErrStat
+ end,
+ {ErrIndex, VarbindsBytes} = dec_int_tag(Rest2),
+ VBs = dec_VBs(VarbindsBytes),
+ #pdu{type = Type, request_id = RequestID, error_status = ErrStatus,
+ error_index = ErrIndex, varbinds = VBs}.
+
+dec_VBs([48 | Bytes]) ->
+ Bytes1 = get_data_bytes(Bytes),
+ dec_individual_VBs(Bytes1, 1, []).
+
+dec_individual_VBs([], _No, VBs) ->
+ lists:reverse(VBs);
+dec_individual_VBs([48 | Bytes], OrgIndex, AccVBs) ->
+ {_SizeOfThisVB, Bytes2} = dec_len(Bytes),
+ {Oid, Rest} = dec_oid_tag(Bytes2),
+ {{Type, Value}, Rest2} = dec_value(Rest),
+ % perhaps we should check that we have eaten SizeOfThisVB bytes, but we
+ % don't consider ourselves to have time for such list traversing stuff.
+ dec_individual_VBs(Rest2, OrgIndex + 1, [#varbind{oid = Oid,
+ variabletype = Type,
+ value = Value,
+ org_index = OrgIndex}
+ | AccVBs]).
+
+dec_usm_security_parameters([48 | Bytes1]) ->
+ {_Len, Bytes2} = dec_len(Bytes1),
+ {MsgAuthEngineID, Bytes3} = dec_oct_str_tag(Bytes2),
+ {MsgAuthEngineBoots, Bytes4} = dec_int_tag(Bytes3),
+ {MsgAuthEngineTime, Bytes5} = dec_int_tag(Bytes4),
+ {MsgUserName, Bytes6} = dec_oct_str_tag(Bytes5),
+ {MsgAuthParams, Bytes7} = dec_oct_str_tag(Bytes6),
+ {MsgPrivParams, []} = dec_oct_str_tag(Bytes7),
+ #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
+ msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
+ msgAuthoritativeEngineTime = MsgAuthEngineTime,
+ msgUserName = MsgUserName,
+ msgAuthenticationParameters = MsgAuthParams,
+ msgPrivacyParameters = MsgPrivParams}.
+
+strip_encrypted_scoped_pdu_data([48 | Bytes]) ->
+ {Size, Tail} = dec_len(Bytes),
+ [48 | elength(Size)] ++ strip(Size, Tail).
+
+strip(N, [H|T]) when N > 0 -> [H | strip(N-1, T)];
+strip(0, _Tail) ->
+ [].
+
+
+%%----------------------------------------------------------------------
+%% Returns:{Type, Value}
+%%----------------------------------------------------------------------
+dec_value([6 | Bytes]) ->
+ {Value, Rest} = dec_oid_notag(Bytes),
+ {{'OBJECT IDENTIFIER', Value}, Rest};
+dec_value([5,0 | T]) ->
+ {{'NULL', 'NULL'}, T};
+dec_value([2 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ {{'INTEGER', Value}, Rest};
+dec_value([4 | Bytes]) ->
+ {Value, Rest} = dec_oct_str_notag(Bytes),
+ {{'OCTET STRING', Value}, Rest};
+dec_value([64 | Bytes]) ->
+ {Value, Rest} = dec_oct_str_notag(Bytes),
+ {{'IpAddress', Value}, Rest};
+dec_value([65 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ if Value >= 0, Value =< 4294967295 ->
+ {{'Counter32', Value}, Rest};
+ true ->
+ exit({error, {bad_counter32, Value}})
+ end;
+dec_value([66 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ if Value >= 0, Value =< 4294967295 ->
+ {{'Unsigned32', Value}, Rest};
+ true ->
+ exit({error, {bad_unsigned32, Value}})
+ end;
+dec_value([67 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ if Value >= 0, Value =< 4294967295 ->
+ {{'TimeTicks', Value}, Rest};
+ true ->
+ exit({error, {bad_timeticks, Value}})
+ end;
+dec_value([68 | Bytes]) ->
+ {Value, Rest} = dec_oct_str_notag(Bytes),
+ {{'Opaque', Value}, Rest};
+dec_value([70 | Bytes]) ->
+ {Value, Rest} = dec_integer_notag(Bytes),
+ if Value >= 0, Value =< 18446744073709551615 ->
+ {{'Counter64', Value}, Rest};
+ true ->
+ exit({error, {bad_counter64, Value}})
+ end;
+dec_value([128,0|T]) ->
+ {{'NULL', noSuchObject}, T};
+dec_value([129,0|T]) ->
+ {{'NULL', noSuchInstance}, T};
+dec_value([130,0|T]) ->
+ {{'NULL', endOfMibView}, T}.
+
+
+%%----------------------------------------------------------------------
+%% Purpose: Uses the beginning length bytes to return the actual data.
+%% If data has the wrong length, the program is exited.
+%% Pre: Tag is removed.
+%%----------------------------------------------------------------------
+get_data_bytes(Bytes) ->
+ {Size, Tail} = dec_len(Bytes),
+ if
+ length(Tail) =:= Size ->
+ Tail;
+ true ->
+ exit({error, {wrong_length, Bytes}})
+ end.
+
+split_at(L, 0, Acc) ->
+ {lists:reverse(Acc), L};
+split_at([H|T], N, Acc) ->
+ split_at(T, N-1, [H|Acc]).
+
+%%----------------------------------------------------------------------
+%% All decoding routines return: {Data, RestBytes}
+%%----------------------------------------------------------------------
+
+dec_int_tag([2 | Bytes]) ->
+ dec_integer_notag(Bytes).
+dec_int_tag([2 | Bytes], SizeLimit) ->
+ dec_integer_notag(Bytes, SizeLimit).
+
+dec_integer_notag(Ints) ->
+ dec_integer_notag(Ints, infinity).
+dec_integer_notag(Ints, SizeLimit) ->
+ case dec_len(Ints) of
+ {Size, Ints2} when SizeLimit =:= infinity ->
+ do_dec_integer_notag(Size, Ints2);
+ {Size, Ints2} when (is_integer(SizeLimit) andalso
+ (Size =< SizeLimit)) ->
+ do_dec_integer_notag(Size, Ints2);
+ {BadSize, _BadInts2} ->
+ throw({error, {bad_integer, {BadSize, SizeLimit}}})
+ end.
+
+do_dec_integer_notag(Size, Ints) ->
+ if hd(Ints) band 128 == 0 -> %% Positive number
+ dec_pos_int(Ints, Size, 8 * (Size - 1));
+ true -> %% Negative
+ dec_neg_int(Ints, Size, 8 * (Size - 1))
+ end.
+
+
+dec_pos_int(T, 0, _) -> {0, T};
+dec_pos_int([Byte|Tail], Size, Shift) ->
+ {Int, Rest} = dec_pos_int(Tail, Size - 1, Shift - 8),
+ {(Byte bsl Shift) bor Int, Rest}.
+
+dec_neg_int(T, 0, _) -> {0, T};
+dec_neg_int([Byte|Tail], Size, Shift) ->
+ {Int, Rest} = dec_pos_int(Tail, Size - 1, Shift-8),
+ {(-128 + (Byte band 127) bsl Shift) bor Int, Rest}.
+
+dec_oct_str_tag([4 | Bytes]) ->
+ dec_oct_str_notag(Bytes).
+
+dec_oct_str_notag(Bytes) ->
+ {Size, Tail} = dec_len(Bytes),
+ split_at(Tail, Size, []).
+
+dec_oid_tag([6 | Bytes]) ->
+ dec_oid_notag(Bytes).
+
+dec_oid_notag(Bytes) ->
+ {Size, [H | Tail]} = dec_len(Bytes),
+ {Oid, Rest} = dec_oid_elements(Tail, Size - 1, []),
+ {[H div 40, H rem 40 | Oid], Rest}.
+
+dec_oid_elements(L, 0, Acc) ->
+ {lists:reverse(Acc), L};
+dec_oid_elements([Dig|Tail], Size, Acc) when Dig < 128 ->
+ dec_oid_elements(Tail, Size - 1, [Dig | Acc]);
+dec_oid_elements([Dig|Tail], Size, Acc) ->
+ {Num, Neaten, Tl} = dec_oid_element(Tail,1,Dig band 127),
+ dec_oid_elements(Tl, Size - Neaten, [Num|Acc]).
+
+dec_oid_element([Dig|Tail], Neaten, Num) when Dig < 128 ->
+ {Num*128+Dig,Neaten+1,Tail};
+dec_oid_element([Dig|Tail],Neaten, Num) ->
+ dec_oid_element(Tail, Neaten+1, Num*128 + (Dig band 127)).
+
+chk_msg_id(MsgId) when (MsgId >= 0) andalso (MsgId =< 2147483647) -> ok;
+chk_msg_id(MsgId) -> exit({bad_msg_id, MsgId}).
+
+chk_msg_max_size(MMS) when (MMS >= 484) andalso (MMS =< 2147483647) -> ok;
+chk_msg_max_size(MMS) -> exit({bad_msg_max_size, MMS}).
+
+chk_msg_sec_model(MsgSecurityModel) when MsgSecurityModel >= 0,
+ MsgSecurityModel =< 2147483647 -> ok;
+chk_msg_sec_model(MsgSecurityModel) ->
+ exit({bad_msg_sec_model, MsgSecurityModel}).
+
+%%----------------------------------------------------------------------
+%% Code copied from the original ASN.1 compiler written by
+%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Returns: {Len, Tail}
+%%----------------------------------------------------------------------
+dec_len([128|_Tail]) ->
+ %% indefinite form - not allowed in SNMP
+ exit({asn1_error, indefinite_length});
+
+dec_len([Hd|Tl]) when Hd >= 0 ->
+ %% definite form
+ if
+ Hd < 128 -> % 8th bit is cleared
+ %% Short form (actually, we can remove this test, since snmp_pdus
+ %% performs this test _before_ calling this function)
+ {Hd,Tl};
+ true ->
+ %% Long form
+ No = Hd band 127, % clear 8th bit
+ {DigList, Rest} = head(No, Tl),
+ Size = dec_integer_len(DigList),
+ {Size, Rest}
+ end.
+
+dec_integer_len([D]) ->
+ D;
+dec_integer_len([A,B]) ->
+ (A bsl 8) bor B;
+dec_integer_len([A,B,C]) ->
+ (A bsl 16) bor (B bsl 8) bor C;
+%% More than 3 elements for length => either *very* long packet
+%% (which we don't handle), or the length is encoded with more octets
+%% than necessary (in which case the first octet must be 0).
+dec_integer_len([0 | T]) ->
+ dec_integer_len(T).
+
+%%-----------------------------------------------------------------
+%% head(N, List) -> {List1, List2}
+%% List == List1 ++ List2
+%% length(List1) == N
+%%-----------------------------------------------------------------
+head(L,List) ->
+ head(L,List,[]).
+
+head(0,L,Res) ->
+ {lists:reverse(Res),L};
+
+head(Int,[H|Tail],Res) ->
+ head(Int-1,Tail,[H|Res]);
+head(Int, [], _Res) ->
+ exit({asn1_error, {bad_length, Int}}).
+
+%%%----------------------------------------------------------------------
+%%% ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING ENCODING
+%%%----------------------------------------------------------------------
+
+enc_message(#message{version = Ver, vsn_hdr = VsnHdr, data = Data}) ->
+ VerBytes = enc_version(Ver),
+ Bytes =
+ case Ver of
+ 'version-3' ->
+ V3HeaderBytes = enc_v3_header(VsnHdr),
+ DataBytes = enc_scoped_pdu(Data),
+ V3HeaderBytes ++ DataBytes;
+ _ ->
+ ComBytes = enc_community(VsnHdr),
+ DataBytes = enc_pdu(Data),
+ ComBytes ++ DataBytes
+ end,
+ Bytes2 = VerBytes ++ Bytes,
+ Len = elength(length(Bytes2)),
+ [48 | Len] ++ Bytes2.
+
+enc_message_only(#message{version = Ver, vsn_hdr = VsnHdr, data = DataBytes}) ->
+ VerBytes = enc_version(Ver),
+ Bytes =
+ case Ver of
+ 'version-3' ->
+ V3HeaderBytes = enc_v3_header(VsnHdr),
+ V3HeaderBytes ++ DataBytes;
+ _ ->
+ ComBytes = enc_community(VsnHdr),
+ ComBytes ++ DataBytes
+ end,
+ Bytes2 = VerBytes ++ Bytes,
+ Len = elength(length(Bytes2)),
+ [48 | Len] ++ Bytes2.
+
+enc_version('version-1') ->
+ [2,1,0];
+enc_version('version-2') ->
+ [2,1,1];
+enc_version('version-3') ->
+ [2,1,3].
+
+enc_community(Com) ->
+ enc_oct_str_tag(Com).
+
+enc_v3_header(#v3_hdr{msgID = MsgID,
+ msgMaxSize = MsgMaxSize,
+ msgFlags = MsgFlags,
+ msgSecurityModel = MsgSecurityModel,
+ msgSecurityParameters = MsgSecurityParameters}) ->
+ Bytes = lists:append([enc_integer_tag(MsgID),
+ enc_integer_tag(MsgMaxSize),
+ enc_oct_str_tag(MsgFlags),
+ enc_integer_tag(MsgSecurityModel)]),
+ Len = elength(length(Bytes)),
+ lists:append([[48 | Len], Bytes, enc_oct_str_tag(MsgSecurityParameters)]).
+
+enc_scoped_pdu(#scopedPdu{contextEngineID = ContextEngineID,
+ contextName = ContextName,
+ data = Data}) ->
+ Bytes = lists:append([enc_oct_str_tag(ContextEngineID),
+ enc_oct_str_tag(ContextName),
+ enc_pdu(Data)]),
+ Len = elength(length(Bytes)),
+ [48 | Len] ++ Bytes.
+
+
+enc_pdu(PDU) when PDU#pdu.type =:= 'get-request' ->
+ enc_pdu(160, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'get-next-request' ->
+ enc_pdu(161, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'get-response' ->
+ enc_pdu(162, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'set-request' ->
+ enc_pdu(163, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'get-bulk-request' ->
+ enc_pdu(165, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'inform-request' ->
+ enc_pdu(166, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= 'snmpv2-trap' ->
+ enc_pdu(167, PDU);
+enc_pdu(PDU) when PDU#pdu.type =:= report ->
+ enc_pdu(168, PDU);
+enc_pdu(TrapPDU) when is_record(TrapPDU, trappdu) ->
+ enc_Trap(TrapPDU).
+
+
+enc_pdu(Tag,PDU) ->
+ Bytes2 = enc_pdu2(PDU),
+ Len2 = elength(length(Bytes2)),
+ lists:append([Tag | Len2], Bytes2).
+
+enc_pdu2(#pdu{type = Type, request_id = ReqId, error_index = ErrIndex,
+ error_status = ErrStat, varbinds = VBs}) ->
+ ReqBytes = enc_integer_tag(ReqId),
+ Val = err_val(ErrStat,Type),
+ ErrStatBytes = enc_integer_tag(Val),
+ ErrIndexBytes = enc_integer_tag(ErrIndex),
+ VBsBytes = enc_VarBindList(VBs),
+ lists:append([ReqBytes, ErrStatBytes, ErrIndexBytes, VBsBytes]).
+
+enc_usm_security_parameters(
+ #usmSecurityParameters{msgAuthoritativeEngineID = MsgAuthEngineID,
+ msgAuthoritativeEngineBoots = MsgAuthEngineBoots,
+ msgAuthoritativeEngineTime = MsgAuthEngineTime,
+ msgUserName = MsgUserName,
+ msgAuthenticationParameters = MsgAuthParams,
+ msgPrivacyParameters = MsgPrivParams}) ->
+ Bytes1 = enc_oct_str_tag(MsgAuthEngineID),
+ Bytes2 = enc_integer_tag(MsgAuthEngineBoots),
+ Bytes3 = enc_integer_tag(MsgAuthEngineTime),
+ Bytes4 = enc_oct_str_tag(MsgUserName),
+ Bytes5 = enc_oct_str_tag(MsgAuthParams),
+ Bytes6 = enc_oct_str_tag(MsgPrivParams),
+ Bytes7 = lists:append([Bytes1, Bytes2, Bytes3, Bytes4, Bytes5, Bytes6]),
+ Len = elength(length(Bytes7)),
+ [48 | Len] ++ Bytes7.
+
+err_val(Int,'get-bulk-request') when is_integer(Int) -> Int;
+err_val(ErrStat, _) ->
+ {value, {_ErrStat, Val}} = lists:keysearch(ErrStat, 1, errMsgs()),
+ Val.
+
+errMsgs() ->
+ [{noError,0},{tooBig,1},{noSuchName,2},
+ {badValue,3},{readOnly,4},{genErr,5},
+ %% v2
+ {noAccess,6},{wrongType,7},{wrongLength,8},{wrongEncoding,9},
+ {wrongValue,10},{noCreation,11},{inconsistentValue,12},
+ {resourceUnavailable,13},{commitFailed,14},{undoFailed,15},
+ {authorizationError,16},{notWritable,17},{inconsistentName,18}].
+
+enc_VarBindList(EncodedVBs) when is_integer(hd(EncodedVBs)) ->
+ Len1 = elength(length(EncodedVBs)),
+ lists:append([48 | Len1],EncodedVBs);
+enc_VarBindList(VBs) ->
+ Bytes1 = lists:append(lists:map(fun enc_varbind/1, VBs)),
+ Len1 = elength(length(Bytes1)),
+ lists:append([48 | Len1],Bytes1).
+
+enc_varbind(Varbind) ->
+ Bytes1 = enc_VarBind_attributes(Varbind),
+ Len1 = elength(length(Bytes1)),
+ lists:append([48 | Len1],Bytes1).
+
+
+enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) ->
+ OidBytes = enc_oid_tag(Oid),
+ ValueBytes = enc_value(Type, Val),
+ lists:append(OidBytes, ValueBytes).
+
+enc_value('INTEGER',Val) ->
+ enc_integer_tag(Val);
+enc_value('OCTET STRING', Val) ->
+ enc_oct_str_tag(Val);
+enc_value('BITS', Val) ->
+ enc_oct_str_tag(bits_to_str(Val));
+enc_value('OBJECT IDENTIFIER', Val) ->
+ enc_oid_tag(Val);
+enc_value('IpAddress',Val) ->
+ Bytes2 = enc_oct_str_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([64 | Len2],Bytes2);
+enc_value('Opaque', Val) ->
+ Bytes2 = enc_oct_str_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([68 | Len2],Bytes2);
+enc_value(_Type, noSuchObject) ->
+ [128,0];
+enc_value(_Type, noSuchInstance) ->
+ [129,0];
+enc_value(_Type, endOfMibView) ->
+ [130,0];
+enc_value('NULL', _Val) ->
+ [5,0];
+enc_value(Type, Val) ->
+ Bytes2 = enc_integer_notag(Val),
+ Len2 = elength(length(Bytes2)),
+ lists:append([enc_val_tag(Type,Val) | Len2],Bytes2).
+
+enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) ->
+ 65;
+enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
+ 66;
+enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) ->
+ 67;
+enc_val_tag('Counter64', Val) when ((Val >= 0) andalso
+ (Val =< 18446744073709551615)) ->
+ 70.
+
+
+%%----------------------------------------------------------------------
+%% Impl according to RFC1906, section 8
+%% For example: the number 1010 0000 (=160) 0100 0001 (=65) is represented as
+%% the octet string: 1000 0010, 0000 0101 (=[130,5])
+%%----------------------------------------------------------------------
+bits_to_str(0) -> "";
+bits_to_str(Int) ->
+ [rev_int8(Int band 255) | bits_to_str(Int div 256)].
+
+rev_int8(Val) ->
+ rev_int(Val,0,1,128).
+
+rev_int(_Val,Res,256,0) -> Res;
+rev_int(Val,Res,OldBit,NewBit) when Val band OldBit =/= 0 ->
+ rev_int(Val,Res+NewBit,OldBit*2,NewBit div 2);
+rev_int(Val,Res,OldBit,NewBit) ->
+ rev_int(Val,Res,OldBit*2,NewBit div 2).
+
+octet_str_to_bits(Str) ->
+ octet_str_to_bits(Str,1).
+
+octet_str_to_bits("",_) -> 0;
+octet_str_to_bits([Byte|Bytes],Mul) ->
+ Mul*rev_int8(Byte)+octet_str_to_bits(Bytes,Mul*256).
+
+
+enc_Trap(TrapPdu) when is_record(TrapPdu, trappdu) ->
+ Bytes1 = enc_trap_data(TrapPdu),
+ Len1 = elength(length(Bytes1)),
+ lists:append([164 | Len1],Bytes1).
+
+
+enc_trap_data(#trappdu{enterprise = Enterprise,
+ agent_addr = AgentAddr,
+ generic_trap = GenericTrap,
+ specific_trap = SpecificTrap,
+ time_stamp = TimeStamp,
+ varbinds = VBs}) ->
+ L1 = enc_oid_tag(Enterprise),
+ L2 = enc_value('IpAddress', AgentAddr),
+ L3 = enc_integer_tag(GenericTrap),
+ L4 = enc_integer_tag(SpecificTrap),
+ L5 = enc_value('TimeTicks', TimeStamp),
+ L6 = enc_VarBindList(VBs),
+ lists:append([L1,L2,L3,L4,L5,L6]).
+
+enc_oid_tag([E1,E2|RestOid]) when E1 * 40 + E2 =< 255 ->
+ Head = 40*E1 + E2, % weird
+ Res = e_object_elements(RestOid, []),
+ lists:append([6 | elength(length(Res) + 1)],[Head|Res]).
+
+e_object_elements([Num | T], Res) ->
+ e_object_elements(T, lists:append(e_object_element(Num),Res));
+
+e_object_elements([], Res) -> lists:reverse(Res).
+
+%%----------------------------------------------------------------------
+%% The reversed encoding for an oid-element
+%%----------------------------------------------------------------------
+e_object_element(Num) when Num > 0 ->
+ [Last|T] = e_object_element2(Num),
+ [Last-128|T];
+e_object_element(0) -> [0].
+
+e_object_element2(Num) when Num > 0 ->
+ Byte = (Num rem 128),
+ [128+Byte | e_object_element2((Num-Byte) div 128)];
+e_object_element2(0) -> [].
+
+enc_integer_tag(Val) when Val >= 0 -> %% stdcase positive ints
+ Bytes = eint(Val,[]),
+ [2 | elength(length(Bytes))] ++ Bytes;
+
+enc_integer_tag(Val) -> %% It's a negative number
+ Bytes = enint(Val,[]),
+ [2 | elength(length(Bytes))] ++ Bytes.
+
+enc_integer_notag(Val) when Val >= 0 -> %% stdcase positive ints
+ eint(Val,[]);
+
+enc_integer_notag(Val) -> %% It's a negative number
+ enint(Val,[]).
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+enc_oct_str_tag(OStr) when is_list(OStr) ->
+ lists:append([4|elength(length(OStr))],OStr);
+enc_oct_str_tag(OBin) ->
+ [4 | elength(size(OBin))] ++ binary_to_list(OBin).
+
+
+enc_oct_str_notag(OStr) -> OStr.
+
+%%-----------------------------------------------------------------
+%% Always use definite form
+%%-----------------------------------------------------------------
+%% Short
+elength(L) when L < 127 ->
+ [L];
+
+%% 3 cases of long form
+elength(L) when L =< 16#FF ->
+ [2#10000001,L];
+
+elength(L) when L =< 16#FFFF ->
+ [2#10000010,(L bsr 8),(L band 16#FF)];
+
+elength(L) when L =< 16#7FFFFF ->
+ [2#10000011,(L bsr 16),((L band 16#FF00) bsr 8), (L band 16#FF)].
+
+
diff --git a/lib/snmp/src/misc/snmp_usm.erl b/lib/snmp/src/misc/snmp_usm.erl
new file mode 100644
index 0000000000..6d216e65d6
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_usm.erl
@@ -0,0 +1,367 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_usm).
+
+-export([passwd2localized_key/3, localize_key/3]).
+-export([auth_in/4, auth_out/4, set_msg_auth_params/3]).
+-export([des_encrypt/3, des_decrypt/3]).
+-export([aes_encrypt/3, aes_decrypt/5]).
+
+
+-define(SNMP_USE_V3, true).
+-include("snmp_types.hrl").
+-include("SNMP-USER-BASED-SM-MIB.hrl").
+-include("SNMP-USM-AES-MIB.hrl").
+
+-define(VMODULE,"USM").
+-include("snmp_verbosity.hrl").
+
+
+%%-----------------------------------------------------------------
+
+-define(twelwe_zeros, [0,0,0,0,0,0,0,0,0,0,0,0]).
+
+-define(i32(Int), (Int bsr 24) band 255, (Int bsr 16) band 255, (Int bsr 8) band 255, Int band 255).
+
+
+%%-----------------------------------------------------------------
+%% Func: passwd2localized_key/3
+%% Types: Alg = md5 | sha
+%% Passwd = string()
+%% EngineID = string()
+%% Purpose: Generates a key that can be used as an authentication
+%% or privacy key using MD5 och SHA. The key is
+%% localized for EngineID.
+%% The algorithm is described in appendix A.1 2) of
+%% rfc2274.
+%%-----------------------------------------------------------------
+passwd2localized_key(Alg, Passwd, EngineID) when length(Passwd) > 0 ->
+ Key = mk_digest(Alg, Passwd),
+ localize_key(Alg, Key, EngineID).
+
+
+%%-----------------------------------------------------------------
+%% Func: localize_key/3
+%% Types: Alg = md5 | sha
+%% Passwd = string()
+%% EngineID = string()
+%% Purpose: Localizes an unlocalized key for EngineID. See rfc2274
+%% section 2.6 for a definition of localized keys.
+%%-----------------------------------------------------------------
+localize_key(Alg, Key, EngineID) ->
+ Str = [Key, EngineID, Key],
+ binary_to_list(crypto:Alg(Str)).
+
+
+mk_digest(md5, Passwd) ->
+ mk_md5_digest(Passwd);
+mk_digest(sha, Passwd) ->
+ mk_sha_digest(Passwd).
+
+mk_md5_digest(Passwd) ->
+ Ctx = crypto:md5_init(),
+ Ctx2 = md5_loop(0, [], Ctx, Passwd, length(Passwd)),
+ crypto:md5_final(Ctx2).
+
+md5_loop(Count, Buf, Ctx, Passwd, PasswdLen) when Count < 1048576 ->
+ {Buf64, NBuf} = mk_buf64(length(Buf), Buf, Passwd, PasswdLen),
+ NCtx = crypto:md5_update(Ctx, Buf64),
+ md5_loop(Count+64, NBuf, NCtx, Passwd, PasswdLen);
+md5_loop(_Count, _Buf, Ctx, _Passwd, _PasswdLen) ->
+ Ctx.
+
+mk_sha_digest(Passwd) ->
+ Ctx = crypto:sha_init(),
+ Ctx2 = sha_loop(0, [], Ctx, Passwd, length(Passwd)),
+ crypto:sha_final(Ctx2).
+
+sha_loop(Count, Buf, Ctx, Passwd, PasswdLen) when Count < 1048576 ->
+ {Buf64, NBuf} = mk_buf64(length(Buf), Buf, Passwd, PasswdLen),
+ NCtx = crypto:sha_update(Ctx, Buf64),
+ sha_loop(Count+64, NBuf, NCtx, Passwd, PasswdLen);
+sha_loop(_Count, _Buf, Ctx, _Passwd, _PasswdLen) ->
+ Ctx.
+
+%% Create a 64 bytes long string, by repeating Passwd as many times
+%% as necessary. Output is the 64 byte string, and the rest of the
+%% last repetition of the Passwd. This is used as input in the next
+%% invocation.
+mk_buf64(BufLen, Buf, Passwd, PasswdLen) ->
+ case BufLen + PasswdLen of
+ TotLen when TotLen > 64 ->
+ {[Buf, lists:sublist(Passwd, 64-BufLen)],
+ lists:sublist(Passwd, 65-BufLen, PasswdLen)};
+ TotLen ->
+ mk_buf64(TotLen, [Buf, Passwd], Passwd, PasswdLen)
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Auth and priv algorithms
+%%-----------------------------------------------------------------
+
+auth_in(usmHMACMD5AuthProtocol, AuthKey, AuthParams, Packet) ->
+ md5_auth_in(AuthKey, AuthParams, Packet);
+auth_in(?usmHMACMD5AuthProtocol, AuthKey, AuthParams, Packet) ->
+ md5_auth_in(AuthKey, AuthParams, Packet);
+auth_in(usmHMACSHAAuthProtocol, AuthKey, AuthParams, Packet) ->
+ sha_auth_in(AuthKey, AuthParams, Packet);
+auth_in(?usmHMACSHAAuthProtocol, AuthKey, AuthParams, Packet) ->
+ sha_auth_in(AuthKey, AuthParams, Packet).
+
+auth_out(usmNoAuthProtocol, _AuthKey, _Message, _UsmSecParams) -> % 3.1.3
+ error(unSupportedSecurityLevel);
+auth_out(?usmNoAuthProtocol, _AuthKey, _Message, _UsmSecParams) -> % 3.1.3
+ error(unSupportedSecurityLevel);
+auth_out(usmHMACMD5AuthProtocol, AuthKey, Message, UsmSecParams) ->
+ md5_auth_out(AuthKey, Message, UsmSecParams);
+auth_out(?usmHMACMD5AuthProtocol, AuthKey, Message, UsmSecParams) ->
+ md5_auth_out(AuthKey, Message, UsmSecParams);
+auth_out(usmHMACSHAAuthProtocol, AuthKey, Message, UsmSecParams) ->
+ sha_auth_out(AuthKey, Message, UsmSecParams);
+auth_out(?usmHMACSHAAuthProtocol, AuthKey, Message, UsmSecParams) ->
+ sha_auth_out(AuthKey, Message, UsmSecParams).
+
+md5_auth_out(AuthKey, Message, UsmSecParams) ->
+ %% 6.3.1.1
+ Message2 = set_msg_auth_params(Message, UsmSecParams, ?twelwe_zeros),
+ Packet = snmp_pdus:enc_message_only(Message2),
+ %% 6.3.1.2-4 is done by the crypto function
+ %% 6.3.1.4
+ MAC = binary_to_list(crypto:md5_mac_96(AuthKey, Packet)),
+ %% 6.3.1.5
+ set_msg_auth_params(Message, UsmSecParams, MAC).
+
+md5_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) == 12 ->
+ %% 6.3.2.3
+ Packet2 = patch_packet(binary_to_list(Packet)),
+ %% 6.3.2.5
+ MAC = binary_to_list(crypto:md5_mac_96(AuthKey, Packet2)),
+ %% 6.3.2.6
+%% ?vtrace("md5_auth_in -> entry with"
+%% "~n Packet2: ~w"
+%% "~n AuthKey: ~w"
+%% "~n AuthParams: ~w"
+%% "~n MAC: ~w", [Packet2, AuthKey, AuthParams, MAC]),
+ MAC == AuthParams;
+md5_auth_in(_AuthKey, _AuthParams, _Packet) ->
+ %% 6.3.2.1
+ ?vtrace("md5_auth_in -> entry with"
+ "~n _AuthKey: ~p"
+ "~n _AuthParams: ~p", [_AuthKey, _AuthParams]),
+ false.
+
+
+sha_auth_out(AuthKey, Message, UsmSecParams) ->
+ %% 7.3.1.1
+ Message2 = set_msg_auth_params(Message, UsmSecParams, ?twelwe_zeros),
+ Packet = snmp_pdus:enc_message_only(Message2),
+ %% 7.3.1.2-4 is done by the crypto function
+ %% 7.3.1.4
+ MAC = binary_to_list(crypto:sha_mac_96(AuthKey, Packet)),
+ %% 7.3.1.5
+ set_msg_auth_params(Message, UsmSecParams, MAC).
+
+sha_auth_in(AuthKey, AuthParams, Packet) when length(AuthParams) =:= 12 ->
+ %% 7.3.2.3
+ Packet2 = patch_packet(binary_to_list(Packet)),
+ %% 7.3.2.5
+ MAC = binary_to_list(crypto:sha_mac_96(AuthKey, Packet2)),
+ %% 7.3.2.6
+ MAC == AuthParams;
+sha_auth_in(_AuthKey, _AuthParams, _Packet) ->
+ %% 7.3.2.1
+ ?vtrace("sha_auth_in -> entry with"
+ "~n _AuthKey: ~p"
+ "~n _AuthParams: ~p", [_AuthKey, _AuthParams]),
+ false.
+
+
+des_encrypt(PrivKey, Data, SaltFun) ->
+ [A,B,C,D,E,F,G,H | PreIV] = PrivKey,
+ DesKey = [A,B,C,D,E,F,G,H],
+ Salt = SaltFun(),
+ IV = snmp_misc:str_xor(PreIV, Salt),
+ TailLen = (8 - (length(Data) rem 8)) rem 8,
+ Tail = mk_tail(TailLen),
+ EncData = crypto:des_cbc_encrypt(DesKey, IV, [Data,Tail]),
+ {ok, binary_to_list(EncData), Salt}.
+
+des_decrypt(PrivKey, MsgPrivParams, EncData)
+ when length(MsgPrivParams) =:= 8 ->
+ [A,B,C,D,E,F,G,H | PreIV] = PrivKey,
+ DesKey = [A,B,C,D,E,F,G,H],
+ Salt = MsgPrivParams,
+ IV = snmp_misc:str_xor(PreIV, Salt),
+ %% Whatabout errors here??? E.g. not a mulitple of 8!
+ Data = binary_to_list(crypto:des_cbc_decrypt(DesKey, IV, EncData)),
+ Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
+ {ok, Data2}.
+
+aes_encrypt(PrivKey, Data, SaltFun) ->
+ AesKey = PrivKey,
+ Salt = SaltFun(),
+ EngineBoots = snmp_framework_mib:get_engine_boots(),
+ EngineTime = snmp_framework_mib:get_engine_time(),
+ IV = [?i32(EngineBoots), ?i32(EngineTime) | Salt],
+ EncData = crypto:aes_cfb_128_encrypt(AesKey, IV, Data),
+ {ok, binary_to_list(EncData), Salt}.
+
+aes_decrypt(PrivKey, MsgPrivParams, EncData, EngineBoots, EngineTime)
+ when length(MsgPrivParams) == 8 ->
+ AesKey = PrivKey,
+ Salt = MsgPrivParams,
+ IV = [?i32(EngineBoots), ?i32(EngineTime) | Salt],
+ %% Whatabout errors here??? E.g. not a mulitple of 8!
+ Data = binary_to_list(crypto:aes_cfb_128_decrypt(AesKey, IV, EncData)),
+ Data2 = snmp_pdus:strip_encrypted_scoped_pdu_data(Data),
+ {ok, Data2}.
+
+
+%%-----------------------------------------------------------------
+%% Utility functions
+%%-----------------------------------------------------------------
+mk_tail(N) when N > 0 ->
+ [0 | mk_tail(N-1)];
+mk_tail(0) ->
+ [].
+
+set_msg_auth_params(Message, UsmSecParams, AuthParams) ->
+ NUsmSecParams =
+ UsmSecParams#usmSecurityParameters{msgAuthenticationParameters =
+ AuthParams},
+ SecBytes = snmp_pdus:enc_usm_security_parameters(NUsmSecParams),
+ VsnHdr = Message#message.vsn_hdr,
+ NVsnHdr = VsnHdr#v3_hdr{msgSecurityParameters = SecBytes},
+ Message#message{vsn_hdr = NVsnHdr}.
+
+
+%% Not very nice...
+%% This function patches the asn.1 encoded message. It changes the
+%% AuthenticationParameters to 12 zeros.
+%% NOTE: returns a deep list of bytes
+patch_packet([48 | T]) ->
+ %% Length for whole packet - 2 is tag for version
+ {Len1, [2 | T1]} = split_len(T),
+ %% Length for version - 48 is tag for header data
+ {Len2, [Vsn,48|T2]} = split_len(T1),
+ %% Length for header data
+ {Len3, T3} = split_len(T2),
+ [48,Len1,2,Len2,Vsn,48,Len3|pp2(dec_len(Len3),T3)].
+
+%% Skip HeaderData - 4 is tag for SecurityParameters
+pp2(0,[4|T]) ->
+ %% 48 is tag for UsmSecParams
+ {Len1,[48|T1]} = split_len(T),
+ %% 4 is tag for EngineID
+ {Len2,[4|T2]} = split_len(T1),
+ %% Len 3 is length for EngineID
+ {Len3,T3} = split_len(T2),
+ [4,Len1,48,Len2,4,Len3|pp3(dec_len(Len3),T3)];
+pp2(N,[H|T]) ->
+ [H|pp2(N-1,T)].
+
+%% Skip EngineID - 2 is tag for EngineBoots
+pp3(0,[2|T]) ->
+ {Len1,T1} = split_len(T),
+ [2,Len1|pp4(dec_len(Len1),T1)];
+pp3(N,[H|T]) ->
+ [H|pp3(N-1,T)].
+
+%% Skip EngineBoots - 2 is tag for EngineTime
+pp4(0,[2|T]) ->
+ {Len1,T1} = split_len(T),
+ [2,Len1|pp5(dec_len(Len1),T1)];
+pp4(N,[H|T]) ->
+ [H|pp4(N-1,T)].
+
+%% Skip EngineTime - 4 is tag for UserName
+pp5(0,[4|T]) ->
+ {Len1,T1} = split_len(T),
+ [4,Len1|pp6(dec_len(Len1),T1)];
+pp5(N,[H|T]) ->
+ [H|pp5(N-1,T)].
+
+%% Skip UserName - 4 is tag for AuthenticationParameters
+%% This is what we're looking for!
+pp6(0,[4|T]) ->
+ {Len1,[_,_,_,_,_,_,_,_,_,_,_,_|T1]} = split_len(T),
+ 12 = dec_len(Len1),
+ [4,Len1,?twelwe_zeros|T1];
+pp6(N,[H|T]) ->
+ [H|pp6(N-1,T)].
+
+
+%% Returns {LengthOctets, Rest}
+split_len([Hd|Tl]) ->
+ %% definite form
+ case is8set(Hd) of
+ 0 -> % Short form
+ {Hd,Tl};
+ 1 -> % Long form - at least one more octet
+ No = clear(Hd, 8),
+ {DigList,Rest} = head(No,Tl),
+ {[Hd | DigList], Rest}
+ end.
+
+dec_len(D) when is_integer(D) ->
+ D;
+dec_len([_LongOctet|T]) ->
+ dl(T).
+dl([D]) ->
+ D;
+dl([A,B]) ->
+ (A bsl 8) bor B;
+dl([A,B,C]) ->
+ (A bsl 16) bor (B bsl 8) bor C;
+dl([0 | T]) ->
+ dl(T).
+
+head(L,List) when length(List) == L -> {List,[]};
+head(L,List) ->
+ head(L,List,[]).
+
+head(0,L,Res) ->
+ {lists:reverse(Res),L};
+
+head(Int,[H|Tail],Res) ->
+ head(Int-1,Tail,[H|Res]).
+
+clear(Byte, 8) ->
+ Byte band 127.
+%% clear(Byte,Pos) when Pos < 9 ->
+%% Mask = bnot bset(0,Pos),
+%% Mask band Byte.
+
+%% bset(Byte, 8) ->
+%% Byte bor 2#10000000;
+%% bset(Byte, Pos) when (Pos < 9) ->
+%% Mask = 1 bsl (Pos-1),
+%% Byte bor Mask.
+
+is8set(Byte) ->
+ if
+ Byte > 127 -> 1;
+ true -> 0
+ end.
+
+error(Reason) ->
+ throw({error, Reason}).
+
diff --git a/lib/snmp/src/misc/snmp_verbosity.erl b/lib/snmp/src/misc/snmp_verbosity.erl
new file mode 100644
index 0000000000..85037ba2ae
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_verbosity.erl
@@ -0,0 +1,161 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_verbosity).
+
+-include_lib("stdlib/include/erl_compile.hrl").
+
+-export([print/4,print/5,printc/4,validate/1]).
+
+-export([process_args/2]).
+
+print(silence,_Severity,_Format,_Arguments) ->
+ ok;
+print(Verbosity,Severity,Format,Arguments) ->
+ print1(printable(Verbosity,Severity),Format,Arguments).
+
+
+print(silence,_Severity,_Module,_Format,_Arguments) ->
+ ok;
+print(Verbosity,Severity,Module,Format,Arguments) ->
+ print1(printable(Verbosity,Severity),Module,Format,Arguments).
+
+
+printc(silence,_Severity,_Format,_Arguments) ->
+ ok;
+printc(Verbosity,Severity,Format,Arguments) ->
+ print2(printable(Verbosity,Severity),Format,Arguments).
+
+
+print1(false,_Format,_Arguments) -> ok;
+print1(Verbosity,Format,Arguments) ->
+ V = image_of_verbosity(Verbosity),
+ S = image_of_sname(get(sname)),
+ A = process_args(Arguments, []),
+ (catch io:format("*** [~s] SNMP ~s ~s *** ~n"
+ " " ++ Format ++ "~n",
+ [timestamp(), S, V | A])).
+
+print1(false,_Module,_Format,_Arguments) -> ok;
+print1(Verbosity,Module,Format,Arguments) ->
+ V = image_of_verbosity(Verbosity),
+ S = image_of_sname(get(sname)),
+ A = process_args(Arguments, []),
+ (catch io:format("*** [~s] SNMP ~s ~s ~s *** ~n"
+ " " ++ Format ++ "~n",
+ [timestamp(), S, Module, V | A])).
+
+
+print2(false,_Format,_Arguments) -> ok;
+print2(_Verbosity,Format,Arguments) ->
+ A = process_args(Arguments, []),
+ (catch io:format(Format ++ "~n",A)).
+
+
+timestamp() ->
+ format_timestamp(now()).
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
+
+process_args([], Acc) ->
+ lists:reverse(Acc);
+process_args([{vapply, {M,F,A}}|T], Acc)
+ when is_atom(M) andalso is_atom(F) andalso is_list(A) ->
+ process_args(T, [(catch apply(M,F,A))|Acc]);
+process_args([H|T], Acc) ->
+ process_args(T, [H|Acc]).
+
+
+%% printable(Verbosity,Severity)
+printable(info,info) -> info;
+printable(log,info) -> info;
+printable(log,log) -> log;
+printable(debug,info) -> info;
+printable(debug,log) -> log;
+printable(debug,debug) -> debug;
+printable(trace,V) -> V;
+printable(_Verb,_Sev) -> false.
+
+
+image_of_verbosity(info) -> "INFO";
+image_of_verbosity(log) -> "LOG";
+image_of_verbosity(debug) -> "DEBUG";
+image_of_verbosity(trace) -> "TRACE";
+image_of_verbosity(_) -> "".
+
+%% ShortName
+image_of_sname(ma) -> "MASTER-AGENT";
+image_of_sname(maw) -> io_lib:format("MASTER-AGENT-worker(~p)",[self()]);
+image_of_sname(madis) -> io_lib:format("MASTER-AGENT-discovery_inform_sender(~p)",
+ [self()]);
+image_of_sname(mais) -> io_lib:format("MASTER-AGENT-inform_sender(~p)",
+ [self()]);
+image_of_sname(mats) -> io_lib:format("MASTER-AGENT-trap_sender(~p)",
+ [self()]);
+image_of_sname(maph) -> io_lib:format("MASTER-AGENT-pdu_handler(~p)",
+ [self()]);
+image_of_sname(sa) -> "SUB-AGENT";
+image_of_sname(saw) -> io_lib:format("SUB-AGENT-worker(~p)",[self()]);
+image_of_sname(sais) -> io_lib:format("SUB-AGENT-inform_sender(~p)",
+ [self()]);
+image_of_sname(sats) -> io_lib:format("SUB-AGENT-trap_sender(~p)",
+ [self()]);
+image_of_sname(saph) -> io_lib:format("SUB-AGENT-pdu_handler(~p)",
+ [self()]);
+image_of_sname(nif) -> "A-NET-IF";
+image_of_sname(ldb) -> "A-LOCAL-DB";
+image_of_sname(ns) -> "A-NOTE-STORE";
+image_of_sname(ss) -> "A-SYMBOLIC-STORE";
+image_of_sname(asup) -> "A-SUPERVISOR";
+image_of_sname(ms) -> "A-MIB-SERVER";
+image_of_sname(tcs) -> "A-TARGET-CACHE-SERVER";
+image_of_sname(conf) -> "A-CONF";
+
+image_of_sname(abs) -> "A-BKP";
+image_of_sname(albs) -> "A-LDB-BKP";
+image_of_sname(ambs) -> "A-MS-BKP";
+image_of_sname(asbs) -> "A-SS-BKP";
+image_of_sname(mcbs) -> "M-C-BKP";
+
+image_of_sname(mse) -> "M-SERVER";
+image_of_sname(msew) -> io_lib:format("M-SERVER-worker(~p)", [self()]);
+image_of_sname(mns) -> "M-NOTE-STORE";
+image_of_sname(mnif) -> "M-NET-IF";
+image_of_sname(mconf) -> "M-CONF";
+
+image_of_sname(mgr) -> "MGR";
+image_of_sname(mgr_misc) -> "MGR_MISC";
+
+image_of_sname(undefined) -> "";
+image_of_sname(V) -> lists:flatten(io_lib:format("~p",[V])).
+
+
+validate(info) -> info;
+validate(log) -> log;
+validate(debug) -> debug;
+validate(trace) -> trace;
+validate(_) -> silence.
+
diff --git a/lib/snmp/src/misc/snmp_verbosity.hrl b/lib/snmp/src/misc/snmp_verbosity.hrl
new file mode 100644
index 0000000000..934d32831f
--- /dev/null
+++ b/lib/snmp/src/misc/snmp_verbosity.hrl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-ifndef(dont_use_verbosity).
+
+-define(vapply(M,F,A),{vapply, {M,F,A}}).
+
+-ifdef(VMODULE).
+
+-define(vinfo(F,A), snmp_verbosity:print(get(verbosity),info, ?VMODULE,F,A)).
+-define(vlog(F,A), snmp_verbosity:print(get(verbosity),log, ?VMODULE,F,A)).
+-define(vdebug(F,A),snmp_verbosity:print(get(verbosity),debug,?VMODULE,F,A)).
+-define(vtrace(F,A),snmp_verbosity:print(get(verbosity),trace,?VMODULE,F,A)).
+
+-else.
+
+-define(vinfo(F,A), snmp_verbosity:print(get(verbosity),info, F,A)).
+-define(vlog(F,A), snmp_verbosity:print(get(verbosity),log, F,A)).
+-define(vdebug(F,A),snmp_verbosity:print(get(verbosity),debug,F,A)).
+-define(vtrace(F,A),snmp_verbosity:print(get(verbosity),trace,F,A)).
+
+-endif.
+
+-define(vvalidate(V), snmp_verbosity:validate(V)).
+
+-define(vinfoc(F,A), snmp_verbosity:printc(get(verbosity),info, F,A)).
+-define(vlogc(F,A), snmp_verbosity:printc(get(verbosity),log, F,A)).
+-define(vdebugc(F,A),snmp_verbosity:printc(get(verbosity),debug,F,A)).
+-define(vtracec(F,A),snmp_verbosity:printc(get(verbosity),trace,F,A)).
+
+-else.
+
+-define(vvalidate(V),ok).
+
+-define(vinfo(F,A),ok).
+-define(vlog(F,A),ok).
+-define(vdebug(F,A),ok).
+-define(vtrace(F,A),ok).
+
+-define(vinfoc(F,A),ok).
+-define(vlogc(F,A),ok).
+-define(vdebugc(F,A),ok).
+-define(vtracec(F,A),ok).
+
+-endif.
+
+
+