29、实现 SNMP MIB 的详细指南

实现 SNMP MIB 的详细指南

1. 代码风格选择

在实现 SNMP MIB 时,会面临代码风格的选择,有 ucd - snmp 风格代码和 Net - SNMP 风格代码两种选项。这里选择了 ucd - snmp 风格代码,原因是有相关经验。虽然 Net - SNMP 类型更新且更灵活,但当前需求并不需要这种灵活性。

2. 头文件 ladProject.h

生成了两个文件:ladProject.c 和 ladProject.h。头文件 ladProject.h 简洁明了,无需过多关注,其内容如下:

/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.old-api.conf,v 1.4 2004/07/28 08:04:58 dts12 Exp $
 */
#ifndef LADPROJECT_H
#define LADPROJECT_H
/*
 * function declarations 
 */
void            init_ladProject(void);
FindVarMethod   var_ladProject;
FindVarMethod   var_ladAlarmTable;
WriteMethod     write_ladAlarmEnable;
WriteMethod     write_ladAlarmLatching;
WriteMethod     write_ladAlarmState;
#endif                          /* LADPROJECT_H */

这些是在代码文件 ladProject.c 中生成的例程的原型。

3. 代码文件 ladProject.c 修改

需要对生成的骨架代码进行以下修改:
1. 包含 libpq(PostgreSQL 库)的头文件。
2. 提供一个函数以连接 ladd 守护进程中的 RTA。
3. 提供通过与 ladd 守护进程的连接来读写 RTA 表的函数。
4. 提供读取 MIB 中每个标量的代码。
5. 提供读取 ladAlarmTable 中每个表对象的代码。
6. 为 MIB 中每个可写对象提供写函数。

3.1 包含必要头文件
/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.old-api.conf,v 1.4 2004/07/28 08:04:58 dts12 Exp $
 */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "pgsql/libpq-fe.h"      /* libpq header file */
#include "ladProject.h"

通过包含定义 libpq PostgreSQL API 的头文件,完成了第一个目标。

3.2 基础 OID
/*
 * ladProject_variables_oid:
 *   this is the top level oid that we want to register under.  This
 *   is essentially a prefix, with the suffix appearing in the
 *   variable below.
 */
oid             ladProject_variables_oid[] =
    { 1, 3, 6, 1, 4, 1, 23528, 1, 1 };

这是 MIB 中定义的所有对象的基础 OID。

3.3 MIB 对象定义

为每个叶子对象生成一个 define,以赋予每个要处理的对象一个唯一编号,例如 ladVersion 变为 LADVERSION。同时,每个对象在 ladProject_variables 表中有自己的条目,提供以下信息:
- 唯一标识符(用于后续 case 语句)
- 数据类型
- 只读或读写属性
- 用于读取对象值的函数
- OID 后缀(与基础 OID 拼接得到完整 OID)

原始生成的代码存在问题:
- 包含了 ladTrapZoneId 和 ladTrapZoneName 作为可读对象,但它们仅用于陷阱,不应作为可读或可写对象,需移除这两行。
- 所有对象的 OID 后缀字段错误,缺少 ladSystem 和 ladSystemScalars 或 ladSystemTables 的编号。

修正后的代码如下:

#define LADVERSION             1
#define LADNUMBEROFZONES       2
#define LADALARMZONEID         5
#define LADALARMZONENAME       6
#define LADALARMENABLE         7
#define LADALARMLATCHING       8
#define LADALARMSTATE          9
#define LADALARMCOUNT          10

