improve future calculations if messages are skipped in unreliable in-order channels
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_core.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2017 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file cadet/gnunet-service-cadet_core.c
23  * @brief cadet service; interaction with CORE service
24  * @author Bartlomiej Polot
25  * @author Christian Grothoff
26  *
27  * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
28  *
29  * TODO:
30  * - Optimization: given BROKEN messages, destroy paths (?)
31  */
32 #include "platform.h"
33 #include "gnunet-service-cadet_core.h"
34 #include "gnunet-service-cadet_paths.h"
35 #include "gnunet-service-cadet_peer.h"
36 #include "gnunet-service-cadet_connection.h"
37 #include "gnunet-service-cadet_tunnels.h"
38 #include "gnunet_core_service.h"
39 #include "gnunet_statistics_service.h"
40 #include "cadet_protocol.h"
41
42
43 #define LOG(level, ...) GNUNET_log_from(level,"cadet-cor",__VA_ARGS__)
44
45 /**
46  * Information we keep per direction for a route.
47  */
48 struct RouteDirection;
49
50
51 /**
52  * Set of CadetRoutes that have exactly the same number of messages
53  * in their buffer.  Used so we can efficiently find all of those
54  * routes that have the current maximum of messages in the buffer (in
55  * case we have to purge).
56  */
57 struct Rung
58 {
59
60   /**
61    * Rung of RouteDirections with one more buffer entry each.
62    */
63   struct Rung *next;
64
65   /**
66    * Rung of RouteDirections with one less buffer entry each.
67    */
68   struct Rung *prev;
69
70   /**
71    * DLL of route directions with a number of buffer entries matching this rung.
72    */
73   struct RouteDirection *rd_head;
74
75   /**
76    * DLL of route directions with a number of buffer entries matching this rung.
77    */
78   struct RouteDirection *rd_tail;
79
80   /**
81    * Total number of route directions in this rung.
82    */
83   unsigned int num_routes;
84
85   /**
86    * Number of messages route directions at this rung have
87    * in their buffer.
88    */
89   unsigned int rung_off;
90 };
91
92
93 /**
94  * Information we keep per direction for a route.
95  */
96 struct RouteDirection
97 {
98
99   /**
100    * DLL of other route directions within the same `struct Rung`.
101    */
102   struct RouteDirection *prev;
103
104   /**
105    * DLL of other route directions within the same `struct Rung`.
106    */
107   struct RouteDirection *next;
108
109   /**
110    * Rung of this route direction (matches length of the buffer DLL).
111    */
112   struct Rung *rung;
113
114   /**
115    * Head of DLL of envelopes we have in the buffer for this direction.
116    */
117   struct GNUNET_MQ_Envelope *env_head;
118
119   /**
120    * Tail of DLL of envelopes we have in the buffer for this direction.
121    */
122   struct GNUNET_MQ_Envelope *env_tail;
123
124   /**
125    * Target peer.
126    */
127   struct CadetPeer *hop;
128
129   /**
130    * Route this direction is part of.
131    */
132   struct CadetRoute *my_route;
133
134   /**
135    * Message queue manager for @e hop.
136    */
137   struct GCP_MessageQueueManager *mqm;
138
139   /**
140    * Is @e mqm currently ready for transmission?
141    */
142   int is_ready;
143
144 };
145
146
147 /**
148  * Description of a segment of a `struct CadetConnection` at the
149  * intermediate peers.  Routes are basically entries in a peer's
150  * routing table for forwarding traffic.  At both endpoints, the
151  * routes are terminated by a `struct CadetConnection`, which knows
152  * the complete `struct CadetPath` that is formed by the individual
153  * routes.
154  */
155 struct CadetRoute
156 {
157
158   /**
159    * Information about the next hop on this route.
160    */
161   struct RouteDirection next;
162
163   /**
164    * Information about the previous hop on this route.
165    */
166   struct RouteDirection prev;
167
168   /**
169    * Unique identifier for the connection that uses this route.
170    */
171   struct GNUNET_CADET_ConnectionTunnelIdentifier cid;
172
173   /**
174    * When was this route last in use?
175    */
176   struct GNUNET_TIME_Absolute last_use;
177
178   /**
179    * Position of this route in the #route_heap.
180    */
181   struct GNUNET_CONTAINER_HeapNode *hn;
182
183   /**
184    * Options for the route, control buffering.
185    */
186   enum GNUNET_CADET_ChannelOption options;
187 };
188
189
190 /**
191  * Handle to the CORE service.
192  */
193 static struct GNUNET_CORE_Handle *core;
194
195 /**
196  * Routes on which this peer is an intermediate.
197  */
198 static struct GNUNET_CONTAINER_MultiShortmap *routes;
199
200 /**
201  * Heap of routes, MIN-sorted by last activity.
202  */
203 static struct GNUNET_CONTAINER_Heap *route_heap;
204
205 /**
206  * Rung zero (always pointed to by #rung_head).
207  */
208 static struct Rung rung_zero;
209
210 /**
211  * DLL of rungs, with the head always point to a rung of
212  * route directions with no messages in the queue.
213  */
214 static struct Rung *rung_head = &rung_zero;
215
216 /**
217  * Tail of the #rung_head DLL.
218  */
219 static struct Rung *rung_tail = &rung_zero;
220
221 /**
222  * Maximum number of concurrent routes this peer will support.
223  */
224 static unsigned long long max_routes;
225
226 /**
227  * Maximum number of envelopes we will buffer at this peer.
228  */
229 static unsigned long long max_buffers;
230
231 /**
232  * Current number of envelopes we have buffered at this peer.
233  */
234 static unsigned long long cur_buffers;
235
236 /**
237  * Task to timeout routes.
238  */
239 static struct GNUNET_SCHEDULER_Task *timeout_task;
240
241
242 /**
243  * Get the route corresponding to a hash.
244  *
245  * @param cid hash generated from the connection identifier
246  */
247 static struct CadetRoute *
248 get_route (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid)
249 {
250   return GNUNET_CONTAINER_multishortmap_get (routes,
251                                              &cid->connection_of_tunnel);
252 }
253
254
255 /**
256  * Lower the rung in which @a dir is by 1.
257  *
258  * @param dir direction to lower in rung.
259  */
260 static void
261 lower_rung (struct RouteDirection *dir)
262 {
263   struct Rung *rung = dir->rung;
264   struct Rung *prev;
265
266   GNUNET_CONTAINER_DLL_remove (rung->rd_head,
267                                rung->rd_tail,
268                                dir);
269   prev = rung->prev;
270   GNUNET_assert (NULL != prev);
271   if (prev->rung_off != rung->rung_off - 1)
272   {
273     prev = GNUNET_new (struct Rung);
274     prev->rung_off = rung->rung_off - 1;
275     GNUNET_CONTAINER_DLL_insert_after (rung_head,
276                                        rung_tail,
277                                        rung->prev,
278                                        prev);
279   }
280   GNUNET_assert (NULL != prev);
281   GNUNET_CONTAINER_DLL_insert (prev->rd_head,
282                                prev->rd_tail,
283                                dir);
284   dir->rung = prev;
285 }
286
287
288 /**
289  * Discard the buffer @a env from the route direction @a dir and
290  * move @a dir down a rung.
291  *
292  * @param dir direction that contains the @a env in the buffer
293  * @param env envelope to discard
294  */
295 static void
296 discard_buffer (struct RouteDirection *dir,
297                 struct GNUNET_MQ_Envelope *env)
298 {
299   GNUNET_MQ_dll_remove (&dir->env_head,
300                         &dir->env_tail,
301                         env);
302   cur_buffers--;
303   GNUNET_MQ_discard (env);
304   lower_rung (dir);
305   GNUNET_STATISTICS_set (stats,
306                          "# buffer use",
307                          cur_buffers,
308                          GNUNET_NO);
309 }
310
311
312 /**
313  * Discard all messages from the highest rung, to make space.
314  */
315 static void
316 discard_all_from_rung_tail ()
317 {
318   struct Rung *tail = rung_tail;
319   struct RouteDirection *dir;
320
321   while (NULL != (dir = tail->rd_head))
322   {
323     LOG (GNUNET_ERROR_TYPE_DEBUG,
324          "Queue full due new message %s on connection %s, dropping old message\n",
325          GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
326     GNUNET_STATISTICS_update (stats,
327                               "# messages dropped due to full buffer",
328                               1,
329                               GNUNET_NO);
330     discard_buffer (dir,
331                     dir->env_head);
332   }
333   GNUNET_CONTAINER_DLL_remove (rung_head,
334                                rung_tail,
335                                tail);
336   GNUNET_free (tail);
337 }
338
339
340 /**
341  * We message @a msg from @a prev.  Find its route by @a cid and
342  * forward to the next hop.  Drop and signal broken route if we do not
343  * have a route.
344  *
345  * @param prev previous hop (sender)
346  * @param cid connection identifier, tells us which route to use
347  * @param msg the message to forward
348  */
349 static void
350 route_message (struct CadetPeer *prev,
351                const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
352                const struct GNUNET_MessageHeader *msg)
353 {
354   struct CadetRoute *route;
355   struct RouteDirection *dir;
356   struct Rung *rung;
357   struct Rung *nxt;
358   struct GNUNET_MQ_Envelope *env;
359
360   route = get_route (cid);
361   if (NULL == route)
362   {
363     struct GNUNET_MQ_Envelope *env;
364     struct GNUNET_CADET_ConnectionBrokenMessage *bm;
365
366     LOG (GNUNET_ERROR_TYPE_DEBUG,
367          "Failed to route message of type %u from %s on connection %s: no route\n",
368          ntohs (msg->type),
369          GCP_2s (prev),
370          GNUNET_sh2s (&cid->connection_of_tunnel));
371     switch (ntohs (msg->type))
372     {
373     case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY:
374     case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN:
375       /* No need to respond to these! */
376       return;
377     }
378     env = GNUNET_MQ_msg (bm,
379                          GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
380     bm->cid = *cid;
381     bm->peer1 = my_full_id;
382     GCP_send_ooo (prev,
383                   env);
384     return;
385   }
386   route->last_use = GNUNET_TIME_absolute_get ();
387   GNUNET_CONTAINER_heap_update_cost (route->hn,
388                                      route->last_use.abs_value_us);
389   dir = (prev == route->prev.hop) ? &route->next : &route->prev;
390   if (GNUNET_YES == dir->is_ready)
391   {
392     LOG (GNUNET_ERROR_TYPE_DEBUG,
393          "Routing message of type %u from %s to %s on connection %s\n",
394          ntohs (msg->type),
395          GCP_2s (prev),
396          GNUNET_i2s (GCP_get_id (dir->hop)),
397          GNUNET_sh2s (&cid->connection_of_tunnel));
398     dir->is_ready = GNUNET_NO;
399     GCP_send (dir->mqm,
400               GNUNET_MQ_msg_copy (msg));
401     return;
402   }
403   /* Check if buffering is disallowed, and if so, make sure we only queue
404      one message per direction. */
405   if ( (0 != (route->options & GNUNET_CADET_OPTION_NOBUFFER)) &&
406        (NULL != dir->env_head) )
407     discard_buffer (dir,
408                     dir->env_head);
409   /* Check for duplicates */
410   for (const struct GNUNET_MQ_Envelope *env = dir->env_head;
411        NULL != env;
412        env = GNUNET_MQ_env_next (env))
413   {
414     const struct GNUNET_MessageHeader *hdr = GNUNET_MQ_env_get_msg (env);
415
416     if ( (hdr->size == msg->size) &&
417          (0 == memcmp (hdr,
418                        msg,
419                        ntohs (msg->size))) )
420     {
421       LOG (GNUNET_ERROR_TYPE_DEBUG,
422            "Received duplicate of message already in buffer, dropping\n");
423       GNUNET_STATISTICS_update (stats,
424                                 "# messages dropped due to duplicate in buffer",
425                                 1,
426                                 GNUNET_NO);
427       return;
428     }
429   }
430
431   rung = dir->rung;
432   if (cur_buffers == max_buffers)
433   {
434     /* Need to make room. */
435     if (NULL != rung->next)
436     {
437       /* Easy case, drop messages from route directions in highest rung */
438       discard_all_from_rung_tail ();
439     }
440     else
441     {
442       /* We are in the highest rung, drop our own! */
443       LOG (GNUNET_ERROR_TYPE_DEBUG,
444            "Queue full due new message %s on connection %s, dropping old message\n",
445            GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
446       GNUNET_STATISTICS_update (stats,
447                                 "# messages dropped due to full buffer",
448                                 1,
449                                 GNUNET_NO);
450       discard_buffer (dir,
451                       dir->env_head);
452       rung = dir->rung;
453     }
454   }
455   /* remove 'dir' from current rung */
456   GNUNET_CONTAINER_DLL_remove (rung->rd_head,
457                                rung->rd_tail,
458                                dir);
459   /* make 'nxt' point to the next higher rung, create if necessary */
460   nxt = rung->next;
461   if ( (NULL == nxt) ||
462        (rung->rung_off + 1 != nxt->rung_off) )
463   {
464     nxt = GNUNET_new (struct Rung);
465     nxt->rung_off = rung->rung_off + 1;
466     GNUNET_CONTAINER_DLL_insert_after (rung_head,
467                                        rung_tail,
468                                        rung,
469                                        nxt);
470   }
471   /* insert 'dir' into next higher rung */
472   GNUNET_CONTAINER_DLL_insert (nxt->rd_head,
473                                nxt->rd_tail,
474                                dir);
475   dir->rung = nxt;
476
477   /* add message into 'dir' buffer */
478   LOG (GNUNET_ERROR_TYPE_DEBUG,
479        "Queueing new message of type %u from %s to %s on connection %s\n",
480        ntohs (msg->type),
481        GCP_2s (prev),
482        GNUNET_i2s (GCP_get_id (dir->hop)),
483        GNUNET_sh2s (&cid->connection_of_tunnel));
484   env = GNUNET_MQ_msg_copy (msg);
485   GNUNET_MQ_dll_insert_tail (&dir->env_head,
486                              &dir->env_tail,
487                              env);
488   cur_buffers++;
489   GNUNET_STATISTICS_set (stats,
490                          "# buffer use",
491                          cur_buffers,
492                          GNUNET_NO);
493   /* Clean up 'rung' if now empty (and not head) */
494   if ( (NULL == rung->rd_head) &&
495        (rung != rung_head) )
496   {
497     GNUNET_CONTAINER_DLL_remove (rung_head,
498                                  rung_tail,
499                                  rung);
500     GNUNET_free (rung);
501   }
502 }
503
504
505 /**
506  * Check if the create_connection message has the appropriate size.
507  *
508  * @param cls Closure (unused).
509  * @param msg Message to check.
510  *
511  * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise.
512  */
513 static int
514 check_connection_create (void *cls,
515                          const struct GNUNET_CADET_ConnectionCreateMessage *msg)
516 {
517   uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
518
519   if (0 != (size % sizeof (struct GNUNET_PeerIdentity)))
520   {
521     GNUNET_break_op (0);
522     return GNUNET_NO;
523   }
524   return GNUNET_YES;
525 }
526
527
528 /**
529  * Free internal data of a route direction.
530  *
531  * @param dir direction to destroy (do NOT free memory of 'dir' itself)
532  */
533 static void
534 destroy_direction (struct RouteDirection *dir)
535 {
536   struct GNUNET_MQ_Envelope *env;
537
538   while (NULL != (env = dir->env_head))
539   {
540     GNUNET_STATISTICS_update (stats,
541                               "# messages dropped due to route destruction",
542                               1,
543                               GNUNET_NO);
544     discard_buffer (dir,
545                     env);
546   }
547   if (NULL != dir->mqm)
548   {
549     GCP_request_mq_cancel (dir->mqm,
550                            NULL);
551     dir->mqm = NULL;
552   }
553   GNUNET_CONTAINER_DLL_remove (rung_head->rd_head,
554                                rung_head->rd_tail,
555                                dir);
556 }
557
558
559 /**
560  * Destroy our state for @a route.
561  *
562  * @param route route to destroy
563  */
564 static void
565 destroy_route (struct CadetRoute *route)
566 {
567   LOG (GNUNET_ERROR_TYPE_DEBUG,
568        "Destroying route from %s to %s of connection %s\n",
569        GNUNET_i2s  (GCP_get_id (route->prev.hop)),
570        GNUNET_i2s2 (GCP_get_id (route->next.hop)),
571        GNUNET_sh2s (&route->cid.connection_of_tunnel));
572   GNUNET_assert (route ==
573                  GNUNET_CONTAINER_heap_remove_node (route->hn));
574   GNUNET_assert (GNUNET_YES ==
575                  GNUNET_CONTAINER_multishortmap_remove (routes,
576                                                         &route->cid.connection_of_tunnel,
577                                                         route));
578   GNUNET_STATISTICS_set (stats,
579                          "# routes",
580                          GNUNET_CONTAINER_multishortmap_size (routes),
581                          GNUNET_NO);
582   destroy_direction (&route->prev);
583   destroy_direction (&route->next);
584   GNUNET_free (route);
585 }
586
587
588 /**
589  * Send message that a route is broken between @a peer1 and @a peer2.
590  *
591  * @param target where to send the message
592  * @param cid connection identifier to use
593  * @param peer1 one of the peers where a link is broken
594  * @param peer2 another one of the peers where a link is broken
595  */
596 static void
597 send_broken (struct RouteDirection *target,
598              const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
599              const struct GNUNET_PeerIdentity *peer1,
600              const struct GNUNET_PeerIdentity *peer2)
601 {
602   struct GNUNET_MQ_Envelope *env;
603   struct GNUNET_CADET_ConnectionBrokenMessage *bm;
604
605   if (NULL == target->mqm)
606     return; /* Can't send notification, connection is down! */
607   LOG (GNUNET_ERROR_TYPE_DEBUG,
608        "Notifying %s about BROKEN route at %s-%s of connection %s\n",
609        GCP_2s (target->hop),
610        GNUNET_i2s (peer1),
611        GNUNET_i2s2 (peer2),
612        GNUNET_sh2s (&cid->connection_of_tunnel));
613
614   env = GNUNET_MQ_msg (bm,
615                        GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
616   bm->cid = *cid;
617   if (NULL != peer1)
618     bm->peer1 = *peer1;
619   if (NULL != peer2)
620     bm->peer2 = *peer2;
621   GCP_request_mq_cancel (target->mqm,
622                          env);
623   target->mqm = NULL;
624 }
625
626
627 /**
628  * Function called to check if any routes have timed out, and if
629  * so, to clean them up.  Finally, schedules itself again at the
630  * earliest time where there might be more work.
631  *
632  * @param cls NULL
633  */
634 static void
635 timeout_cb (void *cls)
636 {
637   struct CadetRoute *r;
638   struct GNUNET_TIME_Relative linger;
639   struct GNUNET_TIME_Absolute exp;
640
641   timeout_task = NULL;
642   linger = GNUNET_TIME_relative_multiply (keepalive_period,
643                                           3);
644   while (NULL != (r = GNUNET_CONTAINER_heap_peek (route_heap)))
645   {
646     exp = GNUNET_TIME_absolute_add (r->last_use,
647                                     linger);
648     if (0 != GNUNET_TIME_absolute_get_remaining (exp).rel_value_us)
649     {
650       /* Route not yet timed out, wait until it does. */
651       timeout_task = GNUNET_SCHEDULER_add_at (exp,
652                                               &timeout_cb,
653                                               NULL);
654       return;
655     }
656     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
657                 "Sending BROKEN due to timeout (%s was last use, %s linger)\n",
658                 GNUNET_STRINGS_absolute_time_to_string (r->last_use),
659                 GNUNET_STRINGS_relative_time_to_string (linger,
660                                                         GNUNET_YES));
661     send_broken (&r->prev,
662                  &r->cid,
663                  NULL,
664                  NULL);
665     send_broken (&r->next,
666                  &r->cid,
667                  NULL,
668                  NULL);
669     destroy_route (r);
670   }
671   /* No more routes left, so no need for a #timeout_task */
672 }
673
674
675 /**
676  * Function called when the message queue to the previous hop
677  * becomes available/unavailable.  We expect this function to
678  * be called immediately when we register, and then again
679  * later if the connection ever goes down.
680  *
681  * @param cls the `struct RouteDirection`
682  * @param available #GNUNET_YES if sending is now possible,
683  *                  #GNUNET_NO if sending is no longer possible
684  *                  #GNUNET_SYSERR if sending is no longer possible
685  *                                 and the last envelope was discarded
686  */
687 static void
688 dir_ready_cb (void *cls,
689               int ready)
690 {
691   struct RouteDirection *dir = cls;
692   struct CadetRoute *route = dir->my_route;
693   struct RouteDirection *odir;
694
695   if (GNUNET_YES == ready)
696   {
697     struct GNUNET_MQ_Envelope *env;
698
699     dir->is_ready = GNUNET_YES;
700     if (NULL != (env = dir->env_head))
701     {
702       GNUNET_MQ_dll_remove (&dir->env_head,
703                             &dir->env_tail,
704                             env);
705       cur_buffers--;
706       GNUNET_STATISTICS_set (stats,
707                              "# buffer use",
708                              cur_buffers,
709                              GNUNET_NO);
710       lower_rung (dir);
711       dir->is_ready = GNUNET_NO;
712       GCP_send (dir->mqm,
713                 env);
714     }
715     return;
716   }
717   odir = (dir == &route->next) ? &route->prev : &route->next;
718   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719               "Sending BROKEN due to MQ going down\n");
720   send_broken (&route->next,
721                &route->cid,
722                GCP_get_id (odir->hop),
723                &my_full_id);
724   destroy_route (route);
725 }
726
727
728 /**
729  * Initialize one of the directions of a route.
730  *
731  * @param route route the direction belongs to
732  * @param dir direction to initialize
733  * @param hop next hop on in the @a dir
734  */
735 static void
736 dir_init (struct RouteDirection *dir,
737           struct CadetRoute *route,
738           struct CadetPeer *hop)
739 {
740   dir->hop = hop;
741   dir->my_route = route;
742   dir->mqm = GCP_request_mq (hop,
743                              &dir_ready_cb,
744                              dir);
745   GNUNET_CONTAINER_DLL_insert (rung_head->rd_head,
746                                rung_head->rd_tail,
747                                dir);
748   dir->rung = rung_head;
749   GNUNET_assert (GNUNET_YES == dir->is_ready);
750 }
751
752
753 /**
754  * We could not create the desired route.  Send a
755  * #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
756  * message to @a target.
757  *
758  * @param target who should receive the message
759  * @param cid identifier of the connection/route that failed
760  * @param failure_at neighbour with which we failed to route,
761  *        or NULL.
762  */
763 static void
764 send_broken_without_mqm (struct CadetPeer *target,
765                          const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
766                          const struct GNUNET_PeerIdentity *failure_at)
767 {
768   struct GNUNET_MQ_Envelope *env;
769   struct GNUNET_CADET_ConnectionBrokenMessage *bm;
770
771   env = GNUNET_MQ_msg (bm,
772                        GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
773   bm->cid = *cid;
774   bm->peer1 = my_full_id;
775   if (NULL != failure_at)
776     bm->peer2 = *failure_at;
777   GCP_send_ooo (target,
778                 env);
779 }
780
781
782 /**
783  * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE
784  *
785  * @param cls Closure (CadetPeer for neighbor that sent the message).
786  * @param msg Message itself.
787  */
788 static void
789 handle_connection_create (void *cls,
790                           const struct GNUNET_CADET_ConnectionCreateMessage *msg)
791 {
792   struct CadetPeer *sender = cls;
793   struct CadetPeer *next;
794   const struct GNUNET_PeerIdentity *pids = (const struct GNUNET_PeerIdentity *) &msg[1];
795   struct CadetRoute *route;
796   uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
797   unsigned int path_length;
798   unsigned int off;
799   enum GNUNET_CADET_ChannelOption options;
800
801   options = (enum GNUNET_CADET_ChannelOption) ntohl (msg->options);
802   path_length = size / sizeof (struct GNUNET_PeerIdentity);
803   if (0 == path_length)
804   {
805     LOG (GNUNET_ERROR_TYPE_DEBUG,
806          "Dropping CADET_CONNECTION_CREATE with empty path\n");
807     GNUNET_break_op (0);
808     return;
809   }
810   LOG (GNUNET_ERROR_TYPE_DEBUG,
811        "Handling CADET_CONNECTION_CREATE from %s for CID %s with %u hops\n",
812        GCP_2s (sender),
813        GNUNET_sh2s (&msg->cid.connection_of_tunnel),
814        path_length);
815   /* Check for loops */
816   {
817     struct GNUNET_CONTAINER_MultiPeerMap *map;
818
819     map = GNUNET_CONTAINER_multipeermap_create (path_length * 2,
820                                                 GNUNET_YES);
821     GNUNET_assert (NULL != map);
822     for (unsigned int i=0;i<path_length;i++)
823     {
824       LOG (GNUNET_ERROR_TYPE_DEBUG,
825            "CADET_CONNECTION_CREATE has peer %s at offset %u\n",
826            GNUNET_i2s (&pids[i]),
827            i);
828       if (GNUNET_SYSERR ==
829           GNUNET_CONTAINER_multipeermap_put (map,
830                                              &pids[i],
831                                              NULL,
832                                              GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
833       {
834         /* bogus request */
835         GNUNET_CONTAINER_multipeermap_destroy (map);
836         LOG (GNUNET_ERROR_TYPE_DEBUG,
837              "Dropping CADET_CONNECTION_CREATE with cyclic path\n");
838         GNUNET_break_op (0);
839         return;
840       }
841     }
842     GNUNET_CONTAINER_multipeermap_destroy (map);
843   }
844   /* Initiator is at offset 0, find us */
845   for (off=1;off<path_length;off++)
846     if (0 == memcmp (&my_full_id,
847                      &pids[off],
848                      sizeof (struct GNUNET_PeerIdentity)))
849       break;
850   if (off == path_length)
851   {
852     LOG (GNUNET_ERROR_TYPE_DEBUG,
853          "Dropping CADET_CONNECTION_CREATE without us in the path\n");
854     GNUNET_break_op (0);
855     return;
856   }
857   /* Check previous hop */
858   if (sender != GCP_get (&pids[off - 1],
859                          GNUNET_NO))
860   {
861     LOG (GNUNET_ERROR_TYPE_DEBUG,
862          "Dropping CADET_CONNECTION_CREATE without sender at previous hop in the path\n");
863     GNUNET_break_op (0);
864     return;
865   }
866   if (NULL !=
867       (route = get_route (&msg->cid)))
868   {
869     /* Duplicate CREATE, pass it on, previous one might have been lost! */
870
871     LOG (GNUNET_ERROR_TYPE_DEBUG,
872          "Passing on duplicate CADET_CONNECTION_CREATE message on connection %s\n",
873          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
874     route_message (sender,
875                    &msg->cid,
876                    &msg->header);
877     return;
878   }
879   if (off == path_length - 1)
880   {
881     /* We are the destination, create connection */
882     struct CadetConnection *cc;
883     struct CadetPeerPath *path;
884     struct CadetPeer *origin;
885
886     cc = GCC_lookup (&msg->cid);
887     if (NULL != cc)
888     {
889       LOG (GNUNET_ERROR_TYPE_DEBUG,
890            "Received duplicate CADET_CONNECTION_CREATE message on connection %s\n",
891            GNUNET_sh2s (&msg->cid.connection_of_tunnel));
892       GCC_handle_duplicate_create (cc);
893       return;
894     }
895
896     origin = GCP_get (&pids[0],
897                       GNUNET_YES);
898     LOG (GNUNET_ERROR_TYPE_DEBUG,
899          "I am destination for CADET_CONNECTION_CREATE message from %s for connection %s, building inverse path\n",
900          GCP_2s (origin),
901          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
902     path = GCPP_get_path_from_route (path_length - 1,
903                                      pids);
904     if (GNUNET_OK !=
905         GCT_add_inbound_connection (GCP_get_tunnel (origin,
906                                                     GNUNET_YES),
907                                     &msg->cid,
908                                     (enum GNUNET_CADET_ChannelOption) ntohl (msg->options),
909                                     path))
910     {
911       /* Send back BROKEN: duplicate connection on the same path,
912          we will use the other one. */
913       LOG (GNUNET_ERROR_TYPE_DEBUG,
914            "Received CADET_CONNECTION_CREATE from %s for %s, but %s already has a connection. Sending BROKEN\n",
915            GCP_2s (sender),
916            GNUNET_sh2s (&msg->cid.connection_of_tunnel),
917            GCPP_2s (path));
918       send_broken_without_mqm (sender,
919                                &msg->cid,
920                                NULL);
921       return;
922     }
923     return;
924   }
925   /* We are merely a hop on the way, check if we can support the route */
926   next = GCP_get (&pids[off + 1],
927                   GNUNET_NO);
928   if ( (NULL == next) ||
929        (GNUNET_NO == GCP_has_core_connection (next)) )
930   {
931     /* unworkable, send back BROKEN notification */
932     LOG (GNUNET_ERROR_TYPE_DEBUG,
933          "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is down. Sending BROKEN\n",
934          GCP_2s (sender),
935          GNUNET_sh2s (&msg->cid.connection_of_tunnel),
936          GNUNET_i2s (&pids[off + 1]),
937          off + 1);
938     send_broken_without_mqm (sender,
939                              &msg->cid,
940                              &pids[off + 1]);
941     return;
942   }
943   if (max_routes <= GNUNET_CONTAINER_multishortmap_size (routes))
944   {
945     LOG (GNUNET_ERROR_TYPE_DEBUG,
946          "Received CADET_CONNECTION_CREATE from %s for %s. We have reached our route limit. Sending BROKEN\n",
947          GCP_2s (sender),
948          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
949     send_broken_without_mqm (sender,
950                              &msg->cid,
951                              &pids[off - 1]);
952     return;
953   }
954
955   /* Workable route, create routing entry */
956   LOG (GNUNET_ERROR_TYPE_DEBUG,
957        "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is up. Creating route\n",
958        GCP_2s (sender),
959        GNUNET_sh2s (&msg->cid.connection_of_tunnel),
960        GNUNET_i2s (&pids[off + 1]),
961        off + 1);
962   route = GNUNET_new (struct CadetRoute);
963   route->options = options;
964   route->cid = msg->cid;
965   route->last_use = GNUNET_TIME_absolute_get ();
966   dir_init (&route->prev,
967             route,
968             sender);
969   dir_init (&route->next,
970             route,
971             next);
972   GNUNET_assert (GNUNET_OK ==
973                  GNUNET_CONTAINER_multishortmap_put (routes,
974                                                      &route->cid.connection_of_tunnel,
975                                                      route,
976                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
977   GNUNET_STATISTICS_set (stats,
978                          "# routes",
979                          GNUNET_CONTAINER_multishortmap_size (routes),
980                          GNUNET_NO);
981   route->hn = GNUNET_CONTAINER_heap_insert (route_heap,
982                                             route,
983                                             route->last_use.abs_value_us);
984   if (NULL == timeout_task)
985     timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (keepalive_period,
986                                                                                 3),
987                                                  &timeout_cb,
988                                                  NULL);
989   /* also pass CREATE message along to next hop */
990   route_message (sender,
991                  &msg->cid,
992                  &msg->header);
993 }
994
995
996 /**
997  * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK
998  *
999  * @param cls Closure (CadetPeer for neighbor that sent the message).
1000  * @param msg Message itself.
1001  */
1002 static void
1003 handle_connection_create_ack (void *cls,
1004                               const struct GNUNET_CADET_ConnectionCreateAckMessage *msg)
1005 {
1006   struct CadetPeer *peer = cls;
1007   struct CadetConnection *cc;
1008
1009   /* First, check if ACK belongs to a connection that ends here. */
1010   cc = GCC_lookup (&msg->cid);
1011   if (NULL != cc)
1012   {
1013     /* verify ACK came from the right direction */
1014     unsigned int len;
1015     struct CadetPeerPath *path = GCC_get_path (cc,
1016                                                &len);
1017
1018     if (peer !=
1019         GCPP_get_peer_at_offset (path,
1020                                  0))
1021     {
1022       /* received ACK from unexpected direction, ignore! */
1023       GNUNET_break_op (0);
1024       return;
1025     }
1026     LOG (GNUNET_ERROR_TYPE_DEBUG,
1027          "Received CONNECTION_CREATE_ACK for connection %s.\n",
1028          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
1029     GCC_handle_connection_create_ack (cc);
1030     return;
1031   }
1032
1033   /* We're just an intermediary peer, route the message along its path */
1034   route_message (peer,
1035                  &msg->cid,
1036                  &msg->header);
1037 }
1038
1039
1040 /**
1041  * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
1042  *
1043  * @param cls Closure (CadetPeer for neighbor that sent the message).
1044  * @param msg Message itself.
1045  * @deprecated duplicate logic with #handle_destroy(); dedup!
1046  */
1047 static void
1048 handle_connection_broken (void *cls,
1049                           const struct GNUNET_CADET_ConnectionBrokenMessage *msg)
1050 {
1051   struct CadetPeer *peer = cls;
1052   struct CadetConnection *cc;
1053   struct CadetRoute *route;
1054
1055   /* First, check if message belongs to a connection that ends here. */
1056   cc = GCC_lookup (&msg->cid);
1057   if (NULL != cc)
1058   {
1059     /* verify message came from the right direction */
1060     unsigned int len;
1061     struct CadetPeerPath *path = GCC_get_path (cc,
1062                                                &len);
1063
1064     if (peer !=
1065         GCPP_get_peer_at_offset (path,
1066                                  0))
1067     {
1068       /* received message from unexpected direction, ignore! */
1069       GNUNET_break_op (0);
1070       return;
1071     }
1072     LOG (GNUNET_ERROR_TYPE_DEBUG,
1073          "Received CONNECTION_BROKEN for connection %s. Destroying it.\n",
1074          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
1075     GCC_destroy_without_core (cc);
1076
1077     /* FIXME: also destroy the path up to the specified link! */
1078     return;
1079   }
1080
1081   /* We're just an intermediary peer, route the message along its path */
1082   route_message (peer,
1083                  &msg->cid,
1084                  &msg->header);
1085   route = get_route (&msg->cid);
1086   if (NULL != route)
1087     destroy_route (route);
1088   /* FIXME: also destroy paths we MAY have up to the specified link! */
1089 }
1090
1091
1092 /**
1093  * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY
1094  *
1095  * @param cls Closure (CadetPeer for neighbor that sent the message).
1096  * @param msg Message itself.
1097  */
1098 static void
1099 handle_connection_destroy (void *cls,
1100                            const struct GNUNET_CADET_ConnectionDestroyMessage *msg)
1101 {
1102   struct CadetPeer *peer = cls;
1103   struct CadetConnection *cc;
1104   struct CadetRoute *route;
1105
1106   /* First, check if message belongs to a connection that ends here. */
1107   cc = GCC_lookup (&msg->cid);
1108   if (NULL != cc)
1109   {
1110     /* verify message came from the right direction */
1111     unsigned int len;
1112     struct CadetPeerPath *path = GCC_get_path (cc,
1113                                                &len);
1114
1115     if (peer !=
1116         GCPP_get_peer_at_offset (path,
1117                                  0))
1118     {
1119       /* received message from unexpected direction, ignore! */
1120       GNUNET_break_op (0);
1121       return;
1122     }
1123     LOG (GNUNET_ERROR_TYPE_DEBUG,
1124          "Received CONNECTION_DESTROY for connection %s. Destroying connection.\n",
1125          GNUNET_sh2s (&msg->cid.connection_of_tunnel));
1126
1127     GCC_destroy_without_core (cc);
1128     return;
1129   }
1130
1131   /* We're just an intermediary peer, route the message along its path */
1132   LOG (GNUNET_ERROR_TYPE_DEBUG,
1133        "Received CONNECTION_DESTROY for connection %s. Destroying route.\n",
1134        GNUNET_sh2s (&msg->cid.connection_of_tunnel));
1135   route_message (peer,
1136                  &msg->cid,
1137                  &msg->header);
1138   route = get_route (&msg->cid);
1139   if (NULL != route)
1140     destroy_route (route);
1141 }
1142
1143
1144 /**
1145  * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX
1146  *
1147  * @param cls Closure (CadetPeer for neighbor that sent the message).
1148  * @param msg Message itself.
1149  */
1150 static void
1151 handle_tunnel_kx (void *cls,
1152                   const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
1153 {
1154   struct CadetPeer *peer = cls;
1155   struct CadetConnection *cc;
1156
1157   /* First, check if message belongs to a connection that ends here. */
1158   LOG (GNUNET_ERROR_TYPE_DEBUG,
1159        "Routing KX with ephemeral %s on CID %s\n",
1160        GNUNET_e2s (&msg->ephemeral_key),
1161        GNUNET_sh2s (&msg->cid.connection_of_tunnel));
1162
1163
1164   cc = GCC_lookup (&msg->cid);
1165   if (NULL != cc)
1166   {
1167     /* verify message came from the right direction */
1168     unsigned int len;
1169     struct CadetPeerPath *path = GCC_get_path (cc,
1170                                                &len);
1171
1172     if (peer !=
1173         GCPP_get_peer_at_offset (path,
1174                                  0))
1175     {
1176       /* received message from unexpected direction, ignore! */
1177       GNUNET_break_op (0);
1178       return;
1179     }
1180     GCC_handle_kx (cc,
1181                    msg);
1182     return;
1183   }
1184
1185   /* We're just an intermediary peer, route the message along its path */
1186   route_message (peer,
1187                  &msg->cid,
1188                  &msg->header);
1189 }
1190
1191
1192 /**
1193  * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH
1194  *
1195  * @param cls Closure (CadetPeer for neighbor that sent the message).
1196  * @param msg Message itself.
1197  */
1198 static void
1199 handle_tunnel_kx_auth (void *cls,
1200                        const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
1201 {
1202   struct CadetPeer *peer = cls;
1203   struct CadetConnection *cc;
1204
1205   /* First, check if message belongs to a connection that ends here. */
1206   cc = GCC_lookup (&msg->kx.cid);
1207   if (NULL != cc)
1208   {
1209     /* verify message came from the right direction */
1210     unsigned int len;
1211     struct CadetPeerPath *path = GCC_get_path (cc,
1212                                                &len);
1213
1214     if (peer !=
1215         GCPP_get_peer_at_offset (path,
1216                                  0))
1217     {
1218       /* received message from unexpected direction, ignore! */
1219       GNUNET_break_op (0);
1220       return;
1221     }
1222     GCC_handle_kx_auth (cc,
1223                         msg);
1224     return;
1225   }
1226
1227   /* We're just an intermediary peer, route the message along its path */
1228   route_message (peer,
1229                  &msg->kx.cid,
1230                  &msg->kx.header);
1231 }
1232
1233
1234 /**
1235  * Check if the encrypted message has the appropriate size.
1236  *
1237  * @param cls Closure (unused).
1238  * @param msg Message to check.
1239  *
1240  * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise.
1241  */
1242 static int
1243 check_tunnel_encrypted (void *cls,
1244                         const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
1245 {
1246   return GNUNET_YES;
1247 }
1248
1249
1250 /**
1251  * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED.
1252  *
1253  * @param cls Closure (CadetPeer for neighbor that sent the message).
1254  * @param msg Message itself.
1255  */
1256 static void
1257 handle_tunnel_encrypted (void *cls,
1258                          const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
1259 {
1260   struct CadetPeer *peer = cls;
1261   struct CadetConnection *cc;
1262
1263   /* First, check if message belongs to a connection that ends here. */
1264   cc = GCC_lookup (&msg->cid);
1265   if (NULL != cc)
1266   {
1267     /* verify message came from the right direction */
1268     unsigned int len;
1269     struct CadetPeerPath *path = GCC_get_path (cc,
1270                                                &len);
1271
1272     if (peer !=
1273         GCPP_get_peer_at_offset (path,
1274                                  0))
1275     {
1276       /* received message from unexpected direction, ignore! */
1277       GNUNET_break_op (0);
1278       return;
1279     }
1280     GCC_handle_encrypted (cc,
1281                           msg);
1282     return;
1283   }
1284   /* We're just an intermediary peer, route the message along its path */
1285   route_message (peer,
1286                  &msg->cid,
1287                  &msg->header);
1288 }
1289
1290
1291 /**
1292  * Function called after #GNUNET_CORE_connect has succeeded (or failed
1293  * for good).  Note that the private key of the peer is intentionally
1294  * not exposed here; if you need it, your process should try to read
1295  * the private key file directly (which should work if you are
1296  * authorized...).  Implementations of this function must not call
1297  * #GNUNET_CORE_disconnect (other than by scheduling a new task to
1298  * do this later).
1299  *
1300  * @param cls closure
1301  * @param my_identity ID of this peer, NULL if we failed
1302  */
1303 static void
1304 core_init_cb (void *cls,
1305               const struct GNUNET_PeerIdentity *my_identity)
1306 {
1307   if (NULL == my_identity)
1308   {
1309     GNUNET_break (0);
1310     return;
1311   }
1312   GNUNET_break (0 ==
1313                 memcmp (my_identity,
1314                         &my_full_id,
1315                         sizeof (struct GNUNET_PeerIdentity)));
1316 }
1317
1318
1319 /**
1320  * Method called whenever a given peer connects.
1321  *
1322  * @param cls closure
1323  * @param peer peer identity this notification is about
1324  */
1325 static void *
1326 core_connect_cb (void *cls,
1327                  const struct GNUNET_PeerIdentity *peer,
1328                  struct GNUNET_MQ_Handle *mq)
1329 {
1330   struct CadetPeer *cp;
1331
1332   LOG (GNUNET_ERROR_TYPE_DEBUG,
1333        "CORE connection to peer %s was established.\n",
1334        GNUNET_i2s (peer));
1335   cp = GCP_get (peer,
1336                 GNUNET_YES);
1337   GCP_set_mq (cp,
1338               mq);
1339   return cp;
1340 }
1341
1342
1343 /**
1344  * Method called whenever a peer disconnects.
1345  *
1346  * @param cls closure
1347  * @param peer peer identity this notification is about
1348  */
1349 static void
1350 core_disconnect_cb (void *cls,
1351                     const struct GNUNET_PeerIdentity *peer,
1352                     void *peer_cls)
1353 {
1354   struct CadetPeer *cp = peer_cls;
1355
1356   LOG (GNUNET_ERROR_TYPE_DEBUG,
1357        "CORE connection to peer %s went down.\n",
1358        GNUNET_i2s (peer));
1359   GCP_set_mq (cp,
1360               NULL);
1361 }
1362
1363
1364 /**
1365  * Initialize the CORE subsystem.
1366  *
1367  * @param c Configuration.
1368  */
1369 void
1370 GCO_init (const struct GNUNET_CONFIGURATION_Handle *c)
1371 {
1372   struct GNUNET_MQ_MessageHandler handlers[] = {
1373     GNUNET_MQ_hd_var_size (connection_create,
1374                            GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE,
1375                            struct GNUNET_CADET_ConnectionCreateMessage,
1376                            NULL),
1377     GNUNET_MQ_hd_fixed_size (connection_create_ack,
1378                              GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK,
1379                              struct GNUNET_CADET_ConnectionCreateAckMessage,
1380                              NULL),
1381     GNUNET_MQ_hd_fixed_size (connection_broken,
1382                              GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN,
1383                              struct GNUNET_CADET_ConnectionBrokenMessage,
1384                              NULL),
1385     GNUNET_MQ_hd_fixed_size (connection_destroy,
1386                              GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY,
1387                              struct GNUNET_CADET_ConnectionDestroyMessage,
1388                              NULL),
1389     GNUNET_MQ_hd_fixed_size (tunnel_kx,
1390                              GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX,
1391                              struct GNUNET_CADET_TunnelKeyExchangeMessage,
1392                              NULL),
1393     GNUNET_MQ_hd_fixed_size (tunnel_kx_auth,
1394                              GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH,
1395                              struct GNUNET_CADET_TunnelKeyExchangeAuthMessage,
1396                              NULL),
1397     GNUNET_MQ_hd_var_size (tunnel_encrypted,
1398                            GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED,
1399                            struct GNUNET_CADET_TunnelEncryptedMessage,
1400                            NULL),
1401     GNUNET_MQ_handler_end ()
1402   };
1403
1404   if (GNUNET_OK !=
1405       GNUNET_CONFIGURATION_get_value_number (c,
1406                                              "CADET",
1407                                              "MAX_ROUTES",
1408                                              &max_routes))
1409     max_routes = 5000;
1410   if (GNUNET_OK !=
1411       GNUNET_CONFIGURATION_get_value_number (c,
1412                                              "CADET",
1413                                              "MAX_MSGS_QUEUE",
1414                                              &max_buffers))
1415     max_buffers = 10000;
1416   routes = GNUNET_CONTAINER_multishortmap_create (1024,
1417                                                   GNUNET_NO);
1418   route_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1419   core = GNUNET_CORE_connect (c,
1420                               NULL,
1421                               &core_init_cb,
1422                               &core_connect_cb,
1423                               &core_disconnect_cb,
1424                               handlers);
1425 }
1426
1427
1428 /**
1429  * Shut down the CORE subsystem.
1430  */
1431 void
1432 GCO_shutdown ()
1433 {
1434   if (NULL != core)
1435   {
1436     GNUNET_CORE_disconnect (core);
1437     core = NULL;
1438   }
1439   GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (routes));
1440   GNUNET_CONTAINER_multishortmap_destroy (routes);
1441   routes = NULL;
1442   GNUNET_CONTAINER_heap_destroy (route_heap);
1443   route_heap = NULL;
1444   if (NULL != timeout_task)
1445   {
1446     GNUNET_SCHEDULER_cancel (timeout_task);
1447     timeout_task = NULL;
1448   }
1449 }
1450
1451 /* end of gnunet-cadet-service_core.c */