aboutsummaryrefslogtreecommitdiffstats
path: root/lib/snmp/src/misc/snmp_pdus.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/snmp/src/misc/snmp_pdus.erl')
-rw-r--r--lib/snmp/src/misc/snmp_pdus.erl770
1 files changed, 770 insertions, 0 deletions
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)].
+
+