struct variable7 ladProject_variables[] = {
    /*
     * magic number,   variable type, ro/rw , callback fn,       L, oidsuffix 
     */
    {LADVERSION,       ASN_OCTET_STR, RONLY,  var_ladProject,    3, {1, 1, 1}},
    {LADNUMBEROFZONES, ASN_INTEGER,   RONLY,  var_ladProject,    3, {1, 1, 2}},
    {LADALARMZONEID,   ASN_INTEGER,   RONLY,  var_ladAlarmTable, 5, {1, 2, 1, 1, 1}},
    {LADALARMZONENAME, ASN_OCTET_STR, RONLY,  var_ladAlarmTable, 5, {1, 2, 1, 1, 2}},
    {LADALARMENABLE,   ASN_INTEGER,   RWRITE, var_ladAlarmTable, 5, {1, 2, 1, 1, 3}},
    {LADALARMLATCHING, ASN_INTEGER,   RWRITE, var_ladAlarmTable, 5, {1, 2, 1, 1, 4}},
    {LADALARMSTATE,    ASN_INTEGER,   RWRITE, var_ladAlarmTable, 5, {1, 2, 1, 1, 5}},
    {LADALARMCOUNT,    ASN_COUNTER,   RONLY,  var_ladAlarmTable, 5, {1, 2, 1, 1, 6}},
};

由于 OID 变长,将使用的结构体类型从 variable4 改为 variable7。variable7 的定义如下:

struct variable7 {
    u_char          magic;        /* passed to function as a hint */
    u_char          type;         /* type of variable */
    u_short         acl;          /* access control list for variable */
    FindVarMethod  *findVar;      /* function that finds variable */
    u_char          namelen;      /* length of name below */
    oid             name[7];
    /* object identifier of variable */
};
4. RTA 访问例程

需要插入用于连接和与 ladd 守护进程通信的函数:lad_connect()、query() 和 update()。

4.1 lad_connect 函数
/* LAD-specific variables */
PGconn     *conn;               /* holds database connection */
/*************************************************** 
 * Connect to the application 
 ***************************************************/
static
int lad_connect (void) 
{
    conn = PQconnectdb("host=localhost port=8888");
    if (PQstatus(conn) == CONNECTION_BAD)        {
        DEBUGMSGTL(("LAD", "Connection to application failed.\n"));
        DEBUGMSGTL(("LAD", "%s", PQerrorMessage(conn)));
        PQfinish(conn);
        return (-1);
    }
    DEBUGMSGTL(("LAD", "Connection to application succeeded.\n"));
    return (0);
}

定义了一个全局变量 conn 来保存与 Laddie 的连接句柄,并定义了 lad_connect 函数来建立此连接。

4.2 query 函数
/*************************************************** 
 * query() - request a value from a table. 
 ***************************************************/
static
int query(char *table, char *field, char *filter, char* options, char 
*output_str, int output_len)
{
    char           query_string[256];  /* holds constructed SQL query */
    PGresult      *result;             /* holds query result */
    ExecStatusType status;             /* return type from PQresultStatus */
    char          *errMsg;
    /* check connection to LAD application */
    if (PQstatus(conn) != CONNECTION_OK) {
        /* try to re-connect */
        if (0 != lad_connect()) {
            return(-1);   /* fail the query */
        }
    }
    /* Construct query string */
    if (strlen(filter) > 0) {
        sprintf (query_string, "select %s from %s where %s %s;", 
                 field, table, filter, options);
    } else {
        sprintf (query_string, "select %s from %s %s;", field, table, options);
    }
    DEBUGMSGTL(("LAD", "sending query '%s'\n", query_string));
    result = PQexec(conn, query_string);   /* send the query */
    if ((status = PQresultStatus(result)) != PGRES_TUPLES_OK) {
        DEBUGMSGTL(("LAD", "query failed:  PQresultStatus returned %d; %s", 
                     status, PQerrorMessage(conn)));
        printf ("%s", errMsg);
        PQclear(result);
        PQfinish(conn);
        return (-1);
    }
    DEBUGMSGTL(("LAD", "success! result = %d\n", status));
    strncpy (output_str, PQgetvalue(result, 0, 0), output_len);
    DEBUGMSGTL(("LAD", "query suceeded; returning '%s'\n", output_str));
    return (0); 
}

