实现 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 |
超级会员免费看
80

被折叠的 条评论
为什么被折叠?



