ページ更新: 2006-08-21 (月) (4655日前)

(2004-03-07 新規作成)

JNIの作例として、MAC Address の取得を行ってみる。 あわせて、build.xml(Ant)とMakefileの例も示す。

目次

[編集]

目的 #

  • JavaからJNIを使って、ホストが搭載しているNICのMAC Addressを取得する。
  • JNIの例を示す。
  • JNIを考慮したbuild.xml(Ant)とMakefileの例を示す。
[編集]

注意点 #

  • 対応OS はLinuxのみ
    • Windowsに対応させるにはWindows/Network Programming を使えばよいのだが、 未着手。
    • Solarisはそもそも環境がないので、未着手、未調査。 (Solaris 9/x86は手元にあるが、まだインストールしてないし、いじれるSPARC機は手元にないし)
  • 対応するJavaは J2SE 1.4以上
    • JNI側のコードでは、インターフェイス名(eth0,eth1..)からMAC Addressを求めている。 インターフェイス名はJ2SE 1.4の java.net.NetworkInterface を使って取得している。よって、J2SE 1.3以下では動作しない。
[編集]

環境 #

開発、動作確認環境。

OS
RedHat9 / Linux 2.4.20-20.9 (on VMware 4)
Java
sun J2SDK 1.4.1_05
Ant
apache Ant 1.5.4
プログラム作成(Java)
eclipse 2.1.1
プログラム作成(C++)
eclipse 2.1.1 + CDT

Linux上でのeclipseのインストール方法については eclipse/Linux を参照のこと。

[編集]

コンパイルから実行まで #

[編集]

ファイルと環境の確認 #

添付ファイル gethwaddr.tar.gzの中のファイル一覧。

$ ls
MacAddress.java  MacAddressFactoryLinux.cpp  Makefile  build.xml

環境

$ echo $JAVA_HOME
/usr/java/j2sdk1.4.1_05

$ echo $ANT_HOME
/usr/java/apache-ant-1.5.4

$ echo $PATH
/usr/bin:/bin:/usr/java/j2sdk1.4.1_05/bin:/usr/java/apache-ant-1.5.4/bin
                                                                                                            

環境変数CLASSPATHは設定していない。(CLASSPATH=.と同等。)

[編集]

Ant実行 #

Javaのコンパイル、C++ヘッダファイルの生成、C++コードのコンパイルを行う。 また、MAC Addressが取得できていて、ifconfigの出力と一致することを目視で確認する。

$ ant all
Buildfile: build.xml
 
compile:
    [javac] Compiling 1 source file to /home/username/workspace/gethwaddr
 
javah:
    [javah] [Search path = (中略) /home/username/workspace/gethwaddr]
    [javah] [Loaded /home/username/workspace/gethwaddr/MacAddress.class]
    [javah] [Loaded /usr/java/j2sdk1.4.1_05/jre/lib/rt.jar(java/lang/Object.class)]
    [javah] [Forcefully writing file /home/username/workspace/gethwaddr/MacAddress.h]
 
compile.jni:
     [exec] rm -f MacAddressFactoryLinux.o
     [exec] makedepend -Y -- -I. -- MacAddressFactoryLinux.cpp > /dev/null 2>&1
     [exec] g++    -c -o MacAddressFactoryLinux.o MacAddressFactoryLinux.cpp
     [exec] g++ -Wall -g -shared -o libmacaddress.so MacAddressFactoryLinux.o
     [exec] strip libmacaddress.so
     [exec] g++ -Wall -g -o getmac MacAddressFactoryLinux.o  -lm
     [exec] /sbin/ifconfig eth0 | awk "/HWaddr/{print \$5}"
     [exec] 00:0C:29:74:25:AC
     [exec] ./getmac eth0
     [exec] 00:0C:29:74:25:AC
 
all:
 
BUILD SUCCESSFUL
Total time: 7 seconds
[編集]

Java, JNIの動作確認 #

すべてのインターフェイスのMACアドレスを表示する。

$ LD_LIBRARY_PATH=. java  MacAddress
eth0=00:0C:29:74:25:AC
lo=00:00:00:00:00:00
[編集]

ソースコード #

以下のコードはPukiwikiへの掲載のために整形したもの。

オリジナルのコードはgethwaddr.tar.gz

[編集]

build.xml #

