技術メモ

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

Javaでsnmpwalk

Javaでsnmpwalkするソースを作成してみた。ちなみに、snmpwalkとは、内部でsnmp get-next-request を繰り返して、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: 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);
        try {
            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 = new OID(translateNameToOID(oidStr));

            // Get MIB data.
            TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory());
            List<TreeEvent> events = treeUtils.getSubtree(target, oid);
            if (events == null || events.size() == 0) {
                System.err.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());
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            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(args[++i].equals('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を使うと意外と簡単に書けますね~。