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