add changelog
[oweals/gnunet.git] / src / transport / transport_api_core.c
index 9c29d4908ec4a57447b651386d13db9b9009cca0..5d841fa10c1fb803864b21da0bd77091d97b5ccf 100644 (file)
@@ -2,21 +2,21 @@
      This file is part of GNUnet.
      Copyright (C) 2009-2013, 2016 GNUnet e.V.
 
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
+     Affero General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-     Boston, MA 02110-1301, USA.
-*/
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
 
 /**
  * @file transport/transport_api_core.c
 #include "gnunet_arm_service.h"
 #include "gnunet_hello_lib.h"
 #include "gnunet_protocols.h"
-#include "gnunet_transport_core_service.h"
+#include "gnunet_transport_service.h"
 #include "transport.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "transport-api-core",__VA_ARGS__)
+#define LOG(kind, ...) GNUNET_log_from (kind, "transport-api-core", __VA_ARGS__)
 
 /**
  * If we could not send any payload to a peer for this amount of
@@ -112,18 +112,15 @@ struct Neighbour
    * Size of the message in @e env.
    */
   uint16_t env_size;
-
 };
 
 
-
 /**
  * Handle for the transport service (includes all of the
  * state for the transport service).
  */
 struct GNUNET_TRANSPORT_CoreHandle
 {
-
   /**
    * Closure for the callbacks.
    */
@@ -138,17 +135,17 @@ struct GNUNET_TRANSPORT_CoreHandle
   /**
    * function to call on connect events
    */
-  GNUNET_TRANSPORT_NotifyConnecT nc_cb;
+  GNUNET_TRANSPORT_NotifyConnect nc_cb;
 
   /**
    * function to call on disconnect events
    */
-  GNUNET_TRANSPORT_NotifyDisconnecT nd_cb;
+  GNUNET_TRANSPORT_NotifyDisconnect nd_cb;
 
   /**
    * function to call on excess bandwidth events
    */
-  GNUNET_TRANSPORT_NotifyExcessBandwidtH neb_cb;
+  GNUNET_TRANSPORT_NotifyExcessBandwidth neb_cb;
 
   /**
    * My client connection to the transport service.
@@ -181,12 +178,20 @@ struct GNUNET_TRANSPORT_CoreHandle
    */
   struct GNUNET_TIME_Relative reconnect_delay;
 
+  /**
+   * Internal counter to check how many more receive OK messages this
+   * CORE service is allowed to send in total. Just to detect easy
+   * cases of protocol violations by the CORE implementation.
+   * NOTE: we may want to make this stronger by counting per peer
+   * instead of globally.
+   */
+  unsigned int rom_pending;
+
   /**
    * Should we check that @e self matches what the service thinks?
    * (if #GNUNET_NO, then @e self is all zeros!).
    */
   int check_self;
-
 };
 
 
@@ -211,8 +216,7 @@ static struct Neighbour *
 neighbour_find (struct GNUNET_TRANSPORT_CoreHandle *h,
                 const struct GNUNET_PeerIdentity *peer)
 {
-  return GNUNET_CONTAINER_multipeermap_get (h->neighbours,
-                                            peer);
+  return GNUNET_CONTAINER_multipeermap_get (h->neighbours, peer);
 }
 
 
@@ -233,9 +237,7 @@ notify_excess_cb (void *cls)
        GNUNET_i2s (&n->id));
 
   if (NULL != h->neb_cb)
-    h->neb_cb (h->cls,
-               &n->id,
-               n->handlers_cls);
+    h->neb_cb (h->cls, &n->id, n->handlers_cls);
 }
 
 
@@ -250,9 +252,7 @@ notify_excess_cb (void *cls)
  *         #GNUNET_NO if not.
  */
 static int
