2 This file is part of GNUnet.
3 (C) 2010,2011 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file transport/gnunet-service-transport_neighbours.c
23 * @brief neighbour management
24 * @author Christian Grothoff
27 #include "gnunet-service-transport_neighbours.h"
28 #include "gnunet-service-transport_validation.h"
29 #include "gnunet-service-transport.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet_constants.h"
32 #include "transport.h"
36 * Size of the neighbour hash map.
38 #define NEIGHBOUR_TABLE_SIZE 256
43 // - have a way to access the currently 'connected' session
44 // (for sending and to notice disconnect of it!)
45 // - have a way to access/update bandwidth/quota information per peer
46 // (for CostReport/TrafficReport callbacks)
49 struct NeighbourMapEntry;
52 * For each neighbour we keep a list of messages
53 * that we still want to transmit to the neighbour.
59 * This is a doubly linked list.
61 struct MessageQueue *next;
64 * This is a doubly linked list.
66 struct MessageQueue *prev;
69 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
70 * stuck together in memory. Allocated at the end of this struct.
72 const char *message_buf;
75 * Size of the message buf
77 size_t message_buf_size;
80 * Client responsible for queueing the message; used to check that a
81 * client has no two messages pending for the same target and to
82 * notify the client of a successful transmission; NULL if this is
83 * an internal message.
85 struct TransportClient *client;
88 * At what time should we fail?
90 struct GNUNET_TIME_Absolute timeout;
93 * Internal message of the transport system that should not be
94 * included in the usual SEND-SEND_OK transmission confirmation
95 * traffic management scheme. Typically, "internal_msg" will
96 * be set whenever "client" is NULL (but it is not strictly
102 * How important is the message?
104 unsigned int priority;
110 * Entry in neighbours.
112 struct NeighbourMapEntry
116 * Head of list of messages we would like to send to this peer;
117 * must contain at most one message per client.
119 struct MessageQueue *messages_head;
122 * Tail of list of messages we would like to send to this peer; must
123 * contain at most one message per client.
125 struct MessageQueue *messages_tail;
128 * Context for validation address iteration.
129 * NULL after we are connected.
131 struct GST_ValidationIteratorContext *vic;
134 * Performance data for the peer.
136 struct GNUNET_TRANSPORT_ATS_Information *ats;
139 * Public key for this peer. Valid only if the respective flag is set below.
141 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
144 * Identity of this neighbour.
146 struct GNUNET_PeerIdentity id;
149 * ID of task scheduled to run when this peer is about to
150 * time out (will free resources associated with the peer).
152 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
155 * ID of task scheduled to run when we should retry transmitting
156 * the head of the message queue. Actually triggered when the
157 * transmission is timing out (we trigger instantly when we have
158 * a chance of success).
160 GNUNET_SCHEDULER_TaskIdentifier retry_task;
163 * How long until we should consider this peer dead (if we don't
164 * receive another message in the meantime)?
166 struct GNUNET_TIME_Absolute peer_timeout;
169 * Tracker for inbound bandwidth.
171 struct GNUNET_BANDWIDTH_Tracker in_tracker;
174 * The latency we have seen for this particular address for
175 * this particular peer. This latency may have been calculated
176 * over multiple transports. This value reflects how long it took
177 * us to receive a response when SENDING via this particular
178 * transport/neighbour/address combination!
180 * FIXME: we need to periodically send PINGs to update this
181 * latency (at least more often than the current "huge" (11h?)
184 struct GNUNET_TIME_Relative latency;
187 * How often has the other peer (recently) violated the inbound
188 * traffic limit? Incremented by 10 per violation, decremented by 1
189 * per non-violation (for each time interval).
191 unsigned int quota_violation_count;
194 * DV distance to this peer (1 if no DV is used).
199 * Number of values in 'ats' array.
201 unsigned int ats_count;
204 * Have we seen an PONG from this neighbour in the past (and
205 * not had a disconnect since)?
210 * Do we have a valid public key for this neighbour?
212 int public_key_valid;
215 * Are we already in the process of disconnecting this neighbour?
220 * Do we currently consider this neighbour connected? (as far as
221 * the connect/disconnect callbacks are concerned)?
229 * All known neighbours and their HELLOs.
231 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
234 * Closure for connect_notify_cb and disconnect_notify_cb
236 static void *callback_cls;
239 * Function to call when we connected to a neighbour.
241 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
244 * Function to call when we disconnected from a neighbour.
246 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
250 * Lookup a neighbour entry in the neighbours hash map.
252 * @param pid identity of the peer to look up
253 * @return the entry, NULL if there is no existing record
255 static struct NeighbourMapEntry *
256 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
258 return GNUNET_CONTAINER_multihashmap_get (neighbours,
265 * Check the ready list for the given neighbour and if a plugin is
266 * ready for transmission (and if we have a message), do so!
268 * @param neighbour target peer for which to transmit
271 try_transmission_to_peer (struct NeighbourMapEntry *n)
273 struct MessageQueue *mq;
274 struct GNUNET_TIME_Relative timeout;
277 if (n->messages_head == NULL)
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Transmission queue for `%4s' is empty\n",
282 GNUNET_i2s (&n->id));
284 return; /* nothing to do */
286 mq = n->messages_head;
287 GNUNET_CONTAINER_DLL_remove (n->messages_head,
290 ret = papi->send (papi->cls,
293 mq->message_buf_size,
295 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
300 &transmit_send_continuation, mq);
303 /* failure, but 'send' would not call continuation in this case,
304 so we need to do it here! */
305 transmit_send_continuation (mq,
314 * Initialize the neighbours subsystem.
316 * @param cls closure for callbacks
317 * @param connect_cb function to call if we connect to a peer
318 * @param disconnect_cb function to call if we disconnect from a peer
321 GST_neighbours_start (void *cls,
322 GNUNET_TRANSPORT_NotifyConnect connect_cb,
323 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
326 connect_notify_cb = connect_cb;
327 disconnect_notify_cb = disconnect_cb;
328 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
333 * Disconnect from the given neighbour, clean up the record.
335 * @param n neighbour to disconnect from
338 disconnect_neighbour (struct NeighbourMapEntry *n)
340 struct MessageQueue *mq;
342 disconnect_notify_cb (callback_cls,
344 GNUNET_assert (GNUNET_YES ==
345 GNUNET_CONTAINER_multihashmap_remove (neighbours,
348 while (NULL != (mq = n->messages_head))
350 GNUNET_CONTAINER_DLL_remove (n->messages_head,
357 GST_validation_get_addresses_cancel (n->vic);
360 GNUNET_array_grow (n->ats,
368 * Disconnect from the given neighbour.
371 * @param key hash of neighbour's public key (not used)
372 * @param value the 'struct NeighbourMapEntry' of the neighbour
375 disconnect_all_neighbours (void *cls,
376 const GNUNET_HashCode *key,
379 struct NeighbourMapEntry *n = value;
382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
383 "Disconnecting peer `%4s', %s\n",
387 disconnect_neighbour (n);
393 * Cleanup the neighbours subsystem.
396 GST_neighbours_stop ()
398 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
399 &disconnect_all_neighbours,
401 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
404 connect_notify_cb = NULL;
405 disconnect_notify_cb = NULL;
410 * Try to connect to the target peer using the given address
413 * @param cls the 'struct NeighbourMapEntry' of the target
414 * @param public_key public key for the peer, never NULL
415 * @param target identity of the target peer
416 * @param valid_until is ZERO if we never validated the address,
417 * otherwise a time up to when we consider it (or was) valid
418 * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO)
419 * is ZERO if the address is considered valid (no validation needed)
420 * otherwise a time in the future if we're currently denying re-validation
421 * @param plugin_name name of the plugin
422 * @param plugin_address binary address
423 * @param plugin_address_len length of address
426 try_connect_using_address (void *cls,
427 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
428 const struct GNUNET_PeerIdentity *target,
429 struct GNUNET_TIME_Absolute valid_until,
430 struct GNUNET_TIME_Absolute validation_block,
431 const char *plugin_name,
432 const void *plugin_address,
433 size_t plugin_address_len)
435 struct NeighbourMapEntry *n = cls;
437 if (n->public_key_valid == GNUNET_NO)
439 n->public_key = *public_key;
440 n->public_key_valid = GNUNET_YES;
442 if (GNUNET_TIME_absolute_get_remaining (valid_until).rel_value == 0)
443 return; /* address is not valid right now */
444 /* FIXME: do ATS here! */
450 * We've tried to connect but waited long enough and failed. Clean up.
452 * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
453 * @param tc scheduler context
456 neighbour_connect_timeout_task (void *cls,
457 const struct GNUNET_SCHEDULER_TaskContext *tc)
459 struct NeighbourMapEntry *n = cls;
461 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
462 GNUNET_assert (GNUNET_YES ==
463 GNUNET_CONTAINER_multihashmap_remove (neighbours,
466 GNUNET_assert (NULL == n->messages_head);
467 GNUNET_assert (NULL == n->ats);
473 * Try to create a connection to the given target (eventually).
475 * @param target peer to try to connect to
478 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
480 struct NeighbourMapEntry *n;
482 GNUNET_assert (0 != memcmp (target,
484 sizeof (struct GNUNET_PeerIdentity)));
485 n = lookup_neighbour (target);
487 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value > 0) )
488 return; /* already connected */
491 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
493 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
494 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
495 MAX_BANDWIDTH_CARRY_S);
496 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
497 n->distance = UINT32_MAX;
498 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
499 &neighbour_connect_timeout_task, n);
500 GNUNET_assert (GNUNET_OK ==
501 GNUNET_CONTAINER_multihashmap_put (neighbours,
504 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
507 return; /* already trying */
508 n->vic = GST_validation_get_addresses (target,
510 &try_connect_using_address,
516 * Test if we're connected to the given peer.
518 * @param target peer to test
519 * @return GNUNET_YES if we are connected, GNUNET_NO if not
522 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
524 struct NeighbourMapEntry *n;
526 n = lookup_neighbour (target);
528 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
529 return GNUNET_NO; /* not connected */
535 * Transmit a message to the given target using the active connection.
537 * @param target destination
538 * @param msg message to send
539 * @param timeout when to fail with timeout
540 * @param cont function to call when done
541 * @param cont_cls closure for 'cont'
544 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
545 const struct GNUNET_MessageHeader *msg,
546 struct GNUNET_TIME_Relative timeout,
547 GST_NeighbourSendContinuation cont,
550 struct NeighbourMapEntry *n;
551 struct MessageQueue *mq;
552 uint16_t message_buf_size;
554 n = lookup_neighbour (target);
556 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
558 GNUNET_STATISTICS_update (GST_stats,
559 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
567 message_buf_size = ntohs (msg->size);
568 GNUNET_assert (message_buf_size >= sizeof (struct GNUNET_MessageHeader));
569 GNUNET_STATISTICS_update (GST_stats,
570 gettext_noop ("# bytes in message queue for other peers"),
573 mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
574 /* FIXME: this memcpy can be up to 7% of our total runtime! */
575 memcpy (&mq[1], msg, message_buf_size);
576 mq->message_buf = (const char*) &mq[1];
577 mq->message_buf_size = message_buf_size;
578 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
579 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
582 // try_transmission_to_peer (n);
587 * Change the incoming quota for the given peer.
589 * @param neighbour identity of peer to change qutoa for
590 * @param quota new quota
593 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
594 struct GNUNET_BANDWIDTH_Value32NBO quota)
596 struct NeighbourMapEntry *n;
598 n = lookup_neighbour (neighbour);
601 GNUNET_STATISTICS_update (GST_stats,
602 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
607 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
609 if (0 != ntohl (quota.value__))
612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613 "Disconnecting peer `%4s' due to `%s'\n",
617 GNUNET_STATISTICS_update (GST_stats,
618 gettext_noop ("# disconnects due to quota of 0"),
621 disconnect_neighbour (n);
626 * Closure for the neighbours_iterate function.
628 struct IteratorContext
631 * Function to call on each connected neighbour.
633 GST_NeighbourIterator cb;
643 * Call the callback from the closure for each connected neighbour.
645 * @param cls the 'struct IteratorContext'
646 * @param key the hash of the public key of the neighbour
647 * @param value the 'struct NeighbourMapEntry'
648 * @return GNUNET_OK (continue to iterate)
651 neighbours_iterate (void *cls,
652 const GNUNET_HashCode *key,
655 struct IteratorContext *ic = cls;
656 struct NeighbourMapEntry *n = value;
658 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
659 return GNUNET_OK; /* not connected */
660 GNUNET_assert (n->ats_count > 0);
670 * Iterate over all connected neighbours.
672 * @param cb function to call
673 * @param cb_cls closure for cb
676 GST_neighbours_iterate (GST_NeighbourIterator cb,
679 struct IteratorContext ic;
683 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
690 * We have received a CONNECT. Set the peer to connected.
692 * @param sender peer sending the PONG
693 * @param hdr the PONG message (presumably)
694 * @param plugin_name name of transport that delivered the PONG
695 * @param sender_address address of the other peer, NULL if other peer
697 * @param sender_address_len number of bytes in sender_address
698 * @param ats performance data
699 * @param ats_count number of entries in ats (excluding 0-termination)
700 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
703 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
704 const struct GNUNET_MessageHeader *hdr,
705 const char *plugin_name,
706 const void *sender_address,
707 size_t sender_address_len,
708 struct Session *session,
709 const struct GNUNET_TRANSPORT_ATS_Information *ats,
712 struct NeighbourMapEntry *n;
714 if (0 == memcmp (sender,
716 sizeof (struct GNUNET_PeerIdentity)))
719 return GNUNET_SYSERR;
721 n = lookup_neighbour (sender);
723 (n->is_connected == GNUNET_YES) )
725 /* already connected */
728 // FIXME: ATS: switch session!?
729 // FIXME: merge/update ats?
735 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
737 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
738 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
739 MAX_BANDWIDTH_CARRY_S);
740 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
741 n->distance = UINT32_MAX;
742 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
743 &neighbour_connect_timeout_task, n);
744 GNUNET_assert (GNUNET_OK ==
745 GNUNET_CONTAINER_multihashmap_put (neighbours,
748 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
751 GNUNET_array_grow (n->ats,
757 GNUNET_array_grow (n->ats,
762 sizeof (struct GNUNET_TRANSPORT_ATS_Information) * ats_count);
767 // FIXME: ATS: switch session!?
768 // n->session = session;
770 n->is_connected = GNUNET_YES;
771 connect_notify_cb (callback_cls,
780 * If we have an active connection to the given target, it must be shutdown.
782 * @param target peer to disconnect from
785 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
787 struct NeighbourMapEntry *n;
789 n = lookup_neighbour (target);
790 /* FIXME: send disconnect message to target... */
791 disconnect_neighbour (n);
796 * We have received a DISCONNECT. Set the peer to disconnected.
798 * @param sender peer sending the PONG
799 * @param hdr the PONG message (presumably)
800 * @param plugin_name name of transport that delivered the PONG
801 * @param sender_address address of the other peer, NULL if other peer
803 * @param sender_address_len number of bytes in sender_address
804 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
807 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
808 const struct GNUNET_MessageHeader *hdr,
809 const char *plugin_name,
810 const void *sender_address,
811 size_t sender_address_len)
813 struct NeighbourMapEntry *n;
815 n = lookup_neighbour (sender);
816 /* FIXME: should disconnects have a signature that we should check here? */
817 disconnect_neighbour (n);
822 /* end of file gnunet-service-transport_neighbours.c */