diff options
Diffstat (limited to 'src/cadet/gnunet-service-cadet_channel.c')
-rw-r--r-- | src/cadet/gnunet-service-cadet_channel.c | 2074 |
1 files changed, 0 insertions, 2074 deletions
diff --git a/src/cadet/gnunet-service-cadet_channel.c b/src/cadet/gnunet-service-cadet_channel.c deleted file mode 100644 index 9a2180cc1..000000000 --- a/src/cadet/gnunet-service-cadet_channel.c +++ /dev/null | |||
@@ -1,2074 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2001-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 | * @file cadet/gnunet-service-cadet_channel.c | ||
22 | * @brief logical links between CADET clients | ||
23 | * @author Bartlomiej Polot | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - Congestion/flow control: | ||
28 | * + estimate max bandwidth using bursts and use to for CONGESTION CONTROL! | ||
29 | * (and figure out how/where to use this!) | ||
30 | * + figure out flow control without ACKs (unreliable traffic!) | ||
31 | * - revisit handling of 'unbuffered' traffic! | ||
32 | * (need to push down through tunnel into connection selection) | ||
33 | * - revisit handling of 'buffered' traffic: 4 is a rather small buffer; maybe | ||
34 | * reserve more bits in 'options' to allow for buffer size control? | ||
35 | */ | ||
36 | #include "platform.h" | ||
37 | #include "cadet.h" | ||
38 | #include "gnunet_statistics_service.h" | ||
39 | #include "gnunet-service-cadet_channel.h" | ||
40 | #include "gnunet-service-cadet_connection.h" | ||
41 | #include "gnunet-service-cadet_tunnels.h" | ||
42 | #include "gnunet-service-cadet_paths.h" | ||
43 | |||
44 | #define LOG(level, ...) GNUNET_log_from (level, "cadet-chn", __VA_ARGS__) | ||
45 | |||
46 | /** | ||
47 | * How long do we initially wait before retransmitting? | ||
48 | */ | ||
49 | #define CADET_INITIAL_RETRANSMIT_TIME \ | ||
50 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250) | ||
51 | |||
52 | /** | ||
53 | * How long do we wait before dropping state about incoming | ||
54 | * connection to closed port? | ||
55 | */ | ||
56 | #define TIMEOUT_CLOSED_PORT \ | ||
57 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | ||
58 | |||
59 | /** | ||
60 | * How long do we wait at least before retransmitting ever? | ||
61 | */ | ||
62 | #define MIN_RTT_DELAY \ | ||
63 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 75) | ||
64 | |||
65 | /** | ||
66 | * Maximum message ID into the future we accept for out-of-order messages. | ||
67 | * If the message is more than this into the future, we drop it. This is | ||
68 | * important both to detect values that are actually in the past, as well | ||
69 | * as to limit adversarially triggerable memory consumption. | ||
70 | * | ||
71 | * Note that right now we have "max_pending_messages = 4" hard-coded in | ||
72 | * the logic below, so a value of 4 would suffice here. But we plan to | ||
73 | * allow larger windows in the future... | ||
74 | */ | ||
75 | #define MAX_OUT_OF_ORDER_DISTANCE 1024 | ||
76 | |||
77 | |||
78 | /** | ||
79 | * All the states a channel can be in. | ||
80 | */ | ||
81 | enum CadetChannelState | ||
82 | { | ||
83 | /** | ||
84 | * Uninitialized status, should never appear in operation. | ||
85 | */ | ||
86 | CADET_CHANNEL_NEW, | ||
87 | |||
88 | /** | ||
89 | * Channel is to a port that is not open, we're waiting for the | ||
90 | * port to be opened. | ||
91 | */ | ||
92 | CADET_CHANNEL_LOOSE, | ||
93 | |||
94 | /** | ||
95 | * CHANNEL_OPEN message sent, waiting for CHANNEL_OPEN_ACK. | ||
96 | */ | ||
97 | CADET_CHANNEL_OPEN_SENT, | ||
98 | |||
99 | /** | ||
100 | * Connection confirmed, ready to carry traffic. | ||
101 | */ | ||
102 | CADET_CHANNEL_READY | ||
103 | }; | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Info needed to retry a message in case it gets lost. | ||
108 | * Note that we DO use this structure also for unreliable | ||
109 | * messages. | ||
110 | */ | ||
111 | struct CadetReliableMessage | ||
112 | { | ||
113 | /** | ||
114 | * Double linked list, FIFO style | ||
115 | */ | ||
116 | struct CadetReliableMessage *next; | ||
117 | |||
118 | /** | ||
119 | * Double linked list, FIFO style | ||
120 | */ | ||
121 | struct CadetReliableMessage *prev; | ||
122 | |||
123 | /** | ||
124 | * Which channel is this message in? | ||
125 | */ | ||
126 | struct CadetChannel *ch; | ||
127 | |||
128 | /** | ||
129 | * Entry in the tunnels queue for this message, NULL if it has left | ||
130 | * the tunnel. Used to cancel transmission in case we receive an | ||
131 | * ACK in time. | ||
132 | */ | ||
133 | struct CadetTunnelQueueEntry *qe; | ||
134 | |||
135 | /** | ||
136 | * Data message we are trying to send. | ||
137 | */ | ||
138 | struct GNUNET_CADET_ChannelAppDataMessage *data_message; | ||
139 | |||
140 | /** | ||
141 | * How soon should we retry if we fail to get an ACK? | ||
142 | * Messages in the queue are sorted by this value. | ||
143 | */ | ||
144 | struct GNUNET_TIME_Absolute next_retry; | ||
145 | |||
146 | /** | ||
147 | * How long do we wait for an ACK after transmission? | ||
148 | * Use for the back-off calculation. | ||
149 | */ | ||
150 | struct GNUNET_TIME_Relative retry_delay; | ||
151 | |||
152 | /** | ||
153 | * Time when we first successfully transmitted the message | ||
154 | * (that is, set @e num_transmissions to 1). | ||
155 | */ | ||
156 | struct GNUNET_TIME_Absolute first_transmission_time; | ||
157 | |||
158 | /** | ||
159 | * Identifier of the connection that this message took when it | ||
160 | * was first transmitted. Only useful if @e num_transmissions is 1. | ||
161 | */ | ||
162 | struct GNUNET_CADET_ConnectionTunnelIdentifier connection_taken; | ||
163 | |||
164 | /** | ||
165 | * How often was this message transmitted? #GNUNET_SYSERR if there | ||
166 | * was an error transmitting the message, #GNUNET_NO if it was not | ||
167 | * yet transmitted ever, otherwise the number of (re) transmissions. | ||
168 | */ | ||
169 | int num_transmissions; | ||
170 | }; | ||
171 | |||
172 | |||
173 | /** | ||
174 | * List of received out-of-order data messages. | ||
175 | */ | ||
176 | struct CadetOutOfOrderMessage | ||
177 | { | ||
178 | /** | ||
179 | * Double linked list, FIFO style | ||
180 | */ | ||
181 | struct CadetOutOfOrderMessage *next; | ||
182 | |||
183 | /** | ||
184 | * Double linked list, FIFO style | ||
185 | */ | ||
186 | struct CadetOutOfOrderMessage *prev; | ||
187 | |||
188 | /** | ||
189 | * ID of the message (messages up to this point needed | ||
190 | * before we give this one to the client). | ||
191 | */ | ||
192 | struct ChannelMessageIdentifier mid; | ||
193 | |||
194 | /** | ||
195 | * The envelope with the payload of the out-of-order message | ||
196 | */ | ||
197 | struct GNUNET_MQ_Envelope *env; | ||
198 | }; | ||
199 | |||
200 | |||
201 | /** | ||
202 | * Client endpoint of a `struct CadetChannel`. A channel may be a | ||
203 | * loopback channel, in which case it has two of these endpoints. | ||
204 | * Note that flow control also is required in both directions. | ||
205 | */ | ||
206 | struct CadetChannelClient | ||
207 | { | ||
208 | /** | ||
209 | * Client handle. Not by itself sufficient to designate | ||
210 | * the client endpoint, as the same client handle may | ||
211 | * be used for both the owner and the destination, and | ||
212 | * we thus also need the channel ID to identify the client. | ||
213 | */ | ||
214 | struct CadetClient *c; | ||
215 | |||
216 | /** | ||
217 | * Head of DLL of messages received out of order or while client was unready. | ||
218 | */ | ||
219 | struct CadetOutOfOrderMessage *head_recv; | ||
220 | |||
221 | /** | ||
222 | * Tail DLL of messages received out of order or while client was unready. | ||
223 | */ | ||
224 | struct CadetOutOfOrderMessage *tail_recv; | ||
225 | |||
226 | /** | ||
227 | * Local tunnel number for this client. | ||
228 | * (if owner >= #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI, | ||
229 | * otherwise < #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) | ||
230 | */ | ||
231 | struct GNUNET_CADET_ClientChannelNumber ccn; | ||
232 | |||
233 | /** | ||
234 | * Number of entries currently in @a head_recv DLL. | ||
235 | */ | ||
236 | unsigned int num_recv; | ||
237 | |||
238 | /** | ||
239 | * Can we send data to the client? | ||
240 | */ | ||
241 | int client_ready; | ||
242 | }; | ||
243 | |||
244 | |||
245 | /** | ||
246 | * Struct containing all information regarding a channel to a remote client. | ||
247 | */ | ||
248 | struct CadetChannel | ||
249 | { | ||
250 | /** | ||
251 | * Tunnel this channel is in. | ||
252 | */ | ||
253 | struct CadetTunnel *t; | ||
254 | |||
255 | /** | ||
256 | * Client owner of the tunnel, if any. | ||
257 | * (Used if this channel represends the initiating end of the tunnel.) | ||
258 | */ | ||
259 | struct CadetChannelClient *owner; | ||
260 | |||
261 | /** | ||
262 | * Client destination of the tunnel, if any. | ||
263 | * (Used if this channel represents the listening end of the tunnel.) | ||
264 | */ | ||
265 | struct CadetChannelClient *dest; | ||
266 | |||
267 | /** | ||
268 | * Last entry in the tunnel's queue relating to control messages | ||
269 | * (#GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN or | ||
270 | * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK). Used to cancel | ||
271 | * transmission in case we receive updated information. | ||
272 | */ | ||
273 | struct CadetTunnelQueueEntry *last_control_qe; | ||
274 | |||
275 | /** | ||
276 | * Head of DLL of messages sent and not yet ACK'd. | ||
277 | */ | ||
278 | struct CadetReliableMessage *head_sent; | ||
279 | |||
280 | /** | ||
281 | * Tail of DLL of messages sent and not yet ACK'd. | ||
282 | */ | ||
283 | struct CadetReliableMessage *tail_sent; | ||
284 | |||
285 | /** | ||
286 | * Task to resend/poll in case no ACK is received. | ||
287 | */ | ||
288 | struct GNUNET_SCHEDULER_Task *retry_control_task; | ||
289 | |||
290 | /** | ||
291 | * Task to resend/poll in case no ACK is received. | ||
292 | */ | ||
293 | struct GNUNET_SCHEDULER_Task *retry_data_task; | ||
294 | |||
295 | /** | ||
296 | * Last time the channel was used | ||
297 | */ | ||
298 | struct GNUNET_TIME_Absolute timestamp; | ||
299 | |||
300 | /** | ||
301 | * Destination port of the channel. | ||
302 | */ | ||
303 | struct GNUNET_HashCode port; | ||
304 | |||
305 | /** | ||
306 | * Hash'ed port of the channel with initiator and destination PID. | ||
307 | */ | ||
308 | struct GNUNET_HashCode h_port; | ||
309 | |||
310 | /** | ||
311 | * Counter for exponential backoff. | ||
312 | */ | ||
313 | struct GNUNET_TIME_Relative retry_time; | ||
314 | |||
315 | /** | ||
316 | * Bitfield of already-received messages past @e mid_recv. | ||
317 | */ | ||
318 | uint64_t mid_futures; | ||
319 | |||
320 | /** | ||
321 | * Next MID expected for incoming traffic. | ||
322 | */ | ||
323 | struct ChannelMessageIdentifier mid_recv; | ||
324 | |||
325 | /** | ||
326 | * Next MID to use for outgoing traffic. | ||
327 | */ | ||
328 | struct ChannelMessageIdentifier mid_send; | ||
329 | |||
330 | /** | ||
331 | * Total (reliable) messages pending ACK for this channel. | ||
332 | */ | ||
333 | unsigned int pending_messages; | ||
334 | |||
335 | /** | ||
336 | * Maximum (reliable) messages pending ACK for this channel | ||
337 | * before we throttle the client. | ||
338 | */ | ||
339 | unsigned int max_pending_messages; | ||
340 | |||
341 | /** | ||
342 | * Number identifying this channel in its tunnel. | ||
343 | */ | ||
344 | struct GNUNET_CADET_ChannelTunnelNumber ctn; | ||
345 | |||
346 | /** | ||
347 | * Channel state. | ||
348 | */ | ||
349 | enum CadetChannelState state; | ||
350 | |||
351 | /** | ||
352 | * Count how many ACKs we skipped, used to prevent long | ||
353 | * sequences of ACK skipping. | ||
354 | */ | ||
355 | unsigned int skip_ack_series; | ||
356 | |||
357 | /** | ||
358 | * Is the tunnel bufferless (minimum latency)? | ||
359 | */ | ||
360 | int nobuffer; | ||
361 | |||
362 | /** | ||
363 | * Is the tunnel reliable? | ||
364 | */ | ||
365 | int reliable; | ||
366 | |||
367 | /** | ||
368 | * Is the tunnel out-of-order? | ||
369 | */ | ||
370 | int out_of_order; | ||
371 | |||
372 | /** | ||
373 | * Is this channel a loopback channel, where the destination is us again? | ||
374 | */ | ||
375 | int is_loopback; | ||
376 | |||
377 | /** | ||
378 | * Flag to signal the destruction of the channel. If this is set to | ||
379 | * #GNUNET_YES the channel will be destroyed once the queue is | ||
380 | * empty. | ||
381 | */ | ||
382 | int destroy; | ||
383 | |||
384 | /** | ||
385 | * Type of message to be dropped. See GCT_send. | ||
386 | */ | ||
387 | uint16_t type GNUNET_PACKED; | ||
388 | |||
389 | }; | ||
390 | |||
391 | /** | ||
392 | * Assign type of message to drop. | ||
393 | * @param ch CadetChannel to assign type to drop. | ||
394 | * @param message GNUNET_CADET_RequestDropCadetMessage to get the type from. | ||
395 | */ | ||
396 | void | ||
397 | GCCH_assign_type_to_drop (struct CadetChannel *ch, const struct | ||
398 | GNUNET_CADET_RequestDropCadetMessage *message) | ||
399 | { | ||
400 | |||
401 | ch->type = message->type; | ||
402 | |||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Check if type of message is the one to drop. | ||
408 | * @param ch CadetChannel to check for message type to drop. | ||
409 | * @param message GNUNET_MessageHeader to compare the type with. | ||
410 | */ | ||
411 | int | ||
412 | GCCH_is_type_to_drop (struct CadetChannel *ch, const struct | ||
413 | GNUNET_MessageHeader *message) | ||
414 | { | ||
415 | |||
416 | if (ch->type == message->type) | ||
417 | { | ||
418 | ch->type = 0; | ||
419 | return GNUNET_YES; | ||
420 | } | ||
421 | else | ||
422 | return GNUNET_NO; | ||
423 | } | ||
424 | |||
425 | |||
426 | /** | ||
427 | * Get the static string for identification of the channel. | ||
428 | * | ||
429 | * @param ch Channel. | ||
430 | * | ||
431 | * @return Static string with the channel IDs. | ||
432 | */ | ||
433 | const char * | ||
434 | GCCH_2s (const struct CadetChannel *ch) | ||
435 | { | ||
436 | static char buf[128]; | ||
437 | |||
438 | GNUNET_snprintf (buf, | ||
439 | sizeof(buf), | ||
440 | "Channel %s:%s ctn:%X(%X/%X)", | ||
441 | (GNUNET_YES == ch->is_loopback) | ||
442 | ? "loopback" | ||
443 | : GNUNET_i2s (GCP_get_id (GCT_get_destination (ch->t))), | ||
444 | GNUNET_h2s (&ch->port), | ||
445 | ch->ctn.cn, | ||
446 | (NULL == ch->owner) | ||
447 | ? 0 | ||
448 | : ntohl (ch->owner->ccn.channel_of_client), | ||
449 | (NULL == ch->dest) | ||
450 | ? 0 | ||
451 | : ntohl (ch->dest->ccn.channel_of_client)); | ||
452 | return buf; | ||
453 | } | ||
454 | |||
455 | |||
456 | /** | ||
457 | * Hash the @a port and @a initiator and @a listener to | ||
458 | * calculate the "challenge" @a h_port we send to the other | ||
459 | * peer on #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN. | ||
460 | * | ||
461 | * @param[out] h_port set to the hash of @a port, @a initiator and @a listener | ||
462 | * @param port cadet port, as seen by CADET clients | ||
463 | * @param listener peer that is listining on @a port | ||
464 | */ | ||
465 | void | ||
466 | GCCH_hash_port (struct GNUNET_HashCode *h_port, | ||
467 | const struct GNUNET_HashCode *port, | ||
468 | const struct GNUNET_PeerIdentity *listener) | ||
469 | { | ||
470 | struct GNUNET_HashContext *hc; | ||
471 | |||
472 | hc = GNUNET_CRYPTO_hash_context_start (); | ||
473 | GNUNET_CRYPTO_hash_context_read (hc, port, sizeof(*port)); | ||
474 | GNUNET_CRYPTO_hash_context_read (hc, listener, sizeof(*listener)); | ||
475 | GNUNET_CRYPTO_hash_context_finish (hc, h_port); | ||
476 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
477 | "Calculated port hash %s\n", | ||
478 | GNUNET_h2s (h_port)); | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * Get the channel's public ID. | ||
484 | * | ||
485 | * @param ch Channel. | ||
486 | * | ||
487 | * @return ID used to identify the channel with the remote peer. | ||
488 | */ | ||
489 | struct GNUNET_CADET_ChannelTunnelNumber | ||
490 | GCCH_get_id (const struct CadetChannel *ch) | ||
491 | { | ||
492 | return ch->ctn; | ||
493 | } | ||
494 | |||
495 | |||
496 | /** | ||
497 | * Release memory associated with @a ccc | ||
498 | * | ||
499 | * @param ccc data structure to clean up | ||
500 | */ | ||
501 | static void | ||
502 | free_channel_client (struct CadetChannelClient *ccc) | ||
503 | { | ||
504 | struct CadetOutOfOrderMessage *com; | ||
505 | |||
506 | while (NULL != (com = ccc->head_recv)) | ||
507 | { | ||
508 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, com); | ||
509 | ccc->num_recv--; | ||
510 | GNUNET_MQ_discard (com->env); | ||
511 | GNUNET_free (com); | ||
512 | } | ||
513 | GNUNET_free (ccc); | ||
514 | } | ||
515 | |||
516 | |||
517 | /** | ||
518 | * Destroy the given channel. | ||
519 | * | ||
520 | * @param ch channel to destroy | ||
521 | */ | ||
522 | static void | ||
523 | channel_destroy (struct CadetChannel *ch) | ||
524 | { | ||
525 | struct CadetReliableMessage *crm; | ||
526 | |||
527 | while (NULL != (crm = ch->head_sent)) | ||
528 | { | ||
529 | GNUNET_assert (ch == crm->ch); | ||
530 | if (NULL != crm->qe) | ||
531 | { | ||
532 | GCT_send_cancel (crm->qe); | ||
533 | crm->qe = NULL; | ||
534 | } | ||
535 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, ch->tail_sent, crm); | ||
536 | GNUNET_free (crm->data_message); | ||
537 | GNUNET_free (crm); | ||
538 | } | ||
539 | if (CADET_CHANNEL_LOOSE == ch->state) | ||
540 | { | ||
541 | GSC_drop_loose_channel (&ch->h_port, ch); | ||
542 | } | ||
543 | if (NULL != ch->owner) | ||
544 | { | ||
545 | free_channel_client (ch->owner); | ||
546 | ch->owner = NULL; | ||
547 | } | ||
548 | if (NULL != ch->dest) | ||
549 | { | ||
550 | free_channel_client (ch->dest); | ||
551 | ch->dest = NULL; | ||
552 | } | ||
553 | if (NULL != ch->last_control_qe) | ||
554 | { | ||
555 | GCT_send_cancel (ch->last_control_qe); | ||
556 | ch->last_control_qe = NULL; | ||
557 | } | ||
558 | if (NULL != ch->retry_data_task) | ||
559 | { | ||
560 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | ||
561 | ch->retry_data_task = NULL; | ||
562 | } | ||
563 | if (NULL != ch->retry_control_task) | ||
564 | { | ||
565 | GNUNET_SCHEDULER_cancel (ch->retry_control_task); | ||
566 | ch->retry_control_task = NULL; | ||
567 | } | ||
568 | if (GNUNET_NO == ch->is_loopback) | ||
569 | { | ||
570 | GCT_remove_channel (ch->t, ch, ch->ctn); | ||
571 | ch->t = NULL; | ||
572 | } | ||
573 | GNUNET_free (ch); | ||
574 | } | ||
575 | |||
576 | |||
577 | /** | ||
578 | * Send a channel create message. | ||
579 | * | ||
580 | * @param cls Channel for which to send. | ||
581 | */ | ||
582 | static void | ||
583 | send_channel_open (void *cls); | ||
584 | |||
585 | |||
586 | /** | ||
587 | * Function called once the tunnel confirms that we sent the | ||
588 | * create message. Delays for a bit until we retry. | ||
589 | * | ||
590 | * @param cls our `struct CadetChannel`. | ||
591 | * @param cid identifier of the connection within the tunnel, NULL | ||
592 | * if transmission failed | ||
593 | */ | ||
594 | static void | ||
595 | channel_open_sent_cb (void *cls, | ||
596 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) | ||
597 | { | ||
598 | struct CadetChannel *ch = cls; | ||
599 | |||
600 | GNUNET_assert (NULL != ch->last_control_qe); | ||
601 | ch->last_control_qe = NULL; | ||
602 | ch->retry_time = GNUNET_TIME_STD_BACKOFF (ch->retry_time); | ||
603 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
604 | "Sent CADET_CHANNEL_OPEN on %s, retrying in %s\n", | ||
605 | GCCH_2s (ch), | ||
606 | GNUNET_STRINGS_relative_time_to_string (ch->retry_time, GNUNET_YES)); | ||
607 | ch->retry_control_task = | ||
608 | GNUNET_SCHEDULER_add_delayed (ch->retry_time, &send_channel_open, ch); | ||
609 | } | ||
610 | |||
611 | |||
612 | /** | ||
613 | * Send a channel open message. | ||
614 | * | ||
615 | * @param cls Channel for which to send. | ||
616 | */ | ||
617 | static void | ||
618 | send_channel_open (void *cls) | ||
619 | { | ||
620 | struct CadetChannel *ch = cls; | ||
621 | struct GNUNET_CADET_ChannelOpenMessage msgcc; | ||
622 | |||
623 | ch->retry_control_task = NULL; | ||
624 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
625 | "Sending CHANNEL_OPEN message for %s\n", | ||
626 | GCCH_2s (ch)); | ||
627 | msgcc.header.size = htons (sizeof(msgcc)); | ||
628 | msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN); | ||
629 | // TODO This will be removed in a major release, because this will be a protocol breaking change. We set the deprecated "reliable" bit here that was removed. | ||
630 | msgcc.opt = 2; | ||
631 | msgcc.h_port = ch->h_port; | ||
632 | msgcc.ctn = ch->ctn; | ||
633 | ch->state = CADET_CHANNEL_OPEN_SENT; | ||
634 | if (NULL != ch->last_control_qe) | ||
635 | GCT_send_cancel (ch->last_control_qe); | ||
636 | ch->last_control_qe = | ||
637 | GCT_send (ch->t, &msgcc.header, &channel_open_sent_cb, ch, &msgcc.ctn); | ||
638 | GNUNET_assert (NULL == ch->retry_control_task); | ||
639 | } | ||
640 | |||
641 | |||
642 | /** | ||
643 | * Function called once and only once after a channel was bound | ||
644 | * to its tunnel via #GCT_add_channel() is ready for transmission. | ||
645 | * Note that this is only the case for channels that this peer | ||
646 | * initiates, as for incoming channels we assume that they are | ||
647 | * ready for transmission immediately upon receiving the open | ||
648 | * message. Used to bootstrap the #GCT_send() process. | ||
649 | * | ||
650 | * @param ch the channel for which the tunnel is now ready | ||
651 | */ | ||
652 | void | ||
653 | GCCH_tunnel_up (struct CadetChannel *ch) | ||
654 | { | ||
655 | GNUNET_assert (NULL == ch->retry_control_task); | ||
656 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
657 | "Tunnel up, sending CHANNEL_OPEN on %s now\n", | ||
658 | GCCH_2s (ch)); | ||
659 | ch->retry_control_task = GNUNET_SCHEDULER_add_now (&send_channel_open, ch); | ||
660 | } | ||
661 | |||
662 | |||
663 | /** | ||
664 | * Create a new channel. | ||
665 | * | ||
666 | * @param owner local client owning the channel | ||
667 | * @param ccn local number of this channel at the @a owner | ||
668 | * @param destination peer to which we should build the channel | ||
669 | * @param port desired port at @a destination | ||
670 | * @param options options for the channel | ||
671 | * @return handle to the new channel | ||
672 | */ | ||
673 | struct CadetChannel * | ||
674 | GCCH_channel_local_new (struct CadetClient *owner, | ||
675 | struct GNUNET_CADET_ClientChannelNumber ccn, | ||
676 | struct CadetPeer *destination, | ||
677 | const struct GNUNET_HashCode *port, | ||
678 | uint32_t options) | ||
679 | { | ||
680 | struct CadetChannel *ch; | ||
681 | struct CadetChannelClient *ccco; | ||
682 | |||
683 | ccco = GNUNET_new (struct CadetChannelClient); | ||
684 | ccco->c = owner; | ||
685 | ccco->ccn = ccn; | ||
686 | ccco->client_ready = GNUNET_YES; | ||
687 | |||
688 | ch = GNUNET_new (struct CadetChannel); | ||
689 | ch->mid_recv.mid = htonl (1); /* The OPEN_ACK counts as message 0! */ | ||
690 | ch->nobuffer = GNUNET_NO; | ||
691 | ch->reliable = GNUNET_YES; | ||
692 | ch->out_of_order = GNUNET_NO; | ||
693 | ch->max_pending_messages = | ||
694 | (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ | ||
695 | ch->owner = ccco; | ||
696 | ch->port = *port; | ||
697 | GCCH_hash_port (&ch->h_port, port, GCP_get_id (destination)); | ||
698 | if (0 == GNUNET_memcmp (&my_full_id, GCP_get_id (destination))) | ||
699 | { | ||
700 | struct OpenPort *op; | ||
701 | |||
702 | ch->is_loopback = GNUNET_YES; | ||
703 | op = GNUNET_CONTAINER_multihashmap_get (open_ports, &ch->h_port); | ||
704 | if (NULL == op) | ||
705 | { | ||
706 | /* port closed, wait for it to possibly open */ | ||
707 | ch->state = CADET_CHANNEL_LOOSE; | ||
708 | (void) GNUNET_CONTAINER_multihashmap_put ( | ||
709 | loose_channels, | ||
710 | &ch->h_port, | ||
711 | ch, | ||
712 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
713 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
714 | "Created loose incoming loopback channel to port %s\n", | ||
715 | GNUNET_h2s (&ch->port)); | ||
716 | } | ||
717 | else | ||
718 | { | ||
719 | GCCH_bind (ch, op->c, &op->port); | ||
720 | } | ||
721 | } | ||
722 | else | ||
723 | { | ||
724 | ch->t = GCP_get_tunnel (destination, GNUNET_YES); | ||
725 | ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; | ||
726 | ch->ctn = GCT_add_channel (ch->t, ch); | ||
727 | } | ||
728 | GNUNET_STATISTICS_update (stats, "# channels", 1, GNUNET_NO); | ||
729 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
730 | "Created channel to port %s at peer %s for %s using %s\n", | ||
731 | GNUNET_h2s (port), | ||
732 | GCP_2s (destination), | ||
733 | GSC_2s (owner), | ||
734 | (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t)); | ||
735 | return ch; | ||
736 | } | ||
737 | |||
738 | |||
739 | /** | ||
740 | * We had an incoming channel to a port that is closed. | ||
741 | * It has not been opened for a while, drop it. | ||
742 | * | ||
743 | * @param cls the channel to drop | ||
744 | */ | ||
745 | static void | ||
746 | timeout_closed_cb (void *cls) | ||
747 | { | ||
748 | struct CadetChannel *ch = cls; | ||
749 | |||
750 | ch->retry_control_task = NULL; | ||
751 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
752 | "Closing incoming channel to port %s from peer %s due to timeout\n", | ||
753 | GNUNET_h2s (&ch->port), | ||
754 | GCP_2s (GCT_get_destination (ch->t))); | ||
755 | channel_destroy (ch); | ||
756 | } | ||
757 | |||
758 | |||
759 | /** | ||
760 | * Create a new channel based on a request coming in over the network. | ||
761 | * | ||
762 | * @param t tunnel to the remote peer | ||
763 | * @param ctn identifier of this channel in the tunnel | ||
764 | * @param h_port desired hash of local port | ||
765 | * @param options options for the channel | ||
766 | * @return handle to the new channel | ||
767 | */ | ||
768 | struct CadetChannel * | ||
769 | GCCH_channel_incoming_new (struct CadetTunnel *t, | ||
770 | struct GNUNET_CADET_ChannelTunnelNumber ctn, | ||
771 | const struct GNUNET_HashCode *h_port, | ||
772 | uint32_t options) | ||
773 | { | ||
774 | struct CadetChannel *ch; | ||
775 | struct OpenPort *op; | ||
776 | |||
777 | ch = GNUNET_new (struct CadetChannel); | ||
778 | ch->h_port = *h_port; | ||
779 | ch->t = t; | ||
780 | ch->ctn = ctn; | ||
781 | ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; | ||
782 | ch->nobuffer = GNUNET_NO; | ||
783 | ch->reliable = GNUNET_YES; | ||
784 | ch->out_of_order = GNUNET_NO; | ||
785 | ch->max_pending_messages = | ||
786 | (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ | ||
787 | GNUNET_STATISTICS_update (stats, "# channels", 1, GNUNET_NO); | ||
788 | |||
789 | op = GNUNET_CONTAINER_multihashmap_get (open_ports, h_port); | ||
790 | if (NULL == op) | ||
791 | { | ||
792 | /* port closed, wait for it to possibly open */ | ||
793 | ch->state = CADET_CHANNEL_LOOSE; | ||
794 | (void) GNUNET_CONTAINER_multihashmap_put ( | ||
795 | loose_channels, | ||
796 | &ch->h_port, | ||
797 | ch, | ||
798 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
799 | GNUNET_assert (NULL == ch->retry_control_task); | ||
800 | ch->retry_control_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT_CLOSED_PORT, | ||
801 | &timeout_closed_cb, | ||
802 | ch); | ||
803 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
804 | "Created loose incoming channel to port %s from peer %s\n", | ||
805 | GNUNET_h2s (&ch->port), | ||
806 | GCP_2s (GCT_get_destination (ch->t))); | ||
807 | } | ||
808 | else | ||
809 | { | ||
810 | GCCH_bind (ch, op->c, &op->port); | ||
811 | } | ||
812 | GNUNET_STATISTICS_update (stats, "# channels", 1, GNUNET_NO); | ||
813 | return ch; | ||
814 | } | ||
815 | |||
816 | |||
817 | /** | ||
818 | * Function called once the tunnel confirms that we sent the | ||
819 | * ACK message. Just remembers it was sent, we do not expect | ||
820 | * ACKs for ACKs ;-). | ||
821 | * | ||
822 | * @param cls our `struct CadetChannel`. | ||
823 | * @param cid identifier of the connection within the tunnel, NULL | ||
824 | * if transmission failed | ||
825 | */ | ||
826 | static void | ||
827 | send_ack_cb (void *cls, | ||
828 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) | ||
829 | { | ||
830 | struct CadetChannel *ch = cls; | ||
831 | |||
832 | GNUNET_assert (NULL != ch->last_control_qe); | ||
833 | ch->last_control_qe = NULL; | ||
834 | } | ||
835 | |||
836 | |||
837 | /** | ||
838 | * Compute and send the current #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK to the other peer. | ||
839 | * | ||
840 | * @param ch channel to send the #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK for | ||
841 | */ | ||
842 | static void | ||
843 | send_channel_data_ack (struct CadetChannel *ch) | ||
844 | { | ||
845 | struct GNUNET_CADET_ChannelDataAckMessage msg; | ||
846 | |||
847 | if (GNUNET_NO == ch->reliable) | ||
848 | return; /* no ACKs */ | ||
849 | msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK); | ||
850 | msg.header.size = htons (sizeof(msg)); | ||
851 | msg.ctn = ch->ctn; | ||
852 | msg.mid.mid = htonl (ntohl (ch->mid_recv.mid)); | ||
853 | msg.futures = GNUNET_htonll (ch->mid_futures); | ||
854 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
855 | "Sending DATA_ACK %u:%llX via %s\n", | ||
856 | (unsigned int) ntohl (msg.mid.mid), | ||
857 | (unsigned long long) ch->mid_futures, | ||
858 | GCCH_2s (ch)); | ||
859 | if (NULL != ch->last_control_qe) | ||
860 | GCT_send_cancel (ch->last_control_qe); | ||
861 | ch->last_control_qe = GCT_send (ch->t, &msg.header, &send_ack_cb, ch, | ||
862 | &msg.ctn); | ||
863 | } | ||
864 | |||
865 | |||
866 | /** | ||
867 | * Send our initial #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK to the client confirming that the | ||
868 | * connection is up. | ||
869 | * | ||
870 | * @param cls the `struct CadetChannel` | ||
871 | */ | ||
872 | static void | ||
873 | send_open_ack (void *cls) | ||
874 | { | ||
875 | struct CadetChannel *ch = cls; | ||
876 | struct GNUNET_CADET_ChannelOpenAckMessage msg; | ||
877 | |||
878 | ch->retry_control_task = NULL; | ||
879 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
880 | "Sending CHANNEL_OPEN_ACK on %s\n", | ||
881 | GCCH_2s (ch)); | ||
882 | msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK); | ||
883 | msg.header.size = htons (sizeof(msg)); | ||
884 | msg.reserved = htonl (0); | ||
885 | msg.ctn = ch->ctn; | ||
886 | msg.port = ch->port; | ||
887 | if (NULL != ch->last_control_qe) | ||
888 | GCT_send_cancel (ch->last_control_qe); | ||
889 | ch->last_control_qe = GCT_send (ch->t, &msg.header, &send_ack_cb, ch, | ||
890 | &msg.ctn); | ||
891 | } | ||
892 | |||
893 | |||
894 | /** | ||
895 | * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for | ||
896 | * this channel. If the binding was successful, (re)transmit the | ||
897 | * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. | ||
898 | * | ||
899 | * @param ch channel that got the duplicate open | ||
900 | * @param cti identifier of the connection that delivered the message | ||
901 | */ | ||
902 | void | ||
903 | GCCH_handle_duplicate_open ( | ||
904 | struct CadetChannel *ch, | ||
905 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) | ||
906 | { | ||
907 | if (NULL == ch->dest) | ||
908 | { | ||
909 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
910 | "Ignoring duplicate CHANNEL_OPEN on %s: port is closed\n", | ||
911 | GCCH_2s (ch)); | ||
912 | return; | ||
913 | } | ||
914 | if (NULL != ch->retry_control_task) | ||
915 | { | ||
916 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
917 | "Ignoring duplicate CHANNEL_OPEN on %s: control message is pending\n", | ||
918 | GCCH_2s (ch)); | ||
919 | return; | ||
920 | } | ||
921 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
922 | "Retransmitting CHANNEL_OPEN_ACK on %s\n", | ||
923 | GCCH_2s (ch)); | ||
924 | ch->retry_control_task = GNUNET_SCHEDULER_add_now (&send_open_ack, ch); | ||
925 | } | ||
926 | |||
927 | |||
928 | /** | ||
929 | * Send a #GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK to the client to solicit more messages. | ||
930 | * | ||
931 | * @param ch channel the ack is for | ||
932 | * @param to_owner #GNUNET_YES to send to owner, | ||
933 | * #GNUNET_NO to send to dest | ||
934 | */ | ||
935 | static void | ||
936 | send_ack_to_client (struct CadetChannel *ch, int to_owner) | ||
937 | { | ||
938 | struct GNUNET_MQ_Envelope *env; | ||
939 | struct GNUNET_CADET_LocalAck *ack; | ||
940 | struct CadetChannelClient *ccc; | ||
941 | |||
942 | ccc = (GNUNET_YES == to_owner) ? ch->owner : ch->dest; | ||
943 | if (NULL == ccc) | ||
944 | { | ||
945 | /* This can happen if we are just getting ACKs after | ||
946 | our local client already disconnected. */ | ||
947 | GNUNET_assert (GNUNET_YES == ch->destroy); | ||
948 | return; | ||
949 | } | ||
950 | env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK); | ||
951 | ack->ccn = ccc->ccn; | ||
952 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
953 | "Sending CADET_LOCAL_ACK to %s (%s) at ccn %X (%u/%u pending)\n", | ||
954 | GSC_2s (ccc->c), | ||
955 | (GNUNET_YES == to_owner) ? "owner" : "dest", | ||
956 | ntohl (ack->ccn.channel_of_client), | ||
957 | ch->pending_messages, | ||
958 | ch->max_pending_messages); | ||
959 | GSC_send_to_client (ccc->c, env); | ||
960 | } | ||
961 | |||
962 | |||
963 | /** | ||
964 | * A client is bound to the port that we have a channel | ||
965 | * open to. Send the acknowledgement for the connection | ||
966 | * request and establish the link with the client. | ||
967 | * | ||
968 | * @param ch open incoming channel | ||
969 | * @param c client listening on the respective @a port | ||
970 | * @param port the port @a is listening on | ||
971 | */ | ||
972 | void | ||
973 | GCCH_bind (struct CadetChannel *ch, | ||
974 | struct CadetClient *c, | ||
975 | const struct GNUNET_HashCode *port) | ||
976 | { | ||
977 | uint32_t options; | ||
978 | struct CadetChannelClient *cccd; | ||
979 | |||
980 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
981 | "Binding %s from %s to port %s of %s\n", | ||
982 | GCCH_2s (ch), | ||
983 | GCT_2s (ch->t), | ||
984 | GNUNET_h2s (&ch->port), | ||
985 | GSC_2s (c)); | ||
986 | if (NULL != ch->retry_control_task) | ||
987 | { | ||
988 | /* there might be a timeout task here */ | ||
989 | GNUNET_SCHEDULER_cancel (ch->retry_control_task); | ||
990 | ch->retry_control_task = NULL; | ||
991 | } | ||
992 | options = 0; | ||
993 | cccd = GNUNET_new (struct CadetChannelClient); | ||
994 | GNUNET_assert (NULL == ch->dest); | ||
995 | ch->dest = cccd; | ||
996 | ch->port = *port; | ||
997 | cccd->c = c; | ||
998 | cccd->client_ready = GNUNET_YES; | ||
999 | cccd->ccn = GSC_bind (c, | ||
1000 | ch, | ||
1001 | (GNUNET_YES == ch->is_loopback) | ||
1002 | ? GCP_get (&my_full_id, GNUNET_YES) | ||
1003 | : GCT_get_destination (ch->t), | ||
1004 | port, | ||
1005 | options); | ||
1006 | GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < | ||
1007 | GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); | ||
1008 | ch->mid_recv.mid = htonl (1); /* The OPEN counts as message 0! */ | ||
1009 | if (GNUNET_YES == ch->is_loopback) | ||
1010 | { | ||
1011 | ch->state = CADET_CHANNEL_OPEN_SENT; | ||
1012 | GCCH_handle_channel_open_ack (ch, NULL, port); | ||
1013 | } | ||
1014 | else | ||
1015 | { | ||
1016 | /* notify other peer that we accepted the connection */ | ||
1017 | ch->state = CADET_CHANNEL_READY; | ||
1018 | ch->retry_control_task = GNUNET_SCHEDULER_add_now (&send_open_ack, ch); | ||
1019 | } | ||
1020 | /* give client it's initial supply of ACKs */ | ||
1021 | GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < | ||
1022 | GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); | ||
1023 | for (unsigned int i = 0; i < ch->max_pending_messages; i++) | ||
1024 | send_ack_to_client (ch, GNUNET_NO); | ||
1025 | } | ||
1026 | |||
1027 | |||
1028 | /** | ||
1029 | * One of our clients has disconnected, tell the other one that we | ||
1030 | * are finished. Done asynchronously to avoid concurrent modification | ||
1031 | * issues if this is the same client. | ||
1032 | * | ||
1033 | * @param cls the `struct CadetChannel` where one of the ends is now dead | ||
1034 | */ | ||
1035 | static void | ||
1036 | signal_remote_destroy_cb (void *cls) | ||
1037 | { | ||
1038 | struct CadetChannel *ch = cls; | ||
1039 | struct CadetChannelClient *ccc; | ||
1040 | |||
1041 | /* Find which end is left... */ | ||
1042 | ch->retry_control_task = NULL; | ||
1043 | ccc = (NULL != ch->owner) ? ch->owner : ch->dest; | ||
1044 | GSC_handle_remote_channel_destroy (ccc->c, ccc->ccn, ch); | ||
1045 | channel_destroy (ch); | ||
1046 | } | ||
1047 | |||
1048 | |||
1049 | /** | ||
1050 | * Destroy locally created channel. Called by the local client, so no | ||
1051 | * need to tell the client. | ||
1052 | * | ||
1053 | * @param ch channel to destroy | ||
1054 | * @param c client that caused the destruction | ||
1055 | * @param ccn client number of the client @a c | ||
1056 | */ | ||
1057 | void | ||
1058 | GCCH_channel_local_destroy (struct CadetChannel *ch, | ||
1059 | struct CadetClient *c, | ||
1060 | struct GNUNET_CADET_ClientChannelNumber ccn) | ||
1061 | { | ||
1062 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1063 | "%s asks for destruction of %s\n", | ||
1064 | GSC_2s (c), | ||
1065 | GCCH_2s (ch)); | ||
1066 | GNUNET_assert (NULL != c); | ||
1067 | if ((NULL != ch->owner) && (c == ch->owner->c) && | ||
1068 | (ccn.channel_of_client == ch->owner->ccn.channel_of_client)) | ||
1069 | { | ||
1070 | free_channel_client (ch->owner); | ||
1071 | ch->owner = NULL; | ||
1072 | } | ||
1073 | else if ((NULL != ch->dest) && (c == ch->dest->c) && | ||
1074 | (ccn.channel_of_client == ch->dest->ccn.channel_of_client)) | ||
1075 | { | ||
1076 | free_channel_client (ch->dest); | ||
1077 | ch->dest = NULL; | ||
1078 | } | ||
1079 | else | ||
1080 | { | ||
1081 | GNUNET_assert (0); | ||
1082 | } | ||
1083 | |||
1084 | if (GNUNET_YES == ch->destroy) | ||
1085 | { | ||
1086 | /* other end already destroyed, with the local client gone, no need | ||
1087 | to finish transmissions, just destroy immediately. */ | ||
1088 | channel_destroy (ch); | ||
1089 | return; | ||
1090 | } | ||
1091 | if ((NULL != ch->head_sent) && ((NULL != ch->owner) || (NULL != ch->dest))) | ||
1092 | { | ||
1093 | /* Wait for other end to destroy us as well, | ||
1094 | and otherwise allow send queue to be transmitted first */ | ||
1095 | ch->destroy = GNUNET_YES; | ||
1096 | return; | ||
1097 | } | ||
1098 | if ((GNUNET_YES == ch->is_loopback) && | ||
1099 | ((NULL != ch->owner) || (NULL != ch->dest))) | ||
1100 | { | ||
1101 | if (NULL != ch->retry_control_task) | ||
1102 | GNUNET_SCHEDULER_cancel (ch->retry_control_task); | ||
1103 | ch->retry_control_task = | ||
1104 | GNUNET_SCHEDULER_add_now (&signal_remote_destroy_cb, ch); | ||
1105 | return; | ||
1106 | } | ||
1107 | if (GNUNET_NO == ch->is_loopback) | ||
1108 | { | ||
1109 | /* If the we ever sent the CHANNEL_CREATE, we need to send a destroy message. */ | ||
1110 | switch (ch->state) | ||
1111 | { | ||
1112 | case CADET_CHANNEL_NEW: | ||
1113 | /* We gave up on a channel that we created as a client to a remote | ||
1114 | target, but that never went anywhere. Nothing to do here. */ | ||
1115 | break; | ||
1116 | |||
1117 | case CADET_CHANNEL_LOOSE: | ||
1118 | break; | ||
1119 | |||
1120 | default: | ||
1121 | GCT_send_channel_destroy (ch->t, ch->ctn); | ||
1122 | } | ||
1123 | } | ||
1124 | /* Nothing left to do, just finish destruction */ | ||
1125 | channel_destroy (ch); | ||
1126 | } | ||
1127 | |||
1128 | |||
1129 | /** | ||
1130 | * We got an acknowledgement for the creation of the channel | ||
1131 | * (the port is open on the other side). Verify that the | ||
1132 | * other end really has the right port, and begin transmissions. | ||
1133 | * | ||
1134 | * @param ch channel to destroy | ||
1135 | * @param cti identifier of the connection that delivered the message | ||
1136 | * @param port port number (needed to verify receiver knows the port) | ||
1137 | */ | ||
1138 | void | ||
1139 | GCCH_handle_channel_open_ack ( | ||
1140 | struct CadetChannel *ch, | ||
1141 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, | ||
1142 | const struct GNUNET_HashCode *port) | ||
1143 | { | ||
1144 | switch (ch->state) | ||
1145 | { | ||
1146 | case CADET_CHANNEL_NEW: | ||
1147 | /* this should be impossible */ | ||
1148 | GNUNET_break (0); | ||
1149 | break; | ||
1150 | |||
1151 | case CADET_CHANNEL_LOOSE: | ||
1152 | /* This makes no sense. */ | ||
1153 | GNUNET_break_op (0); | ||
1154 | break; | ||
1155 | |||
1156 | case CADET_CHANNEL_OPEN_SENT: | ||
1157 | if (NULL == ch->owner) | ||
1158 | { | ||
1159 | /* We're not the owner, wrong direction! */ | ||
1160 | GNUNET_break_op (0); | ||
1161 | return; | ||
1162 | } | ||
1163 | if (0 != GNUNET_memcmp (&ch->port, port)) | ||
1164 | { | ||
1165 | /* Other peer failed to provide the right port, | ||
1166 | refuse connection. */ | ||
1167 | GNUNET_break_op (0); | ||
1168 | return; | ||
1169 | } | ||
1170 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1171 | "Received CHANNEL_OPEN_ACK for waiting %s, entering READY state\n", | ||
1172 | GCCH_2s (ch)); | ||
1173 | if (NULL != ch->retry_control_task) /* can be NULL if ch->is_loopback */ | ||
1174 | { | ||
1175 | GNUNET_SCHEDULER_cancel (ch->retry_control_task); | ||
1176 | ch->retry_control_task = NULL; | ||
1177 | } | ||
1178 | ch->state = CADET_CHANNEL_READY; | ||
1179 | /* On first connect, send client as many ACKs as we allow messages | ||
1180 | to be buffered! */ | ||
1181 | for (unsigned int i = 0; i < ch->max_pending_messages; i++) | ||
1182 | send_ack_to_client (ch, GNUNET_YES); | ||
1183 | break; | ||
1184 | |||
1185 | case CADET_CHANNEL_READY: | ||
1186 | /* duplicate ACK, maybe we retried the CREATE. Ignore. */ | ||
1187 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1188 | "Received duplicate channel OPEN_ACK for %s\n", | ||
1189 | GCCH_2s (ch)); | ||
1190 | GNUNET_STATISTICS_update (stats, "# duplicate CREATE_ACKs", 1, GNUNET_NO); | ||
1191 | break; | ||
1192 | } | ||
1193 | } | ||
1194 | |||
1195 | |||
1196 | /** | ||
1197 | * Test if element @a e1 comes before element @a e2. | ||
1198 | * | ||
1199 | * @param cls closure, to a flag where we indicate duplicate packets | ||
1200 | * @param m1 a message of to sort | ||
1201 | * @param m2 another message to sort | ||
1202 | * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO | ||
1203 | */ | ||
1204 | static int | ||
1205 | is_before (void *cls, | ||
1206 | struct CadetOutOfOrderMessage *m1, | ||
1207 | struct CadetOutOfOrderMessage *m2) | ||
1208 | { | ||
1209 | int *duplicate = cls; | ||
1210 | uint32_t v1 = ntohl (m1->mid.mid); | ||
1211 | uint32_t v2 = ntohl (m2->mid.mid); | ||
1212 | uint32_t delta; | ||
1213 | |||
1214 | delta = v2 - v1; | ||
1215 | if (0 == delta) | ||
1216 | *duplicate = GNUNET_YES; | ||
1217 | if (delta > (uint32_t) INT_MAX) | ||
1218 | { | ||
1219 | /* in overflow range, we can safely assume we wrapped around */ | ||
1220 | return GNUNET_NO; | ||
1221 | } | ||
1222 | else | ||
1223 | { | ||
1224 | /* result is small, thus v2 > v1, thus m1 < m2 */ | ||
1225 | return GNUNET_YES; | ||
1226 | } | ||
1227 | } | ||
1228 | |||
1229 | |||
1230 | /** | ||
1231 | * We got payload data for a channel. Pass it on to the client | ||
1232 | * and send an ACK to the other end (once flow control allows it!) | ||
1233 | * | ||
1234 | * @param ch channel that got data | ||
1235 | * @param cti identifier of the connection that delivered the message | ||
1236 | * @param msg message that was received | ||
1237 | */ | ||
1238 | void | ||
1239 | GCCH_handle_channel_plaintext_data ( | ||
1240 | struct CadetChannel *ch, | ||
1241 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, | ||
1242 | const struct GNUNET_CADET_ChannelAppDataMessage *msg) | ||
1243 | { | ||
1244 | struct GNUNET_MQ_Envelope *env; | ||
1245 | struct GNUNET_CADET_LocalData *ld; | ||
1246 | struct CadetChannelClient *ccc; | ||
1247 | size_t payload_size; | ||
1248 | struct CadetOutOfOrderMessage *com; | ||
1249 | int duplicate; | ||
1250 | uint32_t mid_min; | ||
1251 | uint32_t mid_max; | ||
1252 | uint32_t mid_msg; | ||
1253 | uint32_t delta; | ||
1254 | |||
1255 | GNUNET_assert (GNUNET_NO == ch->is_loopback); | ||
1256 | if ((NULL == ch->owner) && (NULL == ch->dest)) | ||
1257 | { | ||
1258 | /* This client is gone, but we still have messages to send to | ||
1259 | the other end (which is why @a ch is not yet dead). However, | ||
1260 | we cannot pass messages to our client anymore. */ | ||
1261 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1262 | "Dropping incoming payload on %s as this end is already closed\n", | ||
1263 | GCCH_2s (ch)); | ||
1264 | /* send back DESTROY notification to stop further retransmissions! */ | ||
1265 | if (GNUNET_YES == ch->destroy) | ||
1266 | GCT_send_channel_destroy (ch->t, ch->ctn); | ||
1267 | return; | ||
1268 | } | ||
1269 | payload_size = ntohs (msg->header.size) - sizeof(*msg); | ||
1270 | env = GNUNET_MQ_msg_extra (ld, | ||
1271 | payload_size, | ||
1272 | GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); | ||
1273 | ld->ccn = (NULL == ch->dest) ? ch->owner->ccn : ch->dest->ccn; | ||
1274 | GNUNET_memcpy (&ld[1], &msg[1], payload_size); | ||
1275 | ccc = (NULL != ch->owner) ? ch->owner : ch->dest; | ||
1276 | if (GNUNET_YES == ccc->client_ready) | ||
1277 | { | ||
1278 | /* | ||
1279 | * We ad-hoc send the message if | ||
1280 | * - The channel is out-of-order | ||
1281 | * - The channel is reliable and MID matches next expected MID | ||
1282 | * - The channel is unreliable and MID is before lowest seen MID | ||
1283 | */if ((GNUNET_YES == ch->out_of_order) || | ||
1284 | ((msg->mid.mid == ch->mid_recv.mid) && (GNUNET_YES == ch->reliable)) || | ||
1285 | ((GNUNET_NO == ch->reliable) && | ||
1286 | (ntohl (msg->mid.mid) >= ntohl (ch->mid_recv.mid)) && | ||
1287 | ((NULL == ccc->head_recv) || | ||
1288 | (ntohl (msg->mid.mid) < ntohl (ccc->head_recv->mid.mid))))) | ||
1289 | { | ||
1290 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1291 | "Giving %u bytes of payload with MID %u from %s to client %s\n", | ||
1292 | (unsigned int) payload_size, | ||
1293 | ntohl (msg->mid.mid), | ||
1294 | GCCH_2s (ch), | ||
1295 | GSC_2s (ccc->c)); | ||
1296 | ccc->client_ready = GNUNET_NO; | ||
1297 | GSC_send_to_client (ccc->c, env); | ||
1298 | if (GNUNET_NO == ch->out_of_order) | ||
1299 | ch->mid_recv.mid = htonl (1 + ntohl (msg->mid.mid)); | ||
1300 | else | ||
1301 | ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid)); | ||
1302 | ch->mid_futures >>= 1; | ||
1303 | if ((GNUNET_YES == ch->out_of_order) && (GNUNET_NO == ch->reliable)) | ||
1304 | { | ||
1305 | /* possibly shift by more if we skipped messages */ | ||
1306 | uint64_t delta = htonl (msg->mid.mid) - 1 - ntohl (ch->mid_recv.mid); | ||
1307 | |||
1308 | if (delta > 63) | ||
1309 | ch->mid_futures = 0; | ||
1310 | else | ||
1311 | ch->mid_futures >>= delta; | ||
1312 | ch->mid_recv.mid = htonl (1 + ntohl (msg->mid.mid)); | ||
1313 | } | ||
1314 | send_channel_data_ack (ch); | ||
1315 | return; | ||
1316 | } | ||
1317 | } | ||
1318 | |||
1319 | if (GNUNET_YES == ch->reliable) | ||
1320 | { | ||
1321 | /* check if message ought to be dropped because it is ancient/too distant/duplicate */ | ||
1322 | mid_min = ntohl (ch->mid_recv.mid); | ||
1323 | mid_max = mid_min + ch->max_pending_messages; | ||
1324 | mid_msg = ntohl (msg->mid.mid); | ||
1325 | if (((uint32_t) (mid_msg - mid_min) > ch->max_pending_messages) || | ||
1326 | ((uint32_t) (mid_max - mid_msg) > ch->max_pending_messages)) | ||
1327 | { | ||
1328 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1329 | "%s at %u drops ancient or far-future message %u\n", | ||
1330 | GCCH_2s (ch), | ||
1331 | (unsigned int) mid_min, | ||
1332 | ntohl (msg->mid.mid)); | ||
1333 | |||
1334 | GNUNET_STATISTICS_update (stats, | ||
1335 | "# duplicate DATA (ancient or future)", | ||
1336 | 1, | ||
1337 | GNUNET_NO); | ||
1338 | GNUNET_MQ_discard (env); | ||
1339 | send_channel_data_ack (ch); | ||
1340 | return; | ||
1341 | } | ||
1342 | /* mark bit for future ACKs */ | ||
1343 | delta = mid_msg - mid_min - 1; /* overflow/underflow are OK here */ | ||
1344 | if (delta < 64) | ||
1345 | { | ||
1346 | if (0 != (ch->mid_futures & (1LLU << delta))) | ||
1347 | { | ||
1348 | /* Duplicate within the queue, drop also */ | ||
1349 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1350 | "Duplicate payload of %u bytes on %s (mid %u) dropped\n", | ||
1351 | (unsigned int) payload_size, | ||
1352 | GCCH_2s (ch), | ||
1353 | ntohl (msg->mid.mid)); | ||
1354 | GNUNET_STATISTICS_update (stats, "# duplicate DATA", 1, GNUNET_NO); | ||
1355 | GNUNET_MQ_discard (env); | ||
1356 | send_channel_data_ack (ch); | ||
1357 | return; | ||
1358 | } | ||
1359 | ch->mid_futures |= (1LLU << delta); | ||
1360 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1361 | "Marked bit %llX for mid %u (base: %u); now: %llX\n", | ||
1362 | (1LLU << delta), | ||
1363 | mid_msg, | ||
1364 | mid_min, | ||
1365 | (unsigned long long) ch->mid_futures); | ||
1366 | } | ||
1367 | } | ||
1368 | else /* ! ch->reliable */ | ||
1369 | { | ||
1370 | struct CadetOutOfOrderMessage *next_msg; | ||
1371 | |||
1372 | /** | ||
1373 | * We always send if possible in this case. | ||
1374 | * It is guaranteed that the queued MID < received MID | ||
1375 | **/ | ||
1376 | if ((NULL != ccc->head_recv) && (GNUNET_YES == ccc->client_ready)) | ||
1377 | { | ||
1378 | next_msg = ccc->head_recv; | ||
1379 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1380 | "Giving queued MID %u from %s to client %s\n", | ||
1381 | ntohl (next_msg->mid.mid), | ||
1382 | GCCH_2s (ch), | ||
1383 | GSC_2s (ccc->c)); | ||
1384 | ccc->client_ready = GNUNET_NO; | ||
1385 | GSC_send_to_client (ccc->c, next_msg->env); | ||
1386 | ch->mid_recv.mid = htonl (1 + ntohl (next_msg->mid.mid)); | ||
1387 | ch->mid_futures >>= 1; | ||
1388 | send_channel_data_ack (ch); | ||
1389 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, next_msg); | ||
1390 | ccc->num_recv--; | ||
1391 | /* Do not process duplicate MID */ | ||
1392 | if (msg->mid.mid == next_msg->mid.mid) /* Duplicate */ | ||
1393 | { | ||
1394 | /* Duplicate within the queue, drop */ | ||
1395 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1396 | "Message on %s (mid %u) dropped, duplicate\n", | ||
1397 | GCCH_2s (ch), | ||
1398 | ntohl (msg->mid.mid)); | ||
1399 | GNUNET_free (next_msg); | ||
1400 | GNUNET_MQ_discard (env); | ||
1401 | return; | ||
1402 | } | ||
1403 | GNUNET_free (next_msg); | ||
1404 | } | ||
1405 | |||
1406 | if (ntohl (msg->mid.mid) < ntohl (ch->mid_recv.mid)) /* Old */ | ||
1407 | { | ||
1408 | /* Duplicate within the queue, drop */ | ||
1409 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1410 | "Message on %s (mid %u) dropped, old.\n", | ||
1411 | GCCH_2s (ch), | ||
1412 | ntohl (msg->mid.mid)); | ||
1413 | GNUNET_MQ_discard (env); | ||
1414 | return; | ||
1415 | } | ||
1416 | |||
1417 | /* Channel is unreliable, so we do not ACK. But we also cannot | ||
1418 | allow buffering everything, so check if we have space... */ | ||
1419 | if (ccc->num_recv >= ch->max_pending_messages) | ||
1420 | { | ||
1421 | struct CadetOutOfOrderMessage *drop; | ||
1422 | |||
1423 | /* Yep, need to drop. Drop the oldest message in | ||
1424 | the buffer. */ | ||
1425 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1426 | "Queue full due slow client on %s, dropping oldest message\n", | ||
1427 | GCCH_2s (ch)); | ||
1428 | GNUNET_STATISTICS_update (stats, | ||
1429 | "# messages dropped due to slow client", | ||
1430 | 1, | ||
1431 | GNUNET_NO); | ||
1432 | drop = ccc->head_recv; | ||
1433 | GNUNET_assert (NULL != drop); | ||
1434 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, drop); | ||
1435 | ccc->num_recv--; | ||
1436 | GNUNET_MQ_discard (drop->env); | ||
1437 | GNUNET_free (drop); | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | /* Insert message into sorted out-of-order queue */ | ||
1442 | com = GNUNET_new (struct CadetOutOfOrderMessage); | ||
1443 | com->mid = msg->mid; | ||
1444 | com->env = env; | ||
1445 | duplicate = GNUNET_NO; | ||
1446 | GNUNET_CONTAINER_DLL_insert_sorted (struct CadetOutOfOrderMessage, | ||
1447 | is_before, | ||
1448 | &duplicate, | ||
1449 | ccc->head_recv, | ||
1450 | ccc->tail_recv, | ||
1451 | com); | ||
1452 | ccc->num_recv++; | ||
1453 | if (GNUNET_YES == duplicate) | ||
1454 | { | ||
1455 | /* Duplicate within the queue, drop also (this is not covered by | ||
1456 | the case above if "delta" >= 64, which could be the case if | ||
1457 | max_pending_messages is also >= 64 or if our client is unready | ||
1458 | and we are seeing retransmissions of the message our client is | ||
1459 | blocked on. */LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1460 | "Duplicate payload of %u bytes on %s (mid %u) dropped\n", | ||
1461 | (unsigned int) payload_size, | ||
1462 | GCCH_2s (ch), | ||
1463 | ntohl (msg->mid.mid)); | ||
1464 | GNUNET_STATISTICS_update (stats, "# duplicate DATA", 1, GNUNET_NO); | ||
1465 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, com); | ||
1466 | ccc->num_recv--; | ||
1467 | GNUNET_MQ_discard (com->env); | ||
1468 | GNUNET_free (com); | ||
1469 | send_channel_data_ack (ch); | ||
1470 | return; | ||
1471 | } | ||
1472 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1473 | "Queued %s payload of %u bytes on %s-%X(%p) (mid %u, need %u first)\n", | ||
1474 | (GNUNET_YES == ccc->client_ready) ? "out-of-order" : "client-not-ready", | ||
1475 | (unsigned int) payload_size, | ||
1476 | GCCH_2s (ch), | ||
1477 | ntohl (ccc->ccn.channel_of_client), | ||
1478 | ccc, | ||
1479 | ntohl (msg->mid.mid), | ||
1480 | ntohl (ch->mid_recv.mid)); | ||
1481 | /* NOTE: this ACK we _could_ skip, as the packet is out-of-order and | ||
1482 | the sender may already be transmitting the previous one. Needs | ||
1483 | experimental evaluation to see if/when this ACK helps or | ||
1484 | hurts. (We might even want another option.) */ | ||
1485 | send_channel_data_ack (ch); | ||
1486 | } | ||
1487 | |||
1488 | |||
1489 | /** | ||
1490 | * Function called once the tunnel has sent one of our messages. | ||
1491 | * If the message is unreliable, simply frees the `crm`. If the | ||
1492 | * message was reliable, calculate retransmission time and | ||
1493 | * wait for ACK (or retransmit). | ||
1494 | * | ||
1495 | * @param cls the `struct CadetReliableMessage` that was sent | ||
1496 | * @param cid identifier of the connection within the tunnel, NULL | ||
1497 | * if transmission failed | ||
1498 | */ | ||
1499 | static void | ||
1500 | data_sent_cb (void *cls, | ||
1501 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); | ||
1502 | |||
1503 | |||
1504 | /** | ||
1505 | * We need to retry a transmission, the last one took too long to | ||
1506 | * be acknowledged. | ||
1507 | * | ||
1508 | * @param cls the `struct CadetChannel` where we need to retransmit | ||
1509 | */ | ||
1510 | static void | ||
1511 | retry_transmission (void *cls) | ||
1512 | { | ||
1513 | struct CadetChannel *ch = cls; | ||
1514 | struct CadetReliableMessage *crm = ch->head_sent; | ||
1515 | |||
1516 | ch->retry_data_task = NULL; | ||
1517 | GNUNET_assert (NULL == crm->qe); | ||
1518 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1519 | "Retrying transmission on %s of message %u\n", | ||
1520 | GCCH_2s (ch), | ||
1521 | (unsigned int) ntohl (crm->data_message->mid.mid)); | ||
1522 | crm->qe = GCT_send (ch->t, &crm->data_message->header, &data_sent_cb, crm, | ||
1523 | &crm->data_message->ctn); | ||
1524 | GNUNET_assert (NULL == ch->retry_data_task); | ||
1525 | } | ||
1526 | |||
1527 | |||
1528 | /** | ||
1529 | * We got an PLAINTEXT_DATA_ACK for a message in our queue, remove it from | ||
1530 | * the queue and tell our client that it can send more. | ||
1531 | * | ||
1532 | * @param ch the channel that got the PLAINTEXT_DATA_ACK | ||
1533 | * @param cti identifier of the connection that delivered the message | ||
1534 | * @param crm the message that got acknowledged | ||
1535 | */ | ||
1536 | static void | ||
1537 | handle_matching_ack (struct CadetChannel *ch, | ||
1538 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, | ||
1539 | struct CadetReliableMessage *crm) | ||
1540 | { | ||
1541 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, ch->tail_sent, crm); | ||
1542 | ch->pending_messages--; | ||
1543 | GNUNET_assert (ch->pending_messages < ch->max_pending_messages); | ||
1544 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1545 | "Received DATA_ACK on %s for message %u (%u ACKs pending)\n", | ||
1546 | GCCH_2s (ch), | ||
1547 | (unsigned int) ntohl (crm->data_message->mid.mid), | ||
1548 | ch->pending_messages); | ||
1549 | if (NULL != crm->qe) | ||
1550 | { | ||
1551 | GCT_send_cancel (crm->qe); | ||
1552 | crm->qe = NULL; | ||
1553 | } | ||
1554 | if ((1 == crm->num_transmissions) && (NULL != cti)) | ||
1555 | { | ||
1556 | GCC_ack_observed (cti); | ||
1557 | if (0 == GNUNET_memcmp (cti, &crm->connection_taken)) | ||
1558 | { | ||
1559 | GCC_latency_observed (cti, | ||
1560 | GNUNET_TIME_absolute_get_duration ( | ||
1561 | crm->first_transmission_time)); | ||
1562 | } | ||
1563 | } | ||
1564 | GNUNET_free (crm->data_message); | ||
1565 | GNUNET_free (crm); | ||
1566 | send_ack_to_client (ch, (NULL == ch->owner) ? GNUNET_NO : GNUNET_YES); | ||
1567 | } | ||
1568 | |||
1569 | |||
1570 | /** | ||
1571 | * We got an acknowledgement for payload data for a channel. | ||
1572 | * Possibly resume transmissions. | ||
1573 | * | ||
1574 | * @param ch channel that got the ack | ||
1575 | * @param cti identifier of the connection that delivered the message | ||
1576 | * @param ack details about what was received | ||
1577 | */ | ||
1578 | void | ||
1579 | GCCH_handle_channel_plaintext_data_ack ( | ||
1580 | struct CadetChannel *ch, | ||
1581 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, | ||
1582 | const struct GNUNET_CADET_ChannelDataAckMessage *ack) | ||
1583 | { | ||
1584 | struct CadetReliableMessage *crm; | ||
1585 | struct CadetReliableMessage *crmn; | ||
1586 | int found; | ||
1587 | uint32_t mid_base; | ||
1588 | uint64_t mid_mask; | ||
1589 | unsigned int delta; | ||
1590 | |||
1591 | GNUNET_break (GNUNET_NO == ch->is_loopback); | ||
1592 | if (GNUNET_NO == ch->reliable) | ||
1593 | { | ||
1594 | /* not expecting ACKs on unreliable channel, odd */ | ||
1595 | GNUNET_break_op (0); | ||
1596 | return; | ||
1597 | } | ||
1598 | /* mid_base is the MID of the next message that the | ||
1599 | other peer expects (i.e. that is missing!), everything | ||
1600 | LOWER (but excluding mid_base itself) was received. */ | ||
1601 | mid_base = ntohl (ack->mid.mid); | ||
1602 | mid_mask = GNUNET_htonll (ack->futures); | ||
1603 | found = GNUNET_NO; | ||
1604 | for (crm = ch->head_sent; NULL != crm; crm = crmn) | ||
1605 | { | ||
1606 | crmn = crm->next; | ||
1607 | delta = (unsigned int) (ntohl (crm->data_message->mid.mid) - mid_base); | ||
1608 | if (delta >= UINT_MAX - ch->max_pending_messages) | ||
1609 | { | ||
1610 | /* overflow, means crm was a bit in the past, so this ACK counts for it. */ | ||
1611 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1612 | "Got DATA_ACK with base %u satisfying past message %u on %s\n", | ||
1613 | (unsigned int) mid_base, | ||
1614 | ntohl (crm->data_message->mid.mid), | ||
1615 | GCCH_2s (ch)); | ||
1616 | handle_matching_ack (ch, cti, crm); | ||
1617 | found = GNUNET_YES; | ||
1618 | continue; | ||
1619 | } | ||
1620 | delta--; | ||
1621 | if (delta >= 64) | ||
1622 | continue; | ||
1623 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1624 | "Testing bit %llX for mid %u (base: %u)\n", | ||
1625 | (1LLU << delta), | ||
1626 | ntohl (crm->data_message->mid.mid), | ||
1627 | mid_base); | ||
1628 | if (0 != (mid_mask & (1LLU << delta))) | ||
1629 | { | ||
1630 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1631 | "Got DATA_ACK with mask for %u on %s\n", | ||
1632 | ntohl (crm->data_message->mid.mid), | ||
1633 | GCCH_2s (ch)); | ||
1634 | handle_matching_ack (ch, cti, crm); | ||
1635 | found = GNUNET_YES; | ||
1636 | } | ||
1637 | } | ||
1638 | if (GNUNET_NO == found) | ||
1639 | { | ||
1640 | /* ACK for message we already dropped, might have been a | ||
1641 | duplicate ACK? Ignore. */ | ||
1642 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1643 | "Duplicate DATA_ACK on %s, ignoring\n", | ||
1644 | GCCH_2s (ch)); | ||
1645 | GNUNET_STATISTICS_update (stats, "# duplicate DATA_ACKs", 1, GNUNET_NO); | ||
1646 | return; | ||
1647 | } | ||
1648 | if (NULL != ch->retry_data_task) | ||
1649 | { | ||
1650 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | ||
1651 | ch->retry_data_task = NULL; | ||
1652 | } | ||
1653 | if ((NULL != ch->head_sent) && (NULL == ch->head_sent->qe)) | ||
1654 | ch->retry_data_task = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, | ||
1655 | &retry_transmission, | ||
1656 | ch); | ||
1657 | } | ||
1658 | |||
1659 | |||
1660 | /** | ||
1661 | * Destroy channel, based on the other peer closing the | ||
1662 | * connection. Also needs to remove this channel from | ||
1663 | * the tunnel. | ||
1664 | * | ||
1665 | * @param ch channel to destroy | ||
1666 | * @param cti identifier of the connection that delivered the message, | ||
1667 | * NULL if we are simulating receiving a destroy due to shutdown | ||
1668 | */ | ||
1669 | void | ||
1670 | GCCH_handle_remote_destroy ( | ||
1671 | struct CadetChannel *ch, | ||
1672 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) | ||
1673 | { | ||
1674 | struct CadetChannelClient *ccc; | ||
1675 | |||
1676 | GNUNET_assert (GNUNET_NO == ch->is_loopback); | ||
1677 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1678 | "Received remote channel DESTROY for %s\n", | ||
1679 | GCCH_2s (ch)); | ||
1680 | if (GNUNET_YES == ch->destroy) | ||
1681 | { | ||
1682 | /* Local client already gone, this is instant-death. */ | ||
1683 | channel_destroy (ch); | ||
1684 | return; | ||
1685 | } | ||
1686 | ccc = (NULL != ch->owner) ? ch->owner : ch->dest; | ||
1687 | if ((NULL != ccc) && (NULL != ccc->head_recv)) | ||
1688 | { | ||
1689 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1690 | "Lost end of transmission due to remote shutdown on %s\n", | ||
1691 | GCCH_2s (ch)); | ||
1692 | /* FIXME: change API to notify client about truncated transmission! */ | ||
1693 | } | ||
1694 | ch->destroy = GNUNET_YES; | ||
1695 | if (NULL != ccc) | ||
1696 | GSC_handle_remote_channel_destroy (ccc->c, ccc->ccn, ch); | ||
1697 | channel_destroy (ch); | ||
1698 | } | ||
1699 | |||
1700 | |||
1701 | /** | ||
1702 | * Test if element @a e1 comes before element @a e2. | ||
1703 | * | ||
1704 | * @param cls closure, to a flag where we indicate duplicate packets | ||
1705 | * @param crm1 an element of to sort | ||
1706 | * @param crm2 another element to sort | ||
1707 | * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO | ||
1708 | */ | ||
1709 | static int | ||
1710 | cmp_crm_by_next_retry (void *cls, | ||
1711 | struct CadetReliableMessage *crm1, | ||
1712 | struct CadetReliableMessage *crm2) | ||
1713 | { | ||
1714 | if (crm1->next_retry.abs_value_us < crm2->next_retry.abs_value_us) | ||
1715 | return GNUNET_YES; | ||
1716 | return GNUNET_NO; | ||
1717 | } | ||
1718 | |||
1719 | |||
1720 | /** | ||
1721 | * Function called once the tunnel has sent one of our messages. | ||
1722 | * If the message is unreliable, simply frees the `crm`. If the | ||
1723 | * message was reliable, calculate retransmission time and | ||
1724 | * wait for ACK (or retransmit). | ||
1725 | * | ||
1726 | * @param cls the `struct CadetReliableMessage` that was sent | ||
1727 | * @param cid identifier of the connection within the tunnel, NULL | ||
1728 | * if transmission failed | ||
1729 | */ | ||
1730 | static void | ||
1731 | data_sent_cb (void *cls, | ||
1732 | const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) | ||
1733 | { | ||
1734 | struct CadetReliableMessage *crm = cls; | ||
1735 | struct CadetChannel *ch = crm->ch; | ||
1736 | |||
1737 | GNUNET_assert (GNUNET_NO == ch->is_loopback); | ||
1738 | GNUNET_assert (NULL != crm->qe); | ||
1739 | crm->qe = NULL; | ||
1740 | GNUNET_CONTAINER_DLL_remove (ch->head_sent, ch->tail_sent, crm); | ||
1741 | if (GNUNET_NO == ch->reliable) | ||
1742 | { | ||
1743 | GNUNET_free (crm->data_message); | ||
1744 | GNUNET_free (crm); | ||
1745 | ch->pending_messages--; | ||
1746 | send_ack_to_client (ch, (NULL == ch->owner) ? GNUNET_NO : GNUNET_YES); | ||
1747 | return; | ||
1748 | } | ||
1749 | if (NULL == cid) | ||
1750 | { | ||
1751 | /* There was an error sending. */ | ||
1752 | crm->num_transmissions = GNUNET_SYSERR; | ||
1753 | } | ||
1754 | else if (GNUNET_SYSERR != crm->num_transmissions) | ||
1755 | { | ||
1756 | /* Increment transmission counter, and possibly store @a cid | ||
1757 | if this was the first transmission. */ | ||
1758 | crm->num_transmissions++; | ||
1759 | if (1 == crm->num_transmissions) | ||
1760 | { | ||
1761 | crm->first_transmission_time = GNUNET_TIME_absolute_get (); | ||
1762 | crm->connection_taken = *cid; | ||
1763 | GCC_ack_expected (cid); | ||
1764 | } | ||
1765 | } | ||
1766 | if ((0 == crm->retry_delay.rel_value_us) && (NULL != cid)) | ||
1767 | { | ||
1768 | struct CadetConnection *cc = GCC_lookup (cid); | ||
1769 | |||
1770 | if (NULL != cc) | ||
1771 | crm->retry_delay = GCC_get_metrics (cc)->aged_latency; | ||
1772 | else | ||
1773 | crm->retry_delay = ch->retry_time; | ||
1774 | } | ||
1775 | crm->retry_delay = GNUNET_TIME_STD_BACKOFF (crm->retry_delay); | ||
1776 | crm->retry_delay = GNUNET_TIME_relative_max (crm->retry_delay, MIN_RTT_DELAY); | ||
1777 | crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay); | ||
1778 | |||
1779 | GNUNET_CONTAINER_DLL_insert_sorted (struct CadetReliableMessage, | ||
1780 | cmp_crm_by_next_retry, | ||
1781 | NULL, | ||
1782 | ch->head_sent, | ||
1783 | ch->tail_sent, | ||
1784 | crm); | ||
1785 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1786 | "Message %u sent, next transmission on %s in %s\n", | ||
1787 | (unsigned int) ntohl (crm->data_message->mid.mid), | ||
1788 | GCCH_2s (ch), | ||
1789 | GNUNET_STRINGS_relative_time_to_string ( | ||
1790 | GNUNET_TIME_absolute_get_remaining ( | ||
1791 | ch->head_sent->next_retry), | ||
1792 | GNUNET_YES)); | ||
1793 | if (NULL == ch->head_sent->qe) | ||
1794 | { | ||
1795 | if (NULL != ch->retry_data_task) | ||
1796 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | ||
1797 | ch->retry_data_task = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, | ||
1798 | &retry_transmission, | ||
1799 | ch); | ||
1800 | } | ||
1801 | } | ||
1802 | |||
1803 | |||
1804 | /** | ||
1805 | * Handle data given by a client. | ||
1806 | * | ||
1807 | * Check whether the client is allowed to send in this tunnel, save if | ||
1808 | * channel is reliable and send an ACK to the client if there is still | ||
1809 | * buffer space in the tunnel. | ||
1810 | * | ||
1811 | * @param ch Channel. | ||
1812 | * @param sender_ccn ccn of the sender | ||
1813 | * @param buf payload to transmit. | ||
1814 | * @param buf_len number of bytes in @a buf | ||
1815 | * @return #GNUNET_OK if everything goes well, | ||
1816 | * #GNUNET_SYSERR in case of an error. | ||
1817 | */ | ||
1818 | int | ||
1819 | GCCH_handle_local_data (struct CadetChannel *ch, | ||
1820 | struct GNUNET_CADET_ClientChannelNumber sender_ccn, | ||
1821 | const char *buf, | ||
1822 | size_t buf_len) | ||
1823 | { | ||
1824 | struct CadetReliableMessage *crm; | ||
1825 | |||
1826 | if (ch->pending_messages >= ch->max_pending_messages) | ||
1827 | { | ||
1828 | GNUNET_break (0); /* Fails: #5370 */ | ||
1829 | return GNUNET_SYSERR; | ||
1830 | } | ||
1831 | if (GNUNET_YES == ch->destroy) | ||
1832 | { | ||
1833 | /* we are going down, drop messages */ | ||
1834 | return GNUNET_OK; | ||
1835 | } | ||
1836 | ch->pending_messages++; | ||
1837 | |||
1838 | if (GNUNET_YES == ch->is_loopback) | ||
1839 | { | ||
1840 | struct CadetChannelClient *receiver; | ||
1841 | struct GNUNET_MQ_Envelope *env; | ||
1842 | struct GNUNET_CADET_LocalData *ld; | ||
1843 | int ack_to_owner; | ||
1844 | |||
1845 | env = | ||
1846 | GNUNET_MQ_msg_extra (ld, buf_len, GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); | ||
1847 | if ((NULL != ch->owner) && | ||
1848 | (sender_ccn.channel_of_client == ch->owner->ccn.channel_of_client)) | ||
1849 | { | ||
1850 | receiver = ch->dest; | ||
1851 | ack_to_owner = GNUNET_YES; | ||
1852 | } | ||
1853 | else if ((NULL != ch->dest) && | ||
1854 | (sender_ccn.channel_of_client == ch->dest->ccn.channel_of_client)) | ||
1855 | { | ||
1856 | receiver = ch->owner; | ||
1857 | ack_to_owner = GNUNET_NO; | ||
1858 | } | ||
1859 | else | ||
1860 | { | ||
1861 | GNUNET_free (env); | ||
1862 | GNUNET_break (0); | ||
1863 | return GNUNET_SYSERR; | ||
1864 | } | ||
1865 | GNUNET_assert (NULL != receiver); | ||
1866 | ld->ccn = receiver->ccn; | ||
1867 | GNUNET_memcpy (&ld[1], buf, buf_len); | ||
1868 | if (GNUNET_YES == receiver->client_ready) | ||
1869 | { | ||
1870 | ch->pending_messages--; | ||
1871 | GSC_send_to_client (receiver->c, env); | ||
1872 | send_ack_to_client (ch, ack_to_owner); | ||
1873 | } | ||
1874 | else | ||
1875 | { | ||
1876 | struct CadetOutOfOrderMessage *oom; | ||
1877 | |||
1878 | oom = GNUNET_new (struct CadetOutOfOrderMessage); | ||
1879 | oom->env = env; | ||
1880 | GNUNET_CONTAINER_DLL_insert_tail (receiver->head_recv, | ||
1881 | receiver->tail_recv, | ||
1882 | oom); | ||
1883 | receiver->num_recv++; | ||
1884 | } | ||
1885 | return GNUNET_OK; | ||
1886 | } | ||
1887 | |||
1888 | /* Everything is correct, send the message. */ | ||
1889 | crm = GNUNET_malloc (sizeof(*crm)); | ||
1890 | crm->ch = ch; | ||
1891 | crm->data_message = GNUNET_malloc ( | ||
1892 | sizeof(struct GNUNET_CADET_ChannelAppDataMessage) + buf_len); | ||
1893 | crm->data_message->header.size = | ||
1894 | htons (sizeof(struct GNUNET_CADET_ChannelAppDataMessage) + buf_len); | ||
1895 | crm->data_message->header.type = | ||
1896 | htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA); | ||
1897 | ch->mid_send.mid = htonl (ntohl (ch->mid_send.mid) + 1); | ||
1898 | crm->data_message->mid = ch->mid_send; | ||
1899 | crm->data_message->ctn = ch->ctn; | ||
1900 | GNUNET_memcpy (&crm->data_message[1], buf, buf_len); | ||
1901 | GNUNET_CONTAINER_DLL_insert_tail (ch->head_sent, ch->tail_sent, crm); | ||
1902 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1903 | "Sending message %u from local client to %s with %lu bytes\n", | ||
1904 | ntohl (crm->data_message->mid.mid), | ||
1905 | GCCH_2s (ch), | ||
1906 | (unsigned long) buf_len); | ||
1907 | if (NULL != ch->retry_data_task) | ||
1908 | { | ||
1909 | GNUNET_SCHEDULER_cancel (ch->retry_data_task); | ||
1910 | ch->retry_data_task = NULL; | ||
1911 | } | ||
1912 | crm->qe = GCT_send (ch->t, &crm->data_message->header, &data_sent_cb, crm, | ||
1913 | &crm->data_message->ctn); | ||
1914 | GNUNET_assert (NULL == ch->retry_data_task); | ||
1915 | return GNUNET_OK; | ||
1916 | } | ||
1917 | |||
1918 | |||
1919 | /** | ||
1920 | * Handle ACK from client on local channel. Means the client is ready | ||
1921 | * for more data, see if we have any for it. | ||
1922 | * | ||
1923 | * @param ch channel to destroy | ||
1924 | * @param client_ccn ccn of the client sending the ack | ||
1925 | */ | ||
1926 | void | ||
1927 | GCCH_handle_local_ack (struct CadetChannel *ch, | ||
1928 | struct GNUNET_CADET_ClientChannelNumber client_ccn) | ||
1929 | { | ||
1930 | struct CadetChannelClient *ccc; | ||
1931 | struct CadetOutOfOrderMessage *com; | ||
1932 | |||
1933 | if ((NULL != ch->owner) && | ||
1934 | (ch->owner->ccn.channel_of_client == client_ccn.channel_of_client)) | ||
1935 | ccc = ch->owner; | ||
1936 | else if ((NULL != ch->dest) && | ||
1937 | (ch->dest->ccn.channel_of_client == client_ccn.channel_of_client)) | ||
1938 | ccc = ch->dest; | ||
1939 | else | ||
1940 | GNUNET_assert (0); | ||
1941 | ccc->client_ready = GNUNET_YES; | ||
1942 | com = ccc->head_recv; | ||
1943 | if (NULL == com) | ||
1944 | { | ||
1945 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1946 | "Got LOCAL_ACK, %s-%X ready to receive more data, but none pending on %s-%X(%p)!\n", | ||
1947 | GSC_2s (ccc->c), | ||
1948 | ntohl (client_ccn.channel_of_client), | ||
1949 | GCCH_2s (ch), | ||
1950 | ntohl (ccc->ccn.channel_of_client), | ||
1951 | ccc); | ||
1952 | return; /* none pending */ | ||
1953 | } | ||
1954 | if (GNUNET_YES == ch->is_loopback) | ||
1955 | { | ||
1956 | int to_owner; | ||
1957 | |||
1958 | /* Messages are always in-order, just send */ | ||
1959 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, com); | ||
1960 | ccc->num_recv--; | ||
1961 | GSC_send_to_client (ccc->c, com->env); | ||
1962 | /* Notify sender that we can receive more */ | ||
1963 | if ((NULL != ch->owner) && | ||
1964 | (ccc->ccn.channel_of_client == ch->owner->ccn.channel_of_client)) | ||
1965 | { | ||
1966 | to_owner = GNUNET_NO; | ||
1967 | } | ||
1968 | else | ||
1969 | { | ||
1970 | GNUNET_assert ((NULL != ch->dest) && (ccc->ccn.channel_of_client == | ||
1971 | ch->dest->ccn.channel_of_client)); | ||
1972 | to_owner = GNUNET_YES; | ||
1973 | } | ||
1974 | send_ack_to_client (ch, to_owner); | ||
1975 | GNUNET_free (com); | ||
1976 | return; | ||
1977 | } | ||
1978 | |||
1979 | if ((com->mid.mid != ch->mid_recv.mid) && (GNUNET_NO == ch->out_of_order) && | ||
1980 | (GNUNET_YES == ch->reliable)) | ||
1981 | { | ||
1982 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1983 | "Got LOCAL_ACK, %s-%X ready to receive more data (but next one is out-of-order %u vs. %u)!\n", | ||
1984 | GSC_2s (ccc->c), | ||
1985 | ntohl (ccc->ccn.channel_of_client), | ||
1986 | ntohl (com->mid.mid), | ||
1987 | ntohl (ch->mid_recv.mid)); | ||
1988 | return; /* missing next one in-order */ | ||
1989 | } | ||
1990 | |||
1991 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1992 | "Got LOCAL_ACK, giving payload message %u to %s-%X on %s\n", | ||
1993 | ntohl (com->mid.mid), | ||
1994 | GSC_2s (ccc->c), | ||
1995 | ntohl (ccc->ccn.channel_of_client), | ||
1996 | GCCH_2s (ch)); | ||
1997 | |||
1998 | /* all good, pass next message to client */ | ||
1999 | GNUNET_CONTAINER_DLL_remove (ccc->head_recv, ccc->tail_recv, com); | ||
2000 | ccc->num_recv--; | ||
2001 | /* FIXME: if unreliable, this is not aggressive | ||
2002 | enough, as it would be OK to have lost some! */ | ||
2003 | |||
2004 | ch->mid_recv.mid = htonl (1 + ntohl (com->mid.mid)); | ||
2005 | ch->mid_futures >>= 1; /* equivalent to division by 2 */ | ||
2006 | ccc->client_ready = GNUNET_NO; | ||
2007 | GSC_send_to_client (ccc->c, com->env); | ||
2008 | GNUNET_free (com); | ||
2009 | send_channel_data_ack (ch); | ||
2010 | if (NULL != ccc->head_recv) | ||
2011 | return; | ||
2012 | if (GNUNET_NO == ch->destroy) | ||
2013 | return; | ||
2014 | GCT_send_channel_destroy (ch->t, ch->ctn); | ||
2015 | channel_destroy (ch); | ||
2016 | } | ||
2017 | |||
2018 | |||
2019 | #define LOG2(level, ...) \ | ||
2020 | GNUNET_log_from_nocheck (level, "cadet-chn", __VA_ARGS__) | ||
2021 | |||
2022 | |||
2023 | /** | ||
2024 | * Log channel info. | ||
2025 | * | ||
2026 | * @param ch Channel. | ||
2027 | * @param level Debug level to use. | ||
2028 | */ | ||
2029 | void | ||
2030 | GCCH_debug (struct CadetChannel *ch, enum GNUNET_ErrorType level) | ||
2031 | { | ||
2032 | #if ! defined(GNUNET_CULL_LOGGING) | ||
2033 | int do_log; | ||
2034 | |||
2035 | do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), | ||
2036 | "cadet-chn", | ||
2037 | __FILE__, | ||
2038 | __FUNCTION__, | ||
2039 | __LINE__); | ||
2040 | if (0 == do_log) | ||
2041 | return; | ||
2042 | |||
2043 | if (NULL == ch) | ||
2044 | { | ||
2045 | LOG2 (level, "CHN *** DEBUG NULL CHANNEL ***\n"); | ||
2046 | return; | ||
2047 | } | ||
2048 | LOG2 (level, "CHN %s:%X (%p)\n", GCT_2s (ch->t), ch->ctn.cn, ch); | ||
2049 | if (NULL != ch->owner) | ||
2050 | { | ||
2051 | LOG2 (level, | ||
2052 | "CHN origin %s ready %s local-id: %u\n", | ||
2053 | GSC_2s (ch->owner->c), | ||
2054 | ch->owner->client_ready ? "YES" : "NO", | ||
2055 | ntohl (ch->owner->ccn.channel_of_client)); | ||
2056 | } | ||
2057 | if (NULL != ch->dest) | ||
2058 | { | ||
2059 | LOG2 (level, | ||
2060 | "CHN destination %s ready %s local-id: %u\n", | ||
2061 | GSC_2s (ch->dest->c), | ||
2062 | ch->dest->client_ready ? "YES" : "NO", | ||
2063 | ntohl (ch->dest->ccn.channel_of_client)); | ||
2064 | } | ||
2065 | LOG2 (level, | ||
2066 | "CHN Message IDs recv: %d (%llX), send: %d\n", | ||
2067 | ntohl (ch->mid_recv.mid), | ||
2068 | (unsigned long long) ch->mid_futures, | ||
2069 | ntohl (ch->mid_send.mid)); | ||
2070 | #endif | ||
2071 | } | ||
2072 | |||
2073 | |||
2074 | /* end of gnunet-service-cadet_channel.c */ | ||