-neighbour_delete (void *cls,
-                 const struct GNUNET_PeerIdentity *key,
-                  void *value)
+neighbour_delete (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
 {
   struct GNUNET_TRANSPORT_CoreHandle *handle = cls;
   struct Neighbour *n = value;
@@ -262,9 +262,7 @@ neighbour_delete (void *cls,
        GNUNET_i2s (key));
   GNUNET_BANDWIDTH_tracker_notification_stop (&n->out_tracker);
   if (NULL != handle->nd_cb)
-    handle->nd_cb (handle->cls,
-                   &n->id,
-                   n->handlers_cls);
+    handle->nd_cb (handle->cls, &n->id, n->handlers_cls);
   if (NULL != n->timeout_task)
   {
     GNUNET_SCHEDULER_cancel (n->timeout_task);
@@ -277,10 +275,9 @@ neighbour_delete (void *cls,
   }
   GNUNET_MQ_destroy (n->mq);
   GNUNET_assert (NULL == n->mq);
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multipeermap_remove (handle->neighbours,
-                                                       key,
-                                                       n));
+  GNUNET_assert (
+    GNUNET_YES ==
+    GNUNET_CONTAINER_multipeermap_remove (handle->neighbours, key, n));
   GNUNET_free (n);
   return GNUNET_YES;
 }
@@ -296,13 +293,13 @@ neighbour_delete (void *cls,
  * @param error error code
  */
 static void
-mq_error_handler (void *cls,
-                  enum GNUNET_MQ_Error error)
+mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Error receiving from transport service, disconnecting temporarily.\n");
+  LOG (GNUNET_ERROR_TYPE_ERROR,
+       "Error receiving from transport service (%d), disconnecting temporarily.\n",
+       error);
   disconnect_and_schedule_reconnect (h);
 }
 
@@ -315,14 +312,12 @@ mq_error_handler (void *cls,
  * @return #GNUNET_OK if message is well-formed
  */
 static int
-check_hello (void *cls,
-             const struct GNUNET_MessageHeader *msg)
+check_hello (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   struct GNUNET_PeerIdentity me;
 
   if (GNUNET_OK !=
-      GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) msg,
-                           &me))
+      GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) msg, &me))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
@@ -338,8 +333,7 @@ check_hello (void *cls,
  * @param msg message received
  */
 static void
-handle_hello (void *cls,
-              const struct GNUNET_MessageHeader *msg)
+handle_hello (void *cls, const struct GNUNET_MessageHeader *msg)
 {
   /* we do not care => FIXME: signal in options to NEVER send HELLOs! */
 }
@@ -386,8 +380,7 @@ notify_send_done (void *cls)
     n->env = NULL;
     n->traffic_overhead = 0;
   }
-  delay = GNUNET_BANDWIDTH_tracker_get_delay (&n->out_tracker,
-                                              128);
+  delay = GNUNET_BANDWIDTH_tracker_get_delay (&n->out_tracker, 128);
   if (0 == delay.rel_value_us)
   {
     n->is_ready = GNUNET_YES;
@@ -397,9 +390,8 @@ notify_send_done (void *cls)
   GNUNET_MQ_impl_send_in_flight (n->mq);
   /* cannot send even a small message without violating
      quota, wait a before allowing MQ to send next message */
-  n->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
-                                                  &notify_send_done_fin,
-                                                  n);
+  n->timeout_task =
+    GNUNET_SCHEDULER_add_delayed (delay, &notify_send_done_fin, n);
 }
 
 
@@ -425,27 +417,29 @@ mq_send_impl (struct GNUNET_MQ_Handle *mq,
 
   GNUNET_assert (GNUNET_YES == n->is_ready);
   msize = ntohs (msg->size);
-  if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof (*obm))
+  if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(*obm))
   {
     GNUNET_break (0);
     GNUNET_MQ_impl_send_continue (mq);
     return;
   }
   GNUNET_assert (NULL == n->env);
-  n->env = GNUNET_MQ_msg_nested_mh (obm,
-                                    GNUNET_MESSAGE_TYPE_TRANSPORT_SEND,
-                                    msg);
-  obm->reserved = htonl (0);
-  obm->timeout = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_MINUTES); /* FIXME: to be removed */
+  n->env =
+    GNUNET_MQ_msg_nested_mh (obm, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, msg);
+  {
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_get_current_envelope (mq);
+    obm->priority = htonl ((uint32_t) GNUNET_MQ_env_get_options (env));
+  }
+  obm->timeout = GNUNET_TIME_relative_hton (
+    GNUNET_TIME_UNIT_MINUTES); /* FIXME: to be removed */
   obm->peer = n->id;
   GNUNET_assert (NULL == n->timeout_task);
   n->is_ready = GNUNET_NO;
   n->env_size = ntohs (msg->size);
