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_ats-new.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 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
71 * stuck together in memory. Allocated at the end of this struct.
73 const char *message_buf;
76 * Size of the message buf
78 size_t message_buf_size;
81 * Client responsible for queueing the message; used to check that a
82 * client has no two messages pending for the same target and to
83 * notify the client of a successful transmission; NULL if this is
84 * an internal message.
86 struct TransportClient *client;
89 * At what time should we fail?
91 struct GNUNET_TIME_Absolute timeout;
94 * Internal message of the transport system that should not be
95 * included in the usual SEND-SEND_OK transmission confirmation
96 * traffic management scheme. Typically, "internal_msg" will
97 * be set whenever "client" is NULL (but it is not strictly
103 * How important is the message?
105 unsigned int priority;
111 * Entry in neighbours.
113 struct NeighbourMapEntry
117 * Head of list of messages we would like to send to this peer;
118 * must contain at most one message per client.
120 struct MessageQueue *messages_head;
123 * Tail of list of messages we would like to send to this peer; must
124 * contain at most one message per client.
126 struct MessageQueue *messages_tail;
129 * Context for address suggestion.
130 * NULL after we are connected.
132 struct GST_AtsSuggestionContext *asc;
135 * Performance data for the peer.
137 struct GNUNET_TRANSPORT_ATS_Information *ats;
140 * Public key for this peer. Valid only if the respective flag is set below.
142 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
145 * Identity of this neighbour.
147 struct GNUNET_PeerIdentity id;
150 * ID of task scheduled to run when this peer is about to
151 * time out (will free resources associated with the peer).
153 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
156 * ID of task scheduled to run when we should retry transmitting
157 * the head of the message queue. Actually triggered when the
158 * transmission is timing out (we trigger instantly when we have
159 * a chance of success).
161 GNUNET_SCHEDULER_TaskIdentifier retry_task;
164 * How long until we should consider this peer dead (if we don't
165 * receive another message in the meantime)?
167 struct GNUNET_TIME_Absolute peer_timeout;
170 * Tracker for inbound bandwidth.
172 struct GNUNET_BANDWIDTH_Tracker in_tracker;
175 * How often has the other peer (recently) violated the inbound
176 * traffic limit? Incremented by 10 per violation, decremented by 1
177 * per non-violation (for each time interval).
179 unsigned int quota_violation_count;
182 * Number of values in 'ats' array.
184 unsigned int ats_count;
187 * Have we seen an PONG from this neighbour in the past (and
188 * not had a disconnect since)?
193 * Do we have a valid public key for this neighbour?
195 int public_key_valid;
198 * Are we already in the process of disconnecting this neighbour?
203 * Do we currently consider this neighbour connected? (as far as
204 * the connect/disconnect callbacks are concerned)?
212 * All known neighbours and their HELLOs.
214 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
217 * Closure for connect_notify_cb and disconnect_notify_cb
219 static void *callback_cls;
222 * Function to call when we connected to a neighbour.
224 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
227 * Function to call when we disconnected from a neighbour.
229 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
233 * Lookup a neighbour entry in the neighbours hash map.
235 * @param pid identity of the peer to look up
236 * @return the entry, NULL if there is no existing record
238 static struct NeighbourMapEntry *
239 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
241 return GNUNET_CONTAINER_multihashmap_get (neighbours,
248 * Check the ready list for the given neighbour and if a plugin is
249 * ready for transmission (and if we have a message), do so!
251 * @param neighbour target peer for which to transmit
254 try_transmission_to_peer (struct NeighbourMapEntry *n)
256 struct MessageQueue *mq;
257 struct GNUNET_TIME_Relative timeout;
260 if (n->messages_head == NULL)
263 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264 "Transmission queue for `%4s' is empty\n",
265 GNUNET_i2s (&n->id));
267 return; /* nothing to do */
269 mq = n->messages_head;
270 GNUNET_CONTAINER_DLL_remove (n->messages_head,
273 ret = papi->send (papi->cls,
276 mq->message_buf_size,
278 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
283 &transmit_send_continuation, mq);
286 /* failure, but 'send' would not call continuation in this case,
287 so we need to do it here! */
288 transmit_send_continuation (mq,
297 * Initialize the neighbours subsystem.
299 * @param cls closure for callbacks
300 * @param connect_cb function to call if we connect to a peer
301 * @param disconnect_cb function to call if we disconnect from a peer
304 GST_neighbours_start (void *cls,
305 GNUNET_TRANSPORT_NotifyConnect connect_cb,
306 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
309 connect_notify_cb = connect_cb;
310 disconnect_notify_cb = disconnect_cb;
311 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
316 * Disconnect from the given neighbour, clean up the record.
318 * @param n neighbour to disconnect from
321 disconnect_neighbour (struct NeighbourMapEntry *n)
323 struct MessageQueue *mq;
327 disconnect_notify_cb (callback_cls,
329 n->is_connected = GNUNET_NO;
331 GNUNET_assert (GNUNET_YES ==
332 GNUNET_CONTAINER_multihashmap_remove (neighbours,
335 while (NULL != (mq = n->messages_head))
337 GNUNET_CONTAINER_DLL_remove (n->messages_head,
344 GST_ats_suggest_address_cancel (n->asc);
347 GNUNET_array_grow (n->ats,
355 * Disconnect from the given neighbour.
358 * @param key hash of neighbour's public key (not used)
359 * @param value the 'struct NeighbourMapEntry' of the neighbour
362 disconnect_all_neighbours (void *cls,
363 const GNUNET_HashCode *key,
366 struct NeighbourMapEntry *n = value;
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Disconnecting peer `%4s', %s\n",
374 disconnect_neighbour (n);
380 * Cleanup the neighbours subsystem.
383 GST_neighbours_stop ()
385 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
386 &disconnect_all_neighbours,
388 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
391 connect_notify_cb = NULL;
392 disconnect_notify_cb = NULL;
397 * Try to connect to the target peer using the given address
400 * @param cls the 'struct NeighbourMapEntry' of the target
401 * @param public_key public key for the peer, never NULL
402 * @param target identity of the target peer
403 * @param plugin_name name of the plugin
404 * @param plugin_address binary address
405 * @param plugin_address_len length of address
406 * @param ats performance data for the address (as far as known)
407 * @param ats_count number of performance records in 'ats'
410 try_connect_using_address (void *cls,
411 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
412 const struct GNUNET_PeerIdentity *target,
413 const char *plugin_name,
414 const void *plugin_address,
415 size_t plugin_address_len,
416 const struct GNUNET_TRANSPORT_ATS_Information *ats,
419 struct NeighbourMapEntry *n = cls;
422 if (n->public_key_valid == GNUNET_NO)
424 n->public_key = *public_key;
425 n->public_key_valid = GNUNET_YES;
427 /* FIXME: do connect! */
433 * We've tried to connect but waited long enough and failed. Clean up.
435 * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
436 * @param tc scheduler context
439 neighbour_connect_timeout_task (void *cls,
440 const struct GNUNET_SCHEDULER_TaskContext *tc)
442 struct NeighbourMapEntry *n = cls;
444 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
445 GNUNET_assert (GNUNET_YES ==
446 GNUNET_CONTAINER_multihashmap_remove (neighbours,
449 GNUNET_assert (NULL == n->messages_head);
450 GNUNET_assert (NULL == n->ats);
456 * Try to create a connection to the given target (eventually).
458 * @param target peer to try to connect to
461 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
463 struct NeighbourMapEntry *n;
465 GNUNET_assert (0 != memcmp (target,
467 sizeof (struct GNUNET_PeerIdentity)));
468 n = lookup_neighbour (target);
470 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value > 0) )
471 return; /* already connected */
474 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
476 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
477 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
478 MAX_BANDWIDTH_CARRY_S);
479 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
480 &neighbour_connect_timeout_task, n);
481 GNUNET_assert (GNUNET_OK ==
482 GNUNET_CONTAINER_multihashmap_put (neighbours,
485 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
488 return; /* already trying */
489 n->asc = GST_ats_suggest_address (GST_ats,
491 &try_connect_using_address,
497 * Test if we're connected to the given peer.
499 * @param target peer to test
500 * @return GNUNET_YES if we are connected, GNUNET_NO if not
503 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
505 struct NeighbourMapEntry *n;
507 n = lookup_neighbour (target);
509 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
510 return GNUNET_NO; /* not connected */
516 * Transmit a message to the given target using the active connection.
518 * @param target destination
519 * @param msg message to send
520 * @param msg_size number of bytes in msg
521 * @param timeout when to fail with timeout
522 * @param cont function to call when done
523 * @param cont_cls closure for 'cont'
526 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
529 struct GNUNET_TIME_Relative timeout,
530 GST_NeighbourSendContinuation cont,
533 struct NeighbourMapEntry *n;
534 struct MessageQueue *mq;
536 n = lookup_neighbour (target);
538 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
540 GNUNET_STATISTICS_update (GST_stats,
541 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
549 GNUNET_assert (msg_size >= sizeof (struct GNUNET_MessageHeader));
550 GNUNET_STATISTICS_update (GST_stats,
551 gettext_noop ("# bytes in message queue for other peers"),
554 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
555 /* FIXME: this memcpy can be up to 7% of our total runtime! */
556 memcpy (&mq[1], msg, msg_size);
557 mq->message_buf = (const char*) &mq[1];
558 mq->message_buf_size = msg_size;
559 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
560 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
563 // try_transmission_to_peer (n);
568 * Change the incoming quota for the given peer.
570 * @param neighbour identity of peer to change qutoa for
571 * @param quota new quota
574 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
575 struct GNUNET_BANDWIDTH_Value32NBO quota)
577 struct NeighbourMapEntry *n;
579 n = lookup_neighbour (neighbour);
582 GNUNET_STATISTICS_update (GST_stats,
583 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
588 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
590 if (0 != ntohl (quota.value__))
593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
594 "Disconnecting peer `%4s' due to `%s'\n",
598 GNUNET_STATISTICS_update (GST_stats,
599 gettext_noop ("# disconnects due to quota of 0"),
602 disconnect_neighbour (n);
607 * Closure for the neighbours_iterate function.
609 struct IteratorContext
612 * Function to call on each connected neighbour.
614 GST_NeighbourIterator cb;
624 * Call the callback from the closure for each connected neighbour.
626 * @param cls the 'struct IteratorContext'
627 * @param key the hash of the public key of the neighbour
628 * @param value the 'struct NeighbourMapEntry'
629 * @return GNUNET_OK (continue to iterate)
632 neighbours_iterate (void *cls,
633 const GNUNET_HashCode *key,
636 struct IteratorContext *ic = cls;
637 struct NeighbourMapEntry *n = value;
639 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
640 return GNUNET_OK; /* not connected */
641 GNUNET_assert (n->ats_count > 0);
651 * Iterate over all connected neighbours.
653 * @param cb function to call
654 * @param cb_cls closure for cb
657 GST_neighbours_iterate (GST_NeighbourIterator cb,
660 struct IteratorContext ic;
664 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
671 * Peer has been idle for too long. Disconnect.
673 * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
674 * @param tc scheduler context
677 neighbour_idle_timeout_task (void *cls,
678 const struct GNUNET_SCHEDULER_TaskContext *tc)
680 struct NeighbourMapEntry *n = cls;
682 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
683 disconnect_neighbour (n);
688 * We have received a CONNECT. Set the peer to connected.
690 * @param sender peer sending the PONG
691 * @param hdr the PONG message (presumably)
692 * @param plugin_name name of transport that delivered the PONG
693 * @param sender_address address of the other peer, NULL if other peer
695 * @param sender_address_len number of bytes in sender_address
696 * @param ats performance data
697 * @param ats_count number of entries in ats (excluding 0-termination)
698 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
701 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
702 const struct GNUNET_MessageHeader *hdr,
703 const char *plugin_name,
704 const void *sender_address,
705 size_t sender_address_len,
706 struct Session *session,
707 const struct GNUNET_TRANSPORT_ATS_Information *ats,
710 struct NeighbourMapEntry *n;
712 if (0 == memcmp (sender,
714 sizeof (struct GNUNET_PeerIdentity)))
717 return GNUNET_SYSERR;
719 n = lookup_neighbour (sender);
721 (n->is_connected == GNUNET_YES) )
723 /* already connected */
726 // FIXME: ATS: switch session!?
727 // FIXME: merge/update ats?
733 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
735 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
736 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
737 MAX_BANDWIDTH_CARRY_S);
738 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
739 &neighbour_connect_timeout_task, n);
740 GNUNET_assert (GNUNET_OK ==
741 GNUNET_CONTAINER_multihashmap_put (neighbours,
744 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
747 GNUNET_array_grow (n->ats,
753 GNUNET_array_grow (n->ats,
758 sizeof (struct GNUNET_TRANSPORT_ATS_Information) * ats_count);
763 // FIXME: ATS: switch session!?
764 // n->session = session;
766 n->peer_timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
767 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
768 GNUNET_SCHEDULER_cancel (n->timeout_task);
769 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
770 &neighbour_idle_timeout_task,
772 n->is_connected = GNUNET_YES;
773 connect_notify_cb (callback_cls,
782 * If we have an active connection to the given target, it must be shutdown.
784 * @param target peer to disconnect from
787 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
789 struct NeighbourMapEntry *n;
791 n = lookup_neighbour (target);
792 /* FIXME: send disconnect message to target... */
793 disconnect_neighbour (n);
798 * We have received a DISCONNECT. Set the peer to disconnected.
800 * @param sender peer sending the PONG
801 * @param hdr the PONG message (presumably)
802 * @param plugin_name name of transport that delivered the PONG
803 * @param sender_address address of the other peer, NULL if other peer
805 * @param sender_address_len number of bytes in sender_address
806 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
809 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
810 const struct GNUNET_MessageHeader *hdr,
811 const char *plugin_name,
812 const void *sender_address,
813 size_t sender_address_len)
815 struct NeighbourMapEntry *n;
817 n = lookup_neighbour (sender);
818 /* FIXME: should disconnects have a signature that we should check here? */
819 disconnect_neighbour (n);
824 /* end of file gnunet-service-transport_neighbours.c */