使用Java和SNMP4J实现SNMP操作

引言

SNMP(简单网络管理协议)是一种用于网络设备管理的标准协议。本文将介绍如何使用 Java 和 SNMP4J(一个开源的 SNMP 实现库)进行 SNMP 操作。我们将通过编写 SnmpUtil 类来演示如何进行 SNMP 初始化、创建 PDU、发送 SNMP 请求并处理响应。

环境准备

首先,确保您已经添加了 SNMP4J 依赖。可以通过 Maven 或 Gradle 进行依赖管理。

java 复制代码
<dependency>
    <groupId>org.snmp4j</groupId>
    <artifactId>snmp4j</artifactId>
    <version>2.8.6</version>
</dependency>
java 复制代码
implementation 'org.snmp4j:snmp4j:2.8.6'

SnmpUtil 类概述

以下是编写 SnmpUtil 类的整体结构。该类提供了静态方法来初始化 SNMP、创建 SNMP 目标、创建 PDU、发送 SNMP 请求和处理响应。

类的静态初始化

我们在这使用静态块来初始化 SNMP 对象,确保整个应用程序生命周期中只进行一次初始化。

java 复制代码
public class SnmpUtil {
    private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
    private static Snmp snmp = null;

    static {
        try {
            initSnmp();
        } catch (IOException e) {
            log.error("SNMP 初始化失败", e);
        }
    }

    private static synchronized void initSnmp() throws IOException {
        if (snmp == null) {
            MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
            messageDispatcher.addMessageProcessingModel(new MPv1());
            messageDispatcher.addMessageProcessingModel(new MPv2c());
            OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
            USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
            UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
                    PrivAES128.ID, new OctetString("privPassword"));
            usm.addUser(user.getSecurityName(), user);
            messageDispatcher.addMessageProcessingModel(new MPv3(usm));
            TransportMapping<?> transportMapping = new DefaultUdpTransportMapping();
            snmp = new Snmp(messageDispatcher, transportMapping);
            snmp.listen();
        }
    }
}

创建目标

编写createTarget 方法用于创建 SNMP 目标,支持 SNMP v1, v2c 和 v3 版本。

java 复制代码
private static Target createTarget(int version, String community, String ipAddress, int port) {
    Target target = null;
    if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
        log.error("参数version异常");
        return target;
    }
    if (version == SnmpConstants.version3) {
        target = new UserTarget();
        target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
        target.setSecurityName(new OctetString("SNMPV3"));
    } else {
        target = new CommunityTarget();
        ((CommunityTarget) target).setCommunity(new OctetString(community));
        if (version == SnmpConstants.version2c) {
            target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
        }
    }
    target.setVersion(version);
    target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
    target.setRetries(5);
    target.setTimeout(3000);
    return target;
}

创建 PDU

编写createPDU 方法用于创建 Protocol Data Unit (PDU),这是 SNMP 消息的基本构成单元。

java 复制代码
private static PDU createPDU(int version, int type, String oid) {
    PDU pdu = null;
    if (version == SnmpConstants.version3) {
        pdu = new ScopedPDU();
    } else {
        pdu = new PDUv1();
    }
    pdu.setType(type);
    pdu.add(new VariableBinding(new OID(oid)));
    return pdu;
}

private static PDU createPDU(int version, int type, List<String> oids) {
    PDU pdu = null;
    if (version == SnmpConstants.version3) {
        pdu = new ScopedPDU();
    } else {
        pdu = new PDU();
    }
    pdu.setType(type);
    for (String oid : oids) {
        pdu.add(new VariableBinding(new OID(oid)));
    }
    return pdu;
}

发送 SNMP 请求

编写snmpWalk 方法用于发送 GETNEXT 请求,并处理响应。

java 复制代码
public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
    try {
        Target target = createTarget(version, community, ipAddr, port);
        PDU pdu = createPDU(version, PDU.GETNEXT, oid);
        ResponseEvent responseEvent = snmp.send(pdu, target);
        PDU response = responseEvent.getResponse();
        return response;
    } catch (IOException e) {
        log.error("SNMP 发送请求失败", e);
        return new PDU();
    }
}

public static PDU snmpWalk(String ipAddr, int port, int version, String community, List<String> oids) {
    try {
        Target target = createTarget(version, community, ipAddr, port);
        PDU pdu = createPDU(version, PDU.GETNEXT, oids);
        ResponseEvent responseEvent = snmp.send(pdu, target);
        PDU response = responseEvent.getResponse();
        return response;
    } catch (IOException e) {
        log.error("SNMP 发送请求失败", e);
        return new PDU();
    }
}

处理 SNMP 响应

编写snmpWalkAll 和 snmpWalkSegment 方法用于处理连续的 SNMP 响应,直到到达 MIB 的末尾。

