上一篇文章(学习笔记一)我们已经实现Net-Snmp源码的编译安装,并且测试了snmpd服务,不过我们是获取的默认的MIB文件参数值,今天我们进行添加一个自定义的MIB文件。
既然是添加自定义MIB文件,那么首先我们就要先编写一个MIB文件!这里我先给出我自己简单的MIB文件,存放位置放在"/home/lijun/net-snmp-5.9.3/out/share/snmp/mibs"(总之就是放在安装net snmp 位置下的share/snmp/mibs文件夹)。
MY-TEST-MIB DEFINITIONS ::= BEGIN
IMPORTS
enterprises, OBJECT-TYPE, MODULE-IDENTITY
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC;
Test MODULE-IDENTITY ::= { enterprises 9527}
Time OBJECT IDENTIFIER ::= { Test 1 }
GetTime OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Example : 2022/12/09"
::= { Time 1 }
END
所有的MIB file的都以DEFINITIONS ::= BEGIN关键字开始,以END结束。我们所有添加的节点均应在此之间
MY-TEST-MIB DEFINITIONS::=BEGIN
...
...
END
在MIB开始关键字后,即是模块引用区域,利用IMPORTS标识,所有的模块引用及群组的引用均使用FROM关键字说明其出处,引用使用分号(;)结束
MY-TEST-MIB DEFINITIONS ::= BEGIN
IMPORTS
enterprises, OBJECT-TYPE, MODULE-IDENTITY
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC;
...
...
END
MODULE-IDENTITY ,该定义添加了一个公共的标示段来对整个信息描述块进行顶层的文字描述,以加强对管理MIB描述块的文档管理和控制,每个MIB定义中都会有该定义。
MY-TEST-MIB DEFINITIONS ::= BEGIN
IMPORTS
enterprises, OBJECT-TYPE, MODULE-IDENTITY
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC;
Test MODULE-IDENTITY ::= { enterprises 9527 }
...
END
对象标识用关键字OBJECT IDENTIFIER声明,它的主要功能是用来将一类功能的对象结合起来。展开或折叠此对象标识即可展开或折叠此类功能的所有对象。 在ASN.1中,对象标识符类型描述对象的抽象信息,MIB树中的每一个标号是用对象标识符描述的。(声明一个Time 节点, Time是一个子树支,它定义在Test树支下,“9527” 是子树Time 在父树Test下的一个唯一对象标识符。 )
MY-TEST-MIB DEFINITIONS ::= BEGIN
IMPORTS
enterprises, OBJECT-TYPE, MODULE-IDENTITY
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC;
Test MODULE-IDENTITY ::= { enterprises 9527 }
Time OBJECT IDENTIFIER ::= { Test 1 }
...
END
在每一个树支下,可以定义被管理资源的管理对象,其中一类是标量节点(直观地看象树叶的节点)。利用OBJECT-TYPE定义。其定义的句法如下:
ObjName OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
::= { ${parent} ${number} }
关键字 | 说明 | 备注 |
---|---|---|
ObjName | 被管理的对象名字 | |
OBJECT-TYPE | 每一个叶子对象所必须的关键字 | |
syntax | 被管理对象类型的关键字 | Counter(计数) Gauge(标准) DisplayString(显示字符串) Interger(整数) TimeTicks(时间戳)NetworkAddress(网络地址) IpAddress(Ipv4地址) |
access | 该节点支持的访问方式 | read-only(只读) read-write(读写) read-create(读和创建) no-accessible(不可访问) |
status | 该节点的状态 | current (当前的) deprecated (反对的) obsolete(废弃的) |
description | 对被管理对象的功能、特征等进行描述的关键字 |
最后将MIB文件补充完整:
MY-TEST-MIB DEFINITIONS ::= BEGIN
IMPORTS
enterprises, OBJECT-TYPE, MODULE-IDENTITY
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC;
Test MODULE-IDENTITY ::= { enterprises 9527}
Time OBJECT IDENTIFIER ::= { Test 1 }
GetTime OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Example : 2022/12/09"
::= { Time 1 }
END
使用snmpdtranslate工具查看MIB文件格式是否正确:
# MY-TEST-MIB 是我们编写的MIB文件名
# Test 对象的顶层描述
[root@hecs-332420 net-snmp-5.9.3]# cd ./out/bin/
[root@hecs-332420 bin]# ./snmptranslate -Tp -IR MY-TEST-MIB::Test
Expected LAST-UPDATED (:): At line 8 in /home/lijun/net-snmp-5.9.3/out/share/snmp/mibs/MY-TEST-MIB.txt
+--Test(9527)
|
+--Time(1)
|
+-- -R-- String GetTime(1)
Textual Convention: DisplayString
Size: 0..255
[root@hecs-332420 bin]#
我们需要将MIB文件转换为 .c 文件,这里就需要使用到mib2c工具,如上第三步configure配置时携带了--disable-scripts
这一项,那么在我们 out/bin 路径下不会有mib2c工具,但是在源码路径下的local文件夹中存在mib2c,我们可以使用这个,先查看mib2c工具是否可用。
[root@hecs-332420 net-snmp-5.9.3]# cd ./local
[root@hecs-332420 local]# ./mib2c -h
./mib2c [-h] [-c configfile] [-f prefix] mibNode
-h This message.
-c configfile Specifies the configuration file to use
that dictates what the output of mib2c will look like.
-I PATH Specifies a path to look for configuration files in
-f prefix Specifies the output prefix to use. All code
will be put into prefix.c and prefix.h
-d debugging output (don't do it. trust me.)
-S VAR=VAL Set $VAR variable to $VAL
-i Don't run indent on the resulting code
-s Don't look for mibNode.sed and run sed on the resulting code
mibNode The name of the top level mib node you want to
generate code for. By default, the code will be stored in
mibNode.c and mibNode.h (use the -f flag to change this)
# 如果出现如下错误,就说明没有安装perl模块(在./configure配置时需要加上 --with-perl-modules)
# ERROR: You don't have the SNMP perl module installed. Please obtain
# this by getting the latest source release of the net-snmp toolkit from
# http://www.net-snmp.org/download/ . Once you download the source and
# unpack it, the perl module is contained in the perl/SNMP directory.
# See the README file there for instructions.
确定mib2c可用时,再将MIB文件转换为c文件,命令如下:env MIBS=“+/home/lijun/net-snmp-5.9.3/out/share/snmp/mibs/MY-TEST-MIB.txt” ./mib2c Test ;这里MIBS后的路径需要指定自己的路径位置,先选择2,再选择1。
[root@hecs-332420 local]# env MIBS="+/home/lijun/net-snmp-5.9.3/out/share/snmp/mibs/MY-TEST-MIB.txt" ./mib2c Test
Expected LAST-UPDATED (:): At line 8 in /home/lijun/net-snmp-5.9.3/out/share/snmp/mibs/MY-TEST-MIB.txt
writing to -
mib2c has multiple configuration files depending on the type of
code you need to write. You must pick one depending on your need.
You requested mib2c to be run on the following part of the MIB tree:
OID: Test
numeric translation: .1.3.6.1.4.1.9527
number of scalars within: 1
number of tables within: 0
number of notifications within: 0
First, do you want to generate code that is compatible with the
ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code
base (which provides a much greater choice of APIs to pick from). You
can also generate C-header files containing define statements for
column numbers, enums, etc. Or do you simply want to translate the
MIB structures into documentation?
1) ucd-snmp style code
2) Net-SNMP style code
3) Generate header files
4) Generate documentation
Select your choice : 2
**********************************************************************
GENERATING CODE FOR SCALAR OBJECTS:
**********************************************************************
It looks like you have some scalars in the mib you requested, so I
will now generate code for them if you wish. You have two choices
for scalar API styles currently. Pick between them, or choose not
to generate any code for the scalars:
1) If you're writing code for some generic scalars
(by hand use: "mib2c -c mib2c.scalar.conf Test")
2) If you want to magically "tie" integer variables to integer
scalars
(by hand use: "mib2c -c mib2c.int_watch.conf Test")
3) Don't generate any code for the scalars
Select your choice: 1
using the mib2c.scalar.conf configuration file to generate your code.
writing to Test.h
writing to Test.c
**********************************************************************
* NOTE WELL: The code generated by mib2c is only a template. *YOU* *
* must fill in the code before it'll work most of the time. In many *
* cases, spots that MUST be edited within the files are marked with *
* /* XXX */ or /* TODO */ comments. *
**********************************************************************
running indent on Test.h
running indent on Test.c
最后我们可以看见就在当前路径下生产了Test.c Test.h文件
[root@hecs-332420 local]# ll | grep Test
-rw-r--r-- 1 root root 1715 Dec 9 17:20 Test.c
-rw-r--r-- 1 root root 227 Dec 9 17:20 Test.h
[root@hecs-332420 local]#
这里我把文件贴出来:
Test.h
/*
* Note: this file originally auto-generated by mib2c
* using mib2c.scalar.conf
*/
#ifndef TEST_H
#define TEST_H
/* function declarations */
void init_Test(void);
Netsnmp_Node_Handler handle_GetTime;
#endif /* TEST_H */
Test.c
/*
* Note: this file originally auto-generated by mib2c
* using mib2c.scalar.conf
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "Test.h"
/** Initializes the Test module */
void
init_Test(void)
{
const oid GetTime_oid[] = { 1,3,6,1,4,1,9527,1,1 };
DEBUGMSGTL(("Test", "Initializing\n"));
netsnmp_register_scalar(
netsnmp_create_handler_registration("GetTime", handle_GetTime,
GetTime_oid, OID_LENGTH(GetTime_oid),
HANDLER_CAN_RONLY
));
}
int
handle_GetTime(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
/* We are never called for a GETNEXT if it's registered as a
"instance", as it's "magically" handled for us. */
/* a instance handler also only hands us one request at a time, so
we don't need to loop over a list of requests; we'll only get one. */
switch(reqinfo->mode) {
case MODE_GET:
snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
/* XXX: a pointer to the scalar's data */,
/* XXX: the length of the data in bytes */);
break;
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
接下来我们只需要对.c文件做简单的添加即可,这儿我做的很简单,就是在GET方法中,获取本地时间再返回,附上我修改后的.c文件:
/*
* Note: this file originally auto-generated by mib2c
* using mib2c.scalar.conf
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "Test.h"
#include <time.h>
/** Initializes the Test module */
void
init_Test(void)
{
const oid GetTime_oid[] = { 1,3,6,1,4,1,9527,1,1 };
DEBUGMSGTL(("Test", "Initializing\n"));
netsnmp_register_scalar(
netsnmp_create_handler_registration("GetTime", handle_GetTime,
GetTime_oid, OID_LENGTH(GetTime_oid),
HANDLER_CAN_RONLY
));
}
int
handle_GetTime(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
/* We are never called for a GETNEXT if it's registered as a
"instance", as it's "magically" handled for us. */
/* a instance handler also only hands us one request at a time, so
we don't need to loop over a list of requests; we'll only get one. */
switch(reqinfo->mode) {
case MODE_GET:
{
time_t t;
time(&t);
char timeStr[100];
snprintf(timeStr, 100, ctime(&t));
snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
/* XXX: a pointer to the scalar's data */
timeStr,
/* XXX: the length of the data in bytes */
strlen(timeStr));
}
break;
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
!注意:我们也可以不使用 mib2c 工具进行转换 ,可以自己直接编写 .c 文件(当然格式需要正确)进行编译。
将我们自定义的MIB文件生成的源代码Test.c Test.h 加入到NET-SNMP源代码中,将 Test.c 和 Test.h 复制到/home/lijun/net-snmp-5.9.3/agent/mibgroup中,然后修改编译配置,将将 --with-mib-modules=“notification agentx” 修改为 --with-mib-modules=“Test”,再运行配置脚本 ./localBuild.sh,再make && make install,这里我不重复赘述,可以看我上一篇文章学习笔记一。
最后我们可以看见已经将我们的 init_Test()初始化函数放入到mib_module_inits.h文件中
[root@hecs-332420 net-snmp-5.9.3]# cat ./agent/mibgroup/mib_module_inits.h
/* This file is automatically generated by configure. Do not modify by hand. */
if (should_init("Test")) init_Test();
if (should_init("snmp_mib")) init_snmp_mib();
if (should_init("system_mib")) init_system_mib();
if (should_init("sysORTable")) init_sysORTable();
if (should_init("vacm_vars")) init_vacm_vars();
if (should_init("snmpEngine")) init_snmpEngine();
if (should_init("snmpMPDStats")) init_snmpMPDStats();
if (should_init("usmStats")) init_usmStats();
if (should_init("usmUser")) init_usmUser();
if (should_init("vacm_context")) init_vacm_context();
[root@hecs-332420 net-snmp-5.9.3]#
加载完成后,我们就可以测试自定义的命令是否生效了,首先启动snmpd服务
[root@hecs-332420 net-snmp-5.9.3]# cd ./out/sbin/
[root@hecs-332420 sbin]# ./snmpd -f -Lo -d
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 90: Warning: Unknown token: proc.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 92: Warning: Unknown token: proc.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 94: Warning: Unknown token: proc.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 104: Warning: Unknown token: disk.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 105: Warning: Unknown token: disk.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 106: Warning: Unknown token: includeAllDisks.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 116: Warning: Unknown token: load.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 143: Warning: Unknown token: iquerySecName.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 146: Warning: Unknown token: defaultMonitors.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 148: Warning: Unknown token: linkUpDownNotifications.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 160: Warning: Unknown token: extend.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 161: Warning: Unknown token: extend-sh.
/home/lijun/net-snmp-5.9.3/out/share/snmp/snmpd.conf: line 193: Warning: Unknown token: master.
Sending 44 bytes to UDP: [127.0.0.1]:162->[127.0.0.1]:53974
0000: 30 2A 02 01 00 04 06 70 75 62 6C 69 63 A4 1D 06 0*.....public...
0016: 0A 2B 06 01 04 01 BF 08 03 02 0A 40 04 C0 A8 00 .+.........@....
0032: EB 02 01 00 02 01 00 43 01 01 30 00 .......C..0.
NET-SNMP version 5.9.3
再新窗口运行snmpget,查看我们自定义的OID
[root@hecs-332420 bin]# ./snmpget -v 2c -c public localhost .1.3.6.1.4.1.9527.1.1.0
SNMPv2-SMI::enterprises.9527.1.1.0 = No Such Object available on this agent at this OID
[root@hecs-332420 bin]#
我们发现会提示报错,说找不到这个OID,实际上是因为我们的snmpd.conf中没有配置,先停止snmpd服务,在snmpd.conf配置文件中ACCESS CONTROL项增加我们自定义的OID,再重启我们snmpd服务
###############################################################################
#
# ACCESS CONTROL
#
# system + hrSystem groups only
view systemonly included .1.3.6.1.2.1.1
view systemonly included .1.3.6.1.2.1.25.1
#新增我们自定义OID
view systemonly included .1.3.6.1.4.1.9527.1.1
# Full access from the local host
#rocommunity public localhost
# Default access to basic system info
rocommunity public default -V systemonly
重新运行snmpget
[root@hecs-332420 sbin]# cd ../bin/
[root@hecs-332420 bin]# ./snmpget -v 2c -c public localhost .1.3.6.1.4.1.9527.1.1.0
SNMPv2-SMI::enterprises.9527.1.1.0 = STRING: "Fri Dec 9 17:49:53 2022
"
[root@hecs-332420 bin]# ./snmpget -v 2c -c public localhost MY-TEST-MIB::GetTime.0
Expected LAST-UPDATED (:): At line 8 in /home/lijun/net-snmp-5.9.3/out/share/snmp/mibs/MY-TEST-MIB.txt
MY-TEST-MIB::GetTime.0 = STRING: Fri Dec 9 17:50:14 2022
[root@hecs-332420 bin]#