From: Christian Grothoff Date: Wed, 25 Jan 2017 20:02:11 +0000 (+0100) Subject: implement keepalives X-Git-Tag: taler-0.2.1~290 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=aa64c277f9e7e2a0c185456277d1013f4277288c;p=oweals%2Fgnunet.git implement keepalives --- diff --git a/src/cadet/gnunet-service-cadet-new.c b/src/cadet/gnunet-service-cadet-new.c index af5ac13ae..f24c9f518 100644 --- a/src/cadet/gnunet-service-cadet-new.c +++ b/src/cadet/gnunet-service-cadet-new.c @@ -1343,7 +1343,7 @@ run (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (c, "CADET", - "REFRESHC_CONNECTION_TIME", + "REFRESH_CONNECTION_TIME", &keepalive_period)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, diff --git a/src/cadet/gnunet-service-cadet-new_channel.c b/src/cadet/gnunet-service-cadet-new_channel.c index 83911f530..e55a9a77d 100644 --- a/src/cadet/gnunet-service-cadet-new_channel.c +++ b/src/cadet/gnunet-service-cadet-new_channel.c @@ -26,11 +26,18 @@ * * TODO: * - Optimize ACKs by using 'mid_futures' properly! + * - calculate current RTT if possible, use that for initial retransmissions + * (NOTE: needs us to learn which connection the tunnel uses for the message!) * - introduce shutdown so we can have half-closed channels, modify * destroy to include MID to have FIN-ACK equivalents, etc. * - estimate max bandwidth using bursts and use to for CONGESTION CONTROL! + * (and figure out how/where to use this!) * - check that '0xFFULL' really is sufficient for flow control! + * (this is right now a big HACK!) * - revisit handling of 'unreliable' traffic! + * (has not seen enough review) + * - revisit handling of 'unbuffered' traffic! + * (has not seen enough review) * - revisit handling of 'out-of-order' option, especially in combination with/without 'reliable'. * - figure out flow control without ACKs (unreliable traffic!) */ diff --git a/src/cadet/gnunet-service-cadet-new_connection.c b/src/cadet/gnunet-service-cadet-new_connection.c index 60389008c..58922bc1e 100644 --- a/src/cadet/gnunet-service-cadet-new_connection.c +++ b/src/cadet/gnunet-service-cadet-new_connection.c @@ -27,16 +27,18 @@ * @author Christian Grothoff * * TODO: - * - Optimization: keepalive messages / timeout (timeout to be done @ peer level!) + * - Implement: keepalive messages / timeout (timeout to be done @ peer level!) * - Optimization: keep performance metrics (?) */ #include "platform.h" +#include "gnunet-service-cadet-new.h" #include "gnunet-service-cadet-new_channel.h" #include "gnunet-service-cadet-new_connection.h" #include "gnunet-service-cadet-new_paths.h" #include "gnunet-service-cadet-new_peer.h" #include "gnunet-service-cadet-new_tunnels.h" #include "gnunet_cadet_service.h" +#include "gnunet_statistics_service.h" #include "cadet_protocol.h" @@ -118,6 +120,11 @@ struct CadetConnection */ struct GNUNET_SCHEDULER_Task *task; + /** + * Queue entry for keepalive messages. + */ + struct CadetTunnelQueueEntry *keepalive_qe; + /** * Function to call once we are ready to transmit. */ @@ -184,6 +191,11 @@ GCC_destroy (struct CadetConnection *cc) GNUNET_SCHEDULER_cancel (cc->task); cc->task = NULL; } + if (NULL != cc->keepalive_qe) + { + GCT_send_cancel (cc->keepalive_qe); + cc->keepalive_qe = NULL; + } GCPP_del_connection (cc->path, cc->off, cc); @@ -209,7 +221,72 @@ GCC_get_ct (struct CadetConnection *cc) /** - * A CADET_CONNECTION_ACK was received for this connection, implying + * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the + * tunnel to prevent it from timing out. + * + * @param cls the `struct CadetConnection` to keep alive. + */ +static void +send_keepalive (void *cls); + + +/** + * Keepalive was transmitted. Remember this, and possibly + * schedule the next one. + * + * @param cls the `struct CadetConnection` to keep alive. + */ +static void +keepalive_done (void *cls) +{ + struct CadetConnection *cc = cls; + + cc->keepalive_qe = NULL; + if ( (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); +} + + +/** + * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the + * tunnel to prevent it from timing out. + * + * @param cls the `struct CadetConnection` to keep alive. + */ +static void +send_keepalive (void *cls) +{ + struct CadetConnection *cc = cls; + struct GNUNET_MessageHeader msg; + + cc->task = NULL; + GNUNET_assert (NULL != cc->ct); + GNUNET_assert (GNUNET_YES == cc->mqm_ready); + GNUNET_assert (NULL == cc->keepalive_qe); + LOG (GNUNET_ERROR_TYPE_INFO, + "Sending KEEPALIVE on behalf of %s via %s\n", + GCC_2s (cc), + GCT_2s (cc->ct->t)); + GNUNET_STATISTICS_update (stats, + "# keepalives sent", + 1, + GNUNET_NO); + msg.size = htons (sizeof (msg)); + msg.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE); + + cc->keepalive_qe + = GCT_send (cc->ct->t, + &msg, + &keepalive_done, + cc); +} + + +/** + * A #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK was received for this connection, implying * that the end-to-end connection is up. Process it. * * @param cc the connection that got the ACK. @@ -227,15 +304,18 @@ GCC_handle_connection_create_ack (struct CadetConnection *cc) GNUNET_SCHEDULER_cancel (cc->task); cc->task = NULL; } -#if FIXME_KEEPALIVE - cc->task = GNUNET_SCHEDULER_add_delayed (cc->keepalive_period, - &send_keepalive, - cc); -#endif cc->state = CADET_CONNECTION_READY; if (GNUNET_YES == cc->mqm_ready) + { cc->ready_cb (cc->ready_cb_cls, GNUNET_YES); + if ( (NULL == cc->keepalive_qe) && + (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); + } } @@ -288,7 +368,8 @@ GCC_handle_encrypted (struct CadetConnection *cc, /** - * Send a CREATE message to the first hop. + * Send a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE message to the + * first hop. * * @param cls the `struct CadetConnection` to initiate */ @@ -459,6 +540,19 @@ manage_first_hop_mq (void *cls, case CADET_CONNECTION_READY: cc->ready_cb (cc->ready_cb_cls, GNUNET_YES); + if ( (NULL == cc->keepalive_qe) && + (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling keepalive for %s in %s\n", + GCC_2s (cc), + GNUNET_STRINGS_relative_time_to_string (keepalive_period, + GNUNET_YES)); + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); + } break; } } @@ -610,6 +704,11 @@ GCC_transmit (struct CadetConnection *cc, GNUNET_assert (GNUNET_YES == cc->mqm_ready); GNUNET_assert (CADET_CONNECTION_READY == cc->state); cc->mqm_ready = GNUNET_NO; + if (NULL != cc->task) + { + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = NULL; + } GCP_send (cc->mq_man, env); } diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.c b/src/cadet/gnunet-service-cadet-new_tunnels.c index 3420c00db..03067a605 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.c +++ b/src/cadet/gnunet-service-cadet-new_tunnels.c @@ -1963,7 +1963,7 @@ GCT_consider_path (struct CadetTunnel *t, /** - * NOT IMPLEMENTED. + * We got a keepalive. Track in statistics. * * @param cls the `struct CadetTunnel` for which we decrypted the message * @param msg the message we received on the tunnel @@ -1974,7 +1974,13 @@ handle_plaintext_keepalive (void *cls, { struct CadetTunnel *t = cls; - GNUNET_break (0); // FIXME + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received KEEPALIVE on tunnel %s\n", + GCT_2s (t)); + GNUNET_STATISTICS_update (stats, + "# keepalives received", + 1, + GNUNET_NO); } diff --git a/src/cadet/test_cadet.c b/src/cadet/test_cadet.c index df279d72a..efc4f96b3 100644 --- a/src/cadet/test_cadet.c +++ b/src/cadet/test_cadet.c @@ -345,15 +345,21 @@ shutdown_task (void *cls) * operation has executed successfully. */ static void -stats_cont (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg) +stats_cont (void *cls, + struct GNUNET_TESTBED_Operation *op, + const char *emsg) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, " KA sent: %u, KA received: %u\n", - ka_sent, ka_received); + ka_sent, + ka_received); if ( (KEEPALIVE == test) && ( (ka_sent < 2) || (ka_sent > ka_received + 1)) ) + { + GNUNET_break (0); ok--; + } GNUNET_TESTBED_operation_done (stats_op); if (NULL != disconnect_task)