java 复制代码
public static List<VariableBinding> snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
    List<VariableBinding> results = new ArrayList<>();
    Target target = createTarget(version, community, ipAddr, port);
    PDU pdu = new PDU();
    pdu.add(new VariableBinding(new OID(startOid)));

    while (true) {
        try {
            pdu.setType(PDU.GETNEXT);
            ResponseEvent responseEvent = snmp.send(pdu, target);
            PDU response = responseEvent.getResponse();

            if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
                if (response.get(0).getVariable().toString().equals("endOfMibView")) {
                    break;
                }
                results.addAll(response.getVariableBindings());
                pdu.setRequestID(null); // Reset request ID for the next request
                pdu.remove(0); // Remove the old request
                pdu.add(response.get(0)); // Add the new request based on the last response
            } else {
                break;
            }
        } catch (IOException e) {
            log.error("SNMP 发送请求失败", e);
            break;
        }
    }
    return results;
}
// ...其他代码见完整代码

完整代码

java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class SnmpUtil {
    private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
    private static Snmp snmp = null; // 将共享资源的访问控制为静态初始化

    static {
        // 将 SNMP 对象的初始化放在静态块中,确保只初始化一次
        try {
            initSnmp();
        } catch (IOException e) {
            log.error("SNMP 初始化失败", e); // 记录初始化失败的异常信息
        }
    }

    private static synchronized void initSnmp() throws IOException { // 添加 synchronized 关键字保证线程安全
        if (snmp == null) {
            MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
            messageDispatcher.addMessageProcessingModel(new MPv1());
            messageDispatcher.addMessageProcessingModel(new MPv2c());
            OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
            USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
            UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
                    PrivAES128.ID, new OctetString("privPassword"));
            usm.addUser(user.getSecurityName(), user);
            messageDispatcher.addMessageProcessingModel(new MPv3(usm));
            TransportMapping<?> transportMapping = new DefaultUdpTransportMapping();
            snmp = new Snmp(messageDispatcher, transportMapping);
            snmp.listen();
        }
    }

    private static Target createTarget(int version, String community, String ipAddress, int port) {
        Target target = null;
        if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
            log.error("参数version异常"); // 保持错误日志记录
            return target;
        }
        if (version == SnmpConstants.version3) {
            target = new UserTarget();
            target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
            target.setSecurityName(new OctetString("SNMPV3"));
        } else {
            target = new CommunityTarget();
            ((CommunityTarget) target).setCommunity(new OctetString(community));
            if (version == SnmpConstants.version2c) {
                target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
            }
        }
        target.setVersion(version);
        target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
        target.setRetries(5);
        target.setTimeout(3000);
        return target;
    }

    /**
     * 创建 PDU 对象
     * @param version
     * @param type
     * @param oid
     * @return
     */
    private static PDU createPDU(int version, int type, String oid) {
        PDU pdu = null;
        if (version == SnmpConstants.version3) {
            pdu = new ScopedPDU();
        } else {
            pdu = new PDUv1();
        }
        pdu.setType(type);
        pdu.add(new VariableBinding(new OID(oid)));
        return pdu;
    }

    /**
     * 创建 PDU 对象
     * @param version
     * @param type
     * @param oids
     * @return
     */
    private static PDU createPDU(int version, int type, List<String> oids) {
        PDU pdu = null;
        if (version == SnmpConstants.version3) {
            pdu = new ScopedPDU();
        } else {
            pdu = new PDU();
        }
        pdu.setType(type);
        for (String oid : oids) {
            pdu.add(new VariableBinding(new OID(oid)));
        }
        return pdu;
    }

    /**
     * 获取 SNMP 响应
     * @param ipAddr
     * @param port
     * @param version
     * @param community
     * @param oid
     * @return
     */
    public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
        try {
            // 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
            Target target = createTarget(version, community, ipAddr, port);
            PDU pdu = createPDU(version, PDU.GETNEXT, oid); // 保持内部字符串不变
            ResponseEvent responseEvent = snmp.send(pdu, target);
            PDU response = responseEvent.getResponse();
            return response;
        } catch (IOException e) {
            log.error("SNMP 发送请求失败", e); // 增加异常日志记录
            return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
        }
    }

    /**
     * 获取 SNMP 响应
     * @param ipAddr
     * @param port
     * @param version
     * @param community
     * @param oids
     * @return
     */
    public static PDU snmpWalk(String ipAddr, int port, int version, String community, List<String> oids) {
        try {
            // 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
            Target target = createTarget(version, community, ipAddr, port);
            PDU pdu = createPDU(version, PDU.GETNEXT, oids); // 保持内部字符串不变
            ResponseEvent responseEvent = snmp.send(pdu, target);
            PDU response = responseEvent.getResponse();
            return response;
        } catch (IOException e) {
            log.error("SNMP 发送请求失败", e); // 增加异常日志记录
            return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
        }
    }

    /**
     * 获取 SNMP 响应
     * @param ipAddr
     * @param port
     * @param version
     * @param community
     * @param startOid
     * @return
     */
    public static List<VariableBinding> snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
        List<VariableBinding> results = new ArrayList<>();
        Target target = createTarget(version, community, ipAddr, port);
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(new OID(startOid)));

        while (true) {
            try {
                pdu.setType(PDU.GETNEXT);
                ResponseEvent responseEvent = snmp.send(pdu, target);
                PDU response = responseEvent.getResponse();

                if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
                    if (response.get(0).getVariable().toString().equals("endOfMibView")) {
                        break;
                    }
                    results.addAll(response.getVariableBindings());
                    pdu.setRequestID(null); // Reset request ID for the next request
                    pdu.remove(0); // Remove the old request
                    pdu.add(response.get(0)); // Add the new request based on the last response
                } else {
                    break;
                }
            } catch (IOException e) {
                log.error("SNMP 发送请求失败", e);
                break;
            }
        }
        return results;
    }

    /**
     * 获取 SNMP 响应
     * @param ipAddr
     * @param port
     * @param version
     * @param community
     * @param startOid
     * @return
     */
    public static List<VariableBinding> snmpWalkSegment(String ipAddr, int port, int version, String community, String startOid) {
        List<VariableBinding> results = new ArrayList<>();
        Target target = createTarget(version, community, ipAddr, port);
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(new OID(startOid)));

        while (true) {
            try {
                pdu.setType(PDU.GETNEXT);
                ResponseEvent responseEvent = snmp.send(pdu, target);
                PDU response = responseEvent.getResponse();

                if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
                    VariableBinding vb = response.get(0);
                    results.add(vb);

                    // Check if we have reached the end of the branch
                    if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
                        break;
                    }

                    pdu.setRequestID(null); // Reset request ID for the next request
                    pdu.remove(0); // Remove the old request
                    pdu.add(vb); // Add the new request based on the last response
                    System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
                } else {
                    break;
                }
            } catch (IOException e) {
                log.error("SNMP 发送请求失败", e);
                break;
            }
        }
        return results;
    }

    public static List<VariableBinding> snmpWalkSegment(String ipAddr, int port, int version, String community, List<String> startOids) {
        List<VariableBinding> results = new ArrayList<>();
        Target target = createTarget(version, community, ipAddr, port);

        for(String startOid: startOids){
            PDU pdu = new PDU();
            pdu.add(new VariableBinding(new OID(startOid)));
            while (true) {
                try {
                    pdu.setType(PDU.GETNEXT);
                    ResponseEvent responseEvent = snmp.send(pdu, target);
                    PDU response = responseEvent.getResponse();

                    if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
                        VariableBinding vb = response.get(0);
                        results.add(vb);

                        // Check if we have reached the end of the branch
                        if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
                            break;
                        }

                        pdu.setRequestID(null); // Reset request ID for the next request
                        pdu.remove(0); // Remove the old request
                        pdu.add(vb); // Add the new request based on the last response
                        System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
                    } else {
                        break;
                    }
                } catch (IOException e) {
                    log.error("SNMP 发送请求失败", e);
                    break;
                }
            }
        }

        return results;
    }


    public static void main(String[] args) {
        PDU response = SnmpUtil.snmpWalk("127.0.0.1", 161, SnmpConstants.version2c, "public", ".1");
        if (response != null && response.getErrorStatus() == 0) {
            for (VariableBinding vb : response.getVariableBindings()) {
                System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
            }
        } else {
            System.out.println("Error in response: " + response.getErrorStatusText());
        }
    }
}

