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

相关推荐
报错小能手几秒前
深入理解 Linux 虚拟内存管理
开发语言·操作系统
和沐阳学逆向23 分钟前
我现在怎么用 CC Switch 管中转站,顺手拿 Codex 举个例子
开发语言·javascript·ecmascript
小仙女的小稀罕24 分钟前
听不清重要会议录音急疯?这款常见AI工具听脑AI精准转译
开发语言·人工智能·python
mounter62527 分钟前
Linux 7.0 重磅更新:详解 nullfs 如何重塑根文件系统挂载与内核线程隔离
linux·运维·服务器·kernel
书到用时方恨少!30 分钟前
Python random 模块使用指南:从入门到精通
开发语言·python
NGC_661135 分钟前
Java 线程池:execute () 和 submit () 到底有什么区别?
java
cngm11037 分钟前
解决麒麟v10下tomcat无法自动启动的问题
java·tomcat
色空大师42 分钟前
【网站搭建实操(一)环境部署】
java·linux·数据库·mysql·网站搭建
客卿1231 小时前
牛客刷题--找数字-- 字符串检测-字符串 双指针
java·开发语言
烛之武1 小时前
SpringBoot基础
java·spring boot·后端