You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:52:06