3 This file is part of GNUnet.
4 Copyright (C) 2001-2017 GNUnet e.V.
6 GNUnet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 3, or (at your
9 option) any later version.
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
23 * @file cadet/gnunet-service-cadet-new_peer.c
24 * @brief Information we track per peer.
25 * @author Bartlomiej Polot
26 * @author Christian Grothoff
29 * - optimize stopping/restarting DHT search to situations
30 * where we actually need it (i.e. not if we have a direct connection,
31 * or if we already have plenty of good short ones, or maybe even
32 * to take a break if we have some connections and have searched a lot (?))
33 * - optimize MQM ready scans (O(n) -> O(1))
36 #include "gnunet_util_lib.h"
37 #include "gnunet_signatures.h"
38 #include "gnunet_transport_service.h"
39 #include "gnunet_ats_service.h"
40 #include "gnunet_core_service.h"
41 #include "gnunet_statistics_service.h"
42 #include "cadet_protocol.h"
43 #include "cadet_path.h"
44 #include "gnunet-service-cadet-new.h"
45 #include "gnunet-service-cadet-new_connection.h"
46 #include "gnunet-service-cadet-new_dht.h"
47 #include "gnunet-service-cadet-new_peer.h"
48 #include "gnunet-service-cadet-new_paths.h"
49 #include "gnunet-service-cadet-new_tunnels.h"
52 #define LOG(level, ...) GNUNET_log_from(level,"cadet-per",__VA_ARGS__)
56 * How long do we wait until tearing down an idle peer?
58 #define IDLE_PEER_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5)
61 * How long do we keep paths around if we no longer care about the peer?
63 #define IDLE_PATH_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2)
69 * Data structure used to track whom we have to notify about changes
70 * to our message queue.
72 struct GCP_MessageQueueManager
78 struct GCP_MessageQueueManager *next;
83 struct GCP_MessageQueueManager *prev;
86 * Function to call with updated message queue object.
88 GCP_MessageQueueNotificationCallback cb;
96 * The peer this is for.
101 * Envelope this manager would like to transmit once it is its turn.
103 struct GNUNET_MQ_Envelope *env;
109 * Struct containing all information regarding a given peer
116 struct GNUNET_PeerIdentity pid;
119 * Last time we heard from this peer
121 struct GNUNET_TIME_Absolute last_contact;
124 * Array of DLLs of paths traversing the peer, organized by the
125 * offset of the peer on the larger path.
127 struct CadetPeerPathEntry **path_heads;
130 * Array of DLL of paths traversing the peer, organized by the
131 * offset of the peer on the larger path.
133 struct CadetPeerPathEntry **path_tails;
136 * Notifications to call when @e core_mq changes.
138 struct GCP_MessageQueueManager *mqm_head;
141 * Notifications to call when @e core_mq changes.
143 struct GCP_MessageQueueManager *mqm_tail;
146 * MIN-heap of paths owned by this peer (they also end at this
147 * peer). Ordered by desirability.
149 struct GNUNET_CONTAINER_Heap *path_heap;
152 * Handle to stop the DHT search for paths to this peer
154 struct GCD_search_handle *search_h;
157 * Task to stop the DHT search for paths to this peer
159 struct GNUNET_SCHEDULER_Task *search_delayedXXX;
162 * Task to destroy this entry.
164 struct GNUNET_SCHEDULER_Task *destroy_task;
167 * Tunnel to this peer, if any.
169 struct CadetTunnel *t;
172 * Connections that go through this peer; indexed by tid.
174 struct GNUNET_CONTAINER_MultiShortmap *connections;
177 * Handle for core transmissions.
179 struct GNUNET_MQ_Handle *core_mq;
182 * Hello message of the peer.
184 struct GNUNET_HELLO_Message *hello;
187 * Handle to us offering the HELLO to the transport.
189 struct GNUNET_TRANSPORT_OfferHelloHandle *hello_offer;
192 * Handle to our ATS request asking ATS to suggest an address
193 * to TRANSPORT for this peer (to establish a direct link).
195 struct GNUNET_ATS_ConnectivitySuggestHandle *connectivity_suggestion;
198 * How many messages are in the queue to this peer.
200 unsigned int queue_n;
203 * How many paths do we have to this peer (in all @e path_heads DLLs combined).
205 unsigned int num_paths;
208 * Number of message queue managers of this peer that have a message in waiting.
210 * Used to quickly see if we need to bother scanning the @e msm_head DLL.
211 * TODO: could be replaced by another DLL that would then allow us to avoid
212 * the O(n)-scan of the DLL for ready entries!
214 unsigned int mqm_ready_counter;
217 * Current length of the @e path_heads and @path_tails arrays.
218 * The arrays should be grown as needed.
220 unsigned int path_dll_length;
226 * Get the static string for a peer ID.
230 * @return Static string for it's ID.
233 GCP_2s (const struct CadetPeer *peer)
237 return GNUNET_i2s (&peer->pid);
242 * This peer is no longer be needed, clean it up now.
244 * @param cls peer to clean up
247 destroy_peer (void *cls)
249 struct CadetPeer *cp = cls;
251 cp->destroy_task = NULL;
252 GNUNET_assert (NULL == cp->t);
253 GNUNET_assert (NULL == cp->core_mq);
254 GNUNET_assert (0 == cp->path_dll_length);
255 GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections));
256 GNUNET_assert (GNUNET_YES ==
257 GNUNET_CONTAINER_multipeermap_remove (peers,
260 GNUNET_free_non_null (cp->path_heads);
261 GNUNET_free_non_null (cp->path_tails);
262 cp->path_dll_length = 0;
263 if (NULL != cp->search_h)
265 GCD_search_stop (cp->search_h);
268 /* FIXME: clean up search_delayedXXX! */
270 if (NULL != cp->hello_offer)
272 GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer);
273 cp->hello_offer = NULL;
275 if (NULL != cp->connectivity_suggestion)
277 GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion);
278 cp->connectivity_suggestion = NULL;
280 GNUNET_CONTAINER_multishortmap_destroy (cp->connections);
281 GNUNET_CONTAINER_heap_destroy (cp->path_heap);
282 GNUNET_free_non_null (cp->hello);
283 /* Peer should not be freed if paths exist; if there are no paths,
284 there ought to be no connections, and without connections, no
285 notifications. Thus we can assert that mqm_head is empty at this
287 GNUNET_assert (NULL == cp->mqm_head);
293 * Set the message queue to @a mq for peer @a cp and notify watchers.
295 * @param cp peer to modify
296 * @param mq message queue to set (can be NULL)
299 GCP_set_mq (struct CadetPeer *cp,
300 struct GNUNET_MQ_Handle *mq)
304 for (struct GCP_MessageQueueManager *mqm = cp->mqm_head;
310 if (NULL != mqm->env)
312 GNUNET_MQ_discard (mqm->env);
314 mqm->cb (mqm->cb_cls,
319 mqm->cb (mqm->cb_cls,
325 GNUNET_assert (NULL == mqm->env);
326 mqm->cb (mqm->cb_cls,
334 * Transmit current envelope from this @a mqm.
336 * @param mqm mqm to transmit message for now
339 mqm_execute (struct GCP_MessageQueueManager *mqm)
341 struct CadetPeer *cp = mqm->cp;
343 /* Move entry to the end of the DLL, to be fair. */
344 if (mqm != cp->mqm_tail)
346 GNUNET_CONTAINER_DLL_remove (cp->mqm_head,
349 GNUNET_CONTAINER_DLL_insert_tail (cp->mqm_head,
353 GNUNET_MQ_send (cp->core_mq,
356 cp->mqm_ready_counter--;
361 * Function called when CORE took one of the messages from
362 * a message queue manager and transmitted it.
364 * @param cls the `struct CadetPeeer` where we made progress
367 mqm_send_done (void *cls)
369 struct CadetPeer *cp = cls;
371 if (0 == cp->mqm_ready_counter)
372 return; /* nothing to do */
373 for (struct GCP_MessageQueueManager *mqm = cp->mqm_head;
377 if (NULL == mqm->env)
386 * Send the message in @a env to @a cp.
388 * @param mqm the message queue manager to use for transmission
389 * @param env envelope with the message to send; must NOT
390 * yet have a #GNUNET_MQ_notify_sent() callback attached to it
393 GCP_send (struct GCP_MessageQueueManager *mqm,
394 struct GNUNET_MQ_Envelope *env)
396 struct CadetPeer *cp = mqm->cp;
398 GNUNET_assert (NULL != cp->core_mq);
399 GNUNET_assert (NULL == mqm->env);
400 GNUNET_MQ_notify_sent (env,
404 cp->mqm_ready_counter++;
405 if (0 != GNUNET_MQ_get_length (cp->core_mq))
412 * Function called to destroy a peer now.
415 * @param pid identity of the peer (unused)
416 * @param value the `struct CadetPeer` to clean up
417 * @return #GNUNET_OK (continue to iterate)
420 destroy_iterator_cb (void *cls,
421 const struct GNUNET_PeerIdentity *pid,
424 struct CadetPeer *cp = value;
426 if (NULL != cp->destroy_task)
428 GNUNET_SCHEDULER_cancel (cp->destroy_task);
429 cp->destroy_task = NULL;
437 * Clean up all entries about all peers.
438 * Must only be called after all tunnels, CORE-connections and
439 * connections are down.
442 GCP_destroy_all_peers ()
444 GNUNET_CONTAINER_multipeermap_iterate (peers,
445 &destroy_iterator_cb,
451 * This peer may no longer be needed, consider cleaning it up.
453 * @param cp peer to clean up
456 consider_peer_destroy (struct CadetPeer *cp);
460 * We really no longere care about a peer, stop hogging memory with paths to it.
461 * Afterwards, see if there is more to be cleaned up about this peer.
463 * @param cls a `struct CadetPeer`.
466 drop_paths (void *cls)
468 struct CadetPeer *cp = cls;
469 struct CadetPeerPath *path;
471 cp->destroy_task = NULL;
472 while (NULL != (path = GNUNET_CONTAINER_heap_remove_root (cp->path_heap)))
474 consider_peer_destroy (cp);
479 * This peer may no longer be needed, consider cleaning it up.
481 * @param cp peer to clean up
484 consider_peer_destroy (struct CadetPeer *cp)
486 struct GNUNET_TIME_Relative exp;
488 if (NULL != cp->destroy_task)
490 GNUNET_SCHEDULER_cancel (cp->destroy_task);
491 cp->destroy_task = NULL;
494 return; /* still relevant! */
495 if (NULL != cp->core_mq)
496 return; /* still relevant! */
497 if (0 != GNUNET_CONTAINER_multishortmap_size (cp->connections))
498 return; /* still relevant! */
499 if (0 < GNUNET_CONTAINER_heap_get_size (cp->path_heap))
501 cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PATH_TIMEOUT,
506 if (0 < cp->path_dll_length)
507 return; /* still relevant! */
508 if (NULL != cp->hello)
510 /* relevant only until HELLO expires */
511 exp = GNUNET_TIME_absolute_get_remaining (GNUNET_HELLO_get_last_expiration (cp->hello));
512 cp->destroy_task = GNUNET_SCHEDULER_add_delayed (exp,
517 cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PEER_TIMEOUT,
524 * Add an entry to the DLL of all of the paths that this peer is on.
526 * @param cp peer to modify
527 * @param entry an entry on a path
528 * @param off offset of this peer on the path
531 GCP_path_entry_add (struct CadetPeer *cp,
532 struct CadetPeerPathEntry *entry,
535 if (off >= cp->path_dll_length)
537 unsigned int len = cp->path_dll_length;
539 GNUNET_array_grow (cp->path_heads,
542 GNUNET_array_grow (cp->path_tails,
546 GNUNET_CONTAINER_DLL_insert (cp->path_heads[off],
551 /* If we have a tunnel to this peer, tell the tunnel that there is a
552 new path available. */
554 GCT_consider_path (cp->t,
561 * Remove an entry from the DLL of all of the paths that this peer is on.
563 * @param cp peer to modify
564 * @param entry an entry on a path
565 * @param off offset of this peer on the path
568 GCP_path_entry_remove (struct CadetPeer *cp,
569 struct CadetPeerPathEntry *entry,
572 GNUNET_CONTAINER_DLL_remove (cp->path_heads[off],
575 GNUNET_assert (0 < cp->num_paths);
581 * Try adding a @a path to this @a peer. If the peer already
582 * has plenty of paths, return NULL.
584 * @param cp peer to which the @a path leads to
585 * @param path a path looking for an owner; may not be fully initialized yet!
586 * @param off offset of @a cp in @a path
587 * @param force force attaching the path
588 * @return NULL if this peer does not care to become a new owner,
589 * otherwise the node in the peer's path heap for the @a path.
591 struct GNUNET_CONTAINER_HeapNode *
592 GCP_attach_path (struct CadetPeer *cp,
593 struct CadetPeerPath *path,
597 GNUNET_CONTAINER_HeapCostType desirability;
598 struct CadetPeerPath *root;
599 GNUNET_CONTAINER_HeapCostType root_desirability;
600 struct GNUNET_CONTAINER_HeapNode *hn;
602 if (GNUNET_NO == force)
604 /* FIXME: desirability is not yet initialized; tricky! */
605 desirability = GCPP_get_desirability (path);
607 GNUNET_CONTAINER_heap_peek2 (cp->path_heap,
612 root_desirability = 0;
615 if ( (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) &&
616 (desirability < root_desirability) )
620 /* Yes, we'd like to add this path, add to our heap */
621 hn = GNUNET_CONTAINER_heap_insert (cp->path_heap,
625 /* Consider maybe dropping other paths because of the new one */
626 if (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >=
627 2 * DESIRED_CONNECTIONS_PER_TUNNEL)
629 /* Now we have way too many, drop least desirable UNLESS it is in use!
630 (Note that this intentionally keeps highly desireable, but currently
631 unused paths around in the hope that we might be able to switch, even
632 if the number of paths exceeds the threshold.) */
633 root = GNUNET_CONTAINER_heap_peek (cp->path_heap);
634 if ( (path != root) &&
636 GCPP_get_connection (root,
638 GCPP_get_length (root) - 1)) )
640 /* Got plenty of paths to this destination, and this is a low-quality
641 one that we don't care, allow it to die. */
642 GNUNET_assert (root ==
643 GNUNET_CONTAINER_heap_remove_root (cp->path_heap));
652 * This peer can no longer own @a path as the path
653 * has been extended and a peer further down the line
654 * is now the new owner.
656 * @param cp old owner of the @a path
657 * @param path path where the ownership is lost
658 * @param hn note in @a cp's path heap that must be deleted
661 GCP_detach_path (struct CadetPeer *cp,
662 struct CadetPeerPath *path,
663 struct GNUNET_CONTAINER_HeapNode *hn)
665 GNUNET_assert (path ==
666 GNUNET_CONTAINER_heap_remove_node (hn));
671 * Add a @a connection to this @a cp.
673 * @param cp peer via which the @a connection goes
674 * @param cc the connection to add
677 GCP_add_connection (struct CadetPeer *cp,
678 struct CadetConnection *cc)
680 GNUNET_assert (GNUNET_OK ==
681 GNUNET_CONTAINER_multishortmap_put (cp->connections,
682 &GCC_get_id (cc)->connection_of_tunnel,
684 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
689 * Remove a @a connection that went via this @a cp.
691 * @param cp peer via which the @a connection went
692 * @param cc the connection to remove
695 GCP_remove_connection (struct CadetPeer *cp,
696 struct CadetConnection *cc)
698 GNUNET_assert (GNUNET_YES ==
699 GNUNET_CONTAINER_multishortmap_remove (cp->connections,
700 &GCC_get_id (cc)->connection_of_tunnel,
706 * This peer is now on more "active" duty, activate processes related to it.
708 * @param cp the more-active peer
711 consider_peer_activate (struct CadetPeer *cp)
715 if (NULL != cp->destroy_task)
717 /* It's active, do not destory! */
718 GNUNET_SCHEDULER_cancel (cp->destroy_task);
719 cp->destroy_task = NULL;
721 if ( (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections)) &&
724 /* We're just on a path or directly connected; don't bother too much */
725 if (NULL != cp->connectivity_suggestion)
727 GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion);
728 cp->connectivity_suggestion = NULL;
730 if (NULL != cp->search_h)
732 GCD_search_stop (cp->search_h);
737 if (NULL == cp->core_mq)
739 /* Lacks direct connection, try to create one by querying the DHT */
740 if ( (NULL == cp->search_h) &&
741 (DESIRED_CONNECTIONS_PER_TUNNEL < cp->num_paths) )
743 = GCD_search (&cp->pid);
747 /* Have direct connection, stop DHT search if active */
748 if (NULL != cp->search_h)
750 GCD_search_stop (cp->search_h);
755 /* If we have a tunnel, our urge for connections is much bigger */
756 strength = (NULL != cp->t) ? 32 : 1;
757 if (NULL != cp->connectivity_suggestion)
758 GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion);
759 cp->connectivity_suggestion
760 = GNUNET_ATS_connectivity_suggest (ats_ch,
767 * Retrieve the CadetPeer stucture associated with the
768 * peer. Optionally create one and insert it in the appropriate
769 * structures if the peer is not known yet.
771 * @param peer_id Full identity of the peer.
772 * @param create #GNUNET_YES if a new peer should be created if unknown.
773 * #GNUNET_NO to return NULL if peer is unknown.
774 * @return Existing or newly created peer structure.
775 * NULL if unknown and not requested @a create
778 GCP_get (const struct GNUNET_PeerIdentity *peer_id,
781 struct CadetPeer *cp;
783 cp = GNUNET_CONTAINER_multipeermap_get (peers,
787 if (GNUNET_NO == create)
789 cp = GNUNET_new (struct CadetPeer);
791 cp->connections = GNUNET_CONTAINER_multishortmap_create (32,
793 cp->path_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
794 GNUNET_assert (GNUNET_YES ==
795 GNUNET_CONTAINER_multipeermap_put (peers,
798 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
804 * Obtain the peer identity for a `struct CadetPeer`.
806 * @param cp our peer handle
807 * @return the peer identity
809 const struct GNUNET_PeerIdentity *
810 GCP_get_id (struct CadetPeer *cp)
817 * Iterate over all known peers.
819 * @param iter Iterator.
820 * @param cls Closure for @c iter.
823 GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter,
826 GNUNET_CONTAINER_multipeermap_iterate (peers,
833 * Count the number of known paths toward the peer.
835 * @param peer Peer to get path info.
836 * @return Number of known paths.
839 GCP_count_paths (const struct CadetPeer *peer)
841 return peer->num_paths;
846 * Iterate over the paths to a peer.
848 * @param peer Peer to get path info.
849 * @param callback Function to call for every path.
850 * @param callback_cls Closure for @a callback.
851 * @return Number of iterated paths.
854 GCP_iterate_paths (struct CadetPeer *peer,
855 GCP_PathIterator callback,
858 unsigned int ret = 0;
860 for (unsigned int i=0;i<peer->path_dll_length;i++)
862 for (struct CadetPeerPathEntry *pe = peer->path_heads[i];
867 callback (callback_cls,
879 * Iterate over the paths to @a peer where
880 * @a peer is at distance @a dist from us.
882 * @param peer Peer to get path info.
883 * @param dist desired distance of @a peer to us on the path
884 * @param callback Function to call for every path.
885 * @param callback_cls Closure for @a callback.
886 * @return Number of iterated paths.
889 GCP_iterate_paths_at (struct CadetPeer *peer,
891 GCP_PathIterator callback,
894 unsigned int ret = 0;
896 if (dist<peer->path_dll_length)
898 for (struct CadetPeerPathEntry *pe = peer->path_heads[dist];
903 callback (callback_cls,
914 * Get the tunnel towards a peer.
916 * @param cp Peer to get from.
917 * @param create #GNUNET_YES to create a tunnel if we do not have one
918 * @return Tunnel towards peer.
921 GCP_get_tunnel (struct CadetPeer *cp,
926 if ( (NULL != cp->t) ||
927 (GNUNET_NO == create) )
929 cp->t = GCT_create_tunnel (cp);
930 consider_peer_activate (cp);
936 * Hello offer was passed to the transport service. Mark it
939 * @param cls the `struct CadetPeer` where the offer completed
942 hello_offer_done (void *cls)
944 struct CadetPeer *cp = cls;
946 cp->hello_offer = NULL;
951 * We got a HELLO for a @a peer, remember it, and possibly
952 * trigger adequate actions (like trying to connect).
954 * @param cp the peer we got a HELLO for
955 * @param hello the HELLO to remember
958 GCP_set_hello (struct CadetPeer *cp,
959 const struct GNUNET_HELLO_Message *hello)
961 struct GNUNET_HELLO_Message *mrg;
963 if (NULL != cp->hello_offer)
965 GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer);
966 cp->hello_offer = NULL;
968 if (NULL != cp->hello)
970 mrg = GNUNET_HELLO_merge (hello,
972 GNUNET_free (cp->hello);
977 cp->hello = GNUNET_memdup (hello,
978 GNUNET_HELLO_size (hello));
981 = GNUNET_TRANSPORT_offer_hello (cfg,
982 GNUNET_HELLO_get_header (cp->hello) ,
985 /* New HELLO means cp's destruction time may change... */
986 consider_peer_destroy (cp);
991 * The tunnel to the given peer no longer exists, remove it from our
992 * data structures, and possibly clean up the peer itself.
994 * @param cp the peer affected
995 * @param t the dead tunnel
998 GCP_drop_tunnel (struct CadetPeer *cp,
999 struct CadetTunnel *t)
1001 GNUNET_assert (cp->t == t);
1003 consider_peer_destroy (cp);
1008 * Test if @a cp has a core-level connection
1010 * @param cp peer to test
1011 * @return #GNUNET_YES if @a cp has a core-level connection
1014 GCP_has_core_connection (struct CadetPeer *cp)
1016 return (NULL != cp->core_mq) ? GNUNET_YES : GNUNET_NO;
1021 * Start message queue change notifications.
1023 * @param cp peer to notify for
1024 * @param cb function to call if mq becomes available or unavailable
1025 * @param cb_cls closure for @a cb
1026 * @return handle to cancel request
1028 struct GCP_MessageQueueManager *
1029 GCP_request_mq (struct CadetPeer *cp,
1030 GCP_MessageQueueNotificationCallback cb,
1033 struct GCP_MessageQueueManager *mqm;
1035 mqm = GNUNET_new (struct GCP_MessageQueueManager);
1037 mqm->cb_cls = cb_cls;
1039 GNUNET_CONTAINER_DLL_insert (cp->mqm_head,
1042 if (NULL != cp->core_mq)
1050 * Stops message queue change notifications.
1052 * @param mqm handle matching request to cancel
1053 * @param last_env final message to transmit, or NULL
1056 GCP_request_mq_cancel (struct GCP_MessageQueueManager *mqm,
1057 struct GNUNET_MQ_Envelope *last_env)
1059 struct CadetPeer *cp = mqm->cp;
1061 if (NULL != mqm->env)
1062 GNUNET_MQ_discard (mqm->env);
1063 if (NULL != last_env)
1065 if (NULL != cp->core_mq)
1066 GNUNET_MQ_send (cp->core_mq,
1069 GNUNET_MQ_discard (last_env);
1071 GNUNET_CONTAINER_DLL_remove (cp->mqm_head,
1079 * Send the message in @a env to @a cp, overriding queueing logic.
1080 * This function should only be used to send error messages outside
1081 * of flow and congestion control, similar to ICMP. Note that
1082 * the envelope may be silently discarded as well.
1084 * @param cp peer to send the message to
1085 * @param env envelope with the message to send
1088 GCP_send_ooo (struct CadetPeer *cp,
1089 struct GNUNET_MQ_Envelope *env)
1091 if (NULL == cp->core_mq)
1093 GNUNET_MQ_discard (env);
1096 GNUNET_MQ_send (cp->core_mq,
1103 /* end of gnunet-service-cadet-new_peer.c */