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.
22 * @file cadet/gnunet-service-cadet-new_channel.c
23 * @brief logical links between CADET clients
24 * @author Bartlomiej Polot
25 * @author Christian Grothoff
28 * - Optimize ACKs by using 'mid_futures' properly!
29 * - introduce shutdown so we can have half-closed channels, modify
30 * destroy to include MID to have FIN-ACK equivalents, etc.
31 * - estimate max bandwidth using bursts and use to for CONGESTION CONTROL!
32 * - check that '0xFFULL' really is sufficient for flow control!
33 * - revisit handling of 'unreliable' traffic!
34 * - revisit handling of 'out-of-order' option, especially in combination with/without 'reliable'.
35 * - figure out flow control without ACKs (unreliable traffic!)
38 #include "gnunet_util_lib.h"
40 #include "gnunet_statistics_service.h"
41 #include "gnunet-service-cadet-new.h"
42 #include "gnunet-service-cadet-new_channel.h"
43 #include "gnunet-service-cadet-new_connection.h"
44 #include "gnunet-service-cadet-new_tunnels.h"
45 #include "gnunet-service-cadet-new_peer.h"
46 #include "gnunet-service-cadet-new_paths.h"
48 #define LOG(level,...) GNUNET_log_from (level,"cadet-chn",__VA_ARGS__)
51 * How long do we initially wait before retransmitting?
53 #define CADET_INITIAL_RETRANSMIT_TIME GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
56 * How long do we wait before dropping state about incoming
57 * connection to closed port?
59 #define TIMEOUT_CLOSED_PORT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
62 * How long do we wait at least before retransmitting ever?
64 #define MIN_RTT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 75)
67 * Maximum message ID into the future we accept for out-of-order messages.
68 * If the message is more than this into the future, we drop it. This is
69 * important both to detect values that are actually in the past, as well
70 * as to limit adversarially triggerable memory consumption.
72 * Note that right now we have "max_pending_messages = 4" hard-coded in
73 * the logic below, so a value of 4 would suffice here. But we plan to
74 * allow larger windows in the future...
76 #define MAX_OUT_OF_ORDER_DISTANCE 1024
80 * All the states a connection can be in.
82 enum CadetChannelState
85 * Uninitialized status, should never appear in operation.
90 * Connection create message sent, waiting for ACK.
92 CADET_CHANNEL_OPEN_SENT,
95 * Connection confirmed, ready to carry traffic.
102 * Info needed to retry a message in case it gets lost.
103 * Note that we DO use this structure also for unreliable
106 struct CadetReliableMessage
109 * Double linked list, FIFO style
111 struct CadetReliableMessage *next;
114 * Double linked list, FIFO style
116 struct CadetReliableMessage *prev;
119 * Which channel is this message in?
121 struct CadetChannel *ch;
124 * Entry in the tunnels queue for this message, NULL if it has left
125 * the tunnel. Used to cancel transmission in case we receive an
128 struct CadetTunnelQueueEntry *qe;
131 * How soon should we retry if we fail to get an ACK?
132 * Messages in the queue are sorted by this value.
134 struct GNUNET_TIME_Absolute next_retry;
137 * How long do we wait for an ACK after transmission?
138 * Use for the back-off calculation.
140 struct GNUNET_TIME_Relative retry_delay;
143 * Data message we are trying to send.
145 struct GNUNET_CADET_ChannelAppDataMessage *data_message;
151 * List of received out-of-order data messages.
153 struct CadetOutOfOrderMessage
156 * Double linked list, FIFO style
158 struct CadetOutOfOrderMessage *next;
161 * Double linked list, FIFO style
163 struct CadetOutOfOrderMessage *prev;
166 * ID of the message (messages up to this point needed
167 * before we give this one to the client).
169 struct ChannelMessageIdentifier mid;
172 * The envelope with the payload of the out-of-order message
174 struct GNUNET_MQ_Envelope *env;
180 * Client endpoint of a `struct CadetChannel`. A channel may be a
181 * loopback channel, in which case it has two of these endpoints.
182 * Note that flow control also is required in both directions.
184 struct CadetChannelClient
187 * Client handle. Not by itself sufficient to designate
188 * the client endpoint, as the same client handle may
189 * be used for both the owner and the destination, and
190 * we thus also need the channel ID to identify the client.
192 struct CadetClient *c;
195 * Head of DLL of messages received out of order or while client was unready.
197 struct CadetOutOfOrderMessage *head_recv;
200 * Tail DLL of messages received out of order or while client was unready.
202 struct CadetOutOfOrderMessage *tail_recv;
205 * Local tunnel number for this client.
206 * (if owner >= #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI,
207 * otherwise < #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI)
209 struct GNUNET_CADET_ClientChannelNumber ccn;
212 * Can we send data to the client?
220 * Struct containing all information regarding a channel to a remote client.
225 * Tunnel this channel is in.
227 struct CadetTunnel *t;
230 * Client owner of the tunnel, if any.
231 * (Used if this channel represends the initiating end of the tunnel.)
233 struct CadetChannelClient *owner;
236 * Client destination of the tunnel, if any.
237 * (Used if this channel represents the listening end of the tunnel.)
239 struct CadetChannelClient *dest;
242 * Last entry in the tunnel's queue relating to control messages
243 * (#GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN or
244 * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK). Used to cancel
245 * transmission in case we receive updated information.
247 struct CadetTunnelQueueEntry *last_control_qe;
250 * Head of DLL of messages sent and not yet ACK'd.
252 struct CadetReliableMessage *head_sent;
255 * Tail of DLL of messages sent and not yet ACK'd.
257 struct CadetReliableMessage *tail_sent;
260 * Task to resend/poll in case no ACK is received.
262 struct GNUNET_SCHEDULER_Task *retry_control_task;
265 * Task to resend/poll in case no ACK is received.
267 struct GNUNET_SCHEDULER_Task *retry_data_task;
270 * Last time the channel was used
272 struct GNUNET_TIME_Absolute timestamp;
275 * Destination port of the channel.
277 struct GNUNET_HashCode port;
280 * Counter for exponential backoff.
282 struct GNUNET_TIME_Relative retry_time;
285 * How long does it usually take to get an ACK.
287 struct GNUNET_TIME_Relative expected_delay;
290 * Bitfield of already-received messages past @e mid_recv.
292 * FIXME: not yet properly used (bits here are never set!)
294 uint64_t mid_futures;
297 * Next MID expected for incoming traffic.
299 struct ChannelMessageIdentifier mid_recv;
302 * Next MID to use for outgoing traffic.
304 struct ChannelMessageIdentifier mid_send;
307 * Total (reliable) messages pending ACK for this channel.
309 unsigned int pending_messages;
312 * Maximum (reliable) messages pending ACK for this channel
313 * before we throttle the client.
315 unsigned int max_pending_messages;
318 * Number identifying this channel in its tunnel.
320 struct GNUNET_CADET_ChannelTunnelNumber ctn;
325 enum CadetChannelState state;
328 * Is the tunnel bufferless (minimum latency)?
333 * Is the tunnel reliable?
338 * Is the tunnel out-of-order?
343 * Is this channel a loopback channel, where the destination is us again?
348 * Flag to signal the destruction of the channel. If this is set to
349 * #GNUNET_YES the channel will be destroyed once the queue is
358 * Get the static string for identification of the channel.
362 * @return Static string with the channel IDs.
365 GCCH_2s (const struct CadetChannel *ch)
367 static char buf[128];
369 GNUNET_snprintf (buf,
371 "Channel %s:%s ctn:%X(%X/%X)",
372 (GNUNET_YES == ch->is_loopback)
374 : GNUNET_i2s (GCP_get_id (GCT_get_destination (ch->t))),
375 GNUNET_h2s (&ch->port),
377 (NULL == ch->owner) ? 0 : ntohl (ch->owner->ccn.channel_of_client),
378 (NULL == ch->dest) ? 0 : ntohl (ch->dest->ccn.channel_of_client));
384 * Get the channel's public ID.
388 * @return ID used to identify the channel with the remote peer.
390 struct GNUNET_CADET_ChannelTunnelNumber
391 GCCH_get_id (const struct CadetChannel *ch)
398 * Release memory associated with @a ccc
400 * @param ccc data structure to clean up
403 free_channel_client (struct CadetChannelClient *ccc)
405 struct CadetOutOfOrderMessage *com;
407 while (NULL != (com = ccc->head_recv))
409 GNUNET_CONTAINER_DLL_remove (ccc->head_recv,
412 GNUNET_MQ_discard (com->env);
420 * Destroy the given channel.
422 * @param ch channel to destroy
425 channel_destroy (struct CadetChannel *ch)
427 struct CadetReliableMessage *crm;
429 while (NULL != (crm = ch->head_sent))
431 GNUNET_assert (ch == crm->ch);
434 GCT_send_cancel (crm->qe);
437 GNUNET_CONTAINER_DLL_remove (ch->head_sent,
440 GNUNET_free (crm->data_message);
443 if (NULL != ch->owner)
445 free_channel_client (ch->owner);
448 if (NULL != ch->dest)
450 free_channel_client (ch->dest);
453 if (NULL != ch->last_control_qe)
455 GCT_send_cancel (ch->last_control_qe);
456 ch->last_control_qe = NULL;
458 if (NULL != ch->retry_data_task)
460 GNUNET_SCHEDULER_cancel (ch->retry_data_task);
461 ch->retry_data_task = NULL;
463 if (NULL != ch->retry_control_task)
465 GNUNET_SCHEDULER_cancel (ch->retry_control_task);
466 ch->retry_control_task = NULL;
468 if (GNUNET_NO == ch->is_loopback)
470 GCT_remove_channel (ch->t,
480 * Send a channel create message.
482 * @param cls Channel for which to send.
485 send_channel_open (void *cls);
489 * Function called once the tunnel confirms that we sent the
490 * create message. Delays for a bit until we retry.
492 * @param cls our `struct CadetChannel`.
495 channel_open_sent_cb (void *cls)
497 struct CadetChannel *ch = cls;
499 GNUNET_assert (NULL != ch->last_control_qe);
500 ch->last_control_qe = NULL;
501 ch->retry_time = GNUNET_TIME_STD_BACKOFF (ch->retry_time);
502 LOG (GNUNET_ERROR_TYPE_DEBUG,
503 "Sent CADET_CHANNEL_OPEN on %s, retrying in %s\n",
505 GNUNET_STRINGS_relative_time_to_string (ch->retry_time,
507 ch->retry_control_task
508 = GNUNET_SCHEDULER_add_delayed (ch->retry_time,
515 * Send a channel open message.
517 * @param cls Channel for which to send.
520 send_channel_open (void *cls)
522 struct CadetChannel *ch = cls;
523 struct GNUNET_CADET_ChannelOpenMessage msgcc;
526 ch->retry_control_task = NULL;
527 LOG (GNUNET_ERROR_TYPE_DEBUG,
528 "Sending CHANNEL_OPEN message for %s\n",
532 options |= GNUNET_CADET_OPTION_NOBUFFER;
534 options |= GNUNET_CADET_OPTION_RELIABLE;
535 if (ch->out_of_order)
536 options |= GNUNET_CADET_OPTION_OUT_OF_ORDER;
537 msgcc.header.size = htons (sizeof (msgcc));
538 msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN);
539 msgcc.opt = htonl (options);
540 msgcc.port = ch->port;
542 ch->state = CADET_CHANNEL_OPEN_SENT;
543 ch->last_control_qe = GCT_send (ch->t,
545 &channel_open_sent_cb,
547 GNUNET_assert (NULL == ch->retry_control_task);
552 * Function called once and only once after a channel was bound
553 * to its tunnel via #GCT_add_channel() is ready for transmission.
554 * Note that this is only the case for channels that this peer
555 * initiates, as for incoming channels we assume that they are
556 * ready for transmission immediately upon receiving the open
557 * message. Used to bootstrap the #GCT_send() process.
559 * @param ch the channel for which the tunnel is now ready
562 GCCH_tunnel_up (struct CadetChannel *ch)
564 GNUNET_assert (NULL == ch->retry_control_task);
565 LOG (GNUNET_ERROR_TYPE_DEBUG,
566 "Tunnel up, sending CHANNEL_OPEN on %s now\n",
568 ch->retry_control_task
569 = GNUNET_SCHEDULER_add_now (&send_channel_open,
575 * Create a new channel.
577 * @param owner local client owning the channel
578 * @param ccn local number of this channel at the @a owner
579 * @param destination peer to which we should build the channel
580 * @param port desired port at @a destination
581 * @param options options for the channel
582 * @return handle to the new channel
584 struct CadetChannel *
585 GCCH_channel_local_new (struct CadetClient *owner,
586 struct GNUNET_CADET_ClientChannelNumber ccn,
587 struct CadetPeer *destination,
588 const struct GNUNET_HashCode *port,
591 struct CadetChannel *ch;
592 struct CadetChannelClient *ccco;
594 ccco = GNUNET_new (struct CadetChannelClient);
597 ccco->client_ready = GNUNET_YES;
599 ch = GNUNET_new (struct CadetChannel);
600 ch->mid_recv.mid = htonl (1); /* The OPEN_ACK counts as message 0! */
601 ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER));
602 ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE));
603 ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER));
604 ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */
607 if (0 == memcmp (&my_full_id,
608 GCP_get_id (destination),
609 sizeof (struct GNUNET_PeerIdentity)))
611 struct CadetClient *c;
613 ch->is_loopback = GNUNET_YES;
614 c = GNUNET_CONTAINER_multihashmap_get (open_ports,
618 /* port closed, wait for it to possibly open */
619 (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
622 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
623 LOG (GNUNET_ERROR_TYPE_DEBUG,
624 "Created loose incoming loopback channel to port %s\n",
625 GNUNET_h2s (&ch->port));
629 ch->dest = GNUNET_new (struct CadetChannelClient);
631 ch->dest->client_ready = GNUNET_YES;
638 ch->t = GCP_get_tunnel (destination,
640 ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
641 ch->ctn = GCT_add_channel (ch->t,
644 GNUNET_STATISTICS_update (stats,
648 LOG (GNUNET_ERROR_TYPE_DEBUG,
649 "Created channel to port %s at peer %s for %s using %s\n",
651 GCP_2s (destination),
653 (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t));
659 * We had an incoming channel to a port that is closed.
660 * It has not been opened for a while, drop it.
662 * @param cls the channel to drop
665 timeout_closed_cb (void *cls)
667 struct CadetChannel *ch = cls;
669 ch->retry_control_task = NULL;
670 LOG (GNUNET_ERROR_TYPE_DEBUG,
671 "Closing incoming channel to port %s from peer %s due to timeout\n",
672 GNUNET_h2s (&ch->port),
673 GCP_2s (GCT_get_destination (ch->t)));
674 channel_destroy (ch);
679 * Create a new channel based on a request coming in over the network.
681 * @param t tunnel to the remote peer
682 * @param ctn identifier of this channel in the tunnel
683 * @param port desired local port
684 * @param options options for the channel
685 * @return handle to the new channel
687 struct CadetChannel *
688 GCCH_channel_incoming_new (struct CadetTunnel *t,
689 struct GNUNET_CADET_ChannelTunnelNumber ctn,
690 const struct GNUNET_HashCode *port,
693 struct CadetChannel *ch;
694 struct CadetClient *c;
696 ch = GNUNET_new (struct CadetChannel);
700 ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
701 ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER));
702 ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE));
703 ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER));
704 ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */
705 GNUNET_STATISTICS_update (stats,
710 c = GNUNET_CONTAINER_multihashmap_get (open_ports,
714 /* port closed, wait for it to possibly open */
715 (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
718 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
719 ch->retry_control_task
720 = GNUNET_SCHEDULER_add_delayed (TIMEOUT_CLOSED_PORT,
723 LOG (GNUNET_ERROR_TYPE_DEBUG,
724 "Created loose incoming channel to port %s from peer %s\n",
725 GNUNET_h2s (&ch->port),
726 GCP_2s (GCT_get_destination (ch->t)));
733 GNUNET_STATISTICS_update (stats,
742 * Function called once the tunnel confirms that we sent the
743 * ACK message. Just remembers it was sent, we do not expect
746 * @param cls our `struct CadetChannel`.
749 send_ack_cb (void *cls)
751 struct CadetChannel *ch = cls;
753 GNUNET_assert (NULL != ch->last_control_qe);
754 ch->last_control_qe = NULL;
759 * Compute and send the current #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK to the other peer.
761 * @param ch channel to send the #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK for
764 send_channel_data_ack (struct CadetChannel *ch)
766 struct GNUNET_CADET_ChannelDataAckMessage msg;
768 msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK);
769 msg.header.size = htons (sizeof (msg));
771 msg.mid.mid = htonl (ntohl (ch->mid_recv.mid) - 1);
772 msg.futures = GNUNET_htonll (ch->mid_futures);
773 if (NULL != ch->last_control_qe)
774 GCT_send_cancel (ch->last_control_qe);
775 LOG (GNUNET_ERROR_TYPE_DEBUG,
776 "Sending DATA_ACK %u:%llX via %s\n",
777 (unsigned int) ntohl (msg.mid.mid),
778 (unsigned long long) ch->mid_futures,
780 ch->last_control_qe = GCT_send (ch->t,
788 * Send our initial #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK to the client confirming that the
791 * @param cls the `struct CadetChannel`
794 send_open_ack (void *cls)
796 struct CadetChannel *ch = cls;
797 struct GNUNET_CADET_ChannelManageMessage msg;
799 LOG (GNUNET_ERROR_TYPE_DEBUG,
800 "Sending CHANNEL_OPEN_ACK on %s\n",
802 ch->retry_control_task = NULL;
803 msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK);
804 msg.header.size = htons (sizeof (msg));
805 msg.reserved = htonl (0);
807 if (NULL != ch->last_control_qe)
808 GCT_send_cancel (ch->last_control_qe);
809 ch->last_control_qe = GCT_send (ch->t,
817 * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for
818 * this channel. If the binding was successful, (re)transmit the
819 * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK.
821 * @param ch channel that got the duplicate open
824 GCCH_handle_duplicate_open (struct CadetChannel *ch)
826 if (NULL == ch->dest)
828 LOG (GNUNET_ERROR_TYPE_DEBUG,
829 "Ignoring duplicate channel OPEN on %s: port is closed\n",
833 if (NULL != ch->retry_control_task)
835 LOG (GNUNET_ERROR_TYPE_DEBUG,
836 "Ignoring duplicate channel OPEN on %s: control message is pending\n",
840 LOG (GNUNET_ERROR_TYPE_DEBUG,
841 "Retransmitting OPEN_ACK on %s\n",
843 ch->retry_control_task
844 = GNUNET_SCHEDULER_add_now (&send_open_ack,
850 * Send a #GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK to the client to solicit more messages.
852 * @param ch channel the ack is for
853 * @param to_owner #GNUNET_YES to send to owner,
854 * #GNUNET_NO to send to dest
857 send_ack_to_client (struct CadetChannel *ch,
860 struct GNUNET_MQ_Envelope *env;
861 struct GNUNET_CADET_LocalAck *ack;
862 struct CadetChannelClient *ccc;
864 ccc = (GNUNET_YES == to_owner) ? ch->owner : ch->dest;
867 /* This can happen if we are just getting ACKs after
868 our local client already disconnected. */
869 GNUNET_assert (GNUNET_YES == ch->destroy);
872 env = GNUNET_MQ_msg (ack,
873 GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK);
875 LOG (GNUNET_ERROR_TYPE_DEBUG,
876 "Sending CADET_LOCAL_ACK to %s (%s) at ccn %X (%u/%u pending)\n",
878 (GNUNET_YES == to_owner) ? "owner" : "dest",
879 ntohl (ack->ccn.channel_of_client),
880 ch->pending_messages,
881 ch->max_pending_messages);
882 GSC_send_to_client (ccc->c,
888 * A client is bound to the port that we have a channel
889 * open to. Send the acknowledgement for the connection
890 * request and establish the link with the client.
892 * @param ch open incoming channel
893 * @param c client listening on the respective port
896 GCCH_bind (struct CadetChannel *ch,
897 struct CadetClient *c)
900 struct CadetChannelClient *cccd;
902 LOG (GNUNET_ERROR_TYPE_DEBUG,
903 "Binding %s from %s to port %s of %s\n",
906 GNUNET_h2s (&ch->port),
908 if (NULL != ch->retry_control_task)
910 /* there might be a timeout task here */
911 GNUNET_SCHEDULER_cancel (ch->retry_control_task);
912 ch->retry_control_task = NULL;
916 options |= GNUNET_CADET_OPTION_NOBUFFER;
918 options |= GNUNET_CADET_OPTION_RELIABLE;
919 if (ch->out_of_order)
920 options |= GNUNET_CADET_OPTION_OUT_OF_ORDER;
921 cccd = GNUNET_new (struct CadetChannelClient);
924 cccd->client_ready = GNUNET_YES;
925 cccd->ccn = GSC_bind (c,
927 (GNUNET_YES == ch->is_loopback)
928 ? GCP_get (&my_full_id,
930 : GCT_get_destination (ch->t),
933 GNUNET_assert (ntohl (cccd->ccn.channel_of_client) <
934 GNUNET_CADET_LOCAL_CHANNEL_ID_CLI);
935 ch->mid_recv.mid = htonl (1); /* The OPEN counts as message 0! */
936 if (GNUNET_YES == ch->is_loopback)
938 ch->state = CADET_CHANNEL_OPEN_SENT;
939 GCCH_handle_channel_open_ack (ch);
943 /* notify other peer that we accepted the connection */
944 ch->retry_control_task
945 = GNUNET_SCHEDULER_add_now (&send_open_ack,
948 /* give client it's initial supply of ACKs */
949 GNUNET_assert (ntohl (cccd->ccn.channel_of_client) <
950 GNUNET_CADET_LOCAL_CHANNEL_ID_CLI);
951 for (unsigned int i=0;i<ch->max_pending_messages;i++)
952 send_ack_to_client (ch,
958 * Destroy locally created channel. Called by the local client, so no
959 * need to tell the client.
961 * @param ch channel to destroy
962 * @param c client that caused the destruction
963 * @param ccn client number of the client @a c
966 GCCH_channel_local_destroy (struct CadetChannel *ch,
967 struct CadetClient *c,
968 struct GNUNET_CADET_ClientChannelNumber ccn)
970 LOG (GNUNET_ERROR_TYPE_DEBUG,
971 "%s asks for destruction of %s\n",
974 GNUNET_assert (NULL != c);
975 if ( (NULL != ch->owner) &&
976 (c == ch->owner->c) &&
977 (ccn.channel_of_client == ch->owner->ccn.channel_of_client) )
979 free_channel_client (ch->owner);
982 else if ( (NULL != ch->dest) &&
983 (c == ch->dest->c) &&
984 (ccn.channel_of_client == ch->dest->ccn.channel_of_client) )
986 free_channel_client (ch->dest);
994 if (GNUNET_YES == ch->destroy)
996 /* other end already destroyed, with the local client gone, no need
997 to finish transmissions, just destroy immediately. */
998 channel_destroy (ch);
1001 if ( (NULL != ch->head_sent) ||
1002 (NULL != ch->owner) ||
1003 (NULL != ch->dest) )
1005 /* Wait for other end to destroy us as well,
1006 and otherwise allow send queue to be transmitted first */
1007 ch->destroy = GNUNET_YES;
1010 /* If the we ever sent the CHANNEL_CREATE, we need to send a destroy message. */
1011 if (CADET_CHANNEL_NEW != ch->state)
1012 GCT_send_channel_destroy (ch->t,
1014 /* Nothing left to do, just finish destruction */
1015 channel_destroy (ch);
1020 * We got an acknowledgement for the creation of the channel
1021 * (the port is open on the other side). Begin transmissions.
1023 * @param ch channel to destroy
1026 GCCH_handle_channel_open_ack (struct CadetChannel *ch)
1030 case CADET_CHANNEL_NEW:
1031 /* this should be impossible */
1034 case CADET_CHANNEL_OPEN_SENT:
1035 if (NULL == ch->owner)
1037 /* We're not the owner, wrong direction! */
1038 GNUNET_break_op (0);
1041 LOG (GNUNET_ERROR_TYPE_DEBUG,
1042 "Received CHANNEL_OPEN_ACK for waiting %s, entering READY state\n",
1044 if (NULL != ch->retry_control_task) /* can be NULL if ch->is_loopback */
1046 GNUNET_SCHEDULER_cancel (ch->retry_control_task);
1047 ch->retry_control_task = NULL;
1049 ch->state = CADET_CHANNEL_READY;
1050 /* On first connect, send client as many ACKs as we allow messages
1052 for (unsigned int i=0;i<ch->max_pending_messages;i++)
1053 send_ack_to_client (ch,
1056 case CADET_CHANNEL_READY:
1057 /* duplicate ACK, maybe we retried the CREATE. Ignore. */
1058 LOG (GNUNET_ERROR_TYPE_DEBUG,
1059 "Received duplicate channel OPEN_ACK for %s\n",
1061 GNUNET_STATISTICS_update (stats,
1062 "# duplicate CREATE_ACKs",
1071 * Test if element @a e1 comes before element @a e2.
1073 * @param cls closure, to a flag where we indicate duplicate packets
1074 * @param e1 an element of to sort
1075 * @param e2 another element to sort
1076 * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO
1079 is_before (void *cls,
1080 struct CadetOutOfOrderMessage *m1,
1081 struct CadetOutOfOrderMessage *m2)
1083 int *duplicate = cls;
1084 uint32_t v1 = ntohl (m1->mid.mid);
1085 uint32_t v2 = ntohl (m2->mid.mid);
1090 *duplicate = GNUNET_YES;
1091 if (delta > (uint32_t) INT_MAX)
1093 /* in overflow range, we can safely assume we wrapped around */
1098 /* result is small, thus v2 > v1, thus e1 < e2 */
1105 * We got payload data for a channel. Pass it on to the client
1106 * and send an ACK to the other end (once flow control allows it!)
1108 * @param ch channel that got data
1109 * @param msg message that was received
1112 GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
1113 const struct GNUNET_CADET_ChannelAppDataMessage *msg)
1115 struct GNUNET_MQ_Envelope *env;
1116 struct GNUNET_CADET_LocalData *ld;
1117 struct CadetChannelClient *ccc;
1118 size_t payload_size;
1119 struct CadetOutOfOrderMessage *com;
1125 GNUNET_assert (GNUNET_NO == ch->is_loopback);
1126 if ( (GNUNET_YES == ch->destroy) &&
1127 (NULL == ch->owner) &&
1128 (NULL == ch->dest) )
1130 /* This client is gone, but we still have messages to send to
1131 the other end (which is why @a ch is not yet dead). However,
1132 we cannot pass messages to our client anymore. */
1133 LOG (GNUNET_ERROR_TYPE_DEBUG,
1134 "Dropping incoming payload on %s as this end is already closed\n",
1136 /* FIXME: send back ACK/NACK/Closed notification
1137 to stop retransmissions! */
1140 payload_size = ntohs (msg->header.size) - sizeof (*msg);
1141 env = GNUNET_MQ_msg_extra (ld,
1143 GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA);
1144 ld->ccn = (NULL == ch->dest) ? ch->owner->ccn : ch->dest->ccn;
1145 GNUNET_memcpy (&ld[1],
1148 ccc = (NULL != ch->owner) ? ch->owner : ch->dest;
1149 if ( (GNUNET_YES == ccc->client_ready) &&
1150 ( (GNUNET_YES == ch->out_of_order) ||
1151 (msg->mid.mid == ch->mid_recv.mid) ) )
1153 LOG (GNUNET_ERROR_TYPE_DEBUG,
1154 "Giving %u bytes of payload with MID %u from %s to client %s\n",
1155 (unsigned int) payload_size,
1156 ntohl (msg->mid.mid),
1159 ccc->client_ready = GNUNET_NO;
1160 GSC_send_to_client (ccc->c,
1162 ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid));
1163 ch->mid_futures >>= 1;
1164 if (GNUNET_YES == ch->reliable)
1165 send_channel_data_ack (ch);
1169 /* check if message ought to be dropped because it is anicent/too distant/duplicate */
1170 mid_min = ntohl (ch->mid_recv.mid);
1171 mid_max = mid_min + MAX_OUT_OF_ORDER_DISTANCE;
1172 mid_msg = ntohl (msg->mid.mid);
1173 if ( ( (uint32_t) (mid_msg - mid_min) > MAX_OUT_OF_ORDER_DISTANCE) ||
1174 ( (uint32_t) (mid_max - mid_msg) > MAX_OUT_OF_ORDER_DISTANCE) )
1176 LOG (GNUNET_ERROR_TYPE_DEBUG,
1177 "Duplicate ancient or future payload of %u bytes on %s (mid %u) dropped\n",
1178 (unsigned int) payload_size,
1180 ntohl (msg->mid.mid));
1181 GNUNET_STATISTICS_update (stats,
1182 "# duplicate DATA (ancient or future)",
1185 GNUNET_MQ_discard (env);
1189 /* Insert message into sorted out-of-order queue */
1190 com = GNUNET_new (struct CadetOutOfOrderMessage);
1191 com->mid = msg->mid;
1193 duplicate = GNUNET_NO;
1194 GNUNET_CONTAINER_DLL_insert_sorted (struct CadetOutOfOrderMessage,
1200 if (GNUNET_YES == duplicate)
1202 /* Duplicate within the queue, drop also */
1203 LOG (GNUNET_ERROR_TYPE_DEBUG,
1204 "Duplicate payload of %u bytes on %s (mid %u) dropped\n",
1205 (unsigned int) payload_size,
1207 ntohl (msg->mid.mid));
1208 GNUNET_STATISTICS_update (stats,
1212 GNUNET_CONTAINER_DLL_remove (ccc->head_recv,
1215 GNUNET_MQ_discard (com->env);
1217 if (GNUNET_YES == ch->reliable)
1218 send_channel_data_ack (ch);
1221 LOG (GNUNET_ERROR_TYPE_DEBUG,
1222 "Queued %s payload of %u bytes on %s-%X(%p) (mid %u, need %u first)\n",
1223 (GNUNET_YES == ccc->client_ready)
1225 : "client-not-ready",
1226 (unsigned int) payload_size,
1228 ntohl (ccc->ccn.channel_of_client),
1230 ntohl (msg->mid.mid),
1231 ntohl (ch->mid_recv.mid));
1232 send_channel_data_ack (ch);
1237 * Function called once the tunnel has sent one of our messages.
1238 * If the message is unreliable, simply frees the `crm`. If the
1239 * message was reliable, calculate retransmission time and
1240 * wait for ACK (or retransmit).
1242 * @param cls the `struct CadetReliableMessage` that was sent
1245 data_sent_cb (void *cls);
1249 * We need to retry a transmission, the last one took too long to
1252 * @param cls the `struct CadetChannel` where we need to retransmit
1255 retry_transmission (void *cls)
1257 struct CadetChannel *ch = cls;
1258 struct CadetReliableMessage *crm = ch->head_sent;
1260 ch->retry_data_task = NULL;
1261 GNUNET_assert (NULL == crm->qe);
1262 LOG (GNUNET_ERROR_TYPE_DEBUG,
1263 "Retrying transmission on %s of message %u\n",
1265 (unsigned int) ntohl (crm->data_message->mid.mid));
1266 crm->qe = GCT_send (ch->t,
1267 &crm->data_message->header,
1270 GNUNET_assert (NULL == ch->retry_data_task);
1275 * We got an PLAINTEXT_DATA_ACK for a message in our queue, remove it from
1276 * the queue and tell our client that it can send more.
1278 * @param ch the channel that got the PLAINTEXT_DATA_ACK
1279 * @param crm the message that got acknowledged
1282 handle_matching_ack (struct CadetChannel *ch,
1283 struct CadetReliableMessage *crm)
1285 GNUNET_CONTAINER_DLL_remove (ch->head_sent,
1288 ch->pending_messages--;
1289 GNUNET_assert (ch->pending_messages < ch->max_pending_messages);
1290 LOG (GNUNET_ERROR_TYPE_DEBUG,
1291 "Received DATA_ACK on %s for message %u (%u ACKs pending)\n",
1293 (unsigned int) ntohl (crm->data_message->mid.mid),
1294 ch->pending_messages);
1295 if (NULL != crm->qe)
1297 GCT_send_cancel (crm->qe);
1300 GNUNET_free (crm->data_message);
1302 send_ack_to_client (ch,
1310 * We got an acknowledgement for payload data for a channel.
1311 * Possibly resume transmissions.
1313 * @param ch channel that got the ack
1314 * @param ack details about what was received
1317 GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch,
1318 const struct GNUNET_CADET_ChannelDataAckMessage *ack)
1320 struct CadetReliableMessage *crm;
1321 struct CadetReliableMessage *crmn;
1327 GNUNET_break (GNUNET_NO == ch->is_loopback);
1328 if (GNUNET_NO == ch->reliable)
1330 /* not expecting ACKs on unreliable channel, odd */
1331 GNUNET_break_op (0);
1334 mid_base = ntohl (ack->mid.mid);
1335 mid_mask = GNUNET_htonll (ack->futures);
1337 for (crm = ch->head_sent;
1342 if (ack->mid.mid == crm->data_message->mid.mid)
1344 handle_matching_ack (ch,
1349 delta = (unsigned int) (ntohl (crm->data_message->mid.mid) - mid_base) - 1;
1352 if (0 != (mid_mask & (1LLU << delta)))
1354 handle_matching_ack (ch,
1359 if (GNUNET_NO == found)
1361 /* ACK for message we already dropped, might have been a
1362 duplicate ACK? Ignore. */
1363 LOG (GNUNET_ERROR_TYPE_DEBUG,
1364 "Duplicate DATA_ACK on %s, ignoring\n",
1366 GNUNET_STATISTICS_update (stats,
1367 "# duplicate DATA_ACKs",
1372 if (NULL != ch->retry_data_task)
1374 GNUNET_SCHEDULER_cancel (ch->retry_data_task);
1375 ch->retry_data_task = NULL;
1377 if (NULL != ch->head_sent)
1379 = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry,
1380 &retry_transmission,
1386 * Destroy channel, based on the other peer closing the
1387 * connection. Also needs to remove this channel from
1390 * @param ch channel to destroy
1393 GCCH_handle_remote_destroy (struct CadetChannel *ch)
1395 struct CadetChannelClient *ccc;
1397 GNUNET_assert (GNUNET_NO == ch->is_loopback);
1398 LOG (GNUNET_ERROR_TYPE_DEBUG,
1399 "Received remote channel DESTROY for %s\n",
1401 if (GNUNET_YES == ch->destroy)
1403 /* Local client already gone, this is instant-death. */
1404 channel_destroy (ch);
1407 ccc = (NULL != ch->owner) ? ch->owner : ch->dest;
1408 if (NULL != ccc->head_recv)
1410 LOG (GNUNET_ERROR_TYPE_WARNING,
1411 "Lost end of transmission due to remote shutdown on %s\n",
1413 /* FIXME: change API to notify client about truncated transmission! */
1415 ch->destroy = GNUNET_YES;
1416 GSC_handle_remote_channel_destroy (ccc->c,
1419 channel_destroy (ch);
1424 * Test if element @a e1 comes before element @a e2.
1426 * @param cls closure, to a flag where we indicate duplicate packets
1427 * @param crm1 an element of to sort
1428 * @param crm2 another element to sort
1429 * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO
1432 cmp_crm_by_next_retry (void *cls,
1433 struct CadetReliableMessage *crm1,
1434 struct CadetReliableMessage *crm2)
1436 if (crm1->next_retry.abs_value_us <
1437 crm2->next_retry.abs_value_us)
1444 * Function called once the tunnel has sent one of our messages.
1445 * If the message is unreliable, simply frees the `crm`. If the
1446 * message was reliable, calculate retransmission time and
1447 * wait for ACK (or retransmit).
1449 * @param cls the `struct CadetReliableMessage` that was sent
1452 data_sent_cb (void *cls)
1454 struct CadetReliableMessage *crm = cls;
1455 struct CadetChannel *ch = crm->ch;
1457 GNUNET_assert (GNUNET_NO == ch->is_loopback);
1458 GNUNET_assert (NULL != crm->qe);
1460 GNUNET_CONTAINER_DLL_remove (ch->head_sent,
1463 if (GNUNET_NO == ch->reliable)
1465 GNUNET_free (crm->data_message);
1467 ch->pending_messages--;
1468 send_ack_to_client (ch,
1474 if (0 == crm->retry_delay.rel_value_us)
1475 crm->retry_delay = ch->expected_delay;
1477 crm->retry_delay = GNUNET_TIME_STD_BACKOFF (crm->retry_delay);
1478 crm->retry_delay = GNUNET_TIME_relative_max (crm->retry_delay,
1480 crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay);
1482 GNUNET_CONTAINER_DLL_insert_sorted (struct CadetReliableMessage,
1483 cmp_crm_by_next_retry,
1488 LOG (GNUNET_ERROR_TYPE_DEBUG,
1489 "Message %u sent, next transmission on %s in %s\n",
1490 (unsigned int) ntohl (crm->data_message->mid.mid),
1492 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (ch->head_sent->next_retry),
1494 if (crm == ch->head_sent)
1496 /* We are the new head, need to reschedule retry task */
1497 if (NULL != ch->retry_data_task)
1498 GNUNET_SCHEDULER_cancel (ch->retry_data_task);
1500 = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry,
1501 &retry_transmission,
1508 * Handle data given by a client.
1510 * Check whether the client is allowed to send in this tunnel, save if
1511 * channel is reliable and send an ACK to the client if there is still
1512 * buffer space in the tunnel.
1514 * @param ch Channel.
1515 * @param sender_ccn ccn of the sender
1516 * @param buf payload to transmit.
1517 * @param buf_len number of bytes in @a buf
1518 * @return #GNUNET_OK if everything goes well,
1519 * #GNUNET_SYSERR in case of an error.
1522 GCCH_handle_local_data (struct CadetChannel *ch,
1523 struct GNUNET_CADET_ClientChannelNumber sender_ccn,
1527 struct CadetReliableMessage *crm;
1529 if (ch->pending_messages > ch->max_pending_messages)
1532 return GNUNET_SYSERR;
1534 ch->pending_messages++;
1536 if (GNUNET_YES == ch->is_loopback)
1538 struct CadetChannelClient *receiver;
1539 struct GNUNET_MQ_Envelope *env;
1540 struct GNUNET_CADET_LocalData *ld;
1543 env = GNUNET_MQ_msg_extra (ld,
1545 GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA);
1546 if (sender_ccn.channel_of_client ==
1547 ch->owner->ccn.channel_of_client)
1549 receiver = ch->dest;
1550 to_owner = GNUNET_NO;
1554 GNUNET_assert (sender_ccn.channel_of_client ==
1555 ch->dest->ccn.channel_of_client);
1556 receiver = ch->owner;
1557 to_owner = GNUNET_YES;
1559 ld->ccn = receiver->ccn;
1560 GNUNET_memcpy (&ld[1],
1563 if (GNUNET_YES == receiver->client_ready)
1565 GSC_send_to_client (receiver->c,
1567 send_ack_to_client (ch,
1572 struct CadetOutOfOrderMessage *oom;
1574 oom = GNUNET_new (struct CadetOutOfOrderMessage);
1576 GNUNET_CONTAINER_DLL_insert_tail (receiver->head_recv,
1577 receiver->tail_recv,
1583 /* Everything is correct, send the message. */
1584 crm = GNUNET_malloc (sizeof (*crm));
1586 crm->data_message = GNUNET_malloc (sizeof (struct GNUNET_CADET_ChannelAppDataMessage)
1588 crm->data_message->header.size = htons (sizeof (struct GNUNET_CADET_ChannelAppDataMessage) + buf_len);
1589 crm->data_message->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA);
1590 ch->mid_send.mid = htonl (ntohl (ch->mid_send.mid) + 1);
1591 crm->data_message->mid = ch->mid_send;
1592 crm->data_message->ctn = ch->ctn;
1593 GNUNET_memcpy (&crm->data_message[1],
1596 GNUNET_CONTAINER_DLL_insert (ch->head_sent,
1599 LOG (GNUNET_ERROR_TYPE_DEBUG,
1600 "Sending %u bytes from local client to %s with MID %u\n",
1603 ntohl (crm->data_message->mid.mid));
1604 if (NULL != ch->retry_data_task)
1606 GNUNET_SCHEDULER_cancel (ch->retry_data_task);
1607 ch->retry_data_task = NULL;
1609 crm->qe = GCT_send (ch->t,
1610 &crm->data_message->header,
1613 GNUNET_assert (NULL == ch->retry_data_task);
1619 * Handle ACK from client on local channel. Means the client is ready
1620 * for more data, see if we have any for it.
1622 * @param ch channel to destroy
1623 * @param client_ccn ccn of the client sending the ack
1626 GCCH_handle_local_ack (struct CadetChannel *ch,
1627 struct GNUNET_CADET_ClientChannelNumber client_ccn)
1629 struct CadetChannelClient *ccc;
1630 struct CadetOutOfOrderMessage *com;
1632 if ( (NULL != ch->owner) &&
1633 (ch->owner->ccn.channel_of_client == client_ccn.channel_of_client) )
1635 else if ( (NULL != ch->dest) &&
1636 (ch->dest->ccn.channel_of_client == client_ccn.channel_of_client) )
1640 ccc->client_ready = GNUNET_YES;
1641 com = ccc->head_recv;
1644 LOG (GNUNET_ERROR_TYPE_DEBUG,
1645 "Got LOCAL_ACK, %s-%X ready to receive more data, but none pending on %s-%X(%p)!\n",
1647 ntohl (client_ccn.channel_of_client),
1649 ntohl (ccc->ccn.channel_of_client),
1651 return; /* none pending */
1653 if (GNUNET_YES == ch->is_loopback)
1657 /* Messages are always in-order, just send */
1658 GNUNET_CONTAINER_DLL_remove (ccc->head_recv,
1661 GSC_send_to_client (ccc->c,
1663 /* Notify sender that we can receive more */
1664 if (ccc->ccn.channel_of_client ==
1665 ch->owner->ccn.channel_of_client)
1667 to_owner = GNUNET_NO;
1671 GNUNET_assert (ccc->ccn.channel_of_client ==
1672 ch->dest->ccn.channel_of_client);
1673 to_owner = GNUNET_YES;
1675 send_ack_to_client (ch,
1681 if ( (com->mid.mid != ch->mid_recv.mid) &&
1682 (GNUNET_NO == ch->out_of_order) )
1684 LOG (GNUNET_ERROR_TYPE_DEBUG,
1685 "Got LOCAL_ACK, %s-%X ready to receive more data (but next one is out-of-order %u vs. %u)!\n",
1687 ntohl (ccc->ccn.channel_of_client),
1688 ntohl (com->mid.mid),
1689 ntohl (ch->mid_recv.mid));
1690 return; /* missing next one in-order */
1693 LOG (GNUNET_ERROR_TYPE_DEBUG,
1694 "Got LOCAL ACK, passing payload message %u to %s-%X on %s\n",
1695 ntohl (com->mid.mid),
1697 ntohl (ccc->ccn.channel_of_client),
1700 /* all good, pass next message to client */
1701 GNUNET_CONTAINER_DLL_remove (ccc->head_recv,
1704 /* FIXME: if unreliable, this is not aggressive
1705 enough, as it would be OK to have lost some! */
1706 ch->mid_recv.mid = htonl (1 + ntohl (com->mid.mid));
1707 ch->mid_futures >>= 1; /* equivalent to division by 2 */
1708 ccc->client_ready = GNUNET_NO;
1709 GSC_send_to_client (ccc->c,
1712 if ( (0xFFULL == (ch->mid_futures & 0xFFULL)) &&
1713 (GNUNET_YES == ch->reliable) )
1715 /* The next 15 messages were also already received (0xFF), this
1716 suggests that the sender may be blocked on flow control
1717 urgently waiting for an ACK from us. (As we have an inherent
1718 maximum of 64 bits, and 15 is getting too close for comfort.)
1719 So we should send one now. */
1720 LOG (GNUNET_ERROR_TYPE_DEBUG,
1721 "Sender on %s likely blocked on flow-control, sending ACK now.\n",
1723 if (GNUNET_YES == ch->reliable)
1724 send_channel_data_ack (ch);
1727 if (NULL != ccc->head_recv)
1729 if (GNUNET_NO == ch->destroy)
1731 GCT_send_channel_destroy (ch->t,
1733 channel_destroy (ch);
1737 #define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-chn",__VA_ARGS__)
1743 * @param ch Channel.
1744 * @param level Debug level to use.
1747 GCCH_debug (struct CadetChannel *ch,
1748 enum GNUNET_ErrorType level)
1752 do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK),
1754 __FILE__, __FUNCTION__, __LINE__);
1760 LOG2 (level, "CHN *** DEBUG NULL CHANNEL ***\n");
1768 if (NULL != ch->owner)
1771 "CHN origin %s ready %s local-id: %u\n",
1772 GSC_2s (ch->owner->c),
1773 ch->owner->client_ready ? "YES" : "NO",
1774 ntohl (ch->owner->ccn.channel_of_client));
1776 if (NULL != ch->dest)
1779 "CHN destination %s ready %s local-id: %u\n",
1780 GSC_2s (ch->dest->c),
1781 ch->dest->client_ready ? "YES" : "NO",
1782 ntohl (ch->dest->ccn.channel_of_client));
1785 "CHN Message IDs recv: %d (%LLX), send: %d\n",
1786 ntohl (ch->mid_recv.mid),
1787 (unsigned long long) ch->mid_futures,
1788 ntohl (ch->mid_send.mid));
1793 /* end of gnunet-service-cadet-new_channel.c */