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?
223 * All known neighbours and their HELLOs.
225 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
228 * Closure for connect_notify_cb and disconnect_notify_cb
230 static void *callback_cls;
233 * Function to call when we connected to a neighbour.
235 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
238 * Function to call when we disconnected from a neighbour.
240 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
244 * Lookup a neighbour entry in the neighbours hash map.
246 * @param pid identity of the peer to look up
247 * @return the entry, NULL if there is no existing record
249 static struct NeighbourMapEntry *
250 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
252 return GNUNET_CONTAINER_multihashmap_get (neighbours,
259 * Check the ready list for the given neighbour and if a plugin is
260 * ready for transmission (and if we have a message), do so!
262 * @param neighbour target peer for which to transmit
265 try_transmission_to_peer (struct NeighbourMapEntry *n)
267 struct ReadyList *rl;
268 struct MessageQueue *mq;
269 struct GNUNET_TIME_Relative timeout;
273 if (n->messages_head == NULL)
276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
277 "Transmission queue for `%4s' is empty\n",
278 GNUNET_i2s (&n->id));
280 return; /* nothing to do */
283 mq = n->messages_head;
284 force_address = GNUNET_YES;
285 if (mq->specific_address == NULL)
288 mq->specific_address = get_preferred_ats_address(n);
289 GNUNET_STATISTICS_update (stats,
290 gettext_noop ("# transport selected peer address freely"),
293 force_address = GNUNET_NO;
295 if (mq->specific_address == NULL)
297 GNUNET_STATISTICS_update (stats,
298 gettext_noop ("# transport failed to selected peer address"),
301 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
302 if (timeout.rel_value == 0)
305 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
306 "No destination address available to transmit message of size %u to peer `%4s'\n",
307 mq->message_buf_size,
308 GNUNET_i2s (&mq->neighbour_id));
310 GNUNET_STATISTICS_update (stats,
311 gettext_noop ("# bytes in message queue for other peers"),
312 - (int64_t) mq->message_buf_size,
314 GNUNET_STATISTICS_update (stats,
315 gettext_noop ("# bytes discarded (no destination address available)"),
316 mq->message_buf_size,
318 if (mq->client != NULL)
319 transmit_send_ok (mq->client, n, &n->id, GNUNET_NO);
320 GNUNET_CONTAINER_DLL_remove (n->messages_head,
324 return; /* nobody ready */
326 GNUNET_STATISTICS_update (stats,
327 gettext_noop ("# message delivery deferred (no address)"),
330 if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
331 GNUNET_SCHEDULER_cancel (n->retry_task);
332 n->retry_task = GNUNET_SCHEDULER_add_delayed (timeout,
333 &retry_transmission_task,
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "No validated destination address available to transmit message of size %u to peer `%4s', will wait %llums to find an address.\n",
338 mq->message_buf_size,
339 GNUNET_i2s (&mq->neighbour_id),
342 /* FIXME: might want to trigger peerinfo lookup here
343 (unless that's already pending...) */
346 GNUNET_CONTAINER_DLL_remove (n->messages_head,
349 if (mq->specific_address->connected == GNUNET_NO)
350 mq->specific_address->connect_attempts++;
351 rl = mq->specific_address->ready_list;
352 mq->plugin = rl->plugin;
353 if (!mq->internal_msg)
354 mq->specific_address->in_transmit = GNUNET_YES;
356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357 "Sending message of size %u for `%4s' to `%s' via plugin `%s'\n",
358 mq->message_buf_size,
360 (mq->specific_address->addr != NULL)
361 ? a2s (mq->plugin->short_name,
362 mq->specific_address->addr,
363 mq->specific_address->addrlen)
365 rl->plugin->short_name);
367 GNUNET_STATISTICS_update (stats,
368 gettext_noop ("# bytes in message queue for other peers"),
369 - (int64_t) mq->message_buf_size,
371 GNUNET_STATISTICS_update (stats,
372 gettext_noop ("# bytes pending with plugins"),
373 mq->message_buf_size,
376 GNUNET_CONTAINER_DLL_insert (n->cont_head,
380 ret = rl->plugin->api->send (rl->plugin->api->cls,
383 mq->message_buf_size,
385 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
386 mq->specific_address->session,
387 mq->specific_address->addr,
388 mq->specific_address->addrlen,
390 &transmit_send_continuation, mq);
393 /* failure, but 'send' would not call continuation in this case,
394 so we need to do it here! */
395 transmit_send_continuation (mq,
404 * Initialize the neighbours subsystem.
406 * @param cls closure for callbacks
407 * @param connect_cb function to call if we connect to a peer
408 * @param disconnect_cb function to call if we disconnect from a peer
411 GST_neighbours_start (void *cls,
412 GNUNET_TRANSPORT_NotifyConnect connect_cb,
413 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
416 connect_notify_cb = connect_cb;
417 disconnect_notify_cb = disconnect_cb;
418 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
423 * Disconnect from the given neighbour, clean up the record.
425 * @param n neighbour to disconnect from
428 disconnect_neighbour (struct NeighbourMapEntry *n)
430 struct MessageQueue *mq;
432 disconnect_notify_cb (callback_cls,
434 GNUNET_assert (GNUNET_YES ==
435 GNUNET_CONTAINER_multihashmap_remove (neighbours,
438 while (NULL != (mq = n->messages_head))
440 GNUNET_CONTAINER_DLL_remove (n->messages_head,
447 GST_validation_get_addresses_cancel (n->vic);
450 GNUNET_array_grow (n->ats,
458 * Disconnect from the given neighbour.
461 * @param key hash of neighbour's public key (not used)
462 * @param value the 'struct NeighbourMapEntry' of the neighbour
465 disconnect_all_neighbours (void *cls,
466 const GNUNET_HashCode *key,
469 struct NeighbourMapEntry *n = value;
472 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
473 "Disconnecting peer `%4s', %s\n",
477 disconnect_neighbour (n);
483 * Cleanup the neighbours subsystem.
486 GST_neighbours_stop ()
488 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
489 &disconnect_all_neighbours,
491 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
494 connect_notify_cb = NULL;
495 disconnect_notify_cb = NULL;
500 * Try to connect to the target peer using the given address
503 * @param cls the 'struct NeighbourMapEntry' of the target
504 * @param public_key public key for the peer, never NULL
505 * @param target identity of the target peer
506 * @param valid_until is ZERO if we never validated the address,
507 * otherwise a time up to when we consider it (or was) valid
508 * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO)
509 * is ZERO if the address is considered valid (no validation needed)
510 * otherwise a time in the future if we're currently denying re-validation
511 * @param plugin_name name of the plugin
512 * @param plugin_address binary address
513 * @param plugin_address_len length of address
516 try_connect_using_address (void *cls,
517 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
518 const struct GNUNET_PeerIdentity *target,
519 struct GNUNET_TIME_Absolute valid_until,
520 struct GNUNET_TIME_Absolute validation_block,
521 const char *plugin_name,
522 const void *plugin_address,
523 size_t plugin_address_len)
525 struct NeighbourMapEntry *n = cls;
527 if (n->public_key_valid == GNUNET_NO)
529 n->public_key = *public_key;
530 n->public_key_valid = GNUNET_YES;
532 if (GNUNET_TIME_absolute_get_remaining (valid_until).rel_value == 0)
533 return; /* address is not valid right now */
534 /* FIXME: do ATS here! */
540 * We've tried to connect but waited long enough and failed. Clean up.
542 * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
543 * @param tc scheduler context
546 neighbour_connect_timeout_task (void *cls,
547 const struct GNUNET_SCHEDULER_TaskContext *tc)
549 struct NeighbourMapEntry *n = cls;
551 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
552 GNUNET_assert (GNUNET_YES ==
553 GNUNET_CONTAINER_multihashmap_remove (neighbours,
556 GNUNET_assert (NULL == n->messages_head);
557 GNUNET_assert (NULL == n->ats);
563 * Try to create a connection to the given target (eventually).
565 * @param target peer to try to connect to
568 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
570 struct NeighbourMapEntry *n;
572 GNUNET_assert (0 != memcmp (target,
574 sizeof (struct GNUNET_PeerIdentity)));
575 n = lookup_neighbour (target);
577 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value > 0) )
578 return; /* already connected */
581 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
583 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
584 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
585 MAX_BANDWIDTH_CARRY_S);
586 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
587 n->distance = UINT32_MAX;
588 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
589 &neighbour_connect_timeout_task, n);
590 GNUNET_assert (GNUNET_OK ==
591 GNUNET_CONTAINER_multihashmap_put (neighbours,
594 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
597 return; /* already trying */
598 n->vic = GST_validation_get_addresses (target,
600 &try_connect_using_address,
606 * Test if we're connected to the given peer.
608 * @param target peer to test
609 * @return GNUNET_YES if we are connected, GNUNET_NO if not
612 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
614 struct NeighbourMapEntry *n;
616 n = lookup_neighbour (target);
618 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
619 return GNUNET_NO; /* not connected */
625 * Transmit a message to the given target using the active connection.
627 * @param target destination
628 * @param msg message to send
629 * @param timeout when to fail with timeout
630 * @param cont function to call when done
631 * @param cont_cls closure for 'cont'
634 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
635 const struct GNUNET_MessageHeader *msg,
636 struct GNUNET_TIME_Relative timeout,
637 GST_NeighbourSendContinuation cont,
640 struct NeighbourMapEntry *n;
641 struct MessageQueue *mq;
642 uint16_t message_buf_size;
644 n = lookup_neighbour (target);
646 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
648 GNUNET_STATISTICS_update (GST_stats,
649 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
657 message_buf_size = ntohs (msg->size);
658 GNUNET_assert (message_buf_size >= sizeof (struct GNUNET_MessageHeader));
659 GNUNET_STATISTICS_update (GST_stats,
660 gettext_noop ("# bytes in message queue for other peers"),
663 mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
664 /* FIXME: this memcpy can be up to 7% of our total runtime! */
665 memcpy (&mq[1], msg, message_buf_size);
666 mq->message_buf = (const char*) &mq[1];
667 mq->message_buf_size = message_buf_size;
668 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
669 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
672 // try_transmission_to_peer (n);
677 * Change the incoming quota for the given peer.
679 * @param neighbour identity of peer to change qutoa for
680 * @param quota new quota
683 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
684 struct GNUNET_BANDWIDTH_Value32NBO quota)
686 struct NeighbourMapEntry *n;
688 n = lookup_neighbour (neighbour);
691 GNUNET_STATISTICS_update (GST_stats,
692 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
697 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
699 if (0 != ntohl (quota.value__))
702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
703 "Disconnecting peer `%4s' due to `%s'\n",
707 GNUNET_STATISTICS_update (GST_stats,
708 gettext_noop ("# disconnects due to quota of 0"),
711 disconnect_neighbour (n);
716 * Closure for the neighbours_iterate function.
718 struct IteratorContext
721 * Function to call on each connected neighbour.
723 GST_NeighbourIterator cb;
733 * Call the callback from the closure for each connected neighbour.
735 * @param cls the 'struct IteratorContext'
736 * @param key the hash of the public key of the neighbour
737 * @param value the 'struct NeighbourMapEntry'
738 * @return GNUNET_OK (continue to iterate)
741 neighbours_iterate (void *cls,
742 const GNUNET_HashCode *key,
745 struct IteratorContext *ic = cls;
746 struct NeighbourMapEntry *n = value;
748 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
749 return GNUNET_OK; /* not connected */
750 GNUNET_assert (n->ats_count > 0);
760 * Iterate over all connected neighbours.
762 * @param cb function to call
763 * @param cb_cls closure for cb
766 GST_neighbours_iterate (GST_NeighbourIterator cb,
769 struct IteratorContext ic;
773 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
780 * We have received a PONG. Update lifeness of the neighbour.
782 * @param sender peer sending the PONG
783 * @param hdr the PONG message (presumably)
784 * @param plugin_name name of transport that delivered the PONG
785 * @param sender_address address of the other peer, NULL if other peer
787 * @param sender_address_len number of bytes in sender_address
788 * @param ats performance data
789 * @param ats_count number of entries in ats (excluding 0-termination)
790 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
793 GST_neighbours_handle_pong (const struct GNUNET_PeerIdentity *sender,
794 const struct GNUNET_MessageHeader *hdr,
795 const char *plugin_name,
796 const void *sender_address,
797 size_t sender_address_len,
798 const struct GNUNET_TRANSPORT_ATS_Information *ats,
801 return GNUNET_SYSERR;
806 * We have received a CONNECT. Set the peer to connected.
808 * @param sender peer sending the PONG
809 * @param hdr the PONG message (presumably)
810 * @param plugin_name name of transport that delivered the PONG
811 * @param sender_address address of the other peer, NULL if other peer
813 * @param sender_address_len number of bytes in sender_address
814 * @param ats performance data
815 * @param ats_count number of entries in ats (excluding 0-termination)
816 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
819 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
820 const struct GNUNET_MessageHeader *hdr,
821 const char *plugin_name,
822 const void *sender_address,
823 size_t sender_address_len,
824 struct Session *session,
825 const struct GNUNET_TRANSPORT_ATS_Information *ats,
828 struct NeighbourMapEntry *n;
830 if (0 == memcmp (sender,
832 sizeof (struct GNUNET_PeerIdentity)))
835 return GNUNET_SYSERR;
837 n = lookup_neighbour (sender);
839 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value > 0) )
841 /* already connected */
842 // FIXME: switch session!?
847 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
849 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
850 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
851 MAX_BANDWIDTH_CARRY_S);
852 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
853 n->distance = UINT32_MAX;
854 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
855 &neighbour_connect_timeout_task, n);
856 GNUNET_assert (GNUNET_OK ==
857 GNUNET_CONTAINER_multihashmap_put (neighbours,
860 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
862 // FIXME: mark connected, etc?
869 * If we have an active connection to the given target, it must be shutdown.
871 * @param target peer to disconnect from
874 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
876 struct NeighbourMapEntry *n;
878 n = lookup_neighbour (target);
879 /* FIXME: send disconnect message to target... */
880 disconnect_neighbour (n);
885 * We have received a DISCONNECT. Set the peer to disconnected.
887 * @param sender peer sending the PONG
888 * @param hdr the PONG message (presumably)
889 * @param plugin_name name of transport that delivered the PONG
890 * @param sender_address address of the other peer, NULL if other peer
892 * @param sender_address_len number of bytes in sender_address
893 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
896 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
897 const struct GNUNET_MessageHeader *hdr,
898 const char *plugin_name,
899 const void *sender_address,
900 size_t sender_address_len)
902 struct NeighbourMapEntry *n;
904 n = lookup_neighbour (sender);
905 /* FIXME: should disconnects have a signature that we should check here? */
906 disconnect_neighbour (n);
911 /* end of file gnunet-service-transport_neighbours.c */