<?xml version="1.0" encoding="Shift_JIS"?>
<project name="MacAddress" default="all" basedir=".">

    <description>
        get MAC address from network interfaces
    </description>

    <property file="build.properties"/>

    <property name="src" value="."/>
    <property name="lib" value="."/>
    <property name="doc" value="."/>
    <property name="classes" value="."/>

    <property name="jniclassname" value="MacAddress"/>

    <path id="classpath">
        <fileset dir="${lib}">
            <include name="**/*.jar"/>
        </fileset>
    </path>

    <target name="all" description="build all targets."
     depends="compile,javah,compile.jni"/>

    <target name="clean" description="erase generated file">
        <delete>
            <fileset dir="${classes}" includes="**/*.class"/>
            <fileset dir="${basedir}" includes="*.h"/>
        </delete>
    </target>

    <target name="compile" description="compile java code">
        <javac srcdir="${src}" classpathref="classpath" destdir="${classes}"
         source="1.4" debug="on"/>
    </target>
    
    <target name="javah" description="create C/C++ header" depends="compile">
        <javah class="${jniclassname}" destdir="${basedir}" 
         force="Yes" verbose="Yes">
            <classpath>
                <path refid="classpath"/>
                <path location="${classes}"/>
            </classpath>
        </javah>
    </target>

    <target name="compile.jni" description="execute make" depends="javah">
        <exec executable="make" dir="${jniproject}" failonerror="yes">
            <arg line="clean depend all strip test"/>
        </exec>
    </target>

</project>
[編集]

Makefile #

JAVA_HOME = /usr/java/j2sdk1.4.1_05

INCLUDES_JAVA = \
 -I$(JAVA_HOME)/include \
 -I$(JAVA_HOME)/include/linux

INCLUDES_LOCAL = -I.

INCLUDES = $(INCLUDES_JAVA) $(INCLUDES_LOCAL)

CFLAGS = -Wall -g
LDFLAGS = -Wall -g

JNI_LDFLAGS = -Wall -g -shared

TARGET = libmacaddress.so

SRC = MacAddressFactoryLinux.cpp

OBJ = $(SRC:.cpp=.o)

LIBS =

all: $(TARGET)

$(TARGET) : $(OBJ)
    $(CXX) $(JNI_LDFLAGS) -o $@ $(OBJ) $(LIBS)

getmac : $(OBJ)
    $(CXX) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) -lm

clean:
    rm -f $(OBJ)

depend:
    makedepend -Y -- $(INCLUDES_LOCAL) -- $(SRC) > /dev/null 2>&1

strip:
    strip $(TARGET)

test: getmac
    /sbin/ifconfig eth0 | awk "/HWaddr/{print \$$5}"
    ./getmac eth0

# DO NOT DELETE THIS LINE -- make depend depends on it.

MacAddressFactoryLinux.o: MacAddress.h
[編集]

MacAddres.java #

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

/**
 * MAC Addressを格納するクラス。 
 */
public class MacAddress {

    /**
     * get MAC address (HWaddr) on specified interface.
     * 
     * @param ifname in interface name (eth0,..)
     * @param mac out MAC address
     * @return error code. 0:success !=0:failure
     */
    private static native int getmac(String ifname, byte[] mac);

    static {
        System.loadLibrary("macaddress");
    }

    /** MAC address byte[] length */
    private static final int MAC_LENGTH = 6;

    /** MAC address */
    private byte[] mac;

    /**
     * Factory method. Get MAC address instance by specified intarface.
     * 
     * @param iface Interface name
     * @return MacAddress instance
     * @throws IllegalStateException iface == null, or iface is not exist.
     */
    public static MacAddress getMacAddress(NetworkInterface iface)
        throws IllegalStateException
    {
        // check argument
        if (iface == null) {
            throw new IllegalArgumentException("iface == null");
        }

        // get mac address
        byte[] mac = new byte[MacAddress.MAC_LENGTH];
        int status = getmac(iface.getName(), mac);
        if (status < 0) {
            throw new IllegalArgumentException(
                "interface '" + iface.getName() + "' not exist.");
        }
        return new MacAddress(mac);
    }