总结

通过本文的讲解,您应该已经掌握了如何使用 Java 和 SNMP4J 库进行 SNMP 操作。我们介绍了 SnmpUtil 类的设计和实现,包括 SNMP 初始化、创建目标、创建 PDU、发送 SNMP 请求和处理响应等内容。希望这篇教程能够帮助您更好地理解和使用 SNMP4J 进行网络设备管理

相关推荐
知识分享小能手2 分钟前
Java学习教程,从入门到精通,Java 变量命名规则(12)
java·大数据·开发语言·python·学习·java学习·java后端开发
赶紧回家去4 分钟前
Ansible基本使用
运维·ansible
知识分享小能手4 分钟前
Java学习教程,从入门到精通,Java switch语句语法知识点(14)
java·开发语言·python·学习·javaee·大数据开发·java大数据
是程序喵呀6 分钟前
idea 创建java文件增加注释
java·ide·intellij-idea
Suckerbin6 分钟前
Open SSH服务配置
linux·运维·ssh
Maynor9968 分钟前
Nginx 迁移到 Caddy:一次完整的反向代理配置迁移实践
运维·nginx
花心蝴蝶.8 分钟前
Thread类及线程的核心操作
java·jvm·windows
我只会Traceroute8 分钟前
【渗透测试】01-信息收集-名词概念
网络·web安全·网络安全·渗透测试
苹果醋314 分钟前
springboot-springboot官方文档架构
java·运维·spring boot·mysql·nginx
爱就是恒久忍耐15 分钟前
CANopen中错误帧的制造和观测
网络·python·制造