1.说明
1.1 介绍
这一篇是继两篇文章:
之后的第三篇文章。主要是做基本的代码分析。
1.2 资源回顾
写这篇文章的时候,发现之前写的2篇文章,大致内容已经有人写过了:
深入理解Net-SNMP
,https://github.com/chansonZ/Understanding-the-Net-SNMP.,不过缺点是讲的内容都在应用,没有具体去分析代码。
2.SNMP
代码分析基本流程
2.1 基于代码版本
当前基于snmpd
版本5.10
,基于linux
平台做分析:
$ sudo snmpd -v
NET-SNMP version: 5.10
Web: http://www.net-snmp.org/
Email: net-snmp-coders@lists.sourceforge.net
2.2 基本大框架主要流程
分析:agent\snmpd.c
,基本的大的程序流程如下:
main()
---> netsnmp_close_fds(2); //关闭大于2的所有描述符
---> snmp_log_syslogname(app_name); //设置syslog名字为snmpd
---> netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,NETSNMP_DS_LIB_APPTYPE, app_name);//设置为snmpd
---> snmpd_parse_options(&opts, argc, argv)//解析参数
---> snmp_enable_filelog()
---> init_agent(app_name)
---> init_mib_modules()
---> init_snmp(app_name)
---> init_master_agent()
---> netsnmp_daemonize()
---> snmp_store(app_name)
---> send_easy_trap(0, 0)
---> netsnmp_addrcache_initialise()
---> receive()
---> SnmpTrapNodeDown()
其中,init_agent()
流程如下:
int init_agent(const char *app)
---> netsnmp_set_agent_starttime(NULL); //设置gettimeofday(&starttime, NULL);和netsnmp_get_monotonic_clock(&starttimeM);
---> setup_tree();
---> init_agent_read_config(app);
---> register_app_config_handler()
---> register_app_config_handler()
---> register_app_config_handler()
---> ...
---> netsnmp_ds_register_config()
---> netsnmp_ds_register_config()
---> netsnmp_ds_register_config()
---> ...
---> netsnmp_init_handler_conf()
---> snmpd_register_config_handler()
---> snmp_register_callback()
---> se_add_pair_to_slist()
---> _init_agent_callback_transport()
---> netsnmp_init_helpers()
---> netsnmp_init_debug_helper()
---> netsnmp_init_serialize()
---> netsnmp_init_read_only_helper()
---> netsnmp_init_bulk_to_next_helper()
---> netsnmp_init_table_dataset()
---> register_app_config_handler"table", netsnmp_config_parse_table_set, NULL,"tableoid");
---> register_app_config_handler("add_row", netsnmp_config_parse_add_row, NULL, "table_name indexes... values...");
---> netsnmp_init_row_merge()
---> netsnmp_init_stash_cache_helper()
---> init_traps()
---> netsnmp_container_init_list()
---> init_agent_sysORTable()
---> agentx_config_init()
---> netsnmp_register_default_domain("agentx", "unix tcp");
---> netsnmp_register_default_target("agentx", "unix", NETSNMP_AGENTX_SOCKET); //"/var/agentx/master"
---> netsnmp_register_default_target("agentx", "tcp", "localhost:" val(AGENTX_PORT)); // 705
---> netsnmp_udp_agent_config_tokens_register()
---> netsnmp_udp6_agent_config_tokens_register()
---> netsnmp_unix_agent_config_tokens_register()
---> init_perl()
---> netsnmp_certs_agent_init()
这里面有一个函数setup_tree()
,调用关系如下:
void setup_tree(void)
---> oid ccitt[1] = { 0 };
---> oid iso[1] = { 1 };
---> oid joint_ccitt_iso[1] = { 2 };
---> netsnmp_register_null(snmp_duplicate_objid(ccitt, 1), 1);
---> netsnmp_register_null_context(loc, loc_len, NULL);
---> reginfo = SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration);
---> reginfo->handlerName = strdup("");
---> reginfo->rootoid = loc;
---> reginfo->rootoid_len = loc_len;
---> reginfo->modes = HANDLER_CAN_DEFAULT | HANDLER_CAN_GETBULK;
---> netsnmp_register_handler(reginfo);
---> netsnmp_register_mib( ... )
---> ...
---> netsnmp_subtree_replace_first()
---> add_subtree(new_tree, context_name);
---> ...
---> netsnmp_register_null(snmp_duplicate_objid(iso, 1), 1);
---> netsnmp_register_null(snmp_duplicate_objid(joint_ccitt_iso, 1), 1);
完成添加根节点的subtree
.
函数init_mib_modules()
流程如下:
void init_mib_modules(void)
---> # include "mib_module_inits.h"
---> snmp_register_callback( SNMP_CALLBACK_LIBRARY,
SNMP_CALLBACK_SHUTDOWN,
_shutdown_mib_modules,
NULL);
这里面使用了include
代码的方式,实际调用的内容为:
/* This file is automatically generated by configure. Do not modify by hand. */
if (should_init("snmpEngine")) init_snmpEngine();
if (should_init("snmpMPDStats")) init_snmpMPDStats();
if (should_init("usmStats")) init_usmStats();
if (should_init("usmUser")) init_usmUser();
if (should_init("system_mib")) init_system_mib();
if (should_init("sysORTable")) init_sysORTable();
if (should_init("snmp_mib")) init_snmp_mib();
if (should_init("vacm_vars")) init_vacm_vars();
if (should_init("setSerialNo")) init_setSerialNo();
if (should_init("at")) init_at();
if (should_init("ip")) init_ip();
if (should_init("tcp")) init_tcp();
if (should_init("udp")) init_udp();
if (should_init("ipv6")) init_ipv6();
if (should_init("icmp")) init_icmp();
if (should_init("proc")) init_proc();
if (should_init("versioninfo")) init_versioninfo();
if (should_init("pass")) init_pass();
if (should_init("pass_persist")) init_pass_persist();
if (should_init("disk_hw")) init_disk_hw();
if (should_init("loadave")) init_loadave();
if (should_init("extend")) init_extend();
if (should_init("errormib")) init_errormib();
if (should_init("file")) init_file();
if (should_init("dlmod")) init_dlmod();
if (should_init("proxy")) init_proxy();
if (should_init("logmatch")) init_logmatch();
if (should_init("memory")) init_memory();
if (should_init("vmstat")) init_vmstat();
if (should_init("snmpNotifyTable")) init_snmpNotifyTable();
if (should_init("snmpNotifyFilterProfileTable")) init_snmpNotifyFilterProfileTable();
if (should_init("notification_log")) init_notification_log();
if (should_init("snmpTargetAddrEntry")) init_snmpTargetAddrEntry();
if (should_init("snmpTargetParamsEntry")) init_snmpTargetParamsEntry();
if (should_init("target_counters")) init_target_counters();
if (should_init("nsTransactionTable")) init_nsTransactionTable();
if (should_init("nsModuleTable")) init_nsModuleTable();
if (should_init("nsDebug")) init_nsDebug();
if (should_init("nsCache")) init_nsCache();
if (should_init("nsLogging")) init_nsLogging();
if (should_init("nsVacmAccessTable")) init_nsVacmAccessTable();
if (should_init("mteScalars")) init_mteScalars();
if (should_init("mteTrigger")) init_mteTrigger();
if (should_init("mteTriggerTable")) init_mteTriggerTable();
if (should_init("mteTriggerDeltaTable")) init_mteTriggerDeltaTable();
if (should_init("mteTriggerExistenceTable")) init_mteTriggerExistenceTable();
if (should_init("mteTriggerBooleanTable")) init_mteTriggerBooleanTable();
if (should_init("mteTriggerThresholdTable")) init_mteTriggerThresholdTable();
if (should_init("mteTriggerConf")) init_mteTriggerConf();
if (should_init("mteEvent")) init_mteEvent();
if (should_init("mteEventTable")) init_mteEventTable();
if (should_init("mteEventSetTable")) init_mteEventSetTable();
if (should_init("mteEventNotificationTable")) init_mteEventNotificationTable();
if (should_init("mteEventConf")) init_mteEventConf();
if (should_init("mteObjects")) init_mteObjects();
if (should_init("mteObjectsTable")) init_mteObjectsTable();
if (should_init("mteObjectsConf")) init_mteObjectsConf();
if (should_init("schedCore")) init_schedCore();
if (should_init("schedConf")) init_schedConf();
if (should_init("schedTable")) init_schedTable();
if (should_init("override")) init_override();
if (should_init("hr_system")) init_hr_system();
if (should_init("hr_device")) init_hr_device();
if (should_init("hr_other")) init_hr_other();
if (should_init("hr_proc")) init_hr_proc();
if (should_init("hr_network")) init_hr_network();
if (should_init("hr_print")) init_hr_print();
if (should_init("hr_disk")) init_hr_disk();
if (should_init("hr_partition")) init_hr_partition();
if (should_init("hrh_storage")) init_hrh_storage();
if (should_init("hrh_filesys")) init_hrh_filesys();
if (should_init("hrSWInstalledTable")) init_hrSWInstalledTable();
if (should_init("hrSWRunTable")) init_hrSWRunTable();
if (should_init("vacm_context")) init_vacm_context();
if (should_init("var_route")) init_var_route();
if (should_init("tcpTable")) init_tcpTable();
if (should_init("udpTable")) init_udpTable();
if (should_init("ip_scalars")) init_ip_scalars();
if (should_init("snmpNotifyTable_data")) init_snmpNotifyTable_data();
if (should_init("snmpNotifyFilterTable_data_storage")) init_snmpNotifyFilterTable_data_storage();
if (should_init("snmpNotifyFilterTable")) init_snmpNotifyFilterTable();
if (should_init("snmpNotifyFilterProfileTable_data")) init_snmpNotifyFilterProfileTable_data();
if (should_init("snmpTargetAddrEntry_data")) init_snmpTargetAddrEntry_data();
if (should_init("snmpTargetParamsEntry_data")) init_snmpTargetParamsEntry_data();
if (should_init("swinst")) init_swinst();
if (should_init("swrun")) init_swrun();
if (should_init("hrSWRunPerfTable")) init_hrSWRunPerfTable();
if (should_init("ifTable")) init_ifTable();
if (should_init("ifXTable")) init_ifXTable();
if (should_init("ipAddressTable")) init_ipAddressTable();
if (should_init("ipAddressPrefixTable")) init_ipAddressPrefixTable();
if (should_init("ipDefaultRouterTable")) init_ipDefaultRouterTable();
if (should_init("inetNetToMediaTable")) init_inetNetToMediaTable();
if (should_init("ipSystemStatsTable")) init_ipSystemStatsTable();
if (should_init("ipv6ScopeZoneIndexTable")) init_ipv6ScopeZoneIndexTable();
if (should_init("ipIfStatsTable")) init_ipIfStatsTable();
if (should_init("ipCidrRouteTable")) init_ipCidrRouteTable();
if (should_init("inetCidrRouteTable")) init_inetCidrRouteTable();
if (should_init("tcpConnectionTable")) init_tcpConnectionTable();
if (should_init("tcpListenerTable")) init_tcpListenerTable();
if (should_init("udpEndpointTable")) init_udpEndpointTable();
if (should_init("hw_fsys")) init_hw_fsys();
if (should_init("hw_mem")) init_hw_mem();
if (should_init("cpu")) init_cpu();
if (should_init("cpu_linux")) init_cpu_linux();
if (should_init("interface")) init_interface();
这些函数内容init_xx()
,其实在之前的2篇文章中,可以参考它们。这些代码里面也会包含mib
文件,可以参考并对照代码学习。
函数init_snmp()
调用关系如下:
void init_snmp(const char *type)
---> _init_snmp();
---> netsnmp_init_mib_internals();
---> netsnmp_tdomain_init();
---> gettimeofday(&tv, (struct timezone *) 0);
---> netsnmp_register_default_domain("snmp", "udp udp6");
---> netsnmp_register_default_domain("snmptrap", "udp udp6");
---> netsnmp_register_default_target("snmp", "udp", ":161");
---> netsnmp_register_default_target("snmp", "tcp", ":161");
---> netsnmp_register_default_target("snmp", "udp6", ":161");
---> netsnmp_register_default_target("snmp", "tcp6", ":161");
---> netsnmp_register_default_target("snmptrap", "udp", ":162");
---> ...
---> setlocale(LC_CTYPE, "");
---> snmp_debug_init();
---> netsnmp_container_init_list();
---> init_callbacks()
---> init_snmp_logging();
---> snmp_init_statistics();
---> register_mib_handlers();
---> register_prenetsnmp_mib_handler("snmp", "mibdirs", ...)
---> register_prenetsnmp_mib_handler("snmp", "mibs", ...)
---> register_config_handler("snmp", "mibfile", handle_mibfile_conf, NULL, "mibfile-to-read");
---> register_default_handlers();
---> init_snmp_transport();
---> init_snmpv3(type);
---> init_snmp_alarm();
---> init_snmp_enum(type);
---> init_vacm();
---> netsnmp_certs_init()
---> read_premib_configs();
---> read_config_files(PREMIB_CONFIG);
---> read_configs_optional(optional_config, PREMIB_CONFIG);
---> netsnmp_config_process_memories_when(PREMIB_CONFIG, 0);
---> netsnmp_init_mib();
---> netsnmp_init_mib_internals();
---> netsnmp_fixup_mib_directory();
---> add_mibdir(entry);
---> add_mibfile(entry, NULL);
---> read_all_mibs();
---> read_mib(entry);
---> netsnmp_read_module(entry);
---> read_configs();
---> read_config_files(NORMAL_CONFIG);
---> free_config();
---> read_config_files_of_type(when, ctmp)
---> read_configs_optional(optional_config, NORMAL_CONFIG);
---> netsnmp_config_process_memories_when(NORMAL_CONFIG, 1);
```'
函数`netsnmp_init_mib_internals()`定义如下:
```c
static void _init_snmp(void)
---> void netsnmp_init_mib_internals(void)
---> build_translation_table();
---> static void init_tree_roots(void)
---> tp = calloc(1, sizeof(struct tree));
---> lasttp = tp;
---> tp = calloc(1, sizeof(struct tree));
---> tp->next_peer = lasttp;
---> lasttp = tp;
---> tp = calloc(1, sizeof(struct tree));
---> tp->next_peer = lasttp;
---> tree_head = tp;
其中,tree_head()
被snmp_add_var()
调用:
int
snmp_add_var(netsnmp_pdu *pdu,
const oid * name, size_t name_length, char type, const char *value)
---> tp = get_tree(name, name_length, get_tree_head());
---> for (; subtree; subtree = subtree->next_peer) {
---> if (*objid == subtree->subid)
}
函数:handle_mibfile_conf()
如下:
static void handle_mibfile_conf(const char *token, char *line)
---> read_mib(line);
---> new_module(token, filename);
---> netsnmp_read_module(token);
---> status = read_module_internal(name);
---> netsnmp_init_mib_internals();
---> module_map_head = module_map; //重要的mib对应关系名
---> build_translation_table();
---> case OBJID:
---> case OCTETSTR:
---> case INTEGER:
---> case NETADDR:
---> case IPADDR:
---> case COUNTER:
---> case GAUGE:
---> case TIMETICKS:
---> case INTEGER32:
---> ...
---> init_tree_roots(); /* Set up initial roots */
---> base_modid = which_module("SNMPv2-SMI");
---> base_modid = which_module("RFC1155-SMI");
---> base_modid = which_module("RFC1213-MIB");
---> tp->label = strdup("joint-iso-ccitt"); //joint-iso-ccitt .2
---> tp->subid = 2;
---> tp->label = strdup("ccitt"); //ccitt .0
---> tp->subid = 0;
---> tp->label = strdup("iso"); //iso .1
---> tp->subid = 1;
这里就是前面的mib
文件中定义的几个根节点的OID
:
ccitt .0
iso .1
joint-iso-ccitt .2
函数:init_master_agent()
如下:
int init_master_agent(void)
---> netsnmp_set_lookup_cache_size(-1);
---> netsnmp_agent_listen_on(cptr)
---> transport = netsnmp_transport_open_server("snmp", port);
---> handle = netsnmp_register_agent_nsap(transport);
---> real_init_master();
---> snmp_sess_init(&sess);
---> _init_snmp();
---> netsnmp_unix_create_path_with_mode(agentx_dir_perm);
---> netsnmp_unix_dont_create_path();
---> _pdu_stats_init();
2.3 一个scalar
例子的流程
按照之前文章中写到的scalar
例子中,大致的代码流程为:
scalar:
init_WITYUANModule()
const oid wityuanNode_oid[] = { 1,3,6,1,4,1,60000,1,1 };
netsnmp_handler_registration reg;
reg = netsnmp_create_handler_registration("wityuanNode", handle_wityuanNode,wityuanNode_oid, OID_LENGTH(wityuanNode_oid), HANDLER_CAN_RWRITE)
---> netsnmp_mib_handler *handler = netsnmp_create_handler(name, handler_access_method);
---> netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler);
---> ret->access_method = handler_access_method;
---> ret->handler_name = strdup(name);
---> netsnmp_handler_registration_create(name, handler, reg_oid, reg_oid_len, modes);
---> netsnmp_handler_registration *the_reg;
---> the_reg->handler = handler;
---> the_reg->handlerName = strdup(name);
---> the_reg->rootoid = snmp_duplicate_objid(reg_oid, reg_oid_len);
netsnmp_register_scalar(reg)
---> tmp = (oid*)realloc(reginfo->rootoid,
(reginfo->rootoid_len+1) * sizeof(oid) );
---> reginfo->rootoid = tmp;
---> reginfo->rootoid[ reginfo->rootoid_len ] = 0;
---> h1 = netsnmp_get_instance_handler();
---> netsnmp_create_handler("instance",netsnmp_instance_helper_handler);
---> netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler);
---> ret->access_method = handler_access_method;
---> ret->handler_name = strdup(name);
---> h2 = netsnmp_get_scalar_handler();
---> netsnmp_create_handler("scalar",netsnmp_scalar_helper_handler);
---> netsnmp_mib_handler *ret = SNMP_MALLOC_TYPEDEF(netsnmp_mib_handler);
---> ret->access_method = handler_access_method;
---> ret->handler_name = strdup(name);
---> netsnmp_inject_handler(reginfo, h1)
handler2->next = reginfo->handler; //h1->next=reginfo->handler;
---> netsnmp_inject_handler(reginfo, h2)
handler2->next = reginfo->handler; //h2->next=reginfo->handler;
---> netsnmp_register_serialize(reginfo);
---> netsnmp_mib_handler *handler = netsnmp_get_serialize_handler();
---> netsnmp_register_handler(reginfo);
---> netsnmp_register_mib(reginfo->handlerName,....)
大致流图如下所示:
可以留意到3
行代码:
tmp = (oid*)realloc(reginfo->rootoid,
(reginfo->rootoid_len+1) * sizeof(oid) );
reginfo->rootoid = tmp;
reginfo->rootoid[ reginfo->rootoid_len ] = 0;
因此,对于scalar
,总体而言访问的话,其最后应该添加一个.0
.
2.4 一个table
例子的流程,TBD...
流程类似。
3.snmp oid
构建方法,TBD...
后面有机会再来补充吧…
4.snmp parser
,TBD...
后面有机会再来补充吧…
5.snmp over ssh
snmp over ssh
可以参考如下2篇文章:
6.snmptable
的实现原理
snmptable
是net-snmp
提供的一个工具,调用关系如下:
int main(int argc, char *argv[])
---> netsnmp_set_line_buffering(stdout);
---> snmp_parse_oid(argv[optind], root, &rootlen)
---> get_field_names();
---> reverse_fields();
---> ss = snmp_open(&session);
---> if (use_getbulk) getbulk_table_entries(ss);
---> pdu = snmp_pdu_create(SNMP_MSG_GETBULK); //使用bulk方式获取table数据
---> else get_table_entries(ss);
---> pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); //使用getnext方式获取table数据
因此,snmptable
使用的是getbulk
或者getnext
方式获取table
数据。