-  GNUNET_MQ_notify_sent (n->env,
-                         &notify_send_done,
-                         n);
-  GNUNET_MQ_send (h->mq,
-                  n->env);
+  GNUNET_MQ_notify_sent (n->env, &notify_send_done, n);
+  GNUNET_MQ_send (h->mq, n->env);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Queued message of type %u for neighbour `%s'.\n",
        ntohs (msg->type),
@@ -461,8 +455,7 @@ mq_send_impl (struct GNUNET_MQ_Handle *mq,
  * @param impl_state state of the implementation
  */
 static void
-mq_destroy_impl (struct GNUNET_MQ_Handle *mq,
-                 void *impl_state)
+mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
 {
   struct Neighbour *n = impl_state;
 
@@ -479,8 +472,7 @@ mq_destroy_impl (struct GNUNET_MQ_Handle *mq,
  * @param impl_state state specific to the implementation
  */
 static void
-mq_cancel_impl (struct GNUNET_MQ_Handle *mq,
-                void *impl_state)
+mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
 {
   struct Neighbour *n = impl_state;
 
@@ -504,8 +496,7 @@ mq_cancel_impl (struct GNUNET_MQ_Handle *mq,
  * @param error error code
  */
 static void
-peer_mq_error_handler (void *cls,
-                       enum GNUNET_MQ_Error error)
+peer_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
 {
   /* struct Neighbour *n = cls; */
 
@@ -527,12 +518,9 @@ outbound_bw_tracker_update (void *cls)
 
   if (NULL == n->timeout_task)
     return;
-  delay = GNUNET_BANDWIDTH_tracker_get_delay (&n->out_tracker,
-                                              128);
+  delay = GNUNET_BANDWIDTH_tracker_get_delay (&n->out_tracker, 128);
   GNUNET_SCHEDULER_cancel (n->timeout_task);
-  n->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
-                                                  &notify_send_done,
-                                                  n);
+  n->timeout_task = GNUNET_SCHEDULER_add_delayed (delay, &notify_send_done, n);
 }
 
 
@@ -543,8 +531,7 @@ outbound_bw_tracker_update (void *cls)
  * @param cim message received
  */
 static void
-handle_connect (void *cls,
-                const struct ConnectInfoMessage *cim)
+handle_connect (void *cls, const struct ConnectInfoMessage *cim)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
   struct Neighbour *n;
@@ -556,7 +543,7 @@ handle_connect (void *cls,
   n = neighbour_find (h, &cim->id);
   if (NULL != n)
   {
-    GNUNET_break (0);
+    GNUNET_break (0);  /* FIXME: this assertion seems to fail sometimes!? */
     disconnect_and_schedule_reconnect (h);
     return;
   }
@@ -573,13 +560,13 @@ handle_connect (void *cls,
                                   &notify_excess_cb,
                                   n);
   GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONTAINER_multipeermap_put (h->neighbours,
-                                                    &n->id,
-                                                    n,
-                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+                 GNUNET_CONTAINER_multipeermap_put (
+                   h->neighbours,
+                   &n->id,
+                   n,
+                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
 
-  GNUNET_BANDWIDTH_tracker_update_quota (&n->out_tracker,
-                                         cim->quota_out);
+  GNUNET_BANDWIDTH_tracker_update_quota (&n->out_tracker, cim->quota_out);
   n->mq = GNUNET_MQ_queue_for_callbacks (&mq_send_impl,
                                          &mq_destroy_impl,
                                          &mq_cancel_impl,
@@ -589,11 +576,8 @@ handle_connect (void *cls,
                                          n);
   if (NULL != h->nc_cb)
   {
-    n->handlers_cls = h->nc_cb (h->cls,
-                                &n->id,
-                                n->mq);
-    GNUNET_MQ_set_handlers_closure (n->mq,
-                                    n->handlers_cls);
+    n->handlers_cls = h->nc_cb (h->cls, &n->id, n->mq);
+    GNUNET_MQ_set_handlers_closure (n->mq, n->handlers_cls);
   }
 }
 
@@ -605,8 +589,7 @@ handle_connect (void *cls,
  * @param dim message received
  */
 static void
-handle_disconnect (void *cls,
-                   const struct DisconnectInfoMessage *dim)
+handle_disconnect (void *cls, const struct DisconnectInfoMessage *dim)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
   struct Neighbour *n;
@@ -622,10 +605,7 @@ handle_disconnect (void *cls,
     disconnect_and_schedule_reconnect (h);
     return;
   }
-  GNUNET_assert (GNUNET_YES ==
-                 neighbour_delete (h,
-                                   &dim->peer,
-                                   n));
+  GNUNET_assert (GNUNET_YES == neighbour_delete (h, &dim->peer, n));
 }
 
 
@@ -636,8 +616,7 @@ handle_disconnect (void *cls,
  * @param okm message received
  */
 static void
-handle_send_ok (void *cls,
-                const struct SendOkMessage *okm)
+handle_send_ok (void *cls, const struct SendOkMessage *okm)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
   struct Neighbour *n;
@@ -650,8 +629,7 @@ handle_send_ok (void *cls,
        "Receiving SEND_OK message, transmission to %s %s.\n",
        GNUNET_i2s (&okm->peer),
        ntohl (okm->success) == GNUNET_OK ? "succeeded" : "failed");
-  n = neighbour_find (h,
-                      &okm->peer);
+  n = neighbour_find (h, &okm->peer);
   if (NULL == n)
   {
     /* We should never get a 'SEND_OK' for a peer that we are not
@@ -678,14 +656,13 @@ handle_send_ok (void *cls,
  * @param im message received
  */
 static int
-check_recv (void *cls,
-             const struct InboundMessage *im)
+check_recv (void *cls, const struct InboundMessage *im)
 {
   const struct GNUNET_MessageHeader *imm;
   uint16_t size;
 
-  size = ntohs (im->header.size) - sizeof (*im);
-  if (size < sizeof (struct GNUNET_MessageHeader))
+  size = ntohs (im->header.size) - sizeof(*im);
+  if (size < sizeof(struct GNUNET_MessageHeader))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
@@ -707,12 +684,11 @@ check_recv (void *cls,
  * @param im message received
  */
 static void
-handle_recv (void *cls,
-             const struct InboundMessage *im)
+handle_recv (void *cls, const struct InboundMessage *im)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
-  const struct GNUNET_MessageHeader *imm
-    (const struct GNUNET_MessageHeader *) &im[1];
+  const struct GNUNET_MessageHeader *imm =
+    (const struct GNUNET_MessageHeader *) &im[1];
   struct Neighbour *n;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -727,8 +703,8 @@ handle_recv (void *cls,
     disconnect_and_schedule_reconnect (h);
     return;
   }
-  GNUNET_MQ_inject_message (n->mq,
-                            imm);
+  h->rom_pending++;
+  GNUNET_MQ_inject_message (n->mq, imm);
 }
 
 
@@ -739,26 +715,24 @@ handle_recv (void *cls,
  * @param msg message received
  */
 static void
-handle_set_quota (void *cls,
-                  const struct QuotaSetMessage *qm)
+handle_set_quota (void *cls, const struct QuotaSetMessage *qm)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
   struct Neighbour *n;
 
-  n = neighbour_find (h,
-                     &qm->peer);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Receiving SET_QUOTA message for `%s' with quota %u\n",
+       GNUNET_i2s (&qm->peer),
+       ntohl (qm->quota.value__));
+  n = neighbour_find (h, &qm->peer);
   if (NULL == n)
   {
-    GNUNET_break (0);
+    GNUNET_break (
+      0);   /* FIXME: julius reports this assertion fails sometimes? */
     disconnect_and_schedule_reconnect (h);
     return;
   }
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Receiving SET_QUOTA message for `%s' with quota %u\n",
-       GNUNET_i2s (&qm->peer),
-       ntohl (qm->quota.value__));
-  GNUNET_BANDWIDTH_tracker_update_quota (&n->out_tracker,
-                                         qm->quota);
+  GNUNET_BANDWIDTH_tracker_update_quota (&n->out_tracker, qm->quota);
 }
 
 
@@ -771,8 +745,8 @@ static void
 reconnect (void *cls)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h = cls;
-  struct GNUNET_MQ_MessageHandler handlers[] = {
-    GNUNET_MQ_hd_var_size (hello,
+  struct GNUNET_MQ_MessageHandler handlers[] =
+  { GNUNET_MQ_hd_var_size (hello,
                            GNUNET_MESSAGE_TYPE_HELLO,
                            struct GNUNET_MessageHeader,
                            h),
@@ -796,25 +770,19 @@ reconnect (void *cls)
                              GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA,
                              struct QuotaSetMessage,
                              h),
-    GNUNET_MQ_handler_end ()
-  };
+    GNUNET_MQ_handler_end () };
   struct GNUNET_MQ_Envelope *env;
   struct StartMessage *s;
   uint32_t options;
 
   h->reconnect_task = NULL;
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Connecting to transport service.\n");
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n");
   GNUNET_assert (NULL == h->mq);
-  h->mq = GNUNET_CLIENT_connect (h->cfg,
-                                 "transport",
-                                 handlers,
-                                 &mq_error_handler,
-                                 h);
+  h->mq =
+    GNUNET_CLIENT_connect (h->cfg, "transport", handlers, &mq_error_handler, h);
   if (NULL == h->mq)
     return;
-  env = GNUNET_MQ_msg (s,
-                       GNUNET_MESSAGE_TYPE_TRANSPORT_START);
+  env = GNUNET_MQ_msg (s, GNUNET_MESSAGE_TYPE_TRANSPORT_START);
   options = 0;
   if (h->check_self)
     options |= 1;
@@ -822,8 +790,7 @@ reconnect (void *cls)
     options |= 2;
   s->options = htonl (options);
   s->self = h->self;
-  GNUNET_MQ_send (h->mq,
-                  env);
+  GNUNET_MQ_send (h->mq, env);
 }
 
 
@@ -838,9 +805,7 @@ disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h)
 {
   GNUNET_assert (NULL == h->reconnect_task);
   /* Forget about all neighbours that we used to be connected to */
-  GNUNET_CONTAINER_multipeermap_iterate (h->neighbours,
-                                         &neighbour_delete,
-                                         h);
+  GNUNET_CONTAINER_multipeermap_iterate (h->neighbours, &neighbour_delete, h);
   if (NULL != h->mq)
   {
     GNUNET_MQ_destroy (h->mq);
@@ -848,12 +813,9 @@ disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h)
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Scheduling task to reconnect to transport service in %s.\n",
-       GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay,
-                                               GNUNET_YES));
+       GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
   h->reconnect_task =
-      GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
-                                    &reconnect,
-                                    h);
+    GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
 }
 
@@ -871,8 +833,7 @@ GNUNET_TRANSPORT_core_get_mq (struct GNUNET_TRANSPORT_CoreHandle *handle,
 {
   struct Neighbour *n;
 
-  n = neighbour_find (handle,
-                      peer);
+  n = neighbour_find (handle, peer);
   if (NULL == n)
     return NULL;
   return n->mq;
@@ -895,12 +856,12 @@ GNUNET_TRANSPORT_core_get_mq (struct GNUNET_TRANSPORT_CoreHandle *handle,
  */
 struct GNUNET_TRANSPORT_CoreHandle *
 GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                              const struct GNUNET_PeerIdentity *self,
-                              const struct GNUNET_MQ_MessageHandler *handlers,
-                              void *cls,
-                              GNUNET_TRANSPORT_NotifyConnecT nc,
-                              GNUNET_TRANSPORT_NotifyDisconnecT nd,
-                              GNUNET_TRANSPORT_NotifyExcessBandwidtH neb)
+                               const struct GNUNET_PeerIdentity *self,
+                               const struct GNUNET_MQ_MessageHandler *handlers,
+                               void *cls,
+                               GNUNET_TRANSPORT_NotifyConnect nc,
+                               GNUNET_TRANSPORT_NotifyDisconnect nd,
+                               GNUNET_TRANSPORT_NotifyExcessBandwidth neb)
 {
   struct GNUNET_TRANSPORT_CoreHandle *h;
   unsigned int i;
@@ -919,15 +880,14 @@ GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
   if (NULL != handlers)
   {
-    for (i=0;NULL != handlers[i].cb; i++) ;
-    h->handlers = GNUNET_new_array (i + 1,
-                                    struct GNUNET_MQ_MessageHandler);
+    for (i = 0; NULL != handlers[i].cb; i++)
+      ;
+    h->handlers = GNUNET_new_array (i + 1, struct GNUNET_MQ_MessageHandler);
     GNUNET_memcpy (h->handlers,
-                  handlers,
-                  i * sizeof (struct GNUNET_MQ_MessageHandler));
+                   handlers,
+                   i * sizeof(struct GNUNET_MQ_MessageHandler));
   }
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Connecting to transport service\n");
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service\n");
   reconnect (h);
   if (NULL == h->mq)
   {
@@ -936,8 +896,7 @@ GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
     return NULL;
   }
   h->neighbours =
-    GNUNET_CONTAINER_multipeermap_create (STARTING_NEIGHBOURS_SIZE,
-                                          GNUNET_YES);
+    GNUNET_CONTAINER_multipeermap_create (STARTING_NEIGHBOURS_SIZE, GNUNET_YES);
   return h;
 }
 
@@ -945,13 +904,13 @@ GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
 /**
  * Disconnect from the transport service.
  *
- * @param handle handle to the service as returned from #GNUNET_TRANSPORT_core_connect()
+ * @param handle handle to the service as returned from
+ * #GNUNET_TRANSPORT_core_connect()
  */
 void
 GNUNET_TRANSPORT_core_disconnect (struct GNUNET_TRANSPORT_CoreHandle *handle)
 {
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Transport disconnect called!\n");
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n");
   /* this disconnects all neighbours... */
   if (NULL == handle->reconnect_task)
     disconnect_and_schedule_reconnect (handle);
@@ -969,4 +928,40 @@ GNUNET_TRANSPORT_core_disconnect (struct GNUNET_TRANSPORT_CoreHandle *handle)
 }
 
 
+/**
+ * Notification from the CORE service to the TRANSPORT service
+ * that the CORE service has finished processing a message from
+ * TRANSPORT (via the @code{handlers} of #GNUNET_TRANSPORT_core_connect())
+ * and that it is thus now OK for TRANSPORT to send more messages
+ * for @a pid.
+ *
+ * Used to provide flow control, this is our equivalent to
+ * #GNUNET_SERVICE_client_continue() of an ordinary service.
+ *
+ * Note that due to the use of a window, TRANSPORT may send multiple
+ * messages destined for the same peer even without an intermediate
+ * call to this function. However, CORE must still call this function
+ * once per message received, as otherwise eventually the window will
+ * be full and TRANSPORT will stop providing messages to CORE for @a
+ * pid.
+ *
+ * @param ch core handle
+ * @param pid which peer was the message from that was fully processed by CORE
+ */
+void
+GNUNET_TRANSPORT_core_receive_continue (struct GNUNET_TRANSPORT_CoreHandle *ch,
+                                        const struct GNUNET_PeerIdentity *pid)
+{
+  struct RecvOkMessage *rom;
+  struct GNUNET_MQ_Envelope *env;
+
+  GNUNET_assert (ch->rom_pending > 0);
+  ch->rom_pending--;
+  env = GNUNET_MQ_msg (rom, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK);
+  rom->increase_window_delta = htonl (1);
+  rom->peer = *pid;
+  GNUNET_MQ_send (ch->mq, env);
+}
+
+
 /* end of transport_api_core.c */