<!-- Let eclipse know about the generated sources -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>1.8</version>
<executions>
<execution>
<phase>generate-sources</phase>
<artifactId>maven-release-plugin</artifactId>
<version>${maven.release.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ </plugin>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<module>programming</module>
<module>rsvp</module>
<module>topology</module>
+ <module>tcp-md5</module>
<!-- Integration tests -->
<module>integration-tests</module>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<projectDescription>
+ <name>tcpmd5-parent</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<projectDescription>
+ <name>tcpmd5-core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>tcpmd5-parent</artifactId>
+ <version>0.3.1-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>tcpmd5-core</artifactId>
+ <description>RFC2385-enable sockets</description>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-jni</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <!-- Testing dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ org.opendaylight.bgpcep.tcpmd5
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <distributionManagement>
+ <site>
+ <id>${project.artifactId}</id>
+ <name>TCPMD5-CORE Module site</name>
+ <url>${basedir}/target/site/${project.artifactId}</url>
+ </site>
+ </distributionManagement>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5;
+
+import java.io.IOException;
+import java.net.SocketOption;
+import java.nio.channels.NetworkChannel;
+import java.util.Set;
+
+import org.opendaylight.bgpcep.tcpmd5.jni.KeyAccess;
+import org.opendaylight.bgpcep.tcpmd5.jni.NativeKeyAccess;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * Channel option wrapper which unifies the underlying NetworkChannel with
+ * the TCP MD5 Signature option if the channel is supported by the JNI library.
+ */
+final class MD5ChannelOptions {
+ private static final Set<SocketOption<?>> MD5_OPTIONS = ImmutableSet.<SocketOption<?>>of(MD5SocketOptions.TCP_MD5SIG);
+ private final NetworkChannel ch;
+ private final KeyAccess access;
+
+ private MD5ChannelOptions(final NetworkChannel ch, final KeyAccess access) {
+ this.ch = Preconditions.checkNotNull(ch);
+ this.access = access;
+ }
+
+ static MD5ChannelOptions create(final NetworkChannel ch) {
+ final KeyAccess access = NativeKeyAccess.create(Preconditions.checkNotNull(ch));
+ return new MD5ChannelOptions(ch, access);
+ }
+
+ public <T> T getOption(final SocketOption<T> name) throws IOException {
+ if (access != null && name.equals(MD5SocketOptions.TCP_MD5SIG)) {
+ @SuppressWarnings("unchecked")
+ final T key = (T)access.getKey();
+ return key;
+ }
+
+ return ch.getOption(name);
+ }
+
+ public <T> void setOption(final SocketOption<T> name, final T value) throws IOException {
+ if (access != null && name.equals(MD5SocketOptions.TCP_MD5SIG)) {
+ access.setKey((byte[])value);
+ } else {
+ ch.setOption(name, value);
+ }
+ }
+
+ public Set<SocketOption<?>> supportedOptions() {
+ if (access != null) {
+ return Sets.union(MD5_OPTIONS, ch.supportedOptions());
+ } else {
+ return ch.supportedOptions();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.nio.channels.ServerSocketChannel;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * {@link ServerSocketChannel} augmented with support for TCP MD5 Signature
+ * option.
+ */
+public final class MD5ServerSocketChannel extends ServerSocketChannel {
+ private final ServerSocketChannel inner;
+ private final MD5ChannelOptions options;
+
+ public MD5ServerSocketChannel(final ServerSocketChannel inner) {
+ super(inner.provider());
+ this.inner = Preconditions.checkNotNull(inner);
+ this.options = MD5ChannelOptions.create(inner);
+ }
+
+ public static MD5ServerSocketChannel open() throws IOException {
+ return new MD5ServerSocketChannel(ServerSocketChannel.open());
+ }
+
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ return inner.getLocalAddress();
+ }
+
+ @Override
+ public <T> T getOption(final SocketOption<T> name) throws IOException {
+ return options.getOption(name);
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return options.supportedOptions();
+ }
+
+ @Override
+ public ServerSocketChannel bind(final SocketAddress local, final int backlog) throws IOException {
+ inner.bind(local, backlog);
+ return this;
+ }
+
+ @Override
+ public <T> ServerSocketChannel setOption(final SocketOption<T> name, final T value) throws IOException {
+ options.setOption(name, value);
+ return this;
+ }
+
+ @Override
+ public ServerSocket socket() {
+ // FIXME: provider a wrapper
+ return inner.socket();
+ }
+
+ @Override
+ public MD5SocketChannel accept() throws IOException {
+ return new MD5SocketChannel(inner.accept());
+ }
+
+ @Override
+ protected void implCloseSelectableChannel() throws IOException {
+ inner.close();
+ }
+
+ @Override
+ protected void implConfigureBlocking(final boolean block) throws IOException {
+ inner.configureBlocking(block);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Set;
+
+/**
+ * {@link SocketChannel} augmented with support for TCP MD5 Signature option.
+ */
+public final class MD5SocketChannel extends SocketChannel {
+ private final MD5ChannelOptions options;
+ private final SocketChannel inner;
+
+ public MD5SocketChannel(final SocketChannel inner) {
+ super(inner.provider());
+ options = MD5ChannelOptions.create(inner);
+ this.inner = inner;
+ }
+
+ @Override
+ public SocketAddress getLocalAddress() throws IOException {
+ return inner.getLocalAddress();
+ }
+
+ @Override
+ public <T> T getOption(final SocketOption<T> name) throws IOException {
+ return options.getOption(name);
+ }
+
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return options.supportedOptions();
+ }
+
+ @Override
+ public SocketChannel bind(final SocketAddress local) throws IOException {
+ inner.bind(local);
+ return this;
+ }
+
+ @Override
+ public <T> SocketChannel setOption(final SocketOption<T> name, final T value) throws IOException {
+ options.setOption(name, value);
+ return this;
+ }
+
+ @Override
+ public SocketChannel shutdownInput() throws IOException {
+ inner.shutdownInput();
+ return this;
+ }
+
+ @Override
+ public SocketChannel shutdownOutput() throws IOException {
+ inner.shutdownOutput();
+ return this;
+ }
+
+ @Override
+ public Socket socket() {
+ // FIXME: provide a wrapper
+ return inner.socket();
+ }
+
+ @Override
+ public boolean isConnected() {
+ return inner.isConnected();
+ }
+
+ @Override
+ public boolean isConnectionPending() {
+ return inner.isConnectionPending();
+ }
+
+ @Override
+ public boolean connect(final SocketAddress remote) throws IOException {
+ return inner.connect(remote);
+ }
+
+ @Override
+ public boolean finishConnect() throws IOException {
+ return inner.finishConnect();
+ }
+
+ @Override
+ public SocketAddress getRemoteAddress() throws IOException {
+ return inner.getRemoteAddress();
+ }
+
+ @Override
+ public int read(final ByteBuffer dst) throws IOException {
+ return inner.read(dst);
+ }
+
+ @Override
+ public long read(final ByteBuffer[] dsts, final int offset, final int length) throws IOException {
+ return inner.read(dsts, offset, length);
+ }
+
+ @Override
+ public int write(final ByteBuffer src) throws IOException {
+ return inner.write(src);
+ }
+
+ @Override
+ public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException {
+ return inner.write(srcs, offset, length);
+ }
+
+ @Override
+ protected void implCloseSelectableChannel() throws IOException {
+ inner.close();
+ }
+
+ @Override
+ protected void implConfigureBlocking(final boolean block) throws IOException {
+ inner.configureBlocking(block);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5;
+
+import java.net.SocketOption;
+
+/**
+ * Utility class holding the singleton socket options used by the TCP-MD5 support
+ * library.
+ */
+public final class MD5SocketOptions {
+ /**
+ * TCP MD5 Signature option, as defined in RFC 2385.
+ */
+ public static final SocketOption<byte[]> TCP_MD5SIG = new SocketOption<byte[]>() {
+ @Override
+ public String name() {
+ return "TCP_MD5SIG";
+ }
+
+ @Override
+ public Class<byte[]> type() {
+ return byte[].class;
+ }
+ };
+
+ private MD5SocketOptions() {
+ throw new UnsupportedOperationException("Utility class cannot be instatiated");
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<projectDescription>
+ <name>tcpmd5-jni</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>tcpmd5-parent</artifactId>
+ <version>0.3.1-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>tcpmd5-jni</artifactId>
+ <description>Native support for RFC2385-enabled TCP sockets</description>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+ <properties>
+ <nar.groupId>com.github.maven-nar</nar.groupId>
+ <nar.version>3.0.0</nar.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <!-- Testing dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ org.opendaylight.bgpcep.jni
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <!-- Let eclipse know about the generated sources -->
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/nar/nar-generated</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>${nar.groupId}</groupId>
+ <artifactId>nar-maven-plugin</artifactId>
+ <version>${nar.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <c/>
+ <libraries>
+ <library>
+ <type>jni</type>
+ <narSystemPackage>org.opendaylight.bgpcep.tcpmd5.jni</narSystemPackage>
+ <linkCPP>false</linkCPP>
+ </library>
+ </libraries>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>nar-validate</goal>
+ <goal>nar-download</goal>
+ <goal>nar-unpack</goal>
+ <goal>nar-assembly</goal>
+ <goal>nar-system-generate</goal>
+ <goal>nar-resources</goal>
+ <goal>nar-javah</goal>
+ <goal>nar-compile</goal>
+ <goal>nar-package</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>${nar.groupId}</groupId>
+ <artifactId>nar-maven-plugin</artifactId>
+ <versionRange>${nar.version}</versionRange>
+ <goals>
+ <goal>nar-assembly</goal>
+ <goal>nar-compile</goal>
+ <goal>nar-download</goal>
+ <goal>nar-gnu-configure</goal>
+ <goal>nar-gnu-make</goal>
+ <goal>nar-gnu-process</goal>
+ <goal>nar-gnu-resources</goal>
+ <goal>nar-javah</goal>
+ <goal>nar-process-libraries</goal>
+ <goal>nar-resources</goal>
+ <goal>nar-system-generate</goal>
+ <goal>nar-testCompile</goal>
+ <goal>nar-testDownload</goal>
+ <goal>nar-testUnpack</goal>
+ <goal>nar-unpack</goal>
+ <goal>nar-validate</goal>
+ <goal>nar-vcproj</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore/>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>imagej.thirdparty</id>
+ <url>http://maven.imagej.net/content/repositories/thirdparty</url>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <distributionManagement>
+ <site>
+ <id>${project.artifactId}</id>
+ <name>TCPMD5-JNI Module site</name>
+ <url>${basedir}/target/site/${project.artifactId}</url>
+ </site>
+ </distributionManagement>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+#include <org_opendaylight_bgpcep_tcpmd5_jni_NarSystem.h>
+#include <org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/tcp.h>
+
+struct handler {
+ jclass clazz;
+ jfieldID field;
+};
+
+static struct handler *handlers = NULL;
+static unsigned handler_count = 0;
+static jclass illegal_argument = NULL;
+static jclass illegal_state = NULL;
+static jclass io = NULL;
+
+static void __attribute__ ((format (printf, 3, 4))) jni_exception(JNIEnv *env, jclass clazz, const char *fmt, ...)
+{
+ char buf[1024] = "";
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ (*env)->ThrowNew(env, clazz, buf);
+}
+
+#define ILLEGAL_ARGUMENT(...) jni_exception(env, illegal_argument, __VA_ARGS__)
+#define ILLEGAL_STATE(...) jni_exception(env, illegal_state, __VA_ARGS__)
+#define IO(...) jni_exception(env, io, __VA_ARGS__)
+
+static void __attribute__ ((format (printf, 3, 4))) log_message(JNIEnv *env, jobject logger, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#define ERROR(...) log_message(env, logger, __VA_ARGS__)
+#define WARN(...) log_message(env, logger, __VA_ARGS__)
+
+static const struct handler *add_handler(JNIEnv *env, jobject logger, const char *name, const char *field, const char *sig)
+{
+ // Find the class
+ const jclass clazz = (*env)->FindClass(env, name);
+ if (clazz == NULL) {
+ goto out;
+ }
+
+ // Find the field
+ const jfieldID fid = (*env)->GetFieldID(env, clazz, field, sig);
+ if (fid == NULL) {
+ goto out_clazz;
+ }
+
+ // Reallocate the array
+ struct handler *r = realloc(handlers, (handler_count + 1) * sizeof(struct handler));
+ if (r == NULL) {
+ ERROR("Failed to allocate handler: %s", strerror(errno));
+ goto out_clazz;
+ }
+ handlers = r;
+
+ // Fill the array
+ struct handler *ret = handlers + handler_count;
+ ret->clazz = (*env)->NewGlobalRef(env, clazz);
+ ret->field = fid;
+
+ if (ret->clazz == NULL) {
+ goto out_clazz;
+ }
+
+ (*env)->DeleteLocalRef(env, clazz);
+ return ret;
+
+out_clazz:
+ (*env)->DeleteLocalRef(env, clazz);
+out:
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ return NULL;
+}
+
+static const struct handler *find_handler(JNIEnv *env, jclass clazz)
+{
+ if (clazz != NULL) {
+ unsigned i;
+
+ for (i = 0; i < handler_count; ++i) {
+ const struct handler *h = handlers + i;
+ if ((*env)->IsAssignableFrom(env, clazz, h->clazz) == JNI_TRUE) {
+ return h;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static jclass resolve_class(JNIEnv *env, const char *what, jclass *where)
+{
+ if (*where != NULL) {
+ jclass clazz = (*env)->FindClass(env, what);
+ if (clazz == NULL) {
+ return NULL;
+ }
+
+ jclass ref = (*env)->NewGlobalRef(env, clazz);
+ (*env)->DeleteLocalRef(env, clazz);
+ if (ref == NULL) {
+ return NULL;
+ }
+
+ *where = ref;
+ return ref;
+ } else {
+ return *where;
+ }
+}
+
+static void native_error(JNIEnv *env, int err)
+{
+ char buf[256] = "";
+ strerror_r(err, buf, sizeof(buf));
+ IO("Native operation failed: %s", buf);
+}
+
+static int resolve_exceptions(JNIEnv *env)
+{
+ // First resolve Exception classes, these are mandatory
+ if (resolve_class(env, "java/lang/IllegalStateException", &illegal_state) == NULL) {
+ return 1;
+ }
+ if (resolve_class(env, "java/lang/IllegalArgumentException", &illegal_argument) == NULL) {
+ return 1;
+ }
+ if (resolve_class(env, "java/io/IOException", &io) == NULL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static jobject resolve_logging(JNIEnv *env, jclass clazz)
+{
+ jclass lf = (*env)->FindClass(env, "org/slf4j/LoggerFactory");
+ if (lf != NULL) {
+ jmethodID mid = (*env)->GetStaticMethodID(env, lf, "getLogger", "(Ljava/lang/Class;)Lorg/slf4j/Logger");
+ if (mid != NULL) {
+ return (*env)->CallStaticObjectMethod(env, lf, mid, clazz);
+ }
+ }
+
+ return NULL;
+}
+
+static jint sanity_check(JNIEnv *env, jobject logger)
+{
+ struct tcp_md5sig md5sig;
+
+ // Sanity-check maximum key size against the structure
+ if (TCP_MD5SIG_MAXKEYLEN > sizeof(md5sig.tcpm_key)) {
+ ERROR("Structure key size %zu is less than %d", sizeof(md5sig.tcpm_key), TCP_MD5SIG_MAXKEYLEN);
+ return 0;
+ }
+
+ // Now run a quick check to see if we can really the getsockopt/setsockopt calls are
+ // supported.
+ const int fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ ERROR("Failed to open a socket for sanity tests: %s", strerror(errno));
+ return 0;
+ }
+
+ int ret = -1;
+ socklen_t len = sizeof(md5sig);
+ memset(&md5sig, 0, sizeof(md5sig));
+ if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, &len) != 0) {
+ WARN("Platform has no support of TCP_MD5SIG: %s", strerror(errno));
+ goto out_close;
+ }
+
+ // Sanity check: freshly-created sockets should not have a key
+ if (md5sig.tcpm_keylen != 0) {
+ ERROR("Platform advertizes a fresh socket with key length %u", md5sig.tcpm_keylen);
+ goto out_close;
+ }
+
+ // Attempt to set a new key with maximum size
+ md5sig.tcpm_keylen = TCP_MD5SIG_MAXKEYLEN;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) != 0) {
+ ERROR("Failed to set TCP_MD5SIG option: %s", strerror(errno));
+ goto out_close;
+ }
+
+ // Attempt to remove the key
+ md5sig.tcpm_keylen = 0;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) != 0) {
+ ERROR("Failed to clear TCP_MD5SIG option: %s", strerror(errno));
+ goto out_close;
+ }
+
+ ret = 0;
+
+out_close:
+ close(fd);
+ return ret;
+}
+
+/*
+ * Class: org_opendaylight_bgpcep_tcpmd5_jni_NarSystem
+ * Method: runUnitTestsNative
+ * Signature: ()I
+ */
+jint Java_org_opendaylight_bgpcep_tcpmd5_jni_NarSystem_runUnitTestsNative(JNIEnv *env, jobject obj)
+{
+ if (resolve_exceptions(env)) {
+ return 0;
+ }
+
+ jobject logger = resolve_logging(env, (*env)->GetObjectClass(env, obj));
+ if (sanity_check(env, logger)) {
+ return 0;
+ }
+
+ int ret = 0;
+ if (add_handler(env, logger, "sun/nio/ch/ServerSocketChannelImpl", "fdVal", "I")) {
+ ++ret;
+ }
+ if (add_handler(env, logger, "sun/nio/ch/SocketChannelImpl", "fdVal", "I")) {
+ ++ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Class: org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess
+ * Method: isClassSupported0
+ * Signature: (Ljava/lang/Class;)Z
+ */
+jboolean Java_org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess_isClassSupported0(JNIEnv *env, jclass clazz, jclass arg)
+{
+ const struct handler *h = find_handler(env, arg);
+ return h == NULL ? JNI_FALSE : JNI_TRUE;
+}
+
+/*
+ * Class: org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess
+ * Method: getChannelKey0
+ * Signature: (Ljava/nio/channels/Channel;)[B
+ */
+jbyteArray Java_org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess_getChannelKey0(JNIEnv *env, jclass clazz, jobject channel)
+{
+ const struct handler *h = find_handler(env, (*env)->GetObjectClass(env, channel));
+ if (h == NULL) {
+ ILLEGAL_STATE("Failed to find handler");
+ return NULL;
+ }
+
+ const jint fd = (*env)->GetIntField(env, channel, h->field);
+ if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
+ return NULL;
+ }
+
+ struct tcp_md5sig md5sig;
+ memset(&md5sig, 0, sizeof(md5sig));
+ socklen_t len = sizeof(md5sig);
+ if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, &len) != 0) {
+ native_error(env, errno);
+ return NULL;
+ }
+
+ jbyteArray ret = (*env)->NewByteArray(env, md5sig.tcpm_keylen);
+ if (ret != NULL) {
+ (*env)->SetByteArrayRegion(env, ret, 0, md5sig.tcpm_keylen, (jbyte *)md5sig.tcpm_key);
+ }
+
+ return ret;
+}
+
+/*
+ * Class: org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess
+ * Method: setChannelKey0
+ * Signature: (Ljava/nio/channels/Channel;[B)V
+ */
+void Java_org_opendaylight_bgpcep_tcpmd5_jni_NativeKeyAccess_setChannelKey0(JNIEnv *env, jclass clazz, jobject channel, jbyteArray key)
+{
+ const jsize keylen = (*env)->GetArrayLength(env, key);
+ if (keylen < 0) {
+ ILLEGAL_ARGUMENT("Negative array length %d encountered", keylen);
+ return;
+ }
+ if (keylen > TCP_MD5SIG_MAXKEYLEN) {
+ ILLEGAL_ARGUMENT("Key length %d exceeds platform limit %d", keylen, TCP_MD5SIG_MAXKEYLEN);
+ return;
+ }
+
+ struct tcp_md5sig md5sig;
+ memset(&md5sig, 0, sizeof(md5sig));
+ md5sig.tcpm_keylen = (uint16_t) keylen;
+
+ /*
+ * TCP_MD5SIG_MAXKEYLEN may not be an accurate check of key field
+ * length. Check the field size before accessing it.
+ */
+ if (keylen > sizeof(md5sig.tcpm_key)) {
+ ILLEGAL_ARGUMENT("Key length %d exceeds native buffer limit %zu", keylen, sizeof(md5sig.tcpm_key));
+ return;
+ }
+
+ (*env)->GetByteArrayRegion(env, key, 0, keylen, (void *) &md5sig.tcpm_key);
+ if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
+ return;
+ }
+
+ const struct handler *h = find_handler(env, (*env)->GetObjectClass(env, channel));
+ if (h == NULL) {
+ ILLEGAL_STATE("Failed to find handler");
+ return;
+ }
+
+ const jint fd = (*env)->GetIntField(env, channel, h->field);
+ if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
+ return;
+ }
+
+ const int ret = setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig));
+ if (ret != 0) {
+ native_error(env, errno);
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5.jni;
+
+import java.io.IOException;
+
+/**
+ * Interface for accessing key information attached to an object.
+ */
+public interface KeyAccess {
+ /**
+ * Retrieve the key.
+ *
+ * @return The key currently attached, null if there is no key attached.
+ * @throws IOException when the retrieve operation fails.
+ */
+ byte[] getKey() throws IOException;
+
+ /**
+ * Set the key.
+ *
+ * @param key The key to be attached, null indicates removal.
+ * @throws IOException when the set operation fails.
+ */
+ void setKey(byte[] key) throws IOException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Robert Varga. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5.jni;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implementation of KeyAccess using Java Native Interface plugin to talk
+ * directly to the underlying operating system.
+ */
+public final class NativeKeyAccess implements KeyAccess {
+ private static final Logger LOG = LoggerFactory.getLogger(NativeKeyAccess.class);
+
+ static {
+ NarSystem.loadLibrary();
+
+ int rt = NarSystem.runUnitTests();
+ if (rt == 0) {
+ LOG.warn("Run-time initialization failed");
+ } else {
+ LOG.debug("Run-time found {} supported channel classes", rt);
+ }
+ }
+
+ private static native boolean isClassSupported0(Class<?> channel);
+ private static native byte[] getChannelKey0(Channel channel) throws IOException;
+ private static native void setChannelKey0(Channel channel, byte[] key) throws IOException;
+
+ private final Channel channel;
+
+ private NativeKeyAccess(final Channel channel) {
+ this.channel = Preconditions.checkNotNull(channel);
+ }
+
+ public static KeyAccess create(final Channel channel) {
+ if (isClassSupported0(channel.getClass())) {
+ return new NativeKeyAccess(channel);
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean isAvailableForClass(final Class<?> clazz) {
+ return isClassSupported0(clazz);
+ }
+
+ @Override
+ public byte[] getKey() throws IOException {
+ synchronized (channel) {
+ return getChannelKey0(channel);
+ }
+ }
+
+ @Override
+ public void setKey(final byte[] key) throws IOException {
+ synchronized (channel) {
+ setChannelKey0(channel, Preconditions.checkNotNull(key));
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 Robert Varga. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/bgpcep.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/bgpcep.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/BGP_LS_PCEP:Main</url>
+ <tag>HEAD</tag>
+ </scm>
+ <parent>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>commons.parent</artifactId>
+ <version>0.3.1-SNAPSHOT</version>
+ <relativePath>../commons/parent</relativePath>
+ </parent>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+
+ <artifactId>tcpmd5-parent</artifactId>
+ <description>RFC2385-enabled sockets components</description>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+
+ <modules>
+ <module>core</module>
+ <module>jni</module>
+ </modules>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-jni</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>tcpmd5-jni</artifactId>
+ <version>${project.version}</version>
+ <type>nar</type>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+</project>