#define KEEPALIVE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
-#define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
+#define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+
+#define FAST_RECONNECT_RATE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
#define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
*/
struct GNUNET_HELLO_Address *address;
+ /**
+ * Address we currently use.
+ */
+ struct GNUNET_HELLO_Address *fast_reconnect_address;
+
+
/**
* Identity of this neighbour.
*/
*/
GNUNET_SCHEDULER_TaskIdentifier ats_suggest;
+ /**
+ * Delay for ATS request
+ */
+ GNUNET_SCHEDULER_TaskIdentifier ats_request;
+
/**
* Task the resets the peer state after due to an pending
* unsuccessful connection setup
* Did we sent an KEEP_ALIVE message and are we expecting a response?
*/
int expect_latency_response;
+
+ /**
+ * Was the address used successfully
+ */
int address_state;
+
+ /**
+ * Fast reconnect attempts for identical address
+ */
+ unsigned int fast_reconnect_attempts;
};
return GNUNET_CONTAINER_multihashmap_get (neighbours, &pid->hashPubKey);
}
+/**
+ * Disconnect from the given neighbour, clean up the record.
+ *
+ * @param n neighbour to disconnect from
+ */
+static void
+disconnect_neighbour (struct NeighbourMapEntry *n);
+
#define change_state(n, state, ...) change (n, state, __LINE__)
static int
#endif
/* resetting state */
+
+ if (n->state == S_FAST_RECONNECT)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Fast reconnect time out, disconnecting peer `%s'\n",
+ GNUNET_i2s (&n->id));
+ disconnect_neighbour(n);
+ return;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "State for neighbour `%s' %X changed from `%s' to `%s' in line %u\n",
+ GNUNET_i2s (&n->id), n, print_state(n->state), "S_NOT_CONNECTED", __LINE__);
+
n->state = S_NOT_CONNECTED;
/* destroying address */
GNUNET_free (new);
return GNUNET_SYSERR;
}
-#if DEBUG_TRANSPORT
{
char *old = GNUNET_strdup (print_state (n->state));
char *new = GNUNET_strdup (print_state (state));
GNUNET_free (old);
GNUNET_free (new);
}
-#endif
n->state = state;
switch (n->state)
* @param cls closure for callbacks
* @param connect_cb function to call if we connect to a peer
* @param disconnect_cb function to call if we disconnect from a peer
- * @param address_cb function to call if we change an active address
+ * @param peer_address_cb function to call if we change an active address
* of a neighbour
*/
void
GST_neighbours_start (void *cls,
GNUNET_TRANSPORT_NotifyConnect connect_cb,
GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
- GNUNET_TRANSPORT_PeerIterateCallback address_cb)
+ GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb)
{
callback_cls = cls;
connect_notify_cb = connect_cb;
disconnect_notify_cb = disconnect_cb;
- address_change_cb = address_cb;
+ address_change_cb = peer_address_cb;
neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
}
struct SessionDisconnectMessage disconnect_msg;
#if DEBUG_TRANSPORT
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Sending DISCONNECT message to peer `%4s'\n",
GNUNET_i2s (&n->id));
#endif
GNUNET_assert (NULL != n->address);
if (n->address_state == USED)
{
- GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
+ GST_validation_set_address_use (n->address, n->session, GNUNET_NO, __LINE__);
GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
n->address_state = UNUSED;
}
GNUNET_SCHEDULER_cancel (n->ats_suggest);
n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
}
+ if (n->ats_request != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (n->ats_request);
+ n->ats_request = GNUNET_SCHEDULER_NO_TASK;
+ }
+
if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
{
GNUNET_SCHEDULER_cancel (n->timeout_task);
GNUNET_SCHEDULER_cancel (n->transmission_task);
n->transmission_task = GNUNET_SCHEDULER_NO_TASK;
}
+ if (NULL != n->fast_reconnect_address)
+ {
+ GNUNET_HELLO_address_free (n->fast_reconnect_address);
+ n->fast_reconnect_address = NULL;
+ }
if (NULL != n->address)
{
GNUNET_HELLO_address_free (n->address);
struct NeighbourMapEntry *n = cls;
n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer`%4s' disconnected due to timeout\n",
+ GNUNET_i2s (&n->id));
GNUNET_STATISTICS_update (GST_stats,
gettext_noop
"ATS did not suggested address to connect to peer `%s'\n",
GNUNET_i2s (&n->id));
+ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# ATS address suggestions failed"), 1,
+ GNUNET_NO);
+
+
disconnect_neighbour (n);
}
}
+static void
+ats_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct NeighbourMapEntry *n = cls;
+ n->ats_request = GNUNET_SCHEDULER_NO_TASK;
+
+ n->ats_suggest =
+ GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
+ n);
+}
+
+
/**
* We tried to switch addresses with an peer already connected. If it failed,
* we should tell ATS to not use this address anymore (until it is re-validated).
GNUNET_assert ((n->state == S_CONNECTED) || (n->state == S_FAST_RECONNECT));
if (GNUNET_YES != success)
{
-#if DEBUG_TRANSPORT
+
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to switch connected peer `%s' to address '%s' session %X, asking ATS for new address \n",
- GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
-#endif
+ "Failed to switch connected peer `%s' in state %s to address '%s' session %X, asking ATS for new address \n",
+ GNUNET_i2s (&n->id), print_state(n->state), GST_plugins_a2s (cc->address), cc->session);
+
GNUNET_assert (strlen (cc->address->transport_name) > 0);
GNUNET_ATS_address_destroyed (GST_ats, cc->address, cc->session);
if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
+ {
GNUNET_SCHEDULER_cancel (n->ats_suggest);
- n->ats_suggest =
+ n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
+ }
+
+ if (n->state == S_FAST_RECONNECT)
+ {
+ if (GNUNET_SCHEDULER_NO_TASK == n->ats_request)
+ {
+ /* Throtteled ATS request for fast reconnect */
+ struct GNUNET_TIME_Relative delay = GNUNET_TIME_relative_multiply(FAST_RECONNECT_RATE, n->fast_reconnect_attempts);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Fast reconnect attempt %u failed, delay ATS request for %llu ms\n", n->fast_reconnect_attempts, delay.rel_value);
+ n->ats_request =
+ GNUNET_SCHEDULER_add_delayed (delay,
+ ats_request,
+ n);
+ }
+ }
+ else
+ {
+ /* Immediate ATS request for connected peers */
+ n->ats_suggest =
GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT, ats_suggest_cancel,
n);
- GNUNET_ATS_suggest_address (GST_ats, &n->id);
+ GNUNET_ATS_suggest_address (GST_ats, &n->id);
+ }
GNUNET_HELLO_address_free (cc->address);
GNUNET_free (cc);
return;
case S_CONNECTED:
if (n->address_state == FRESH)
{
- GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES);
+ GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES, __LINE__);
GNUNET_ATS_address_update (GST_ats, cc->address, cc->session, NULL, 0);
+ if (cc->session != n->session)
+ GNUNET_break (0);
GNUNET_ATS_address_in_use (GST_ats, cc->address, cc->session, GNUNET_YES);
n->address_state = USED;
}
if (n->address_state == FRESH)
{
- GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES);
+ GST_validation_set_address_use (cc->address, cc->session, GNUNET_YES, __LINE__);
GNUNET_ATS_address_update (GST_ats, cc->address, cc->session, NULL, 0);
GNUNET_ATS_address_in_use (GST_ats, cc->address, cc->session, GNUNET_YES);
n->address_state = USED;
/**
* For an existing neighbour record, set the active connection to
- * the given address.
+ * use the given address.
*
* @param peer identity of the peer to switch the address for
* @param address address of the other peer, NULL if other peer
* @param session session to use (or NULL)
* @param ats performance data
* @param ats_count number of entries in ats
+ * @param bandwidth_in inbound quota to be used when connection is up
+ * @param bandwidth_out outbound quota to be used when connection is up
* @return GNUNET_YES if we are currently connected, GNUNET_NO if the
* connection is not up (yet)
*/
}
/* checks successful and neighbour != NULL */
-#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"ATS tells us to switch to address '%s' session %p for peer `%s' in state `%s'\n",
- GST_plugins_a2s (address), session, GNUNET_i2s (peer),
+ (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
+ session,
+ GNUNET_i2s (peer),
print_state (n->state));
-#endif
+
if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (n->ats_suggest);
n->ats_suggest = GNUNET_SCHEDULER_NO_TASK;
}
+
+
+ if (n->state == S_FAST_RECONNECT)
+ {
+ /* Throtteled fast reconnect */
+
+ if (NULL == n->fast_reconnect_address)
+ {
+ n->fast_reconnect_address = GNUNET_HELLO_address_copy (address);
+
+ }
+ else if (0 == GNUNET_HELLO_address_cmp(address, n->fast_reconnect_address))
+ {
+ n->fast_reconnect_attempts ++;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "FAST RECONNECT to peer `%s' and address '%s' with identical address attempt %u\n",
+ GNUNET_i2s (&n->id), GST_plugins_a2s (address), n->fast_reconnect_attempts);
+ }
+ }
+ else
+ {
+ n->fast_reconnect_attempts = 0;
+ }
+
/* do not switch addresses just update quotas */
if ((n->state == S_CONNECTED) && (NULL != n->address) &&
(0 == GNUNET_HELLO_address_cmp (address, n->address)) &&
GNUNET_assert (NULL != n->address);
if (n->address_state == USED)
{
- GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
+ GST_validation_set_address_use (n->address, n->session, GNUNET_NO, __LINE__);
GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
n->address_state = UNUSED;
}
/* Obtain an session for this address from plugin */
struct GNUNET_TRANSPORT_PluginFunctions *papi;
papi = GST_plugins_find (address->transport_name);
- GNUNET_assert (papi != NULL);
+
+ if (papi == NULL)
+ {
+ /* we don't have the plugin for this address */
+ GNUNET_ATS_address_destroyed (GST_ats, n->address, NULL);
+
+ if (n->ats_suggest != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (n->ats_suggest);
+ n->ats_suggest = GNUNET_SCHEDULER_add_delayed (ATS_RESPONSE_TIMEOUT,
+ ats_suggest_cancel,
+ n);
+ GNUNET_ATS_suggest_address (GST_ats, &n->id);
+ GNUNET_HELLO_address_free (n->address);
+ n->address = NULL;
+ n->session = NULL;
+ return GNUNET_NO;
+ }
+
if (session == NULL)
{
n->session = papi->get_session (papi->cls, address);
/* Session could not be initiated */
if (n->session == NULL)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Failed to obtain new session %p for peer `%s' and address '%s'\n",
n->session, GNUNET_i2s (&n->id), GST_plugins_a2s (n->address));
GNUNET_ATS_suggest_address (GST_ats, &n->id);
GNUNET_HELLO_address_free (n->address);
n->address = NULL;
+ n->session = NULL;
return GNUNET_NO;
}
}
else
{
+ n->session = session;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Using existing session %p for peer `%s' and address '%s'\n",
- n->session, GNUNET_i2s (&n->id), GST_plugins_a2s (n->address));
- n->session = session;
+ n->session,
+ GNUNET_i2s (&n->id),
+ (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>");
}
switch (n->state)
GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
cc = GNUNET_malloc (sizeof (struct ContinutionContext));
- cc->session = session;
+ cc->session = n->session;
cc->address = GNUNET_HELLO_address_copy (address);
ret = send_with_session (n,
connect_msg.timestamp =
GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
cc = GNUNET_malloc (sizeof (struct ContinutionContext));
- cc->session = session;
+ cc->session = n->session;
cc->address = GNUNET_HELLO_address_copy (address);
ret = send_with_session(n,
connect_msg.timestamp =
GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
cc = GNUNET_malloc (sizeof (struct ContinutionContext));
- cc->session = session;
+ cc->session = n->session;
cc->address = GNUNET_HELLO_address_copy (address);
ret = send_with_session(n,
(const void *) &connect_msg, msg_len,
{
if (n->address_state == USED)
{
- GST_validation_set_address_use (n->address, n->session, GNUNET_NO);
+ GST_validation_set_address_use (n->address, n->session, GNUNET_NO, __LINE__);
GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_NO);
n->address_state = UNUSED;
+
}
}
* neighbours_connected -= 1
* BUT: no disconnect_cb to notify clients about disconnect
*/
-#if DEBUG_TRANSPORT
+
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying fast reconnect to peer `%s'\n",
GNUNET_i2s (peer));
-#endif
+
GNUNET_assert (neighbours_connected > 0);
change_state (n, S_FAST_RECONNECT);
neighbours_connected--;
size_t msg_len;
size_t ret;
-#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received CONNECT_ACK message from peer `%s'\n",
GNUNET_i2s (peer));
-#endif
if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
{
* We also received an CONNECT message, switched from SENDT to RECV and
* ATS already suggested us an address after a successful blacklist check
*/
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CONNECT_ACK message from peer `%s' in state `%s'\n",
+ GNUNET_i2s (peer),
+ print_state(n->state));
+
+ if ((n->address != NULL) && (n->state == S_CONNECTED))
+ {
+ /* After fast reconnect: send ACK (ACK) even when we are connected */
+ msg_len = sizeof (msg);
+ msg.size = htons (msg_len);
+ msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
+
+ ret = send_with_session(n,
+ (const char *) &msg, msg_len,
+ UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL, NULL);
+
+ if (ret == GNUNET_SYSERR)
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to send SESSION_ACK to `%4s' using address '%s' session %X\n",
+ GNUNET_i2s (&n->id), GST_plugins_a2s (n->address), n->session);
+ return;
+ }
+
if ((n->state != S_CONNECT_SENT) &&
((n->state != S_CONNECT_RECV) && (n->address != NULL)))
{
GNUNET_NO);
return;
}
-
- change_state (n, S_CONNECTED);
+ if (n->state != S_CONNECTED)
+ change_state (n, S_CONNECTED);
if (NULL != session)
{
}
GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
GNUNET_assert (NULL != n->address);
-//LOOKAT
+
if ((n->address_state == FRESH) && (0 == GNUNET_HELLO_address_cmp(address, n->address)))
{
- GST_validation_set_address_use (n->address, n->session, GNUNET_YES);
+ GST_validation_set_address_use (n->address, n->session, GNUNET_YES, __LINE__);
GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_YES);
n->address_state = USED;
}
session, address->transport_name, GNUNET_i2s (peer));
GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
GNUNET_assert (n->address != NULL);
-// LOOKAT
+
if ((n->address_state == FRESH) && (0 == GNUNET_HELLO_address_cmp(address, n->address)))
{
- GST_validation_set_address_use (n->address, n->session, GNUNET_YES);
+ GST_validation_set_address_use (n->address, n->session, GNUNET_YES, __LINE__);
GNUNET_ATS_address_in_use (GST_ats, n->address, n->session, GNUNET_YES);
n->address_state = USED;
}
GNUNET_ATS_address_update (GST_ats, address, session, ats, ats_count);
n = lookup_neighbour (peer);
- if ((n != NULL) && (S_CONNECTED == n->state))
+ if ((n != NULL) && ((S_CONNECTED == n->state) || (S_FAST_RECONNECT == n->state)))
{
- /* connected peer switches addresses */
+ /* connected peer switches addresses or is trying to do a fast reconnect*/
return;
}