此函数用于从 RTA 表中读取值,先检查连接是否有效,若无效则重新连接,然后构造查询字符串并执行查询,根据结果返回相应信息。

5. 流程图:query 函数流程
graph TD;
    A[开始] --> B{连接是否有效};
    B -- 否 --> C[重新连接];
    C -- 失败 --> D[返回错误];
    C -- 成功 --> E[构造查询字符串];
    B -- 是 --> E;
    E --> F[执行查询];
    F --> G{查询结果是否有效};
    G -- 否 --> H[清理资源并返回错误];
    G -- 是 --> I[提取结果并返回成功];
6. 表格:MIB 对象信息
对象名称 唯一标识符 数据类型 读写属性 回调函数 OID 后缀长度 OID 后缀
LADVERSION 1 ASN_OCTET_STR RONLY var_ladProject 3 {1, 1, 1}
LADNUMBEROFZONES 2 ASN_INTEGER RONLY var_ladProject 3 {1, 1, 2}
LADALARMZONEID 5 ASN_INTEGER RONLY var_ladAlarmTable 5 {1, 2, 1, 1, 1}
LADALARMZONENAME 6 ASN_OCTET_STR RONLY var_ladAlarmTable 5 {1, 2, 1, 1, 2}
LADALARMENABLE 7 ASN_INTEGER RWRITE var_ladAlarmTable 5 {1, 2, 1, 1, 3}
LADALARMLATCHING 8 ASN_INTEGER RWRITE var_ladAlarmTable 5 {1, 2, 1, 1, 4}
LADALARMSTATE 9 ASN_INTEGER RWRITE var_ladAlarmTable 5 {1, 2, 1, 1, 5}
LADALARMCOUNT 10 ASN_COUNTER RONLY var_ladAlarmTable 5 {1, 2, 1, 1, 6}

实现 SNMP MIB 的详细指南

7. update 函数
/**************************************************
 * update() - update a value in a table. 
 ***************************************************/
static
int update(char *table, char *field, char *filter, char *newval_str)
{
    char           update_string[256];  /* holds constructed SQL update */
    PGresult      *result;             /* holds update result */
    ExecStatusType status;             /* return type from PQresultStatus */
    char          *errMsg;
    /* check connection to LAD application */
    if (PQstatus(conn) != CONNECTION_OK) {
        /* try to re-connect */
        if (0 != lad_connect()) {
            return(-1);   /* fail the update */
        }
    }
    sprintf (update_string, "update %s SET %s=%s where %s;", 
             table, field, newval_str, filter);
    DEBUGMSGTL(("LAD", "sending update '%s'\n", update_string));
    result = PQexec(conn, update_string);   /* send the update */
    if ((status = PQresultStatus(result)) != PGRES_COMMAND_OK) {
        DEBUGMSGTL(("LAD", "update failed:  PQresultStatus returned %d; %s", 
                     status, PQerrorMessage(conn)));
        printf ("%s", errMsg);
        PQclear(result);
        PQfinish(conn);
        return (-1);
    }
    DEBUGMSGTL(("LAD", "success! result = %d\n", status));
    return (0); 
}

该函数用于更新表中的值,与 query 函数类似,先检查连接有效性,若无效则尝试重新连接,接着构造更新字符串并执行更新操作,根据结果返回相应状态。这些函数是用于读写 PostgreSQL 数据库和 RTA 表的通用例程,无 SNMP 或 Laddie 特定内容,可用于任何需要访问 RTA 表的 C 应用,仅需更改端口号。

8. 初始化例程
/** Initializes the ladProject module */
void
init_ladProject(void)
{
    DEBUGMSGTL(("LAD", "Initializing\n"));
    /*
     * register ourselves with the agent to handle our mib tree 
     */
    REGISTER_MIB("ladProject", ladProject_variables, variable7,
                 ladProject_variables_oid);
    /*
     * place any other initialization junk you need here 
     */
}

