Must call RegisterNatives after getMethodID, else get UnsatisfiedLinkError
Kelly O'Hair
kelly.ohair at oracle.com
Wed Jun 9 12:52:50 PDT 2010
It's been a while since I worked with this stuff, but I would have
done a:
jclass cls = env->FindClass("Test");
Which, if I recall, FindClass actually initializes the class.
Just a stab in the dark.
-kto
On Jun 9, 2010, at 12:39 PM, Martin Buchholz wrote:
> David, Thanks for the bug archaeology. Judging by the other
> commenters, this bug is a regression from jdk5, and has cost many
> other users hours of frustrating debugging time, so I think an
> increase to P2 is in order. Since it used to work, it shouldn't be
> too hard to fix - all you need to do is to properly track registered
> native methods.
>
> Sorry for not doing this kind of research myself - it's just too
> painful without my beloved jbugs script :-(
>
> Here's an improved version of our test case, with
> -XX:+PrintJNIResolving output added,
> that you might add to the bug report.
>
> $ cat Test.java; echo ---------------------; cat jvm.cc; echo
> --------------------------; JDK=`jver 1.7.0-b96`; g++ -I$JDK/include
> -I$JDK/include/linux -ldl -DJDK=\"$JDK\" jvm.cc && ./a.out
> public class Test {
> public Test() {
> System.out.println("My class loader is " +
> getClass().getClassLoader());
> testNative();
> System.out.println("back to Java");
> }
> public static native void testNative();
> }
> ---------------------
> #include <cstdio>
> #include <cstdlib>
> #include <dlfcn.h>
> #include <jni.h>
>
> JavaVM* jvm;
> JNIEnv* env;
> const char* libjvm = JDK "/jre/lib/amd64/server/libjvm.so";
> const char* loader_url = "file://./";
> const char* class_name = "Test";
>
> #define countof(array) (sizeof(array)/sizeof(array[0]))
>
> void InitializeJVM() {
> JavaVMOption options[] = {
> { (char*)"-Djava.class.path=.", NULL },
> { (char*)"-XX:+PrintJNIResolving", NULL },
> //{ (char*)"-verbose:jni", NULL },
> };
>
> JavaVMInitArgs vm_args;
> vm_args.version = JNI_VERSION_1_2;
> vm_args.options = options;
> vm_args.nOptions = countof(options);
> vm_args.ignoreUnrecognized = JNI_TRUE;
>
> void* handle = dlopen(libjvm, RTLD_LAZY);
> if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); }
>
> jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) =
> reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)>
> (dlsym(handle, "JNI_CreateJavaVM"));
> if (!func_create_java_vm) { fprintf(stderr, "%s\n", dlerror());
> exit(1); }
>
> jint result = (*func_create_java_vm)(&jvm, (void**)(&env), &vm_args);
> }
>
> void TestNative(JNIEnv* env, jclass cls) {
> printf("Successfully called registered native method\n");
> }
>
> void RegisterNativeMethod(jclass cls) {
> static const JNINativeMethod jni_method =
> { (char*)"testNative",
> (char*)"()V",
> (void*)&TestNative };
> env->RegisterNatives(cls, &jni_method, 1);
> if (env->ExceptionCheck()) env->ExceptionDescribe();
> }
>
> void Test() {
> // URL[] urls = {new URL(url)}
> jclass cls = env->FindClass("java/net/URL");
> jmethodID method = env->GetMethodID(cls, "<init>", "(Ljava/lang/
> String;)V");
> jstring jurl_str = env->NewStringUTF(loader_url);
> jobject jurl = env->NewObject(cls, method, jurl_str);
> jobjectArray jurls = env->NewObjectArray(1, cls, jurl);
>
> // URLClassLoader loader = new URLClassLoaer(urls)
> cls = env->FindClass("java/net/URLClassLoader");
> method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V");
> jobject jloader = env->NewObject(cls, method, jurls);
>
> // Class cls = loader.loadClass(name)
> method = env->GetMethodID(
> cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;");
> jstring jname = env->NewStringUTF(class_name);
> cls = (jclass)env->CallObjectMethod(jloader, method, jname,
> (jboolean) true);
>
> // RegisterNatives must be called after GetMethodID.
> // If the order is reversed, we get UnsatisfiedLinkError,
> // which seems like a bug.
> RegisterNativeMethod(cls);
>
> method = env->GetMethodID(cls, "<init>", "()V");
> if (env->ExceptionCheck()) env->ExceptionDescribe();
>
> env->NewObject(cls, method);
> if (env->ExceptionCheck()) env->ExceptionDescribe();
> }
>
> int main(int argc, char** argv) {
> InitializeJVM();
> Test();
>
> return 0;
> }
> --------------------------
> [Dynamic-linking native method java.lang.Object.registerNatives ...
> JNI]
> [Registering JNI native method java.lang.Object.hashCode]
> [Registering JNI native method java.lang.Object.wait]
> [Registering JNI native method java.lang.Object.notify]
> [Registering JNI native method java.lang.Object.notifyAll]
> [Registering JNI native method java.lang.Object.clone]
> [Dynamic-linking native method java.lang.System.registerNatives ...
> JNI]
> [Registering JNI native method java.lang.System.currentTimeMillis]
> [Registering JNI native method java.lang.System.nanoTime]
> [Registering JNI native method java.lang.System.arraycopy]
> [Dynamic-linking native method java.lang.Thread.registerNatives ...
> JNI]
> [Registering JNI native method java.lang.Thread.start0]
> [Registering JNI native method java.lang.Thread.stop0]
> [Registering JNI native method java.lang.Thread.isAlive]
> [Registering JNI native method java.lang.Thread.suspend0]
> [Registering JNI native method java.lang.Thread.resume0]
> [Registering JNI native method java.lang.Thread.setPriority0]
> [Registering JNI native method java.lang.Thread.yield]
> [Registering JNI native method java.lang.Thread.sleep]
> [Registering JNI native method java.lang.Thread.currentThread]
> [Registering JNI native method java.lang.Thread.countStackFrames]
> [Registering JNI native method java.lang.Thread.interrupt0]
> [Registering JNI native method java.lang.Thread.isInterrupted]
> [Registering JNI native method java.lang.Thread.holdsLock]
> [Registering JNI native method java.lang.Thread.getThreads]
> [Registering JNI native method java.lang.Thread.dumpThreads]
> [Dynamic-linking native method
> java.security.AccessController.getStackAccessControlContext ... JNI]
> [Dynamic-linking native method
> java.security.AccessController.getInheritedAccessControlContext ...
> JNI]
> [Dynamic-linking native method
> java.lang.ClassLoader.registerNatives ... JNI]
> [Registering JNI native method
> java.lang.ClassLoader.retrieveDirectives]
> [Dynamic-linking native method
> java.security.AccessController.doPrivileged ... JNI]
> [Dynamic-linking native method java.lang.Class.registerNatives ...
> JNI]
> [Registering JNI native method java.lang.Class.getName0]
> [Registering JNI native method java.lang.Class.getSuperclass]
> [Registering JNI native method java.lang.Class.getInterfaces]
> [Registering JNI native method java.lang.Class.getClassLoader0]
> [Registering JNI native method java.lang.Class.isInterface]
> [Registering JNI native method java.lang.Class.getSigners]
> [Registering JNI native method java.lang.Class.setSigners]
> [Registering JNI native method java.lang.Class.isArray]
> [Registering JNI native method java.lang.Class.isPrimitive]
> [Registering JNI native method java.lang.Class.getComponentType]
> [Registering JNI native method java.lang.Class.getModifiers]
> [Registering JNI native method java.lang.Class.getDeclaredFields0]
> [Registering JNI native method java.lang.Class.getDeclaredMethods0]
> [Registering JNI native method
> java.lang.Class.getDeclaredConstructors0]
> [Registering JNI native method java.lang.Class.getProtectionDomain0]
> [Registering JNI native method java.lang.Class.setProtectionDomain0]
> [Registering JNI native method java.lang.Class.getDeclaredClasses0]
> [Registering JNI native method java.lang.Class.getDeclaringClass]
> [Registering JNI native method java.lang.Class.getGenericSignature]
> [Registering JNI native method java.lang.Class.getRawAnnotations]
> [Registering JNI native method java.lang.Class.getConstantPool]
> [Registering JNI native method
> java.lang.Class.desiredAssertionStatus0]
> [Registering JNI native method java.lang.Class.getEnclosingMethod0]
> [Dynamic-linking native method java.lang.Class.getPrimitiveClass ...
> JNI]
> [Dynamic-linking native method java.lang.System.initProperties ...
> JNI]
> [Dynamic-linking native method sun.misc.Unsafe.registerNatives ...
> JNI]
> [Registering JNI native method sun.misc.Unsafe.getLoadAverage]
> [Dynamic-linking native method
> java.lang.Throwable.fillInStackTrace ... JNI]
> [Registering JNI native method sun.misc.Unsafe.copyMemory]
> [Registering JNI native method sun.misc.Unsafe.setMemory]
> [Registering JNI native method sun.misc.Unsafe.getObject]
> [Registering JNI native method sun.misc.Unsafe.putObject]
> [Registering JNI native method sun.misc.Unsafe.getObjectVolatile]
> [Registering JNI native method sun.misc.Unsafe.putObjectVolatile]
> [Registering JNI native method sun.misc.Unsafe.getBoolean]
> [Registering JNI native method sun.misc.Unsafe.putBoolean]
> [Registering JNI native method sun.misc.Unsafe.getBooleanVolatile]
> [Registering JNI native method sun.misc.Unsafe.putBooleanVolatile]
> [Registering JNI native method sun.misc.Unsafe.getByte]
> [Registering JNI native method sun.misc.Unsafe.putByte]
> [Registering JNI native method sun.misc.Unsafe.getByteVolatile]
> [Registering JNI native method sun.misc.Unsafe.putByteVolatile]
> [Registering JNI native method sun.misc.Unsafe.getShort]
> [Registering JNI native method sun.misc.Unsafe.putShort]
> [Registering JNI native method sun.misc.Unsafe.getShortVolatile]
> [Registering JNI native method sun.misc.Unsafe.putShortVolatile]
> [Registering JNI native method sun.misc.Unsafe.getChar]
> [Registering JNI native method sun.misc.Unsafe.putChar]
> [Registering JNI native method sun.misc.Unsafe.getCharVolatile]
> [Registering JNI native method sun.misc.Unsafe.putCharVolatile]
> [Registering JNI native method sun.misc.Unsafe.getInt]
> [Registering JNI native method sun.misc.Unsafe.putInt]
> [Registering JNI native method sun.misc.Unsafe.getIntVolatile]
> [Registering JNI native method sun.misc.Unsafe.putIntVolatile]
> [Registering JNI native method sun.misc.Unsafe.getLong]
> [Registering JNI native method sun.misc.Unsafe.putLong]
> [Registering JNI native method sun.misc.Unsafe.getLongVolatile]
> [Registering JNI native method sun.misc.Unsafe.putLongVolatile]
> [Registering JNI native method sun.misc.Unsafe.getFloat]
> [Registering JNI native method sun.misc.Unsafe.putFloat]
> [Registering JNI native method sun.misc.Unsafe.getFloatVolatile]
> [Registering JNI native method sun.misc.Unsafe.putFloatVolatile]
> [Registering JNI native method sun.misc.Unsafe.getDouble]
> [Registering JNI native method sun.misc.Unsafe.putDouble]
> [Registering JNI native method sun.misc.Unsafe.getDoubleVolatile]
> [Registering JNI native method sun.misc.Unsafe.putDoubleVolatile]
> [Registering JNI native method sun.misc.Unsafe.getByte]
> [Registering JNI native method sun.misc.Unsafe.putByte]
> [Registering JNI native method sun.misc.Unsafe.getShort]
> [Registering JNI native method sun.misc.Unsafe.putShort]
> [Registering JNI native method sun.misc.Unsafe.getChar]
> [Registering JNI native method sun.misc.Unsafe.putChar]
> [Registering JNI native method sun.misc.Unsafe.getInt]
> [Registering JNI native method sun.misc.Unsafe.putInt]
> [Registering JNI native method sun.misc.Unsafe.getLong]
> [Registering JNI native method sun.misc.Unsafe.putLong]
> [Registering JNI native method sun.misc.Unsafe.getFloat]
> [Registering JNI native method sun.misc.Unsafe.putFloat]
> [Registering JNI native method sun.misc.Unsafe.getDouble]
> [Registering JNI native method sun.misc.Unsafe.putDouble]
> [Registering JNI native method sun.misc.Unsafe.getAddress]
> [Registering JNI native method sun.misc.Unsafe.putAddress]
> [Registering JNI native method sun.misc.Unsafe.allocateMemory]
> [Registering JNI native method sun.misc.Unsafe.reallocateMemory]
> [Registering JNI native method sun.misc.Unsafe.freeMemory]
> [Registering JNI native method sun.misc.Unsafe.objectFieldOffset]
> [Registering JNI native method sun.misc.Unsafe.staticFieldOffset]
> [Registering JNI native method sun.misc.Unsafe.staticFieldBase]
> [Registering JNI native method sun.misc.Unsafe.ensureClassInitialized]
> [Registering JNI native method sun.misc.Unsafe.arrayBaseOffset]
> [Registering JNI native method sun.misc.Unsafe.arrayIndexScale]
> [Registering JNI native method sun.misc.Unsafe.addressSize]
> [Registering JNI native method sun.misc.Unsafe.pageSize]
> [Registering JNI native method sun.misc.Unsafe.defineClass]
> [Registering JNI native method sun.misc.Unsafe.defineClass]
> [Registering JNI native method sun.misc.Unsafe.allocateInstance]
> [Registering JNI native method sun.misc.Unsafe.monitorEnter]
> [Registering JNI native method sun.misc.Unsafe.monitorExit]
> [Registering JNI native method sun.misc.Unsafe.tryMonitorEnter]
> [Registering JNI native method sun.misc.Unsafe.throwException]
> [Registering JNI native method sun.misc.Unsafe.compareAndSwapObject]
> [Registering JNI native method sun.misc.Unsafe.compareAndSwapInt]
> [Registering JNI native method sun.misc.Unsafe.compareAndSwapLong]
> [Registering JNI native method sun.misc.Unsafe.putOrderedObject]
> [Registering JNI native method sun.misc.Unsafe.putOrderedInt]
> [Registering JNI native method sun.misc.Unsafe.putOrderedLong]
> [Registering JNI native method sun.misc.Unsafe.park]
> [Registering JNI native method sun.misc.Unsafe.unpark]
> [Dynamic-linking native method java.lang.Float.floatToRawIntBits ...
> JNI]
> [Dynamic-linking native method
> java.lang.Double.doubleToRawLongBits ... JNI]
> [Dynamic-linking native method
> sun.reflect.Reflection.getCallerClass ... JNI]
> [Dynamic-linking native method java.lang.String.intern ... JNI]
> [Dynamic-linking native method java.lang.Object.getClass ... JNI]
> [Dynamic-linking native method java.lang.Class.forName0 ... JNI]
> [Dynamic-linking native method
> sun.reflect.Reflection.getClassAccessFlags ... JNI]
> [Dynamic-linking native method
> sun.reflect.NativeConstructorAccessorImpl.newInstance0 ... JNI]
> [Dynamic-linking native method sun.misc.VM.initialize ... JNI]
> [Dynamic-linking native method java.io.FileSystem.getFileSystem ...
> JNI]
> [Dynamic-linking native method java.io.UnixFileSystem.initIDs ... JNI]
> [Dynamic-linking native method java.lang.System.mapLibraryName ...
> JNI]
> [Dynamic-linking native method
> java.io.UnixFileSystem.getBooleanAttributes0 ... JNI]
> [Dynamic-linking native method
> java.io.UnixFileSystem.canonicalize0 ... JNI]
> [Dynamic-linking native method java.lang.ClassLoader
> $NativeLibrary.load ... JNI]
> [Dynamic-linking native method java.io.FileInputStream.initIDs ...
> JNI]
> [Dynamic-linking native method java.io.FileDescriptor.initIDs ... JNI]
> [Dynamic-linking native method java.io.FileOutputStream.initIDs ...
> JNI]
> [Dynamic-linking native method java.lang.System.setIn0 ... JNI]
> [Dynamic-linking native method java.lang.System.setOut0 ... JNI]
> [Dynamic-linking native method java.lang.System.setErr0 ... JNI]
> [Dynamic-linking native method sun.misc.Signal.findSignal ... JNI]
> [Dynamic-linking native method sun.misc.Signal.handle0 ... JNI]
> [Dynamic-linking native method java.lang.Runtime.maxMemory ... JNI]
> [Dynamic-linking native method
> java.lang.Compiler.registerNatives ... JNI]
> [Registering JNI native method java.lang.Compiler.compileClass]
> [Registering JNI native method java.lang.Compiler.compileClasses]
> [Registering JNI native method java.lang.Compiler.command]
> [Registering JNI native method java.lang.Compiler.enable]
> [Registering JNI native method java.lang.Compiler.disable]
> [Dynamic-linking native method java.lang.ClassLoader.getCaller ...
> JNI]
> [Dynamic-linking native method java.lang.ClassLoader
> $NativeLibrary.find ... JNI]
> [Dynamic-linking native method
> java.security.AccessController.doPrivileged ... JNI]
> [Dynamic-linking native method java.io.FileInputStream.open ... JNI]
> [Dynamic-linking native method java.io.FileInputStream.readBytes ...
> JNI]
> [Dynamic-linking native method java.io.FileInputStream.available ...
> JNI]
> [Dynamic-linking native method java.lang.reflect.Array.newArray ...
> JNI]
> [Dynamic-linking native method java.io.FileInputStream.close0 ... JNI]
> [Dynamic-linking native method java.io.UnixFileSystem.list ... JNI]
> [Dynamic-linking native method
> java.lang.ClassLoader.findLoadedClass0 ... JNI]
> [Dynamic-linking native method
> java.lang.ClassLoader.findBootstrapClass ... JNI]
> [Dynamic-linking native method
> java.security.AccessController.doPrivileged ... JNI]
> [Dynamic-linking native method java.io.UnixFileSystem.getLength ...
> JNI]
> [Dynamic-linking native method sun.misc.Perf.registerNatives ... JNI]
> [Registering JNI native method sun.misc.Perf.attach]
> [Registering JNI native method sun.misc.Perf.detach]
> [Registering JNI native method sun.misc.Perf.createLong]
> [Registering JNI native method sun.misc.Perf.createByteArray]
> [Registering JNI native method sun.misc.Perf.highResCounter]
> [Registering JNI native method sun.misc.Perf.highResFrequency]
> [Dynamic-linking native method
> java.lang.ClassLoader.defineClass1 ... JNI]
> [Dynamic-linking native method
> java.lang.ClassLoader.resolveClass0 ... JNI]
> [Registering JNI native method Test.testNative]
> [Dynamic-linking native method
> java.io.FileOutputStream.writeBytes ... JNI]
> My class loader is sun.misc.Launcher$AppClassLoader at b92d342
> Exception in thread "main" java.lang.UnsatisfiedLinkError:
> Test.testNative()V
> [Dynamic-linking native method
> java.lang.Throwable.getStackTraceDepth ... JNI]
> [Dynamic-linking native method
> java.lang.Throwable.getStackTraceElement ... JNI]
> at Test.testNative(Native Method)
> at Test.<init>(Test.java:4)
>
> Martin
>
> On Wed, Jun 9, 2010 at 03:25, David Holmes <David.Holmes at oracle.com>
> wrote:
>> Hi Martin,
>>
>> Turns out this is a known (and old) issue (thanks Yuri!):
>>
>> CR 6493522 "JNI_RegisterNatives fails to bind a method of an
>> uninitialized
>> class"
>>
>> I'll add this email info to the CR.
>>
>> David
>>
>> Martin Buchholz said the following on 06/09/10 09:12:
>>>
>>> Hi ClassLoader maintainers,
>>>
>>> This is a bug report.
>>>
>>> (I think this is a hotspot bug, but it might be a core library
>>> ClassLoader bug or even a URLClassLoader bug)
>>>
>>> If you use RegisterNatives with a class loaded using URLClassLoader,
>>> you must call GetMethodID
>>> before RegisterNatives, or you get an UnsatisfiedLinkError as in the
>>> test case below
>>>
>>> (you will need to edit the below to fill in the location of your jni
>>> include directory and your libjvm.so)
>>>
>>> $ cat Test.java; echo ---------------------; cat jvm.cc; echo
>>> --------------------------; g++ -I$JDK/include -I$JDK/include/linux
>>> -ldl jvm.cc && ./a.out
>>> public class Test {
>>> public Test() {
>>> System.out.println("My class loader is " +
>>> getClass().getClassLoader());
>>> testNative();
>>> System.out.println("back to Java");
>>> }
>>> public static native void testNative();
>>> }
>>> ---------------------
>>> #include <cstdio>
>>> #include <dlfcn.h>
>>> #include <jni.h>
>>>
>>> JavaVM* jvm;
>>> JNIEnv* env;
>>> const char* libjvm =
>>> "$JDKDIR/libjvm.so";
>>> const char* loader_url = "file://./";
>>> const char* class_name = "Test";
>>>
>>> void InitializeJVM() {
>>> JavaVMOption options[1];
>>> options[0].optionString = (char*)"-Djava.class.path=.";
>>> //options[1].optionString = (char*)"-verbose:jni";
>>>
>>> JavaVMInitArgs vm_args;
>>> vm_args.version = JNI_VERSION_1_2;
>>> vm_args.options = options;
>>> vm_args.nOptions = 2;
>>> vm_args.ignoreUnrecognized = JNI_TRUE;
>>>
>>> void* handle = dlopen(libjvm, RTLD_LAZY);
>>> if (handle == NULL) perror("dlopen");
>>> jint JNICALL (*func_create_java_vm)(JavaVM**, void**, void*) =
>>> reinterpret_cast<jint JNICALL (*)(JavaVM**, void**, void*)>
>>> (dlsym(handle, "JNI_CreateJavaVM"));
>>> if (func_create_java_vm == NULL) perror("dlsym");
>>> jint result = (*func_create_java_vm)(&jvm, (void**)(&env),
>>> &vm_args);
>>> }
>>>
>>> void TestNative(JNIEnv* env, jclass cls) {
>>> printf("Successfully called registered native method\n");
>>> }
>>>
>>> void RegisterNativeMethod(jclass cls) {
>>> static const JNINativeMethod jni_method =
>>> { (char*)"testNative",
>>> (char*)"()V",
>>> (void*)&TestNative };
>>> env->RegisterNatives(cls, &jni_method, 1);
>>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>>> }
>>>
>>> void Test() {
>>> // URL[] urls = {new URL(url)}
>>> jclass cls = env->FindClass("java/net/URL");
>>> jmethodID method = env->GetMethodID(cls, "<init>",
>>> "(Ljava/lang/String;)V");
>>> jstring jurl_str = env->NewStringUTF(loader_url);
>>> jobject jurl = env->NewObject(cls, method, jurl_str);
>>> jobjectArray jurls = env->NewObjectArray(1, cls, jurl);
>>>
>>> // URLClassLoader loader = new URLClassLoaer(urls)
>>> cls = env->FindClass("java/net/URLClassLoader");
>>> method = env->GetMethodID(cls, "<init>", "([Ljava/net/URL;)V");
>>> jobject jloader = env->NewObject(cls, method, jurls);
>>>
>>> // Class cls = loader.loadClass(name)
>>> method = env->GetMethodID(
>>> cls, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;");
>>> jstring jname = env->NewStringUTF(class_name);
>>> cls = (jclass)env->CallObjectMethod(jloader, method, jname,
>>> (jboolean)
>>> true);
>>>
>>> method = env->GetMethodID(cls, "<init>", "()V");
>>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>>>
>>> // RegisterNatives must be called after GetMethodID.
>>> // If the order is reversed, we get UnsatisfiedLinkError,
>>> // which seems like a bug.
>>> RegisterNativeMethod(cls);
>>>
>>> env->NewObject(cls, method);
>>> if (env->ExceptionCheck()) env->ExceptionDescribe();
>>> }
>>>
>>> int main(int argc, char** argv) {
>>> InitializeJVM();
>>> Test();
>>>
>>> return 0;
>>> }
>>> --------------------------
>>> My class loader is sun.misc.Launcher$AppClassLoader at 1f7182c1
>>> Successfully called registered native method
>>> back to Java
>>>
>>> Thanks,
>>>
>>> Martin
>>
More information about the net-dev
mailing list