技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

JavaでSNMPトラップ送信

JavaSNMPトラップを送信するコードを作成した。snmp4j (https://www.snmp4j.org/) を使用しているので、コンパイル&実行には snmp4j-x.x.x.jar が必要。(実機検証は Java1.8 + snmp4j-2.6.2.jar で実施した)


実行方法
java TrapSender [-v {1|2c|2}] [-a agent-addr] [-o trapOid] [-e enterpriseID] destHost


実行例 (SNMPv1 トラップ送信)
java TrapSender -v 1 -a 192.168.0.1 -e .1.2.3.4.5.6 192.168.0.2

実行例 (SNMPv2c トラップ送信)
java TrapSender -v 2c -o .1.2.3.4.5.6 192.168.0.2

import org.snmp4j.*;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;

public class TrapSender {

    // Set default value.
    private String destAddr = "127.0.0.1";
    private String agentAddr = "127.0.0.1";
    private int destPort = 162;
    private String trapOid = ".1.2.3.4.5";
    private String enterpriseId = ".1.2.3.4.5";
    private String commName = "public";
    private int snmpVersion = SnmpConstants.version2c;
    private int varBindNum = 3;

    void sendTrap () throws IOException {
        if (snmpVersion == SnmpConstants.version1) {
            sendV1Trap();
        }
        else {
            if (snmpVersion == SnmpConstants.version2c) {
                sendV2cTrap();
            }
        }
    }

    void sendV1Trap () throws IOException {

        // Construct a trap pdu.
        PDUv1 trapPdu = new PDUv1();
        trapPdu.setType(PDU.V1TRAP);
        trapPdu.setEnterprise(new OID(enterpriseId));
        trapPdu.setGenericTrap(PDUv1.ENTERPRISE_SPECIFIC);
        trapPdu.setSpecificTrap(1);
        trapPdu.setAgentAddress(new IpAddress(agentAddr));
        addVarBindsTo(trapPdu, varBindNum);

        // Specify a target(dest info).
        CommunityTarget target = createTarget(commName, SnmpConstants.version1, destAddr, destPort);

        // Send a snmp trap.
        System.out.println("Sending an SNMPv1 trap to " + destAddr + " ...");
        sendPdu(trapPdu, target);
    }

    void sendV2cTrap () throws IOException {

        // Construct a trap pdu.
        PDU trapPdu = new PDU();
        trapPdu.setType(PDU.NOTIFICATION);
        trapPdu.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(1000)));
        trapPdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID(trapOid)));
        addVarBindsTo(trapPdu, varBindNum);

        // Specify a target(dest info).
        CommunityTarget target = createTarget(commName, SnmpConstants.version2c, destAddr, destPort);

        // Send a snmp trap.
        System.out.println("Sending an SNMPv2c trap to " + destAddr + " ...");
        sendPdu(trapPdu, target);
    }

    private CommunityTarget createTarget(String commName,
                                         int snmpVersion,
                                         String destIpAddr,
                                         int destPort) throws IOException {
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(commName));
        target.setVersion(SnmpConstants.version2c);
        target.setAddress(new UdpAddress(destIpAddr + "/" + destPort));
        return target;
    }

    private void sendPdu (PDU trapPdu, CommunityTarget target) throws IOException {
        TransportMapping transport = new DefaultUdpTransportMapping();
        Snmp snmp = null;
        try {
            transport.listen();
            snmp = new Snmp(transport);
            snmp.send(trapPdu, target);
            snmp.close();
        } catch (IOException ex) {
            throw ex;
        } finally {
            snmp.close();
        }
    }

    private void addVarBindsTo (PDU trapPdu, int varBindNum) {
        if (trapPdu == null || varBindNum == 0) {
            return;
        }
        for (int i=1; i<=varBindNum; i++) {
            String oid = ".1.2.3.4.5." + i;
            String val = "val" + i;
            trapPdu.add(new VariableBinding(new OID(oid), new OctetString(val)));
        }
    }

    void setParams(String[] args) throws IllegalArgumentException {

        for (int i=0; i<args.length; i++) {
            if ("-v".equals(args[i])) {
                i++;
                if ("1".equals(args[i])) {
                    snmpVersion = SnmpConstants.version1;
                }
                else if ("2".equals(args[i]) || "2c".equals(args[i])) {
                    snmpVersion = SnmpConstants.version2c;
                }
                else {
                    throw new IllegalArgumentException("Specified SNMP version is invalid...");
                }
            }
            if ("-a".equals(args[i])) {
                i++;
                if (!isValidIpv4Addr(args[i])) {
                    throw new IllegalArgumentException("Specified agent address is invalid...");
                }
                agentAddr = args[i];
            }
            if ("-o".equals(args[i])) {
                i++;
                if (!isValidOid(args[i])) {
                    throw new IllegalArgumentException("Specified trap oid is invalid...");
                }
                trapOid = args[i];
            }
            if ("-e".equals(args[i])) {
                i++;
                if (!isValidOid(args[i])) {
                    throw new IllegalArgumentException("Specified enterprise id is invalid...");
                }
                enterpriseId = args[i];
            }
            else{
                destAddr = args[i];
            }
        }
        if(destAddr == null) {
            throw new IllegalArgumentException("Trap dest is not specified...");
        }
    }

    private boolean isValidIpv4Addr(String addrStr) {
        String[] octets = addrStr.split("\\.");
        if (octets.length != 4) {
            return false;
        }
        for (String octet : octets) {
            int octetNum;
            try {
                octetNum = Integer.parseInt(octet);
            }
            catch (NumberFormatException ex) {
                return false;
            }
            if (octetNum < 0 || 255 < octetNum) {
                return false;
            }
        }
        return true;
    }

    private boolean isValidOid(String oidStr) {
        final int SUB_ID_CNT_MAX = 128;
        final double SUB_ID_MAX = Math.pow(2,32)-1;
        if (oidStr.startsWith(".")) {
            oidStr = oidStr.substring(1);
        }
        String[] subIds = oidStr.split("\\.");
        if (SUB_ID_CNT_MAX < subIds.length) {
            return false;
        }
        for (int i=0; i<subIds.length; i++) {
            int subIdNum;
            try {
                subIdNum = Integer.parseInt(subIds[i]);
            }
            catch (NumberFormatException ex) {
                return false;
            }
            if (i == 0) {
                if (subIdNum != 0 && subIdNum != 1 && subIdNum != 2) {
                    return false;
                }
            }
            if (subIdNum < 0 || SUB_ID_MAX < subIdNum) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        TrapSender trapsender = new TrapSender();
        try {
            trapsender.setParams(args);
            trapsender.sendTrap();
        } catch (IOException ex) {
            System.err.println("Exception happened while sending an snmp trap...");
            System.err.println(ex.getMessage());
            ex.printStackTrace();
            System.exit(1);
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            ex.printStackTrace();
            System.exit(1);
        }
    }
}

上記で送信したSNMPトラップをwiresharkで見て、意図した内容になっていたので問題ないはず。
現時点では、トラップに含まれるvarbindなど、ハードコードしている部分もあるので、今後はそれらも引数で変更できるようエンハンスしたい。