这是一个简单的初始化例程,未对生成的代码做修改。 REGISTER_MIB 行将 OID 子树注册到中央代理代码,使其知道调用相应函数来读写该子树中的对象。注意这里也将 variable4 改为了 variable7。

9. 标量处理

标量是 MIB 中的叶子对象,通过 var_ladProject 函数处理标量的读取。该函数的输入参数如下:
- vp :指向 ladProject_variables 数组中相关条目的指针。
- name :请求的 OID, length 是该 OID 的长度。
- exact :指示是处理精确 OID 请求(如 GET 或 SET)还是需要查找要处理的 OID(如 GETNEXT)。
- var_len :输出参数,函数需设置为返回数据的长度。
- write_method :输出参数,指向处理可写 OID 的 SET 操作的函数。
- 函数返回请求数据的值,若数据不可用则返回 NULL。

unsigned char *
var_ladProject(struct variable *vp,
                oid     *name,
                size_t  *length,
                int     exact,
                size_t  *var_len,
                WriteMethod **write_method)
{
    /* variables we may use later */
    static long          long_ret;
    static u_long        ulong_ret;
    static unsigned char string[SPRINT_MAX_LEN];
    static oid           objid[MAX_OID_LEN];
    static struct        counter64 c64;
    if (header_generic(vp,name,length,exact,var_len,write_method)
                                  == MATCH_FAILED )
    return NULL;
  /* 
   * this is where we do the value assignments for the mib results.
   */
    switch(vp->magic) {
    case LADVERSION:
        if (0 == query("Config", 
                       "version", 
                       "", 
                       "LIMIT 1 OFFSET 1", 
                       string,   
                       SPRINT_MAX_LEN)) {
        *var_len = strlen(string);
        return (u_char*) &string;
        }
        break;
    case LADNUMBEROFZONES:
        if (0 == query("rta_tables", 
                       "nrows", 
                       "name=Zone",
                       "", 
                       string, 
                       SPRINT_MAX_LEN)) {
            long_ret = atol(string);
            return (u_char*) &long_ret;
        }
        break;
    default:
      ERROR_MSG("");
    }
    return NULL;
} 

该函数根据 vp->magic 的值进行不同处理,通过调用 query 函数从 RTA 表中获取相应值并返回。

10. 流程图:var_ladProject 函数流程
graph TD;
    A[开始] --> B{header_generic 匹配是否成功};
    B -- 否 --> C[返回 NULL];
    B -- 是 --> D{根据 vp->magic 匹配 case};
    D -- LADVERSION --> E[调用 query 获取 version 值];
    E -- 成功 --> F[设置 var_len 并返回值];
    E -- 失败 --> G[继续];
    D -- LADNUMBEROFZONES --> H[调用 query 获取 nrows 值];
    H -- 成功 --> I[转换为 long 并返回值];
    H -- 失败 --> G;
    D -- 其他 --> J[输出错误信息];
    G --> K[返回 NULL];
    J --> K;
11. 总结

通过以上步骤,完成了 SNMP MIB 的实现。从代码风格选择、头文件和代码文件的处理,到 RTA 访问例程的编写、初始化例程的设置以及标量处理函数的实现,逐步构建了一个完整的 SNMP MIB 系统。在实现过程中,需要注意代码的正确性和规范性,对生成的代码进行必要的修改和优化,以确保系统的正常运行。

12. 表格:函数总结
函数名称 功能 输入参数 返回值
lad_connect 连接到 ladd 守护进程中的 RTA 0 表示成功,-1 表示失败
query 从 RTA 表中读取值 表名、字段名、过滤字符串、选项、输出缓冲区、缓冲区长度 0 表示成功,-1 表示失败
update 更新 RTA 表中的值 表名、字段名、过滤字符串、新值字符串 0 表示成功,-1 表示失败
init_ladProject 初始化 ladProject 模块
var_ladProject 处理标量的读取 结构体指针、OID、OID 长度、精确标志、数据长度指针、写方法指针 数据值或 NULL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值