如何使用NET-SNMP库实现C语言SNMP入站请求接收
基于NET-SNMP的SNMP服务器端入站请求接收问题排查与修复
问题背景
项目采用SNMP协议与NET-SNMP库开发,已完成SNMP客户端(管理器)的C语言实现并运行正常,但在开发服务器端(代理)时,现有代码无法正常接收并处理入站SNMP请求,需排查问题并实现功能。
现有代码的核心问题
- 会话初始化错误:原代码使用客户端模式的
snmp_sess_open并指定peername,这是客户端连接目标服务器的配置逻辑,服务器端需监听本地地址,应调整为无目标地址的会话配置。 - 循环逻辑致命错误:初始化
pdu = NULL后立即判断if(!pdu)并执行continue,导致永远不会触发snmp_sess_read接收请求,直接进入空转死循环。 - PDU命令打印错误:
pdu->command是枚举整数类型,用%s格式符打印会触发未定义行为,需改为整数打印或映射为对应字符串。 - 响应发送方式错误:
snmp_synch_response是客户端同步发送请求的接口,服务器端应使用snmp_send发送响应,且需将请求PDU的request_id复制到响应PDU中,否则客户端无法识别响应。
修正后的完整代码
#include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> #include <signal.h> int running = 1; // 信号处理函数,支持Ctrl+C优雅退出 void sig_handler(int signo) { if (signo == SIGINT || signo == SIGTERM) { running = 0; printf("\nShutting down SNMP server...\n"); } } int main(int argc, char **argv) { netsnmp_session session, *ss; netsnmp_pdu *pdu; netsnmp_pdu *response; oid anOID[MAX_OID_LEN]; size_t anOID_len; int status; // 注册信号处理 signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); // 初始化SNMP库 init_snmp("snmp_server"); // 配置服务器端会话参数 snmp_sess_init(&session); // 服务器端监听所有本地地址,不指定目标地址 session.peername = NULL; session.version = SNMP_VERSION_2c; // 设置服务端验证用的社区字符串 session.community = (u_char *)"public"; session.community_len = strlen((char *)session.community); // 指定监听默认SNMP端口161 session.remote_port = SNMP_PORT; // 打开SNMP服务器端会话 ss = snmp_open(&session); if (!ss) { snmp_perror("snmp_open"); exit(1); } printf("SNMP server started, listening on port %d...\n", SNMP_PORT); // 循环接收并处理请求 while (running) { pdu = NULL; // 阻塞等待接收SNMP请求 status = snmp_read(ss, &pdu); if (status == STAT_SUCCESS && pdu != NULL) { // 映射请求类型为可读字符串 const char *cmd_str = NULL; switch (pdu->command) { case SNMP_MSG_GET: cmd_str = "GET"; break; case SNMP_MSG_GETNEXT: cmd_str = "GETNEXT"; break; case SNMP_MSG_SET: cmd_str = "SET"; break; case SNMP_MSG_TRAP: cmd_str = "TRAP"; break; default: cmd_str = "UNKNOWN"; break; } printf("Received request: %s (code: %d)\n", cmd_str, pdu->command); // 处理GET/GETNEXT请求并返回响应 if (pdu->command == SNMP_MSG_GET || pdu->command == SNMP_MSG_GETNEXT) { // 创建响应PDU response = snmp_pdu_create(SNMP_MSG_RESPONSE); // 复制请求的request_id,确保客户端能匹配响应 response->request_id = pdu->request_id; response->errstat = SNMP_ERR_NOERROR; response->errindex = 0; // 添加响应变量(系统描述符OID) anOID_len = MAX_OID_LEN; if (snmp_parse_oid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len) != NULL) { snmp_add_var(response, anOID, anOID_len, 's', "Custom SNMP Server - Hello World!"); } else { snmp_perror("Failed to parse OID .1.3.6.1.2.1.1.1.0"); response->errstat = SNMP_ERR_NOSUCHNAME; } // 发送响应,snmp_send会自动释放response status = snmp_send(ss, response); if (status != STAT_SUCCESS) { snmp_perror("Failed to send response"); } } // 释放请求PDU snmp_free_pdu(pdu); } else if (status != STAT_SUCCESS) { // 忽略非致命错误,继续运行 snmp_perror("snmp_read failed"); } } // 清理资源 snmp_close(ss); snmp_shutdown("snmp_server"); printf("SNMP server stopped.\n"); return 0; }
编译与运行说明
- 编译时链接NET-SNMP库:
gcc -o snmp_server snmp_server.c -lnetsnmp - 由于SNMP默认端口161是特权端口,需以root权限运行:
sudo ./snmp_server - 使用
snmpget工具测试:snmpget -v 2c -c public localhost .1.3.6.1.2.1.1.1.0
内容的提问来源于stack exchange,提问作者sajad mosaybi
相关产品推荐
相关产品推荐

