实现 SNMP MIB 的详细指南
1. 数据查询与获取
在实现 SNMP MIB 时,主要的改动集中在
switch
语句中。通过调用
query()
例程从 Laddie 的 RTA 表中检索值以满足请求。例如,
ladVersion
的值来自 Laddie 的
Config
表的
version
字段。由于
Config
表有多个用途不同的行,为了只获取一个结果,添加了
LIMIT 1 OFFSET 1
选项。
LIMIT 1
确保只返回一个答案,
OFFSET 1
则指定获取表的第二行。
对于区域数量,直接从
rta_tables
表的
nrows
(行数)字段获取。不过,需要使用
query()
的过滤参数来选择
name
字段为
Zone
的行,因为
rta_tables
表为
ladd
守护进程中的每个 RTA 表都有一行记录。
2. 读取警报表
var_ladAlarmTable()
函数用于处理
ladAlarmTable
中对象的读取操作。大部分工作是在
switch
语句中添加代码。读取表的例程与处理标量值的主要区别在于需要处理表行的索引和确定表的大小。
以下是
var_ladAlarmTable()
函数的代码:
unsigned char *
var_ladAlarmTable(struct variable *vp,
oid *name,
size_t *length,
int exact,
size_t *var_len,
WriteMethod **write_method)
{
static long long_ret;
static u_long ulong_ret;
static u_long table_size;
static u_long table_index;
static unsigned char string[SPRINT_MAX_LEN];
static unsigned char filter[SPRINT_MAX_LEN];
static oid objid[MAX_OID_LEN];
static struct counter64 c64;
if (0 == query("rta_tables", "nrows", "name=Zone", "", string, SPRINT_MAX_LEN)) {
table_size = atol(string);
} else {
return NULL;
}
if (header_simple_table(vp,
name,
length,
exact,
var_len,
write_method,
table_size)
== MATCH_FAILED )
return NULL;
table_index = name[*length-1];
sprintf(filter, "id=%d", table_index);
switch(vp->magic) {
case LADALARMZONENAME:
DEBUGMSGTL(("LAD", "reading ladAlarmZoneName\n"));
if (0 == query("Zone", "name", filter, "", string, SPRINT_MAX_LEN)) {
DEBUGMSGTL(("LAD", "ladAlarmZoneName[%d]=%s\n", table_index, string));
*var_len = strlen(string);
return (u_char*) &string;
}
break;
case LADALARMENABLE:
DEBUGMSGTL(("LAD", "reading ladAlarmEnable\n"));
if (0 == query("Zone", "enabled", filter, "", string, SPRINT_MAX_LEN)) {
DEBUGMSGTL(("LAD", "ladAlarmZoneEnable[%d]=%s\n", table_index, string));
*write_method = write_ladAlarmEnable;
long_ret = atol(string);
return (u_char*) &long_ret;
}
break;
case LADALARMLATCHING:
DEBUGMSGTL(("LAD", "reading ladAlarmLatching\n"));
if (0 == query("Zone", "latching", filter, "", string, SPRINT_MAX_LEN)) {
DEBUGMSGTL(("LAD", "ladAlarmZoneLatching[%d]=%s\n", table_index, string));
*write_method = write_ladAlarmLatching;
long_ret = atol(string);
return (u_char*) &long_ret;
}
break;
case LADALARMSTATE:
DEBUGMSGTL(("LAD", "reading ladAlarmState\n"));
if (0 == query("Zone", "alarm", filter, "", string, SPRINT_MAX_LEN)) {
DEBUGMSGTL(("LAD", "ladAlarmZoneState[%d]=%s\n", table_index, string));
*write_method = write_ladAlarmState;
long_ret = atol(string);
return (u_char*) &long_ret;
}
break;
case LADALARMCOUNT:
DEBUGMSGTL(("LAD", "reading ladAlarmCount\n"));
if (0 == query("Zone", "count", filter, "", string, SPRINT_MAX_LEN)) {
DEBUGMSGTL(("LAD", "ladAlarmZoneCount[%d]=%s\n", table_index, string));
long_ret = atol(string);
return (u_char*) &long_ret;
}
break;
default:
ERROR_MSG("");
}
return NULL;
}
在每个
case
中,查询
Zone
表的某个字段以获取值。如果查询失败,则跳出
switch
语句并返回
NULL
,表示该值不可检索。对于定义为具有读写访问权限的对象,需要返回一个
write_method
,即处理
SET
操作的例程的指针。
3. 写入警报表
以
ladAlarmEnable
对象为例,写入例程围绕
switch
语句根据
action
参数进行不同的处理。
switch
语句的
case
通常包括:
-
RESERVE1
-
RESERVE2
-
FREE
-
ACTION
-
UNDO
-
COMMIT
以下是
write_ladAlarmEnable()
函数的代码:
int
write_ladAlarmEnable(int action,
u_char *var_val,
u_char var_val_type,
size_t var_val_len,
u_char *statP,
oid *name,
size_t name_len)
{
static long value;
int size;
static int saved_value;
static u_long table_index;
static unsigned char string[SPRINT_MAX_LEN];
static unsigned char filter[SPRINT_MAX_LEN];
table_index = name[name_len-1];
switch ( action ) {
case RESERVE1:
if (var_val_type != ASN_INTEGER) {
DEBUGMSGTL(("LAD", "write to ladAlarmEnable - not ASN_INTEGER\n"));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > sizeof(long)) {
DEBUGMSGTL(("LAD","write to ladAlarmEnable - bad length\n"));
return SNMP_ERR_WRONGLENGTH;
}
value = *((long *) var_val);
if (value > 1) {
DEBUGMSGTL(("LAD", "write to ladAlarmEnable - wrong value %x\n", value));
return SNMP_ERR_WRONGVALUE;
}
DEBUGMSGTL(("LAD", "\nRESERVE1 ok; value is %d\n", value));
break;
case RESERVE2:
size = var_val_len;
value = * (long *) var_val;
DEBUGMSGTL(("LAD", "\nRESERVE2 ok; value is %d\n", value));
break;
case FREE:
DEBUGMSGTL(("LAD", "\nFREE ok; value is %d\n", value));
break;
case ACTION:
DEBUGMSGTL(("LAD", "\nACTION; value is %d\n", value));
DEBUGMSGTL(("LAD", "writing ladAlarmEnable in row %d\n", table_index));
sprintf(filter, "id=%d", table_index);
if (0 != query("Zone", "enabled", filter, "", string, SPRINT_MAX_LEN)) {
saved_value = -1;
return SNMP_ERR_RESOURCEUNAVAILABLE;
}
saved_value = atol(string);
sprintf(filter, "id=%d", table_index);
sprintf(string, "%d", value);
if (0 != update("Zone", "enabled", filter, string)) {
return SNMP_ERR_RESOURCEUNAVAILABLE;
}
break;
case UNDO:
sprintf(filter, "id=%d", table_index);
sprintf(string, "%d", saved_value);
if (saved_value != -1) {
if (0 != update("Zone", "enabled", filter, string)) {
return SNMP_ERR_RESOURCEUNAVAILABLE;
}
}
break;
case COMMIT:
break;
}
return SNMP_ERR_NOERROR;
}
-
RESERVE1:检查值的类型和长度是否准确,并添加了值的范围检查,确保enable值只能是 0 或 1。 -
RESERVE2:保存值的长度和值本身。 -
FREE:释放已分配的资源。 -
ACTION:在写入新值之前,先检索当前值并保存到saved_value中,以便在UNDO时使用。 -
UNDO:如果ACTION成功检索到旧值,则将其写回。 -
COMMIT:由于ACTION已经将值写入 RTA 表,所以此步骤无需操作。
4. 修改 Makefile
之前使用
Makefile
生成代理,现在需要对其进行修改以包含 LAD - MIB 代码。以下是修改后的
Makefile
:
NETSNMP_VERSION = 5.2.1
BUILD_DIR = ./net-snmp-$(NETSNMP_VERSION)
# Targets
all: clean setup config build
clean:
rm -rf $(BUILD_DIR)
rm -rf /opt/snmp/*
setup:
tar zxvf net-snmp-$(NETSNMP_VERSION).tar.gz;
mkdir $(BUILD_DIR)/agent/mibgroup/lad;
cp ladProject.[ch] $(BUILD_DIR)/agent/mibgroup/lad
config:
cd $(BUILD_DIR);
export LDFLAGS="-lpq";
./configure --prefix=/opt/snmp --with-mib-modules="lad/ladProject"<../configure.input;
cd ..
build:
cd $(BUILD_DIR);
make
install:
cd $(BUILD_DIR);
install
-
setup:创建agent/mibgroup/lad目录,并将ladProject的头文件和 C 文件复制到该目录。 -
config:在LDFLAGS中添加-lpq以告诉链接器包含libpq库,同时告诉configure包含我们的 MIB 模块。
5. 调试
借助
DEBUGMSGTL
宏,可以跟踪代理的控制流程。调试步骤如下:
1. 停止后台运行的代理:
/etc/rc.d/init.d/snmpd stop
- 从命令行运行自己的代理:
/opt/snmp/sbin/snmpd -D "LAD" -Le -f -c /opt/snmp/etc/snmp/snmpd.conf -C
- `-D "LAD"`:激活指定 `LAD` 的 `DEBUGMSGTL` 语句。
- `-Le`:将输出发送到 `stderr`。
- `-f`:防止代理分叉并进入后台,以便在当前终端窗口查看输出。
-
在另一个终端窗口使用
snmpget、snmpset或snmpwalk查询代理,并观察输出或保存到文件。
6. 陷阱
在 MIB 中定义了两个陷阱,但本文不讨论如何发送这些陷阱。可以参考之前的相关内容,使用
snmptrap
实用工具发送陷阱,或者使用日志事件触发 SNMP 陷阱。需要注意的是,这些陷阱不是由 SNMP 代理生成的,而是通过
ladd
守护进程记录的事件通过日志子系统生成的。
7. 总结
通过以上步骤,我们学习了如何为 Net - SNMP 代理创建 MIB 扩展以实现 MIB,以及如何使用 PostgreSQL 接口库和 RTA 从另一个守护进程检索 MIB 数据值。现在应该能够独立完成这些工作,并且熟悉 MIB 和 Net - SNMP 代理扩展的结构,可以轻松地在 MIB 和代码中添加新对象。但要注意,不能重新分配 OID,应始终在分支末尾添加新对象。
为了检验对这些内容的理解,可以尝试将之前忽略的 Laddie 表的
edge
和
input
列添加到 MIB 和代理中。
以下是读取和写入警报表的流程总结表格:
| 操作 | 步骤 |
| ---- | ---- |
| 读取警报表 | 1. 确定表大小
2. 处理表行索引
3. 根据对象查询
Zone
表获取值 |
| 写入警报表 | 1.
RESERVE1
检查值类型和长度
2.
RESERVE2
保存值信息
3.
FREE
释放资源
4.
ACTION
写入新值并保存旧值
5.
UNDO
恢复旧值
6.
COMMIT
无需操作 |
以下是写入警报表的 mermaid 流程图:
graph TD;
A[开始] --> B[RESERVE1];
B -->|检查失败| C[返回错误];
B -->|检查成功| D[RESERVE2];
D --> E[FREE];
E --> F[ACTION];
F -->|查询失败| C;
F -->|查询成功| G[保存旧值];
G --> H[写入新值];
H -->|写入失败| C;
H -->|写入成功| I[UNDO];
I -->|有旧值| J[恢复旧值];
J -->|恢复失败| C;
J -->|恢复成功| K[COMMIT];
K --> L[结束];
实现 SNMP MIB 的详细指南
8. 关键技术点分析
-
数据查询优化
:在查询
Config表获取ladVersion时,使用LIMIT 1 OFFSET 1选项可以避免不必要的数据读取,提高查询效率。对于rta_tables表获取区域数量,使用过滤参数选择特定行,减少了查询的数据量。这种针对不同表结构和查询需求进行优化的方式,能有效提升系统性能。 -
表操作处理
:在读取和写入警报表时,对于表行索引和表大小的处理是关键。通过从 OID 中获取表行索引,并在写入时根据不同的
action参数进行相应操作,确保了数据的准确读写和错误处理。例如,在ACTION步骤中保存旧值,以便在UNDO时恢复,保证了数据的一致性。 -
错误处理机制
:在整个实现过程中,错误处理机制贯穿始终。如在查询失败时返回
NULL,在写入操作中对不同的错误情况返回相应的错误码,如SNMP_ERR_WRONGTYPE、SNMP_ERR_WRONGLENGTH等,这些机制能让系统在出现异常时及时反馈,便于调试和维护。
9. 实际应用场景
-
监控系统
:SNMP MIB 可以用于构建监控系统,通过读取警报表中的数据,如
ladAlarmState、ladAlarmCount等,实时监控系统的运行状态。当出现异常时,可以及时触发警报,通知管理员进行处理。 -
配置管理
:对于可读写的对象,如
ladAlarmEnable,可以通过 SNMP 协议远程配置系统的参数,实现对系统的动态管理。
10. 拓展与创新
-
添加新对象
:根据实际需求,可以在 MIB 文件中添加新的对象。具体步骤如下:
- 在 MIB 文件中添加新对象的定义,这主要是一些复制粘贴的工作。
- 在定义列表中为新变量定义新的编号。
-
在
ladProject_variables数组中添加相应的行。 -
在适当的
switch语句中添加新的case分支,处理新对象的读写操作。
- 性能优化 :可以进一步优化查询和写入操作,例如使用缓存机制减少对数据库的频繁访问,或者优化 SQL 查询语句以提高查询效率。
11. 常见问题及解决方法
| 问题 | 原因 | 解决方法 |
|---|---|---|
查询失败,返回
NULL
| 数据库连接问题、表结构变化等 | 检查数据库连接配置,确认表结构是否正确。 |
| 写入操作返回错误码 | 值类型不匹配、长度错误、资源不可用等 | 根据错误码检查输入值的类型和长度,确保数据库资源可用。 |
| 调试时无输出 |
DEBUGMSGTL
未激活、输出路径设置错误等
|
检查
-D
选项是否正确设置,确认
-Le
选项指定的输出路径。
|
12. 总结与展望
通过本文的学习,我们详细了解了实现 SNMP MIB 的全过程,包括数据查询、表的读写操作、Makefile 的修改、调试方法以及陷阱的相关知识。掌握了这些内容后,我们能够独立创建 MIB 扩展,并且可以根据实际需求对其进行拓展和优化。
在未来的应用中,随着网络规模的不断扩大和系统复杂度的增加,SNMP MIB 的重要性将更加凸显。我们可以进一步研究如何更好地利用 SNMP 协议进行系统监控和管理,提高网络的可靠性和性能。同时,也可以探索与其他技术的结合,如大数据分析、人工智能等,为网络管理带来更多的创新和可能性。
以下是添加新对象到 MIB 和代码的流程 mermaid 流程图:
graph TD;
A[开始] --> B[在 MIB 文件添加对象定义];
B --> C[定义新变量编号];
C --> D[在 ladProject_variables 数组添加行];
D --> E[在 switch 语句添加 case 分支];
E --> F[结束];
以下是性能优化的步骤列表:
1. 分析系统性能瓶颈,确定需要优化的部分,如查询频繁的表或写入操作耗时较长的对象。
2. 考虑使用缓存机制,如内存缓存或分布式缓存,减少对数据库的直接访问。
3. 优化 SQL 查询语句,避免全表扫描,使用索引提高查询效率。
4. 对代码进行性能测试,验证优化效果,根据测试结果进行调整和改进。
超级会员免费看
58

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



