Skip to content

Commit

Permalink
Handshake probe.
Browse files Browse the repository at this point in the history
Stop handshake after deep sleep.

Signed-off-by: Achim Kraus <achim.kraus@bosch-si.com>
  • Loading branch information
Achim Kraus committed Dec 9, 2019
1 parent 87fff93 commit b9db9f1
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public class DtlsEndpointContext extends MapBasedEndpointContext {
* the connector is configured to act as server only.
*/
public static final String HANDSHAKE_MODE_FORCE = "force";
/**
* Force handshake probe before send this message. Doesn't start a
* handshake, if the connector is configured to act as server only.
*/
public static final String HANDSHAKE_MODE_PROBE = "probe";
/**
* Don't start a handshake, even, if no session is available.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,11 @@ public void handshakeFailed(Handshaker handshaker, Throwable error) {
message.onError(error);
}
}
if (handshaker.isProbing()) {
LOGGER.debug("Handshake with [{}] failed within probe!",
handshaker.getPeerAddress());
return;
}
Connection connection = handshaker.getConnection();
if (!connection.hasEstablishedSession()) {
connectionStore.remove(connection, false);
Expand Down Expand Up @@ -1167,8 +1172,13 @@ public void processRecord(Record record, Connection connection) {
record.getType(), record.getFragmentLength(), epoch, record.getSequenceNumber());

DTLSSession session = connection.getSession(epoch);
Handshaker handshaker = connection.getOngoingHandshake();
if (handshaker != null && handshaker.isExpired()) {
// handshake spaning Android / OS "deep sleep" expired
handshaker.handshakeFailed(new Exception("handshake already expired!"));
handshaker = null;
}
if (session == null) {
Handshaker handshaker = connection.getOngoingHandshake();
if (handshaker != null && handshaker.getSession().getReadEpoch() == 0 && epoch == 1) {
// future records, apply session after handshake finished.
handshaker.addRecordsForDeferredProcessing(record);
Expand Down Expand Up @@ -1215,6 +1225,13 @@ public void processRecord(Record record, Connection connection) {

record.applySession(session);

if (handshaker != null && handshaker.isProbing()) {
// received record, probe successful
handshaker.resetProbing();
connection.resetSession();
LOGGER.debug("handshake probe successful {}", connection.getPeerAddress());
}

switch (record.getType()) {
case APPLICATION_DATA:
processApplicationDataRecord(record, connection);
Expand Down Expand Up @@ -2055,6 +2072,21 @@ private void sendMessage(final long nanos, final RawData message, final Connecti
return;
}
LOGGER.debug("Sending application layer message to [{}]", message.getEndpointContext());

Handshaker handshaker = connection.getOngoingHandshake();
if (handshaker != null) {
if (handshaker.isExpired()) {
// handshake spaning Android / OS "deep sleep" expired
handshaker.handshakeFailed(new Exception("handshake already expired!"));
} else if (handshaker.isProbing()) {
if (checkOutboundEndpointContext(message, null)) {
message.onConnecting();
handshaker.addApplicationDataForDeferredProcessing(message);
}
return;
}
}

if (connection.isActive()) {
sendMessageWithSession(message, connection);
} else {
Expand Down Expand Up @@ -2121,8 +2153,9 @@ private void sendMessageWithSession(final RawData message, final Connection conn
return;
}
} else {
boolean probing = DtlsEndpointContext.HANDSHAKE_MODE_PROBE.equals(handshakeMode);
boolean full = DtlsEndpointContext.HANDSHAKE_MODE_FORCE_FULL.equals(handshakeMode);
boolean force = full || DtlsEndpointContext.HANDSHAKE_MODE_FORCE.equals(handshakeMode);
boolean force = probing || full || DtlsEndpointContext.HANDSHAKE_MODE_FORCE.equals(handshakeMode);
if (force || connection.isAutoResumptionRequired(getAutResumptionTimeout(message))) {
// create the session to resume from the previous one.
if (serverOnly) {
Expand All @@ -2136,15 +2169,26 @@ private void sendMessageWithSession(final RawData message, final Connection conn
Handshaker previousHandshaker = connection.getOngoingHandshake();
SessionTicket ticket;
SessionId sessionId;
if (session != null) {
if (full) {
sessionId = null;
ticket = null;
} else if (session != null) {
sessionId = session.getSessionIdentifier();
ticket = session.getSessionTicket();
connectionStore.removeFromEstablishedSessions(session, connection);
if (probing) {
// Only reset a resumption trigger, but keep the session for now
// the session will be reseted with the first received data
connection.setResumptionRequired(false);
} else {
connectionStore.removeFromEstablishedSessions(session, connection);
connection.resetSession();
}
} else {
sessionId = connection.getSessionIdentity();
ticket = connection.getSessionTicket();
connection.resetSession();
probing = false;
}
connection.resetSession();
Handshaker newHandshaker;
if (full || sessionId.isEmpty()) {
// server may use a empty session id to indicate,
Expand All @@ -2159,7 +2203,7 @@ private void sendMessageWithSession(final RawData message, final Connection conn
SecretUtil.destroy(ticket);
resumableSession.setHostName(message.getEndpointContext().getVirtualHost());
newHandshaker = new ResumingClientHandshaker(resumableSession, this, connection, config,
maximumTransmissionUnit);
maximumTransmissionUnit, probing);
}
initializeHandshaker(newHandshaker);
if (previousHandshaker != null) {
Expand Down Expand Up @@ -2251,9 +2295,7 @@ public final DTLSSession getSessionByAddress(InetSocketAddress address) {
@Override
public void sendFlight(DTLSFlight flight, Connection connection) throws IOException {
if (flight != null) {
if (flight.isRetransmissionNeeded()) {
scheduleRetransmission(flight, connection);
}
scheduleRetransmission(flight, connection);
sendFlightOverNetwork(flight);
}
}
Expand Down Expand Up @@ -2338,15 +2380,20 @@ protected void sendNextDatagramOverNetwork(final DatagramPacket datagramPacket)

private void handleTimeout(DTLSFlight flight, Connection connection) {

if (!flight.isResponseCompleted() && !connection.hasEstablishedSession()) {
if (!flight.isResponseCompleted()) {
Handshaker handshaker = connection.getOngoingHandshake();
if (null != handshaker) {
if (!handshaker.isProbing() && connection.hasEstablishedSession()) {
return;
}
Exception cause = null;
String message = "";
if (!connection.isExecuting() || !running.get()) {
message = " Stopped by shutdown!";
} else if (connectionStore.get(flight.getPeerAddress()) != connection) {
message = " Stopped by address change!";
} else if (handshaker.isExpired()) {
message = " Stopped by expired realtime!";
} else {
// set DTLS retransmission maximum
final int max = config.getMaxRetransmissions();
Expand Down Expand Up @@ -2428,6 +2475,9 @@ private void scheduleRetransmission(DTLSFlight flight, Connection connection) {
// schedule retransmission task
ScheduledFuture<?> f = timer.schedule(new TimeoutPeerTask(connection, flight), flight.getTimeout(), TimeUnit.MILLISECONDS);
flight.setTimeoutTask(f);
LOGGER.trace("handshake flight to peer {}, retransmission {} ms.", connection.getPeerAddress(), flight.getTimeout());
} else {
LOGGER.trace("handshake flight to peer {}, no retransmission!", connection.getPeerAddress());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import javax.crypto.SecretKey;
Expand Down Expand Up @@ -176,6 +177,11 @@ public abstract class Handshaker implements Destroyable {
/** Realtime nanoseconds of last sending a flight */
private long flightSendNanos;

/** Expire realtime nanoseconds */
private long expiresNanos;

private final long expiresNanosTimeout;

/** The current flight number. */
protected int flightNumber = 0;

Expand Down Expand Up @@ -233,6 +239,7 @@ public abstract class Handshaker implements Destroyable {
protected int statesIndex;
protected HandshakeState[] states;

protected boolean probe = false;
private boolean changeCipherSuiteMessageExpected = false;
private boolean sessionEstablished = false;
private boolean handshakeFailed = false;
Expand Down Expand Up @@ -299,7 +306,8 @@ protected Handshaker(boolean isClient, int initialMessageSeq, DTLSSession sessio
this.session.setMaxTransmissionUnit(maxTransmissionUnit);
this.applicationLevelInfoSupplier = config.getApplicationLevelInfoSupplier();
this.inboundMessageBuffer = new InboundMessageBuffer();

long timeout = config.getRetransmissionTimeout();
this.expiresNanosTimeout = TimeUnit.MILLISECONDS.toNanos(timeout << (config.getMaxRetransmissions() + 1));
addSessionListener(connection.getSessionListener());
}

Expand Down Expand Up @@ -1142,6 +1150,7 @@ public void sendFlight(DTLSFlight flight) {
setPendingFlight(null);
try {
flightSendNanos = ClockUtil.nanoRealtime();
expiresNanos = expiresNanosTimeout + flightSendNanos;
recordLayer.sendFlight(flight, connection);
setPendingFlight(flight);
} catch (IOException e) {
Expand Down Expand Up @@ -1228,6 +1237,39 @@ public final void handshakeFailed(Throwable cause) {
SecretUtil.destroy(this);
}

/**
* Test, if handshake was started in probing mode.
*
* Usually a client handshake removes the session from the connection store
* with the start. Probing removes the session only with the first data
* received back.
*
* @return {@code true}, if handshake is in probing mode, {@code false},
* otherwise.
*/
public boolean isProbing() {
return probe;
}

/**
* Reset probing mode, when data is received during.
*/
public void resetProbing() {
probe = false;
}

/**
* Test, if handshake is expired according nano realtime.
*
* Used to mitigate deep sleep during handhsakes.
*
* @return {@code true}, if handshake is expired, mainly during deep sleep,
* {@code false}, if the handshake is still in time.
*/
public boolean isExpired() {
return pendingFlight.get() != null && expiresNanos < ClockUtil.nanoRealtime();
}

/**
* Get cause of failure.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ public class ResumingClientHandshaker extends ClientHandshaker {
* if session, recordLayer or config is <code>null</code>
*/
public ResumingClientHandshaker(DTLSSession session, RecordLayer recordLayer, Connection connection,
DtlsConnectorConfig config, int maxTransmissionUnit) {
DtlsConnectorConfig config, int maxTransmissionUnit, boolean probe) {
super(session, recordLayer, connection, config, maxTransmissionUnit);
if (session.getSessionIdentifier() == null) {
throw new IllegalArgumentException("Session must contain the ID of the session to resume");
}
this.probe = probe;
}

// Methods ////////////////////////////////////////////////////////
Expand Down
Loading

0 comments on commit b9db9f1

Please sign in to comment.