    /**
     * コンストラクタ。
     * 
     * @param b MAC address
     * @throws IllegalArgumentException b=null, or b.length != MAC_LENGTH
     */
    private MacAddress(byte[] b) {
        // 引数の検査
        if (b == null) {
            throw new IllegalArgumentException("b == null");
        }

        // クラス不変条件の検査
        if (b.length != MAC_LENGTH) {
            throw new IllegalArgumentException("b.length != " + MAC_LENGTH);
        }

        // インスタンスに値を
        mac = new byte[MAC_LENGTH];
        System.arraycopy(b, 0, mac, 0, MAC_LENGTH);
    }

    /**
     * toString.
     * 
     * @return    string "XX:XX:XX:XX:XX:XX"
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append(byteToHexString(mac[0]));
        for (int i = 1; i < MAC_LENGTH; i++) {
            buf.append(":");
            buf.append(byteToHexString(mac[i]));
        }
        return buf.toString();
    }

    /**
     * byte value to hexString.
     * 
     * @param b byte-data
     * @return hexa-decimal string of byte b.
     */
    private String byteToHexString(byte b) {
        String s = "0" + Integer.toHexString(b & 0xFF);
        String hex = s.substring(s.length() - 2, s.length());
        return hex.toUpperCase();
    }

    public static void main(String[] args) {
        Enumeration en;
        try {
            en = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            e.printStackTrace();
            return;
        }

        try {
            System.out.println();    
            while (en.hasMoreElements()) {
                NetworkInterface iface = (NetworkInterface)en.nextElement();
                MacAddress mac = MacAddress.getMacAddress(iface);
                System.out.println(iface.getName() + "=" + mac);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}
[編集]

MacAddressFactoryLinux.cpp #

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_arp.h>

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <jni.h>

/*
 * if you want doc, use 'doxygen'
 */

/**
 * Show NIC's hardware address (=MAC Address).
 *
 * @param interface  set interface name (eth0, eth1,..)
 * @param mac  result. MAC Address
 * @return  status 0=success -1=error
 * @see  man ioctl_list
 * @see  man ioctl
 * @see  man socket
 */
static int
getmac(const char *interface, unsigned char mac[])
{
    int sd;
    struct ifreq ifr;

    /* make socket */
    sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sd < 0) {
        return -1; // error: can't create socket.
    }

    /* set interface name (lo, eth0, eth1,..) */
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_ifrn.ifrn_name, interface, IFNAMSIZ);

    /* get a Get Interface Hardware Address */
    ioctl(sd, SIOCGIFHWADDR, &ifr);

    close(sd);

    memcpy(mac, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6);
    return 0;
}

#include "MacAddress.h"
/*
 * Class:     MacAddress
 * Method:    getmac
 * Signature: (Ljava/lang/String;[B)I
 */
JNIEXPORT jint JNICALL Java_MacAddress_getmac
  (JNIEnv *env, jclass cls, jstring ifname, jbyteArray mac)
{
    const char *str = env->GetStringUTFChars(ifname, NULL);
    if (str == NULL) {
        return -1;    // can't allocate memory
    }

    unsigned char buf[6];
    int status = getmac(str, buf);
    env->ReleaseStringUTFChars(ifname, str);
    env->SetByteArrayRegion(mac, 0, 6, (jbyte*)buf);

    return status;
}

/**
 * for test
 */
int
main(int argc, char *argv[]) {
    char *interface;
  
    /* check commandline argument. */
    if (argc-1 == 0) {
        interface = "eth0";
    } else {
        interface = argv[1];
    }

    unsigned char mac[6];
    int status = getmac(interface, mac);
    if (status < 0) {
        printf("error: code=%d\n", status);
        return status;
    } else {
        printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
            mac[0] & 0xFF,
            mac[1] & 0xFF,
            mac[2] & 0xFF,
            mac[3] & 0xFF,
            mac[4] & 0xFF,
            mac[5] & 0xFF);
    }

    return EXIT_SUCCESS;
}
[編集]

その他 #

Java Technology Forums - determine MAC Address - some sample code to discuss Apr 19, 2002
http://forum.java.sun.com/thread.jsp?thread=245711&forum=4&message=1279708

  • ifconfig や nslookup などのコマンドを呼び出して取得する方法。

ブログ: 岡崎 - Jan 19 2006, 08:58:23 PM - 今日のAPI: JavaからネットワークカードのMACアドレスを取得する(Java SE 6)
http://blogs.sun.com/roller/page/okazaki/20060119