1背景
在上一篇snmp v1 get请求响应c++实现里简单实现了一个队oid为system.sysNam的响应,但是实际上不能这么使用,不然就一直是Demo。 在这一篇,计划去借鉴net-snmp的源码,对之前的进行改造优化。
2 net-snmp 源码
github 地址可以从官网的首页找到 www.net-snmp.org/
或者直接访问 github.com/net-snmp/ne…
前面 snmp wireshark 抓包 下载的二进制是5.5.0的,这里用tag为5.9.4的源码版本(5.5的基本是2011年,还是用个最新的吧)
tag所在地址 github.com/net-snmp/ne…
3 分析net-snmp源码
3.1 目录整理
agent ---服务器
apps ---客户端
3.2 从main到初始化agent
以下所有的都是在agent目录 main函数 (snmpd.c)
//snmpd.c
int __cdecl
main(int argc, TCHAR * argv[])
{
LPCTSTR lpszServiceName = app_name_long; /* Service Registry Name */
LPCTSTR lpszServiceDisplayName = _T("Net-SNMP Agent"); /* Display Name */
LPCTSTR lpszServiceDescription =
InputParams InputOptions;
enum net_snmp_cmd_line_action nRunType = RUN_AS_CONSOLE;
int quiet = 0;
nRunType = ParseCmdLineForServiceOption(argc, argv, &quiet);
switch (nRunType) {
case REGISTER_SERVICE:
/*
* Register As service
*/
InputOptions.Argc = argc;
InputOptions.Argv = argv;
return RegisterService(lpszServiceName,
lpszServiceDisplayName,
lpszServiceDescription, &InputOptions, quiet);
case UN_REGISTER_SERVICE:
/*
* Unregister service
*/
return UnregisterService(lpszServiceName, quiet);
case RUN_AS_SERVICE:
/*
* Run as service
*/
/*
* Register Stop Function
*/
RegisterStopFunction(StopSnmpAgent);
return RunAsService(SnmpDaemonMain);
default:
/*
* Run in console mode
*/
return SnmpDaemonMain(argc, argv);
}
}
上面的过滤掉其他用不到的,默认执行的是RUN_AS_CONSOLE,最终执行的就一个 SnmpDaemonMain。继续往下找
#ifdef WIN32SERVICE
static int
SnmpDaemonMain(int argc, TCHAR * argv[])
#else
int
main(int argc, char *argv[])
#endif
{
if (init_agent(app_name) != 0) {
snmp_log(LOG_ERR, "Agent initialization failed\n");
goto out;
}
init_mib_modules();
/*
* start library
*/
init_snmp(app_name);
if ((ret = init_master_agent()) != 0) {
/*
* Some error opening one of the specified agent transports.
*/
snmp_log(LOG_ERR, "Server Exiting with code 1\n");
goto out;
}
}
上面的处理了解析命令行等等,重点关注 init_master_agent 其他的不在关注的范围内
3.3 绑定udp端口
//snmp_agent.c
int
init_master_agent(void)
{
char *cptr;
char *buf = NULL;
char *st;
/* default to a default cache size */
netsnmp_set_lookup_cache_size(-1);
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
DEBUGMSGTL(("snmp_agent",
"init_master_agent; not master agent\n"));
netsnmp_assert("agent role !master && !sub_agent");
return 0; /* No error if ! MASTER_AGENT */
}
#ifndef NETSNMP_NO_LISTEN_SUPPORT
/*
* Have specific agent ports been specified?
*/
cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_PORTS);
if (cptr) {
buf = strdup(cptr);
if (!buf) {
snmp_log(LOG_ERR,
"Error processing transport \"%s\"\n", cptr);
return 1;
}
} else {
/*
* No, so just specify the default port.
*/
buf = strdup("");
}
DEBUGMSGTL(("snmp_agent", "final port spec: \"%s\"\n", buf));
st = buf;
do {
/*
* Specification format:
*
* NONE: (a pseudo-transport)
* UDP:[address:]port (also default if no transport is specified)
* TCP:[address:]port (if supported)
* Unix:pathname (if supported)
* AAL5PVC:itf.vpi.vci (if supported)
* IPX:[network]:node[/port] (if supported)
*
*/
cptr = st;
st = strchr(st, ',');
if (st)
*st++ = '\0';
DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
cptr));
if (strncasecmp(cptr, "none", 4) == 0) {
DEBUGMSGTL(("snmp_agent",
"init_master_agent; pseudo-transport \"none\" "
"requested\n"));
break;
}
if (-1 == netsnmp_agent_listen_on(cptr)) {
SNMP_FREE(buf);
return 1;
}
} while(st && *st != '\0');
SNMP_FREE(buf);
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
#ifdef USING_AGENTX_MASTER_MODULE
if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
real_init_master();
#endif
#ifdef USING_SMUX_MODULE
if(should_init("smux"))
real_init_smux();
#endif
#ifndef NETSNMP_NO_PDU_STATS
_pdu_stats_init();
#endif /* NETSNMP_NO_PDU_STATS */
return 0;
}
这里除了netsnmp_agent_listen_on,还有两种USING_AGENTX_MASTER_MODULE和USING_SMUX_MODULE,可能是给多线程下使用的,跳过
//snmp_agent.c
int netsnmp_agent_listen_on(const char *port)
{
netsnmp_transport *transport;
int handle;
if (NULL == port)
return -1;
transport = netsnmp_transport_open_server("snmp", port);
if (transport == NULL) {
snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n", port);
return -1;
}
handle = netsnmp_register_agent_nsap(transport);
if (handle < 0) {
snmp_log(LOG_ERR, "Error registering specified transport \"%s\" as an "
"agent NSAP\n", port);
return -1;
} else {
DEBUGMSGTL(("snmp_agent",
"init_master_agent; \"%s\" registered as an agent NSAP\n",
port));
}
return handle;
}
这里的netsnmp_register_agent_nsap查看实现
//snmp_agent.c
int
netsnmp_register_agent_nsap(netsnmp_transport *t)
{
netsnmp_session *s, *sp = NULL;
agent_nsap *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
int handle = 0;
void *isp = NULL;
if (t == NULL) {
return -1;
}
DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));
n = (agent_nsap *) malloc(sizeof(agent_nsap));
if (n == NULL) {
return -1;
}
s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
if (s == NULL) {
SNMP_FREE(n);
return -1;
}
snmp_sess_init(s);
/*
* Set up the session appropriately for an agent.
*/
s->version = SNMP_DEFAULT_VERSION;
s->callback = handle_snmp_packet;
s->authenticator = NULL;
s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_FLAGS);
s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;
/* Optional supplimental transport configuration information and
final call to actually open the transport */
if (netsnmp_sess_config_transport(s->transport_configuration, t)
!= SNMPERR_SUCCESS) {
SNMP_FREE(s);
SNMP_FREE(n);
return -1;
}
if (t->f_open)
t = t->f_open(t);
if (NULL == t) {
SNMP_FREE(s);
SNMP_FREE(n);
return -1;
}
t->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
sp = snmp_add(s, t, netsnmp_agent_check_packet,
netsnmp_agent_check_parse);
if (sp == NULL) {
SNMP_FREE(s);
SNMP_FREE(n);
return -1;
}
isp = snmp_sess_pointer(sp);
if (isp == NULL) { /* over-cautious */
SNMP_FREE(s);
SNMP_FREE(n);
return -1;
}
n->s = isp;
n->t = t;
if (main_session == NULL) {
main_session = snmp_sess_session(isp);
}
for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
a = a->next) {
handle = a->handle;
prevNext = &(a->next);
}
if (handle < INT_MAX) {
n->handle = handle + 1;
n->next = a;
*prevNext = n;
SNMP_FREE(s);
DEBUGMSGTL(("netsnmp_register_agent_nsap", "handle %d\n", n->handle));
return n->handle;
} else {
SNMP_FREE(s);
SNMP_FREE(n);
return -1;
}
}