使用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 进行网络设备管理

相关推荐
安科瑞刘鸿鹏13 分钟前
老旧小区用电安全保护装置#限流式防火保护器参数介绍#
运维·服务器·物联网·能源
好开心3315 分钟前
axios的使用
开发语言·前端·javascript·前端框架·html
ladymorgana22 分钟前
【运维笔记】windows 11 中提示:无法成功完成操作,因为文件包含病毒或潜在的垃圾软件。
运维·windows·笔记
Rain_Rong33 分钟前
linux检测硬盘
linux·运维·服务器
又蓝38 分钟前
使用 Python 操作 Excel 表格
开发语言·python·excel
小灰灰要减肥38 分钟前
装饰者模式
java
张铁铁是个小胖子1 小时前
MyBatis学习
java·学习·mybatis
余~~185381628001 小时前
稳定的碰一碰发视频、碰一碰矩阵源码技术开发,支持OEM
开发语言·人工智能·python·音视频
0zxm1 小时前
06 - Django 视图view
网络·后端·python·django
李昊哲小课1 小时前
deepin 安装 zookeeper
大数据·运维·zookeeper·debian·hbase