ITエンジニアの技術メモ

神奈川在住のITエンジニアの備忘録です。主にプログラミング(Perl, Java など)やネットワーク技術について、自分の中で整理するためにゆるゆると書いています。誰かのご参考になれば幸いです。

Javaでsnmpwalk

Javaでsnmpwalkするソースを作成した。snmpwalkとは、内部でsnmp get-nextを繰り返して、SNMPエージェントからMIBツリー配下のMIBを次々取ること。
SNMPの通信のところを一から作るのは難しいので、snmp4j ライブラリを使用している。なので、前提として snmp4j ライブラリが必要となる。

実行方法は、ソースのusageにある通り、「java snmpwalk [-c communityName -p portNumber -v snmpVersion(1 or 2)] targetAddr oid」となる。
実行例: java snmpwalk -c public -v 2 192.168.0.1 .1.3.6.1.2.1.1

import java.io.IOException;
import java.util.List;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
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 org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TreeUtils;
import org.snmp4j.util.TreeEvent;

public class Snmpwalk {
    private String targetAddr;
    private String oidStr;
    private String commStr;
    private int snmpVersion;
    private String portNum;
    private String usage;

    Snmpwalk() {
        // Set default value.
        targetAddr = null;
        oidStr = null;
        commStr = "public";
        snmpVersion = SnmpConstants.version2c;
        portNum =  "161";
        usage = "Usage: java snmpwalk [-c communityName -p portNumber -v snmpVersion(1 or 2)] targetAddr oid";
    }

    private void execSnmpwalk() throws IOException {
        Address targetAddress = GenericAddress.parse("udp:"+ targetAddr + "/" + portNum);
        TransportMapping<? extends Address> transport = new DefaultUdpTransportMapping();
        Snmp snmp = new Snmp(transport);
        transport.listen();

        // setting up target
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(commStr));
        target.setAddress(targetAddress);
        target.setRetries(3);
        target.setTimeout(1000 * 3);
        target.setVersion(snmpVersion);
        OID oid;
        try {
            oid = new OID(translateNameToOID(oidStr));
        } catch (Exception e) {
            System.err.println("Can't parse the OID or object name.");
            throw e;
        }

        // Get MIB data.
        TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory());
        List<TreeEvent> events = treeUtils.getSubtree(target, oid);
        if(events == null || events.size() == 0) {
            System.out.println("No result returned.");
            System.exit(1);
        }

        // Handle the snmpwalk result.
        for (TreeEvent event : events) {
            if(event == null) {
                continue;
            }
            if (event.isError()) {
                System.err.println("oid [" + oid + "] " + event.getErrorMessage());
                continue;
            }

            VariableBinding[] varBindings = event.getVariableBindings();
            if(varBindings == null || varBindings.length == 0) {
                continue;
            }
            for (VariableBinding varBinding : varBindings) {
                if (varBinding == null) {
                    continue;
                }
                System.out.println(
                    varBinding.getOid() +
                            " : " +
                    varBinding.getVariable().getSyntaxString() +
                            " : " +
                    varBinding.getVariable());
            }
        }
        snmp.close();
    }

    private String translateNameToOID(String oidStr) {
        switch (oidStr) {
            case "mib-2":
                oidStr = ".1.3.6.1.2.1";
                break;
            case "mib2":
                oidStr = ".1.3.6.1.2.1";
                break;
            case "system":
                oidStr = ".1.3.6.1.2.1.1";
                break;
            case "interfaces":
                oidStr = ".1.3.6.1.2.1.2";
                break;
            case "at":
                oidStr = ".1.3.6.1.2.1.3";
                break;
            case "ip":
                oidStr = ".1.3.6.1.2.1.4";
                break;
            case "icmp":
                oidStr = ".1.3.6.1.2.1.5";
                break;
            case "tcp":
                oidStr = ".1.3.6.1.2.1.6";
                break;
            case "udp":
                oidStr = ".1.3.6.1.2.1.7";
                break;
            case "egp":
                oidStr = ".1.3.6.1.2.1.8";
                break;
            case "transmission":
                oidStr = ".1.3.6.1.2.1.10";
                break;
            case "snmp":
                oidStr = ".1.3.6.1.2.1.11";
                break;
        }
        return oidStr;
    }

    private void setArgs(String[] args) {
        if(args.length < 2) {
            System.err.println(usage);
            System.exit(1);
        }

        for (int i=0; i<args.length; i++) {
            if("-c".equals(args[i])) {
                commStr = args[++i];
            }
            else if ("-v".equals(args[i])) {
                if(Integer.parseInt(args[++i]) == 1) {
                    snmpVersion = SnmpConstants.version1;
                }
                else {
                    snmpVersion = SnmpConstants.version2c;
                }
            }
            else if ("-p".equals(args[i])) {
                portNum = args[++i];
            }
            else{
                targetAddr = args[i++];
                oidStr = args[i];
            }
        }
        if(targetAddr == null || oidStr == null) {
            System.err.println(usage);
            System.exit(1);
        }
    }

    // Delegate main function to Snmpwalk.
    public static void main(String[] args) {
        Snmpwalk snmpwalk = new Snmpwalk();
        try{
            snmpwalk.setArgs(args);
            snmpwalk.execSnmpwalk();
        }
        catch(Exception e) {
            System.err.println("----- An Exception happened as follows. Please confirm the usage etc. -----");
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

最初に引数チェックを行い、引数チェックOKであれば、snmpwalkを実施し(getSubtree()を発行し)、その後、SNMPエージェントから帰ってきたMIBの OID、型、値を次々と表示している。
利便性を考慮し、MIB2配下の幾つかのMIBついては、OIDのテキスト表記でも対応できるようにした。(translateNameToOIDメソッド)

こういった処理はsnmp4jを使うと意外と簡単に書けますね~。