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_ats_service.h"
28 #include "gnunet-service-transport_neighbours.h"
29 #include "gnunet-service-transport_validation.h"
30 #include "gnunet-service-transport.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_constants.h"
33 #include "transport.h"
37 * Size of the neighbour hash map.
39 #define NEIGHBOUR_TABLE_SIZE 256
44 // - have a way to access the currently 'connected' session
45 // (for sending and to notice disconnect of it!)
46 // - have a way to access/update bandwidth/quota information per peer
47 // (for CostReport/TrafficReport callbacks)
50 struct NeighbourMapEntry;
53 * For each neighbour we keep a list of messages
54 * that we still want to transmit to the neighbour.
60 * This is a doubly linked list.
62 struct MessageQueue *next;
65 * This is a doubly linked list.
67 struct MessageQueue *prev;
70 * Function to call once we're done.
72 GST_NeighbourSendContinuation cont;
80 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
81 * stuck together in memory. Allocated at the end of this struct.
83 const char *message_buf;
86 * Size of the message buf
88 size_t message_buf_size;
91 * At what time should we fail?
93 struct GNUNET_TIME_Absolute timeout;
99 * Entry in neighbours.
101 struct NeighbourMapEntry
105 * Head of list of messages we would like to send to this peer;
106 * must contain at most one message per client.
108 struct MessageQueue *messages_head;
111 * Tail of list of messages we would like to send to this peer; must
112 * contain at most one message per client.
114 struct MessageQueue *messages_tail;
117 * Context for address suggestion.
118 * NULL after we are connected.
120 struct GNUNET_ATS_SuggestionContext *asc;
123 * Performance data for the peer.
125 struct GNUNET_TRANSPORT_ATS_Information *ats;
128 * Active session for communicating with the peer.
130 struct Session *session;
133 * Name of the plugin we currently use.
138 * Address used for communicating with the peer, NULL for inbound connections.
143 * Number of bytes in 'addr'.
148 * Public key for this peer. Valid only if the respective flag is set below.
150 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
153 * Identity of this neighbour.
155 struct GNUNET_PeerIdentity id;
158 * ID of task scheduled to run when this peer is about to
159 * time out (will free resources associated with the peer).
161 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
164 * ID of task scheduled to run when we should retry transmitting
165 * the head of the message queue. Actually triggered when the
166 * transmission is timing out (we trigger instantly when we have
167 * a chance of success).
169 GNUNET_SCHEDULER_TaskIdentifier retry_task;
172 * How long until we should consider this peer dead (if we don't
173 * receive another message in the meantime)?
175 struct GNUNET_TIME_Absolute peer_timeout;
178 * Tracker for inbound bandwidth.
180 struct GNUNET_BANDWIDTH_Tracker in_tracker;
183 * How often has the other peer (recently) violated the inbound
184 * traffic limit? Incremented by 10 per violation, decremented by 1
185 * per non-violation (for each time interval).
187 unsigned int quota_violation_count;
190 * Number of values in 'ats' array.
192 unsigned int ats_count;
195 * Have we seen an PONG from this neighbour in the past (and
196 * not had a disconnect since)?
198 // int received_pong;
201 * Do we have a valid public key for this neighbour?
203 int public_key_valid;
206 * Are we already in the process of disconnecting this neighbour?
208 // int in_disconnect;
211 * Do we currently consider this neighbour connected? (as far as
212 * the connect/disconnect callbacks are concerned)?
220 * All known neighbours and their HELLOs.
222 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
225 * Closure for connect_notify_cb and disconnect_notify_cb
227 static void *callback_cls;
230 * Function to call when we connected to a neighbour.
232 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
235 * Function to call when we disconnected from a neighbour.
237 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
241 * Lookup a neighbour entry in the neighbours hash map.
243 * @param pid identity of the peer to look up
244 * @return the entry, NULL if there is no existing record
246 static struct NeighbourMapEntry *
247 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
249 return GNUNET_CONTAINER_multihashmap_get (neighbours,
256 * Check the ready list for the given neighbour and if a plugin is
257 * ready for transmission (and if we have a message), do so!
259 * @param neighbour target peer for which to transmit
262 try_transmission_to_peer (struct NeighbourMapEntry *n)
264 struct MessageQueue *mq;
265 struct GNUNET_TIME_Relative timeout;
268 if (n->messages_head == NULL)
271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
272 "Transmission queue for `%4s' is empty\n",
273 GNUNET_i2s (&n->id));
275 return; /* nothing to do */
277 mq = n->messages_head;
278 GNUNET_CONTAINER_DLL_remove (n->messages_head,
281 ret = papi->send (papi->cls,
284 mq->message_buf_size,
286 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
291 &transmit_send_continuation, mq);
294 /* failure, but 'send' would not call continuation in this case,
295 so we need to do it here! */
296 transmit_send_continuation (mq,
305 * Initialize the neighbours subsystem.
307 * @param cls closure for callbacks
308 * @param connect_cb function to call if we connect to a peer
309 * @param disconnect_cb function to call if we disconnect from a peer
312 GST_neighbours_start (void *cls,
313 GNUNET_TRANSPORT_NotifyConnect connect_cb,
314 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
317 connect_notify_cb = connect_cb;
318 disconnect_notify_cb = disconnect_cb;
319 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
324 * Disconnect from the given neighbour, clean up the record.
326 * @param n neighbour to disconnect from
329 disconnect_neighbour (struct NeighbourMapEntry *n)
331 struct MessageQueue *mq;
335 disconnect_notify_cb (callback_cls,
337 n->is_connected = GNUNET_NO;
339 GNUNET_assert (GNUNET_YES ==
340 GNUNET_CONTAINER_multihashmap_remove (neighbours,
343 while (NULL != (mq = n->messages_head))
345 GNUNET_CONTAINER_DLL_remove (n->messages_head,
352 GNUNET_ATS_suggest_address_cancel (n->asc);
355 GNUNET_array_grow (n->ats,
358 if (NULL != n->plugin_name)
360 GNUNET_free (n->plugin_name);
361 n->plugin_name = NULL;
365 GNUNET_free (n->addr);
375 * Disconnect from the given neighbour.
378 * @param key hash of neighbour's public key (not used)
379 * @param value the 'struct NeighbourMapEntry' of the neighbour
382 disconnect_all_neighbours (void *cls,
383 const GNUNET_HashCode *key,
386 struct NeighbourMapEntry *n = value;
389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390 "Disconnecting peer `%4s', %s\n",
394 disconnect_neighbour (n);
400 * Cleanup the neighbours subsystem.
403 GST_neighbours_stop ()
405 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
406 &disconnect_all_neighbours,
408 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
411 connect_notify_cb = NULL;
412 disconnect_notify_cb = NULL;
417 * For an existing neighbour record, set the active connection to
420 * @param plugin_name name of transport that delivered the PONG
421 * @param address address of the other peer, NULL if other peer
423 * @param address_len number of bytes in address
424 * @param ats performance data
425 * @param ats_count number of entries in ats (excluding 0-termination)
428 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
429 const char *plugin_name,
432 struct Session *session,
433 const struct GNUNET_TRANSPORT_ATS_Information *ats,
436 struct NeighbourMapEntry *n;
438 n = lookup_neighbour (peer);
444 GNUNET_free_non_null (n->addr);
445 n->addr = GNUNET_malloc (address_len);
446 memcpy (n->addr, address, address_len);
447 n->addrlen = address_len;
448 n->session = session;
449 GNUNET_array_grow (n->ats,
454 ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
455 GNUNET_free_non_null (n->plugin_name);
456 n->plugin_name = GNUNET_strdup (plugin_name);
461 * Try to connect to the target peer using the given address
463 * @param cls the 'struct NeighbourMapEntry' of the target
464 * @param public_key public key for the peer, never NULL
465 * @param target identity of the target peer
466 * @param plugin_name name of the plugin
467 * @param plugin_address binary address
468 * @param plugin_address_len length of address
469 * @param bandwidth available bandwidth
470 * @param ats performance data for the address (as far as known)
471 * @param ats_count number of performance records in 'ats'
474 try_connect_using_address (void *cls,
475 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
476 const struct GNUNET_PeerIdentity *target,
477 const char *plugin_name,
478 const void *plugin_address,
479 size_t plugin_address_len,
480 struct GNUNET_BANDWIDTH_Value32NBO bandwidth,
481 const struct GNUNET_TRANSPORT_ATS_Information *ats,
484 struct NeighbourMapEntry *n = cls;
487 if (n->public_key_valid == GNUNET_NO)
489 n->public_key = *public_key;
490 n->public_key_valid = GNUNET_YES;
492 GST_neighbours_switch_to_address (target,
498 if (GNUNET_YES == n->is_connected)
500 n->is_connected = GNUNET_YES;
501 connect_notify_cb (callback_cls,
509 * We've tried to connect but waited long enough and failed. Clean up.
511 * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
512 * @param tc scheduler context
515 neighbour_connect_timeout_task (void *cls,
516 const struct GNUNET_SCHEDULER_TaskContext *tc)
518 struct NeighbourMapEntry *n = cls;
520 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
521 GNUNET_assert (GNUNET_YES ==
522 GNUNET_CONTAINER_multihashmap_remove (neighbours,
525 GNUNET_assert (NULL == n->messages_head);
526 GNUNET_assert (NULL == n->ats);
532 * Try to create a connection to the given target (eventually).
534 * @param target peer to try to connect to
537 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
539 struct NeighbourMapEntry *n;
541 GNUNET_assert (0 != memcmp (target,
543 sizeof (struct GNUNET_PeerIdentity)));
544 n = lookup_neighbour (target);
546 (GNUNET_YES == n->is_connected) )
547 return; /* already connected */
550 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
552 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
553 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
554 MAX_BANDWIDTH_CARRY_S);
555 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
556 &neighbour_connect_timeout_task, n);
557 GNUNET_assert (GNUNET_OK ==
558 GNUNET_CONTAINER_multihashmap_put (neighbours,
561 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
564 return; /* already trying */
565 n->asc = GNUNET_ATS_suggest_address (GST_ats,
567 &try_connect_using_address,
573 * Test if we're connected to the given peer.
575 * @param target peer to test
576 * @return GNUNET_YES if we are connected, GNUNET_NO if not
579 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
581 struct NeighbourMapEntry *n;
583 n = lookup_neighbour (target);
585 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
586 return GNUNET_NO; /* not connected */
592 * Transmit a message to the given target using the active connection.
594 * @param target destination
595 * @param msg message to send
596 * @param msg_size number of bytes in msg
597 * @param timeout when to fail with timeout
598 * @param cont function to call when done
599 * @param cont_cls closure for 'cont'
602 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
605 struct GNUNET_TIME_Relative timeout,
606 GST_NeighbourSendContinuation cont,
609 struct NeighbourMapEntry *n;
610 struct MessageQueue *mq;
612 n = lookup_neighbour (target);
614 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
616 GNUNET_STATISTICS_update (GST_stats,
617 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
625 GNUNET_assert (msg_size >= sizeof (struct GNUNET_MessageHeader));
626 GNUNET_STATISTICS_update (GST_stats,
627 gettext_noop ("# bytes in message queue for other peers"),
630 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
632 mq->cont_cls = cont_cls;
633 /* FIXME: this memcpy can be up to 7% of our total runtime! */
634 memcpy (&mq[1], msg, msg_size);
635 mq->message_buf = (const char*) &mq[1];
636 mq->message_buf_size = msg_size;
637 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
638 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
641 // try_transmission_to_peer (n);
646 * Change the incoming quota for the given peer.
648 * @param neighbour identity of peer to change qutoa for
649 * @param quota new quota
652 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
653 struct GNUNET_BANDWIDTH_Value32NBO quota)
655 struct NeighbourMapEntry *n;
657 n = lookup_neighbour (neighbour);
660 GNUNET_STATISTICS_update (GST_stats,
661 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
666 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
668 if (0 != ntohl (quota.value__))
671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672 "Disconnecting peer `%4s' due to `%s'\n",
676 GNUNET_STATISTICS_update (GST_stats,
677 gettext_noop ("# disconnects due to quota of 0"),
680 disconnect_neighbour (n);
685 * Closure for the neighbours_iterate function.
687 struct IteratorContext
690 * Function to call on each connected neighbour.
692 GST_NeighbourIterator cb;
702 * Call the callback from the closure for each connected neighbour.
704 * @param cls the 'struct IteratorContext'
705 * @param key the hash of the public key of the neighbour
706 * @param value the 'struct NeighbourMapEntry'
707 * @return GNUNET_OK (continue to iterate)
710 neighbours_iterate (void *cls,
711 const GNUNET_HashCode *key,
714 struct IteratorContext *ic = cls;
715 struct NeighbourMapEntry *n = value;
717 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
718 return GNUNET_OK; /* not connected */
719 GNUNET_assert (n->ats_count > 0);
729 * Iterate over all connected neighbours.
731 * @param cb function to call
732 * @param cb_cls closure for cb
735 GST_neighbours_iterate (GST_NeighbourIterator cb,
738 struct IteratorContext ic;
742 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
749 * Peer has been idle for too long. Disconnect.
751 * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
752 * @param tc scheduler context
755 neighbour_idle_timeout_task (void *cls,
756 const struct GNUNET_SCHEDULER_TaskContext *tc)
758 struct NeighbourMapEntry *n = cls;
760 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
761 disconnect_neighbour (n);
766 * We have received a CONNECT. Set the peer to connected.
768 * @param sender peer sending the PONG
769 * @param hdr the PONG message (presumably)
770 * @param plugin_name name of transport that delivered the PONG
771 * @param sender_address address of the other peer, NULL if other peer
773 * @param sender_address_len number of bytes in sender_address
774 * @param bandwidth bandwidth for the connection
775 * @param ats performance data
776 * @param ats_count number of entries in ats (excluding 0-termination)
777 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
780 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
781 const struct GNUNET_MessageHeader *hdr,
782 const char *plugin_name,
783 const void *sender_address,
784 size_t sender_address_len,
785 struct Session *session,
786 const struct GNUNET_TRANSPORT_ATS_Information *ats,
789 struct NeighbourMapEntry *n;
791 if (0 == memcmp (sender,
793 sizeof (struct GNUNET_PeerIdentity)))
796 return GNUNET_SYSERR;
798 n = lookup_neighbour (sender);
800 (n->is_connected == GNUNET_YES) )
801 return GNUNET_OK; /* ats will consider switching and tell us */
804 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
806 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
807 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
808 MAX_BANDWIDTH_CARRY_S);
809 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
810 &neighbour_connect_timeout_task, n);
811 GNUNET_assert (GNUNET_OK ==
812 GNUNET_CONTAINER_multihashmap_put (neighbours,
815 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
817 GST_neighbours_switch_to_address (sender,
825 /* inbound bi-directional connection, just use it */
826 n->is_connected = GNUNET_YES;
827 connect_notify_cb (callback_cls,
831 n->peer_timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
832 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
833 GNUNET_SCHEDULER_cancel (n->timeout_task);
834 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
835 &neighbour_idle_timeout_task,
840 /* try to establish our connection back to the initiator */
841 GST_neighbours_try_connect (sender);
848 * If we have an active connection to the given target, it must be shutdown.
850 * @param target peer to disconnect from
853 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
855 struct NeighbourMapEntry *n;
857 n = lookup_neighbour (target);
858 /* FIXME: send disconnect message to target... */
859 disconnect_neighbour (n);
864 * We have received a DISCONNECT. Set the peer to disconnected.
866 * @param sender peer sending the PONG
867 * @param hdr the PONG message (presumably)
868 * @param plugin_name name of transport that delivered the PONG
869 * @param sender_address address of the other peer, NULL if other peer
871 * @param sender_address_len number of bytes in sender_address
872 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
875 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
876 const struct GNUNET_MessageHeader *hdr,
877 const char *plugin_name,
878 const void *sender_address,
879 size_t sender_address_len)
881 struct NeighbourMapEntry *n;
883 n = lookup_neighbour (sender);
884 /* FIXME: should disconnects have a signature that we should check here? */
885 disconnect_neighbour (n);
890 /* end of file gnunet-service-transport_neighbours.c */