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 * How often has the other peer (recently) violated the inbound
175 * traffic limit? Incremented by 10 per violation, decremented by 1
176 * per non-violation (for each time interval).
178 unsigned int quota_violation_count;
181 * Number of values in 'ats' array.
183 unsigned int ats_count;
186 * Have we seen an PONG from this neighbour in the past (and
187 * not had a disconnect since)?
192 * Do we have a valid public key for this neighbour?
194 int public_key_valid;
197 * Are we already in the process of disconnecting this neighbour?
202 * Do we currently consider this neighbour connected? (as far as
203 * the connect/disconnect callbacks are concerned)?
211 * All known neighbours and their HELLOs.
213 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
216 * Closure for connect_notify_cb and disconnect_notify_cb
218 static void *callback_cls;
221 * Function to call when we connected to a neighbour.
223 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
226 * Function to call when we disconnected from a neighbour.
228 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
232 * Lookup a neighbour entry in the neighbours hash map.
234 * @param pid identity of the peer to look up
235 * @return the entry, NULL if there is no existing record
237 static struct NeighbourMapEntry *
238 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
240 return GNUNET_CONTAINER_multihashmap_get (neighbours,
247 * Check the ready list for the given neighbour and if a plugin is
248 * ready for transmission (and if we have a message), do so!
250 * @param neighbour target peer for which to transmit
253 try_transmission_to_peer (struct NeighbourMapEntry *n)
255 struct MessageQueue *mq;
256 struct GNUNET_TIME_Relative timeout;
259 if (n->messages_head == NULL)
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263 "Transmission queue for `%4s' is empty\n",
264 GNUNET_i2s (&n->id));
266 return; /* nothing to do */
268 mq = n->messages_head;
269 GNUNET_CONTAINER_DLL_remove (n->messages_head,
272 ret = papi->send (papi->cls,
275 mq->message_buf_size,
277 GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
282 &transmit_send_continuation, mq);
285 /* failure, but 'send' would not call continuation in this case,
286 so we need to do it here! */
287 transmit_send_continuation (mq,
296 * Initialize the neighbours subsystem.
298 * @param cls closure for callbacks
299 * @param connect_cb function to call if we connect to a peer
300 * @param disconnect_cb function to call if we disconnect from a peer
303 GST_neighbours_start (void *cls,
304 GNUNET_TRANSPORT_NotifyConnect connect_cb,
305 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
308 connect_notify_cb = connect_cb;
309 disconnect_notify_cb = disconnect_cb;
310 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
315 * Disconnect from the given neighbour, clean up the record.
317 * @param n neighbour to disconnect from
320 disconnect_neighbour (struct NeighbourMapEntry *n)
322 struct MessageQueue *mq;
326 disconnect_notify_cb (callback_cls,
328 n->is_connected = GNUNET_NO;
330 GNUNET_assert (GNUNET_YES ==
331 GNUNET_CONTAINER_multihashmap_remove (neighbours,
334 while (NULL != (mq = n->messages_head))
336 GNUNET_CONTAINER_DLL_remove (n->messages_head,
343 GST_validation_get_addresses_cancel (n->vic);
346 GNUNET_array_grow (n->ats,
354 * Disconnect from the given neighbour.
357 * @param key hash of neighbour's public key (not used)
358 * @param value the 'struct NeighbourMapEntry' of the neighbour
361 disconnect_all_neighbours (void *cls,
362 const GNUNET_HashCode *key,
365 struct NeighbourMapEntry *n = value;
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Disconnecting peer `%4s', %s\n",
373 disconnect_neighbour (n);
379 * Cleanup the neighbours subsystem.
382 GST_neighbours_stop ()
384 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
385 &disconnect_all_neighbours,
387 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
390 connect_notify_cb = NULL;
391 disconnect_notify_cb = NULL;
396 * Try to connect to the target peer using the given address
399 * @param cls the 'struct NeighbourMapEntry' of the target
400 * @param public_key public key for the peer, never NULL
401 * @param target identity of the target peer
402 * @param valid_until is ZERO if we never validated the address,
403 * otherwise a time up to when we consider it (or was) valid
404 * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO)
405 * is ZERO if the address is considered valid (no validation needed)
406 * otherwise a time in the future if we're currently denying re-validation
407 * @param plugin_name name of the plugin
408 * @param plugin_address binary address
409 * @param plugin_address_len length of address
412 try_connect_using_address (void *cls,
413 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
414 const struct GNUNET_PeerIdentity *target,
415 struct GNUNET_TIME_Absolute valid_until,
416 struct GNUNET_TIME_Absolute validation_block,
417 const char *plugin_name,
418 const void *plugin_address,
419 size_t plugin_address_len)
421 struct NeighbourMapEntry *n = cls;
423 if (n->public_key_valid == GNUNET_NO)
425 n->public_key = *public_key;
426 n->public_key_valid = GNUNET_YES;
428 if (GNUNET_TIME_absolute_get_remaining (valid_until).rel_value == 0)
429 return; /* address is not valid right now */
430 /* FIXME: do ATS here! */
436 * We've tried to connect but waited long enough and failed. Clean up.
438 * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
439 * @param tc scheduler context
442 neighbour_connect_timeout_task (void *cls,
443 const struct GNUNET_SCHEDULER_TaskContext *tc)
445 struct NeighbourMapEntry *n = cls;
447 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
448 GNUNET_assert (GNUNET_YES ==
449 GNUNET_CONTAINER_multihashmap_remove (neighbours,
452 GNUNET_assert (NULL == n->messages_head);
453 GNUNET_assert (NULL == n->ats);
459 * Try to create a connection to the given target (eventually).
461 * @param target peer to try to connect to
464 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
466 struct NeighbourMapEntry *n;
468 GNUNET_assert (0 != memcmp (target,
470 sizeof (struct GNUNET_PeerIdentity)));
471 n = lookup_neighbour (target);
473 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value > 0) )
474 return; /* already connected */
477 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
479 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
480 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
481 MAX_BANDWIDTH_CARRY_S);
482 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
483 &neighbour_connect_timeout_task, n);
484 GNUNET_assert (GNUNET_OK ==
485 GNUNET_CONTAINER_multihashmap_put (neighbours,
488 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
491 return; /* already trying */
492 n->vic = GST_validation_get_addresses (target,
494 &try_connect_using_address,
500 * Test if we're connected to the given peer.
502 * @param target peer to test
503 * @return GNUNET_YES if we are connected, GNUNET_NO if not
506 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
508 struct NeighbourMapEntry *n;
510 n = lookup_neighbour (target);
512 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
513 return GNUNET_NO; /* not connected */
519 * Transmit a message to the given target using the active connection.
521 * @param target destination
522 * @param msg message to send
523 * @param timeout when to fail with timeout
524 * @param cont function to call when done
525 * @param cont_cls closure for 'cont'
528 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
529 const struct GNUNET_MessageHeader *msg,
530 struct GNUNET_TIME_Relative timeout,
531 GST_NeighbourSendContinuation cont,
534 struct NeighbourMapEntry *n;
535 struct MessageQueue *mq;
536 uint16_t message_buf_size;
538 n = lookup_neighbour (target);
540 (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
542 GNUNET_STATISTICS_update (GST_stats,
543 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
551 message_buf_size = ntohs (msg->size);
552 GNUNET_assert (message_buf_size >= sizeof (struct GNUNET_MessageHeader));
553 GNUNET_STATISTICS_update (GST_stats,
554 gettext_noop ("# bytes in message queue for other peers"),
557 mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
558 /* FIXME: this memcpy can be up to 7% of our total runtime! */
559 memcpy (&mq[1], msg, message_buf_size);
560 mq->message_buf = (const char*) &mq[1];
561 mq->message_buf_size = message_buf_size;
562 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
563 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
566 // try_transmission_to_peer (n);
571 * Change the incoming quota for the given peer.
573 * @param neighbour identity of peer to change qutoa for
574 * @param quota new quota
577 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
578 struct GNUNET_BANDWIDTH_Value32NBO quota)
580 struct NeighbourMapEntry *n;
582 n = lookup_neighbour (neighbour);
585 GNUNET_STATISTICS_update (GST_stats,
586 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
591 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
593 if (0 != ntohl (quota.value__))
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597 "Disconnecting peer `%4s' due to `%s'\n",
601 GNUNET_STATISTICS_update (GST_stats,
602 gettext_noop ("# disconnects due to quota of 0"),
605 disconnect_neighbour (n);
610 * Closure for the neighbours_iterate function.
612 struct IteratorContext
615 * Function to call on each connected neighbour.
617 GST_NeighbourIterator cb;
627 * Call the callback from the closure for each connected neighbour.
629 * @param cls the 'struct IteratorContext'
630 * @param key the hash of the public key of the neighbour
631 * @param value the 'struct NeighbourMapEntry'
632 * @return GNUNET_OK (continue to iterate)
635 neighbours_iterate (void *cls,
636 const GNUNET_HashCode *key,
639 struct IteratorContext *ic = cls;
640 struct NeighbourMapEntry *n = value;
642 if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
643 return GNUNET_OK; /* not connected */
644 GNUNET_assert (n->ats_count > 0);
654 * Iterate over all connected neighbours.
656 * @param cb function to call
657 * @param cb_cls closure for cb
660 GST_neighbours_iterate (GST_NeighbourIterator cb,
663 struct IteratorContext ic;
667 GNUNET_CONTAINER_multihashmap_iterate (neighbours,
674 * Peer has been idle for too long. Disconnect.
676 * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
677 * @param tc scheduler context
680 neighbour_idle_timeout_task (void *cls,
681 const struct GNUNET_SCHEDULER_TaskContext *tc)
683 struct NeighbourMapEntry *n = cls;
685 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
686 disconnect_neighbour (n);
691 * We have received a CONNECT. Set the peer to connected.
693 * @param sender peer sending the PONG
694 * @param hdr the PONG message (presumably)
695 * @param plugin_name name of transport that delivered the PONG
696 * @param sender_address address of the other peer, NULL if other peer
698 * @param sender_address_len number of bytes in sender_address
699 * @param ats performance data
700 * @param ats_count number of entries in ats (excluding 0-termination)
701 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
704 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
705 const struct GNUNET_MessageHeader *hdr,
706 const char *plugin_name,
707 const void *sender_address,
708 size_t sender_address_len,
709 struct Session *session,
710 const struct GNUNET_TRANSPORT_ATS_Information *ats,
713 struct NeighbourMapEntry *n;
715 if (0 == memcmp (sender,
717 sizeof (struct GNUNET_PeerIdentity)))
720 return GNUNET_SYSERR;
722 n = lookup_neighbour (sender);
724 (n->is_connected == GNUNET_YES) )
726 /* already connected */
729 // FIXME: ATS: switch session!?
730 // FIXME: merge/update ats?
736 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
738 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
739 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
740 MAX_BANDWIDTH_CARRY_S);
741 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
742 &neighbour_connect_timeout_task, n);
743 GNUNET_assert (GNUNET_OK ==
744 GNUNET_CONTAINER_multihashmap_put (neighbours,
747 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
750 GNUNET_array_grow (n->ats,
756 GNUNET_array_grow (n->ats,
761 sizeof (struct GNUNET_TRANSPORT_ATS_Information) * ats_count);
766 // FIXME: ATS: switch session!?
767 // n->session = session;
769 n->peer_timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
770 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
771 GNUNET_SCHEDULER_cancel (n->timeout_task);
772 n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
773 &neighbour_idle_timeout_task,
775 n->is_connected = GNUNET_YES;
776 connect_notify_cb (callback_cls,
785 * If we have an active connection to the given target, it must be shutdown.
787 * @param target peer to disconnect from
790 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
792 struct NeighbourMapEntry *n;
794 n = lookup_neighbour (target);
795 /* FIXME: send disconnect message to target... */
796 disconnect_neighbour (n);
801 * We have received a DISCONNECT. Set the peer to disconnected.
803 * @param sender peer sending the PONG
804 * @param hdr the PONG message (presumably)
805 * @param plugin_name name of transport that delivered the PONG
806 * @param sender_address address of the other peer, NULL if other peer
808 * @param sender_address_len number of bytes in sender_address
809 * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
812 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
813 const struct GNUNET_MessageHeader *hdr,
814 const char *plugin_name,
815 const void *sender_address,
816 size_t sender_address_len)
818 struct NeighbourMapEntry *n;
820 n = lookup_neighbour (sender);
821 /* FIXME: should disconnects have a signature that we should check here? */
822 disconnect_neighbour (n);
827 /* end of file gnunet-service-transport_neighbours.c */