2 This file is part of GNUnet.
3 (C) 2010-2013 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_plugins.h"
30 #include "gnunet-service-transport_validation.h"
31 #include "gnunet-service-transport_clients.h"
32 #include "gnunet-service-transport.h"
33 #include "gnunet_peerinfo_service.h"
34 #include "gnunet-service-transport_blacklist.h"
35 #include "gnunet_constants.h"
36 #include "transport.h"
41 * Size of the neighbour hash map.
43 #define NEIGHBOUR_TABLE_SIZE 256
46 * Time we give plugin to transmit DISCONNECT message before the
47 * neighbour entry self-destructs.
49 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
52 * How often must a peer violate bandwidth quotas before we start
53 * to simply drop its messages?
55 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
58 * How long are we willing to wait for a response from ATS before timing out?
60 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5000)
63 * How long are we willing to wait for an ACK from the other peer before
64 * giving up on our connect operation?
66 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
69 * How long are we willing to wait for a successful reconnect if
70 * an existing connection went down? Much shorter than the
71 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
72 * higher layers about the disconnect during this period.
74 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
77 * How long are we willing to wait for a response from the blacklist
78 * subsystem before timing out?
80 #define BLACKLIST_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
83 * Interval to send utilization data
85 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
87 GNUNET_NETWORK_STRUCT_BEGIN
90 * Message a peer sends to another to indicate that it intends to
91 * setup a connection/session for data exchange. A 'SESSION_CONNECT'
92 * should be answered with a 'SESSION_CONNECT_ACK' with the same body
93 * to confirm. A 'SESSION_CONNECT_ACK' should then be followed with
94 * a 'SESSION_ACK'. Once the 'SESSION_ACK' is received, both peers
95 * should be connected.
97 struct SessionConnectMessage
100 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT
101 * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK
103 struct GNUNET_MessageHeader header;
108 uint32_t reserved GNUNET_PACKED;
111 * Absolute time at the sender. Only the most recent connect
112 * message implies which session is preferred by the sender.
114 struct GNUNET_TIME_AbsoluteNBO timestamp;
120 * Message a peer sends to another when connected to indicate that a
121 * session is in use and the peer is still alive or to respond to a keep alive.
122 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
123 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
124 * When the keep alive response with type is received, transport service
125 * will call the respective plugin to update the session timeout
127 struct SessionKeepAliveMessage
130 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
131 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
133 struct GNUNET_MessageHeader header;
136 * A nonce to identify the session the keep alive is used for
138 uint32_t nonce GNUNET_PACKED;
142 * Message we send to the other peer to notify him that we intentionally
143 * are disconnecting (to reduce timeouts). This is just a friendly
144 * notification, peers must not rely on always receiving disconnect
147 struct SessionDisconnectMessage
150 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
152 struct GNUNET_MessageHeader header;
157 uint32_t reserved GNUNET_PACKED;
160 * Purpose of the signature. Extends over the timestamp.
161 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
163 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
166 * Absolute time at the sender. Only the most recent connect
167 * message implies which session is preferred by the sender.
169 struct GNUNET_TIME_AbsoluteNBO timestamp;
172 * Public key of the sender.
174 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
177 * Signature of the peer that sends us the disconnect. Only
178 * valid if the timestamp is AFTER the timestamp from the
179 * corresponding 'CONNECT' message.
181 struct GNUNET_CRYPTO_EddsaSignature signature;
185 GNUNET_NETWORK_STRUCT_END
189 * For each neighbour we keep a list of messages
190 * that we still want to transmit to the neighbour.
196 * This is a doubly linked list.
198 struct MessageQueue *next;
201 * This is a doubly linked list.
203 struct MessageQueue *prev;
206 * Function to call once we're done.
208 GST_NeighbourSendContinuation cont;
211 * Closure for @e cont
216 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
217 * stuck together in memory. Allocated at the end of this struct.
219 const char *message_buf;
222 * Size of the message buf
224 size_t message_buf_size;
227 * At what time should we fail?
229 struct GNUNET_TIME_Absolute timeout;
237 * A possible address we could use to communicate with a neighbour.
239 struct NeighbourAddress
243 * Active session for this address.
245 struct Session *session;
248 * Network-level address information.
250 struct GNUNET_HELLO_Address *address;
253 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
254 * peer for this address. Use to check that the ACK is in response
255 * to our most recent 'CONNECT'.
257 struct GNUNET_TIME_Absolute connect_timestamp;
260 * Inbound bandwidth from ATS for this address.
262 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
265 * Outbound bandwidth from ATS for this address.
267 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
270 * Did we tell ATS that this is our 'active' address?
275 * The current nonce sent in the last keep alive messages
277 uint32_t keep_alive_nonce;
282 * Entry in neighbours.
284 struct NeighbourMapEntry
288 * Head of list of messages we would like to send to this peer;
289 * must contain at most one message per client.
291 struct MessageQueue *messages_head;
294 * Tail of list of messages we would like to send to this peer; must
295 * contain at most one message per client.
297 struct MessageQueue *messages_tail;
300 * Are we currently trying to send a message? If so, which one?
302 struct MessageQueue *is_active;
305 * Primary address we currently use to communicate with the neighbour.
307 struct NeighbourAddress primary_address;
310 * Alternative address currently under consideration for communicating
311 * with the neighbour.
313 struct NeighbourAddress alternative_address;
316 * Identity of this neighbour.
318 struct GNUNET_PeerIdentity id;
321 * Main task that drives this peer (timeouts, keepalives, etc.).
322 * Always runs the 'master_task'.
324 GNUNET_SCHEDULER_TaskIdentifier task;
327 * At what time should we sent the next keep-alive message?
329 struct GNUNET_TIME_Absolute keep_alive_time;
332 * At what time did we sent the last keep-alive message? Used
333 * to calculate round-trip time ("latency").
335 struct GNUNET_TIME_Absolute last_keep_alive_time;
338 * Timestamp we should include in our next CONNECT_ACK message.
339 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
340 * our CONNECT_ACK message.
342 struct GNUNET_TIME_Absolute connect_ack_timestamp;
345 * ATS address suggest handle
347 struct GNUNET_ATS_SuggestHandle *suggest_handle;
350 * Time where we should cut the connection (timeout) if we don't
351 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
352 * if we are in S_CONNECTED).
354 struct GNUNET_TIME_Absolute timeout;
357 * Latest calculated latency value
359 struct GNUNET_TIME_Relative latency;
362 * Tracker for inbound bandwidth.
364 struct GNUNET_BANDWIDTH_Tracker in_tracker;
367 * How often has the other peer (recently) violated the inbound
368 * traffic limit? Incremented by 10 per violation, decremented by 1
369 * per non-violation (for each time interval).
371 unsigned int quota_violation_count;
374 * The current state of the peer.
376 enum GNUNET_TRANSPORT_PeerState state;
379 * Did we sent an KEEP_ALIVE message and are we expecting a response?
381 int expect_latency_response;
384 * Flag to set if we still need to send a CONNECT_ACK message to the other peer
385 * (once we have an address to use and the peer has been allowed by our
386 * blacklist). Set to 1 if we need to send a CONNECT_ACK. Set to 2 if we
387 * did send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving
388 * a 'SESSION_ACK' (regardless of what our own state machine might say).
390 int send_connect_ack;
393 * Tracking utilization of outbound bandwidth
395 uint32_t util_payload_bytes_sent;
398 * Tracking utilization of inbound bandwidth
400 uint32_t util_payload_bytes_recv;
403 * Tracking utilization of outbound bandwidth
405 uint32_t util_total_bytes_sent;
408 * Tracking utilization of inbound bandwidth
410 uint32_t util_total_bytes_recv;
413 * Date of last utilization transmission
415 struct GNUNET_TIME_Absolute last_util_transmission;
420 * Context for blacklist checks and the #handle_test_blacklist_cont()
421 * function. Stores information about ongoing blacklist checks.
423 struct BlackListCheckContext
427 * We keep blacklist checks in a DLL.
429 struct BlackListCheckContext *next;
432 * We keep blacklist checks in a DLL.
434 struct BlackListCheckContext *prev;
437 * Address that is being checked.
439 struct NeighbourAddress na;
442 * Handle to the ongoing blacklist check.
444 struct GST_BlacklistCheck *bc;
449 * Hash map from peer identities to the respective 'struct NeighbourMapEntry'.
451 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
454 * We keep blacklist checks in a DLL so that we can find
455 * the 'sessions' in their 'struct NeighbourAddress' if
456 * a session goes down.
458 static struct BlackListCheckContext *bc_head;
461 * We keep blacklist checks in a DLL.
463 static struct BlackListCheckContext *bc_tail;
466 * List of pending blacklist checks: head
468 static struct BlacklistCheckSwitchContext *pending_bc_head;
471 * List of pending blacklist checks: tail
473 static struct BlacklistCheckSwitchContext *pending_bc_tail;
476 * Closure for #connect_notify_cb, #disconnect_notify_cb and #neighbour_change_cb
478 static void *callback_cls;
481 * Function to call when we connected to a neighbour.
483 static NotifyConnect connect_notify_cb;
486 * Function to call when we disconnected from a neighbour.
488 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
491 * Function to call when a neighbour changed address, state or bandwidth.
493 static GNUNET_TRANSPORT_NeighbourChangeCallback neighbour_change_cb;
496 * counter for connected neighbours
498 static unsigned int neighbours_connected;
501 * Number of bytes we have currently queued for transmission.
503 static unsigned long long bytes_in_send_queue;
506 * Task transmitting utilization data
508 static GNUNET_SCHEDULER_TaskIdentifier util_transmission_tk;
511 static struct GNUNET_CONTAINER_MultiPeerMap *registered_quota_notifications;
514 * Lookup a neighbour entry in the neighbours hash map.
516 * @param pid identity of the peer to look up
517 * @return the entry, NULL if there is no existing record
519 static struct NeighbourMapEntry *
520 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
522 if (NULL == neighbours)
524 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
529 * Test if we're connected to the given peer.
531 * @param n neighbour entry of peer to test
532 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
535 test_connected (struct NeighbourMapEntry *n)
539 return GNUNET_TRANSPORT_is_connected (n->state);
543 * Send information about a new outbound quota to our clients.
545 * @param target affected peer
546 * @param quota new quota
549 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
550 struct GNUNET_BANDWIDTH_Value32NBO quota)
552 struct QuotaSetMessage q_msg;
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
556 ntohl (quota.value__), GNUNET_i2s (target));
557 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
558 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
560 q_msg.peer = (*target);
561 GST_clients_broadcast (&q_msg.header, GNUNET_NO);
566 * We don't need a given neighbour address any more.
567 * Release its resources and give appropriate notifications
568 * to ATS and other subsystems.
570 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
573 free_address (struct NeighbourAddress *na)
575 if (GNUNET_YES == na->ats_active)
577 GST_validation_set_address_use (na->address, na->session, GNUNET_NO);
578 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
581 na->ats_active = GNUNET_NO;
582 na->keep_alive_nonce = 0;
583 if (NULL != na->address)
585 GNUNET_HELLO_address_free (na->address);
593 * Set net state for this neighbour and notify monitoring
595 * @param n the respective neighbour
596 * @param s the new state
599 set_state (struct NeighbourMapEntry *n, enum GNUNET_TRANSPORT_PeerState s)
602 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' changed state to %s\n",
604 GNUNET_TRANSPORT_ps2s(s));
605 neighbour_change_cb (callback_cls,
607 n->primary_address.address,
608 n->state, n->timeout,
609 n->primary_address.bandwidth_in,
610 n->primary_address.bandwidth_out);
615 * Set net state and state timeout for this neighbour and notify monitoring
617 * @param n the respective neighbour
618 * @param s the new state
619 * @param timeout the new timeout
622 set_state_and_timeout (struct NeighbourMapEntry *n,
623 enum GNUNET_TRANSPORT_PeerState s,
624 struct GNUNET_TIME_Absolute timeout)
627 n->timeout = timeout;
628 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' changed state to %s with timeout %s\n",
630 GNUNET_TRANSPORT_ps2s(s),
631 GNUNET_STRINGS_absolute_time_to_string (timeout));
632 neighbour_change_cb (callback_cls,
634 n->primary_address.address,
635 n->state, n->timeout,
636 n->primary_address.bandwidth_in,
637 n->primary_address.bandwidth_out);
642 * Set new state timeout for this neighbour and notify monitoring
644 * @param n the respective neighbour
645 * @param timeout the new timeout
648 set_timeout (struct NeighbourMapEntry *n,
649 struct GNUNET_TIME_Absolute timeout)
651 n->timeout = timeout;
652 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' changed timeout %s\n",
654 GNUNET_STRINGS_absolute_time_to_string (timeout));
655 neighbour_change_cb (callback_cls,
657 n->primary_address.address,
658 n->state, n->timeout,
659 n->primary_address.bandwidth_in,
660 n->primary_address.bandwidth_out);
665 * Initialize the alternative address of a neighbour
667 * @param n the neighbour
668 * @param address address of the other peer, NULL if other peer
670 * @param session session to use (or NULL, in which case an
671 * address must be setup)
672 * @param bandwidth_in inbound quota to be used when connection is up
673 * @param bandwidth_out outbound quota to be used when connection is up
676 set_alternative_address (struct NeighbourMapEntry *n,
677 const struct GNUNET_HELLO_Address *address,
678 struct Session *session,
679 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
680 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
682 struct GNUNET_TRANSPORT_PluginFunctions *papi;
683 if (NULL == (papi = GST_plugins_find (address->transport_name)))
688 if (session == n->alternative_address.session)
690 n->alternative_address.bandwidth_in = bandwidth_in;
691 n->alternative_address.bandwidth_out = bandwidth_out;
694 free_address (&n->alternative_address);
696 session = papi->get_session (papi->cls, address);
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700 "Failed to obtain new session for peer `%s' and address '%s'\n",
701 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
702 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
706 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' configured alternative address %s\n",
708 GST_plugins_a2s(address));
710 n->alternative_address.address = GNUNET_HELLO_address_copy (address);
711 n->alternative_address.bandwidth_in = bandwidth_in;
712 n->alternative_address.bandwidth_out = bandwidth_out;
713 n->alternative_address.session = session;
714 n->alternative_address.ats_active = GNUNET_NO;
715 n->alternative_address.keep_alive_nonce = 0;
720 * Initialize the primary address of a neighbour
722 * @param n the neighbour
723 * @param address address of the other peer, NULL if other peer
725 * @param session session to use (or NULL, in which case an
726 * address must be setup)
727 * @param bandwidth_in inbound quota to be used when connection is up
728 * @param bandwidth_out outbound quota to be used when connection is up
729 * @param is_active #GNUNET_YES to mark this as the active address with ATS
732 set_primary_address (struct NeighbourMapEntry *n,
733 const struct GNUNET_HELLO_Address *address,
734 struct Session *session,
735 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
736 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
739 struct GNUNET_TRANSPORT_PluginFunctions *papi;
741 if (NULL == (papi = GST_plugins_find (address->transport_name)))
746 if (session == n->primary_address.session)
748 n->primary_address.bandwidth_in = bandwidth_in;
749 n->primary_address.bandwidth_out = bandwidth_out;
750 if (is_active != n->primary_address.ats_active)
752 n->primary_address.ats_active = is_active;
753 GNUNET_ATS_address_in_use (GST_ats, n->primary_address.address, n->primary_address.session, is_active);
754 GST_validation_set_address_use (n->primary_address.address, n->primary_address.session, is_active);
756 if (GNUNET_YES == is_active)
758 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
759 send_outbound_quota (&address->peer, bandwidth_out);
763 free_address (&n->primary_address);
765 session = papi->get_session (papi->cls, address);
768 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
769 "Failed to obtain new session for peer `%s' and address '%s'\n",
770 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
771 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
775 n->primary_address.address = GNUNET_HELLO_address_copy (address);
776 n->primary_address.bandwidth_in = bandwidth_in;
777 n->primary_address.bandwidth_out = bandwidth_out;
778 n->primary_address.session = session;
779 n->primary_address.ats_active = is_active;
780 n->primary_address.keep_alive_nonce = 0;
781 if (GNUNET_YES == is_active)
783 /* Telling ATS about new session */
784 GNUNET_ATS_address_in_use (GST_ats, n->primary_address.address, n->primary_address.session, GNUNET_YES);
785 GST_validation_set_address_use (n->primary_address.address, n->primary_address.session, GNUNET_YES);
786 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
787 send_outbound_quota (&address->peer, bandwidth_out);
790 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' switched to address `%s'\n",
792 GST_plugins_a2s(address));
794 neighbour_change_cb (callback_cls,
796 n->primary_address.address,
797 n->state, n->timeout,
798 n->primary_address.bandwidth_in,
799 n->primary_address.bandwidth_out);
803 * Clear the primary address of a neighbour since this primary address is not
804 * valid anymore and notify monitoring about it
806 * @param n the neighbour
809 unset_primary_address (struct NeighbourMapEntry *n)
816 * Free a neighbour map entry.
818 * @param n entry to free
819 * @param keep_sessions #GNUNET_NO to tell plugin to terminate sessions,
820 * #GNUNET_YES to keep all sessions
823 free_neighbour (struct NeighbourMapEntry *n,
826 struct MessageQueue *mq;
827 struct GNUNET_TRANSPORT_PluginFunctions *papi;
828 struct GNUNET_HELLO_Address *backup_primary;
830 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831 "Freeing neighbur state of peer `%s'\n",
832 GNUNET_i2s (&n->id));
833 n->is_active = NULL; /* always free'd by its own continuation! */
835 /* fail messages currently in the queue */
836 while (NULL != (mq = n->messages_head))
838 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
839 if (NULL != mq->cont)
840 mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
843 /* It is too late to send other peer disconnect notifications, but at
844 least internally we need to get clean... */
845 if (GNUNET_YES == test_connected (n))
847 GNUNET_STATISTICS_set (GST_stats,
848 gettext_noop ("# peers connected"),
849 --neighbours_connected,
851 disconnect_notify_cb (callback_cls, &n->id);
853 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
855 if (NULL != n->primary_address.address)
857 backup_primary = GNUNET_HELLO_address_copy (n->primary_address.address);
860 backup_primary = NULL;
862 /* free addresses and mark as unused */
863 free_address (&n->primary_address);
864 free_address (&n->alternative_address);
866 /* FIXME: Note that if we are switching between two TCP sessions to
867 the same peer, we might want to selectively kill only one of
868 them! Killing all sessions like this seems to be very, very
871 /* cut transport-level connection */
872 if ((GNUNET_NO == keep_sessions) &&
873 (NULL != backup_primary) &&
874 (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
875 papi->disconnect_peer (papi->cls, &n->id);
877 GNUNET_free_non_null (backup_primary);
879 GNUNET_assert (GNUNET_YES ==
880 GNUNET_CONTAINER_multipeermap_remove (neighbours,
883 // FIXME-ATS-API: we might want to be more specific about
884 // which states we do this from in the future (ATS should
885 // have given us a 'suggest_address' handle, and if we have
886 // such a handle, we should cancel the operation here!
887 if (NULL != n->suggest_handle)
889 GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
890 n->suggest_handle = NULL;
893 if (GNUNET_SCHEDULER_NO_TASK != n->task)
895 GNUNET_SCHEDULER_cancel (n->task);
896 n->task = GNUNET_SCHEDULER_NO_TASK;
898 /* free rest of memory */
904 * Transmit a message using the current session of the given
907 * @param n entry for the recipient
908 * @param msgbuf buffer to transmit
909 * @param msgbuf_size number of bytes in @a msgbuf buffer
910 * @param priority transmission priority
911 * @param timeout transmission timeout
912 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
913 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
914 * @param cont continuation to call when finished (can be NULL)
915 * @param cont_cls closure for @a cont
916 * @return timeout (copy of @a timeout or a calculated one if
917 * @a use_keepalive_timeout is #GNUNET_YES.
919 static struct GNUNET_TIME_Relative
920 send_with_session (struct NeighbourMapEntry *n,
921 const char *msgbuf, size_t msgbuf_size,
923 struct GNUNET_TIME_Relative timeout,
924 unsigned int use_keepalive_timeout,
925 GNUNET_TRANSPORT_TransmitContinuation cont,
928 struct GNUNET_TRANSPORT_PluginFunctions *papi;
929 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
931 GNUNET_assert (n->primary_address.session != NULL);
932 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
933 (-1 == papi->send (papi->cls,
934 n->primary_address.session,
937 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
938 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
939 papi->query_keepalive_factor (papi->cls))),
940 cont, cont_cls)))) &&
942 cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
943 GST_neighbours_notify_data_sent (&n->id,
944 n->primary_address.address, n->primary_address.session, msgbuf_size);
945 GNUNET_break (NULL != papi);
951 * Master task run for every neighbour. Performs all of the time-related
952 * activities (keep alive, send next message, disconnect if idle, finish
953 * clean up after disconnect).
955 * @param cls the `struct NeighbourMapEntry` for which we are running
956 * @param tc scheduler context (unused)
959 master_task (void *cls,
960 const struct GNUNET_SCHEDULER_TaskContext *tc);
964 * Function called when the 'DISCONNECT' message has been sent by the
965 * plugin. Frees the neighbour --- if the entry still exists.
968 * @param target identity of the neighbour that was disconnected
969 * @param result #GNUNET_OK if the disconnect got out successfully
970 * @param payload bytes payload
971 * @param physical bytes physical
974 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
975 int result, size_t payload, size_t physical)
977 struct NeighbourMapEntry *n;
979 n = lookup_neighbour (target);
981 return; /* already gone */
982 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
983 return; /* have created a fresh entry since */
984 if (GNUNET_SCHEDULER_NO_TASK != n->task)
985 GNUNET_SCHEDULER_cancel (n->task);
986 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
991 * Transmit a DISCONNECT message to the other peer.
993 * @param n neighbour to send DISCONNECT message.
996 send_disconnect (struct NeighbourMapEntry *n)
998 struct SessionDisconnectMessage disconnect_msg;
1000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1001 "Sending DISCONNECT message to peer `%4s'\n",
1002 GNUNET_i2s (&n->id));
1003 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1004 disconnect_msg.header.type =
1005 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1006 disconnect_msg.reserved = htonl (0);
1007 disconnect_msg.purpose.size =
1008 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1009 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1010 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1011 disconnect_msg.purpose.purpose =
1012 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1013 disconnect_msg.timestamp =
1014 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1015 disconnect_msg.public_key = GST_my_identity.public_key;
1016 GNUNET_assert (GNUNET_OK ==
1017 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1018 &disconnect_msg.purpose,
1019 &disconnect_msg.signature));
1021 (void) send_with_session (n,
1022 (const char *) &disconnect_msg, sizeof (disconnect_msg),
1023 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1024 GNUNET_NO, &send_disconnect_cont, NULL);
1025 GNUNET_STATISTICS_update (GST_stats,
1027 ("# DISCONNECT messages sent"), 1,
1033 * Disconnect from the given neighbour, clean up the record.
1035 * @param n neighbour to disconnect from
1038 disconnect_neighbour (struct NeighbourMapEntry *n)
1040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1041 "Disconnecting from peer %s in state %s\n",
1042 GNUNET_i2s (&n->id),
1043 GNUNET_TRANSPORT_ps2s (n->state));
1044 /* depending on state, notify neighbour and/or upper layers of this peer
1048 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1049 case GNUNET_TRANSPORT_PS_INIT_ATS:
1050 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
1051 /* other peer is completely unaware of us, no need to send DISCONNECT */
1052 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
1053 free_neighbour (n, GNUNET_NO);
1055 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
1056 send_disconnect (n);
1057 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1059 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
1060 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
1061 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
1062 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1063 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
1064 free_neighbour (n, GNUNET_NO);
1066 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
1067 /* we DID ACK the other peer's request, must send DISCONNECT */
1068 send_disconnect (n);
1069 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1071 case GNUNET_TRANSPORT_PS_CONNECTED:
1072 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
1073 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1074 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
1075 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
1076 /* we are currently connected, need to send disconnect and do
1077 internal notifications and update statistics */
1078 send_disconnect (n);
1079 GNUNET_STATISTICS_set (GST_stats,
1080 gettext_noop ("# peers connected"),
1081 --neighbours_connected,
1083 disconnect_notify_cb (callback_cls, &n->id);
1084 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1086 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1087 /* ATS address request timeout, disconnect without sending disconnect message */
1088 GNUNET_STATISTICS_set (GST_stats,
1089 gettext_noop ("# peers connected"),
1090 --neighbours_connected,
1092 disconnect_notify_cb (callback_cls, &n->id);
1093 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1095 case GNUNET_TRANSPORT_PS_DISCONNECT:
1096 /* already disconnected, ignore */
1098 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1099 /* already cleaned up, how did we get here!? */
1103 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1104 "Unhandled state `%s'\n",
1105 GNUNET_TRANSPORT_ps2s (n->state));
1109 /* schedule timeout to clean up */
1110 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1111 GNUNET_SCHEDULER_cancel (n->task);
1112 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1118 * We're done with our transmission attempt, continue processing.
1120 * @param cls the `struct MessageQueue` of the message
1121 * @param receiver intended receiver
1122 * @param success whether it worked or not
1123 * @param size_payload bytes payload sent
1124 * @param physical bytes sent on wire
1127 transmit_send_continuation (void *cls,
1128 const struct GNUNET_PeerIdentity *receiver,
1129 int success, size_t size_payload, size_t physical)
1131 struct MessageQueue *mq = cls;
1132 struct NeighbourMapEntry *n;
1134 if (NULL == (n = lookup_neighbour (receiver)))
1137 return; /* disconnect or other error while transmitting, can happen */
1139 if (n->is_active == mq)
1141 /* this is still "our" neighbour, remove us from its queue
1142 and allow it to send the next message now */
1143 n->is_active = NULL;
1144 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1145 GNUNET_SCHEDULER_cancel (n->task);
1146 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1148 if (bytes_in_send_queue < mq->message_buf_size)
1150 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1151 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1152 bytes_in_send_queue, mq->message_buf_size,
1153 (GNUNET_OK == success) ? "OK" : "FAIL",
1154 size_payload, physical);
1159 GNUNET_break (size_payload == mq->message_buf_size);
1160 bytes_in_send_queue -= mq->message_buf_size;
1161 GNUNET_STATISTICS_set (GST_stats,
1163 ("# bytes in message queue for other peers"),
1164 bytes_in_send_queue, GNUNET_NO);
1165 if (GNUNET_OK == success)
1166 GNUNET_STATISTICS_update (GST_stats,
1168 ("# messages transmitted to other peers"),
1171 GNUNET_STATISTICS_update (GST_stats,
1173 ("# transmission failures for messages to other peers"),
1175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176 "Sending message to `%s' of type %u was a %s\n",
1177 GNUNET_i2s (receiver),
1178 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1179 (success == GNUNET_OK) ? "success" : "FAILURE");
1180 if (NULL != mq->cont)
1181 mq->cont (mq->cont_cls, success, size_payload, physical);
1187 * Check the message list for the given neighbour and if we can
1188 * send a message, do so. This function should only be called
1189 * if the connection is at least generally ready for transmission.
1190 * While we will only send one message at a time, no bandwidth
1191 * quota management is performed here. If a message was given to
1192 * the plugin, the continuation will automatically re-schedule
1193 * the 'master' task once the next message might be transmitted.
1195 * @param n target peer for which to transmit
1198 try_transmission_to_peer (struct NeighbourMapEntry *n)
1200 struct MessageQueue *mq;
1201 struct GNUNET_TIME_Relative timeout;
1203 if (NULL == n->primary_address.address)
1205 /* no address, why are we here? */
1209 if ((0 == n->primary_address.address->address_length) &&
1210 (NULL == n->primary_address.session))
1212 /* no address, why are we here? */
1216 if (NULL != n->is_active)
1218 /* transmission already pending */
1222 /* timeout messages from the queue that are past their due date */
1223 while (NULL != (mq = n->messages_head))
1225 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1226 if (timeout.rel_value_us > 0)
1228 GNUNET_STATISTICS_update (GST_stats,
1230 ("# messages timed out while in transport queue"),
1232 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1234 transmit_send_continuation (mq, &n->id,
1236 mq->message_buf_size, 0); /* timeout */
1239 return; /* no more messages */
1240 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1242 (void) send_with_session (n,
1243 mq->message_buf, mq->message_buf_size,
1244 0 /* priority */, timeout, GNUNET_NO,
1245 &transmit_send_continuation, mq);
1250 * Send keepalive message to the neighbour. Must only be called
1251 * if we are on 'connected' state or while trying to switch addresses.
1252 * Will internally determine if a keepalive is truly needed (so can
1253 * always be called).
1255 * @param n neighbour that went idle and needs a keepalive
1258 send_keepalive (struct NeighbourMapEntry *n)
1260 struct SessionKeepAliveMessage m;
1261 struct GNUNET_TIME_Relative timeout;
1264 GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1265 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1266 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT));
1267 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1268 return; /* no keepalive needed at this time */
1270 nonce = 0; /* 0 indicates 'not set' */
1272 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1275 "Sending keep alive to peer `%s' with nonce %u\n",
1276 GNUNET_i2s (&n->id), nonce);
1278 m.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1279 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1280 m.nonce = htonl (nonce);
1282 timeout = send_with_session (n,
1283 (const void *) &m, sizeof (m),
1284 UINT32_MAX /* priority */,
1285 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1287 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1289 n->primary_address.keep_alive_nonce = nonce;
1290 n->expect_latency_response = GNUNET_YES;
1291 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1292 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1298 * Keep the connection to the given neighbour alive longer,
1299 * we received a KEEPALIVE (or equivalent); send a response.
1301 * @param neighbour neighbour to keep alive (by sending keep alive response)
1302 * @param m the keep alive message containing the nonce to respond to
1305 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1306 const struct GNUNET_MessageHeader *m)
1308 struct NeighbourMapEntry *n;
1309 const struct SessionKeepAliveMessage *msg_in;
1310 struct SessionKeepAliveMessage msg;
1312 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1315 msg_in = (struct SessionKeepAliveMessage *) m;
1316 if (NULL == (n = lookup_neighbour (neighbour)))
1318 GNUNET_STATISTICS_update (GST_stats,
1320 ("# KEEPALIVE messages discarded (peer unknown)"),
1324 if (NULL == n->primary_address.session)
1326 GNUNET_STATISTICS_update (GST_stats,
1328 ("# KEEPALIVE messages discarded (no session)"),
1333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1334 "Received keep alive request from peer `%s' with nonce %u\n",
1335 GNUNET_i2s (&n->id), ntohl (msg_in->nonce));
1337 /* send reply to allow neighbour to measure latency */
1338 msg.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1339 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1340 msg.nonce = msg_in->nonce;
1341 (void) send_with_session(n,
1342 (const void *) &msg, sizeof (struct SessionKeepAliveMessage),
1343 UINT32_MAX /* priority */,
1344 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1350 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1351 * latency to this peer. Pass the updated information (existing ats
1352 * plus calculated latency) to ATS.
1354 * @param neighbour neighbour to keep alive
1355 * @param m the message containing the keep alive response
1358 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1359 const struct GNUNET_MessageHeader *m)
1361 struct NeighbourMapEntry *n;
1362 const struct SessionKeepAliveMessage *msg;
1363 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1365 struct GNUNET_ATS_Information ats;
1367 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1370 msg = (const struct SessionKeepAliveMessage *) m;
1371 if (NULL == (n = lookup_neighbour (neighbour)))
1373 GNUNET_STATISTICS_update (GST_stats,
1375 ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1379 if ( (GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1380 (GNUNET_YES != n->expect_latency_response) )
1382 GNUNET_STATISTICS_update (GST_stats,
1384 ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1388 if (NULL == n->primary_address.address)
1390 GNUNET_STATISTICS_update (GST_stats,
1392 ("# KEEPALIVE_RESPONSE messages discarded (address changed)"),
1396 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1398 GNUNET_STATISTICS_update (GST_stats,
1400 ("# KEEPALIVE_RESPONSE messages discarded (wrong nonce)"),
1406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1407 "Received keep alive response from peer `%s' for session %p\n",
1408 GNUNET_i2s (&n->id), n->primary_address.session);
1412 /* Update session timeout here */
1413 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1416 "Updating session for peer `%s' for session %p\n",
1417 GNUNET_i2s (&n->id), n->primary_address.session);
1418 papi->update_session_timeout (papi->cls, &n->id, n->primary_address.session);
1425 n->primary_address.keep_alive_nonce = 0;
1426 n->expect_latency_response = GNUNET_NO;
1427 n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1428 set_timeout (n, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1431 "Latency for peer `%s' is %s\n",
1432 GNUNET_i2s (&n->id),
1433 GNUNET_STRINGS_relative_time_to_string (n->latency,
1435 /* append latency */
1436 ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1437 if (n->latency.rel_value_us > UINT32_MAX)
1438 latency = UINT32_MAX;
1440 latency = n->latency.rel_value_us;
1441 ats.value = htonl (latency);
1442 GST_ats_update_metrics (&n->id, n->primary_address.address,
1443 n->primary_address.session, &ats, 1);
1448 * We have received a message from the given sender. How long should
1449 * we delay before receiving more? (Also used to keep the peer marked
1452 * @param sender sender of the message
1453 * @param size size of the message
1454 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1455 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1456 * #GNUNET_SYSERR if the connection is not fully up yet
1457 * @return how long to wait before reading more from this sender
1459 struct GNUNET_TIME_Relative
1460 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1461 *sender, ssize_t size, int *do_forward)
1463 struct NeighbourMapEntry *n;
1464 struct GNUNET_TIME_Relative ret;
1466 if (NULL == neighbours)
1468 *do_forward = GNUNET_NO;
1469 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1471 if (NULL == (n = lookup_neighbour (sender)))
1473 GST_neighbours_try_connect (sender);
1474 if (NULL == (n = lookup_neighbour (sender)))
1476 GNUNET_STATISTICS_update (GST_stats,
1478 ("# messages discarded due to lack of neighbour record"),
1480 *do_forward = GNUNET_NO;
1481 return GNUNET_TIME_UNIT_ZERO;
1484 if (! test_connected (n))
1486 *do_forward = GNUNET_SYSERR;
1487 return GNUNET_TIME_UNIT_ZERO;
1489 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1491 n->quota_violation_count++;
1492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1493 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1494 n->in_tracker.available_bytes_per_s__,
1495 n->quota_violation_count);
1496 /* Discount 32k per violation */
1497 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1501 if (n->quota_violation_count > 0)
1503 /* try to add 32k back */
1504 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1505 n->quota_violation_count--;
1508 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1510 GNUNET_STATISTICS_update (GST_stats,
1512 ("# bandwidth quota violations by other peers"),
1514 *do_forward = GNUNET_NO;
1515 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1517 *do_forward = GNUNET_YES;
1518 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1519 if (ret.rel_value_us > 0)
1521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1522 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1523 (unsigned long long) n->in_tracker.
1524 consumption_since_last_update__,
1525 (unsigned int) n->in_tracker.available_bytes_per_s__,
1526 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1527 GNUNET_STATISTICS_update (GST_stats,
1528 gettext_noop ("# ms throttling suggested"),
1529 (int64_t) ret.rel_value_us / 1000LL,
1537 * Transmit a message to the given target using the active connection.
1539 * @param target destination
1540 * @param msg message to send
1541 * @param msg_size number of bytes in msg
1542 * @param timeout when to fail with timeout
1543 * @param cont function to call when done
1544 * @param cont_cls closure for 'cont'
1547 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1548 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1549 GST_NeighbourSendContinuation cont, void *cont_cls)
1551 struct NeighbourMapEntry *n;
1552 struct MessageQueue *mq;
1554 /* All ove these cases should never happen; they are all API violations.
1555 But we check anyway, just to be sure. */
1556 if (NULL == (n = lookup_neighbour (target)))
1560 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1563 if (GNUNET_YES != test_connected (n))
1567 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1570 bytes_in_send_queue += msg_size;
1571 GNUNET_STATISTICS_set (GST_stats,
1573 ("# bytes in message queue for other peers"),
1574 bytes_in_send_queue, GNUNET_NO);
1575 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1577 mq->cont_cls = cont_cls;
1578 memcpy (&mq[1], msg, msg_size);
1579 mq->message_buf = (const char *) &mq[1];
1580 mq->message_buf_size = msg_size;
1581 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1582 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1583 if ( (NULL != n->is_active) ||
1584 ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1586 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1587 GNUNET_SCHEDULER_cancel (n->task);
1588 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1593 * Send a SESSION_CONNECT message via the given address.
1595 * @param na address to use
1598 send_session_connect (struct NeighbourAddress *na)
1600 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1601 struct SessionConnectMessage connect_msg;
1603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1604 "Sending SESSION_CONNECT message to peer %s\n",
1605 GNUNET_i2s (&na->address->peer));
1606 if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1611 if (NULL == na->session)
1612 na->session = papi->get_session (papi->cls, na->address);
1613 if (NULL == na->session)
1618 GNUNET_STATISTICS_update (GST_stats,
1620 ("# SESSION_CONNECT messages sent"),
1622 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1623 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1624 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1625 connect_msg.reserved = htonl (0);
1626 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1628 papi->send (papi->cls,
1630 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1632 GNUNET_TIME_UNIT_FOREVER_REL,
1635 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1636 _("Failed to transmit CONNECT message via plugin to %s\n"),
1637 GST_plugins_a2s (na->address));
1639 GST_neighbours_notify_data_sent (&na->address->peer,
1642 sizeof (struct SessionConnectMessage));
1648 * Send a SESSION_CONNECT_ACK message via the given address.
1650 * @param address address to use
1651 * @param session session to use
1652 * @param timestamp timestamp to use for the ACK message
1655 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1656 struct Session *session,
1657 struct GNUNET_TIME_Absolute timestamp)
1659 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1660 struct SessionConnectMessage connect_msg;
1662 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1663 "Sending CONNECT_ACK to peer `%s'\n",
1664 GNUNET_i2s (&address->peer));
1665 if (NULL == (papi = GST_plugins_find (address->transport_name)))
1670 if (NULL == session)
1671 session = papi->get_session (papi->cls, address);
1672 if (NULL == session)
1677 GNUNET_STATISTICS_update (GST_stats,
1679 ("# CONNECT_ACK messages sent"),
1681 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1682 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1683 connect_msg.reserved = htonl (0);
1684 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1685 (void) papi->send (papi->cls,
1687 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1689 GNUNET_TIME_UNIT_FOREVER_REL,
1694 struct QuotaNotificationRequest
1696 struct GNUNET_PeerIdentity peer;
1697 struct Session *session;
1701 struct QNR_LookContext
1703 struct GNUNET_PeerIdentity peer;
1704 struct Session *session;
1707 struct QuotaNotificationRequest *res;
1711 find_notification_request (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1713 struct QNR_LookContext *qnr_ctx = cls;
1714 struct QuotaNotificationRequest *qnr = value;
1716 if ((qnr->session == qnr_ctx->session) &&
1717 (0 == memcmp (&qnr->peer, &qnr_ctx->peer, sizeof (struct GNUNET_PeerIdentity))) &&
1718 (0 == strcmp(qnr_ctx->plugin, qnr->plugin)))
1720 qnr_ctx->res = value;
1727 GST_neighbours_register_quota_notification(void *cls,
1728 const struct GNUNET_PeerIdentity *peer, const char *plugin,
1729 struct Session *session)
1731 struct QuotaNotificationRequest *qnr;
1732 struct QNR_LookContext qnr_ctx;
1734 qnr_ctx.peer = (*peer);
1735 qnr_ctx.plugin = plugin;
1736 qnr_ctx.session = session;
1740 res = GNUNET_CONTAINER_multipeermap_get_multiple (registered_quota_notifications,
1741 peer, &find_notification_request, &qnr_ctx);
1742 if (NULL != qnr_ctx.res)
1748 qnr = GNUNET_new (struct QuotaNotificationRequest);
1749 qnr->peer = (*peer);
1750 qnr->plugin = GNUNET_strdup (plugin);
1751 qnr->session = session;
1753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1754 "Adding notification for peer `%s' plugin `%s' session %p \n",
1755 GNUNET_i2s (peer), plugin, session);
1757 GNUNET_CONTAINER_multipeermap_put (registered_quota_notifications, peer,
1758 qnr, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1763 GST_neighbours_unregister_quota_notification(void *cls,
1764 const struct GNUNET_PeerIdentity *peer, const char *plugin, struct Session *session)
1766 struct QNR_LookContext qnr_ctx;
1767 qnr_ctx.peer = (*peer);
1768 qnr_ctx.plugin = plugin;
1769 qnr_ctx.session = session;
1773 res = GNUNET_CONTAINER_multipeermap_iterate (registered_quota_notifications,
1774 &find_notification_request, &qnr_ctx);
1775 if (NULL == qnr_ctx.res)
1781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1782 "Removing notification for peer `%s' plugin `%s' session %p \n",
1783 GNUNET_i2s (peer), plugin, session);
1785 GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, peer,
1787 GNUNET_free (qnr_ctx.res->plugin);
1788 GNUNET_free (qnr_ctx.res);
1792 notification_cb(void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1794 struct NeighbourMapEntry *n = cls;
1795 struct QuotaNotificationRequest *qnr = value;
1796 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1797 struct GNUNET_TIME_Relative delay;
1800 papi = GST_plugins_find(qnr->plugin);
1807 delay = GST_neighbours_calculate_receive_delay (key, 0, &do_forward);
1808 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1809 "New inbound delay for peer `%s' is %llu ms\n", GNUNET_i2s (key),
1810 delay.rel_value_us / 1000);
1812 if (NULL != papi->update_inbound_delay)
1813 papi->update_inbound_delay (papi->cls, key, qnr->session, delay);
1819 free_notification_cb(void *cls, const struct GNUNET_PeerIdentity *key,
1822 struct NeighbourMapEntry *n = cls;
1823 struct QuotaNotificationRequest *qnr = value;
1825 GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, key,
1833 inbound_bw_tracker_update(void *cls)
1835 struct NeighbourMapEntry *n = cls;
1837 /* Quota was updated, tell plugins to update the time to receive next */
1838 GNUNET_CONTAINER_multipeermap_get_multiple (registered_quota_notifications,
1839 &n->id, ¬ification_cb, n);
1844 * Create a fresh entry in the neighbour map for the given peer
1846 * @param peer peer to create an entry for
1847 * @return new neighbour map entry
1849 static struct NeighbourMapEntry *
1850 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1852 struct NeighbourMapEntry *n;
1854 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1855 "Creating new neighbour entry for `%s'\n",
1857 n = GNUNET_new (struct NeighbourMapEntry);
1859 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1860 n->last_util_transmission = GNUNET_TIME_absolute_get();
1861 n->util_payload_bytes_recv = 0;
1862 n->util_payload_bytes_sent = 0;
1863 n->util_total_bytes_recv = 0;
1864 n->util_total_bytes_sent = 0;
1865 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker, &inbound_bw_tracker_update, n,
1866 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1867 MAX_BANDWIDTH_CARRY_S);
1868 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1869 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_FOREVER_ABS);
1870 GNUNET_assert (GNUNET_OK ==
1871 GNUNET_CONTAINER_multipeermap_put (neighbours,
1873 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1879 * Check if the two given addresses are the same.
1880 * Actually only checks if the sessions are non-NULL
1881 * (which they should be) and then if they are identical;
1882 * the actual addresses don't matter if the session
1883 * pointers match anyway, and we must have session pointers
1886 * @param a1 first address to compare
1887 * @param a2 other address to compare
1888 * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
1891 address_matches (const struct NeighbourAddress *a1,
1892 const struct NeighbourAddress *a2)
1894 if ( (NULL == a1->session) ||
1895 (NULL == a2->session) )
1900 return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1905 address_suggest_cont (void *cls,
1906 const struct GNUNET_PeerIdentity *peer,
1907 const struct GNUNET_HELLO_Address *address, struct Session *session,
1908 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
1909 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1910 const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
1916 struct BlacklistCheckSwitchContext
1918 struct BlacklistCheckSwitchContext *prev;
1919 struct BlacklistCheckSwitchContext *next;
1922 struct GST_BlacklistCheck *blc;
1924 struct GNUNET_HELLO_Address *address;
1925 struct Session *session;
1926 struct GNUNET_ATS_Information *ats;
1929 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
1930 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
1935 try_connect_bl_check_cont (void *cls,
1936 const struct GNUNET_PeerIdentity *peer, int result)
1938 struct BlacklistCheckSwitchContext *blc_ctx = cls;
1939 struct NeighbourMapEntry *n;
1941 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
1942 GNUNET_free (blc_ctx);
1944 if (GNUNET_OK != result)
1946 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1947 _("Blacklisting dissaproved to connect to peer `%s'\n"),
1951 n = setup_neighbour (peer);
1952 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1954 GNUNET_ATS_reset_backoff (GST_ats, peer);
1955 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer,
1956 &address_suggest_cont, n);
1962 * Try to create a connection to the given target (eventually).
1964 * @param target peer to try to connect to
1967 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1969 struct NeighbourMapEntry *n;
1970 struct GST_BlacklistCheck *blc;
1971 struct BlacklistCheckSwitchContext *blc_ctx;
1973 if (NULL == neighbours)
1975 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1976 "Asked to connect to peer `%s' during shutdown\n",
1977 GNUNET_i2s (target));
1978 return; /* during shutdown, do nothing */
1980 n = lookup_neighbour (target);
1981 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1982 "Asked to connect to peer `%s' (state: %s)\n",
1983 GNUNET_i2s (target),
1984 (NULL != n) ? GNUNET_TRANSPORT_ps2s(n->state) : "NEW PEER");
1989 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1990 /* this should not be possible */
1992 free_neighbour (n, GNUNET_NO);
1994 case GNUNET_TRANSPORT_PS_INIT_ATS:
1995 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
1996 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
1997 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
1998 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
1999 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2000 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2001 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2002 "Ignoring request to try to connect to `%s', already trying!\n",
2003 GNUNET_i2s (target));
2004 return; /* already trying */
2005 case GNUNET_TRANSPORT_PS_CONNECTED:
2006 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2007 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2008 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2009 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2010 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2011 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2012 "Ignoring request to try to connect, already connected to `%s'!\n",
2013 GNUNET_i2s (target));
2014 return; /* already connected */
2015 case GNUNET_TRANSPORT_PS_DISCONNECT:
2016 /* get rid of remains, ready to re-try immediately */
2017 free_neighbour (n, GNUNET_NO);
2019 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2020 /* should not be possible */
2023 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2024 "Unhandled state `%s'\n",
2025 GNUNET_TRANSPORT_ps2s (n->state));
2027 free_neighbour (n, GNUNET_NO);
2032 /* Do blacklist check if connection is allowed */
2033 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2034 GNUNET_CONTAINER_DLL_insert (pending_bc_head, pending_bc_tail, blc_ctx);
2036 if (NULL != (blc = GST_blacklist_test_allowed (target, NULL,
2037 &try_connect_bl_check_cont, blc_ctx)))
2045 * Function called with the result of a blacklist check.
2047 * @param cls closure with the `struct BlackListCheckContext`
2048 * @param peer peer this check affects
2049 * @param result #GNUNET_OK if the address is allowed
2052 handle_test_blacklist_cont (void *cls,
2053 const struct GNUNET_PeerIdentity *peer,
2056 struct BlackListCheckContext *bcc = cls;
2057 struct NeighbourMapEntry *n;
2060 GNUNET_CONTAINER_DLL_remove (bc_head,
2063 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2064 "Connection to new address of peer `%s' based on blacklist is `%s'\n",
2066 (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
2067 if (GNUNET_OK == result)
2069 GST_ats_add_address (bcc->na.address, bcc->na.session, NULL, 0);
2073 /* Blacklist disagreed on connecting to a peer with this address
2074 * Destroy address because we are not allowed to use it
2076 if (NULL != bcc->na.session)
2077 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, bcc->na.session);
2078 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, NULL);
2080 if (NULL == (n = lookup_neighbour (peer)))
2082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2083 "No neighbor entry for peer `%s', ignoring blacklist result\n",
2085 goto cleanup; /* nobody left to care about new address */
2087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088 "Received blacklist result for peer `%s' in state %s/%d\n",
2090 GNUNET_TRANSPORT_ps2s (n->state),
2091 n->send_connect_ack);
2094 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2095 /* this should not be possible */
2097 free_neighbour (n, GNUNET_NO);
2099 case GNUNET_TRANSPORT_PS_INIT_ATS:
2100 /* waiting on ATS suggestion; still, pass address to ATS as a
2103 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2104 /* check if the address the blacklist was fine with matches
2105 ATS suggestion, if so, we can move on! */
2106 if ( (GNUNET_OK == result) &&
2107 (1 == n->send_connect_ack) )
2109 n->send_connect_ack = 2;
2110 send_session_connect_ack_message (bcc->na.address,
2112 n->connect_ack_timestamp);
2114 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2116 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2117 "Blacklist result for peer %s is for non-primary address, ignored\n",
2119 break; /* result for an address we currently don't care about */
2121 if (GNUNET_OK == result)
2123 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_SENT, GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2124 send_session_connect (&n->primary_address);
2128 free_address (&n->primary_address);
2129 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2132 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2133 /* waiting on CONNECT_ACK, send ACK if one is pending */
2134 if ( (GNUNET_OK == result) &&
2135 (1 == n->send_connect_ack) )
2137 n->send_connect_ack = 2;
2138 send_session_connect_ack_message (n->primary_address.address,
2139 n->primary_address.session,
2140 n->connect_ack_timestamp);
2143 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2144 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2145 GNUNET_ATS_reset_backoff (GST_ats, peer);
2146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2147 "Suggesting address for peer %s to ATS\n",
2149 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer, &address_suggest_cont, n);
2151 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2152 /* waiting on ATS suggestion, don't care about blacklist */
2154 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2155 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2158 "Blacklist result ignored, as it is not for our primary address\n");
2159 break; /* result for an address we currently don't care about */
2161 if (GNUNET_OK == result)
2163 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK, GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2164 send_session_connect_ack_message (bcc->na.address,
2166 n->connect_ack_timestamp);
2167 if (1 == n->send_connect_ack)
2168 n->send_connect_ack = 2;
2172 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
2174 plugin = GST_plugins_find (bcc->na.address->transport_name);
2175 if ( (NULL != plugin) &&
2176 (NULL != bcc->na.session) )
2178 plugin->disconnect_session (plugin->cls,
2182 GNUNET_break (NULL != plugin);
2183 free_address (&n->primary_address);
2184 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2185 GNUNET_ATS_reset_backoff (GST_ats, peer);
2188 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2189 /* waiting on SESSION_ACK, send ACK if one is pending */
2190 if ( (GNUNET_OK == result) &&
2191 (1 == n->send_connect_ack) )
2193 n->send_connect_ack = 2;
2194 send_session_connect_ack_message (n->primary_address.address,
2195 n->primary_address.session,
2196 n->connect_ack_timestamp);
2199 case GNUNET_TRANSPORT_PS_CONNECTED:
2200 /* already connected, don't care about blacklist */
2202 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2203 /* still waiting on ATS suggestion, don't care about blacklist */
2205 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2206 if ( (GNUNET_OK == result) &&
2207 (1 == n->send_connect_ack) )
2209 n->send_connect_ack = 2;
2210 send_session_connect_ack_message (bcc->na.address,
2212 n->connect_ack_timestamp);
2214 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2217 "Blacklist result ignored, as it is not for our primary address\n");
2218 break; /* result for an address we currently don't care about */
2220 if (GNUNET_OK == result)
2222 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2223 send_session_connect (&n->primary_address);
2227 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2230 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2231 /* waiting on CONNECT_ACK, don't care about blacklist */
2232 if ( (GNUNET_OK == result) &&
2233 (1 == n->send_connect_ack) )
2235 n->send_connect_ack = 2;
2236 send_session_connect_ack_message (n->primary_address.address,
2237 n->primary_address.session,
2238 n->connect_ack_timestamp);
2241 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2242 if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2245 "Blacklist result ignored, as it is not for our primary address\n");
2246 break; /* result for an address we currently don't care about */
2248 if (GNUNET_OK == result)
2250 send_session_connect (&n->alternative_address);
2251 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT);
2255 set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
2256 free_address (&n->alternative_address);
2259 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2260 /* waiting on CONNECT_ACK, don't care about blacklist */
2261 if ( (GNUNET_OK == result) &&
2262 (1 == n->send_connect_ack) )
2264 n->send_connect_ack = 2;
2265 send_session_connect_ack_message (n->primary_address.address,
2266 n->primary_address.session,
2267 n->connect_ack_timestamp);
2270 case GNUNET_TRANSPORT_PS_DISCONNECT:
2271 /* Nothing to do here, ATS will already do what can be done */
2273 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2274 /* should not be possible */
2278 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2279 "Unhandled state `%s'\n",
2280 GNUNET_TRANSPORT_ps2s (n->state));
2282 free_neighbour (n, GNUNET_NO);
2286 GNUNET_HELLO_address_free (bcc->na.address);
2292 * We want to know if connecting to a particular peer via
2293 * a particular address is allowed. Check it!
2295 * @param peer identity of the peer to switch the address for
2296 * @param ts time at which the check was initiated
2297 * @param address address of the other peer, NULL if other peer
2299 * @param session session to use (or NULL)
2302 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2303 struct GNUNET_TIME_Absolute ts,
2304 const struct GNUNET_HELLO_Address *address,
2305 struct Session *session)
2307 struct BlackListCheckContext *bcc;
2308 struct GST_BlacklistCheck *bc;
2310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2311 "Checking peer `%s' against blacklist\n",
2313 bcc = GNUNET_new (struct BlackListCheckContext);
2314 bcc->na.address = GNUNET_HELLO_address_copy (address);
2315 bcc->na.session = session;
2316 bcc->na.connect_timestamp = ts;
2317 GNUNET_CONTAINER_DLL_insert (bc_head,
2320 if (NULL != (bc = GST_blacklist_test_allowed (peer,
2321 address->transport_name,
2322 &handle_test_blacklist_cont, bcc)))
2324 /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2325 we must only store 'bc' if 'bc' is non-NULL... */
2330 * We received a 'SESSION_CONNECT' message from the other peer.
2331 * Consider switching to it.
2333 * @param message possibly a 'struct SessionConnectMessage' (check format)
2334 * @param peer identity of the peer to switch the address for
2335 * @param address address of the other peer, NULL if other peer
2337 * @param session session to use (or NULL)
2338 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2341 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2342 const struct GNUNET_PeerIdentity *peer,
2343 const struct GNUNET_HELLO_Address *address,
2344 struct Session *session)
2346 const struct SessionConnectMessage *scm;
2347 struct NeighbourMapEntry *n;
2348 struct GNUNET_TIME_Absolute ts;
2350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2351 "Received CONNECT message from peer `%s'\n",
2353 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2355 GNUNET_break_op (0);
2356 return GNUNET_SYSERR;
2358 GNUNET_STATISTICS_update (GST_stats,
2360 ("# CONNECT messages received"),
2362 if (NULL == neighbours)
2364 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2365 _("CONNECT request from peer `%s' ignored due impending shutdown\n"),
2367 return GNUNET_OK; /* we're shutting down */
2369 scm = (const struct SessionConnectMessage *) message;
2370 GNUNET_break_op (0 == ntohl (scm->reserved));
2371 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2372 n = lookup_neighbour (peer);
2374 n = setup_neighbour (peer);
2375 n->send_connect_ack = 1;
2376 n->connect_ack_timestamp = ts;
2378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2379 "Received SESSION_CONNECT for peer `%s' in state %s/%d\n",
2381 GNUNET_TRANSPORT_ps2s (n->state),
2382 n->send_connect_ack);
2385 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2386 /* Do a blacklist check for the new address */
2387 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
2388 check_blacklist (peer, ts, address, session);
2390 case GNUNET_TRANSPORT_PS_INIT_ATS:
2391 /* CONNECT message takes priority over us asking ATS for address */
2392 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
2394 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2395 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2396 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2397 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2398 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2399 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2400 /* It can never hurt to have an alternative address in the above cases,
2401 see if it is allowed */
2402 check_blacklist (peer, ts, address, session);
2404 case GNUNET_TRANSPORT_PS_CONNECTED:
2405 /* we are already connected and can thus send the ACK immediately;
2406 still, it can never hurt to have an alternative address, so also
2407 tell ATS about it */
2408 GNUNET_assert (NULL != n->primary_address.address);
2409 GNUNET_assert (NULL != n->primary_address.session);
2410 n->send_connect_ack = 0;
2411 send_session_connect_ack_message (n->primary_address.address,
2412 n->primary_address.session, ts);
2413 check_blacklist (peer, ts, address, session);
2415 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2416 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2417 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2418 /* It can never hurt to have an alternative address in the above cases,
2419 see if it is allowed */
2420 check_blacklist (peer, ts, address, session);
2422 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2423 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2424 /* we are already connected and can thus send the ACK immediately;
2425 still, it can never hurt to have an alternative address, so also
2426 tell ATS about it */
2427 GNUNET_assert (NULL != n->primary_address.address);
2428 GNUNET_assert (NULL != n->primary_address.session);
2429 n->send_connect_ack = 0;
2430 send_session_connect_ack_message (n->primary_address.address,
2431 n->primary_address.session, ts);
2432 check_blacklist (peer, ts, address, session);
2434 case GNUNET_TRANSPORT_PS_DISCONNECT:
2435 /* get rid of remains without terminating sessions, ready to re-try */
2436 free_neighbour (n, GNUNET_YES);
2437 n = setup_neighbour (peer);
2438 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS);
2439 GNUNET_ATS_reset_backoff (GST_ats, peer);
2441 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2442 /* should not be possible */
2446 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2447 "Unhandled state `%s'\n",
2448 GNUNET_TRANSPORT_ps2s (n->state));
2450 return GNUNET_SYSERR;
2456 switch_address_bl_check_cont (void *cls,
2457 const struct GNUNET_PeerIdentity *peer, int result)
2459 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2460 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2461 struct NeighbourMapEntry *n;
2463 if ( (NULL == (n = lookup_neighbour (peer))) || (result == GNUNET_NO) ||
2464 (NULL == (papi = GST_plugins_find (blc_ctx->address->transport_name))) )
2468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2469 "Peer %s is unknown, suggestion ignored\n",
2472 if (result == GNUNET_NO)
2474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2475 "Blacklist denied to switch to suggested address `%s' sesion %p for peer `%s'\n",
2476 GST_plugins_a2s (blc_ctx->address),
2478 GNUNET_i2s (&blc_ctx->address->peer));
2480 /* Delete address (or session if existing) in ATS */
2481 GNUNET_ATS_address_destroyed (GST_ats, blc_ctx->address, blc_ctx->session);
2483 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
2484 GNUNET_HELLO_address_free(blc_ctx->address);
2485 GNUNET_free_non_null (blc_ctx->ats);
2486 GNUNET_free (blc_ctx);
2490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2491 "Blacklist accepted to switch to suggested address `%s' for peer `%s'\n",
2492 GST_plugins_a2s (blc_ctx->address),
2494 GNUNET_i2s (&blc_ctx->address->peer));
2496 if (NULL == blc_ctx->session)
2498 blc_ctx->session = papi->get_session (papi->cls, blc_ctx->address);
2499 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2500 "Obtained new session for peer `%s' and address '%s': %p\n",
2501 GNUNET_i2s (&blc_ctx->address->peer), GST_plugins_a2s (blc_ctx->address), blc_ctx->session);
2503 if (NULL == blc_ctx->session)
2505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2506 "Failed to obtain new session for peer `%s' and address '%s'\n",
2507 GNUNET_i2s (&blc_ctx->address->peer), GST_plugins_a2s (blc_ctx->address));
2508 GNUNET_ATS_address_destroyed (GST_ats, blc_ctx->address, NULL);
2513 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2515 free_neighbour (n, GNUNET_NO);
2517 case GNUNET_TRANSPORT_PS_INIT_ATS:
2518 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2519 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2520 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_BLACKLIST,
2521 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2522 check_blacklist (&n->id, n->connect_ack_timestamp,
2523 blc_ctx->address, blc_ctx->session);
2525 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2526 /* ATS suggests a different address, switch again */
2527 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2528 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2529 set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2530 check_blacklist (&n->id, n->connect_ack_timestamp,
2531 blc_ctx->address, blc_ctx->session);
2533 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2534 /* ATS suggests a different address, switch again */
2535 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2536 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2537 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_BLACKLIST,
2538 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2539 check_blacklist (&n->id, n->connect_ack_timestamp,
2540 blc_ctx->address, blc_ctx->session);
2542 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2543 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2544 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2545 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST,
2546 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2547 check_blacklist (&n->id, n->connect_ack_timestamp,
2548 blc_ctx->address, blc_ctx->session);
2550 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2551 set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2552 check_blacklist (&n->id, n->connect_ack_timestamp,
2553 blc_ctx->address, blc_ctx->session);
2555 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2556 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2557 /* ATS asks us to switch while we were trying to connect; switch to new
2558 address and check blacklist again */
2559 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2560 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2561 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST,
2562 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2563 check_blacklist (&n->id, n->connect_ack_timestamp,
2564 blc_ctx->address, blc_ctx->session);
2566 case GNUNET_TRANSPORT_PS_CONNECTED:
2567 GNUNET_assert (NULL != n->primary_address.address);
2568 GNUNET_assert (NULL != n->primary_address.session);
2569 if (n->primary_address.session == blc_ctx->session)
2571 /* not an address change, just a quota change */
2572 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2573 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_YES);
2576 /* ATS asks us to switch a life connection; see if we can get
2577 a CONNECT_ACK on it before we actually do this! */
2578 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
2579 set_alternative_address (n, blc_ctx->address, blc_ctx->session,
2580 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
2581 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2582 blc_ctx->address, blc_ctx->session);
2584 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2585 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2586 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2587 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST,
2588 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2589 check_blacklist (&n->id, n->connect_ack_timestamp,
2590 blc_ctx->address, blc_ctx->session);
2592 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2593 /* ATS asks us to switch while we were trying to reconnect; switch to new
2594 address and check blacklist again */
2595 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2596 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2597 set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2598 check_blacklist (&n->id, n->connect_ack_timestamp,
2599 blc_ctx->address, blc_ctx->session);
2601 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2602 /* ATS asks us to switch while we were trying to reconnect; switch to new
2603 address and check blacklist again */
2604 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2605 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2606 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST,
2607 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2608 check_blacklist (&n->id, n->connect_ack_timestamp,
2609 blc_ctx->address, blc_ctx->session);
2611 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2612 if (n->primary_address.session == blc_ctx->session)
2614 /* ATS switches back to still-active session */
2615 set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
2616 free_address (&n->alternative_address);
2619 /* ATS asks us to switch a life connection, update blacklist check */
2620 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2621 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2622 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2623 blc_ctx->address, blc_ctx->session);
2625 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2626 if (n->primary_address.session == blc_ctx->session)
2628 /* ATS switches back to still-active session */
2629 free_address (&n->alternative_address);
2630 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
2633 /* ATS asks us to switch a life connection, update blacklist check */
2634 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
2635 set_alternative_address (n, blc_ctx->address, blc_ctx->session,
2636 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
2637 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2638 blc_ctx->address, blc_ctx->session);
2640 case GNUNET_TRANSPORT_PS_DISCONNECT:
2641 /* not going to switch addresses while disconnecting */
2643 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2647 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2648 "Unhandled state `%s'\n",
2649 GNUNET_TRANSPORT_ps2s (n->state));
2654 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
2655 GNUNET_HELLO_address_free(blc_ctx->address);
2656 GNUNET_free_non_null (blc_ctx->ats);
2657 GNUNET_free (blc_ctx);
2663 * For the given peer, switch to this address.
2665 * Before accepting this addresses and actively using it, a blacklist check
2666 * is performed. If this blacklist check fails the address will be destroyed.
2668 * @param peer identity of the peer to switch the address for
2669 * @param address address of the other peer,
2670 * @param session session to use or NULL if transport should initiate a session
2671 * @param ats performance data
2672 * @param ats_count number of entries in ats
2673 * @param bandwidth_in inbound quota to be used when connection is up,
2674 * 0 to disconnect from peer
2675 * @param bandwidth_out outbound quota to be used when connection is up,
2676 * 0 to disconnect from peer
2679 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2680 const struct GNUNET_HELLO_Address *address,
2681 struct Session *session,
2682 const struct GNUNET_ATS_Information *ats,
2684 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2685 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2687 struct NeighbourMapEntry *n;
2688 struct GST_BlacklistCheck *blc;
2689 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2690 struct BlacklistCheckSwitchContext *blc_ctx;
2693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2694 "ATS has decided on an address for peer %s\n",
2696 GNUNET_assert (NULL != address->transport_name);
2697 if (NULL == (n = lookup_neighbour (peer)))
2699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2700 "Peer %s is unknown, suggestion ignored\n",
2705 /* Obtain an session for this address from plugin */
2706 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2708 /* we don't have the plugin for this address */
2709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2710 "Plugin `%s' is unknown, suggestion for peer %s ignored\n",
2711 address->transport_name,
2713 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2716 if ((NULL == session) &&
2717 (GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2719 /* This is a inbound address and we do not have a session to use! */
2720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2721 "Inbound address without session `%s'! Destroying address...\n",
2722 GST_plugins_a2s (address));
2723 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2727 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2728 "ATS tells us to switch to %s address '%s' session %p for "
2729 "peer `%s' in state %s/%d (quota in/out %u %u )\n",
2730 GNUNET_HELLO_address_check_option (address,
2731 GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "",
2732 GST_plugins_a2s (address), session, GNUNET_i2s (peer),
2733 GNUNET_TRANSPORT_ps2s (n->state), n->send_connect_ack,
2734 ntohl (bandwidth_in.value__), ntohl (bandwidth_out.value__));
2736 /* Perform blacklist check */
2737 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2738 blc_ctx->address = GNUNET_HELLO_address_copy (address);
2739 blc_ctx->session = session;
2740 blc_ctx->bandwidth_in = bandwidth_in;
2741 blc_ctx->bandwidth_out = bandwidth_out;
2742 blc_ctx->ats_count = ats_count;
2743 blc_ctx->ats = NULL;
2746 blc_ctx->ats = GNUNET_malloc (ats_count * sizeof (struct GNUNET_ATS_Information));
2747 for (c = 0; c < ats_count; c++)
2749 blc_ctx->ats[c].type = ats[c].type;
2750 blc_ctx->ats[c].value = ats[c].value;
2754 GNUNET_CONTAINER_DLL_insert (pending_bc_head, pending_bc_tail, blc_ctx);
2755 if (NULL != (blc = GST_blacklist_test_allowed (peer, address->transport_name,
2756 &switch_address_bl_check_cont, blc_ctx)))
2764 send_utilization_data (void *cls,
2765 const struct GNUNET_PeerIdentity *key,
2768 struct NeighbourMapEntry *n = value;
2769 struct GNUNET_ATS_Information atsi[4];
2771 uint32_t bps_pl_out;
2774 struct GNUNET_TIME_Relative delta;
2776 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2777 GNUNET_TIME_absolute_get ());
2781 if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2782 bps_pl_in = (1000LL * 1000LL * n->util_payload_bytes_recv) / (delta.rel_value_us);
2784 if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2785 bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2787 "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2792 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2793 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2795 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2796 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2800 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2804 atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2805 atsi[0].value = htonl (bps_out);
2806 atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2807 atsi[1].value = htonl (bps_in);
2809 atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2810 atsi[2].value = htonl (bps_pl_out);
2811 atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2812 atsi[3].value = htonl (bps_pl_in);
2814 GST_ats_update_metrics (key, n->primary_address.address,
2815 n->primary_address.session, atsi, 4);
2816 n->util_payload_bytes_recv = 0;
2817 n->util_payload_bytes_sent = 0;
2818 n->util_total_bytes_recv = 0;
2819 n->util_total_bytes_sent = 0;
2820 n->last_util_transmission = GNUNET_TIME_absolute_get();
2826 * Task transmitting utilization in a regular interval
2828 * @param cls the 'struct NeighbourMapEntry' for which we are running
2829 * @param tc scheduler context (unused)
2832 utilization_transmission (void *cls,
2833 const struct GNUNET_SCHEDULER_TaskContext *tc)
2835 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2837 if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2838 GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2840 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2841 utilization_transmission, NULL);
2847 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2848 const struct GNUNET_HELLO_Address *address,
2849 struct Session *session,
2850 const struct GNUNET_MessageHeader *message)
2852 struct NeighbourMapEntry *n;
2854 n = lookup_neighbour (peer);
2857 n->util_total_bytes_recv += ntohs(message->size);
2862 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2863 const struct GNUNET_HELLO_Address *address,
2864 struct Session *session,
2865 const struct GNUNET_MessageHeader *message)
2867 struct NeighbourMapEntry *n;
2868 n = lookup_neighbour (peer);
2871 n->util_payload_bytes_recv += ntohs(message->size);
2876 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2877 const struct GNUNET_HELLO_Address *address,
2878 struct Session *session,
2881 struct NeighbourMapEntry *n;
2882 n = lookup_neighbour (peer);
2885 if (n->primary_address.session != session)
2887 n->util_total_bytes_sent += size;
2892 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2895 struct NeighbourMapEntry *n;
2896 n = lookup_neighbour (peer);
2899 n->util_payload_bytes_sent += size;
2904 * Master task run for every neighbour. Performs all of the time-related
2905 * activities (keep alive, send next message, disconnect if idle, finish
2906 * clean up after disconnect).
2908 * @param cls the 'struct NeighbourMapEntry' for which we are running
2909 * @param tc scheduler context (unused)
2912 master_task (void *cls,
2913 const struct GNUNET_SCHEDULER_TaskContext *tc)
2915 struct NeighbourMapEntry *n = cls;
2916 struct GNUNET_TIME_Relative delay;
2918 n->task = GNUNET_SCHEDULER_NO_TASK;
2919 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2921 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2922 GNUNET_i2s (&n->id),
2923 GNUNET_TRANSPORT_ps2s(n->state),
2924 GNUNET_STRINGS_relative_time_to_string (delay,
2928 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2929 /* invalid state for master task, clean up */
2931 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2932 free_neighbour (n, GNUNET_NO);
2934 case GNUNET_TRANSPORT_PS_INIT_ATS:
2935 if (0 == delay.rel_value_us)
2937 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2938 "Connection to `%s' timed out waiting for ATS to provide address\n",
2939 GNUNET_i2s (&n->id));
2940 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2941 free_neighbour (n, GNUNET_NO);
2945 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2946 if (0 == delay.rel_value_us)
2948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2949 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2950 GNUNET_i2s (&n->id));
2951 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2952 free_neighbour (n, GNUNET_NO);
2956 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2957 if (0 == delay.rel_value_us)
2959 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2960 "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2961 GNUNET_i2s (&n->id));
2962 /* We could not send to this address, delete address and session */
2963 if (NULL != n->primary_address.session)
2964 GNUNET_ATS_address_destroyed (GST_ats,
2965 n->primary_address.address, n->primary_address.session);
2966 GNUNET_ATS_address_destroyed (GST_ats,
2967 n->primary_address.address, NULL);
2968 disconnect_neighbour (n);
2972 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2973 if (0 == delay.rel_value_us)
2975 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2976 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2977 GNUNET_i2s (&n->id));
2978 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2979 free_neighbour (n, GNUNET_NO);
2983 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2984 if (0 == delay.rel_value_us)
2986 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2987 "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2988 GNUNET_i2s (&n->id));
2989 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2990 free_neighbour (n, GNUNET_NO);
2994 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2995 if (0 == delay.rel_value_us)
2997 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2998 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2999 GNUNET_i2s (&n->id));
3000 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3001 free_neighbour (n, GNUNET_NO);
3005 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3006 if (0 == delay.rel_value_us)
3008 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3009 "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
3010 GNUNET_i2s (&n->id));
3011 disconnect_neighbour (n);
3015 case GNUNET_TRANSPORT_PS_CONNECTED:
3016 if (0 == delay.rel_value_us)
3018 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3019 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3020 GNUNET_i2s (&n->id));
3021 disconnect_neighbour (n);
3024 try_transmission_to_peer (n);
3027 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3028 if (0 == delay.rel_value_us)
3030 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3031 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3032 GNUNET_i2s (&n->id));
3033 disconnect_neighbour (n);
3037 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3038 if (0 == delay.rel_value_us)
3040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3041 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
3042 GNUNET_i2s (&n->id));
3043 disconnect_neighbour (n);
3047 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3048 if (0 == delay.rel_value_us)
3050 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3051 "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
3052 GNUNET_i2s (&n->id));
3053 disconnect_neighbour (n);
3057 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3058 if (0 == delay.rel_value_us)
3060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3061 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3062 GNUNET_i2s (&n->id));
3063 disconnect_neighbour (n);
3066 try_transmission_to_peer (n);
3069 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3070 if (0 == delay.rel_value_us)
3072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3073 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
3074 GNUNET_i2s (&n->id));
3075 disconnect_neighbour (n);
3078 try_transmission_to_peer (n);
3081 case GNUNET_TRANSPORT_PS_DISCONNECT:
3082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3083 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3084 GNUNET_i2s (&n->id));
3085 free_neighbour (n, GNUNET_NO);
3087 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3088 /* how did we get here!? */
3092 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3093 "Unhandled state `%s'\n",
3094 GNUNET_TRANSPORT_ps2s (n->state));
3098 if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
3099 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3100 (GNUNET_TRANSPORT_PS_CONNECTED == n->state) )
3102 /* if we are *now* in one of these three states, we're sending
3103 keep alive messages, so we need to consider the keepalive
3104 delay, not just the connection timeout */
3105 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
3108 if (GNUNET_SCHEDULER_NO_TASK == n->task)
3109 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3116 * Send a SESSION_ACK message to the neighbour to confirm that we
3117 * got his CONNECT_ACK.
3119 * @param n neighbour to send the SESSION_ACK to
3122 send_session_ack_message (struct NeighbourMapEntry *n)
3124 struct GNUNET_MessageHeader msg;
3126 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
3127 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3128 (void) send_with_session(n,
3129 (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
3130 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_NO,
3136 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
3137 * Consider switching to it.
3139 * @param message possibly a 'struct SessionConnectMessage' (check format)
3140 * @param peer identity of the peer to switch the address for
3141 * @param address address of the other peer, NULL if other peer
3143 * @param session session to use (or NULL)
3144 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3147 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
3148 const struct GNUNET_PeerIdentity *peer,
3149 const struct GNUNET_HELLO_Address *address,
3150 struct Session *session)
3152 const struct SessionConnectMessage *scm;
3153 struct GNUNET_TIME_Absolute ts;
3154 struct NeighbourMapEntry *n;
3156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3157 "Received CONNECT_ACK message from peer `%s'\n",
3160 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
3162 GNUNET_break_op (0);
3163 return GNUNET_SYSERR;
3165 GNUNET_STATISTICS_update (GST_stats,
3167 ("# CONNECT_ACK messages received"),
3169 scm = (const struct SessionConnectMessage *) message;
3170 GNUNET_break_op (ntohl (scm->reserved) == 0);
3171 if (NULL == (n = lookup_neighbour (peer)))
3173 GNUNET_STATISTICS_update (GST_stats,
3175 ("# unexpected CONNECT_ACK messages (no peer)"),
3177 return GNUNET_SYSERR;
3179 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3182 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3184 free_neighbour (n, GNUNET_NO);
3185 return GNUNET_SYSERR;
3186 case GNUNET_TRANSPORT_PS_INIT_ATS:
3187 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3188 GNUNET_STATISTICS_update (GST_stats,
3190 ("# unexpected CONNECT_ACK messages (not ready)"),
3193 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3194 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3196 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3197 "CONNECT_ACK ignored as the timestamp does not match our CONNECT request\n");
3200 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3201 GNUNET_STATISTICS_set (GST_stats,
3202 gettext_noop ("# peers connected"),
3203 ++neighbours_connected,
3205 connect_notify_cb (callback_cls, &n->id,
3206 n->primary_address.bandwidth_in,
3207 n->primary_address.bandwidth_out);
3208 /* Tell ATS that the outbound session we created to send CONNECT was successful */
3209 GST_ats_add_address (n->primary_address.address,
3210 n->primary_address.session,
3212 set_primary_address (n,
3213 n->primary_address.address,
3214 n->primary_address.session,
3215 n->primary_address.bandwidth_in,
3216 n->primary_address.bandwidth_out,
3218 send_session_ack_message (n);
3220 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3221 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3222 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3223 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3224 GNUNET_STATISTICS_update (GST_stats,
3226 ("# unexpected CONNECT_ACK messages (not ready)"),
3229 case GNUNET_TRANSPORT_PS_CONNECTED:
3230 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
3231 send_session_ack_message (n);
3233 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3234 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3235 /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
3236 to give us a new address... */
3237 GNUNET_STATISTICS_update (GST_stats,
3239 ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
3242 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3243 /* new address worked; go back to connected! */
3244 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
3245 send_session_ack_message (n);
3247 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3248 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
3249 send_session_ack_message (n);
3251 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3252 /* new address worked; adopt it and go back to connected! */
3253 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3254 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3256 GST_ats_add_address (n->alternative_address.address,
3257 n->alternative_address.session,
3259 set_primary_address (n, n->alternative_address.address,
3260 n->alternative_address.session, n->alternative_address.bandwidth_in,
3261 n->alternative_address.bandwidth_out, GNUNET_YES);
3263 free_address (&n->alternative_address);
3264 send_session_ack_message (n);
3266 case GNUNET_TRANSPORT_PS_DISCONNECT:
3267 GNUNET_STATISTICS_update (GST_stats,
3269 ("# unexpected CONNECT_ACK messages (disconnecting)"),
3271 return GNUNET_SYSERR;
3272 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3277 "Unhandled state `%s'\n",
3278 GNUNET_TRANSPORT_ps2s (n->state));
3280 return GNUNET_SYSERR;
3287 * A session was terminated. Take note; if needed, try to get
3288 * an alternative address from ATS.
3290 * @param peer identity of the peer where the session died
3291 * @param session session that is gone
3292 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3293 * this session was not in use
3296 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3297 struct Session *session)
3299 struct NeighbourMapEntry *n;
3300 struct BlackListCheckContext *bcc;
3301 struct BlackListCheckContext *bcc_next;
3303 /* make sure to cancel all ongoing blacklist checks involving 'session' */
3305 while (NULL != (bcc = bcc_next))
3307 bcc_next = bcc->next;
3308 if (bcc->na.session == session)
3310 if (NULL != bcc->bc)
3311 GST_blacklist_test_cancel (bcc->bc);
3312 GNUNET_HELLO_address_free (bcc->na.address);
3313 GNUNET_CONTAINER_DLL_remove (bc_head,
3319 if (NULL == (n = lookup_neighbour (peer)))
3320 return GNUNET_NO; /* can't affect us */
3321 if (session != n->primary_address.session)
3323 if (session == n->alternative_address.session)
3325 if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3326 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
3327 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
3330 free_address (&n->alternative_address);
3332 return GNUNET_NO; /* doesn't affect us further */
3335 n->expect_latency_response = GNUNET_NO;
3338 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3340 free_neighbour (n, GNUNET_NO);
3342 case GNUNET_TRANSPORT_PS_INIT_ATS:
3344 free_neighbour (n, GNUNET_NO);
3346 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3347 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3348 free_address (&n->primary_address);
3349 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3351 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3352 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3353 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3354 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3355 /* error on inbound session; free neighbour entirely */
3356 free_address (&n->primary_address);
3357 free_neighbour (n, GNUNET_NO);
3359 case GNUNET_TRANSPORT_PS_CONNECTED:
3360 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3361 free_address (&n->primary_address);
3363 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3364 /* we don't have an address, how can it go down? */
3367 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3368 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3369 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3371 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3372 /* primary went down while we were checking secondary against
3373 blacklist, adopt secondary as primary */
3374 free_address (&n->primary_address);
3375 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3376 n->primary_address = n->alternative_address;
3377 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3379 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3380 /* primary went down while we were waiting for CONNECT_ACK on secondary;
3381 secondary as primary */
3382 free_address (&n->primary_address);
3383 n->primary_address = n->alternative_address;
3384 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3385 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3387 case GNUNET_TRANSPORT_PS_DISCONNECT:
3388 free_address (&n->primary_address);
3390 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3391 /* neighbour was freed and plugins told to terminate session */
3395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3396 "Unhandled state `%s'\n",
3397 GNUNET_TRANSPORT_ps2s (n->state));
3401 if (GNUNET_SCHEDULER_NO_TASK != n->task)
3402 GNUNET_SCHEDULER_cancel (n->task);
3403 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3409 * We received a 'SESSION_ACK' message from the other peer.
3410 * If we sent a 'CONNECT_ACK' last, this means we are now
3411 * connected. Otherwise, do nothing.
3413 * @param message possibly a 'struct SessionConnectMessage' (check format)
3414 * @param peer identity of the peer to switch the address for
3415 * @param address address of the other peer, NULL if other peer
3417 * @param session session to use (or NULL)
3418 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3421 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3422 const struct GNUNET_PeerIdentity *peer,
3423 const struct GNUNET_HELLO_Address *address,
3424 struct Session *session)
3426 struct NeighbourMapEntry *n;
3428 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3429 "Received SESSION_ACK message from peer `%s'\n",
3431 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3433 GNUNET_break_op (0);
3434 return GNUNET_SYSERR;
3436 GNUNET_STATISTICS_update (GST_stats,
3438 ("# SESSION_ACK messages received"),
3440 if (NULL == (n = lookup_neighbour (peer)))
3442 GNUNET_break_op (0);
3443 return GNUNET_SYSERR;
3445 /* check if we are in a plausible state for having sent
3446 a CONNECT_ACK. If not, return, otherwise break */
3447 if ( ( (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state) &&
3448 (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state) ) ||
3449 (2 != n->send_connect_ack) )
3451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3452 "Received SESSION_ACK message from peer `%s' in state %s/%d\n",
3454 GNUNET_TRANSPORT_ps2s (n->state),
3455 n->send_connect_ack);
3456 GNUNET_STATISTICS_update (GST_stats,
3457 gettext_noop ("# unexpected SESSION_ACK messages"), 1,
3461 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3462 GNUNET_STATISTICS_set (GST_stats,
3463 gettext_noop ("# peers connected"),
3464 ++neighbours_connected,
3466 connect_notify_cb (callback_cls, &n->id,
3467 n->primary_address.bandwidth_in,
3468 n->primary_address.bandwidth_out);
3470 GST_ats_add_address (n->primary_address.address,
3471 n->primary_address.session,
3473 set_primary_address (n,
3474 n->primary_address.address,
3475 n->primary_address.session,
3476 n->primary_address.bandwidth_in,
3477 n->primary_address.bandwidth_out,
3484 * Test if we're connected to the given peer.
3486 * @param target peer to test
3487 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3490 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3492 return test_connected (lookup_neighbour (target));
3496 * Change the incoming quota for the given peer.
3498 * @param neighbour identity of peer to change qutoa for
3499 * @param quota new quota
3502 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3503 struct GNUNET_BANDWIDTH_Value32NBO quota)
3505 struct NeighbourMapEntry *n;
3507 if (NULL == (n = lookup_neighbour (neighbour)))
3509 GNUNET_STATISTICS_update (GST_stats,
3511 ("# SET QUOTA messages ignored (no such peer)"),
3515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3516 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3517 ntohl (quota.value__), GNUNET_i2s (&n->id));
3518 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3519 if (0 != ntohl (quota.value__))
3521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3522 "Disconnecting peer `%4s' due to SET_QUOTA\n",
3523 GNUNET_i2s (&n->id));
3524 if (GNUNET_YES == test_connected (n))
3525 GNUNET_STATISTICS_update (GST_stats,
3526 gettext_noop ("# disconnects due to quota of 0"),
3528 disconnect_neighbour (n);
3533 * We received a disconnect message from the given peer,
3534 * validate and process.
3536 * @param peer sender of the message
3537 * @param msg the disconnect message
3540 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
3541 const struct GNUNET_MessageHeader *msg)
3543 struct NeighbourMapEntry *n;
3544 const struct SessionDisconnectMessage *sdm;
3546 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3547 "Received DISCONNECT message from peer `%s'\n",
3549 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3551 // GNUNET_break_op (0);
3552 GNUNET_STATISTICS_update (GST_stats,
3554 ("# disconnect messages ignored (old format)"), 1,
3558 GNUNET_STATISTICS_update (GST_stats,
3560 ("# DISCONNECT messages received"),
3562 sdm = (const struct SessionDisconnectMessage *) msg;
3563 if (NULL == (n = lookup_neighbour (peer)))
3564 return; /* gone already */
3565 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3567 GNUNET_STATISTICS_update (GST_stats,
3569 ("# disconnect messages ignored (timestamp)"), 1,
3573 if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3575 GNUNET_break_op (0);
3578 if (ntohl (sdm->purpose.size) !=
3579 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3580 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3581 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3583 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3584 "%s message from peer `%s' has invalid size \n",
3587 GNUNET_break_op (0);
3591 GNUNET_CRYPTO_eddsa_verify
3592 (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3593 &sdm->signature, &sdm->public_key))
3595 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3596 "%s message from peer `%s' cannot be verified \n",
3599 GNUNET_break_op (0);
3602 if (GNUNET_YES == test_connected (n))
3603 GNUNET_STATISTICS_update (GST_stats,
3605 ("# other peer asked to disconnect from us"), 1,
3607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3608 "Disconnecting by request from peer %s\n",
3610 disconnect_neighbour (n);
3615 * Closure for the neighbours_iterate function.
3617 struct IteratorContext
3620 * Function to call on each connected neighbour.
3622 GST_NeighbourIterator cb;
3632 * Call the callback from the closure for each neighbour.
3634 * @param cls the `struct IteratorContext`
3635 * @param key the hash of the public key of the neighbour
3636 * @param value the `struct NeighbourMapEntry`
3637 * @return #GNUNET_OK (continue to iterate)
3640 neighbours_iterate (void *cls,
3641 const struct GNUNET_PeerIdentity *key,
3644 struct IteratorContext *ic = cls;
3645 struct NeighbourMapEntry *n = value;
3646 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3647 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3650 if (NULL != n->primary_address.address)
3652 bandwidth_in = n->primary_address.bandwidth_in;
3653 bandwidth_out = n->primary_address.bandwidth_out;
3657 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3658 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3662 n->primary_address.address,
3665 bandwidth_in, bandwidth_out);
3671 * Iterate over all connected neighbours.
3673 * @param cb function to call
3674 * @param cb_cls closure for cb
3677 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3679 struct IteratorContext ic;
3681 if (NULL == neighbours)
3682 return; /* can happen during shutdown */
3685 GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3690 * If we have an active connection to the given target, it must be shutdown.
3692 * @param target peer to disconnect from
3695 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3697 struct NeighbourMapEntry *n;
3699 if (NULL == (n = lookup_neighbour (target)))
3700 return; /* not active */
3701 if (GNUNET_YES == test_connected (n))
3702 GNUNET_STATISTICS_update (GST_stats,
3704 ("# disconnected from peer upon explicit request"), 1,
3706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3707 "Forced disconnect from peer %s\n",
3708 GNUNET_i2s (target));
3709 disconnect_neighbour (n);
3714 * Obtain current latency information for the given neighbour.
3716 * @param peer to get the latency for
3717 * @return observed latency of the address, FOREVER if the
3718 * the connection is not up
3720 struct GNUNET_TIME_Relative
3721 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3723 struct NeighbourMapEntry *n;
3725 n = lookup_neighbour (peer);
3727 return GNUNET_TIME_UNIT_FOREVER_REL;
3730 case GNUNET_TRANSPORT_PS_CONNECTED:
3731 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3732 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3733 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3734 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3735 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3737 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3738 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3739 case GNUNET_TRANSPORT_PS_INIT_ATS:
3740 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3741 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3742 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3743 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3744 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3745 case GNUNET_TRANSPORT_PS_DISCONNECT:
3746 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3747 return GNUNET_TIME_UNIT_FOREVER_REL;
3749 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3750 "Unhandled state `%s'\n",
3751 GNUNET_TRANSPORT_ps2s (n->state));
3755 return GNUNET_TIME_UNIT_FOREVER_REL;
3760 * Obtain current address information for the given neighbour.
3763 * @return address currently used
3765 struct GNUNET_HELLO_Address *
3766 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3768 struct NeighbourMapEntry *n;
3770 n = lookup_neighbour (peer);
3773 return n->primary_address.address;
3778 * Initialize the neighbours subsystem.
3780 * @param cls closure for callbacks
3781 * @param connect_cb function to call if we connect to a peer
3782 * @param disconnect_cb function to call if we disconnect from a peer
3783 * @param peer_address_cb function to call if we change an active address
3785 * @param max_fds maximum number of fds to use
3788 GST_neighbours_start (void *cls,
3789 NotifyConnect connect_cb,
3790 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3791 GNUNET_TRANSPORT_NeighbourChangeCallback peer_address_cb,
3792 unsigned int max_fds)
3795 connect_notify_cb = connect_cb;
3796 disconnect_notify_cb = disconnect_cb;
3797 neighbour_change_cb = peer_address_cb;
3798 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3799 registered_quota_notifications = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3800 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3801 utilization_transmission, NULL);
3806 * Disconnect from the given neighbour.
3809 * @param key hash of neighbour's public key (not used)
3810 * @param value the 'struct NeighbourMapEntry' of the neighbour
3811 * @return #GNUNET_OK (continue to iterate)
3814 disconnect_all_neighbours (void *cls,
3815 const struct GNUNET_PeerIdentity *key,
3818 struct NeighbourMapEntry *n = value;
3820 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3821 "Disconnecting peer `%4s', %s\n",
3822 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3823 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3824 free_neighbour (n, GNUNET_NO);
3830 * Cleanup the neighbours subsystem.
3833 GST_neighbours_stop ()
3835 struct BlacklistCheckSwitchContext *cur;
3836 struct BlacklistCheckSwitchContext *next;
3838 if (NULL == neighbours)
3840 if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3842 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3843 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3846 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3847 &disconnect_all_neighbours,
3849 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3851 next = pending_bc_head;
3852 for (cur = next; NULL != cur; cur = next )
3855 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, cur);
3857 if (NULL != cur->blc)
3859 GST_blacklist_test_cancel (cur->blc);
3862 if (NULL != cur->address)
3863 GNUNET_HELLO_address_free (cur->address);
3864 GNUNET_free_non_null (cur->ats);
3868 GNUNET_CONTAINER_multipeermap_iterate (registered_quota_notifications,
3869 &free_notification_cb, NULL);
3870 GNUNET_CONTAINER_multipeermap_destroy (registered_quota_notifications);
3873 callback_cls = NULL;
3874 connect_notify_cb = NULL;
3875 disconnect_notify_cb = NULL;
3876 neighbour_change_cb = NULL;
3880 /* end of file gnunet-service-transport_neighbours.c */