diff options
Diffstat (limited to 'lib/snmp/src/agent/snmp_community_mib.erl')
-rw-r--r-- | lib/snmp/src/agent/snmp_community_mib.erl | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl new file mode 100644 index 0000000000..a2ee7bf0c9 --- /dev/null +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -0,0 +1,590 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-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_community_mib). + +-export([configure/1, reconfigure/1, + snmpCommunityTable/1, snmpCommunityTable/3, + snmpTargetAddrExtTable/3, + community2vacm/2, vacm2community/2, + get_target_addr_ext_mms/2]). +-export([add_community/5, delete_community/1]). +-export([check_community/1]). + +-include("SNMP-COMMUNITY-MIB.hrl"). +-include("SNMP-TARGET-MIB.hrl"). +-include("SNMPv2-TC.hrl"). +-include("snmp_types.hrl"). + +-define(VMODULE,"COMMUNITY-MIB"). +-include("snmp_verbosity.hrl"). + +-ifndef(default_verbosity). +-define(default_verbosity,silence). +-endif. + + +%%%----------------------------------------------------------------- +%%% Implements the instrumentation functions and additional +%%% functions for the SNMP-COMMUNITY-MIB. +%%% This MIB contains objects that makes it possible to use VACM +%%% with SNMPv1 and SNMPv2c. +%%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% Func: configure/1 +%% Args: Dir is the directory where the configuration files are found. +%% Purpose: If the tables doesn't exist, this function reads +%% the config-files for the community mib tables, and +%% inserts the data. This means that the data in the tables +%% survive a reboot. However, the StorageType column is +%% checked for each row. If volatile, the row is deleted. +%% Returns: ok +%% Fails: exit(configuration_error) +%%----------------------------------------------------------------- +configure(Dir) -> + set_sname(), + case db(snmpCommunityTable) of + {_, mnesia} -> + ?vlog("community table in mnesia: cleanup",[]), + gc_tabs(); + TabDb -> + case snmpa_local_db:table_exists(TabDb) of + true -> + ?vlog("community table exist: cleanup",[]), + gc_tabs(); + false -> + ?vlog("community table does not exist: reconfigure",[]), + reconfigure(Dir) + end + end. + +%%----------------------------------------------------------------- +%% Func: reconfigure/1 +%% Args: Dir is the directory where the configuration files are found. +%% Purpose: Reads the config-files for the community mib tables, and +%% inserts the data. Makes sure that all old data in +%% the tables are deleted, and the new data inserted. +%% This function makes sure that all (and only) +%% config-file-data are in the tables. +%% Returns: ok +%% Fails: exit(configuration_error) +%%----------------------------------------------------------------- +reconfigure(Dir) -> + set_sname(), + case (catch do_reconfigure(Dir)) of + ok -> + ok; + {error, Reason} -> + ?vinfo("reconfigure error: ~p", [Reason]), + config_err("reconfigure failed: ~p", [Reason]), + exit(configuration_error); + Error -> + ?vinfo("reconfigure failed: ~p", [Error]), + config_err("reconfigure failed: ~p", [Error]), + exit(configuration_error) + end. + +do_reconfigure(Dir) -> + ?vdebug("read community config files",[]), + Comms = read_community_config_files(Dir), + ?vdebug("initiate tables",[]), + init_tabs(Comms), + ok. + +init_tabs(Comms) -> + ?vdebug("create community table",[]), + snmpa_local_db:table_delete(db(snmpCommunityTable)), + snmpa_local_db:table_create(db(snmpCommunityTable)), + ?vdebug("invalidate cache",[]), + invalidate_cache(), + ?vdebug("initiate community table",[]), + init_comm_table(Comms). + + +read_community_config_files(Dir) -> + ?vdebug("read community config file",[]), + Gen = fun(_) -> ok end, + Filter = fun(Comms) -> Comms end, + Check = fun(Entry) -> check_community(Entry) end, + [Comms] = + snmp_conf:read_files(Dir, [{Gen, Filter, Check, "community.conf"}]), + Comms. + +check_community({Index, CommunityName, SecName, CtxName, TransportTag}) -> + snmp_conf:check_string(Index,{gt,0}), + snmp_conf:check_string(CommunityName), + snmp_conf:check_string(SecName), + snmp_conf:check_string(CtxName), + snmp_conf:check_string(TransportTag), + EngineID = get_engine_id(), + Comm = {Index, CommunityName, SecName, EngineID, CtxName, TransportTag, + ?'StorageType_nonVolatile', ?'RowStatus_active'}, + {ok, Comm}; +check_community(X) -> + error({invalid_community, X}). + +%% This is for the case when check_community is called from the +%% snmp_config module (to generate community config file) and +%% the agent is not started. The actual return value is not +%% checked, as long as it is '{ok, _}'. +get_engine_id() -> + case (catch snmp_framework_mib:get_engine_id()) of + {'EXIT', _} -> + "agentEngine"; + EngineID -> + EngineID + end. + +init_comm_table([Row | T]) -> + ?vtrace("init_comm_table -> entry with" + "~n Row: ~p", [Row]), + Key = element(1, Row), + snmpa_local_db:table_create_row(db(snmpCommunityTable), Key, Row), + update_cache(Key), + init_comm_table(T); +init_comm_table([]) -> + ?vtrace("init_comm_table -> entry when done", []), + true. + +table_cre_row(Tab, Key, Row) -> + snmpa_mib_lib:table_cre_row(db(Tab), Key, Row). + +table_del_row(Tab, Key) -> + snmpa_mib_lib:table_del_row(db(Tab), Key). + + +%% FIXME: does not work with mnesia +add_community(Idx, CommName, SecName, CtxName, TransportTag) -> + Community = {Idx, CommName, SecName, CtxName, TransportTag}, + case (catch check_community(Community)) of + {ok, Row} -> + Key = element(1, Row), + case table_cre_row(snmpCommunityTable, Key, Row) of + true -> + update_cache(Key), + {ok, Key}; + false -> + {error, create_failed} + end; + {error, Reason} -> + {error, Reason}; + Error -> + {error, Error} + end. + +%% FIXME: does not work with mnesia +delete_community(Key) -> + invalidate_cache(Key), + case table_del_row(snmpCommunityTable, Key) of + true -> + ok; + false -> + {error, delete_failed} + end. + + +gc_tabs() -> + DB = db(snmpCommunityTable), + STC = stc(snmpCommunityTable), + FOI = foi(snmpCommunityTable), + IR = fun(RowIndex) -> invalidate_cache(RowIndex) end, + UR = fun(RowIndex) -> update_cache(RowIndex) end, + snmpa_mib_lib:gc_tab(DB, STC, FOI, IR, UR), + ok. + + +%%----------------------------------------------------------------- +%% API functions +%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% We keep two caches for mapping; +%% one that maps CommunityName to CommunityIndex, and +%% one that maps +%% {SecName, ContextEngineId, ContextName} to {CommunityName, Tag} +%% Name -> Index instead of Name -> Vacm allows us to save a +%% few bytes of memory, although it introduces an extra level of +%% indirection. +%%----------------------------------------------------------------- +community2vacm(Community, Addr) -> + Idxs = ets:lookup(snmp_community_cache, Community), + loop_c2v_rows(lists:keysort(2, Idxs), Addr). + +loop_c2v_rows([{_, CommunityIndex} | T], Addr) -> + ?vtrace("loop_c2v_rows -> entry with" + "~n CommunityIndex: ~p", [CommunityIndex]), + case get_row(CommunityIndex) of + {_Community, VacmParams, Tag} -> + {TDomain, TAddr} = Addr, + case snmp_target_mib:is_valid_tag(Tag, TDomain, TAddr) of + true -> + ?vdebug("loop_c2v_rows -> " + "~p valid tag for community index ~p", + [Tag, CommunityIndex]), + VacmParams; + false -> + ?vtrace("loop_c2v_rows -> " + "~p not valid tag community index ~p", + [Tag, CommunityIndex]), + loop_c2v_rows(T, Addr) + end; + undefined -> + loop_c2v_rows(T, Addr) + end; +loop_c2v_rows([], _Addr) -> + undefined. + + +%%----------------------------------------------------------------- +%% Func: vacm2community(Vacm, {TDomain, TAddr}) -> +%% {ok, Community} | undefined +%% Types: Vacm = {SecName, ContextEngineId, ContextName} +%% Purpose: Follows the steps in RFC 2576 section +%% 5.2.3 in order to find a community string to be used +%% in a notification. +%%----------------------------------------------------------------- +vacm2community(Vacm, Addr) -> + Names = ets:lookup(snmp_community_cache, Vacm), + loop_v2c_rows(lists:keysort(2, Names), Addr). + +loop_v2c_rows([{_, {CommunityName, Tag}} | T], Addr) -> + ?vtrace("loop_v2c_rows -> entry with" + "~n CommunityName: ~p" + "~n Tag: ~p", [CommunityName, Tag]), + {TDomain, TAddr} = Addr, + case snmp_target_mib:is_valid_tag(Tag, TDomain, TAddr) of + true -> + ?vdebug("loop_v2c_rows -> " + "~p valid tag for community name ~p", + [Tag, CommunityName]), + {ok, CommunityName}; + false -> + loop_v2c_rows(T, Addr) + end; +loop_v2c_rows([], _Addr) -> + undefined. + + + +get_row(RowIndex) -> + case snmp_generic:table_get_row(db(snmpCommunityTable), RowIndex, + foi(snmpCommunityTable)) of + {_, CommunityName, SecName, ContextEngineId, ContextName, + TransportTag, _StorageType, ?'RowStatus_active'} -> + {CommunityName, {SecName, ContextEngineId, ContextName}, + TransportTag}; + _ -> + undefined + end. + +invalidate_cache(RowIndex) -> + case get_row(RowIndex) of + {CommunityName, VacmParams, TransportTag} -> + ets:match_delete(snmp_community_cache, + {CommunityName, RowIndex}), + ets:match_delete(snmp_community_cache, + {VacmParams, {CommunityName, TransportTag}}); + undefined -> + ok + end. + +update_cache(RowIndex) -> + case get_row(RowIndex) of + {CommunityName, VacmParams, TransportTag} -> + ets:insert(snmp_community_cache, + {CommunityName, RowIndex}), % CommunityIndex + ets:insert(snmp_community_cache, + {VacmParams, {CommunityName, TransportTag}}); + undefined -> + ok + end. + +invalidate_cache() -> + ets:match_delete(snmp_community_cache, {'_', '_'}). + + +get_target_addr_ext_mms(TDomain, TAddress) -> + get_target_addr_ext_mms(TDomain, TAddress, []). +get_target_addr_ext_mms(TDomain, TAddress, Key) -> + case snmp_target_mib:table_next(snmpTargetAddrTable, Key) of + endOfTable -> + false; + NextKey -> + case snmp_target_mib:get( + snmpTargetAddrTable, NextKey, [?snmpTargetAddrTDomain, + ?snmpTargetAddrTAddress, + 12]) of + [{value, TDomain}, {value, TAddress}, {value, MMS}] -> + {ok, MMS}; + _ -> + get_target_addr_ext_mms(TDomain, TAddress, NextKey) + end + end. +%%----------------------------------------------------------------- +%% Instrumentation Functions +%%----------------------------------------------------------------- +%% Op = print - Used for debugging purposes +snmpCommunityTable(print) -> + Table = snmpCommunityTable, + DB = db(Table), + FOI = foi(Table), + PrintRow = + fun(Prefix, Row) -> + lists:flatten( + io_lib:format("~sIndex: ~p" + "~n~sName: ~p" + "~n~sSecurityName: ~p" + "~n~sContextEngineID: ~p" + "~n~sContextName: ~p" + "~n~sTransportTag: ~p" + "~n~sStorageType: ~p (~w)" + "~n~sStatus: ~p (~w)", + [Prefix, element(?snmpCommunityIndex, Row), + Prefix, element(?snmpCommunityName, Row), + Prefix, element(?snmpCommunitySecurityName, Row), + Prefix, element(?snmpCommunityContextEngineID, Row), + Prefix, element(?snmpCommunityContextName, Row), + Prefix, element(?snmpCommunityTransportTag, Row), + Prefix, element(?snmpCommunityStorageType, Row), + case element(?snmpCommunityStorageType, Row) of + ?'snmpCommunityStorageType_readOnly' -> readOnly; + ?'snmpCommunityStorageType_permanent' -> permanent; + ?'snmpCommunityStorageType_nonVolatile' -> nonVolatile; + ?'snmpCommunityStorageType_volatile' -> volatile; + ?'snmpCommunityStorageType_other' -> other; + _ -> undefined + end, + Prefix, element(?snmpCommunityStatus, Row), + case element(?snmpCommunityStatus, Row) of + ?'snmpCommunityStatus_destroy' -> destroy; + ?'snmpCommunityStatus_createAndWait' -> createAndWait; + ?'snmpCommunityStatus_createAndGo' -> createAndGo; + ?'snmpCommunityStatus_notReady' -> notReady; + ?'snmpCommunityStatus_notInService' -> notInService; + ?'snmpCommunityStatus_active' -> active; + _ -> undefined + end])) + end, + snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow); +%% Op == new | delete +snmpCommunityTable(Op) -> + snmp_generic:table_func(Op, db(snmpCommunityTable)). + +%% Op == get | is_set_ok | set | get_next +snmpCommunityTable(get, RowIndex, Cols) -> + get(snmpCommunityTable, RowIndex, Cols); +snmpCommunityTable(get_next, RowIndex, Cols) -> + next(snmpCommunityTable, RowIndex, Cols); +snmpCommunityTable(is_set_ok, RowIndex, Cols0) -> + case (catch verify_snmpCommunityTable_is_set_ok(Cols0)) of + {ok, Cols1} -> + case (catch verify_snmpCommunityTable_cols(Cols1, [])) of + {ok, Cols} -> + Db = db(snmpCommunityTable), + snmp_generic:table_func(is_set_ok, RowIndex, Cols, Db); + Error -> + Error + end; + Error -> + Error + end; +snmpCommunityTable(set, RowIndex, Cols0) -> + case (catch verify_snmpCommunityTable_cols(Cols0, [])) of + {ok, Cols} -> + invalidate_cache(RowIndex), + Db = db(snmpCommunityTable), + Res = snmp_generic:table_func(set, RowIndex, Cols, Db), + snmpa_agent:invalidate_ca_cache(), + update_cache(RowIndex), + Res; + Error -> + Error + end; +snmpCommunityTable(Op, Arg1, Arg2) -> + snmp_generic:table_func(Op, Arg1, Arg2, db(snmpCommunityTable)). + + +verify_snmpCommunityTable_is_set_ok(Cols) -> + LocalEngineID = snmp_framework_mib:get_engine_id(), + case lists:keysearch(?snmpCommunityContextEngineID, 1, Cols) of + {value, {_, LocalEngineID}} -> + {ok, Cols}; + {value, _} -> + {inconsistentValue, ?snmpCommunityContextEngineID}; + false -> + {ok, kinsert(Cols, LocalEngineID)} + end. + +verify_snmpCommunityTable_cols([], Cols) -> + {ok, lists:reverse(Cols)}; +verify_snmpCommunityTable_cols([{Col, Val0}|Cols], Acc) -> + Val = verify_snmpCommunityTable_col(Col, Val0), + verify_snmpCommunityTable_cols(Cols, [{Col, Val}|Acc]). + +verify_snmpCommunityTable_col(?snmpCommunityIndex, Index) -> + case (catch snmp_conf:check_string(Index,{gt,0})) of + ok -> + Index; + _ -> + wrongValue(?snmpCommunityIndex) + end; +verify_snmpCommunityTable_col(?snmpCommunityName, Name) -> + case (catch snmp_conf:check_string(Name)) of + ok -> + Name; + _ -> + wrongValue(?snmpCommunityName) + end; +verify_snmpCommunityTable_col(?snmpCommunitySecurityName, Name) -> + case (catch snmp_conf:check_string(Name)) of + ok -> + Name; + _ -> + wrongValue(?snmpCommunitySecurityName) + end; +verify_snmpCommunityTable_col(?snmpCommunityContextName, Name) -> + case (catch snmp_conf:check_string(Name)) of + ok -> + Name; + _ -> + wrongValue(?snmpCommunityContextName) + end; +verify_snmpCommunityTable_col(?snmpCommunityTransportTag, Tag) -> + case (catch snmp_conf:check_string(Tag)) of + ok -> + Tag; + _ -> + wrongValue(?snmpCommunityTransportTag) + end; +verify_snmpCommunityTable_col(_, Val) -> + Val. + + + +%% Op == get | is_set_ok | set | get_next +snmpTargetAddrExtTable(get, RowIndex, Cols) -> + NCols = conv1(Cols), + get(snmpTargetAddrExtTable, RowIndex, NCols); +snmpTargetAddrExtTable(get_next, RowIndex, Cols) -> + NCols = conv1(Cols), + conv2(next(snmpTargetAddrExtTable, RowIndex, NCols)); +snmpTargetAddrExtTable(set, RowIndex, Cols0) -> + case (catch verify_snmpTargetAddrExtTable_cols(Cols0, [])) of + {ok, Cols} -> + NCols = conv3(Cols), + snmp_generic:table_func(set, RowIndex, NCols, + db(snmpTargetAddrExtTable)); + Error -> + Error + end; +snmpTargetAddrExtTable(is_set_ok, RowIndex, Cols0) -> + case (catch verify_snmpTargetAddrExtTable_cols(Cols0, [])) of + {ok, Cols} -> + NCols = conv3(Cols), + snmp_generic:table_func(is_set_ok, RowIndex, NCols, + db(snmpTargetAddrExtTable)); + Error -> + Error + end. + + +verify_snmpTargetAddrExtTable_cols([], Cols) -> + {ok, lists:reverse(Cols)}; +verify_snmpTargetAddrExtTable_cols([{Col, Val0}|Cols], Acc) -> + Val = verify_snmpTargetAddrExtTable_col(Col, Val0), + verify_snmpTargetAddrExtTable_cols(Cols, [{Col, Val}|Acc]). + +verify_snmpTargetAddrExtTable_col(?snmpTargetAddrTMask, []) -> + []; +verify_snmpTargetAddrExtTable_col(?snmpTargetAddrTMask, TMask) -> + case (catch snmp_conf:check_taddress(TMask)) of + ok -> + TMask; + _ -> + wrongValue(?snmpTargetAddrTMask) + end; +verify_snmpTargetAddrExtTable_col(?snmpTargetAddrMMS, MMS) -> + case (catch snmp_conf:check_packet_size(MMS)) of + ok -> + MMS; + _ -> + wrongValue(?snmpTargetAddrMMS) + end; +verify_snmpTargetAddrExtTable_col(_, Val) -> + Val. + +db(snmpTargetAddrExtTable) -> db(snmpTargetAddrTable); +db(X) -> snmpa_agent:db(X). + +fa(snmpCommunityTable) -> ?snmpCommunityName; +fa(snmpTargetAddrExtTable) -> 11. + +foi(snmpCommunityTable) -> ?snmpCommunityIndex; +foi(snmpTargetAddrExtTable) -> 11. + +noc(snmpCommunityTable) -> 8; +noc(snmpTargetAddrExtTable) -> 12. + +stc(snmpCommunityTable) -> ?snmpCommunityStorageType. + +next(Name, RowIndex, Cols) -> + snmp_generic:handle_table_next(db(Name), RowIndex, Cols, + fa(Name), foi(Name), noc(Name)). + +conv1([Col | T]) -> [Col + 10 | conv1(T)]; +conv1([]) -> []. + + +conv2([{[Col | Oid], Val} | T]) -> + [{[Col - 10 | Oid], Val} | conv2(T)]; +conv2([X | T]) -> + [X | conv2(T)]; +conv2(X) -> X. + + +conv3([{Idx, Val}|T]) -> [{Idx+10, Val} | conv3(T)]; +conv3([]) -> []. + + +get(Name, RowIndex, Cols) -> + snmp_generic:handle_table_get(db(Name), RowIndex, Cols, foi(Name)). + +kinsert([H | T], EngineID) when element(1, H) < ?snmpCommunityContextEngineID -> + [H | kinsert(T, EngineID)]; +kinsert(Cols, EngineID) -> + [{?snmpCommunityContextEngineID, EngineID} | Cols]. + + +wrongValue(V) -> throw({wrongValue, V}). + + +%% ----- + +set_sname() -> + set_sname(get(sname)). + +set_sname(undefined) -> + put(sname,conf); +set_sname(_) -> %% Keep it, if already set. + ok. + + +error(Reason) -> + throw({error, Reason}). + +config_err(F, A) -> + snmpa_error:config_err("[COMMUNITY-MIB]: " ++ F, A). |