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.h"
29 #include "gnunet_peerinfo_service.h"
30 #include "gnunet_constants.h"
34 * Size of the neighbour hash map.
36 #define NEIGHBOUR_TABLE_SIZE 256
40 // - have a way to access the currently 'connected' session
41 // (for sending and to notice disconnect of it!)
42 // - have a way to access/update bandwidth/quota information per peer
43 // (for CostReport/TrafficReport callbacks)
46 struct NeighbourMapEntry;
49 * For each neighbour we keep a list of messages
50 * that we still want to transmit to the neighbour.
56 * This is a doubly linked list.
58 struct MessageQueue *next;
61 * This is a doubly linked list.
63 struct MessageQueue *prev;
66 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
67 * stuck together in memory. Allocated at the end of this struct.
69 const char *message_buf;
72 * Size of the message buf
74 size_t message_buf_size;
77 * Client responsible for queueing the message; used to check that a
78 * client has no two messages pending for the same target and to
79 * notify the client of a successful transmission; NULL if this is
80 * an internal message.
82 struct TransportClient *client;
85 * At what time should we fail?
87 struct GNUNET_TIME_Absolute timeout;
90 * Internal message of the transport system that should not be
91 * included in the usual SEND-SEND_OK transmission confirmation
92 * traffic management scheme. Typically, "internal_msg" will
93 * be set whenever "client" is NULL (but it is not strictly
99 * How important is the message?
101 unsigned int priority;
107 * Entry in neighbours.
109 struct NeighbourMapEntry
113 * Head of list of messages we would like to send to this peer;
114 * must contain at most one message per client.
116 struct MessageQueue *messages_head;
119 * Tail of list of messages we would like to send to this peer; must
120 * contain at most one message per client.
122 struct MessageQueue *messages_tail;
125 * Context for peerinfo iteration.
126 * NULL after we are done processing peerinfo's information.
128 struct GNUNET_PEERINFO_IteratorContext *piter;
131 * Performance data for the peer.
133 struct GNUNET_TRANSPORT_ATS_Information *ats;
136 * Public key for this peer. Valid only if the respective flag is set below.
138 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
141 * Identity of this neighbour.
143 struct GNUNET_PeerIdentity id;
146 * ID of task scheduled to run when this peer is about to
147 * time out (will free resources associated with the peer).
149 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
152 * ID of task scheduled to run when we should retry transmitting
153 * the head of the message queue. Actually triggered when the
154 * transmission is timing out (we trigger instantly when we have
155 * a chance of success).
157 GNUNET_SCHEDULER_TaskIdentifier retry_task;
160 * How long until we should consider this peer dead (if we don't
161 * receive another message in the meantime)?
163 struct GNUNET_TIME_Absolute peer_timeout;
166 * Tracker for inbound bandwidth.
168 struct GNUNET_BANDWIDTH_Tracker in_tracker;
171 * The latency we have seen for this particular address for
172 * this particular peer. This latency may have been calculated
173 * over multiple transports. This value reflects how long it took
174 * us to receive a response when SENDING via this particular
175 * transport/neighbour/address combination!
177 * FIXME: we need to periodically send PINGs to update this
178 * latency (at least more often than the current "huge" (11h?)
181 struct GNUNET_TIME_Relative latency;
184 * How often has the other peer (recently) violated the inbound
185 * traffic limit? Incremented by 10 per violation, decremented by 1
186 * per non-violation (for each time interval).
188 unsigned int quota_violation_count;
191 * DV distance to this peer (1 if no DV is used).
196 * Number of values in 'ats' array.
198 unsigned int ats_count;
201 * Have we seen an PONG from this neighbour in the past (and
202 * not had a disconnect since)?
207 * Do we have a valid public key for this neighbour?
209 int public_key_valid;
212 * Are we already in the process of disconnecting this neighbour?
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 ReadyList *rl;
265 struct MessageQueue *mq;
266 struct GNUNET_TIME_Relative timeout;
270 if (n->messages_head == NULL)
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274 "Transmission queue for `%4s' is empty\n",
275 GNUNET_i2s (&n->id));
277 return; /* nothing to do */
280 mq = n->messages_head;
281 force_address = GNUNET_YES;
282 if (mq->specific_address == NULL)
285 mq->specific_address = get_preferred_ats_address(n);
286 GNUNET_STATISTICS_update (stats,
287 gettext_noop ("# transport selected peer address freely"),
290 force_address = GNUNET_NO;
292 if (mq->specific_address == NULL)
294 GNUNET_STATISTICS_update (stats,
295 gettext_noop ("# transport failed to selected peer address"),
298 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
299 if (timeout.rel_value == 0)
302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303 "No destination address available to transmit message of size %u to peer `%4s'\n",
304 mq->message_buf_size,
305 GNUNET_i2s (&mq->neighbour_id));
307 GNUNET_STATISTICS_update (stats,
308 gettext_noop ("# bytes in message queue for other peers"),
309 - (int64_t) mq->message_buf_size,
311 GNUNET_STATISTICS_update (stats,
312 gettext_noop ("# bytes discarded (no destination address available)"),
313 mq->message_buf_size,
315 if (mq->client != NULL)
316 transmit_send_ok (mq->client, n, &n->id, GNUNET_NO);
317 GNUNET_CONTAINER_DLL_remove (n->messages_head,
321 return; /* nobody ready */
323 GNUNET_STATISTICS_update (stats,
324 gettext_noop ("# message delivery deferred (no address)"),
327 if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
328 GNUNET_SCHEDULER_cancel (n->retry_task);
329 n->retry_task = GNUNET_SCHEDULER_add_delayed (timeout,
330 &retry_transmission_task,
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "No validated destination address available to transmit message of size %u to peer `%4s', will wait %llums to find an address.\n",
335 mq->message_buf_size,
336 GNUNET_i2s (&mq->neighbour_id),
339 /* FIXME: might want to trigger peerinfo lookup here
340 (unless that's already pending...) */
343 GNUNET_CONTAINER_DLL_remove (n->messages_head,
346 if (mq->specific_address->connected == GNUNET_NO)
347 mq->specific_address->connect_attempts++;
348 rl = mq->specific_address->ready_list;
349 mq->plugin = rl->plugin;
350 if (!mq->internal_msg)
351 mq->specific_address->in_transmit = GNUNET_YES;
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354 "Sending message of size %u for `%4s' to `%s' via plugin `%s'\n",
355 mq->message_buf_size,
357 (mq->specific_address->addr != NULL)
358 ? a2s (mq->plugin->short_name,
359 mq->specific_address->addr,
360 mq->specific_address->addrlen)
362 rl->plugin->short_name);
364 GNUNET_STATISTICS_update (stats,
365 gettext_noop ("# bytes in message queue for other peers"),
366 - (int64_t) mq->message_buf_size,
368 GNUNET_STATISTICS_update (stats,
369 gettext_noop ("# bytes pending with plugins"),
370 mq->message_buf_size,
373 GNUNET_CONTAINER_DLL_insert (n->cont_head,
377 ret = rl->plugin->api->send (rl->plugin->api->cls,
380 mq->message_buf_size,
382 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
383 mq->specific_address->session,
384 mq->specific_address->addr,
385 mq->specific_address->addrlen,
387 &transmit_send_continuation, mq);
390 /* failure, but 'send' would not call continuation in this case,
391 so we need to do it here! */
392 transmit_send_continuation (mq,
400 * Send the specified message to the specified peer.
402 * @param client source of the transmission request (can be NULL)
403 * @param peer_address ForeignAddressList where we should send this message
404 * @param priority how important is the message
405 * @param timeout how long do we have to transmit?
406 * @param message_buf message(s) to send GNUNET_MessageHeader(s)
407 * @param message_buf_size total size of all messages in message_buf
408 * @param is_internal is this an internal message; these are pre-pended and
409 * also do not count for plugins being "ready" to transmit
410 * @param neighbour handle to the neighbour for transmission
413 transmit_to_peer (struct TransportClient *client,
414 struct ForeignAddressList *peer_address,
415 unsigned int priority,
416 struct GNUNET_TIME_Relative timeout,
417 const char *message_buf,
418 size_t message_buf_size,
419 int is_internal, struct NeighbourMapEntry *neighbour)
421 struct MessageQueue *mq;
426 /* check for duplicate submission */
427 mq = neighbour->messages_head;
430 if (mq->client == client)
432 /* client transmitted to same peer twice
433 before getting SEND_OK! */
441 GNUNET_STATISTICS_update (stats,
442 gettext_noop ("# bytes in message queue for other peers"),
445 mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
446 mq->specific_address = peer_address;
448 /* FIXME: this memcpy can be up to 7% of our total runtime! */
449 memcpy (&mq[1], message_buf, message_buf_size);
450 mq->message_buf = (const char*) &mq[1];
451 mq->message_buf_size = message_buf_size;
452 memcpy(&mq->neighbour_id, &neighbour->id, sizeof(struct GNUNET_PeerIdentity));
453 mq->internal_msg = is_internal;
454 mq->priority = priority;
455 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
457 GNUNET_CONTAINER_DLL_insert (neighbour->messages_head,
458 neighbour->messages_tail,
461 GNUNET_CONTAINER_DLL_insert_after (neighbour->messages_head,
462 neighbour->messages_tail,
463 neighbour->messages_tail,
465 try_transmission_to_peer (neighbour);
470 * Create a fresh entry in our neighbour list for the given peer.
471 * Will try to transmit our current HELLO to the new neighbour.
472 * Do not call this function directly, use 'setup_peer_check_blacklist.
474 * @param peer the peer for which we create the entry
475 * @param do_hello should we schedule transmitting a HELLO
476 * @return the new neighbour list entry
478 static struct NeighbourMapEntry *
479 setup_new_neighbour (const struct GNUNET_PeerIdentity *peer,
482 struct NeighbourMapEntry *n;
483 struct TransportPlugin *tp;
484 struct ReadyList *rl;
486 GNUNET_assert (0 != memcmp (peer,
488 sizeof (struct GNUNET_PeerIdentity)));
490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 "Setting up state for neighbour `%4s'\n",
494 GNUNET_STATISTICS_update (stats,
495 gettext_noop ("# active neighbours"),
498 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
501 GNUNET_TIME_relative_to_absolute
502 (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
503 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
504 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
505 MAX_BANDWIDTH_CARRY_S);
509 if ((tp->api->send != NULL) && (!is_blacklisted(peer, tp)))
511 rl = GNUNET_malloc (sizeof (struct ReadyList));
513 rl->next = n->plugins;
516 rl->addresses = NULL;
520 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
522 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
523 &neighbour_timeout_task, n);
524 GNUNET_CONTAINER_multihashmap_put (neighbours,
527 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
530 GNUNET_STATISTICS_update (stats,
531 gettext_noop ("# peerinfo new neighbor iterate requests"),
534 GNUNET_STATISTICS_update (stats,
535 gettext_noop ("# outstanding peerinfo iterate requests"),
538 n->piter = GNUNET_PEERINFO_iterate (peerinfo, peer,
539 GNUNET_TIME_UNIT_FOREVER_REL,
540 &add_hello_for_peer, n);
542 GNUNET_STATISTICS_update (stats,
543 gettext_noop ("# HELLO's sent to new neighbors"),
546 if (NULL != our_hello)
547 transmit_to_peer (NULL, NULL, 0,
548 HELLO_ADDRESS_EXPIRATION,
549 (const char *) our_hello, GNUNET_HELLO_size(our_hello),
558 * Initialize the neighbours subsystem.
560 * @param cls closure for callbacks
561 * @param connect_cb function to call if we connect to a peer
562 * @param disconnect_cb function to call if we disconnect from a peer
565 GST_neighbours_start (void *cls,
566 GNUNET_TRANSPORT_NotifyConnect connect_cb,
567 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
570 connect_notify_cb = connect_cb;
571 disconnect_notify_cb = disconnect_cb;
572 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
577 * Disconnect from the given neighbour, clean up the record.
579 * @param n neighbour to disconnect from
582 disconnect_neighbour (struct NeighbourMapEntry *n)
584 struct MessageQueue *mq;
586 disconnect_notify_cb (callback_cls,
588 GNUNET_assert (GNUNET_YES ==
589 GNUNET_CONTAINER_multihashmap_remove (neighbours,
592 while (NULL != (mq = n->messages_head))
594 GNUNET_CONTAINER_DLL_remove (n->messages_head,
599 if (NULL != n->piter)
601 GNUNET_PEERINFO_iterate_cancel (n->piter);
604 GNUNET_array_grow (n->ats,
612 * Disconnect from the given neighbour.
615 * @param key hash of neighbour's public key (not used)
616 * @param value the 'struct NeighbourMapEntry' of the neighbour
619 disconnect_all_neighbours (void *cls,
620 const GNUNET_HashCode *key,
623 struct NeighbourMapEntry *n = value;
626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 "Disconnecting peer `%4s', %s\n",
631 disconnect_neighbour (n);
637 * Cleanup the neighbours subsystem.
640 GST_neighbours_stop ()
642 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
643 &disconnect_all_neighbours,
645 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
648 connect_notify_cb = NULL;
649 disconnect_notify_cb = NULL;
654 * Try to create a connection to the given target (eventually).
656 * @param target peer to try to connect to
659 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
665 * Test if we're connected to the given peer.
667 * @param target peer to test
668 * @return GNUNET_YES if we are connected, GNUNET_NO if not
671 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
678 * Transmit a message to the given target using the active connection.
680 * @param target destination
681 * @param msg message to send
682 * @param cont function to call when done
683 * @param cont_cls closure for 'cont'
686 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
687 const struct GNUNET_MessageHeader *msg,
688 GST_NeighbourSendContinuation cont,
695 * Change the incoming quota for the given peer.
697 * @param neighbour identity of peer to change qutoa for
698 * @param quota new quota
701 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
702 struct GNUNET_BANDWIDTH_Value32NBO quota)
704 struct NeighbourMapEntry *n;
706 n = lookup_neighbour (neighbour);
709 GNUNET_STATISTICS_update (GST_stats,
710 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
715 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
717 if (0 != ntohl (quota.value__))
720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
721 "Disconnecting peer `%4s' due to `%s'\n",
725 GNUNET_STATISTICS_update (GST_stats,
726 gettext_noop ("# disconnects due to quota of 0"),
729 disconnect_neighbour (n);
734 * If we have an active connection to the given target, it must be shutdown.
736 * @param target peer to disconnect from
739 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
741 struct NeighbourMapEntry *n;
743 n = lookup_neighbour (target);
744 disconnect_neighbour (n);
749 * Closure for the neighbours_iterate function.
751 struct IteratorContext
754 * Function to call on each connected neighbour.
756 GST_NeighbourIterator cb;
766 * Call the callback from the closure for each connected neighbour.
768 * @param cls the 'struct IteratorContext'
769 * @param key the hash of the public key of the neighbour
770 * @param value the 'struct NeighbourMapEntry'
771 * @return GNUNET_OK (continue to iterate)
774 neighbours_iterate (void *cls,
775 const GNUNET_HashCode *key,
778 struct IteratorContext *ic = cls;
779 struct NeighbourMapEntry *n = value;
781 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
782 return GNUNET_OK; /* not connected */
783 GNUNET_assert (n->ats_count > 0);
793 * Iterate over all connected neighbours.
795 * @param cb function to call
796 * @param cb_cls closure for cb
799 GST_neighbours_iterate (GST_NeighbourIterator cb,
802 struct IteratorContext ic;
806 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
813 * We have received a PONG. Update lifeness of the neighbour.
815 * @param sender peer sending the PONG
816 * @param hdr the PONG message (presumably)
817 * @param plugin_name name of transport that delivered the PONG
818 * @param sender_address address of the other peer, NULL if other peer
820 * @param sender_address_len number of bytes in sender_address
821 * @param ats performance data
822 * @param ats_count number of entries in ats (excluding 0-termination)
823 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
826 GST_neighbours_handle_pong (const struct GNUNET_PeerIdentity *sender,
827 const struct GNUNET_MessageHeader *hdr,
828 const char *plugin_name,
829 const void *sender_address,
830 size_t sender_address_len,
831 const struct GNUNET_TRANSPORT_ATS_Information *ats,
834 return GNUNET_SYSERR;
839 * We have received a CONNECT. Set the peer to connected.
841 * @param sender peer sending the PONG
842 * @param hdr the PONG message (presumably)
843 * @param plugin_name name of transport that delivered the PONG
844 * @param sender_address address of the other peer, NULL if other peer
846 * @param sender_address_len number of bytes in sender_address
847 * @param ats performance data
848 * @param ats_count number of entries in ats (excluding 0-termination)
849 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
852 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
853 const struct GNUNET_MessageHeader *hdr,
854 const char *plugin_name,
855 const void *sender_address,
856 size_t sender_address_len,
857 const struct GNUNET_TRANSPORT_ATS_Information *ats,
860 return GNUNET_SYSERR;
865 * We have received a DISCONNECT. Set the peer to disconnected.
867 * @param sender peer sending the PONG
868 * @param hdr the PONG message (presumably)
869 * @param plugin_name name of transport that delivered the PONG
870 * @param sender_address address of the other peer, NULL if other peer
872 * @param sender_address_len number of bytes in sender_address
873 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
876 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
877 const struct GNUNET_MessageHeader *hdr,
878 const char *plugin_name,
879 const void *sender_address,
880 size_t sender_address_len)
882 return GNUNET_SYSERR;
886 /* end of file gnunet-service-transport_neighbours.c */