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.c3947
1 files changed, 0 insertions, 3947 deletions
diff --git a/src/transport/gnunet-service-transport_neighbours.c b/src/transport/gnunet-service-transport_neighbours.c
deleted file mode 100644
index e61441d9f..000000000
--- a/src/transport/gnunet-service-transport_neighbours.c
+++ /dev/null
@@ -1,3947 +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
1771void
1772GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
1773 const void *msg,
1774 size_t msg_size,
1775 struct GNUNET_TIME_Relative timeout,
1776 GST_NeighbourSendContinuation cont,
1777 void *cont_cls)
1778{
1779 struct NeighbourMapEntry *n;
1780 struct MessageQueue *mq;
1781
1782 /* All ove these cases should never happen; they are all API violations.
1783 But we check anyway, just to be sure. */
1784 if (NULL == (n = lookup_neighbour (target)))
1785 {
1786 GNUNET_break (0);
1787 if (NULL != cont)
1788 cont (cont_cls,
1789 GNUNET_SYSERR,
1790 msg_size,
1791 0);
1792 return;
1793 }
1794 if (GNUNET_YES != test_connected (n))
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 bytes_in_send_queue += msg_size;
1805 GNUNET_STATISTICS_set (GST_stats,
1806 gettext_noop
1807 ("# bytes in message queue for other peers"),
1808 bytes_in_send_queue, GNUNET_NO);
1809 mq = GNUNET_malloc (sizeof(struct MessageQueue) + msg_size);
1810 mq->cont = cont;
1811 mq->cont_cls = cont_cls;
1812 GNUNET_memcpy (&mq[1], msg, msg_size);
1813 mq->message_buf = (const char *) &mq[1];
1814 mq->message_buf_size = msg_size;
1815 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1816
1817 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1818 "Enqueueing %u bytes to send to peer %s\n",
1819 (unsigned int) msg_size,
1820 GNUNET_i2s (target));
1821 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
1822 n->messages_tail,
1823 mq);
1824 if (NULL != n->task)
1825 GNUNET_SCHEDULER_cancel (n->task);
1826 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1827}
1828
1829
1830/**
1831 * Continuation called from our attempt to transmitted our
1832 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1833 * target. Continue processing based on the @a result. Specifically,
1834 * if we failed to transmit, discard the address we used.
1835 *
1836 * @param cls NULL
1837 * @param target which peer received the transmission
1838 * @param result #GNUNET_OK if sending worked
1839 * @param size_payload how many bytes of payload were sent (ignored)
1840 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1841 */
1842static void
1843send_session_syn_cont (void *cls,
1844 const struct GNUNET_PeerIdentity *target,
1845 int result,
1846 size_t size_payload,
1847 size_t size_on_wire)
1848{
1849 struct NeighbourMapEntry *n;
1850
1851 (void) cls;
1852 (void) size_payload;
1853 (void) size_on_wire;
1854 n = lookup_neighbour (target);
1855 if (NULL == n)
1856 {
1857 /* SYN continuation was called after neighbor was freed,
1858 * for example due to a time out for the state or the session
1859 * used was already terminated: nothing to do here... */
1860 return;
1861 }
1862
1863 if ((GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1864 (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1865 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1866 {
1867 /* SYN continuation was called after neighbor changed state,
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 if (GNUNET_OK == result)
1873 return;
1874
1875 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1876 _ ("Failed to send SYN message to peer `%s'\n"),
1877 GNUNET_i2s (target));
1878 switch (n->state)
1879 {
1880 case GNUNET_TRANSPORT_PS_SYN_SENT:
1881 /* Remove address and request an additional one */
1882 unset_primary_address (n);
1883 set_state_and_timeout (n,
1884 GNUNET_TRANSPORT_PS_INIT_ATS,
1885 GNUNET_TIME_relative_to_absolute (
1886 FAST_RECONNECT_TIMEOUT));
1887 break;
1888
1889 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1890 /* Remove address and request an additional one */
1891 unset_primary_address (n);
1892 set_state_and_timeout (n,
1893 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1894 GNUNET_TIME_relative_to_absolute (
1895 ATS_RESPONSE_TIMEOUT));
1896 break;
1897
1898 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1899 /* Remove address and request and go back to primary address */
1900 GNUNET_STATISTICS_update (GST_stats,
1901 gettext_noop (
1902 "# Failed attempts to switch addresses (failed to send SYN CONT)"),
1903 1,
1904 GNUNET_NO);
1905 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1906 "Switch failed, cleaning up alternative address\n");
1907 free_address (&n->alternative_address);
1908 set_state_and_timeout (n,
1909 GNUNET_TRANSPORT_PS_CONNECTED,
1910 GNUNET_TIME_relative_to_absolute (
1911 ATS_RESPONSE_TIMEOUT));
1912 break;
1913
1914 default:
1915 disconnect_neighbour (n);
1916 break;
1917 }
1918}
1919
1920
1921/**
1922 * Send a SYN message via the given address.
1923 *
1924 * @param na address to use
1925 */
1926static void
1927send_syn (struct NeighbourAddress *na)
1928{
1929 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1930 struct TransportSynMessage connect_msg;
1931 struct NeighbourMapEntry *n;
1932
1933 GNUNET_assert (NULL != na->session);
1934 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1935 "Sending SYN message to peer `%s' at %s\n",
1936 GNUNET_i2s (&na->address->peer),
1937 GST_plugins_a2s (na->address));
1938
1939 papi = GST_plugins_find (na->address->transport_name);
1940 GNUNET_assert (NULL != papi);
1941 GNUNET_STATISTICS_update (GST_stats,
1942 gettext_noop
1943 ("# SYN messages sent"),
1944 1, GNUNET_NO);
1945 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1946 connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
1947 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1948 connect_msg.reserved = htonl (0);
1949 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1950 if (-1 ==
1951 papi->send (papi->cls,
1952 na->session,
1953 (const char *) &connect_msg,
1954 sizeof(struct TransportSynMessage),
1955 UINT_MAX,
1956 SETUP_CONNECTION_TIMEOUT,
1957 &send_session_syn_cont, NULL))
1958 {
1959 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1960 _ ("Failed to transmit SYN message to %s\n"),
1961 GST_plugins_a2s (na->address));
1962 n = lookup_neighbour (&na->address->peer);
1963 if (NULL == n)
1964 {
1965 GNUNET_break (0);
1966 return;
1967 }
1968 switch (n->state)
1969 {
1970 case GNUNET_TRANSPORT_PS_SYN_SENT:
1971 /* Remove address and request and additional one */
1972 GNUNET_assert (na == &n->primary_address);
1973 unset_primary_address (n);
1974 set_state_and_timeout (n,
1975 GNUNET_TRANSPORT_PS_INIT_ATS,
1976 GNUNET_TIME_relative_to_absolute (
1977 FAST_RECONNECT_TIMEOUT));
1978 /* Hard failure to send the SYN message with this address:
1979 Destroy address and session */
1980 break;
1981
1982 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1983 /* Remove address and request an additional one */
1984 GNUNET_assert (na == &n->primary_address);
1985 unset_primary_address (n);
1986 set_state_and_timeout (n,
1987 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1988 GNUNET_TIME_relative_to_absolute (
1989 ATS_RESPONSE_TIMEOUT));
1990 break;
1991
1992 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1993 GNUNET_assert (na == &n->alternative_address);
1994 GNUNET_STATISTICS_update (GST_stats,
1995 gettext_noop (
1996 "# Failed attempts to switch addresses (failed to send SYN)"),
1997 1,
1998 GNUNET_NO);
1999 /* Remove address and request an additional one */
2000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2001 "Switch failed, cleaning up alternative address\n");
2002 free_address (&n->alternative_address);
2003 set_state_and_timeout (n,
2004 GNUNET_TRANSPORT_PS_CONNECTED,
2005 GNUNET_TIME_relative_to_absolute (
2006 ATS_RESPONSE_TIMEOUT));
2007 break;
2008
2009 default:
2010 GNUNET_break (0);
2011 disconnect_neighbour (n);
2012 break;
2013 }
2014 return;
2015 }
2016 GST_neighbours_notify_data_sent (na->address,
2017 na->session,
2018 sizeof(struct TransportSynMessage));
2019}
2020
2021
2022/**
2023 * Continuation called from our attempt to transmitted our
2024 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
2025 * target. Continue processing based on the @a result. Specifically,
2026 * if we failed to transmit, discard the address we used.
2027 *
2028 * @param cls NULL
2029 * @param target which peer received the transmission
2030 * @param result #GNUNET_OK if sending worked
2031 * @param size_payload how many bytes of payload were sent (ignored)
2032 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
2033 */
2034static void
2035send_session_syn_ack_cont (void *cls,
2036 const struct GNUNET_PeerIdentity *target,
2037 int result,
2038 size_t size_payload,
2039 size_t size_on_wire)
2040{
2041 struct NeighbourMapEntry *n;
2042
2043 (void) cls;
2044 (void) size_payload;
2045 (void) size_on_wire;
2046 n = lookup_neighbour (target);
2047 if (NULL == n)
2048 {
2049 /* SYN_ACK continuation was called after neighbor was freed,
2050 * for example due to a time out for the state or the session
2051 * used was already terminated: nothing to do here... */
2052 return;
2053 }
2054
2055 if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2056 {
2057 /* SYN_ACK continuation was called after neighbor changed state,
2058 * for example due to a time out for the state or the session
2059 * used was already terminated: nothing to do here... */
2060 return;
2061 }
2062 if (GNUNET_OK == result)
2063 return;
2064
2065 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2066 _ (
2067 "Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2068 GNUNET_i2s (target),
2069 GST_plugins_a2s (n->primary_address.address));
2070
2071 /* Remove address and request and additional one */
2072 /* FIXME: what if the neighbour's primary address
2073 changed in the meantime? Might want to instead
2074 pass "something" around in closure to be sure. */
2075 unset_primary_address (n);
2076 n->ack_state = ACK_SEND_SYN_ACK;
2077 set_state_and_timeout (n,
2078 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2079 GNUNET_TIME_relative_to_absolute (
2080 ATS_RESPONSE_TIMEOUT));
2081}
2082
2083
2084/**
2085 * Send a SYN_ACK message via the given address.
2086 *
2087 * @param na address and session to use
2088 * @param timestamp timestamp to use for the ACK message
2089 * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2090 */
2091static void
2092send_syn_ack_message (struct NeighbourAddress *na,
2093 struct GNUNET_TIME_Absolute timestamp)
2094{
2095 const struct GNUNET_HELLO_Address *address = na->address;
2096 struct GNUNET_ATS_Session *session = na->session;
2097 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2098 struct TransportSynMessage connect_msg;
2099 struct NeighbourMapEntry *n;
2100
2101 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2102 "Sending SYN_ACK to peer `%s'\n",
2103 GNUNET_i2s (&address->peer));
2104
2105 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2106 {
2107 GNUNET_break (0);
2108 return;
2109 }
2110 if (NULL == session)
2111 session = papi->get_session (papi->cls,
2112 address);
2113 if (NULL == session)
2114 {
2115 GNUNET_break (0);
2116 return;
2117 }
2118 GST_ats_new_session (address,
2119 session);
2120 GNUNET_STATISTICS_update (GST_stats,
2121 gettext_noop
2122 ("# SYN_ACK messages sent"),
2123 1, GNUNET_NO);
2124 connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
2125 connect_msg.header.type = htons (
2126 GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2127 connect_msg.reserved = htonl (0);
2128 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
2129
2130 if (GNUNET_SYSERR ==
2131 papi->send (papi->cls,
2132 session,
2133 (const char *) &connect_msg,
2134 sizeof(struct TransportSynMessage),
2135 UINT_MAX,
2136 GNUNET_TIME_UNIT_FOREVER_REL,
2137 &send_session_syn_ack_cont, NULL))
2138 {
2139 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2140 _ ("Failed to transmit SYN_ACK message to %s\n"),
2141 GST_plugins_a2s (address));
2142
2143 n = lookup_neighbour (&address->peer);
2144 if (NULL == n)
2145 {
2146 GNUNET_break (0);
2147 return;
2148 }
2149 /* Remove address and request and additional one */
2150 unset_primary_address (n);
2151 n->ack_state = ACK_SEND_SYN_ACK;
2152 set_state_and_timeout (n,
2153 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2154 GNUNET_TIME_relative_to_absolute (
2155 ATS_RESPONSE_TIMEOUT));
2156 return;
2157 }
2158}
2159
2160
2161/**
2162 * Function called by the bandwidth tracker for a peer whenever
2163 * the tracker's state changed such that we need to recalculate
2164 * the delay for flow control. We calculate the latest delay
2165 * and inform the plugin (if applicable).
2166 *
2167 * @param cls the `struct NeighbourMapEntry` to update calculations for
2168 */
2169static void
2170inbound_bw_tracker_update (void *cls)
2171{
2172 struct NeighbourMapEntry *n = cls;
2173 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2174 struct GNUNET_TIME_Relative delay;
2175 int do_forward;
2176
2177 if (NULL == n->primary_address.address)
2178 return; /* not active, ignore */
2179 papi = GST_plugins_find (n->primary_address.address->transport_name);
2180 GNUNET_assert (NULL != papi);
2181 if (NULL == papi->update_inbound_delay)
2182 return;
2183 delay = GST_neighbours_calculate_receive_delay (&n->id,
2184 0,
2185 &do_forward);
2186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2187 "New inbound delay for peer `%s' is %llu ms\n",
2188 GNUNET_i2s (&n->id),
2189 (unsigned long long) delay.rel_value_us / 1000LL);
2190 if (NULL == n->primary_address.session)
2191 return;
2192 papi->update_inbound_delay (papi->cls,
2193 &n->id,
2194 n->primary_address.session,
2195 delay);
2196}
2197
2198
2199/**
2200 * Create a fresh entry in the neighbour map for the given peer
2201 *
2202 * @param peer peer to create an entry for
2203 * @return new neighbour map entry
2204 */
2205static struct NeighbourMapEntry *
2206setup_neighbour (const struct GNUNET_PeerIdentity *peer)
2207{
2208 struct NeighbourMapEntry *n;
2209
2210 if (0 ==
2211 memcmp (&GST_my_identity,
2212 peer,
2213 sizeof(struct GNUNET_PeerIdentity)))
2214 {
2215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2216 "Cowardly refusing to consider myself my neighbour!\n");
2217 return NULL;
2218 }
2219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2220 "Creating new neighbour entry for `%s'\n",
2221 GNUNET_i2s (peer));
2222 n = GNUNET_new (struct NeighbourMapEntry);
2223 n->id = *peer;
2224 n->ack_state = ACK_UNDEFINED;
2225 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2226 n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2227 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
2228 &inbound_bw_tracker_update,
2229 n,
2230 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2231 MAX_BANDWIDTH_CARRY_S);
2232 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
2233 set_state_and_timeout (n,
2234 GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2235 GNUNET_TIME_UNIT_FOREVER_ABS);
2236 GNUNET_assert (GNUNET_OK ==
2237 GNUNET_CONTAINER_multipeermap_put (neighbours,
2238 &n->id,
2239 n,
2240 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2241 n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect,
2242 peer,
2243 0);
2244
2245 return n;
2246}
2247
2248
2249/**
2250 * Entry in a DLL we use to keep track of pending blacklist checks.
2251 */
2252struct BlacklistCheckSwitchContext
2253{
2254 /**
2255 * DLL prev pointer.
2256 */
2257 struct BlacklistCheckSwitchContext *prev;
2258
2259 /**
2260 * DLL next pointer.
2261 */
2262 struct BlacklistCheckSwitchContext *next;
2263
2264 /**
2265 * Handle to the blacklist check we are performing.
2266 */
2267 struct GST_BlacklistCheck *blc;
2268
2269 /**
2270 * Inbound bandwidth that was assigned to @e address.
2271 */
2272 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2273
2274 /**
2275 * Outbound bandwidth that was assigned to @e address.
2276 */
2277 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2278};
2279
2280
2281/**
2282 * We received a 'SYN' message from the other peer.
2283 * Consider switching to it.
2284 *
2285 * @param message possibly a `struct TransportSynMessage` (check format)
2286 * @param peer identity of the peer to switch the address for
2287 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2288 */
2289int
2290GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message,
2291 const struct GNUNET_PeerIdentity *peer)
2292{
2293 const struct TransportSynMessage *scm;
2294 struct NeighbourMapEntry *n;
2295 struct GNUNET_TIME_Absolute ts;
2296
2297 if (ntohs (message->size) != sizeof(struct TransportSynMessage))
2298 {
2299 GNUNET_break_op (0);
2300 return GNUNET_SYSERR;
2301 }
2302 GNUNET_STATISTICS_update (GST_stats,
2303 gettext_noop
2304 ("# SYN messages received"),
2305 1, GNUNET_NO);
2306 if (NULL == neighbours)
2307 {
2308 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2309 _ (
2310 "SYN request from peer `%s' ignored due impending shutdown\n"),
2311 GNUNET_i2s (peer));
2312 return GNUNET_OK; /* we're shutting down */
2313 }
2314 scm = (const struct TransportSynMessage *) message;
2315 GNUNET_break_op (0 == ntohl (scm->reserved));
2316 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2317 if (0 ==
2318 memcmp (&GST_my_identity,
2319 peer,
2320 sizeof(struct GNUNET_PeerIdentity)))
2321 {
2322 /* loopback connection-to-self, ignore */
2323 return GNUNET_SYSERR;
2324 }
2325 n = lookup_neighbour (peer);
2326 if (NULL == n)
2327 {
2328 /* This is a new neighbour and set to not connected */
2329 n = setup_neighbour (peer);
2330 GNUNET_assert (NULL != n);
2331 }
2332
2333 /* Remember this SYN message in neighbour */
2334 n->ack_state = ACK_SEND_SYN_ACK;
2335 n->connect_ack_timestamp = ts;
2336
2337 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2338 "Received SYN for peer `%s' in state %s/%s\n",
2339 GNUNET_i2s (peer),
2340 GNUNET_TRANSPORT_ps2s (n->state),
2341 print_ack_state (n->ack_state));
2342
2343 switch (n->state)
2344 {
2345 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2346 /* Request an address from ATS to send SYN_ACK to this peer */
2347 set_state_and_timeout (n,
2348 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2349 GNUNET_TIME_relative_to_absolute (
2350 ATS_RESPONSE_TIMEOUT));
2351 break;
2352
2353 case GNUNET_TRANSPORT_PS_INIT_ATS:
2354 /* SYN message takes priority over us asking ATS for address:
2355 * Wait for ATS to suggest an address and send SYN_ACK */
2356 set_state_and_timeout (n,
2357 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2358 GNUNET_TIME_relative_to_absolute (
2359 ATS_RESPONSE_TIMEOUT));
2360 break;
2361
2362 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2363 /* We already wait for an address to send an SYN_ACK */
2364 break;
2365
2366 case GNUNET_TRANSPORT_PS_SYN_SENT:
2367 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2368 /* Send ACK immediately */
2369 n->ack_state = ACK_SEND_ACK;
2370 send_syn_ack_message (&n->primary_address,
2371 ts);
2372 break;
2373
2374 case GNUNET_TRANSPORT_PS_CONNECTED:
2375 /* we are already connected and can thus send the ACK immediately */
2376 GNUNET_assert (NULL != n->primary_address.address);
2377 GNUNET_assert (NULL != n->primary_address.session);
2378 n->ack_state = ACK_SEND_ACK;
2379 send_syn_ack_message (&n->primary_address,
2380 ts);
2381 break;
2382
2383 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2384 /* We wait for ATS address suggestion */
2385 break;
2386
2387 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2388 /* We received a SYN message while waiting for a SYN_ACK in fast
2389 * reconnect. Send SYN_ACK immediately */
2390 n->ack_state = ACK_SEND_ACK;
2391 send_syn_ack_message (&n->primary_address,
2392 n->connect_ack_timestamp);
2393 break;
2394
2395 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2396 /* We are already connected and can thus send the ACK immediately;
2397 still, it can never hurt to have an alternative address, so also
2398 tell ATS about it */
2399 GNUNET_assert (NULL != n->primary_address.address);
2400 GNUNET_assert (NULL != n->primary_address.session);
2401 n->ack_state = ACK_SEND_ACK;
2402 send_syn_ack_message (&n->primary_address,
2403 ts);
2404 break;
2405
2406 case GNUNET_TRANSPORT_PS_DISCONNECT:
2407 /* Get rid of remains and re-try */
2408 free_neighbour (n);
2409 n = setup_neighbour (peer);
2410 GNUNET_assert (NULL != n);
2411 /* Remember the SYN time stamp for ACK message */
2412 n->ack_state = ACK_SEND_SYN_ACK;
2413 n->connect_ack_timestamp = ts;
2414 /* Request an address for the peer */
2415 set_state_and_timeout (n,
2416 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2417 GNUNET_TIME_relative_to_absolute (
2418 ATS_RESPONSE_TIMEOUT));
2419 break;
2420
2421 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2422 /* should not be possible */
2423 GNUNET_assert (0);
2424 break;
2425
2426 default:
2427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2428 "Unhandled state `%s'\n",
2429 GNUNET_TRANSPORT_ps2s (n->state));
2430 GNUNET_break (0);
2431 return GNUNET_SYSERR;
2432 }
2433 return GNUNET_OK;
2434}
2435
2436
2437/**
2438 * Check if the given @a address is the same that we are already
2439 * using for the respective neighbour. If so, update the bandwidth
2440 * assignment and possibly the session and return #GNUNET_OK.
2441 * If the new address is different from what the neighbour is
2442 * using right now, return #GNUNET_NO.
2443 *
2444 * @param address address of the other peer,
2445 * @param session session to use or NULL if transport should initiate a session
2446 * @param bandwidth_in inbound quota to be used when connection is up,
2447 * 0 to disconnect from peer
2448 * @param bandwidth_out outbound quota to be used when connection is up,
2449 * 0 to disconnect from peer
2450 * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2451 * #GNUNET_NO if more extensive changes are required (address changed)
2452 */
2453static int
2454try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address,
2455 struct GNUNET_ATS_Session *session,
2456 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2457 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2458{
2459 struct NeighbourMapEntry *n;
2460
2461 n = lookup_neighbour (&address->peer);
2462 if ((NULL == n) ||
2463 (NULL == n->primary_address.address) ||
2464 (0 != GNUNET_HELLO_address_cmp (address,
2465 n->primary_address.address)))
2466 return GNUNET_NO;
2467 /* We are not really switching addresses, but merely adjusting
2468 session and/or bandwidth, can do fast ATS update! */
2469 if (session != n->primary_address.session)
2470 {
2471 /* switch to a different session, but keeping same address; could
2472 happen if there is a 2nd inbound connection */
2473 n->primary_address.session = session;
2474 GNUNET_assert (GNUNET_YES ==
2475 GST_ats_is_known (n->primary_address.address,
2476 n->primary_address.session));
2477 }
2478 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2479 {
2480 n->primary_address.bandwidth_in = bandwidth_in;
2481 if (GNUNET_YES !=
2482 set_incoming_quota (n,
2483 bandwidth_in))
2484 return GNUNET_NO;
2485 }
2486 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2487 {
2488 n->primary_address.bandwidth_out = bandwidth_out;
2489 send_outbound_quota_to_clients (n);
2490 }
2491 return GNUNET_OK;
2492}
2493
2494
2495/**
2496 * We've been asked to switch addresses, and just now got the result
2497 * from the blacklist check to see if this is allowed.
2498 *
2499 * @param cls the `struct BlacklistCheckSwitchContext` with
2500 * the information about the future address
2501 * @param peer the peer we may switch addresses on
2502 * @param address address associated with the request
2503 * @param session session associated with the request
2504 * @param result #GNUNET_OK if the connection is allowed,
2505 * #GNUNET_NO if not,
2506 * #GNUNET_SYSERR if operation was aborted
2507 */
2508static void
2509switch_address_bl_check_cont (void *cls,
2510 const struct GNUNET_PeerIdentity *peer,
2511 const struct GNUNET_HELLO_Address *address,
2512 struct GNUNET_ATS_Session *session,
2513 int result)
2514{
2515 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2516 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2517 struct NeighbourMapEntry *n;
2518
2519 if (GNUNET_SYSERR == result)
2520 goto cleanup;
2521
2522 papi = GST_plugins_find (address->transport_name);
2523 if (NULL == papi)
2524 {
2525 /* This can happen during shutdown. */
2526 goto cleanup;
2527 }
2528
2529 if (GNUNET_NO == result)
2530 {
2531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2532 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2533 GST_plugins_a2s (address),
2534 session,
2535 GNUNET_i2s (peer));
2536 GNUNET_STATISTICS_update (GST_stats,
2537 "# ATS suggestions ignored (blacklist denied)",
2538 1,
2539 GNUNET_NO);
2540 if (NULL != session)
2541 papi->disconnect_session (papi->cls,
2542 session);
2543 if (GNUNET_YES !=
2544 GNUNET_HELLO_address_check_option (address,
2545 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2546 GST_ats_block_address (address,
2547 NULL);
2548 goto cleanup;
2549 }
2550
2551
2552 if (NULL == session)
2553 {
2554 /* need to create a session, ATS only gave us an address */
2555 session = papi->get_session (papi->cls,
2556 address);
2557 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2558 "Obtained new session for peer `%s' and address '%s': %p\n",
2559 GNUNET_i2s (&address->peer),
2560 GST_plugins_a2s (address),
2561 session);
2562 if (NULL != session)
2563 GST_ats_new_session (address,
2564 session);
2565 }
2566 if (NULL == session)
2567 {
2568 /* session creation failed, bad!, fail! */
2569 GNUNET_STATISTICS_update (GST_stats,
2570 "# ATS suggestions ignored (failed to create session)",
2571 1,
2572 GNUNET_NO);
2573 /* No session could be obtained, remove blacklist check and clean up */
2574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2575 "Failed to obtain new session for peer `%s' and address '%s'\n",
2576 GNUNET_i2s (&address->peer),
2577 GST_plugins_a2s (address));
2578 GST_ats_block_address (address,
2579 session);
2580 goto cleanup;
2581 }
2582
2583 /* We did this check already before going into blacklist, but
2584 it is theoretically possible that the situation changed in
2585 the meantime, hence we check again here */
2586 if (GNUNET_OK ==
2587 try_run_fast_ats_update (address,
2588 session,
2589 blc_ctx->bandwidth_in,
2590 blc_ctx->bandwidth_out))
2591 goto cleanup; /* was just a minor update, we're done */
2592
2593 /* check if we also need to setup the neighbour entry */
2594 if (NULL == (n = lookup_neighbour (peer)))
2595 {
2596 n = setup_neighbour (peer);
2597 if (NULL == n)
2598 {
2599 /* not sure how this can happen... */
2600 GNUNET_break (0);
2601 goto cleanup;
2602 }
2603 n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2604 }
2605
2606 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2607 "Peer `%s' switches to address `%s'\n",
2608 GNUNET_i2s (&address->peer),
2609 GST_plugins_a2s (address));
2610
2611 switch (n->state)
2612 {
2613 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2614 GNUNET_break (0);
2615 GST_ats_block_address (address,
2616 session);
2617 free_neighbour (n);
2618 return;
2619
2620 case GNUNET_TRANSPORT_PS_INIT_ATS:
2621 /* We requested an address and ATS suggests one:
2622 * set primary address and send SYN message*/
2623 set_primary_address (n,
2624 address,
2625 session,
2626 blc_ctx->bandwidth_in,
2627 blc_ctx->bandwidth_out);
2628 if (ACK_SEND_SYN_ACK == n->ack_state)
2629 {
2630 /* Send pending SYN_ACK message */
2631 n->ack_state = ACK_SEND_ACK;
2632 send_syn_ack_message (&n->primary_address,
2633 n->connect_ack_timestamp);
2634 }
2635 set_state_and_timeout (n,
2636 GNUNET_TRANSPORT_PS_SYN_SENT,
2637 GNUNET_TIME_relative_to_absolute (
2638 SETUP_CONNECTION_TIMEOUT));
2639 send_syn (&n->primary_address);
2640 break;
2641
2642 case GNUNET_TRANSPORT_PS_SYN_SENT:
2643 /* ATS suggested a new address while waiting for an SYN_ACK:
2644 * Switch and send new SYN */
2645 /* ATS suggests a different address, switch again */
2646 set_primary_address (n,
2647 address,
2648 session,
2649 blc_ctx->bandwidth_in,
2650 blc_ctx->bandwidth_out);
2651 if (ACK_SEND_SYN_ACK == n->ack_state)
2652 {
2653 /* Send pending SYN_ACK message */
2654 n->ack_state = ACK_SEND_ACK;
2655 send_syn_ack_message (&n->primary_address,
2656 n->connect_ack_timestamp);
2657 }
2658 set_state_and_timeout (n,
2659 GNUNET_TRANSPORT_PS_SYN_SENT,
2660 GNUNET_TIME_relative_to_absolute (
2661 SETUP_CONNECTION_TIMEOUT));
2662 send_syn (&n->primary_address);
2663 break;
2664
2665 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2666 /* We requested an address and ATS suggests one:
2667 * set primary address and send SYN_ACK message*/
2668 set_primary_address (n,
2669 address,
2670 session,
2671 blc_ctx->bandwidth_in,
2672 blc_ctx->bandwidth_out);
2673 /* Send an ACK message as a response to the SYN msg */
2674 set_state_and_timeout (n,
2675 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2676 GNUNET_TIME_relative_to_absolute (
2677 SETUP_CONNECTION_TIMEOUT));
2678 send_syn_ack_message (&n->primary_address,
2679 n->connect_ack_timestamp);
2680 if ((ACK_SEND_SYN_ACK == n->ack_state) ||
2681 (ACK_UNDEFINED == n->ack_state))
2682 n->ack_state = ACK_SEND_ACK;
2683 break;
2684
2685 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2686 /* ATS asks us to switch while we were trying to connect; switch to new
2687 address and check blacklist again */
2688 if ((ACK_SEND_SYN_ACK == n->ack_state))
2689 {
2690 n->ack_state = ACK_SEND_ACK;
2691 send_syn_ack_message (&n->primary_address,
2692 n->connect_ack_timestamp);
2693 }
2694 set_primary_address (n,
2695 address,
2696 session,
2697 blc_ctx->bandwidth_in,
2698 blc_ctx->bandwidth_out);
2699 set_state_and_timeout (n,
2700 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2701 GNUNET_TIME_relative_to_absolute (
2702 SETUP_CONNECTION_TIMEOUT));
2703 break;
2704
2705 case GNUNET_TRANSPORT_PS_CONNECTED:
2706 GNUNET_assert (NULL != n->primary_address.address);
2707 GNUNET_assert (NULL != n->primary_address.session);
2708 GNUNET_break (n->primary_address.session != session);
2709 /* ATS asks us to switch a life connection; see if we can get
2710 a SYN_ACK on it before we actually do this! */
2711 set_alternative_address (n,
2712 address,
2713 session,
2714 blc_ctx->bandwidth_in,
2715 blc_ctx->bandwidth_out);
2716 set_state_and_timeout (n,
2717 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2718 GNUNET_TIME_relative_to_absolute (
2719 SETUP_CONNECTION_TIMEOUT));
2720 GNUNET_STATISTICS_update (GST_stats,
2721 gettext_noop ("# Attempts to switch addresses"),
2722 1,
2723 GNUNET_NO);
2724 send_syn (&n->alternative_address);
2725 break;
2726
2727 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2728 set_primary_address (n,
2729 address,
2730 session,
2731 blc_ctx->bandwidth_in,
2732 blc_ctx->bandwidth_out);
2733 if (ACK_SEND_SYN_ACK == n->ack_state)
2734 {
2735 /* Send pending SYN_ACK message */
2736 n->ack_state = ACK_SEND_ACK;
2737 send_syn_ack_message (&n->primary_address,
2738 n->connect_ack_timestamp);
2739 }
2740 set_state_and_timeout (n,
2741 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2742 GNUNET_TIME_relative_to_absolute (
2743 FAST_RECONNECT_TIMEOUT));
2744 send_syn (&n->primary_address);
2745 break;
2746
2747 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2748 /* ATS asks us to switch while we were trying to reconnect; switch to new
2749 address and send SYN again */
2750 set_primary_address (n,
2751 address,
2752 session,
2753 blc_ctx->bandwidth_in,
2754 blc_ctx->bandwidth_out);
2755 set_state_and_timeout (n,
2756 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2757 GNUNET_TIME_relative_to_absolute (
2758 FAST_RECONNECT_TIMEOUT));
2759 send_syn (&n->primary_address);
2760 break;
2761
2762 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2763 if ((0 == GNUNET_HELLO_address_cmp (n->primary_address.address,
2764 address)) &&
2765 (n->primary_address.session == session))
2766 {
2767 /* ATS switches back to still-active session */
2768 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2769 "ATS double-switched, cleaning up alternative address\n");
2770 free_address (&n->alternative_address);
2771 set_state_and_timeout (n,
2772 GNUNET_TRANSPORT_PS_CONNECTED,
2773 n->timeout);
2774 break;
2775 }
2776 /* ATS asks us to switch a life connection, send */
2777 set_alternative_address (n,
2778 address,
2779 session,
2780 blc_ctx->bandwidth_in,
2781 blc_ctx->bandwidth_out);
2782 set_state_and_timeout (n,
2783 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2784 GNUNET_TIME_relative_to_absolute (
2785 SETUP_CONNECTION_TIMEOUT));
2786 send_syn (&n->alternative_address);
2787 break;
2788
2789 case GNUNET_TRANSPORT_PS_DISCONNECT:
2790 /* not going to switch addresses while disconnecting */
2791 GNUNET_STATISTICS_update (GST_stats,
2792 "# ATS suggestion ignored (disconnecting)",
2793 1,
2794 GNUNET_NO);
2795 return;
2796
2797 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2798 GNUNET_assert (0);
2799 break;
2800
2801 default:
2802 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2803 "Unhandled state `%s'\n",
2804 GNUNET_TRANSPORT_ps2s (n->state));
2805 GNUNET_break (0);
2806 break;
2807 }
2808cleanup:
2809 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2810 pending_bc_tail,
2811 blc_ctx);
2812 GNUNET_free (blc_ctx);
2813}
2814
2815
2816/**
2817 * For the given peer, switch to this address.
2818 *
2819 * Before accepting this addresses and actively using it, a blacklist check
2820 * is performed.
2821 *
2822 * If any check fails or the suggestion can somehow not be followed, we
2823 * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2824 * could not be satisfied and force ATS to do something else.
2825 *
2826 * @param address address of the other peer,
2827 * @param session session to use or NULL if transport should initiate a session
2828 * @param bandwidth_in inbound quota to be used when connection is up,
2829 * 0 to disconnect from peer
2830 * @param bandwidth_out outbound quota to be used when connection is up,
2831 * 0 to disconnect from peer
2832 */
2833void
2834GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address,
2835 struct GNUNET_ATS_Session *session,
2836 struct GNUNET_BANDWIDTH_Value32NBO
2837 bandwidth_in,
2838 struct GNUNET_BANDWIDTH_Value32NBO
2839 bandwidth_out)
2840{
2841 struct GST_BlacklistCheck *blc;
2842 struct BlacklistCheckSwitchContext *blc_ctx;
2843
2844 GNUNET_assert (NULL != address->transport_name);
2845 if (GNUNET_OK ==
2846 try_run_fast_ats_update (address,
2847 session,
2848 bandwidth_in,
2849 bandwidth_out))
2850 return;
2851
2852 /* Check if plugin is available */
2853 if (NULL == (GST_plugins_find (address->transport_name)))
2854 {
2855 /* we don't have the plugin for this address */
2856 GNUNET_break (0);
2857 GST_ats_block_address (address,
2858 session);
2859 return;
2860 }
2861 if ((NULL == session) &&
2862 (GNUNET_HELLO_address_check_option (address,
2863 GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2864 {
2865 /* This is a inbound address and we do not have a session to use! */
2866 GNUNET_break (0);
2867 GST_ats_block_address (address,
2868 session);
2869 return;
2870 }
2871
2872 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2873 "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2874 GST_plugins_a2s (address),
2875 GNUNET_i2s (&address->peer),
2876 (unsigned int) ntohl (bandwidth_in.value__),
2877 (unsigned int) ntohl (bandwidth_out.value__));
2878
2879 /* Perform blacklist check */
2880 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2881 blc_ctx->bandwidth_in = bandwidth_in;
2882 blc_ctx->bandwidth_out = bandwidth_out;
2883 GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2884 pending_bc_tail,
2885 blc_ctx);
2886 if (NULL != (blc = GST_blacklist_test_allowed (&address->peer,
2887 address->transport_name,
2888 &switch_address_bl_check_cont,
2889 blc_ctx,
2890 address,
2891 session)))
2892 {
2893 blc_ctx->blc = blc;
2894 }
2895}
2896
2897
2898/**
2899 * Function called to send network utilization data to ATS for
2900 * each active connection.
2901 *
2902 * @param cls NULL
2903 * @param key peer we send utilization data for
2904 * @param value the `struct NeighbourMapEntry *` with data to send
2905 * @return #GNUNET_OK (continue to iterate)
2906 */
2907static int
2908send_utilization_data (void *cls,
2909 const struct GNUNET_PeerIdentity *key,
2910 void *value)
2911{
2912 struct NeighbourMapEntry *n = value;
2913 uint32_t bps_in;
2914 uint32_t bps_out;
2915 struct GNUNET_TIME_Relative delta;
2916
2917 (void) cls;
2918 if ((GNUNET_YES != test_connected (n)) ||
2919 (NULL == n->primary_address.address))
2920 return GNUNET_OK;
2921 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2922 GNUNET_TIME_absolute_get ());
2923 bps_in = 0;
2924 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2925 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv)
2926 / (delta.rel_value_us);
2927 bps_out = 0;
2928 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2929 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2930
2931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2932 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2933 GNUNET_i2s (key),
2934 bps_in,
2935 bps_out);
2936 GST_ats_update_utilization (n->primary_address.address,
2937 bps_in,
2938 bps_out);
2939 n->util_total_bytes_recv = 0;
2940 n->util_total_bytes_sent = 0;
2941 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2942 return GNUNET_OK;
2943}
2944
2945
2946/**
2947 * Task transmitting utilization in a regular interval
2948 *
2949 * @param cls the `struct NeighbourMapEntry` for which we are running
2950 */
2951static void
2952utilization_transmission (void *cls)
2953{
2954 (void) cls;
2955 util_transmission_tk = NULL;
2956 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
2957 &send_utilization_data,
2958 NULL);
2959 util_transmission_tk
2960 = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2961 &utilization_transmission,
2962 NULL);
2963}
2964
2965
2966/**
2967 * Track information about data we received from the
2968 * given address (used to notify ATS about our utilization
2969 * of allocated resources).
2970 *
2971 * @param address the address we got data from
2972 * @param message the message we received (really only the size is used)
2973 */
2974void
2975GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address,
2976 const struct GNUNET_MessageHeader *message)
2977{
2978 struct NeighbourMapEntry *n;
2979
2980 n = lookup_neighbour (&address->peer);
2981 if (NULL == n)
2982 return;
2983 n->util_total_bytes_recv += ntohs (message->size);
2984}
2985
2986
2987/**
2988 * Track information about data we transmitted using the given @a
2989 * address and @a session (used to notify ATS about our utilization of
2990 * allocated resources).
2991 *
2992 * @param address the address we transmitted data to
2993 * @param session session we used to transmit data
2994 * @param message the message we sent (really only the size is used)
2995 */
2996void
2997GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address,
2998 struct GNUNET_ATS_Session *session,
2999 size_t size)
3000{
3001 struct NeighbourMapEntry *n;
3002
3003 n = lookup_neighbour (&address->peer);
3004 if (NULL == n)
3005 return;
3006 if (n->primary_address.session != session)
3007 return;
3008 n->util_total_bytes_sent += size;
3009}
3010
3011
3012static void
3013master_task (void *cls)
3014{
3015 struct NeighbourMapEntry *n = cls;
3016 struct GNUNET_TIME_Relative delay;
3017
3018 n->task = NULL;
3019 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3020 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3021 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
3022 GNUNET_i2s (&n->id),
3023 GNUNET_TRANSPORT_ps2s (n->state),
3024 GNUNET_STRINGS_relative_time_to_string (delay,
3025 GNUNET_YES));
3026 switch (n->state)
3027 {
3028 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3029 /* invalid state for master task, clean up */
3030 GNUNET_break (0);
3031 free_neighbour (n);
3032 return;
3033
3034 case GNUNET_TRANSPORT_PS_INIT_ATS:
3035 if (0 == delay.rel_value_us)
3036 {
3037 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3038 "Connection to `%s' timed out waiting for ATS to provide address\n",
3039 GNUNET_i2s (&n->id));
3040 free_neighbour (n);
3041 return;
3042 }
3043 break;
3044
3045 case GNUNET_TRANSPORT_PS_SYN_SENT:
3046 if (0 == delay.rel_value_us)
3047 {
3048 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3049 "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
3050 GNUNET_i2s (&n->id));
3051 /* Remove address and request and additional one */
3052 unset_primary_address (n);
3053 set_state_and_timeout (n,
3054 GNUNET_TRANSPORT_PS_INIT_ATS,
3055 GNUNET_TIME_relative_to_absolute (
3056 ATS_RESPONSE_TIMEOUT));
3057 return;
3058 }
3059 break;
3060
3061 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3062 if (0 == delay.rel_value_us)
3063 {
3064 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3065 "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
3066 GNUNET_i2s (&n->id));
3067 free_neighbour (n);
3068 return;
3069 }
3070 break;
3071
3072 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3073 if (0 == delay.rel_value_us)
3074 {
3075 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3076 "Connection to `%s' timed out waiting for other peer to send ACK\n",
3077 GNUNET_i2s (&n->id));
3078 disconnect_neighbour (n);
3079 return;
3080 }
3081 break;
3082
3083 case GNUNET_TRANSPORT_PS_CONNECTED:
3084 if (0 == delay.rel_value_us)
3085 {
3086 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3087 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3088 GNUNET_i2s (&n->id));
3089 disconnect_neighbour (n);
3090 return;
3091 }
3092 try_transmission_to_peer (n);
3093 send_keepalive (n);
3094 break;
3095
3096 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3097 if (0 == delay.rel_value_us)
3098 {
3099 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3100 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3101 GNUNET_i2s (&n->id));
3102 disconnect_neighbour (n);
3103 return;
3104 }
3105 break;
3106
3107 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3108 if (0 == delay.rel_value_us)
3109 {
3110 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3111 "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3112 GNUNET_i2s (&n->id));
3113 disconnect_neighbour (n);
3114 return;
3115 }
3116 break;
3117
3118 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3119 if (0 == delay.rel_value_us)
3120 {
3121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3122 "Switch failed, cleaning up alternative address\n");
3123 free_address (&n->alternative_address);
3124 set_state_and_timeout (n,
3125 GNUNET_TRANSPORT_PS_CONNECTED,
3126 GNUNET_TIME_relative_to_absolute (
3127 SETUP_CONNECTION_TIMEOUT));
3128 }
3129 try_transmission_to_peer (n);
3130 send_keepalive (n);
3131 break;
3132
3133 case GNUNET_TRANSPORT_PS_DISCONNECT:
3134 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3135 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3136 GNUNET_i2s (&n->id));
3137 free_neighbour (n);
3138 return;
3139
3140 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3141 /* how did we get here!? */
3142 GNUNET_assert (0);
3143 break;
3144
3145 default:
3146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3147 "Unhandled state `%s'\n",
3148 GNUNET_TRANSPORT_ps2s (n->state));
3149 GNUNET_break (0);
3150 break;
3151 }
3152 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3153 if ((GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3154 (GNUNET_TRANSPORT_PS_CONNECTED == n->state))
3155 {
3156 /* if we are *now* in one of the two states, we're sending
3157 keep alive messages, so we need to consider the keepalive
3158 delay, not just the connection timeout */
3159 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (
3160 n->keep_alive_time),
3161 delay);
3162 }
3163 if (NULL == n->task)
3164 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3165 &master_task,
3166 n);
3167}
3168
3169
3170/**
3171 * Send a ACK message to the neighbour to confirm that we
3172 * got its SYN_ACK.
3173 *
3174 * @param n neighbour to send the ACK to
3175 */
3176static void
3177send_session_ack_message (struct NeighbourMapEntry *n)
3178{
3179 struct GNUNET_MessageHeader msg;
3180
3181 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3182 "Sending ACK message to peer `%s'\n",
3183 GNUNET_i2s (&n->id));
3184
3185 msg.size = htons (sizeof(struct GNUNET_MessageHeader));
3186 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3187 (void) send_with_session (n,
3188 &msg,
3189 sizeof(struct GNUNET_MessageHeader),
3190 UINT32_MAX,
3191 GNUNET_TIME_UNIT_FOREVER_REL,
3192 GNUNET_NO,
3193 NULL, NULL);
3194}
3195
3196
3197/**
3198 * We received a 'SESSION_SYN_ACK' message from the other peer.
3199 * Consider switching to it.
3200 *
3201 * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3202 * @param peer identity of the peer to switch the address for
3203 * @param address address of the other peer, NULL if other peer
3204 * connected to us
3205 * @param session session to use (or NULL)
3206 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3207 */
3208int
3209GST_neighbours_handle_session_syn_ack (const struct
3210 GNUNET_MessageHeader *message,
3211 const struct
3212 GNUNET_HELLO_Address *address,
3213 struct GNUNET_ATS_Session *session)
3214{
3215 const struct TransportSynMessage *scm;
3216 struct GNUNET_TIME_Absolute ts;
3217 struct NeighbourMapEntry *n;
3218
3219 (void) session;
3220 if (ntohs (message->size) != sizeof(struct TransportSynMessage))
3221 {
3222 GNUNET_break_op (0);
3223 return GNUNET_SYSERR;
3224 }
3225 GNUNET_STATISTICS_update (GST_stats,
3226 gettext_noop
3227 ("# SYN_ACK messages received"),
3228 1, GNUNET_NO);
3229 scm = (const struct TransportSynMessage *) message;
3230 GNUNET_break_op (ntohl (scm->reserved) == 0);
3231 if (NULL == (n = lookup_neighbour (&address->peer)))
3232 {
3233 GNUNET_STATISTICS_update (GST_stats,
3234 gettext_noop
3235 ("# unexpected SYN_ACK messages (no peer)"),
3236 1, GNUNET_NO);
3237 return GNUNET_SYSERR;
3238 }
3239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3240 "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3241 GNUNET_i2s (&address->peer),
3242 GNUNET_TRANSPORT_ps2s (n->state),
3243 print_ack_state (n->ack_state));
3244 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3245 switch (n->state)
3246 {
3247 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3248 GNUNET_break (0);
3249 free_neighbour (n);
3250 return GNUNET_SYSERR;
3251
3252 case GNUNET_TRANSPORT_PS_INIT_ATS:
3253 GNUNET_STATISTICS_update (GST_stats,
3254 gettext_noop (
3255 "# unexpected SYN_ACK messages (not ready)"),
3256 1,
3257 GNUNET_NO);
3258 break;
3259
3260 case GNUNET_TRANSPORT_PS_SYN_SENT:
3261 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3262 {
3263 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3264 "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3265 return GNUNET_OK;
3266 }
3267 set_state_and_timeout (n,
3268 GNUNET_TRANSPORT_PS_CONNECTED,
3269 GNUNET_TIME_relative_to_absolute (
3270 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3271 set_primary_address (n,
3272 n->primary_address.address,
3273 n->primary_address.session,
3274 n->primary_address.bandwidth_in,
3275 n->primary_address.bandwidth_out);
3276 send_session_ack_message (n);
3277 break;
3278
3279 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3280 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3281 GNUNET_STATISTICS_update (GST_stats,
3282 gettext_noop (
3283 "# unexpected SYN_ACK messages (not ready)"),
3284 1,
3285 GNUNET_NO);
3286 break;
3287
3288 case GNUNET_TRANSPORT_PS_CONNECTED:
3289 /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3290 send_session_ack_message (n);
3291 break;
3292
3293 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3294 /* we didn't expect any SYN_ACK, as we are waiting for ATS
3295 to give us a new address... */
3296 GNUNET_STATISTICS_update (GST_stats,
3297 gettext_noop (
3298 "# unexpected SYN_ACK messages (waiting on ATS)"),
3299 1,
3300 GNUNET_NO);
3301 break;
3302
3303 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3304 /* Reconnecting with new address address worked; go back to connected! */
3305 set_state_and_timeout (n,
3306 GNUNET_TRANSPORT_PS_CONNECTED,
3307 GNUNET_TIME_relative_to_absolute (
3308 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3309 send_session_ack_message (n);
3310 break;
3311
3312 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3313 /* new address worked; adopt it and go back to connected! */
3314 set_state_and_timeout (n,
3315 GNUNET_TRANSPORT_PS_CONNECTED,
3316 GNUNET_TIME_relative_to_absolute (
3317 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3318 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3319
3320 /* Set primary addresses */
3321 set_primary_address (n,
3322 n->alternative_address.address,
3323 n->alternative_address.session,
3324 n->alternative_address.bandwidth_in,
3325 n->alternative_address.bandwidth_out);
3326 GNUNET_STATISTICS_update (GST_stats,
3327 gettext_noop (
3328 "# Successful attempts to switch addresses"),
3329 1,
3330 GNUNET_NO);
3331
3332 GNUNET_HELLO_address_free (n->alternative_address.address);
3333 memset (&n->alternative_address,
3334 0,
3335 sizeof(n->alternative_address));
3336 send_session_ack_message (n);
3337 break;
3338
3339 case GNUNET_TRANSPORT_PS_DISCONNECT:
3340 GNUNET_STATISTICS_update (GST_stats,
3341 gettext_noop
3342 ("# unexpected SYN_ACK messages (disconnecting)"),
3343 1, GNUNET_NO);
3344 return GNUNET_SYSERR;
3345
3346 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3347 GNUNET_assert (0);
3348 break;
3349
3350 default:
3351 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3352 "Unhandled state `%s'\n",
3353 GNUNET_TRANSPORT_ps2s (n->state));
3354 GNUNET_break (0);
3355 return GNUNET_SYSERR;
3356 }
3357 return GNUNET_OK;
3358}
3359
3360
3361/**
3362 * A session was terminated. Take note; if needed, try to get
3363 * an alternative address from ATS.
3364 *
3365 * @param peer identity of the peer where the session died
3366 * @param session session that is gone
3367 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3368 * this session was not in use
3369 */
3370int
3371GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3372 struct GNUNET_ATS_Session *session)
3373{
3374 struct NeighbourMapEntry *n;
3375
3376 if (NULL == (n = lookup_neighbour (peer)))
3377 return GNUNET_NO; /* can't affect us */
3378 if (session != n->primary_address.session)
3379 {
3380 /* Free alternative address */
3381 if (session == n->alternative_address.session)
3382 {
3383 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3384 set_state_and_timeout (n,
3385 GNUNET_TRANSPORT_PS_CONNECTED,
3386 n->timeout);
3387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3388 "Session died, cleaning up alternative address\n");
3389 free_address (&n->alternative_address);
3390 }
3391 return GNUNET_NO; /* doesn't affect us further */
3392 }
3393
3394 n->expect_latency_response = GNUNET_NO;
3395 /* The session for neighbour's primary address died */
3396 switch (n->state)
3397 {
3398 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3399 GNUNET_break (0);
3400 free_neighbour (n);
3401 return GNUNET_YES;
3402
3403 case GNUNET_TRANSPORT_PS_INIT_ATS:
3404 GNUNET_break (0);
3405 free_neighbour (n);
3406 return GNUNET_YES;
3407
3408 case GNUNET_TRANSPORT_PS_SYN_SENT:
3409 /* The session used to send the SYN terminated:
3410 * this implies a connect error*/
3411 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3412 "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n",
3413 GST_plugins_a2s (n->primary_address.address),
3414 n->primary_address.session);
3415
3416 /* Destroy the address since it cannot be used */
3417 unset_primary_address (n);
3418 set_state_and_timeout (n,
3419 GNUNET_TRANSPORT_PS_INIT_ATS,
3420 GNUNET_TIME_relative_to_absolute (
3421 ATS_RESPONSE_TIMEOUT));
3422 break;
3423
3424 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3425 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3426 /* error on inbound session; free neighbour entirely */
3427 free_neighbour (n);
3428 return GNUNET_YES;
3429
3430 case GNUNET_TRANSPORT_PS_CONNECTED:
3431 /* Our primary connection died, try a fast reconnect */
3432 unset_primary_address (n);
3433 set_state_and_timeout (n,
3434 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3435 GNUNET_TIME_relative_to_absolute (
3436 ATS_RESPONSE_TIMEOUT));
3437 break;
3438
3439 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3440 /* we don't have an address, how can it go down? */
3441 GNUNET_break (0);
3442 break;
3443
3444 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3445 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3446 "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n",
3447 GST_plugins_a2s (n->primary_address.address),
3448 n->primary_address.session);
3449 /* Destroy the address since it cannot be used */
3450 unset_primary_address (n);
3451 set_state_and_timeout (n,
3452 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3453 GNUNET_TIME_relative_to_absolute (
3454 ATS_RESPONSE_TIMEOUT));
3455 break;
3456
3457 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3458 /* primary went down while we were waiting for SYN_ACK on secondary;
3459 secondary as primary */
3460
3461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3462 "Connection `%s' %p to peer `%s' was terminated while switching, "
3463 "switching to alternative address `%s' %p\n",
3464 GST_plugins_a2s (n->primary_address.address),
3465 n->primary_address.session,
3466 GNUNET_i2s (peer),
3467 GST_plugins_a2s (n->alternative_address.address),
3468 n->alternative_address.session);
3469
3470 /* Destroy the inbound address since it cannot be used */
3471 free_address (&n->primary_address);
3472 n->primary_address = n->alternative_address;
3473 GNUNET_assert (GNUNET_YES ==
3474 GST_ats_is_known (n->primary_address.address,
3475 n->primary_address.session));
3476 memset (&n->alternative_address,
3477 0,
3478 sizeof(struct NeighbourAddress));
3479 set_state_and_timeout (n,
3480 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3481 GNUNET_TIME_relative_to_absolute (
3482 FAST_RECONNECT_TIMEOUT));
3483 break;
3484
3485 case GNUNET_TRANSPORT_PS_DISCONNECT:
3486 unset_primary_address (n);
3487 break;
3488
3489 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3490 /* neighbour was freed and plugins told to terminate session */
3491 return GNUNET_NO;
3492
3493 default:
3494 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3495 "Unhandled state `%s'\n",
3496 GNUNET_TRANSPORT_ps2s (n->state));
3497 GNUNET_break (0);
3498 break;
3499 }
3500 if (NULL != n->task)
3501 GNUNET_SCHEDULER_cancel (n->task);
3502 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3503 return GNUNET_YES;
3504}
3505
3506
3507/**
3508 * We received a 'ACK' message from the other peer.
3509 * If we sent a 'SYN_ACK' last, this means we are now
3510 * connected. Otherwise, do nothing.
3511 *
3512 * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3513 * @param address address of the other peer
3514 * @param session session to use (or NULL)
3515 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3516 */
3517int
3518GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3519 const struct GNUNET_HELLO_Address *address,
3520 struct GNUNET_ATS_Session *session)
3521{
3522 struct NeighbourMapEntry *n;
3523
3524 (void) session;
3525 if (ntohs (message->size) != sizeof(struct GNUNET_MessageHeader))
3526 {
3527 GNUNET_break_op (0);
3528 return GNUNET_SYSERR;
3529 }
3530 GNUNET_STATISTICS_update (GST_stats,
3531 gettext_noop ("# ACK messages received"),
3532 1,
3533 GNUNET_NO);
3534 if (NULL == (n = lookup_neighbour (&address->peer)))
3535 {
3536 GNUNET_break_op (0);
3537 return GNUNET_SYSERR;
3538 }
3539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3540 "Received ACK for peer `%s' in state %s/%s\n",
3541 GNUNET_i2s (&address->peer),
3542 GNUNET_TRANSPORT_ps2s (n->state),
3543 print_ack_state (n->ack_state));
3544
3545 /* Check if we are in a plausible state for having sent
3546 a SYN_ACK. If not, return, otherwise break.
3547
3548 The remote peers sends a ACK as a response for a SYN_ACK
3549 message.
3550
3551 We expect a ACK:
3552 - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3553 now wait for the ACK to finally be connected
3554 - If we sent a SYN_ACK to this peer before */if (((GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3555 (ACK_SEND_ACK != n->ack_state)) ||
3556 (NULL == n->primary_address.address))
3557 {
3558 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3559 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3560 GNUNET_i2s (&address->peer),
3561 GNUNET_TRANSPORT_ps2s (n->state),
3562 print_ack_state (n->ack_state));
3563
3564 GNUNET_STATISTICS_update (GST_stats,
3565 gettext_noop ("# unexpected ACK messages"),
3566 1,
3567 GNUNET_NO);
3568 return GNUNET_OK;
3569 }
3570 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3571 {
3572 /* We tried to switch addresses while being connect. We explicitly wait
3573 * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3574 * so we do not want to set the address as in use! */
3575 return GNUNET_OK;
3576 }
3577 set_state_and_timeout (n,
3578 GNUNET_TRANSPORT_PS_CONNECTED,
3579 GNUNET_TIME_relative_to_absolute (
3580 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3581
3582 if (NULL == n->primary_address.address)
3583 {
3584 /* See issue #3693.
3585 * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3586 * really means we did try (and succeed) to send a SYN and are waiting for
3587 * an ACK.
3588 * That suggests that the primary_address used to be non-NULL, but maybe it
3589 * got reset to NULL without the state being changed appropriately?
3590 */GNUNET_break (0);
3591 return GNUNET_OK;
3592 }
3593
3594 /* Reset backoff for primary address */
3595 GST_ats_block_reset (n->primary_address.address,
3596 n->primary_address.session);
3597 return GNUNET_OK;
3598}
3599
3600
3601/**
3602 * Test if we're connected to the given peer.
3603 *
3604 * @param target peer to test
3605 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3606 */
3607int
3608GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3609{
3610 return test_connected (lookup_neighbour (target));
3611}
3612
3613
3614/**
3615 * Task to asynchronously run #free_neighbour().
3616 *
3617 * @param cls the `struct NeighbourMapEntry` to free
3618 */
3619static void
3620delayed_disconnect (void *cls)
3621{
3622 struct NeighbourMapEntry *n = cls;
3623
3624 n->delayed_disconnect_task = NULL;
3625 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3626 "Disconnecting by request from peer %s\n",
3627 GNUNET_i2s (&n->id));
3628 free_neighbour (n);
3629}
3630
3631
3632void
3633GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer,
3634 const struct GNUNET_MessageHeader *msg)
3635{
3636 struct NeighbourMapEntry *n;
3637 const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3638 struct GNUNET_BANDWIDTH_Value32NBO last;
3639
3640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3641 "Received QUOTA message from peer `%s'\n",
3642 GNUNET_i2s (peer));
3643 if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionQuotaMessage))
3644 {
3645 GNUNET_break_op (0);
3646 GNUNET_STATISTICS_update (GST_stats,
3647 gettext_noop (
3648 "# quota messages ignored (malformed)"),
3649 1,
3650 GNUNET_NO);
3651 return;
3652 }
3653 GNUNET_STATISTICS_update (GST_stats,
3654 gettext_noop
3655 ("# QUOTA messages received"),
3656 1, GNUNET_NO);
3657 sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg;
3658 if (NULL == (n = lookup_neighbour (peer)))
3659 {
3660 /* gone already */
3661 return;
3662 }
3663 last = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3664 GNUNET_BANDWIDTH_value_init (ntohl (
3665 sqm->quota)));
3666 if (last.value__ != n->neighbour_receive_quota.value__)
3667 {
3668 n->neighbour_receive_quota = last;
3669 send_outbound_quota_to_clients (n);
3670 }
3671}
3672
3673
3674/**
3675 * We received a disconnect message from the given peer,
3676 * validate and process.
3677 *
3678 * @param peer sender of the message
3679 * @param msg the disconnect message
3680 */
3681void
3682GST_neighbours_handle_disconnect_message (const struct
3683 GNUNET_PeerIdentity *peer,
3684 const struct
3685 GNUNET_MessageHeader *msg)
3686{
3687 struct NeighbourMapEntry *n;
3688 const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3689
3690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3691 "Received DISCONNECT message from peer `%s'\n",
3692 GNUNET_i2s (peer));
3693 if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionDisconnectMessage))
3694 {
3695 GNUNET_break_op (0);
3696 GNUNET_STATISTICS_update (GST_stats,
3697 gettext_noop
3698 ("# disconnect messages ignored (malformed)"),
3699 1,
3700 GNUNET_NO);
3701 return;
3702 }
3703 GNUNET_STATISTICS_update (GST_stats,
3704 gettext_noop
3705 ("# DISCONNECT messages received"),
3706 1, GNUNET_NO);
3707 sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg;
3708 if (NULL == (n = lookup_neighbour (peer)))
3709 {
3710 /* gone already */
3711 return;
3712 }
3713 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <=
3714 n->connect_ack_timestamp.abs_value_us)
3715 {
3716 GNUNET_STATISTICS_update (GST_stats,
3717 gettext_noop (
3718 "# disconnect messages ignored (timestamp)"),
3719 1,
3720 GNUNET_NO);
3721 return;
3722 }
3723 if (0 != memcmp (peer,
3724 &sdm->public_key,
3725 sizeof(struct GNUNET_PeerIdentity)))
3726 {
3727 GNUNET_break_op (0);
3728 return;
3729 }
3730 if (ntohl (sdm->purpose.size) !=
3731 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
3732 + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
3733 + sizeof(struct GNUNET_TIME_AbsoluteNBO))
3734 {
3735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3736 "DISCONNECT message from peer `%s' has invalid size\n",
3737 GNUNET_i2s (peer));
3738 GNUNET_break_op (0);
3739 return;
3740 }
3741 if (GNUNET_OK !=
3742 GNUNET_CRYPTO_eddsa_verify_ (
3743 GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3744 &sdm->purpose,
3745 &sdm->signature,
3746 &sdm->public_key))
3747 {
3748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3749 "DISCONNECT message from peer `%s' cannot be verified \n",
3750 GNUNET_i2s (peer));
3751 GNUNET_break_op (0);
3752 return;
3753 }
3754 if (NULL == n->delayed_disconnect_task)
3755 {
3756 n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect,
3757 n);
3758 }
3759}
3760
3761
3762/**
3763 * Closure for the #neighbours_iterate() function.
3764 */
3765struct IteratorContext
3766{
3767 /**
3768 * Function to call on each connected neighbour.
3769 */
3770 GST_NeighbourIterator cb;
3771
3772 /**
3773 * Closure for @e cb.
3774 */
3775 void *cb_cls;
3776};
3777
3778
3779/**
3780 * Call the callback from the closure for each neighbour.
3781 *
3782 * @param cls the `struct IteratorContext`
3783 * @param key the hash of the public key of the neighbour
3784 * @param value the `struct NeighbourMapEntry`
3785 * @return #GNUNET_OK (continue to iterate)
3786 */
3787static int
3788neighbours_iterate (void *cls,
3789 const struct GNUNET_PeerIdentity *key,
3790 void *value)
3791{
3792 struct IteratorContext *ic = cls;
3793 struct NeighbourMapEntry *n = value;
3794 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3795 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3796
3797 (void) key;
3798 if (NULL != n->primary_address.address)
3799 {
3800 bandwidth_in = n->primary_address.bandwidth_in;
3801 bandwidth_out = n->primary_address.bandwidth_out;
3802 }
3803 else
3804 {
3805 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3806 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3807 }
3808 ic->cb (ic->cb_cls,
3809 &n->id,
3810 n->primary_address.address,
3811 n->state,
3812 n->timeout,
3813 bandwidth_in, bandwidth_out);
3814 return GNUNET_OK;
3815}
3816
3817
3818/**
3819 * Iterate over all connected neighbours.
3820 *
3821 * @param cb function to call
3822 * @param cb_cls closure for @a cb
3823 */
3824void
3825GST_neighbours_iterate (GST_NeighbourIterator cb,
3826 void *cb_cls)
3827{
3828 struct IteratorContext ic;
3829
3830 if (NULL == neighbours)
3831 return; /* can happen during shutdown */
3832 ic.cb = cb;
3833 ic.cb_cls = cb_cls;
3834 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3835 &neighbours_iterate,
3836 &ic);
3837}
3838
3839
3840/**
3841 * If we have an active connection to the given target, it must be shutdown.
3842 *
3843 * @param target peer to disconnect from
3844 */
3845void
3846GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3847{
3848 struct NeighbourMapEntry *n;
3849
3850 if (NULL == (n = lookup_neighbour (target)))
3851 return; /* not active */
3852 if (GNUNET_YES == test_connected (n))
3853 GNUNET_STATISTICS_update (GST_stats,
3854 gettext_noop (
3855 "# disconnected from peer upon explicit request"),
3856 1,
3857 GNUNET_NO);
3858 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3859 "Forced disconnect from peer %s\n",
3860 GNUNET_i2s (target));
3861 disconnect_neighbour (n);
3862}
3863
3864
3865/**
3866 * Obtain current address information for the given neighbour.
3867 *
3868 * @param peer
3869 * @return address currently used
3870 */
3871const struct GNUNET_HELLO_Address *
3872GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3873{
3874 struct NeighbourMapEntry *n;
3875
3876 n = lookup_neighbour (peer);
3877 if (NULL == n)
3878 return NULL;
3879 return n->primary_address.address;
3880}
3881
3882
3883/**
3884 * Initialize the neighbours subsystem.
3885 *
3886 * @param max_fds maximum number of fds to use
3887 */
3888void
3889GST_neighbours_start (unsigned int max_fds)
3890{
3891 (void) max_fds;
3892 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE,
3893 GNUNET_NO);
3894 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (
3895 UTIL_TRANSMISSION_INTERVAL,
3896 &utilization_transmission,
3897 NULL);
3898}
3899
3900
3901/**
3902 * Disconnect from the given neighbour.
3903 *
3904 * @param cls unused
3905 * @param key hash of neighbour's public key (not used)
3906 * @param value the `struct NeighbourMapEntry` of the neighbour
3907 * @return #GNUNET_OK (continue to iterate)
3908 */
3909static int
3910disconnect_all_neighbours (void *cls,
3911 const struct GNUNET_PeerIdentity *key,
3912 void *value)
3913{
3914 struct NeighbourMapEntry *n = value;
3915
3916 (void) cls;
3917 (void) key;
3918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3919 "Disconnecting peer `%4s' during shutdown\n",
3920 GNUNET_i2s (&n->id));
3921 free_neighbour (n);
3922 return GNUNET_OK;
3923}
3924
3925
3926/**
3927 * Cleanup the neighbours subsystem.
3928 */
3929void
3930GST_neighbours_stop ()
3931{
3932 if (NULL == neighbours)
3933 return;
3934 if (NULL != util_transmission_tk)
3935 {
3936 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3937 util_transmission_tk = NULL;
3938 }
3939 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3940 &disconnect_all_neighbours,
3941 NULL);
3942 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3943 neighbours = NULL;
3944}
3945
3946
3947/* end of file gnunet-service-transport_neighbours.c */