aboutsummaryrefslogtreecommitdiff
path: root/src/transport/gnunet-service-transport_neighbours.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/gnunet-service-transport_neighbours.c')
-rw-r--r--src/transport/gnunet-service-transport_neighbours.c3971
1 files changed, 0 insertions, 3971 deletions
diff --git a/src/transport/gnunet-service-transport_neighbours.c b/src/transport/gnunet-service-transport_neighbours.c
deleted file mode 100644
index ca1b4d1da..000000000
--- a/src/transport/gnunet-service-transport_neighbours.c
+++ /dev/null
@@ -1,3971 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file transport/gnunet-service-transport_neighbours.c
23 * @brief neighbour management
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_ats_service.h"
28#include "gnunet-service-transport_ats.h"
29#include "gnunet-service-transport_neighbours.h"
30#include "gnunet-service-transport_manipulation.h"
31#include "gnunet-service-transport_plugins.h"
32#include "gnunet-service-transport_validation.h"
33#include "gnunet-service-transport.h"
34#include "gnunet_peerinfo_service.h"
35#include "gnunet_constants.h"
36#include "transport.h"
37
38/**
39 * Experimental option to ignore SessionQuotaMessages from
40 * the other peer.
41 */
42#define IGNORE_INBOUND_QUOTA GNUNET_YES
43
44/**
45 * Size of the neighbour hash map.
46 */
47#define NEIGHBOUR_TABLE_SIZE 256
48
49/**
50 * Time we give plugin to transmit DISCONNECT message before the
51 * neighbour entry self-destructs.
52 */
53#define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply ( \
54 GNUNET_TIME_UNIT_MILLISECONDS, 500)
55
56/**
57 * How often must a peer violate bandwidth quotas before we start
58 * to simply drop its messages?
59 */
60#define QUOTA_VIOLATION_DROP_THRESHOLD 10
61
62/**
63 * How long are we willing to wait for a response from ATS before timing out?
64 */
65#define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply ( \
66 GNUNET_TIME_UNIT_SECONDS, 5)
67
68/**
69 * How long are we willing to wait for an ACK from the other peer before
70 * giving up on our connect operation?
71 */
72#define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply ( \
73 GNUNET_TIME_UNIT_SECONDS, 15)
74
75/**
76 * How long are we willing to wait for a successful reconnect if
77 * an existing connection went down? Much shorter than the
78 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
79 * higher layers about the disconnect during this period.
80 */
81#define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS
82
83/**
84 * Interval to send utilization data
85 */
86#define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS
87
88/**
89 * State describing which kind a reply this neighbour should send
90 */
91enum GST_ACK_State
92{
93 /**
94 * We did not receive a SYN message for this neighbour
95 */
96 ACK_UNDEFINED = 0,
97
98 /**
99 * The neighbour received a SYN message and has to send a SYN_ACK
100 * as reply
101 */
102 ACK_SEND_SYN_ACK = 1,
103
104 /**
105 * The neighbour sent a SYN_ACK message and has to send a ACK
106 * as reply
107 */
108 ACK_SEND_ACK = 2
109};
110
111
112GNUNET_NETWORK_STRUCT_BEGIN
113
114/**
115 * Message a peer sends to another to indicate that it intends to
116 * setup a connection/session for data exchange. A 'SESSION_SYN'
117 * should be answered with a 'SESSION_SYN_ACK' with the same body
118 * to confirm. A 'SESSION_SYN_ACK' should then be followed with
119 * a 'ACK'. Once the 'ACK' is received, both peers
120 * should be connected.
121 */
122struct TransportSynMessage
123{
124 /**
125 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN
126 * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK
127 */
128 struct GNUNET_MessageHeader header;
129
130 /**
131 * Always zero.
132 */
133 uint32_t reserved GNUNET_PACKED;
134
135 /**
136 * Absolute time at the sender. Only the most recent connect
137 * message implies which session is preferred by the sender.
138 */
139 struct GNUNET_TIME_AbsoluteNBO timestamp;
140};
141
142
143/**
144 * Message a peer sends to another when connected to indicate that a
145 * session is in use and the peer is still alive or to respond to a keep alive.
146 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
147 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
148 * When the keep alive response with type is received, transport service
149 * will call the respective plugin to update the session timeout
150 */
151struct GNUNET_ATS_SessionKeepAliveMessage
152{
153 /**
154 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
155 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
156 */
157 struct GNUNET_MessageHeader header;
158
159 /**
160 * A nonce to identify the session the keep alive is used for
161 */
162 uint32_t nonce GNUNET_PACKED;
163};
164
165
166/**
167 * Message a peer sends to another when connected to indicate that
168 * the other peer should limit transmissions to the indicated
169 * quota.
170 */
171struct GNUNET_ATS_SessionQuotaMessage
172{
173 /**
174 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA.
175 */
176 struct GNUNET_MessageHeader header;
177
178 /**
179 * Quota to use (for sending), in bytes per second.
180 */
181 uint32_t quota GNUNET_PACKED;
182};
183
184
185/**
186 * Message we send to the other peer to notify it that we intentionally
187 * are disconnecting (to reduce timeouts). This is just a friendly
188 * notification, peers must not rely on always receiving disconnect
189 * messages.
190 */
191struct GNUNET_ATS_SessionDisconnectMessage
192{
193 /**
194 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
195 */
196 struct GNUNET_MessageHeader header;
197
198 /**
199 * Always zero.
200 */
201 uint32_t reserved GNUNET_PACKED;
202
203 /**
204 * Purpose of the signature. Extends over the timestamp.
205 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
206 */
207 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
208
209 /**
210 * Absolute time at the sender. Only the most recent connect
211 * message implies which session is preferred by the sender.
212 */
213 struct GNUNET_TIME_AbsoluteNBO timestamp;
214
215 /**
216 * Public key of the sender.
217 */
218 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
219
220 /**
221 * Signature of the peer that sends us the disconnect. Only
222 * valid if the timestamp is AFTER the timestamp from the
223 * corresponding 'SYN' message.
224 */
225 struct GNUNET_CRYPTO_EddsaSignature signature;
226};
227
228GNUNET_NETWORK_STRUCT_END
229
230
231/**
232 * For each neighbour we keep a list of messages
233 * that we still want to transmit to the neighbour.
234 */
235struct MessageQueue
236{
237 /**
238 * This is a doubly linked list.
239 */
240 struct MessageQueue *next;
241
242 /**
243 * This is a doubly linked list.
244 */
245 struct MessageQueue *prev;
246
247 /**
248 * Function to call once we're done.
249 */
250 GST_NeighbourSendContinuation cont;
251
252 /**
253 * Closure for @e cont
254 */
255 void *cont_cls;
256
257 /**
258 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
259 * stuck together in memory. Allocated at the end of this struct.
260 */
261 const char *message_buf;
262
263 /**
264 * Size of the message buf
265 */
266 size_t message_buf_size;
267
268 /**
269 * At what time should we fail?
270 */
271 struct GNUNET_TIME_Absolute timeout;
272};
273
274
275/**
276 * A possible address we could use to communicate with a neighbour.
277 */
278struct NeighbourAddress
279{
280 /**
281 * Active session for this address.
282 */
283 struct GNUNET_ATS_Session *session;
284
285 /**
286 * Network-level address information.
287 */
288 struct GNUNET_HELLO_Address *address;
289
290 /**
291 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
292 * peer for this address. Use to check that the ACK is in response
293 * to our most recent 'SYN'.
294 */
295 struct GNUNET_TIME_Absolute connect_timestamp;
296
297 /**
298 * Inbound bandwidth from ATS for this address.
299 */
300 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
301
302 /**
303 * Outbound bandwidth from ATS for this address.
304 */
305 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
306
307 /**
308 * Did we tell ATS that this is our 'active' address?
309 */
310 int ats_active;
311
312 /**
313 * The current nonce sent in the last keep alive messages
314 */
315 uint32_t keep_alive_nonce;
316};
317
318
319/**
320 * Entry in neighbours.
321 */
322struct NeighbourMapEntry
323{
324 /**
325 * Head of list of messages we would like to send to this peer;
326 * must contain at most one message per client.
327 */
328 struct MessageQueue *messages_head;
329
330 /**
331 * Tail of list of messages we would like to send to this peer; must
332 * contain at most one message per client.
333 */
334 struct MessageQueue *messages_tail;
335
336 /**
337 * Are we currently trying to send a message? If so, which one?
338 */
339 struct MessageQueue *is_active;
340
341 /**
342 * Primary address we currently use to communicate with the neighbour.
343 */
344 struct NeighbourAddress primary_address;
345
346 /**
347 * Alternative address currently under consideration for communicating
348 * with the neighbour.
349 */
350 struct NeighbourAddress alternative_address;
351
352 /**
353 * Identity of this neighbour.
354 */
355 struct GNUNET_PeerIdentity id;
356
357 /**
358 * Main task that drives this peer (timeouts, keepalives, etc.).
359 * Always runs the #master_task().
360 */
361 struct GNUNET_SCHEDULER_Task *task;
362
363 /**
364 * Task to disconnect neighbour after we received a DISCONNECT message
365 */
366 struct GNUNET_SCHEDULER_Task *delayed_disconnect_task;
367
368 /**
369 * At what time should we sent the next keep-alive message?
370 */
371 struct GNUNET_TIME_Absolute keep_alive_time;
372
373 /**
374 * At what time did we sent the last keep-alive message? Used
375 * to calculate round-trip time ("latency").
376 */
377 struct GNUNET_TIME_Absolute last_keep_alive_time;
378
379 /**
380 * Timestamp we should include in our next SYN_ACK message.
381 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
382 * our SYN_ACK message.
383 */
384 struct GNUNET_TIME_Absolute connect_ack_timestamp;
385
386 /**
387 * ATS address suggest handle
388 */
389 struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle;
390
391 /**
392 * Time where we should cut the connection (timeout) if we don't
393 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
394 * if we are in #GNUNET_TRANSPORT_PS_CONNECTED).
395 */
396 struct GNUNET_TIME_Absolute timeout;
397
398 /**
399 * Tracker for inbound bandwidth.
400 */
401 struct GNUNET_BANDWIDTH_Tracker in_tracker;
402
403 /**
404 * How often has the other peer (recently) violated the inbound
405 * traffic limit? Incremented by 10 per violation, decremented by 1
406 * per non-violation (for each time interval).
407 */
408 unsigned int quota_violation_count;
409
410 /**
411 * Latest quota the other peer send us in bytes per second.
412 * We should not send more, least the other peer throttle
413 * receiving our traffic.
414 */
415 struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota;
416
417 /**
418 * The current state of the peer.
419 */
420 enum GNUNET_TRANSPORT_PeerState state;
421
422 /**
423 * Did we sent an KEEP_ALIVE message and are we expecting a response?
424 */
425 int expect_latency_response;
426
427 /**
428 * When a peer wants to connect we have to reply to the 1st SYN message
429 * with a SYN_ACK message. But sometime we cannot send this message
430 * immediately since we do not have an address and then we have to remember
431 * to send this message as soon as we have an address.
432 *
433 * Flag to set if we still need to send a SYN_ACK message to the other peer
434 * (once we have an address to use and the peer has been allowed by our
435 * blacklist). Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK
436 * if we need to send a SYN_ACK. Set to #ACK_SEND_ACK if we did
437 * send a SYN_ACK and should go to #S_CONNECTED upon receiving a
438 * 'ACK' (regardless of what our own state machine might say).
439 */
440 enum GST_ACK_State ack_state;
441
442 /**
443 * Tracking utilization of outbound bandwidth
444 */
445 uint32_t util_total_bytes_sent;
446
447 /**
448 * Tracking utilization of inbound bandwidth
449 */
450 uint32_t util_total_bytes_recv;
451
452 /**
453 * Date of last utilization transmission
454 */
455 struct GNUNET_TIME_Absolute last_util_transmission;
456};
457
458
459/**
460 * Hash map from peer identities to the respective `struct NeighbourMapEntry`.
461 */
462static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
463
464/**
465 * List of pending blacklist checks: head
466 */
467static struct BlacklistCheckSwitchContext *pending_bc_head;
468
469/**
470 * List of pending blacklist checks: tail
471 */
472static struct BlacklistCheckSwitchContext *pending_bc_tail;
473
474/**
475 * counter for connected neighbours
476 */
477static unsigned int neighbours_connected;
478
479/**
480 * Number of bytes we have currently queued for transmission.
481 */
482static unsigned long long bytes_in_send_queue;
483
484/**
485 * Task transmitting utilization data
486 */
487static struct GNUNET_SCHEDULER_Task *util_transmission_tk;
488
489
490/**
491 * Convert the given ACK state to a string.
492 *
493 * @param s state
494 * @return corresponding human-readable string
495 */
496static char *
497print_ack_state (enum GST_ACK_State s)
498{
499 switch (s)
500 {
501 case ACK_UNDEFINED:
502 return "UNDEFINED";
503
504 case ACK_SEND_SYN_ACK:
505 return "SEND_SYN_ACK";
506
507 case ACK_SEND_ACK:
508 return "SEND_ACK";
509
510 default:
511 GNUNET_break (0);
512 return "N/A";
513 }
514}
515
516
517/**
518 * Send information about a new outbound quota to our clients.
519 * Note that the outbound quota is enforced client-side (i.e.
520 * in libgnunettransport).
521 *
522 * @param n affected peer
523 */
524static void
525send_outbound_quota_to_clients (struct NeighbourMapEntry *n)
526{
527 struct QuotaSetMessage q_msg;
528 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
529
530 if (! GNUNET_TRANSPORT_is_connected (n->state))
531 return;
532#if IGNORE_INBOUND_QUOTA
533 bandwidth_min = n->primary_address.bandwidth_out;
534#else
535 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
536 n->neighbour_receive_quota);
537#endif
538
539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
540 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
541 ntohl (bandwidth_min.value__),
542 GNUNET_i2s (&n->id));
543 q_msg.header.size = htons (sizeof(struct QuotaSetMessage));
544 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
545 q_msg.quota = bandwidth_min;
546 q_msg.peer = n->id;
547 GST_clients_broadcast (&q_msg.header,
548 GNUNET_NO);
549}
550
551
552/**
553 * Notify our clients that another peer connected to us.
554 *
555 * @param n the peer that connected
556 */
557static void
558neighbours_connect_notification (struct NeighbourMapEntry *n)
559{
560 size_t len = sizeof(struct ConnectInfoMessage);
561 char buf[len] GNUNET_ALIGN;
562 struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
563 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
564
565#if IGNORE_INBOUND_QUOTA
566 bandwidth_min = n->primary_address.bandwidth_out;
567#else
568 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
569 n->neighbour_receive_quota);
570#endif
571 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
572 "We are now connected to peer `%s'\n",
573 GNUNET_i2s (&n->id));
574 connect_msg->header.size = htons (sizeof(buf));
575 connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
576 connect_msg->id = n->id;
577 connect_msg->quota_out = bandwidth_min;
578 GST_clients_broadcast (&connect_msg->header,
579 GNUNET_NO);
580}
581
582
583/**
584 * Notify our clients (and manipulation) that a peer disconnected from
585 * us.
586 *
587 * @param n the peer that disconnected
588 */
589static void
590neighbours_disconnect_notification (struct NeighbourMapEntry *n)
591{
592 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
593 "Peer `%s' disconnected\n",
594 GNUNET_i2s (&n->id));
595 GST_manipulation_peer_disconnect (&n->id);
596 GST_clients_broadcast_disconnect (&n->id);
597}
598
599
600/**
601 * Notify transport clients that a neighbour peer changed its active
602 * address.
603 *
604 * @param peer identity of the peer
605 * @param address address possibly NULL if peer is not connected
606 * @param state current state this peer is in
607 * @param state_timeout timeout for the current state of the peer
608 * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
609 * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
610 */
611static void
612neighbours_changed_notification (const struct GNUNET_PeerIdentity *peer,
613 const struct GNUNET_HELLO_Address *address,
614 enum GNUNET_TRANSPORT_PeerState state,
615 struct GNUNET_TIME_Absolute state_timeout,
616 struct GNUNET_BANDWIDTH_Value32NBO
617 bandwidth_in,
618 struct GNUNET_BANDWIDTH_Value32NBO
619 bandwidth_out)
620{
621 (void) bandwidth_in;
622 (void) bandwidth_out;
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
625 GNUNET_i2s (peer),
626 GST_plugins_a2s (address),
627 GNUNET_TRANSPORT_ps2s (state),
628 GNUNET_STRINGS_absolute_time_to_string (state_timeout));
629 /* FIXME: include bandwidth in notification! */
630 GST_clients_broadcast_peer_notification (peer,
631 address,
632 state,
633 state_timeout);
634}
635
636
637/**
638 * Lookup a neighbour entry in the neighbours hash map.
639 *
640 * @param pid identity of the peer to look up
641 * @return the entry, NULL if there is no existing record
642 */
643static struct NeighbourMapEntry *
644lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
645{
646 if (NULL == neighbours)
647 return NULL;
648 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
649}
650
651
652/**
653 * Test if we're connected to the given peer.
654 *
655 * @param n neighbour entry of peer to test
656 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
657 */
658static int
659test_connected (struct NeighbourMapEntry *n)
660{
661 if (NULL == n)
662 return GNUNET_NO;
663 return GNUNET_TRANSPORT_is_connected (n->state);
664}
665
666
667/**
668 * We don't need a given neighbour address any more.
669 * Release its resources and give appropriate notifications
670 * to ATS and other subsystems.
671 *
672 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
673 */
674static void
675free_address (struct NeighbourAddress *na)
676{
677 if (GNUNET_YES == na->ats_active)
678 GST_validation_set_address_use (na->address,
679 GNUNET_NO);
680 if (NULL != na->address)
681 {
682 GST_ats_block_address (na->address,
683 na->session);
684 GNUNET_HELLO_address_free (na->address);
685 na->address = NULL;
686 }
687 na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
688 na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
689 na->ats_active = GNUNET_NO;
690 na->keep_alive_nonce = 0;
691 na->session = NULL;
692}
693
694
695/**
696 * Master task run for every neighbour. Performs all of the time-related
697 * activities (keep alive, send next message, disconnect if idle, finish
698 * clean up after disconnect).
699 *
700 * @param cls the `struct NeighbourMapEntry` for which we are running
701 */
702static void
703master_task (void *cls);
704
705
706/**
707 * Set net state and state timeout for this neighbour and notify monitoring
708 *
709 * @param n the respective neighbour
710 * @param s the new state
711 * @param timeout the new timeout
712 */
713static void
714set_state_and_timeout (struct NeighbourMapEntry *n,
715 enum GNUNET_TRANSPORT_PeerState s,
716 struct GNUNET_TIME_Absolute timeout)
717{
718 if (GNUNET_TRANSPORT_is_connected (s) &&
719 (! GNUNET_TRANSPORT_is_connected (n->state)))
720 {
721 neighbours_connect_notification (n);
722 GNUNET_STATISTICS_set (GST_stats,
723 gettext_noop ("# peers connected"),
724 ++neighbours_connected,
725 GNUNET_NO);
726 }
727 if ((! GNUNET_TRANSPORT_is_connected (s)) &&
728 GNUNET_TRANSPORT_is_connected (n->state))
729 {
730 GNUNET_STATISTICS_set (GST_stats,
731 gettext_noop ("# peers connected"),
732 --neighbours_connected,
733 GNUNET_NO);
734 neighbours_disconnect_notification (n);
735 }
736 n->state = s;
737 if ((timeout.abs_value_us < n->timeout.abs_value_us) &&
738 (NULL != n->task))
739 {
740 /* new timeout is earlier, reschedule master task */
741 GNUNET_SCHEDULER_cancel (n->task);
742 n->task = GNUNET_SCHEDULER_add_at (timeout,
743 &master_task,
744 n);
745 }
746 n->timeout = timeout;
747 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
748 "Neighbour `%s' changed state to %s with timeout %s\n",
749 GNUNET_i2s (&n->id),
750 GNUNET_TRANSPORT_ps2s (s),
751 GNUNET_STRINGS_absolute_time_to_string (timeout));
752 neighbours_changed_notification (&n->id,
753 n->primary_address.address,
754 n->state,
755 n->timeout,
756 n->primary_address.bandwidth_in,
757 n->primary_address.bandwidth_out);
758}
759
760
761/**
762 * Initialize the alternative address of a neighbour
763 *
764 * @param n the neighbour
765 * @param address address of the other peer, NULL if other peer
766 * connected to us
767 * @param session session to use (or NULL, in which case an
768 * address must be setup)
769 * @param bandwidth_in inbound quota to be used when connection is up
770 * @param bandwidth_out outbound quota to be used when connection is up
771 */
772static void
773set_alternative_address (struct NeighbourMapEntry *n,
774 const struct GNUNET_HELLO_Address *address,
775 struct GNUNET_ATS_Session *session,
776 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
777 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
778{
779 struct GNUNET_TRANSPORT_PluginFunctions *papi;
780
781 if (NULL == (papi = GST_plugins_find (address->transport_name)))
782 {
783 GNUNET_break (0);
784 return;
785 }
786 if (session == n->alternative_address.session)
787 {
788 n->alternative_address.bandwidth_in = bandwidth_in;
789 n->alternative_address.bandwidth_out = bandwidth_out;
790 return;
791 }
792 if (NULL != n->alternative_address.address)
793 {
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "Replacing existing alternative address with another one\n");
796 free_address (&n->alternative_address);
797 }
798 if (NULL == session)
799 session = papi->get_session (papi->cls,
800 address);
801 if (NULL == session)
802 {
803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804 "Failed to obtain new session for peer `%s' and address '%s'\n",
805 GNUNET_i2s (&address->peer),
806 GST_plugins_a2s (address));
807 GNUNET_STATISTICS_update (GST_stats,
808 gettext_noop ("# session creation failed"),
809 1,
810 GNUNET_NO);
811 return;
812 }
813 GST_ats_new_session (address,
814 session);
815 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
816 "Neighbour `%s' configured alternative address %s\n",
817 GNUNET_i2s (&n->id),
818 GST_plugins_a2s (address));
819
820 n->alternative_address.address = GNUNET_HELLO_address_copy (address);
821 n->alternative_address.bandwidth_in = bandwidth_in;
822 n->alternative_address.bandwidth_out = bandwidth_out;
823 n->alternative_address.session = session;
824 n->alternative_address.ats_active = GNUNET_NO;
825 n->alternative_address.keep_alive_nonce = 0;
826 GNUNET_assert (GNUNET_YES ==
827 GST_ats_is_known (n->alternative_address.address,
828 n->alternative_address.session));
829}
830
831
832/**
833 * Transmit a message using the current session of the given
834 * neighbour.
835 *
836 * @param n entry for the recipient
837 * @param msgbuf buffer to transmit
838 * @param msgbuf_size number of bytes in @a msgbuf buffer
839 * @param priority transmission priority
840 * @param timeout transmission timeout
841 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
842 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
843 * @param cont continuation to call when finished (can be NULL)
844 * @param cont_cls closure for @a cont
845 * @return timeout (copy of @a timeout or a calculated one if
846 * @a use_keepalive_timeout is #GNUNET_YES.
847 */
848static struct GNUNET_TIME_Relative
849send_with_session (struct NeighbourMapEntry *n,
850 const void *msgbuf,
851 size_t msgbuf_size,
852 uint32_t priority,
853 struct GNUNET_TIME_Relative timeout,
854 unsigned int use_keepalive_timeout,
855 GNUNET_TRANSPORT_TransmitContinuation cont,
856 void *cont_cls)
857{
858 struct GNUNET_TRANSPORT_PluginFunctions *papi;
859 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
860
861 GNUNET_assert (NULL != n->primary_address.session);
862 if ((((NULL == (papi = GST_plugins_find (
863 n->primary_address.address->transport_name))) ||
864 (-1 == papi->send (papi->cls,
865 n->primary_address.session,
866 msgbuf,
867 msgbuf_size,
868 priority,
869 (result = (GNUNET_NO == use_keepalive_timeout) ?
870 timeout :
871 GNUNET_TIME_relative_divide (
872 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
873 papi->
874 query_keepalive_factor (papi->cls))),
875 cont,
876 cont_cls)))) &&
877 (NULL != cont))
878 cont (cont_cls,
879 &n->id,
880 GNUNET_SYSERR,
881 msgbuf_size,
882 0);
883 GST_neighbours_notify_data_sent (n->primary_address.address,
884 n->primary_address.session,
885 msgbuf_size);
886 GNUNET_break (NULL != papi);
887 return result;
888}
889
890
891/**
892 * Clear the primary address of a neighbour since this address is not
893 * valid anymore and notify monitoring about it
894 *
895 * @param n the neighbour
896 */
897static void
898unset_primary_address (struct NeighbourMapEntry *n)
899{
900 /* Notify monitoring about change */
901 if (NULL == n->primary_address.address)
902 return;
903 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
904 "Disabling primary address\n");
905 neighbours_changed_notification (&n->id,
906 n->primary_address.address,
907 n->state,
908 n->timeout,
909 GNUNET_BANDWIDTH_value_init (0),
910 GNUNET_BANDWIDTH_value_init (0));
911 free_address (&n->primary_address);
912}
913
914
915/**
916 * Free a neighbour map entry.
917 *
918 * @param n entry to free
919 */
920static void
921free_neighbour (struct NeighbourMapEntry *n)
922{
923 struct MessageQueue *mq;
924
925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
926 "Freeing neighbour state of peer `%s'\n",
927 GNUNET_i2s (&n->id));
928 n->is_active = NULL; /* always free'd by its own continuation! */
929
930 /* fail messages currently in the queue */
931 while (NULL != (mq = n->messages_head))
932 {
933 GNUNET_CONTAINER_DLL_remove (n->messages_head,
934 n->messages_tail,
935 mq);
936 if (NULL != mq->cont)
937 mq->cont (mq->cont_cls,
938 GNUNET_SYSERR,
939 mq->message_buf_size,
940 0);
941 GNUNET_free (mq);
942 }
943 /* Mark peer as disconnected */
944 set_state_and_timeout (n,
945 GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED,
946 GNUNET_TIME_UNIT_FOREVER_ABS);
947 /* free addresses and mark as unused */
948 unset_primary_address (n);
949
950 if (NULL != n->alternative_address.address)
951 {
952 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953 "Cleaning up alternative address\n");
954 free_address (&n->alternative_address);
955 }
956 GNUNET_assert (GNUNET_YES ==
957 GNUNET_CONTAINER_multipeermap_remove (neighbours,
958 &n->id,
959 n));
960
961 /* Cancel address requests for this peer */
962 if (NULL != n->suggest_handle)
963 {
964 GNUNET_ATS_connectivity_suggest_cancel (n->suggest_handle);
965 n->suggest_handle = NULL;
966 }
967
968 /* Cancel the disconnect task */
969 if (NULL != n->delayed_disconnect_task)
970 {
971 GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task);
972 n->delayed_disconnect_task = NULL;
973 }
974
975 /* Cancel the master task */
976 if (NULL != n->task)
977 {
978 GNUNET_SCHEDULER_cancel (n->task);
979 n->task = NULL;
980 }
981 /* free rest of memory */
982 GNUNET_free (n);
983}
984
985
986/**
987 * Function called when the 'DISCONNECT' message has been sent by the
988 * plugin. Frees the neighbour --- if the entry still exists.
989 *
990 * @param cls NULL
991 * @param target identity of the neighbour that was disconnected
992 * @param result #GNUNET_OK if the disconnect got out successfully
993 * @param payload bytes payload
994 * @param physical bytes on wire
995 */
996static void
997send_disconnect_cont (void *cls,
998 const struct GNUNET_PeerIdentity *target,
999 int result,
1000 size_t payload,
1001 size_t physical)
1002{
1003 struct NeighbourMapEntry *n;
1004
1005 (void) cls;
1006 (void) result;
1007 (void) payload;
1008 (void) physical;
1009 n = lookup_neighbour (target);
1010 if (NULL == n)
1011 return; /* already gone */
1012 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
1013 return; /* have created a fresh entry since */
1014 if (NULL != n->task)
1015 GNUNET_SCHEDULER_cancel (n->task);
1016 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1017}
1018
1019
1020/**
1021 * Transmit a DISCONNECT message to the other peer.
1022 *
1023 * @param n neighbour to send DISCONNECT message.
1024 */
1025static void
1026send_disconnect (struct NeighbourMapEntry *n)
1027{
1028 struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg;
1029
1030 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1031 "Sending DISCONNECT message to peer `%4s'\n",
1032 GNUNET_i2s (&n->id));
1033 disconnect_msg.header.size = htons (sizeof(struct
1034 GNUNET_ATS_SessionDisconnectMessage));
1035 disconnect_msg.header.type =
1036 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1037 disconnect_msg.reserved = htonl (0);
1038 disconnect_msg.purpose.size =
1039 htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
1040 + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
1041 + sizeof(struct GNUNET_TIME_AbsoluteNBO));
1042 disconnect_msg.purpose.purpose =
1043 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1044 disconnect_msg.timestamp =
1045 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1046 disconnect_msg.public_key = GST_my_identity.public_key;
1047 GNUNET_assert (GNUNET_OK ==
1048 GNUNET_CRYPTO_eddsa_sign_ (&GST_my_private_key,
1049 &disconnect_msg.purpose,
1050 &disconnect_msg.signature));
1051
1052 (void) send_with_session (n,
1053 &disconnect_msg,
1054 sizeof(disconnect_msg),
1055 UINT32_MAX,
1056 GNUNET_TIME_UNIT_FOREVER_REL,
1057 GNUNET_NO,
1058 &send_disconnect_cont,
1059 NULL);
1060 GNUNET_STATISTICS_update (GST_stats,
1061 gettext_noop ("# DISCONNECT messages sent"),
1062 1,
1063 GNUNET_NO);
1064}
1065
1066
1067/**
1068 * Disconnect from the given neighbour, clean up the record.
1069 *
1070 * @param n neighbour to disconnect from
1071 */
1072static void
1073disconnect_neighbour (struct NeighbourMapEntry *n)
1074{
1075 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1076 "Disconnecting from peer %s in state %s\n",
1077 GNUNET_i2s (&n->id),
1078 GNUNET_TRANSPORT_ps2s (n->state));
1079 /* depending on state, notify neighbour and/or upper layers of this peer
1080 about disconnect */
1081 switch (n->state)
1082 {
1083 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1084 case GNUNET_TRANSPORT_PS_INIT_ATS:
1085 /* other peer is completely unaware of us, no need to send DISCONNECT */
1086 free_neighbour (n);
1087 return;
1088
1089 case GNUNET_TRANSPORT_PS_SYN_SENT:
1090 send_disconnect (n);
1091 set_state_and_timeout (n,
1092 GNUNET_TRANSPORT_PS_DISCONNECT,
1093 GNUNET_TIME_UNIT_FOREVER_ABS);
1094 break;
1095
1096 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
1097 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1098 free_neighbour (n);
1099 return;
1100
1101 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
1102 /* we DID ACK the other peer's request, must send DISCONNECT */
1103 send_disconnect (n);
1104 set_state_and_timeout (n,
1105 GNUNET_TRANSPORT_PS_DISCONNECT,
1106 GNUNET_TIME_UNIT_FOREVER_ABS);
1107 break;
1108
1109 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1110 case GNUNET_TRANSPORT_PS_CONNECTED:
1111 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1112 /* we are currently connected, need to send disconnect and do
1113 internal notifications and update statistics */
1114 send_disconnect (n);
1115 set_state_and_timeout (n,
1116 GNUNET_TRANSPORT_PS_DISCONNECT,
1117 GNUNET_TIME_UNIT_FOREVER_ABS);
1118 break;
1119
1120 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1121 /* Disconnecting while waiting for an ATS address to reconnect,
1122 * cannot send DISCONNECT */
1123 free_neighbour (n);
1124 return;
1125
1126 case GNUNET_TRANSPORT_PS_DISCONNECT:
1127 /* already disconnected, ignore */
1128 break;
1129
1130 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1131 /* already cleaned up, how did we get here!? */
1132 GNUNET_assert (0);
1133 break;
1134
1135 default:
1136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1137 "Unhandled state `%s'\n",
1138 GNUNET_TRANSPORT_ps2s (n->state));
1139 GNUNET_break (0);
1140 break;
1141 }
1142 /* schedule timeout to clean up */
1143 if (NULL != n->task)
1144 GNUNET_SCHEDULER_cancel (n->task);
1145 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1146 &master_task,
1147 n);
1148}
1149
1150
1151/**
1152 * Change the incoming quota for the given peer. Updates
1153 * our own receive rate and informs the neighbour about
1154 * the new quota.
1155 *
1156 * @param n neighbour entry to change quota for
1157 * @param quota new quota
1158 * @return #GNUNET_YES if @a n is still valid, #GNUNET_NO if
1159 * @a n was freed
1160 */
1161static int
1162set_incoming_quota (struct NeighbourMapEntry *n,
1163 struct GNUNET_BANDWIDTH_Value32NBO quota)
1164{
1165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1166 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
1167 ntohl (quota.value__), GNUNET_i2s (&n->id));
1168 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
1169 quota);
1170 if (0 != ntohl (quota.value__))
1171 {
1172 struct GNUNET_ATS_SessionQuotaMessage sqm;
1173
1174 sqm.header.size = htons (sizeof(struct GNUNET_ATS_SessionQuotaMessage));
1175 sqm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA);
1176 sqm.quota = quota.value__;
1177 if (NULL != n->primary_address.session)
1178 (void) send_with_session (n,
1179 &sqm,
1180 sizeof(sqm),
1181 UINT32_MAX - 1,
1182 GNUNET_TIME_UNIT_FOREVER_REL,
1183 GNUNET_NO,
1184 NULL, NULL);
1185 return GNUNET_YES;
1186 }
1187 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1188 "Disconnecting peer `%s' due to SET_QUOTA\n",
1189 GNUNET_i2s (&n->id));
1190 if (GNUNET_YES == test_connected (n))
1191 GNUNET_STATISTICS_update (GST_stats,
1192 gettext_noop ("# disconnects due to quota of 0"),
1193 1, GNUNET_NO);
1194 disconnect_neighbour (n);
1195 return GNUNET_NO;
1196}
1197
1198
1199/**
1200 * Initialize the primary address of a neighbour
1201 *
1202 * @param n the neighbour
1203 * @param address address of the other peer, NULL if other peer
1204 * connected to us
1205 * @param session session to use (or NULL, in which case an
1206 * address must be setup)
1207 * @param bandwidth_in inbound quota to be used when connection is up
1208 * @param bandwidth_out outbound quota to be used when connection is up
1209 */
1210static void
1211set_primary_address (struct NeighbourMapEntry *n,
1212 const struct GNUNET_HELLO_Address *address,
1213 struct GNUNET_ATS_Session *session,
1214 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1215 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
1216{
1217 if (session == n->primary_address.session)
1218 {
1219 GST_validation_set_address_use (n->primary_address.address,
1220 GNUNET_YES);
1221 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
1222 {
1223 n->primary_address.bandwidth_in = bandwidth_in;
1224 if (GNUNET_YES !=
1225 set_incoming_quota (n,
1226 bandwidth_in))
1227 return;
1228 }
1229 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
1230 {
1231 n->primary_address.bandwidth_out = bandwidth_out;
1232 send_outbound_quota_to_clients (n);
1233 }
1234 return;
1235 }
1236 if ((NULL != n->primary_address.address) &&
1237 (0 == GNUNET_HELLO_address_cmp (address,
1238 n->primary_address.address)))
1239 {
1240 GNUNET_break (0);
1241 return;
1242 }
1243 if (NULL == session)
1244 {
1245 GNUNET_break (0);
1246 GST_ats_block_address (address,
1247 session);
1248 return;
1249 }
1250 if (NULL != n->primary_address.address)
1251 {
1252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1253 "Replacing existing primary address with another one\n");
1254 free_address (&n->primary_address);
1255 }
1256 n->primary_address.address = GNUNET_HELLO_address_copy (address);
1257 n->primary_address.bandwidth_in = bandwidth_in;
1258 n->primary_address.bandwidth_out = bandwidth_out;
1259 n->primary_address.session = session;
1260 n->primary_address.keep_alive_nonce = 0;
1261 GNUNET_assert (GNUNET_YES ==
1262 GST_ats_is_known (n->primary_address.address,
1263 n->primary_address.session));
1264 /* subsystems about address use */
1265 GST_validation_set_address_use (n->primary_address.address,
1266 GNUNET_YES);
1267 if (GNUNET_YES !=
1268 set_incoming_quota (n,
1269 bandwidth_in))
1270 return;
1271 send_outbound_quota_to_clients (n);
1272 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1273 "Neighbour `%s' switched to address `%s'\n",
1274 GNUNET_i2s (&n->id),
1275 GST_plugins_a2s (address));
1276
1277 neighbours_changed_notification (&n->id,
1278 n->primary_address.address,
1279 n->state,
1280 n->timeout,
1281 n->primary_address.bandwidth_in,
1282 n->primary_address.bandwidth_out);
1283}
1284
1285
1286/**
1287 * We're done with our transmission attempt, continue processing.
1288 *
1289 * @param cls the `struct MessageQueue` of the message
1290 * @param receiver intended receiver
1291 * @param success whether it worked or not
1292 * @param size_payload bytes payload sent
1293 * @param physical bytes sent on wire
1294 */
1295static void
1296transmit_send_continuation (void *cls,
1297 const struct GNUNET_PeerIdentity *receiver,
1298 int success,
1299 size_t size_payload,
1300 size_t physical)
1301{
1302 struct MessageQueue *mq = cls;
1303 struct NeighbourMapEntry *n;
1304
1305 if (NULL == (n = lookup_neighbour (receiver)))
1306 {
1307 if (NULL != mq->cont)
1308 mq->cont (mq->cont_cls,
1309 GNUNET_SYSERR /* not connected */,
1310 size_payload,
1311 0);
1312 GNUNET_free (mq);
1313 return; /* disconnect or other error while transmitting, can happen */
1314 }
1315 if (n->is_active == mq)
1316 {
1317 /* this is still "our" neighbour, remove us from its queue
1318 and allow it to send the next message now */
1319 n->is_active = NULL;
1320 if (NULL != n->task)
1321 GNUNET_SCHEDULER_cancel (n->task);
1322 n->task = GNUNET_SCHEDULER_add_now (&master_task,
1323 n);
1324 }
1325 if (bytes_in_send_queue < mq->message_buf_size)
1326 {
1327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1328 "Bytes_in_send_queue `%llu', Message_size %u, result: %s, payload %u, on wire %u\n",
1329 bytes_in_send_queue,
1330 (unsigned int) mq->message_buf_size,
1331 (GNUNET_OK == success) ? "OK" : "FAIL",
1332 (unsigned int) size_payload,
1333 (unsigned int) physical);
1334 GNUNET_break (0);
1335 }
1336
1337 GNUNET_break (size_payload == mq->message_buf_size);
1338 bytes_in_send_queue -= mq->message_buf_size;
1339 GNUNET_STATISTICS_set (GST_stats,
1340 gettext_noop (
1341 "# bytes in message queue for other peers"),
1342 bytes_in_send_queue,
1343 GNUNET_NO);
1344 if (GNUNET_OK == success)
1345 GNUNET_STATISTICS_update (GST_stats,
1346 gettext_noop (
1347 "# messages transmitted to other peers"),
1348 1,
1349 GNUNET_NO);
1350 else
1351 GNUNET_STATISTICS_update (GST_stats,
1352 gettext_noop
1353 (
1354 "# transmission failures for messages to other peers"),
1355 1, GNUNET_NO);
1356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1357 "Sending message to `%s' of type %u with %u bytes was a %s\n",
1358 GNUNET_i2s (receiver),
1359 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1360 (unsigned int) mq->message_buf_size,
1361 (success == GNUNET_OK) ? "success" : "FAILURE");
1362 if (NULL != mq->cont)
1363 mq->cont (mq->cont_cls,
1364 success,
1365 size_payload,
1366 physical);
1367 GNUNET_free (mq);
1368}
1369
1370
1371/**
1372 * Check the message list for the given neighbour and if we can
1373 * send a message, do so. This function should only be called
1374 * if the connection is at least generally ready for transmission.
1375 * While we will only send one message at a time, no bandwidth
1376 * quota management is performed here. If a message was given to
1377 * the plugin, the continuation will automatically re-schedule
1378 * the 'master' task once the next message might be transmitted.
1379 *
1380 * @param n target peer for which to transmit
1381 */
1382static void
1383try_transmission_to_peer (struct NeighbourMapEntry *n)
1384{
1385 struct MessageQueue *mq;
1386 struct GNUNET_TIME_Relative timeout;
1387
1388 if (NULL == n->primary_address.address)
1389 {
1390 /* no address, why are we here? */
1391 GNUNET_break (0);
1392 return;
1393 }
1394 if ((0 == n->primary_address.address->address_length) &&
1395 (NULL == n->primary_address.session))
1396 {
1397 /* no address, why are we here? */
1398 GNUNET_break (0);
1399 return;
1400 }
1401 if (NULL != n->is_active)
1402 {
1403 /* transmission already pending */
1404 return;
1405 }
1406
1407 /* timeout messages from the queue that are past their due date */
1408 while (NULL != (mq = n->messages_head))
1409 {
1410 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1411 if (timeout.rel_value_us > 0)
1412 break;
1413 GNUNET_STATISTICS_update (GST_stats,
1414 gettext_noop (
1415 "# messages timed out while in transport queue"),
1416 1,
1417 GNUNET_NO);
1418 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1419 n->messages_tail,
1420 mq);
1421 n->is_active = mq;
1422 transmit_send_continuation (mq,
1423 &n->id,
1424 GNUNET_SYSERR,
1425 mq->message_buf_size,
1426 0); /* timeout */
1427 }
1428 if (NULL == mq)
1429 return; /* no more messages */
1430 if (NULL == n->primary_address.address)
1431 {
1432 /* transmit_send_continuation() caused us to drop session,
1433 can't try transmission anymore. */
1434 return;
1435 }
1436
1437
1438 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1439 n->messages_tail,
1440 mq);
1441 n->is_active = mq;
1442
1443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1444 "Giving message with %u bytes to plugin session %p\n",
1445 (unsigned int) mq->message_buf_size,
1446 n->primary_address.session);
1447 (void) send_with_session (n,
1448 mq->message_buf,
1449 mq->message_buf_size,
1450 0 /* priority */,
1451 timeout,
1452 GNUNET_NO,
1453 &transmit_send_continuation,
1454 mq);
1455}
1456
1457
1458/**
1459 * Send keepalive message to the neighbour. Must only be called
1460 * if we are on 'connected' state or while trying to switch addresses.
1461 * Will internally determine if a keepalive is truly needed (so can
1462 * always be called).
1463 *
1464 * @param n neighbour that went idle and needs a keepalive
1465 */
1466static void
1467send_keepalive (struct NeighbourMapEntry *n)
1468{
1469 struct GNUNET_ATS_SessionKeepAliveMessage m;
1470 struct GNUNET_TIME_Relative timeout;
1471 uint32_t nonce;
1472
1473 GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1474 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state));
1475 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1476 return; /* no keepalive needed at this time */
1477
1478 nonce = 0; /* 0 indicates 'not set' */
1479 while (0 == nonce)
1480 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1481 UINT32_MAX);
1482
1483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1484 "Sending KEEPALIVE to peer `%s' with nonce %u\n",
1485 GNUNET_i2s (&n->id),
1486 nonce);
1487 m.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1488 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1489 m.nonce = htonl (nonce);
1490
1491 timeout = send_with_session (n,
1492 &m,
1493 sizeof(m),
1494 UINT32_MAX /* priority */,
1495 GNUNET_TIME_UNIT_FOREVER_REL,
1496 GNUNET_YES,
1497 NULL, NULL);
1498 GNUNET_STATISTICS_update (GST_stats,
1499 gettext_noop ("# KEEPALIVES sent"),
1500 1,
1501 GNUNET_NO);
1502 n->primary_address.keep_alive_nonce = nonce;
1503 n->expect_latency_response = GNUNET_YES;
1504 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1505 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1506}
1507
1508
1509/**
1510 * Keep the connection to the given neighbour alive longer,
1511 * we received a KEEPALIVE (or equivalent); send a response.
1512 *
1513 * @param neighbour neighbour to keep alive (by sending keep alive response)
1514 * @param m the keep alive message containing the nonce to respond to
1515 */
1516void
1517GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1518 const struct GNUNET_MessageHeader *m)
1519{
1520 struct NeighbourMapEntry *n;
1521 const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in;
1522 struct GNUNET_ATS_SessionKeepAliveMessage msg;
1523
1524 if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1525 {
1526 GNUNET_break_op (0);
1527 return;
1528 }
1529
1530 msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1531 if (NULL == (n = lookup_neighbour (neighbour)))
1532 {
1533 GNUNET_STATISTICS_update (GST_stats,
1534 gettext_noop
1535 ("# KEEPALIVE messages discarded (peer unknown)"),
1536 1, GNUNET_NO);
1537 return;
1538 }
1539 if (NULL == n->primary_address.session)
1540 {
1541 GNUNET_STATISTICS_update (GST_stats,
1542 gettext_noop
1543 ("# KEEPALIVE messages discarded (no session)"),
1544 1, GNUNET_NO);
1545 return;
1546 }
1547
1548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1549 "Received KEEPALIVE request from peer `%s' with nonce %u\n",
1550 GNUNET_i2s (&n->id),
1551 ntohl (msg_in->nonce));
1552 GNUNET_STATISTICS_update (GST_stats,
1553 gettext_noop (
1554 "# KEEPALIVES received in good order"),
1555 1,
1556 GNUNET_NO);
1557
1558 /* send reply to allow neighbour to measure latency */
1559 msg.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1560 msg.header.type = htons (
1561 GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1562 msg.nonce = msg_in->nonce;
1563 (void) send_with_session (n,
1564 &msg,
1565 sizeof(struct GNUNET_ATS_SessionKeepAliveMessage),
1566 UINT32_MAX /* priority */,
1567 GNUNET_TIME_UNIT_FOREVER_REL,
1568 GNUNET_YES,
1569 NULL, NULL);
1570}
1571
1572
1573/**
1574 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1575 * latency to this peer. Pass the updated information (existing ats
1576 * plus calculated latency) to ATS.
1577 *
1578 * @param neighbour neighbour to keep alive
1579 * @param m the message containing the keep alive response
1580 */
1581void
1582GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1583 const struct GNUNET_MessageHeader *m)
1584{
1585 struct NeighbourMapEntry *n;
1586 const struct GNUNET_ATS_SessionKeepAliveMessage *msg;
1587 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1588 struct GNUNET_TIME_Relative latency;
1589
1590 if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1591 {
1592 GNUNET_break_op (0);
1593 return;
1594 }
1595
1596 msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1597 if (NULL == (n = lookup_neighbour (neighbour)))
1598 {
1599 GNUNET_STATISTICS_update (GST_stats,
1600 gettext_noop (
1601 "# KEEPALIVE_RESPONSEs discarded (not connected)"),
1602 1,
1603 GNUNET_NO);
1604 return;
1605 }
1606 if ((GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1607 (GNUNET_YES != n->expect_latency_response))
1608 {
1609 GNUNET_STATISTICS_update (GST_stats,
1610 gettext_noop (
1611 "# KEEPALIVE_RESPONSEs discarded (not expected)"),
1612 1,
1613 GNUNET_NO);
1614 return;
1615 }
1616 if (NULL == n->primary_address.address)
1617 {
1618 GNUNET_STATISTICS_update (GST_stats,
1619 gettext_noop (
1620 "# KEEPALIVE_RESPONSEs discarded (address changed)"),
1621 1,
1622 GNUNET_NO);
1623 return;
1624 }
1625 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1626 {
1627 if (0 == n->primary_address.keep_alive_nonce)
1628 GNUNET_STATISTICS_update (GST_stats,
1629 gettext_noop (
1630 "# KEEPALIVE_RESPONSEs discarded (no nonce)"),
1631 1,
1632 GNUNET_NO);
1633 else
1634 GNUNET_STATISTICS_update (GST_stats,
1635 gettext_noop (
1636 "# KEEPALIVE_RESPONSEs discarded (bad nonce)"),
1637 1,
1638 GNUNET_NO);
1639 return;
1640 }
1641 GNUNET_STATISTICS_update (GST_stats,
1642 gettext_noop (
1643 "# KEEPALIVE_RESPONSEs received (OK)"),
1644 1,
1645 GNUNET_NO);
1646
1647
1648 /* Update session timeout here */
1649 if (NULL != (papi = GST_plugins_find (
1650 n->primary_address.address->transport_name)))
1651 {
1652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1653 "Updating session for peer `%s' for session %p\n",
1654 GNUNET_i2s (&n->id),
1655 n->primary_address.session);
1656 papi->update_session_timeout (papi->cls,
1657 &n->id,
1658 n->primary_address.session);
1659 }
1660 else
1661 {
1662 GNUNET_break (0);
1663 }
1664
1665 n->primary_address.keep_alive_nonce = 0;
1666 n->expect_latency_response = GNUNET_NO;
1667 set_state_and_timeout (n,
1668 n->state,
1669 GNUNET_TIME_relative_to_absolute (
1670 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1671
1672 latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1674 "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n",
1675 GNUNET_i2s (&n->id),
1676 GNUNET_STRINGS_relative_time_to_string (latency,
1677 GNUNET_YES));
1678 GST_ats_update_delay (n->primary_address.address,
1679 GNUNET_TIME_relative_divide (latency,
1680 2));
1681}
1682
1683
1684/**
1685 * We have received a message from the given sender. How long should
1686 * we delay before receiving more? (Also used to keep the peer marked
1687 * as live).
1688 *
1689 * @param sender sender of the message
1690 * @param size size of the message
1691 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1692 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1693 * #GNUNET_SYSERR if the connection is not fully up yet
1694 * @return how long to wait before reading more from this sender
1695 */
1696struct GNUNET_TIME_Relative
1697GST_neighbours_calculate_receive_delay (const struct
1698 GNUNET_PeerIdentity *sender,
1699 ssize_t size,
1700 int *do_forward)
1701{
1702 struct NeighbourMapEntry *n;
1703 struct GNUNET_TIME_Relative ret;
1704
1705 if (NULL == neighbours)
1706 {
1707 *do_forward = GNUNET_NO;
1708 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1709 }
1710 if (NULL == (n = lookup_neighbour (sender)))
1711 {
1712 GNUNET_STATISTICS_update (GST_stats,
1713 gettext_noop (
1714 "# messages discarded due to lack of neighbour record"),
1715 1,
1716 GNUNET_NO);
1717 *do_forward = GNUNET_NO;
1718 return GNUNET_TIME_UNIT_ZERO;
1719 }
1720 if (! test_connected (n))
1721 {
1722 *do_forward = GNUNET_SYSERR;
1723 return GNUNET_TIME_UNIT_ZERO;
1724 }
1725 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1726 {
1727 n->quota_violation_count++;
1728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1729 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1730 n->in_tracker.available_bytes_per_s__,
1731 n->quota_violation_count);
1732 /* Discount 32k per violation */
1733 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1734 }
1735 else
1736 {
1737 if (n->quota_violation_count > 0)
1738 {
1739 /* try to add 32k back */
1740 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1741 n->quota_violation_count--;
1742 }
1743 }
1744 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1745 {
1746 GNUNET_STATISTICS_update (GST_stats,
1747 gettext_noop
1748 ("# bandwidth quota violations by other peers"),
1749 1, GNUNET_NO);
1750 *do_forward = GNUNET_NO;
1751 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1752 }
1753 *do_forward = GNUNET_YES;
1754 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1755 if (ret.rel_value_us > 0)
1756 {
1757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1758 "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n",
1759 (long long) n->in_tracker.consumption_since_last_update__,
1760 (unsigned int) n->in_tracker.available_bytes_per_s__,
1761 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1762 GNUNET_STATISTICS_update (GST_stats,
1763 gettext_noop ("# ms throttling suggested"),
1764 (int64_t) ret.rel_value_us / 1000LL,
1765 GNUNET_NO);
1766 }
1767 return ret;
1768}
1769
1770
1771/**
1772 * Transmit a message to the given target using the active connection.
1773 *
1774 * @param target destination
1775 * @param msg message to send
1776 * @param msg_size number of bytes in msg
1777 * @param timeout when to fail with timeout
1778 * @param cont function to call when done
1779 * @param cont_cls closure for @a cont
1780 */
1781void
1782GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
1783 const void *msg,
1784 size_t msg_size,
1785 struct GNUNET_TIME_Relative timeout,
1786 GST_NeighbourSendContinuation cont,
1787 void *cont_cls)
1788{
1789 struct NeighbourMapEntry *n;
1790 struct MessageQueue *mq;
1791
1792 /* All ove these cases should never happen; they are all API violations.
1793 But we check anyway, just to be sure. */
1794 if (NULL == (n = lookup_neighbour (target)))
1795 {
1796 GNUNET_break (0);
1797 if (NULL != cont)
1798 cont (cont_cls,
1799 GNUNET_SYSERR,
1800 msg_size,
1801 0);
1802 return;
1803 }
1804 if (GNUNET_YES != test_connected (n))
1805 {
1806 GNUNET_break (0);
1807 if (NULL != cont)
1808 cont (cont_cls,
1809 GNUNET_SYSERR,
1810 msg_size,
1811 0);
1812 return;
1813 }
1814 bytes_in_send_queue += msg_size;
1815 GNUNET_STATISTICS_set (GST_stats,
1816 gettext_noop
1817 ("# bytes in message queue for other peers"),
1818 bytes_in_send_queue, GNUNET_NO);
1819 mq = GNUNET_malloc (sizeof(struct MessageQueue) + msg_size);
1820 mq->cont = cont;
1821 mq->cont_cls = cont_cls;
1822 GNUNET_memcpy (&mq[1], msg, msg_size);
1823 mq->message_buf = (const char *) &mq[1];
1824 mq->message_buf_size = msg_size;
1825 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1826
1827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1828 "Enqueueing %u bytes to send to peer %s\n",
1829 (unsigned int) msg_size,
1830 GNUNET_i2s (target));
1831 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
1832 n->messages_tail,
1833 mq);
1834 if (NULL != n->task)
1835 GNUNET_SCHEDULER_cancel (n->task);
1836 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1837}
1838
1839
1840/**
1841 * Continuation called from our attempt to transmitted our
1842 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1843 * target. Continue processing based on the @a result. Specifically,
1844 * if we failed to transmit, discard the address we used.
1845 *
1846 * @param cls NULL
1847 * @param target which peer received the transmission
1848 * @param result #GNUNET_OK if sending worked
1849 * @param size_payload how many bytes of payload were sent (ignored)
1850 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1851 */
1852static void
1853send_session_syn_cont (void *cls,
1854 const struct GNUNET_PeerIdentity *target,
1855 int result,
1856 size_t size_payload,
1857 size_t size_on_wire)
1858{
1859 struct NeighbourMapEntry *n;
1860
1861 (void) cls;
1862 (void) size_payload;
1863 (void) size_on_wire;
1864 n = lookup_neighbour (target);
1865 if (NULL == n)
1866 {
1867 /* SYN continuation was called after neighbor was freed,
1868 * for example due to a time out for the state or the session
1869 * used was already terminated: nothing to do here... */
1870 return;
1871 }
1872
1873 if ((GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1874 (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1875 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1876 {
1877 /* SYN continuation was called after neighbor changed state,
1878 * for example due to a time out for the state or the session
1879 * used was already terminated: nothing to do here... */
1880 return;
1881 }
1882 if (GNUNET_OK == result)
1883 return;
1884
1885 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1886 _ ("Failed to send SYN message to peer `%s'\n"),
1887 GNUNET_i2s (target));
1888 switch (n->state)
1889 {
1890 case GNUNET_TRANSPORT_PS_SYN_SENT:
1891 /* Remove address and request an additional one */
1892 unset_primary_address (n);
1893 set_state_and_timeout (n,
1894 GNUNET_TRANSPORT_PS_INIT_ATS,
1895 GNUNET_TIME_relative_to_absolute (
1896 FAST_RECONNECT_TIMEOUT));
1897 break;
1898
1899 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1900 /* Remove address and request an additional one */
1901 unset_primary_address (n);
1902 set_state_and_timeout (n,
1903 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1904 GNUNET_TIME_relative_to_absolute (
1905 ATS_RESPONSE_TIMEOUT));
1906 break;
1907
1908 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1909 /* Remove address and request and go back to primary address */
1910 GNUNET_STATISTICS_update (GST_stats,
1911 gettext_noop (
1912 "# Failed attempts to switch addresses (failed to send SYN CONT)"),
1913 1,
1914 GNUNET_NO);
1915 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1916 "Switch failed, cleaning up alternative address\n");
1917 free_address (&n->alternative_address);
1918 set_state_and_timeout (n,
1919 GNUNET_TRANSPORT_PS_CONNECTED,
1920 GNUNET_TIME_relative_to_absolute (
1921 ATS_RESPONSE_TIMEOUT));
1922 break;
1923
1924 default:
1925 disconnect_neighbour (n);
1926 break;
1927 }
1928}
1929
1930
1931/**
1932 * Send a SYN message via the given address.
1933 *
1934 * @param na address to use
1935 */
1936static void
1937send_syn (struct NeighbourAddress *na)
1938{
1939 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1940 struct TransportSynMessage connect_msg;
1941 struct NeighbourMapEntry *n;
1942
1943 GNUNET_assert (NULL != na->session);
1944 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1945 "Sending SYN message to peer `%s' at %s\n",
1946 GNUNET_i2s (&na->address->peer),
1947 GST_plugins_a2s (na->address));
1948
1949 papi = GST_plugins_find (na->address->transport_name);
1950 GNUNET_assert (NULL != papi);
1951 GNUNET_STATISTICS_update (GST_stats,
1952 gettext_noop
1953 ("# SYN messages sent"),
1954 1, GNUNET_NO);
1955 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1956 connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
1957 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1958 connect_msg.reserved = htonl (0);
1959 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1960 if (-1 ==
1961 papi->send (papi->cls,
1962 na->session,
1963 (const char *) &connect_msg,
1964 sizeof(struct TransportSynMessage),
1965 UINT_MAX,
1966 SETUP_CONNECTION_TIMEOUT,
1967 &send_session_syn_cont, NULL))
1968 {
1969 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1970 _ ("Failed to transmit SYN message to %s\n"),
1971 GST_plugins_a2s (na->address));
1972 n = lookup_neighbour (&na->address->peer);
1973 if (NULL == n)
1974 {
1975 GNUNET_break (0);
1976 return;
1977 }
1978 switch (n->state)
1979 {
1980 case GNUNET_TRANSPORT_PS_SYN_SENT:
1981 /* Remove address and request and additional one */
1982 GNUNET_assert (na == &n->primary_address);
1983 unset_primary_address (n);
1984 set_state_and_timeout (n,
1985 GNUNET_TRANSPORT_PS_INIT_ATS,
1986 GNUNET_TIME_relative_to_absolute (
1987 FAST_RECONNECT_TIMEOUT));
1988 /* Hard failure to send the SYN message with this address:
1989 Destroy address and session */
1990 break;
1991
1992 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1993 /* Remove address and request an additional one */
1994 GNUNET_assert (na == &n->primary_address);
1995 unset_primary_address (n);
1996 set_state_and_timeout (n,
1997 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1998 GNUNET_TIME_relative_to_absolute (
1999 ATS_RESPONSE_TIMEOUT));
2000 break;
2001
2002 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2003 GNUNET_assert (na == &n->alternative_address);
2004 GNUNET_STATISTICS_update (GST_stats,
2005 gettext_noop (
2006 "# Failed attempts to switch addresses (failed to send SYN)"),
2007 1,
2008 GNUNET_NO);
2009 /* Remove address and request an additional one */
2010 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2011 "Switch failed, cleaning up alternative address\n");
2012 free_address (&n->alternative_address);
2013 set_state_and_timeout (n,
2014 GNUNET_TRANSPORT_PS_CONNECTED,
2015 GNUNET_TIME_relative_to_absolute (
2016 ATS_RESPONSE_TIMEOUT));
2017 break;
2018
2019 default:
2020 GNUNET_break (0);
2021 disconnect_neighbour (n);
2022 break;
2023 }
2024 return;
2025 }
2026 GST_neighbours_notify_data_sent (na->address,
2027 na->session,
2028 sizeof(struct TransportSynMessage));
2029}
2030
2031
2032/**
2033 * Continuation called from our attempt to transmitted our
2034 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
2035 * target. Continue processing based on the @a result. Specifically,
2036 * if we failed to transmit, discard the address we used.
2037 *
2038 * @param cls NULL
2039 * @param target which peer received the transmission
2040 * @param result #GNUNET_OK if sending worked
2041 * @param size_payload how many bytes of payload were sent (ignored)
2042 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
2043 */
2044static void
2045send_session_syn_ack_cont (void *cls,
2046 const struct GNUNET_PeerIdentity *target,
2047 int result,
2048 size_t size_payload,
2049 size_t size_on_wire)
2050{
2051 struct NeighbourMapEntry *n;
2052
2053 (void) cls;
2054 (void) size_payload;
2055 (void) size_on_wire;
2056 n = lookup_neighbour (target);
2057 if (NULL == n)
2058 {
2059 /* SYN_ACK continuation was called after neighbor was freed,
2060 * for example due to a time out for the state or the session
2061 * used was already terminated: nothing to do here... */
2062 return;
2063 }
2064
2065 if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2066 {
2067 /* SYN_ACK continuation was called after neighbor changed state,
2068 * for example due to a time out for the state or the session
2069 * used was already terminated: nothing to do here... */
2070 return;
2071 }
2072 if (GNUNET_OK == result)
2073 return;
2074
2075 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2076 _ (
2077 "Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2078 GNUNET_i2s (target),
2079 GST_plugins_a2s (n->primary_address.address));
2080
2081 /* Remove address and request and additional one */
2082 /* FIXME: what if the neighbour's primary address
2083 changed in the meantime? Might want to instead
2084 pass "something" around in closure to be sure. */
2085 unset_primary_address (n);
2086 n->ack_state = ACK_SEND_SYN_ACK;
2087 set_state_and_timeout (n,
2088 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2089 GNUNET_TIME_relative_to_absolute (
2090 ATS_RESPONSE_TIMEOUT));
2091}
2092
2093
2094/**
2095 * Send a SYN_ACK message via the given address.
2096 *
2097 * @param na address and session to use
2098 * @param timestamp timestamp to use for the ACK message
2099 * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2100 */
2101static void
2102send_syn_ack_message (struct NeighbourAddress *na,
2103 struct GNUNET_TIME_Absolute timestamp)
2104{
2105 const struct GNUNET_HELLO_Address *address = na->address;
2106 struct GNUNET_ATS_Session *session = na->session;
2107 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2108 struct TransportSynMessage connect_msg;
2109 struct NeighbourMapEntry *n;
2110
2111 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2112 "Sending SYN_ACK to peer `%s'\n",
2113 GNUNET_i2s (&address->peer));
2114
2115 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2116 {
2117 GNUNET_break (0);
2118 return;
2119 }
2120 if (NULL == session)
2121 session = papi->get_session (papi->cls,
2122 address);
2123 if (NULL == session)
2124 {
2125 GNUNET_break (0);
2126 return;
2127 }
2128 GST_ats_new_session (address,
2129 session);
2130 GNUNET_STATISTICS_update (GST_stats,
2131 gettext_noop
2132 ("# SYN_ACK messages sent"),
2133 1, GNUNET_NO);
2134 connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
2135 connect_msg.header.type = htons (
2136 GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2137 connect_msg.reserved = htonl (0);
2138 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
2139
2140 if (GNUNET_SYSERR ==
2141 papi->send (papi->cls,
2142 session,
2143 (const char *) &connect_msg,
2144 sizeof(struct TransportSynMessage),
2145 UINT_MAX,
2146 GNUNET_TIME_UNIT_FOREVER_REL,
2147 &send_session_syn_ack_cont, NULL))
2148 {
2149 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2150 _ ("Failed to transmit SYN_ACK message to %s\n"),
2151 GST_plugins_a2s (address));
2152
2153 n = lookup_neighbour (&address->peer);
2154 if (NULL == n)
2155 {
2156 GNUNET_break (0);
2157 return;
2158 }
2159 /* Remove address and request and additional one */
2160 unset_primary_address (n);
2161 n->ack_state = ACK_SEND_SYN_ACK;
2162 set_state_and_timeout (n,
2163 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2164 GNUNET_TIME_relative_to_absolute (
2165 ATS_RESPONSE_TIMEOUT));
2166 return;
2167 }
2168}
2169
2170
2171/**
2172 * Function called by the bandwidth tracker for a peer whenever
2173 * the tracker's state changed such that we need to recalculate
2174 * the delay for flow control. We calculate the latest delay
2175 * and inform the plugin (if applicable).
2176 *
2177 * @param cls the `struct NeighbourMapEntry` to update calculations for
2178 */
2179static void
2180inbound_bw_tracker_update (void *cls)
2181{
2182 struct NeighbourMapEntry *n = cls;
2183 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2184 struct GNUNET_TIME_Relative delay;
2185 int do_forward;
2186
2187 if (NULL == n->primary_address.address)
2188 return; /* not active, ignore */
2189 papi = GST_plugins_find (n->primary_address.address->transport_name);
2190 GNUNET_assert (NULL != papi);
2191 if (NULL == papi->update_inbound_delay)
2192 return;
2193 delay = GST_neighbours_calculate_receive_delay (&n->id,
2194 0,
2195 &do_forward);
2196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197 "New inbound delay for peer `%s' is %llu ms\n",
2198 GNUNET_i2s (&n->id),
2199 (unsigned long long) delay.rel_value_us / 1000LL);
2200 if (NULL == n->primary_address.session)
2201 return;
2202 papi->update_inbound_delay (papi->cls,
2203 &n->id,
2204 n->primary_address.session,
2205 delay);
2206}
2207
2208
2209/**
2210 * Create a fresh entry in the neighbour map for the given peer
2211 *
2212 * @param peer peer to create an entry for
2213 * @return new neighbour map entry
2214 */
2215static struct NeighbourMapEntry *
2216setup_neighbour (const struct GNUNET_PeerIdentity *peer)
2217{
2218 struct NeighbourMapEntry *n;
2219
2220 if (0 ==
2221 memcmp (&GST_my_identity,
2222 peer,
2223 sizeof(struct GNUNET_PeerIdentity)))
2224 {
2225 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2226 "Cowardly refusing to consider myself my neighbour!\n");
2227 return NULL;
2228 }
2229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2230 "Creating new neighbour entry for `%s'\n",
2231 GNUNET_i2s (peer));
2232 n = GNUNET_new (struct NeighbourMapEntry);
2233 n->id = *peer;
2234 n->ack_state = ACK_UNDEFINED;
2235 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2236 n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2237 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
2238 &inbound_bw_tracker_update,
2239 n,
2240 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2241 MAX_BANDWIDTH_CARRY_S);
2242 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
2243 set_state_and_timeout (n,
2244 GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2245 GNUNET_TIME_UNIT_FOREVER_ABS);
2246 GNUNET_assert (GNUNET_OK ==
2247 GNUNET_CONTAINER_multipeermap_put (neighbours,
2248 &n->id,
2249 n,
2250 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2251 n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect,
2252 peer,
2253 0);
2254
2255 return n;
2256}
2257
2258
2259/**
2260 * Entry in a DLL we use to keep track of pending blacklist checks.
2261 */
2262struct BlacklistCheckSwitchContext
2263{
2264 /**
2265 * DLL prev pointer.
2266 */
2267 struct BlacklistCheckSwitchContext *prev;
2268
2269 /**
2270 * DLL next pointer.
2271 */
2272 struct BlacklistCheckSwitchContext *next;
2273
2274 /**
2275 * Handle to the blacklist check we are performing.
2276 */
2277 struct GST_BlacklistCheck *blc;
2278
2279 /**
2280 * Inbound bandwidth that was assigned to @e address.
2281 */
2282 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2283
2284 /**
2285 * Outbound bandwidth that was assigned to @e address.
2286 */
2287 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2288};
2289
2290
2291/**
2292 * We received a 'SYN' message from the other peer.
2293 * Consider switching to it.
2294 *
2295 * @param message possibly a `struct TransportSynMessage` (check format)
2296 * @param peer identity of the peer to switch the address for
2297 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2298 */
2299int
2300GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message,
2301 const struct GNUNET_PeerIdentity *peer)
2302{
2303 const struct TransportSynMessage *scm;
2304 struct NeighbourMapEntry *n;
2305 struct GNUNET_TIME_Absolute ts;
2306
2307 if (ntohs (message->size) != sizeof(struct TransportSynMessage))
2308 {
2309 GNUNET_break_op (0);
2310 return GNUNET_SYSERR;
2311 }
2312 GNUNET_STATISTICS_update (GST_stats,
2313 gettext_noop
2314 ("# SYN messages received"),
2315 1, GNUNET_NO);
2316 if (NULL == neighbours)
2317 {
2318 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2319 _ (
2320 "SYN request from peer `%s' ignored due impending shutdown\n"),
2321 GNUNET_i2s (peer));
2322 return GNUNET_OK; /* we're shutting down */
2323 }
2324 scm = (const struct TransportSynMessage *) message;
2325 GNUNET_break_op (0 == ntohl (scm->reserved));
2326 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2327 if (0 ==
2328 memcmp (&GST_my_identity,
2329 peer,
2330 sizeof(struct GNUNET_PeerIdentity)))
2331 {
2332 /* loopback connection-to-self, ignore */
2333 return GNUNET_SYSERR;
2334 }
2335 n = lookup_neighbour (peer);
2336 if (NULL == n)
2337 {
2338 /* This is a new neighbour and set to not connected */
2339 n = setup_neighbour (peer);
2340 GNUNET_assert (NULL != n);
2341 }
2342
2343 /* Remember this SYN message in neighbour */
2344 n->ack_state = ACK_SEND_SYN_ACK;
2345 n->connect_ack_timestamp = ts;
2346
2347 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2348 "Received SYN for peer `%s' in state %s/%s\n",
2349 GNUNET_i2s (peer),
2350 GNUNET_TRANSPORT_ps2s (n->state),
2351 print_ack_state (n->ack_state));
2352
2353 switch (n->state)
2354 {
2355 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2356 /* Request an address from ATS to send SYN_ACK to this peer */
2357 set_state_and_timeout (n,
2358 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2359 GNUNET_TIME_relative_to_absolute (
2360 ATS_RESPONSE_TIMEOUT));
2361 break;
2362
2363 case GNUNET_TRANSPORT_PS_INIT_ATS:
2364 /* SYN message takes priority over us asking ATS for address:
2365 * Wait for ATS to suggest an address and send SYN_ACK */
2366 set_state_and_timeout (n,
2367 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2368 GNUNET_TIME_relative_to_absolute (
2369 ATS_RESPONSE_TIMEOUT));
2370 break;
2371
2372 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2373 /* We already wait for an address to send an SYN_ACK */
2374 break;
2375
2376 case GNUNET_TRANSPORT_PS_SYN_SENT:
2377 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2378 /* Send ACK immediately */
2379 n->ack_state = ACK_SEND_ACK;
2380 send_syn_ack_message (&n->primary_address,
2381 ts);
2382 break;
2383
2384 case GNUNET_TRANSPORT_PS_CONNECTED:
2385 /* we are already connected and can thus send the ACK immediately */
2386 GNUNET_assert (NULL != n->primary_address.address);
2387 GNUNET_assert (NULL != n->primary_address.session);
2388 n->ack_state = ACK_SEND_ACK;
2389 send_syn_ack_message (&n->primary_address,
2390 ts);
2391 break;
2392
2393 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2394 /* We wait for ATS address suggestion */
2395 break;
2396
2397 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2398 /* We received a SYN message while waiting for a SYN_ACK in fast
2399 * reconnect. Send SYN_ACK immediately */
2400 n->ack_state = ACK_SEND_ACK;
2401 send_syn_ack_message (&n->primary_address,
2402 n->connect_ack_timestamp);
2403 break;
2404
2405 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2406 /* We are already connected and can thus send the ACK immediately;
2407 still, it can never hurt to have an alternative address, so also
2408 tell ATS about it */
2409 GNUNET_assert (NULL != n->primary_address.address);
2410 GNUNET_assert (NULL != n->primary_address.session);
2411 n->ack_state = ACK_SEND_ACK;
2412 send_syn_ack_message (&n->primary_address,
2413 ts);
2414 break;
2415
2416 case GNUNET_TRANSPORT_PS_DISCONNECT:
2417 /* Get rid of remains and re-try */
2418 free_neighbour (n);
2419 n = setup_neighbour (peer);
2420 GNUNET_assert (NULL != n);
2421 /* Remember the SYN time stamp for ACK message */
2422 n->ack_state = ACK_SEND_SYN_ACK;
2423 n->connect_ack_timestamp = ts;
2424 /* Request an address for the peer */
2425 set_state_and_timeout (n,
2426 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2427 GNUNET_TIME_relative_to_absolute (
2428 ATS_RESPONSE_TIMEOUT));
2429 break;
2430
2431 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2432 /* should not be possible */
2433 GNUNET_assert (0);
2434 break;
2435
2436 default:
2437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2438 "Unhandled state `%s'\n",
2439 GNUNET_TRANSPORT_ps2s (n->state));
2440 GNUNET_break (0);
2441 return GNUNET_SYSERR;
2442 }
2443 return GNUNET_OK;
2444}
2445
2446
2447/**
2448 * Check if the given @a address is the same that we are already
2449 * using for the respective neighbour. If so, update the bandwidth
2450 * assignment and possibly the session and return #GNUNET_OK.
2451 * If the new address is different from what the neighbour is
2452 * using right now, return #GNUNET_NO.
2453 *
2454 * @param address address of the other peer,
2455 * @param session session to use or NULL if transport should initiate a session
2456 * @param bandwidth_in inbound quota to be used when connection is up,
2457 * 0 to disconnect from peer
2458 * @param bandwidth_out outbound quota to be used when connection is up,
2459 * 0 to disconnect from peer
2460 * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2461 * #GNUNET_NO if more extensive changes are required (address changed)
2462 */
2463static int
2464try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address,
2465 struct GNUNET_ATS_Session *session,
2466 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2467 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2468{
2469 struct NeighbourMapEntry *n;
2470
2471 n = lookup_neighbour (&address->peer);
2472 if ((NULL == n) ||
2473 (NULL == n->primary_address.address) ||
2474 (0 != GNUNET_HELLO_address_cmp (address,
2475 n->primary_address.address)))
2476 return GNUNET_NO;
2477 /* We are not really switching addresses, but merely adjusting
2478 session and/or bandwidth, can do fast ATS update! */
2479 if (session != n->primary_address.session)
2480 {
2481 /* switch to a different session, but keeping same address; could
2482 happen if there is a 2nd inbound connection */
2483 n->primary_address.session = session;
2484 GNUNET_assert (GNUNET_YES ==
2485 GST_ats_is_known (n->primary_address.address,
2486 n->primary_address.session));
2487 }
2488 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2489 {
2490 n->primary_address.bandwidth_in = bandwidth_in;
2491 if (GNUNET_YES !=
2492 set_incoming_quota (n,
2493 bandwidth_in))
2494 return GNUNET_NO;
2495 }
2496 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2497 {
2498 n->primary_address.bandwidth_out = bandwidth_out;
2499 send_outbound_quota_to_clients (n);
2500 }
2501 return GNUNET_OK;
2502}
2503
2504
2505/**
2506 * We've been asked to switch addresses, and just now got the result
2507 * from the blacklist check to see if this is allowed.
2508 *
2509 * @param cls the `struct BlacklistCheckSwitchContext` with
2510 * the information about the future address
2511 * @param peer the peer we may switch addresses on
2512 * @param address address associated with the request
2513 * @param session session associated with the request
2514 * @param result #GNUNET_OK if the connection is allowed,
2515 * #GNUNET_NO if not,
2516 * #GNUNET_SYSERR if operation was aborted
2517 */
2518static void
2519switch_address_bl_check_cont (void *cls,
2520 const struct GNUNET_PeerIdentity *peer,
2521 const struct GNUNET_HELLO_Address *address,
2522 struct GNUNET_ATS_Session *session,
2523 int result)
2524{
2525 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2526 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2527 struct NeighbourMapEntry *n;
2528
2529 if (GNUNET_SYSERR == result)
2530 goto cleanup;
2531
2532 papi = GST_plugins_find (address->transport_name);
2533 if (NULL == papi)
2534 {
2535 /* This can happen during shutdown. */
2536 goto cleanup;
2537 }
2538
2539 if (GNUNET_NO == result)
2540 {
2541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2542 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2543 GST_plugins_a2s (address),
2544 session,
2545 GNUNET_i2s (peer));
2546 GNUNET_STATISTICS_update (GST_stats,
2547 "# ATS suggestions ignored (blacklist denied)",
2548 1,
2549 GNUNET_NO);
2550 if (NULL != session)
2551 papi->disconnect_session (papi->cls,
2552 session);
2553 if (GNUNET_YES !=
2554 GNUNET_HELLO_address_check_option (address,
2555 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2556 GST_ats_block_address (address,
2557 NULL);
2558 goto cleanup;
2559 }
2560
2561
2562 if (NULL == session)
2563 {
2564 /* need to create a session, ATS only gave us an address */
2565 session = papi->get_session (papi->cls,
2566 address);
2567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568 "Obtained new session for peer `%s' and address '%s': %p\n",
2569 GNUNET_i2s (&address->peer),
2570 GST_plugins_a2s (address),
2571 session);
2572 if (NULL != session)
2573 GST_ats_new_session (address,
2574 session);
2575 }
2576 if (NULL == session)
2577 {
2578 /* session creation failed, bad!, fail! */
2579 GNUNET_STATISTICS_update (GST_stats,
2580 "# ATS suggestions ignored (failed to create session)",
2581 1,
2582 GNUNET_NO);
2583 /* No session could be obtained, remove blacklist check and clean up */
2584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2585 "Failed to obtain new session for peer `%s' and address '%s'\n",
2586 GNUNET_i2s (&address->peer),
2587 GST_plugins_a2s (address));
2588 GST_ats_block_address (address,
2589 session);
2590 goto cleanup;
2591 }
2592
2593 /* We did this check already before going into blacklist, but
2594 it is theoretically possible that the situation changed in
2595 the meantime, hence we check again here */
2596 if (GNUNET_OK ==
2597 try_run_fast_ats_update (address,
2598 session,
2599 blc_ctx->bandwidth_in,
2600 blc_ctx->bandwidth_out))
2601 goto cleanup; /* was just a minor update, we're done */
2602
2603 /* check if we also need to setup the neighbour entry */
2604 if (NULL == (n = lookup_neighbour (peer)))
2605 {
2606 n = setup_neighbour (peer);
2607 if (NULL == n)
2608 {
2609 /* not sure how this can happen... */
2610 GNUNET_break (0);
2611 goto cleanup;
2612 }
2613 n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2614 }
2615
2616 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2617 "Peer `%s' switches to address `%s'\n",
2618 GNUNET_i2s (&address->peer),
2619 GST_plugins_a2s (address));
2620
2621 switch (n->state)
2622 {
2623 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2624 GNUNET_break (0);
2625 GST_ats_block_address (address,
2626 session);
2627 free_neighbour (n);
2628 return;
2629
2630 case GNUNET_TRANSPORT_PS_INIT_ATS:
2631 /* We requested an address and ATS suggests one:
2632 * set primary address and send SYN message*/
2633 set_primary_address (n,
2634 address,
2635 session,
2636 blc_ctx->bandwidth_in,
2637 blc_ctx->bandwidth_out);
2638 if (ACK_SEND_SYN_ACK == n->ack_state)
2639 {
2640 /* Send pending SYN_ACK message */
2641 n->ack_state = ACK_SEND_ACK;
2642 send_syn_ack_message (&n->primary_address,
2643 n->connect_ack_timestamp);
2644 }
2645 set_state_and_timeout (n,
2646 GNUNET_TRANSPORT_PS_SYN_SENT,
2647 GNUNET_TIME_relative_to_absolute (
2648 SETUP_CONNECTION_TIMEOUT));
2649 send_syn (&n->primary_address);
2650 break;
2651
2652 case GNUNET_TRANSPORT_PS_SYN_SENT:
2653 /* ATS suggested a new address while waiting for an SYN_ACK:
2654 * Switch and send new SYN */
2655 /* ATS suggests a different address, switch again */
2656 set_primary_address (n,
2657 address,
2658 session,
2659 blc_ctx->bandwidth_in,
2660 blc_ctx->bandwidth_out);
2661 if (ACK_SEND_SYN_ACK == n->ack_state)
2662 {
2663 /* Send pending SYN_ACK message */
2664 n->ack_state = ACK_SEND_ACK;
2665 send_syn_ack_message (&n->primary_address,
2666 n->connect_ack_timestamp);
2667 }
2668 set_state_and_timeout (n,
2669 GNUNET_TRANSPORT_PS_SYN_SENT,
2670 GNUNET_TIME_relative_to_absolute (
2671 SETUP_CONNECTION_TIMEOUT));
2672 send_syn (&n->primary_address);
2673 break;
2674
2675 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2676 /* We requested an address and ATS suggests one:
2677 * set primary address and send SYN_ACK message*/
2678 set_primary_address (n,
2679 address,
2680 session,
2681 blc_ctx->bandwidth_in,
2682 blc_ctx->bandwidth_out);
2683 /* Send an ACK message as a response to the SYN msg */
2684 set_state_and_timeout (n,
2685 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2686 GNUNET_TIME_relative_to_absolute (
2687 SETUP_CONNECTION_TIMEOUT));
2688 send_syn_ack_message (&n->primary_address,
2689 n->connect_ack_timestamp);
2690 if ((ACK_SEND_SYN_ACK == n->ack_state) ||
2691 (ACK_UNDEFINED == n->ack_state))
2692 n->ack_state = ACK_SEND_ACK;
2693 break;
2694
2695 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2696 /* ATS asks us to switch while we were trying to connect; switch to new
2697 address and check blacklist again */
2698 if ((ACK_SEND_SYN_ACK == n->ack_state))
2699 {
2700 n->ack_state = ACK_SEND_ACK;
2701 send_syn_ack_message (&n->primary_address,
2702 n->connect_ack_timestamp);
2703 }
2704 set_primary_address (n,
2705 address,
2706 session,
2707 blc_ctx->bandwidth_in,
2708 blc_ctx->bandwidth_out);
2709 set_state_and_timeout (n,
2710 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2711 GNUNET_TIME_relative_to_absolute (
2712 SETUP_CONNECTION_TIMEOUT));
2713 break;
2714
2715 case GNUNET_TRANSPORT_PS_CONNECTED:
2716 GNUNET_assert (NULL != n->primary_address.address);
2717 GNUNET_assert (NULL != n->primary_address.session);
2718 GNUNET_break (n->primary_address.session != session);
2719 /* ATS asks us to switch a life connection; see if we can get
2720 a SYN_ACK on it before we actually do this! */
2721 set_alternative_address (n,
2722 address,
2723 session,
2724 blc_ctx->bandwidth_in,
2725 blc_ctx->bandwidth_out);
2726 set_state_and_timeout (n,
2727 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2728 GNUNET_TIME_relative_to_absolute (
2729 SETUP_CONNECTION_TIMEOUT));
2730 GNUNET_STATISTICS_update (GST_stats,
2731 gettext_noop ("# Attempts to switch addresses"),
2732 1,
2733 GNUNET_NO);
2734 send_syn (&n->alternative_address);
2735 break;
2736
2737 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2738 set_primary_address (n,
2739 address,
2740 session,
2741 blc_ctx->bandwidth_in,
2742 blc_ctx->bandwidth_out);
2743 if (ACK_SEND_SYN_ACK == n->ack_state)
2744 {
2745 /* Send pending SYN_ACK message */
2746 n->ack_state = ACK_SEND_ACK;
2747 send_syn_ack_message (&n->primary_address,
2748 n->connect_ack_timestamp);
2749 }
2750 set_state_and_timeout (n,
2751 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2752 GNUNET_TIME_relative_to_absolute (
2753 FAST_RECONNECT_TIMEOUT));
2754 send_syn (&n->primary_address);
2755 break;
2756
2757 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2758 /* ATS asks us to switch while we were trying to reconnect; switch to new
2759 address and send SYN again */
2760 set_primary_address (n,
2761 address,
2762 session,
2763 blc_ctx->bandwidth_in,
2764 blc_ctx->bandwidth_out);
2765 set_state_and_timeout (n,
2766 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2767 GNUNET_TIME_relative_to_absolute (
2768 FAST_RECONNECT_TIMEOUT));
2769 send_syn (&n->primary_address);
2770 break;
2771
2772 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2773 if ((0 == GNUNET_HELLO_address_cmp (n->primary_address.address,
2774 address)) &&
2775 (n->primary_address.session == session))
2776 {
2777 /* ATS switches back to still-active session */
2778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2779 "ATS double-switched, cleaning up alternative address\n");
2780 free_address (&n->alternative_address);
2781 set_state_and_timeout (n,
2782 GNUNET_TRANSPORT_PS_CONNECTED,
2783 n->timeout);
2784 break;
2785 }
2786 /* ATS asks us to switch a life connection, send */
2787 set_alternative_address (n,
2788 address,
2789 session,
2790 blc_ctx->bandwidth_in,
2791 blc_ctx->bandwidth_out);
2792 set_state_and_timeout (n,
2793 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2794 GNUNET_TIME_relative_to_absolute (
2795 SETUP_CONNECTION_TIMEOUT));
2796 send_syn (&n->alternative_address);
2797 break;
2798
2799 case GNUNET_TRANSPORT_PS_DISCONNECT:
2800 /* not going to switch addresses while disconnecting */
2801 GNUNET_STATISTICS_update (GST_stats,
2802 "# ATS suggestion ignored (disconnecting)",
2803 1,
2804 GNUNET_NO);
2805 return;
2806
2807 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2808 GNUNET_assert (0);
2809 break;
2810
2811 default:
2812 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2813 "Unhandled state `%s'\n",
2814 GNUNET_TRANSPORT_ps2s (n->state));
2815 GNUNET_break (0);
2816 break;
2817 }
2818cleanup:
2819 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2820 pending_bc_tail,
2821 blc_ctx);
2822 GNUNET_free (blc_ctx);
2823}
2824
2825
2826/**
2827 * For the given peer, switch to this address.
2828 *
2829 * Before accepting this addresses and actively using it, a blacklist check
2830 * is performed.
2831 *
2832 * If any check fails or the suggestion can somehow not be followed, we
2833 * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2834 * could not be satisfied and force ATS to do something else.
2835 *
2836 * @param address address of the other peer,
2837 * @param session session to use or NULL if transport should initiate a session
2838 * @param bandwidth_in inbound quota to be used when connection is up,
2839 * 0 to disconnect from peer
2840 * @param bandwidth_out outbound quota to be used when connection is up,
2841 * 0 to disconnect from peer
2842 */
2843void
2844GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address,
2845 struct GNUNET_ATS_Session *session,
2846 struct GNUNET_BANDWIDTH_Value32NBO
2847 bandwidth_in,
2848 struct GNUNET_BANDWIDTH_Value32NBO
2849 bandwidth_out)
2850{
2851 struct GST_BlacklistCheck *blc;
2852 struct BlacklistCheckSwitchContext *blc_ctx;
2853
2854 GNUNET_assert (NULL != address->transport_name);
2855 if (GNUNET_OK ==
2856 try_run_fast_ats_update (address,
2857 session,
2858 bandwidth_in,
2859 bandwidth_out))
2860 return;
2861
2862 /* Check if plugin is available */
2863 if (NULL == (GST_plugins_find (address->transport_name)))
2864 {
2865 /* we don't have the plugin for this address */
2866 GNUNET_break (0);
2867 GST_ats_block_address (address,
2868 session);
2869 return;
2870 }
2871 if ((NULL == session) &&
2872 (GNUNET_HELLO_address_check_option (address,
2873 GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2874 {
2875 /* This is a inbound address and we do not have a session to use! */
2876 GNUNET_break (0);
2877 GST_ats_block_address (address,
2878 session);
2879 return;
2880 }
2881
2882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2883 "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2884 GST_plugins_a2s (address),
2885 GNUNET_i2s (&address->peer),
2886 (unsigned int) ntohl (bandwidth_in.value__),
2887 (unsigned int) ntohl (bandwidth_out.value__));
2888
2889 /* Perform blacklist check */
2890 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2891 blc_ctx->bandwidth_in = bandwidth_in;
2892 blc_ctx->bandwidth_out = bandwidth_out;
2893 GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2894 pending_bc_tail,
2895 blc_ctx);
2896 if (NULL != (blc = GST_blacklist_test_allowed (&address->peer,
2897 address->transport_name,
2898 &switch_address_bl_check_cont,
2899 blc_ctx,
2900 address,
2901 session)))
2902 {
2903 blc_ctx->blc = blc;
2904 }
2905}
2906
2907
2908/**
2909 * Function called to send network utilization data to ATS for
2910 * each active connection.
2911 *
2912 * @param cls NULL
2913 * @param key peer we send utilization data for
2914 * @param value the `struct NeighbourMapEntry *` with data to send
2915 * @return #GNUNET_OK (continue to iterate)
2916 */
2917static int
2918send_utilization_data (void *cls,
2919 const struct GNUNET_PeerIdentity *key,
2920 void *value)
2921{
2922 struct NeighbourMapEntry *n = value;
2923 uint32_t bps_in;
2924 uint32_t bps_out;
2925 struct GNUNET_TIME_Relative delta;
2926
2927 (void) cls;
2928 if ((GNUNET_YES != test_connected (n)) ||
2929 (NULL == n->primary_address.address))
2930 return GNUNET_OK;
2931 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2932 GNUNET_TIME_absolute_get ());
2933 bps_in = 0;
2934 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2935 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv)
2936 / (delta.rel_value_us);
2937 bps_out = 0;
2938 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2939 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2940
2941 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2942 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2943 GNUNET_i2s (key),
2944 bps_in,
2945 bps_out);
2946 GST_ats_update_utilization (n->primary_address.address,
2947 bps_in,
2948 bps_out);
2949 n->util_total_bytes_recv = 0;
2950 n->util_total_bytes_sent = 0;
2951 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2952 return GNUNET_OK;
2953}
2954
2955
2956/**
2957 * Task transmitting utilization in a regular interval
2958 *
2959 * @param cls the `struct NeighbourMapEntry` for which we are running
2960 */
2961static void
2962utilization_transmission (void *cls)
2963{
2964 (void) cls;
2965 util_transmission_tk = NULL;
2966 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
2967 &send_utilization_data,
2968 NULL);
2969 util_transmission_tk
2970 = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2971 &utilization_transmission,
2972 NULL);
2973}
2974
2975
2976/**
2977 * Track information about data we received from the
2978 * given address (used to notify ATS about our utilization
2979 * of allocated resources).
2980 *
2981 * @param address the address we got data from
2982 * @param message the message we received (really only the size is used)
2983 */
2984void
2985GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address,
2986 const struct GNUNET_MessageHeader *message)
2987{
2988 struct NeighbourMapEntry *n;
2989
2990 n = lookup_neighbour (&address->peer);
2991 if (NULL == n)
2992 return;
2993 n->util_total_bytes_recv += ntohs (message->size);
2994}
2995
2996
2997/**
2998 * Track information about data we transmitted using the given @a
2999 * address and @a session (used to notify ATS about our utilization of
3000 * allocated resources).
3001 *
3002 * @param address the address we transmitted data to
3003 * @param session session we used to transmit data
3004 * @param message the message we sent (really only the size is used)
3005 */
3006void
3007GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address,
3008 struct GNUNET_ATS_Session *session,
3009 size_t size)
3010{
3011 struct NeighbourMapEntry *n;
3012
3013 n = lookup_neighbour (&address->peer);
3014 if (NULL == n)
3015 return;
3016 if (n->primary_address.session != session)
3017 return;
3018 n->util_total_bytes_sent += size;
3019}
3020
3021
3022/**
3023 * Master task run for every neighbour. Performs all of the time-related
3024 * activities (keep alive, send next message, disconnect if idle, finish
3025 * clean up after disconnect).
3026 *
3027 * @param cls the 'struct NeighbourMapEntry' for which we are running
3028 */
3029static void
3030master_task (void *cls)
3031{
3032 struct NeighbourMapEntry *n = cls;
3033 struct GNUNET_TIME_Relative delay;
3034
3035 n->task = NULL;
3036 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3037 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3038 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
3039 GNUNET_i2s (&n->id),
3040 GNUNET_TRANSPORT_ps2s (n->state),
3041 GNUNET_STRINGS_relative_time_to_string (delay,
3042 GNUNET_YES));
3043 switch (n->state)
3044 {
3045 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3046 /* invalid state for master task, clean up */
3047 GNUNET_break (0);
3048 free_neighbour (n);
3049 return;
3050
3051 case GNUNET_TRANSPORT_PS_INIT_ATS:
3052 if (0 == delay.rel_value_us)
3053 {
3054 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3055 "Connection to `%s' timed out waiting for ATS to provide address\n",
3056 GNUNET_i2s (&n->id));
3057 free_neighbour (n);
3058 return;
3059 }
3060 break;
3061
3062 case GNUNET_TRANSPORT_PS_SYN_SENT:
3063 if (0 == delay.rel_value_us)
3064 {
3065 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3066 "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
3067 GNUNET_i2s (&n->id));
3068 /* Remove address and request and additional one */
3069 unset_primary_address (n);
3070 set_state_and_timeout (n,
3071 GNUNET_TRANSPORT_PS_INIT_ATS,
3072 GNUNET_TIME_relative_to_absolute (
3073 ATS_RESPONSE_TIMEOUT));
3074 return;
3075 }
3076 break;
3077
3078 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3079 if (0 == delay.rel_value_us)
3080 {
3081 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3082 "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
3083 GNUNET_i2s (&n->id));
3084 free_neighbour (n);
3085 return;
3086 }
3087 break;
3088
3089 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3090 if (0 == delay.rel_value_us)
3091 {
3092 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3093 "Connection to `%s' timed out waiting for other peer to send ACK\n",
3094 GNUNET_i2s (&n->id));
3095 disconnect_neighbour (n);
3096 return;
3097 }
3098 break;
3099
3100 case GNUNET_TRANSPORT_PS_CONNECTED:
3101 if (0 == delay.rel_value_us)
3102 {
3103 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3104 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3105 GNUNET_i2s (&n->id));
3106 disconnect_neighbour (n);
3107 return;
3108 }
3109 try_transmission_to_peer (n);
3110 send_keepalive (n);
3111 break;
3112
3113 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3114 if (0 == delay.rel_value_us)
3115 {
3116 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3117 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3118 GNUNET_i2s (&n->id));
3119 disconnect_neighbour (n);
3120 return;
3121 }
3122 break;
3123
3124 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3125 if (0 == delay.rel_value_us)
3126 {
3127 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3128 "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3129 GNUNET_i2s (&n->id));
3130 disconnect_neighbour (n);
3131 return;
3132 }
3133 break;
3134
3135 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3136 if (0 == delay.rel_value_us)
3137 {
3138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3139 "Switch failed, cleaning up alternative address\n");
3140 free_address (&n->alternative_address);
3141 set_state_and_timeout (n,
3142 GNUNET_TRANSPORT_PS_CONNECTED,
3143 GNUNET_TIME_relative_to_absolute (
3144 SETUP_CONNECTION_TIMEOUT));
3145 }
3146 try_transmission_to_peer (n);
3147 send_keepalive (n);
3148 break;
3149
3150 case GNUNET_TRANSPORT_PS_DISCONNECT:
3151 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3152 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3153 GNUNET_i2s (&n->id));
3154 free_neighbour (n);
3155 return;
3156
3157 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3158 /* how did we get here!? */
3159 GNUNET_assert (0);
3160 break;
3161
3162 default:
3163 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3164 "Unhandled state `%s'\n",
3165 GNUNET_TRANSPORT_ps2s (n->state));
3166 GNUNET_break (0);
3167 break;
3168 }
3169 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3170 if ((GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3171 (GNUNET_TRANSPORT_PS_CONNECTED == n->state))
3172 {
3173 /* if we are *now* in one of the two states, we're sending
3174 keep alive messages, so we need to consider the keepalive
3175 delay, not just the connection timeout */
3176 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (
3177 n->keep_alive_time),
3178 delay);
3179 }
3180 if (NULL == n->task)
3181 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3182 &master_task,
3183 n);
3184}
3185
3186
3187/**
3188 * Send a ACK message to the neighbour to confirm that we
3189 * got its SYN_ACK.
3190 *
3191 * @param n neighbour to send the ACK to
3192 */
3193static void
3194send_session_ack_message (struct NeighbourMapEntry *n)
3195{
3196 struct GNUNET_MessageHeader msg;
3197
3198 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3199 "Sending ACK message to peer `%s'\n",
3200 GNUNET_i2s (&n->id));
3201
3202 msg.size = htons (sizeof(struct GNUNET_MessageHeader));
3203 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3204 (void) send_with_session (n,
3205 &msg,
3206 sizeof(struct GNUNET_MessageHeader),
3207 UINT32_MAX,
3208 GNUNET_TIME_UNIT_FOREVER_REL,
3209 GNUNET_NO,
3210 NULL, NULL);
3211}
3212
3213
3214/**
3215 * We received a 'SESSION_SYN_ACK' message from the other peer.
3216 * Consider switching to it.
3217 *
3218 * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3219 * @param peer identity of the peer to switch the address for
3220 * @param address address of the other peer, NULL if other peer
3221 * connected to us
3222 * @param session session to use (or NULL)
3223 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3224 */
3225int
3226GST_neighbours_handle_session_syn_ack (const struct
3227 GNUNET_MessageHeader *message,
3228 const struct
3229 GNUNET_HELLO_Address *address,
3230 struct GNUNET_ATS_Session *session)
3231{
3232 const struct TransportSynMessage *scm;
3233 struct GNUNET_TIME_Absolute ts;
3234 struct NeighbourMapEntry *n;
3235
3236 (void) session;
3237 if (ntohs (message->size) != sizeof(struct TransportSynMessage))
3238 {
3239 GNUNET_break_op (0);
3240 return GNUNET_SYSERR;
3241 }
3242 GNUNET_STATISTICS_update (GST_stats,
3243 gettext_noop
3244 ("# SYN_ACK messages received"),
3245 1, GNUNET_NO);
3246 scm = (const struct TransportSynMessage *) message;
3247 GNUNET_break_op (ntohl (scm->reserved) == 0);
3248 if (NULL == (n = lookup_neighbour (&address->peer)))
3249 {
3250 GNUNET_STATISTICS_update (GST_stats,
3251 gettext_noop
3252 ("# unexpected SYN_ACK messages (no peer)"),
3253 1, GNUNET_NO);
3254 return GNUNET_SYSERR;
3255 }
3256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3257 "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3258 GNUNET_i2s (&address->peer),
3259 GNUNET_TRANSPORT_ps2s (n->state),
3260 print_ack_state (n->ack_state));
3261 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3262 switch (n->state)
3263 {
3264 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3265 GNUNET_break (0);
3266 free_neighbour (n);
3267 return GNUNET_SYSERR;
3268
3269 case GNUNET_TRANSPORT_PS_INIT_ATS:
3270 GNUNET_STATISTICS_update (GST_stats,
3271 gettext_noop (
3272 "# unexpected SYN_ACK messages (not ready)"),
3273 1,
3274 GNUNET_NO);
3275 break;
3276
3277 case GNUNET_TRANSPORT_PS_SYN_SENT:
3278 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3279 {
3280 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3281 "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3282 return GNUNET_OK;
3283 }
3284 set_state_and_timeout (n,
3285 GNUNET_TRANSPORT_PS_CONNECTED,
3286 GNUNET_TIME_relative_to_absolute (
3287 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3288 set_primary_address (n,
3289 n->primary_address.address,
3290 n->primary_address.session,
3291 n->primary_address.bandwidth_in,
3292 n->primary_address.bandwidth_out);
3293 send_session_ack_message (n);
3294 break;
3295
3296 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3297 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3298 GNUNET_STATISTICS_update (GST_stats,
3299 gettext_noop (
3300 "# unexpected SYN_ACK messages (not ready)"),
3301 1,
3302 GNUNET_NO);
3303 break;
3304
3305 case GNUNET_TRANSPORT_PS_CONNECTED:
3306 /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3307 send_session_ack_message (n);
3308 break;
3309
3310 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3311 /* we didn't expect any SYN_ACK, as we are waiting for ATS
3312 to give us a new address... */
3313 GNUNET_STATISTICS_update (GST_stats,
3314 gettext_noop (
3315 "# unexpected SYN_ACK messages (waiting on ATS)"),
3316 1,
3317 GNUNET_NO);
3318 break;
3319
3320 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3321 /* Reconnecting with new address address worked; go back to connected! */
3322 set_state_and_timeout (n,
3323 GNUNET_TRANSPORT_PS_CONNECTED,
3324 GNUNET_TIME_relative_to_absolute (
3325 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3326 send_session_ack_message (n);
3327 break;
3328
3329 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3330 /* new address worked; adopt it and go back to connected! */
3331 set_state_and_timeout (n,
3332 GNUNET_TRANSPORT_PS_CONNECTED,
3333 GNUNET_TIME_relative_to_absolute (
3334 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3335 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3336
3337 /* Set primary addresses */
3338 set_primary_address (n,
3339 n->alternative_address.address,
3340 n->alternative_address.session,
3341 n->alternative_address.bandwidth_in,
3342 n->alternative_address.bandwidth_out);
3343 GNUNET_STATISTICS_update (GST_stats,
3344 gettext_noop (
3345 "# Successful attempts to switch addresses"),
3346 1,
3347 GNUNET_NO);
3348
3349 GNUNET_HELLO_address_free (n->alternative_address.address);
3350 memset (&n->alternative_address,
3351 0,
3352 sizeof(n->alternative_address));
3353 send_session_ack_message (n);
3354 break;
3355
3356 case GNUNET_TRANSPORT_PS_DISCONNECT:
3357 GNUNET_STATISTICS_update (GST_stats,
3358 gettext_noop
3359 ("# unexpected SYN_ACK messages (disconnecting)"),
3360 1, GNUNET_NO);
3361 return GNUNET_SYSERR;
3362
3363 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3364 GNUNET_assert (0);
3365 break;
3366
3367 default:
3368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3369 "Unhandled state `%s'\n",
3370 GNUNET_TRANSPORT_ps2s (n->state));
3371 GNUNET_break (0);
3372 return GNUNET_SYSERR;
3373 }
3374 return GNUNET_OK;
3375}
3376
3377
3378/**
3379 * A session was terminated. Take note; if needed, try to get
3380 * an alternative address from ATS.
3381 *
3382 * @param peer identity of the peer where the session died
3383 * @param session session that is gone
3384 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3385 * this session was not in use
3386 */
3387int
3388GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3389 struct GNUNET_ATS_Session *session)
3390{
3391 struct NeighbourMapEntry *n;
3392
3393 if (NULL == (n = lookup_neighbour (peer)))
3394 return GNUNET_NO; /* can't affect us */
3395 if (session != n->primary_address.session)
3396 {
3397 /* Free alternative address */
3398 if (session == n->alternative_address.session)
3399 {
3400 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3401 set_state_and_timeout (n,
3402 GNUNET_TRANSPORT_PS_CONNECTED,
3403 n->timeout);
3404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3405 "Session died, cleaning up alternative address\n");
3406 free_address (&n->alternative_address);
3407 }
3408 return GNUNET_NO; /* doesn't affect us further */
3409 }
3410
3411 n->expect_latency_response = GNUNET_NO;
3412 /* The session for neighbour's primary address died */
3413 switch (n->state)
3414 {
3415 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3416 GNUNET_break (0);
3417 free_neighbour (n);
3418 return GNUNET_YES;
3419
3420 case GNUNET_TRANSPORT_PS_INIT_ATS:
3421 GNUNET_break (0);
3422 free_neighbour (n);
3423 return GNUNET_YES;
3424
3425 case GNUNET_TRANSPORT_PS_SYN_SENT:
3426 /* The session used to send the SYN terminated:
3427 * this implies a connect error*/
3428 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3429 "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n",
3430 GST_plugins_a2s (n->primary_address.address),
3431 n->primary_address.session);
3432
3433 /* Destroy the address since it cannot be used */
3434 unset_primary_address (n);
3435 set_state_and_timeout (n,
3436 GNUNET_TRANSPORT_PS_INIT_ATS,
3437 GNUNET_TIME_relative_to_absolute (
3438 ATS_RESPONSE_TIMEOUT));
3439 break;
3440
3441 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3442 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3443 /* error on inbound session; free neighbour entirely */
3444 free_neighbour (n);
3445 return GNUNET_YES;
3446
3447 case GNUNET_TRANSPORT_PS_CONNECTED:
3448 /* Our primary connection died, try a fast reconnect */
3449 unset_primary_address (n);
3450 set_state_and_timeout (n,
3451 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3452 GNUNET_TIME_relative_to_absolute (
3453 ATS_RESPONSE_TIMEOUT));
3454 break;
3455
3456 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3457 /* we don't have an address, how can it go down? */
3458 GNUNET_break (0);
3459 break;
3460
3461 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3462 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3463 "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n",
3464 GST_plugins_a2s (n->primary_address.address),
3465 n->primary_address.session);
3466 /* Destroy the address since it cannot be used */
3467 unset_primary_address (n);
3468 set_state_and_timeout (n,
3469 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3470 GNUNET_TIME_relative_to_absolute (
3471 ATS_RESPONSE_TIMEOUT));
3472 break;
3473
3474 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3475 /* primary went down while we were waiting for SYN_ACK on secondary;
3476 secondary as primary */
3477
3478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3479 "Connection `%s' %p to peer `%s' was terminated while switching, "
3480 "switching to alternative address `%s' %p\n",
3481 GST_plugins_a2s (n->primary_address.address),
3482 n->primary_address.session,
3483 GNUNET_i2s (peer),
3484 GST_plugins_a2s (n->alternative_address.address),
3485 n->alternative_address.session);
3486
3487 /* Destroy the inbound address since it cannot be used */
3488 free_address (&n->primary_address);
3489 n->primary_address = n->alternative_address;
3490 GNUNET_assert (GNUNET_YES ==
3491 GST_ats_is_known (n->primary_address.address,
3492 n->primary_address.session));
3493 memset (&n->alternative_address,
3494 0,
3495 sizeof(struct NeighbourAddress));
3496 set_state_and_timeout (n,
3497 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3498 GNUNET_TIME_relative_to_absolute (
3499 FAST_RECONNECT_TIMEOUT));
3500 break;
3501
3502 case GNUNET_TRANSPORT_PS_DISCONNECT:
3503 unset_primary_address (n);
3504 break;
3505
3506 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3507 /* neighbour was freed and plugins told to terminate session */
3508 return GNUNET_NO;
3509
3510 default:
3511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3512 "Unhandled state `%s'\n",
3513 GNUNET_TRANSPORT_ps2s (n->state));
3514 GNUNET_break (0);
3515 break;
3516 }
3517 if (NULL != n->task)
3518 GNUNET_SCHEDULER_cancel (n->task);
3519 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3520 return GNUNET_YES;
3521}
3522
3523
3524/**
3525 * We received a 'ACK' message from the other peer.
3526 * If we sent a 'SYN_ACK' last, this means we are now
3527 * connected. Otherwise, do nothing.
3528 *
3529 * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3530 * @param address address of the other peer
3531 * @param session session to use (or NULL)
3532 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3533 */
3534int
3535GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3536 const struct GNUNET_HELLO_Address *address,
3537 struct GNUNET_ATS_Session *session)
3538{
3539 struct NeighbourMapEntry *n;
3540
3541 (void) session;
3542 if (ntohs (message->size) != sizeof(struct GNUNET_MessageHeader))
3543 {
3544 GNUNET_break_op (0);
3545 return GNUNET_SYSERR;
3546 }
3547 GNUNET_STATISTICS_update (GST_stats,
3548 gettext_noop ("# ACK messages received"),
3549 1,
3550 GNUNET_NO);
3551 if (NULL == (n = lookup_neighbour (&address->peer)))
3552 {
3553 GNUNET_break_op (0);
3554 return GNUNET_SYSERR;
3555 }
3556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3557 "Received ACK for peer `%s' in state %s/%s\n",
3558 GNUNET_i2s (&address->peer),
3559 GNUNET_TRANSPORT_ps2s (n->state),
3560 print_ack_state (n->ack_state));
3561
3562 /* Check if we are in a plausible state for having sent
3563 a SYN_ACK. If not, return, otherwise break.
3564
3565 The remote peers sends a ACK as a response for a SYN_ACK
3566 message.
3567
3568 We expect a ACK:
3569 - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3570 now wait for the ACK to finally be connected
3571 - If we sent a SYN_ACK to this peer before */if (((GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3572 (ACK_SEND_ACK != n->ack_state)) ||
3573 (NULL == n->primary_address.address))
3574 {
3575 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3576 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3577 GNUNET_i2s (&address->peer),
3578 GNUNET_TRANSPORT_ps2s (n->state),
3579 print_ack_state (n->ack_state));
3580
3581 GNUNET_STATISTICS_update (GST_stats,
3582 gettext_noop ("# unexpected ACK messages"),
3583 1,
3584 GNUNET_NO);
3585 return GNUNET_OK;
3586 }
3587 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3588 {
3589 /* We tried to switch addresses while being connect. We explicitly wait
3590 * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3591 * so we do not want to set the address as in use! */
3592 return GNUNET_OK;
3593 }
3594 set_state_and_timeout (n,
3595 GNUNET_TRANSPORT_PS_CONNECTED,
3596 GNUNET_TIME_relative_to_absolute (
3597 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3598
3599 if (NULL == n->primary_address.address)
3600 {
3601 /* See issue #3693.
3602 * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3603 * really means we did try (and succeed) to send a SYN and are waiting for
3604 * an ACK.
3605 * That suggests that the primary_address used to be non-NULL, but maybe it
3606 * got reset to NULL without the state being changed appropriately?
3607 */GNUNET_break (0);
3608 return GNUNET_OK;
3609 }
3610
3611 /* Reset backoff for primary address */
3612 GST_ats_block_reset (n->primary_address.address,
3613 n->primary_address.session);
3614 return GNUNET_OK;
3615}
3616
3617
3618/**
3619 * Test if we're connected to the given peer.
3620 *
3621 * @param target peer to test
3622 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3623 */
3624int
3625GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3626{
3627 return test_connected (lookup_neighbour (target));
3628}
3629
3630
3631/**
3632 * Task to asynchronously run #free_neighbour().
3633 *
3634 * @param cls the `struct NeighbourMapEntry` to free
3635 */
3636static void
3637delayed_disconnect (void *cls)
3638{
3639 struct NeighbourMapEntry *n = cls;
3640
3641 n->delayed_disconnect_task = NULL;
3642 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3643 "Disconnecting by request from peer %s\n",
3644 GNUNET_i2s (&n->id));
3645 free_neighbour (n);
3646}
3647
3648
3649/**
3650 * We received a quota message from the given peer,
3651 * validate and process.
3652 *
3653 * @param peer sender of the message
3654 * @param msg the quota message
3655 */
3656void
3657GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer,
3658 const struct GNUNET_MessageHeader *msg)
3659{
3660 struct NeighbourMapEntry *n;
3661 const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3662 struct GNUNET_BANDWIDTH_Value32NBO last;
3663
3664 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3665 "Received QUOTA message from peer `%s'\n",
3666 GNUNET_i2s (peer));
3667 if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionQuotaMessage))
3668 {
3669 GNUNET_break_op (0);
3670 GNUNET_STATISTICS_update (GST_stats,
3671 gettext_noop (
3672 "# quota messages ignored (malformed)"),
3673 1,
3674 GNUNET_NO);
3675 return;
3676 }
3677 GNUNET_STATISTICS_update (GST_stats,
3678 gettext_noop
3679 ("# QUOTA messages received"),
3680 1, GNUNET_NO);
3681 sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg;
3682 if (NULL == (n = lookup_neighbour (peer)))
3683 {
3684 /* gone already */
3685 return;
3686 }
3687 last = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3688 GNUNET_BANDWIDTH_value_init (ntohl (
3689 sqm->quota)));
3690 if (last.value__ != n->neighbour_receive_quota.value__)
3691 {
3692 n->neighbour_receive_quota = last;
3693 send_outbound_quota_to_clients (n);
3694 }
3695}
3696
3697
3698/**
3699 * We received a disconnect message from the given peer,
3700 * validate and process.
3701 *
3702 * @param peer sender of the message
3703 * @param msg the disconnect message
3704 */
3705void
3706GST_neighbours_handle_disconnect_message (const struct
3707 GNUNET_PeerIdentity *peer,
3708 const struct
3709 GNUNET_MessageHeader *msg)
3710{
3711 struct NeighbourMapEntry *n;
3712 const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3713
3714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3715 "Received DISCONNECT message from peer `%s'\n",
3716 GNUNET_i2s (peer));
3717 if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionDisconnectMessage))
3718 {
3719 GNUNET_break_op (0);
3720 GNUNET_STATISTICS_update (GST_stats,
3721 gettext_noop
3722 ("# disconnect messages ignored (malformed)"),
3723 1,
3724 GNUNET_NO);
3725 return;
3726 }
3727 GNUNET_STATISTICS_update (GST_stats,
3728 gettext_noop
3729 ("# DISCONNECT messages received"),
3730 1, GNUNET_NO);
3731 sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg;
3732 if (NULL == (n = lookup_neighbour (peer)))
3733 {
3734 /* gone already */
3735 return;
3736 }
3737 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <=
3738 n->connect_ack_timestamp.abs_value_us)
3739 {
3740 GNUNET_STATISTICS_update (GST_stats,
3741 gettext_noop (
3742 "# disconnect messages ignored (timestamp)"),
3743 1,
3744 GNUNET_NO);
3745 return;
3746 }
3747 if (0 != memcmp (peer,
3748 &sdm->public_key,
3749 sizeof(struct GNUNET_PeerIdentity)))
3750 {
3751 GNUNET_break_op (0);
3752 return;
3753 }
3754 if (ntohl (sdm->purpose.size) !=
3755 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
3756 + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
3757 + sizeof(struct GNUNET_TIME_AbsoluteNBO))
3758 {
3759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3760 "DISCONNECT message from peer `%s' has invalid size\n",
3761 GNUNET_i2s (peer));
3762 GNUNET_break_op (0);
3763 return;
3764 }
3765 if (GNUNET_OK !=
3766 GNUNET_CRYPTO_eddsa_verify_ (
3767 GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3768 &sdm->purpose,
3769 &sdm->signature,
3770 &sdm->public_key))
3771 {
3772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3773 "DISCONNECT message from peer `%s' cannot be verified \n",
3774 GNUNET_i2s (peer));
3775 GNUNET_break_op (0);
3776 return;
3777 }
3778 if (NULL == n->delayed_disconnect_task)
3779 {
3780 n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect,
3781 n);
3782 }
3783}
3784
3785
3786/**
3787 * Closure for the #neighbours_iterate() function.
3788 */
3789struct IteratorContext
3790{
3791 /**
3792 * Function to call on each connected neighbour.
3793 */
3794 GST_NeighbourIterator cb;
3795
3796 /**
3797 * Closure for @e cb.
3798 */
3799 void *cb_cls;
3800};
3801
3802
3803/**
3804 * Call the callback from the closure for each neighbour.
3805 *
3806 * @param cls the `struct IteratorContext`
3807 * @param key the hash of the public key of the neighbour
3808 * @param value the `struct NeighbourMapEntry`
3809 * @return #GNUNET_OK (continue to iterate)
3810 */
3811static int
3812neighbours_iterate (void *cls,
3813 const struct GNUNET_PeerIdentity *key,
3814 void *value)
3815{
3816 struct IteratorContext *ic = cls;
3817 struct NeighbourMapEntry *n = value;
3818 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3819 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3820
3821 (void) key;
3822 if (NULL != n->primary_address.address)
3823 {
3824 bandwidth_in = n->primary_address.bandwidth_in;
3825 bandwidth_out = n->primary_address.bandwidth_out;
3826 }
3827 else
3828 {
3829 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3830 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3831 }
3832 ic->cb (ic->cb_cls,
3833 &n->id,
3834 n->primary_address.address,
3835 n->state,
3836 n->timeout,
3837 bandwidth_in, bandwidth_out);
3838 return GNUNET_OK;
3839}
3840
3841
3842/**
3843 * Iterate over all connected neighbours.
3844 *
3845 * @param cb function to call
3846 * @param cb_cls closure for @a cb
3847 */
3848void
3849GST_neighbours_iterate (GST_NeighbourIterator cb,
3850 void *cb_cls)
3851{
3852 struct IteratorContext ic;
3853
3854 if (NULL == neighbours)
3855 return; /* can happen during shutdown */
3856 ic.cb = cb;
3857 ic.cb_cls = cb_cls;
3858 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3859 &neighbours_iterate,
3860 &ic);
3861}
3862
3863
3864/**
3865 * If we have an active connection to the given target, it must be shutdown.
3866 *
3867 * @param target peer to disconnect from
3868 */
3869void
3870GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3871{
3872 struct NeighbourMapEntry *n;
3873
3874 if (NULL == (n = lookup_neighbour (target)))
3875 return; /* not active */
3876 if (GNUNET_YES == test_connected (n))
3877 GNUNET_STATISTICS_update (GST_stats,
3878 gettext_noop (
3879 "# disconnected from peer upon explicit request"),
3880 1,
3881 GNUNET_NO);
3882 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3883 "Forced disconnect from peer %s\n",
3884 GNUNET_i2s (target));
3885 disconnect_neighbour (n);
3886}
3887
3888
3889/**
3890 * Obtain current address information for the given neighbour.
3891 *
3892 * @param peer
3893 * @return address currently used
3894 */
3895const struct GNUNET_HELLO_Address *
3896GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3897{
3898 struct NeighbourMapEntry *n;
3899
3900 n = lookup_neighbour (peer);
3901 if (NULL == n)
3902 return NULL;
3903 return n->primary_address.address;
3904}
3905
3906
3907/**
3908 * Initialize the neighbours subsystem.
3909 *
3910 * @param max_fds maximum number of fds to use
3911 */
3912void
3913GST_neighbours_start (unsigned int max_fds)
3914{
3915 (void) max_fds;
3916 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE,
3917 GNUNET_NO);
3918 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (
3919 UTIL_TRANSMISSION_INTERVAL,
3920 &utilization_transmission,
3921 NULL);
3922}
3923
3924
3925/**
3926 * Disconnect from the given neighbour.
3927 *
3928 * @param cls unused
3929 * @param key hash of neighbour's public key (not used)
3930 * @param value the `struct NeighbourMapEntry` of the neighbour
3931 * @return #GNUNET_OK (continue to iterate)
3932 */
3933static int
3934disconnect_all_neighbours (void *cls,
3935 const struct GNUNET_PeerIdentity *key,
3936 void *value)
3937{
3938 struct NeighbourMapEntry *n = value;
3939
3940 (void) cls;
3941 (void) key;
3942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3943 "Disconnecting peer `%4s' during shutdown\n",
3944 GNUNET_i2s (&n->id));
3945 free_neighbour (n);
3946 return GNUNET_OK;
3947}
3948
3949
3950/**
3951 * Cleanup the neighbours subsystem.
3952 */
3953void
3954GST_neighbours_stop ()
3955{
3956 if (NULL == neighbours)
3957 return;
3958 if (NULL != util_transmission_tk)
3959 {
3960 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3961 util_transmission_tk = NULL;
3962 }
3963 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3964 &disconnect_all_neighbours,
3965 NULL);
3966 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3967 neighbours = NULL;
3968}
3969
3970
3971/* end of file gnunet-service-transport_neighbours.c */