闽公网安备 35020302035485号

SNMP架构主要由三个关键部分组成:网络管理系统(Network Management System,NMS)、代理(Agent)以及管理信息库(Management Information Base,MIB)。NMS 在网络管理中充当管理者的角色,通常运行在服务器上,负责向各个设备上的Agent发送管理请求,以查询或修改设备的参数值。同时,NMS也接收来自Agent主动发送的Trap信息,从而及时了解被管理设备的状态变化。
Agent则是驻留在被管理设备中的代理进程,其主要职责是维护设备的信息数据,并对NMS发送的请求做出响应,将操作结果反馈给 NMS。当设备发生故障或其他重要事件时,Agent会主动向NMS发送Trap信息。MIB则是网络设备上被 SNMP 管理的参数集合,它采用层次化的结构,类似于文件系统的目录结构,每个管理对象在MIB中都有其唯一的位置标识,即对象标识符(Object Identifier,OID)。通过OID,NMS和Agent能够准确地定位和操作特定的管理对象。
SNMP基于请求/响应模型进行工作。NMS通过向Agent发送不同类型的请求报文来实现对设备的管理操作。常见的请求类型包括 Get 请求,用于从Agent获取一个或多个参数值;Set请求,用于修改 Agent 上的一个或多个参数值;GetNext请求,通常用于遍历 MIB 中的表结构,获取下一个对象的值;GetBulk请求,适用于一次性获取大量数据,特别是在检索表格信息时能显著提高效率;Inform请求,Agent使用该请求向NMS发送通知,与Trap类似,但Inform请求需要NMS进行确认。
Agent在接收到NMS的请求后,会根据请求类型在MIB中查找或修改相应的管理对象,并将结果封装在响应报文中返回给 NMS。当设备出现特定事件(如接口状态改变、设备故障等)时,Agent会主动向NMS发送Trap报文,通知NMS设备端发生的情况。
SNMPv3:在安全性方面做了显著改进,支持用户级别的认证和加密。它提供了三种主要的安全功能:认证,用于确保消息的发送者身份真实可靠;加密,保护数据在传输过程中的机密性,防止数据被窃取;消息完整性,保证消息在传输过程中未被篡改。通过这些安全特性,SNMPv3能够更好地满足现代网络对安全性的要求,适用于对安全较为敏感的网络环境。
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.8.4</version>
</dependency>
application.yml配置示例:snmp:
target:
ip: 192.168.1.1
port: 161
version: v2c
community: public
timeout: 3000
retries: 2
trap:
listener-port: 162
配置类实现:@Configuration
@ConfigurationProperties(prefix = "snmp")
public class SnmpConfig {
private TargetConfig target;
private String version;
private String community;
private int timeout;
private int retries;
private TrapConfig trap;
// 堆代码 duidaima.com
// 内部静态类用于封装目标设备配置
public static class TargetConfig {
private String ip;
private int port;
// getters and setters
}
public static class TrapConfig {
private int listenerPort;
// getters and setters
}
// getters and setters
}
SNMP 客户端初始化@Configuration
public class SnmpClientConfig {
@Autowired
private SnmpConfig snmpConfig;
@Bean
public Snmp snmp() throws IOException {
// 创建传输层对象(UDP协议)
TransportMapping<?> transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen(); // 启动监听(用于接收响应)
return snmp;
}
@Bean
public Target snmpTarget() {
Address targetAddress = GenericAddress.parse(
"udp:" + snmpConfig.getTarget().getIp() + "/" + snmpConfig.getTarget().getPort()
);
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(snmpConfig.getCommunity()));
target.setAddress(targetAddress);
// 设置SNMP版本
if ("v3".equals(snmpConfig.getVersion())) {
target.setVersion(SnmpConstants.version3);
} elseif ("v2c".equals(snmpConfig.getVersion())) {
target.setVersion(SnmpConstants.version2c);
} else {
target.setVersion(SnmpConstants.version1);
}
target.setTimeout(snmpConfig.getTimeout()); // 超时时间(毫秒)
target.setRetries(snmpConfig.getRetries()); // 重试次数
return target;
}
}
Get 请求实现@Service
public class SnmpService {
@Autowired
private Snmp snmp;
@Autowired
private Target target;
/**
* 发送Get请求获取单个OID的值
* @param oid 目标对象标识符
* @return 解析后的结果值
*/
public String get(String oid) throws IOException {
// 创建Get请求PDU
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(oid)));
pdu.setType(PDU.GET);
// 发送请求并获取响应
ResponseEvent event = snmp.send(pdu, target);
PDU response = event.getResponse();
if (response == null) {
throw new RuntimeException("未收到SNMP响应");
} elseif (response.getErrorStatus() != PDU.noError) {
throw new RuntimeException(
"SNMP错误: " + response.getErrorStatusText() +
" (错误状态码: " + response.getErrorStatus() + ")"
);
}
// 解析响应结果
VariableBinding binding = response.get(0);
return binding.getVariable().toString();
}
/**
* 批量获取多个OID的值
* @param oids OID列表
* @return 以Map形式返回OID与对应值的映射
*/
public Map<String, String> getBulk(List<String> oids) throws IOException {
PDU pdu = new PDU();
for (String oid : oids) {
pdu.add(new VariableBinding(new OID(oid)));
}
pdu.setType(PDU.GETBULK);
pdu.setMaxRepetitions(5); // 设置批量获取的最大重复次数
ResponseEvent event = snmp.send(pdu, target);
PDU response = event.getResponse();
// 响应处理逻辑(省略,类似单个Get请求)
Map<String, String> result = new HashMap<>();
for (VariableBinding binding : response.getVariableBindings()) {
result.put(binding.getOid().toString(), binding.getVariable().toString());
}
return result;
}
}
Set 请求实现/**
* 发送Set请求修改设备参数
* @param oid 目标OID
* @param value 要设置的新值
* @return 是否设置成功
*/
public boolean set(String oid, String value) throws IOException {
PDU pdu = new PDU();
Variable variable = new OctetString(value);
pdu.add(new VariableBinding(new OID(oid), variable));
pdu.setType(PDU.SET);
ResponseEvent event = snmp.send(pdu, target);
PDU response = event.getResponse();
return response != null && response.getErrorStatus() == PDU.noError;
}
Trap 监听与处理@Slf4j
@Component
public class SnmpTrapReceiver implements ApplicationRunner, CommandResponder {
@Override
public void processPdu(CommandResponderEvent commandResponderEvent) {
try{
Map<String,Object> requestMap = new HashMap<>();
if (commandResponderEvent.getPDU().getType() == PDU.TRAP || commandResponderEvent.getPDU().getType() == PDU.V1TRAP) {
PDU pdu=commandResponderEvent.getPDU();
if (pdu != null) {
Vector<? extends VariableBinding> resVBs = pdu.getVariableBindings();
for (int i = 0; i < resVBs.size(); i++) {
VariableBinding recVB = resVBs.elementAt(i);
String oid = recVB.getOid().toString();
Variable variable = recVB.getVariable();
String valueStr = "";
if(variable instanceof OctetString){
OctetString octetString = (OctetString) variable;
valueStr = StrUtil.utf8Str(octetString.getValue());
}elseif (variable instanceof Gauge32) {
Gauge32 gauge32 = (Gauge32) variable;
valueStr = String.valueOf(gauge32.getValue());
}elseif (variable instanceof Integer32) {
Integer32 integer32 = (Integer32) variable;
valueStr = String.valueOf(integer32.getValue());
}else {
valueStr = variable.toString();
}
log.info("oid:" + oid + " value:" + valueStr);
}
}
}
}catch (Exception e){
log.error("处理Trap信息异常,异常信息为:{}", e.getMessage());
}
}
@Override
public void run(ApplicationArguments args){
try {
ThreadPool threadPool = ThreadPool.create("snmptrap", 10);
MultiThreadedMessageDispatcher dispatcher = new MultiThreadedMessageDispatcher(threadPool, new MessageDispatcherImpl());
Address listenAddress = GenericAddress.parse(System.getProperty("snmp4j.listenAddress", "udp:192.168.1.1/162"));
TransportMapping transport;
// 对TCP与UDP协议进行处理
if (listenAddress instanceof UdpAddress) {
transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
log.info("使用UDP协议");
} else {
transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
log.info("使用TCP协议");
}
Snmp snmp = new Snmp(dispatcher, transport);
snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());
USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
snmp.listen();
snmp.addCommandResponder(this);
log.info("开始监听Trap信息");
} catch (IOException e) {
log.info("监听Trap信息异常,异常信息为:{}", e.getMessage());
}
}
}
安全配置(SNMPv3)public Target createV3Target() {
UserTarget target = new UserTarget();
target.setAddress(GenericAddress.parse("udp:192.168.1.1/161"));
target.setVersion(SnmpConstants.version3);
target.setSecurityLevel(SecurityLevel.AUTH_PRIV); // 认证并加密
target.setSecurityName(new OctetString("admin")); // 用户名
return target;
}
// 初始化SNMPv3用户
@Bean
public void initV3User() throws IOException {
USM usm = new USM(SecurityProtocols.getInstance(),
new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
// 添加用户(认证密码、加密密码)
snmp.getUSM().addUser(
new OctetString("admin"),
new UsmUser(
new OctetString("admin"),
AuthMD5.ID, new OctetString("authPass123"),
PrivDES.ID, new OctetString("privPass123")
)
);
}