aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/agent/tmp/snmpa_get_lib.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/agent/tmp/snmpa_get_lib.erl')
-rw-r--r--lib/snmp/src/agent/tmp/snmpa_get_lib.erl507
1 files changed, 507 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/tmp/snmpa_get_lib.erl b/lib/snmp/src/agent/tmp/snmpa_get_lib.erl
new file mode 100644
index 0000000000..2bd7e4a1d2
--- /dev/null
+++ b/lib/snmp/src/agent/tmp/snmpa_get_lib.erl
@@ -0,0 +1,507 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmpa_get_lib).
+
+-export([
+ try_get/2,
+ try_get_instance/1,
+
+ delete_prefixes/2,
+ delete_index/1,
+
+ sort_vbs/1,
+ split_gb_vbs/2,
+ split_vbs/1, split_vbs/3,
+ split_vbs_view/2,
+
+ make_value_a_correct_value/3,
+
+ dbg_apply/3,
+
+ user_err/2
+ ]).
+
+
+
+%% *** sort_vbs ***
+
+-spec sort_vbs(VBs :: [snmp:varbind()]) -> [snmp:ivarbind()].
+
+sort_vbs(VBs) ->
+ snmpa_svbl:sort_varbindlist(get(mibserver), VBs).
+
+
+%% *** split_vbs ***
+
+-spec split_gb_vbs(NonRepeaters :: integer(),
+ VBs :: [varbind()]) ->
+ {NonRepVBs :: [varbind()], RestVBs :: [varbind()]}.
+
+split_gb_vbs(N, VBs) ->
+ split_gb_vbs(N, VBs, []).
+
+split_gb_vbs(N, Varbinds, Res) when N =< 0 ->
+ {Res, Varbinds};
+split_gb_vbs(N, [H | T], Res) ->
+ split_gb_vbs(N-1, T, [H | Res]);
+split_gb_vbs(_N, [], Res) ->
+ {Res, []}.
+
+
+
+%% *** split_vbs_view ***
+
+-spec split_vbs_view(VBs :: [varbind()],
+ MibView :: snmp_view_based_acm_mib:mibview()) ->
+ {OutSideView :: [varbind()],
+ InSideView :: [varbind()]}.
+
+split_vbs_view(VBs, MibView) ->
+ ?vtrace("split the varbinds view", []),
+ split_vbs_view(VBs, MibView, [], []).
+
+split_vbs_view([], _MibView, Out, In) ->
+ {Out, In};
+split_vbs_view([Vb | Vbs], MibView, Out, In) ->
+ case snmpa_acm:validate_mib_view(Vb#varbind.oid, MibView) of
+ true -> split_vbs_view(Vbs, MibView, Out, [Vb | In]);
+ false -> split_vbs_view(Vbs, MibView,
+ [Vb#varbind{value = noSuchObject} | Out], In)
+ end.
+
+
+
+%% *** split_vbs/1,3 ***
+%%
+%% Sort out the 'end of' things (mib view of tables).
+%%
+
+-spec split_vbs(VBs :: [varbind()]) ->
+ {ok, Res :: [varbind()], EndOfs :: [varbond()]}.
+
+split_vbs(VBs) ->
+ split_vbs(VBs, [], []).
+
+-spec split_vbs(VBs :: [varbind()],
+ Res :: [varbind()],
+ EndOfs :: [varbond()]) ->
+ {ok, Res :: [varbind()], EndOfs :: [varbond()]}.
+
+split_vbs([], Res, EndOfs) ->
+ {ok, Res, EndOfs};
+split_vbs([Vb | Vbs], Res, EndOfs) ->
+ case Vb#varbind.value of
+ {endOfMibView, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]);
+ {endOfTable, _} -> split_varbinds(Vbs, Res, [Vb | EndOfs]);
+ _ -> split_varbinds(Vbs, [Vb | Res], EndOfs)
+ end.
+
+
+next_oid(Oid) ->
+ case lists:reverse(Oid) of
+ [H | T] -> lists:reverse([H+1 | T]);
+ [] -> []
+ end.
+
+
+
+%% *** delete_prefixes ***
+
+-spec delete_prefixes(Prefix :: oid(),
+ IVBs :: [ivarbind()]) ->
+ [{ShortOID :: snmp:oid(),
+ ASN1Type :: snmp:asn1_type()}].
+
+delete_prefixes(Prefix, IVBs) ->
+ [{snmp_misc:diff(Oid, Prefix), ME#me.asn1_type} ||
+ #ivarbind{varbind = #varbind{oid = Oid}, mibentry = ME} <- IVBs].
+
+
+
+%% *** delete_index ***
+
+-spec delete_index(TableOids :: [{Col :: integer(),
+ Val :: term(),
+ OrgIndex :: integer()}]) ->
+ [Col :: integer()].
+
+delete_index(TableOids) ->
+ [Col || {Col, _Val, _OrgIndex} <- TableOids].
+
+
+
+%%-----------------------------------------------------------------
+%% transforms a (hopefully correct) return value ((perhaps) from a
+%% mib-function) to a typed and guaranteed correct return value.
+%% An incorrect return value is transformed to {error, genErr}.
+%% A correct return value is on the form:
+%% {error, <error-msg>} | {value, <variable-type>, <value>}
+%%-----------------------------------------------------------------
+
+-spec make_value_a_correct_value(Value :: term(),
+ ASN1 :: snmp:asn1_type(),
+ MFA :: term()) ->
+ {error, Error :: atom()} |
+ {value, ValueType :: atom(),
+ Value :: term()}.
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when Asn1#asn1_type.bertype =:= 'INTEGER' ->
+ check_integer(Val, Asn1, Mfa);
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when Asn1#asn1_type.bertype =:= 'Counter32' ->
+ check_integer(Val, Asn1, Mfa);
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when Asn1#asn1_type.bertype =:= 'Unsigned32' ->
+ check_integer(Val, Asn1, Mfa);
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when Asn1#asn1_type.bertype =:= 'TimeTicks' ->
+ check_integer(Val, Asn1, Mfa);
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when Asn1#asn1_type.bertype =:= 'Counter64' ->
+ check_integer(Val, Asn1, Mfa);
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when (Asn1#asn1_type.bertype =:= 'BITS') andalso is_list(Val) ->
+ {value,Kibbles} = snmp_misc:assq(kibbles,Asn1#asn1_type.assocList),
+ case snmp_misc:bits_to_int(Val,Kibbles) of
+ error ->
+ wrongValue(Val, Mfa);
+ Int ->
+ make_value_a_correct_value({value,Int},Asn1,Mfa)
+ end;
+
+make_value_a_correct_value({value, Val}, Asn1, Mfa)
+ when (Asn1#asn1_type.bertype =:= 'BITS') andalso is_integer(Val) ->
+ {value,Kibbles} = snmp_misc:assq(kibbles,Asn1#asn1_type.assocList),
+ {_Kibble,BitNo} = lists:last(Kibbles),
+ case (1 bsl (BitNo+1)) of
+ X when Val < X ->
+ {value,'BITS',Val};
+ _Big ->
+ wrongValue(Val, Mfa)
+ end;
+
+make_value_a_correct_value({value, String},
+ #asn1_type{bertype = 'OCTET STRING',
+ hi = Hi, lo = Lo}, Mfa) ->
+ check_octet_string(String, Hi, Lo, Mfa, 'OCTET STRING');
+
+make_value_a_correct_value({value, String},
+ #asn1_type{bertype = 'IpAddress',
+ hi = Hi, lo = Lo}, Mfa) ->
+ check_octet_string(String, Hi, Lo, Mfa, 'IpAddress');
+
+make_value_a_correct_value({value, Oid},
+ #asn1_type{bertype = 'OBJECT IDENTIFIER'},
+ _Mfa) ->
+ case snmp_misc:is_oid(Oid) of
+ true -> {value, 'OBJECT IDENTIFIER', Oid};
+ _Else -> {error, wrongType}
+ end;
+
+make_value_a_correct_value({value, Val}, Asn1, _Mfa)
+ when Asn1#asn1_type.bertype =:= 'Opaque' ->
+ if is_list(Val) -> {value, 'Opaque', Val};
+ true -> {error, wrongType}
+ end;
+
+make_value_a_correct_value({noValue, noSuchObject}, _ASN1Type, _Mfa) ->
+ {value, noValue, noSuchObject};
+make_value_a_correct_value({noValue, noSuchInstance}, _ASN1Type, _Mfa) ->
+ {value, noValue, noSuchInstance};
+make_value_a_correct_value({noValue, noSuchName}, _ASN1Type, _Mfa) ->
+ %% Transform this into a v2 value. It is converted to noSuchName
+ %% later if it was v1. If it was v2, we use noSuchInstance.
+ {value, noValue, noSuchInstance};
+%% For backwards compatibility only - we really shouldn't allow this;
+%% it makes no sense to return unSpecified for a variable! But we did
+%% allow it previously. -- We transform unSpecified to noSuchInstance
+%% (OTP-3303).
+make_value_a_correct_value({noValue, unSpecified}, _ASN1Type, _Mfa) ->
+ {value, noValue, noSuchInstance};
+make_value_a_correct_value(genErr, _ASN1Type, _MFA) ->
+ {error, genErr};
+
+make_value_a_correct_value(_WrongVal, _ASN1Type, undef) ->
+ {error, genErr};
+
+make_value_a_correct_value(WrongVal, ASN1Type, Mfa) ->
+ user_err("Got ~w from ~w. (~w) Using genErr",
+ [WrongVal, Mfa, ASN1Type]),
+ {error, genErr}.
+
+check_integer(Val, Asn1, Mfa) ->
+ case Asn1#asn1_type.assocList of
+ undefined -> check_size(Val, Asn1, Mfa);
+ Alist ->
+ case snmp_misc:assq(enums, Alist) of
+ {value, Enums} -> check_enums(Val, Asn1, Enums, Mfa);
+ false -> check_size(Val, Asn1, Mfa)
+ end
+ end.
+
+check_octet_string(String, Hi, Lo, Mfa, Type) ->
+ Len = (catch length(String)), % it might not be a list
+ case snmp_misc:is_string(String) of
+ true when Lo =:= undefined -> {value, Type, String};
+ true when Len =< Hi, Len >= Lo ->
+ {value, Type, String};
+ true ->
+ wrongLength(String, Mfa);
+ _Else ->
+ wrongType(String, Mfa)
+ end.
+
+check_size(Val, #asn1_type{lo = Lo, hi = Hi, bertype = Type}, Mfa)
+ when is_integer(Val) ->
+ ?vtrace("check size of integer: "
+ "~n Value: ~p"
+ "~n Upper limit: ~p"
+ "~n Lower limit: ~p"
+ "~n BER-type: ~p",
+ [Val,Hi,Lo,Type]),
+ if
+ (Lo =:= undefined) andalso (Hi =:= undefined) -> {value, Type, Val};
+ (Lo =:= undefined) andalso is_integer(Hi) andalso (Val =< Hi) ->
+ {value, Type, Val};
+ is_integer(Lo) andalso (Val >= Lo) andalso (Hi =:= undefined) ->
+ {value, Type, Val};
+ is_integer(Lo) andalso is_integer(Hi) andalso (Val >= Lo) andalso (Val =< Hi) ->
+ {value, Type, Val};
+ true ->
+ wrongValue(Val, Mfa)
+ end;
+check_size(Val, _, Mfa) ->
+ wrongType(Val, Mfa).
+
+check_enums(Val, Asn1, Enums, Mfa) ->
+ Association =
+ if
+ is_integer(Val) -> lists:keysearch(Val, 2, Enums);
+ is_atom(Val) -> lists:keysearch(Val, 1, Enums);
+ true -> {error, wrongType}
+ end,
+ case Association of
+ {value, {_AliasIntName, Val2}} ->
+ {value, Asn1#asn1_type.bertype, Val2};
+ false ->
+ wrongValue(Val, Mfa);
+ {error, wrongType} ->
+ wrongType(Val, Mfa)
+ end.
+
+wrongLength(Val, Mfa) ->
+ report_err(Val, Mfa, wrongLength).
+
+wrongValue(Val, Mfa) ->
+ report_err(Val, Mfa, wrongValue).
+
+wrongType(Val, Mfa) ->
+ report_err(Val, Mfa, wrongType).
+
+report_err(_Val, undef, Err) ->
+ {error, Err};
+report_err(Val, Mfa, Err) ->
+ user_err("Got ~p from ~w. Using ~w", [Val, Mfa, Err]),
+ {error, Err}.
+
+
+
+
+is_valid_pdu_type('get-request') -> true;
+is_valid_pdu_type('get-next-request') -> true;
+is_valid_pdu_type('get-bulk-request') -> true;
+is_valid_pdu_type('set-request') -> true;
+is_valid_pdu_type(_) -> false.
+
+
+
+%%-----------------------------------------------------------------
+%% Func: try_get/2
+%% Returns: {error, ErrorStatus, OrgIndex} |
+%% #varbind |
+%% List of #varbind
+%%-----------------------------------------------------------------
+
+-spec try_get(IVB :: ivarbind(),
+ IsNotification :: boolean()) ->
+ {error, ErrorStatus, OrgIndex} | varbind() | [varbind()].
+
+try_get(IVb, IsNotification) when is_record(IVb, ivarbind) ->
+ ?vtrace("try_get(ivarbind) -> entry with"
+ "~n IVb: ~p", [IVb]),
+ get_var_value_from_ivb(IVb, IsNotification);
+try_get({TableOid, TableVbs}, IsNotification) ->
+ ?vtrace("try_get(table) -> entry with"
+ "~n TableOid: ~p"
+ "~n TableVbs: ~p", [TableOid, TableVbs]),
+ [#ivarbind{mibentry = MibEntry}|_] = TableVbs,
+ {NoAccessVbs, AccessVbs} =
+ check_all_table_vbs(TableVbs, IsNotification, [], []),
+ case get_tab_value_from_mib(MibEntry, TableOid, AccessVbs) of
+ {error, ErrorStatus, OrgIndex} ->
+ {error, ErrorStatus, OrgIndex};
+ NVbs ->
+ NVbs ++ NoAccessVbs
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Func: try_get_instance/1
+%% Returns: {value, noValue, term()} |
+%% {value, Type, Value} |
+%% {error, ErrorStatus}
+%%-----------------------------------------------------------------
+
+-spec try_get_instance(ME :: snmp:me()) ->
+ {value, noValue, term()} |
+ {value, Type :: asn1_type(), Value :: term()} |
+ {error, error_status()}.
+
+try_get_instance(#me{mfa = {M, F, A}, asn1_type = ASN1Type}) ->
+ ?vtrace("try_get_instance -> entry with"
+ "~n M: ~p"
+ "~n F: ~p"
+ "~n A: ~p", [M,F,A]),
+ Result = (catch dbg_apply(M, F, [get | A])),
+ % mib shall return {value, <a-nice-value-within-range>} |
+ % {noValue, noSuchName} (v1) |
+ % {noValue, noSuchObject | noSuchInstance} (v2, v1)
+ % everything else (including 'genErr') will generate 'genErr'.
+ make_value_a_correct_value(Result, ASN1Type, {M, F, A}).
+
+
+
+%%-----------------------------------------------------------------
+%% Returns: {error, ErrorStatus, OrgIndex} |
+%% #varbind
+%%-----------------------------------------------------------------
+
+get_var_value_from_ivb(#ivarbind{status = noError,
+ mibentry = ME,
+ varbind = VB} = IVb, IsNotification) ->
+ ?vtrace("get_var_value_from_ivb(noError) -> entry", []),
+ #varbind{org_index = OrgIndex, oid = Oid} = Vb,
+ case ME#me.access of
+ 'not-accessible' ->
+ Vb#varbind{value = noSuchInstance};
+ 'accessible-for-notify' when (IsNotification =:= false) ->
+ Vb#varbind{value = noSuchInstance};
+ 'write-only' ->
+ Vb#varbind{value = noSuchInstance};
+ _ ->
+ case get_var_value_from_mib(Me, Oid) of
+ {value, Type, Value} ->
+ Vb#varbind{variabletype = Type, value = Value};
+ {error, ErrorStatus} ->
+ {error, ErrorStatus, OrgIndex}
+ end
+ end;
+get_var_value_from_ivb(#ivarbind{status = Status, varbind = VB}, _) ->
+ ?vtrace("get_var_value_from_ivb(~p) -> entry", [Status]),
+ VB#varbind{value = Status}.
+
+
+
+%%-----------------------------------------------------------------
+%% Func: get_var_value_from_mib/1
+%% Purpose:
+%% Pre: Oid is a correct instance Oid (lookup checked that).
+%% Returns: {error, ErrorStatus} |
+%% {value, Type, Value}
+%% Returns: A correct return value (see make_value_a_correct_value)
+%%-----------------------------------------------------------------
+get_var_value_from_mib(#me{entrytype = variable,
+ asn1_type = ASN1Type,
+ mfa = {Mod, Func, Args}},
+ _Oid) ->
+ ?vtrace("get_var_value_from_mib(variable) -> entry when"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p", [Mod, Func, Args]),
+ Result = (catch dbg_apply(Mod, Func, [get | Args])),
+ % mib shall return {value, <a-nice-value-within-range>} |
+ % {noValue, noSuchName} (v1) |
+ % {noValue, noSuchObject | noSuchInstance} (v2, v1)
+ % everything else (including 'genErr') will generate 'genErr'.
+ make_value_a_correct_value(Result, ASN1Type, {Mod, Func, Args});
+
+get_var_value_from_mib(#me{entrytype = table_column,
+ oid = MeOid,
+ asn1_type = ASN1Type,
+ mfa = {Mod, Func, Args}},
+ Oid) ->
+ ?vtrace("get_var_value_from_mib(table_column) -> entry when"
+ "~n MeOid: ~p"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Args: ~p"
+ "~n Oid: ~p", [MeOid, Mod, Func, Args, Oid]),
+ Col = lists:last(MeOid),
+ Indexes = snmp_misc:diff(Oid, MeOid),
+ [Result] = (catch dbg_apply(Mod, Func, [get, Indexes, [Col] | Args])),
+ make_value_a_correct_value(Result, ASN1Type,
+ {Mod, Func, Args, Indexes, Col}).
+
+
+
+%%-----------------------------------------------------------------
+%% Runtime debugging of the agent.
+%%-----------------------------------------------------------------
+
+-spec dbg_apply(M :: atom(), F :: atom(), A :: list()) ->
+ any().
+
+dbg_apply(M, F, A) ->
+ case get(verbosity) of
+ silence ->
+ apply(M,F,A);
+ _ ->
+ ?vlog("~n apply: ~w,~w,~p~n", [M,F,A]),
+ Res = (catch apply(M,F,A)),
+ case Res of
+ {'EXIT', Reason} ->
+ ?vinfo("Call to: "
+ "~n Module: ~p"
+ "~n Function: ~p"
+ "~n Args: ~p"
+ "~n"
+ "~nresulted in an exit"
+ "~n"
+ "~n ~p", [M, F, A, Reason]);
+ _ ->
+ ?vlog("~n returned: ~p", [Res])
+ end,
+ Res
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+user_err(F, A) ->
+ snmpa_error:user_err(F, A).
+
+