如何在net-snmp中循环执行GET_BULK?代码异常排查
问题:循环执行SNMP GET_BULK仅返回单次请求数据
我用以下C代码循环执行GET_BULK获取交换机日志(日志子树数据量大,所以用循环),代码能正常运行,但每次循环都只输出单次GET_BULK请求的数据,和MIB浏览器单次GETBULK的输出一致。怀疑是GET_BULK缓存或循环逻辑有问题,刚接触net-snmp,找不到问题所在。
原始代码
#include <stdio.h> #include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> int main() { oid target_oid[] = {1, 3, 6, 1, 4, 1, 89, 82, 2, 9, 1, 6}; size_t target_oid_len = OID_LENGTH(target_oid); init_snmp("snmpget"); // SNMP会话初始化 netsnmp_session session, *ss; snmp_sess_init(&session); session.peername = strdup("172.16.100.32"); session.version = SNMP_VERSION_2c; session.community = "public"; session.community_len = strlen(session.community); // 初始化套接字 SOCK_STARTUP; ss = snmp_open(&session); if (!ss) { snmp_perror("snmp_open"); SOCK_CLEANUP; exit(1); } netsnmp_pdu *response; netsnmp_pdu *pdu; int status; int start_index = 1; int num_requests = 20; for (int i = 0; i < num_requests; i++) { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = 0; pdu->max_repetitions = 1000; snmp_pdu_add_variable(pdu, target_oid, target_oid_len, ASN_NULL, NULL, 0); pdu->variables->name[target_oid_len - 1] = start_index + i; pdu->variables->name_length = target_oid_len + 1; status = snmp_synch_response(ss, pdu, &response); if (status == STAT_SUCCESS && response->variables) { for (netsnmp_variable_list *vars = response->variables; vars; vars = vars->next_variable) { if (vars->name[11] == 6 && vars->name[9] == 9) { printf("%.*s\n", (int)vars->val_len, vars->val.string); } } } else { fprintf(stderr, "Error: %s\n", snmp_api_errstring(snmp_errno)); snmp_sess_perror("snmpget", ss); } // 清理资源 snmp_free_pdu(response); } snmp_close(ss); SOCK_CLEANUP; return 0; }
问题原因分析
- 错误的OID索引修改方式:手动修改
pdu->variables->name[target_oid_len - 1]并增加长度的做法不符合SNMP GET_BULK的工作逻辑。GET_BULK需要以上一次返回的最后一个OID作为下一次请求的起始点,而非手动递增索引。 - 固定循环次数不合理:日志数据量不确定,固定循环20次可能提前终止或做无用请求,无法遍历所有数据。
- OID越界风险:直接修改
name数组元素会导致内存越界,因为snmp_pdu_add_variable创建的OID缓冲区长度为target_oid_len,将name_length设为target_oid_len + 1会访问未分配内存。
修正后的代码
#include <stdio.h> #include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> #include <string.h> int main() { // 目标列OID:日志表的某一列 oid target_oid[] = {1, 3, 6, 1, 4, 1, 89, 82, 2, 9, 1, 6}; size_t target_oid_len = OID_LENGTH(target_oid); // 存储当前起始OID,初始为目标列OID oid *current_oid = malloc(target_oid_len * sizeof(oid)); if (!current_oid) { perror("malloc failed"); exit(1); } memcpy(current_oid, target_oid, target_oid_len * sizeof(oid)); size_t current_oid_len = target_oid_len; init_snmp("snmpgetbulk"); // SNMP会话初始化 netsnmp_session session, *ss; snmp_sess_init(&session); session.peername = strdup("172.16.100.32"); session.version = SNMP_VERSION_2c; session.community = "public"; session.community_len = strlen(session.community); // 初始化套接字 SOCK_STARTUP; ss = snmp_open(&session); if (!ss) { snmp_perror("snmp_open"); free(current_oid); SOCK_CLEANUP; exit(1); } netsnmp_pdu *response; netsnmp_pdu *pdu; int status; int done = 0; while (!done) { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = 0; pdu->max_repetitions = 1000; // 添加当前起始OID到请求PDU snmp_pdu_add_variable(pdu, current_oid, current_oid_len, ASN_NULL, NULL, 0); status = snmp_synch_response(ss, pdu, &response); if (status == STAT_SUCCESS && response->variables) { netsnmp_variable_list *last_var = NULL; int has_valid_data = 0; for (netsnmp_variable_list *vars = response->variables; vars; vars = vars->next_variable) { // 检查返回的OID是否属于目标列子树 if (vars->name_length > target_oid_len && memcmp(vars->name, target_oid, target_oid_len * sizeof(oid)) == 0) { has_valid_data = 1; printf("%.*s\n", (int)vars->val_len, vars->val.string); last_var = vars; } else { // 超出目标子树,终止循环 done = 1; break; } } if (!has_valid_data) { // 无有效数据,终止循环 done = 1; } else if (last_var && !done) { // 更新起始OID为最后一个返回OID的下一个实例 free(current_oid); current_oid_len = last_var->name_length; current_oid = malloc(current_oid_len * sizeof(oid)); if (!current_oid) { perror("malloc failed"); done = 1; break; } memcpy(current_oid, last_var->name, current_oid_len * sizeof(oid)); current_oid[current_oid_len - 1] += 1; } } else { fprintf(stderr, "Error: %s\n", snmp_api_errstring(snmp_errno)); snmp_sess_perror("snmpgetbulk", ss); done = 1; } // 清理资源 snmp_free_pdu(response); } free(current_oid); snmp_close(ss); SOCK_CLEANUP; return 0; }
修正关键点说明
- 跟踪起始OID:每次请求后,将起始OID更新为上一次返回的最后一个有效OID的下一个索引,确保遍历所有后续实例。
- 动态终止循环:当返回数据超出目标子树或无有效数据时自动终止,避免无用请求。
- 安全的OID管理:使用动态内存分配存储当前OID,避免内存越界问题。
- 子树校验:检查返回的OID是否属于目标列的子树,确保只处理有效日志数据。
内容的提问来源于stack exchange,提问作者Rishi Kaimal
相关产品推荐
相关产品推荐

