Skip to content

Commit

Permalink
OpenSSL ByteBuffer BIO
Browse files Browse the repository at this point in the history
Motivation:
Currently Netty utilizes BIO_new_bio_pair so we can control all FD lifetime and event notification but delegates to OpenSSL for encryption/decryption. The current implementation sets up a pair of BIO buffers to read/write encrypted/plaintext data. This approach requires copying of data from Java ByteBuffers to native memory BIO buffers, and also requires both BIO buffers to be sufficiently large to hold application data. If direct ByteBuffers are used we can avoid coyping to/from the intermediate BIO buffer and just read/write directly from the direct ByteBuffer memory. We still need an internal buffer because OpenSSL may generate write data as a result of read calls (e.g. handshake, alerts, renegotiation, etc..), but this buffer doesn't have to be be large enough to hold application data.

Modifications:
- Take advantage of the new ByteBuffer based BIO provided by netty-tcnative instead of using BIO_read and BIO_write.

Result:
Less copying and lower memory footprint requirement per TLS connection.
  • Loading branch information
Scottmitch committed Feb 9, 2017
1 parent 007048d commit d06990f
Show file tree
Hide file tree
Showing 5 changed files with 384 additions and 349 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.tcnative.jni.CertificateVerifier;
import io.netty.tcnative.jni.SSL;
import io.netty.tcnative.jni.SSLContext;
import io.netty.util.AbstractReferenceCounted;
import io.netty.util.ReferenceCounted;
import io.netty.util.ResourceLeakDetector;
Expand All @@ -27,9 +30,6 @@
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.tcnative.jni.CertificateVerifier;
import io.netty.tcnative.jni.SSL;
import io.netty.tcnative.jni.SSLContext;

import java.security.AccessController;
import java.security.PrivateKey;
Expand All @@ -45,7 +45,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
Expand All @@ -57,6 +56,7 @@
import javax.net.ssl.X509TrustManager;

import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;

/**
* An implementation of {@link SslContext} which works with libraries that support the
Expand Down Expand Up @@ -85,6 +85,17 @@ public Boolean run() {
return SystemPropertyUtil.getBoolean("jdk.tls.rejectClientInitiatedRenegotiation", false);
}
});

private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE =
AccessController.doPrivileged(new PrivilegedAction<Integer>() {
@Override
public Integer run() {
return Math.max(1,
SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize",
2048));
}
});

private static final List<String> DEFAULT_CIPHERS;
private static final Integer DH_KEY_LENGTH;
private static final ResourceLeakDetector<ReferenceCountedOpenSslContext> leakDetector =
Expand Down Expand Up @@ -130,7 +141,8 @@ protected void deallocate() {
final Certificate[] keyCertChain;
final ClientAuth clientAuth;
final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
volatile boolean rejectRemoteInitiatedRenegotiation;
private volatile boolean rejectRemoteInitiatedRenegotiation;
private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE;

static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
new OpenSslApplicationProtocolNegotiator() {
Expand Down Expand Up @@ -266,7 +278,7 @@ public String run() {
SSLContext.setOptions(ctx, SSL.SSL_OP_SINGLE_DH_USE);
SSLContext.setOptions(ctx, SSL.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);

// We do not support compression as the moment so we should explicitly disable it.
// We do not support compression at the moment so we should explicitly disable it.
SSLContext.setOptions(ctx, SSL.SSL_OP_NO_COMPRESSION);

// Disable ticket support by default to be more inline with SSLEngineImpl of the JDK.
Expand Down Expand Up @@ -430,6 +442,29 @@ public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedR
this.rejectRemoteInitiatedRenegotiation = rejectRemoteInitiatedRenegotiation;
}

/**
* Returns if remote initiated renegotiation is supported or not.
*/
public boolean getRejectRemoteInitiatedRenegotiation() {
return rejectRemoteInitiatedRenegotiation;
}

/**
* Set the size of the buffer used by the BIO for non-application based writes
* (e.g. handshake, renegotiation, etc...).
*/
public void setBioNonApplicationBufferSize(int bioNonApplicationSize) {
this.bioNonApplicationBufferSize =
checkPositiveOrZero(bioNonApplicationSize, "bioNonApplicationBufferSize");
}

/**
* Returns the size of the buffer used by the BIO for non-application based writes
*/
public int getBioNonApplicationBufferSize() {
return bioNonApplicationBufferSize;
}

/**
* Sets the SSL session ticket keys of this context.
*
Expand Down Expand Up @@ -783,7 +818,7 @@ private static long newBIO(ByteBuf buffer) throws Exception {
try {
long bio = SSL.newMemBIO();
int readable = buffer.readableBytes();
if (SSL.writeToBIO(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) {
SSL.freeBIO(bio);
throw new IllegalStateException("Could not write data to memory BIO");
}
Expand Down
Loading

0 comments on commit d06990f

Please sign in to comment.