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);
805 * Clear the primary address of a neighbour since this primary address is not
806 * valid anymore and notify monitoring about it
808 * @param n the neighbour
811 unset_primary_address (struct NeighbourMapEntry *n)
820 * Free a neighbour map entry.
822 * @param n entry to free
823 * @param keep_sessions #GNUNET_NO to tell plugin to terminate sessions,
824 * #GNUNET_YES to keep all sessions
827 free_neighbour (struct NeighbourMapEntry *n,
830 struct MessageQueue *mq;
831 struct GNUNET_TRANSPORT_PluginFunctions *papi;
832 struct GNUNET_HELLO_Address *backup_primary;
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835 "Freeing neighbur state of peer `%s'\n",
836 GNUNET_i2s (&n->id));
837 n->is_active = NULL; /* always free'd by its own continuation! */
839 /* fail messages currently in the queue */
840 while (NULL != (mq = n->messages_head))
842 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
843 if (NULL != mq->cont)
844 mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
847 /* It is too late to send other peer disconnect notifications, but at
848 least internally we need to get clean... */
849 if (GNUNET_YES == test_connected (n))
851 GNUNET_STATISTICS_set (GST_stats,
852 gettext_noop ("# peers connected"),
853 --neighbours_connected,
855 disconnect_notify_cb (callback_cls, &n->id);
857 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
859 if (NULL != n->primary_address.address)
861 backup_primary = GNUNET_HELLO_address_copy (n->primary_address.address);
864 backup_primary = NULL;
866 /* free addresses and mark as unused */
867 free_address (&n->primary_address);
868 free_address (&n->alternative_address);
870 /* FIXME: Note that if we are switching between two TCP sessions to
871 the same peer, we might want to selectively kill only one of
872 them! Killing all sessions like this seems to be very, very
875 /* cut transport-level connection */
876 if ((GNUNET_NO == keep_sessions) &&
877 (NULL != backup_primary) &&
878 (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
879 papi->disconnect_peer (papi->cls, &n->id);
881 GNUNET_free_non_null (backup_primary);
883 GNUNET_assert (GNUNET_YES ==
884 GNUNET_CONTAINER_multipeermap_remove (neighbours,
887 // FIXME-ATS-API: we might want to be more specific about
888 // which states we do this from in the future (ATS should
889 // have given us a 'suggest_address' handle, and if we have
890 // such a handle, we should cancel the operation here!
891 if (NULL != n->suggest_handle)
893 GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
894 n->suggest_handle = NULL;
897 if (GNUNET_SCHEDULER_NO_TASK != n->task)
899 GNUNET_SCHEDULER_cancel (n->task);
900 n->task = GNUNET_SCHEDULER_NO_TASK;
902 /* free rest of memory */
908 * Transmit a message using the current session of the given
911 * @param n entry for the recipient
912 * @param msgbuf buffer to transmit
913 * @param msgbuf_size number of bytes in @a msgbuf buffer
914 * @param priority transmission priority
915 * @param timeout transmission timeout
916 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
917 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
918 * @param cont continuation to call when finished (can be NULL)
919 * @param cont_cls closure for @a cont
920 * @return timeout (copy of @a timeout or a calculated one if
921 * @a use_keepalive_timeout is #GNUNET_YES.
923 static struct GNUNET_TIME_Relative
924 send_with_session (struct NeighbourMapEntry *n,
925 const char *msgbuf, size_t msgbuf_size,
927 struct GNUNET_TIME_Relative timeout,
928 unsigned int use_keepalive_timeout,
929 GNUNET_TRANSPORT_TransmitContinuation cont,
932 struct GNUNET_TRANSPORT_PluginFunctions *papi;
933 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
935 GNUNET_assert (n->primary_address.session != NULL);
936 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
937 (-1 == papi->send (papi->cls,
938 n->primary_address.session,
941 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
942 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
943 papi->query_keepalive_factor (papi->cls))),
944 cont, cont_cls)))) &&
946 cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
947 GST_neighbours_notify_data_sent (&n->id,
948 n->primary_address.address, n->primary_address.session, msgbuf_size);
949 GNUNET_break (NULL != papi);
955 * Master task run for every neighbour. Performs all of the time-related
956 * activities (keep alive, send next message, disconnect if idle, finish
957 * clean up after disconnect).
959 * @param cls the `struct NeighbourMapEntry` for which we are running
960 * @param tc scheduler context (unused)
963 master_task (void *cls,
964 const struct GNUNET_SCHEDULER_TaskContext *tc);
968 * Function called when the 'DISCONNECT' message has been sent by the
969 * plugin. Frees the neighbour --- if the entry still exists.
972 * @param target identity of the neighbour that was disconnected
973 * @param result #GNUNET_OK if the disconnect got out successfully
974 * @param payload bytes payload
975 * @param physical bytes physical
978 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
979 int result, size_t payload, size_t physical)
981 struct NeighbourMapEntry *n;
983 n = lookup_neighbour (target);
985 return; /* already gone */
986 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
987 return; /* have created a fresh entry since */
988 if (GNUNET_SCHEDULER_NO_TASK != n->task)
989 GNUNET_SCHEDULER_cancel (n->task);
990 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
995 * Transmit a DISCONNECT message to the other peer.
997 * @param n neighbour to send DISCONNECT message.
1000 send_disconnect (struct NeighbourMapEntry *n)
1002 struct SessionDisconnectMessage disconnect_msg;
1004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005 "Sending DISCONNECT message to peer `%4s'\n",
1006 GNUNET_i2s (&n->id));
1007 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1008 disconnect_msg.header.type =
1009 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1010 disconnect_msg.reserved = htonl (0);
1011 disconnect_msg.purpose.size =
1012 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1013 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1014 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1015 disconnect_msg.purpose.purpose =
1016 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1017 disconnect_msg.timestamp =
1018 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1019 disconnect_msg.public_key = GST_my_identity.public_key;
1020 GNUNET_assert (GNUNET_OK ==
1021 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1022 &disconnect_msg.purpose,
1023 &disconnect_msg.signature));
1025 (void) send_with_session (n,
1026 (const char *) &disconnect_msg, sizeof (disconnect_msg),
1027 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1028 GNUNET_NO, &send_disconnect_cont, NULL);
1029 GNUNET_STATISTICS_update (GST_stats,
1031 ("# DISCONNECT messages sent"), 1,
1037 * Disconnect from the given neighbour, clean up the record.
1039 * @param n neighbour to disconnect from
1042 disconnect_neighbour (struct NeighbourMapEntry *n)
1044 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045 "Disconnecting from peer %s in state %s\n",
1046 GNUNET_i2s (&n->id),
1047 GNUNET_TRANSPORT_ps2s (n->state));
1048 /* depending on state, notify neighbour and/or upper layers of this peer
1052 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1053 case GNUNET_TRANSPORT_PS_INIT_ATS:
1054 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
1055 /* other peer is completely unaware of us, no need to send DISCONNECT */
1056 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
1057 free_neighbour (n, GNUNET_NO);
1059 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
1060 send_disconnect (n);
1061 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1063 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
1064 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
1065 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
1066 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1067 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
1068 free_neighbour (n, GNUNET_NO);
1070 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
1071 /* we DID ACK the other peer's request, must send DISCONNECT */
1072 send_disconnect (n);
1073 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1075 case GNUNET_TRANSPORT_PS_CONNECTED:
1076 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
1077 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1078 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
1079 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
1080 /* we are currently connected, need to send disconnect and do
1081 internal notifications and update statistics */
1082 send_disconnect (n);
1083 GNUNET_STATISTICS_set (GST_stats,
1084 gettext_noop ("# peers connected"),
1085 --neighbours_connected,
1087 disconnect_notify_cb (callback_cls, &n->id);
1088 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1090 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1091 /* ATS address request timeout, disconnect without sending disconnect message */
1092 GNUNET_STATISTICS_set (GST_stats,
1093 gettext_noop ("# peers connected"),
1094 --neighbours_connected,
1096 disconnect_notify_cb (callback_cls, &n->id);
1097 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
1099 case GNUNET_TRANSPORT_PS_DISCONNECT:
1100 /* already disconnected, ignore */
1102 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1103 /* already cleaned up, how did we get here!? */
1107 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1108 "Unhandled state `%s'\n",
1109 GNUNET_TRANSPORT_ps2s (n->state));
1113 /* schedule timeout to clean up */
1114 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1115 GNUNET_SCHEDULER_cancel (n->task);
1116 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1122 * We're done with our transmission attempt, continue processing.
1124 * @param cls the `struct MessageQueue` of the message
1125 * @param receiver intended receiver
1126 * @param success whether it worked or not
1127 * @param size_payload bytes payload sent
1128 * @param physical bytes sent on wire
1131 transmit_send_continuation (void *cls,
1132 const struct GNUNET_PeerIdentity *receiver,
1133 int success, size_t size_payload, size_t physical)
1135 struct MessageQueue *mq = cls;
1136 struct NeighbourMapEntry *n;
1138 if (NULL == (n = lookup_neighbour (receiver)))
1141 return; /* disconnect or other error while transmitting, can happen */
1143 if (n->is_active == mq)
1145 /* this is still "our" neighbour, remove us from its queue
1146 and allow it to send the next message now */
1147 n->is_active = NULL;
1148 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1149 GNUNET_SCHEDULER_cancel (n->task);
1150 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1152 if (bytes_in_send_queue < mq->message_buf_size)
1154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1156 bytes_in_send_queue, mq->message_buf_size,
1157 (GNUNET_OK == success) ? "OK" : "FAIL",
1158 size_payload, physical);
1163 GNUNET_break (size_payload == mq->message_buf_size);
1164 bytes_in_send_queue -= mq->message_buf_size;
1165 GNUNET_STATISTICS_set (GST_stats,
1167 ("# bytes in message queue for other peers"),
1168 bytes_in_send_queue, GNUNET_NO);
1169 if (GNUNET_OK == success)
1170 GNUNET_STATISTICS_update (GST_stats,
1172 ("# messages transmitted to other peers"),
1175 GNUNET_STATISTICS_update (GST_stats,
1177 ("# transmission failures for messages to other peers"),
1179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1180 "Sending message to `%s' of type %u was a %s\n",
1181 GNUNET_i2s (receiver),
1182 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1183 (success == GNUNET_OK) ? "success" : "FAILURE");
1184 if (NULL != mq->cont)
1185 mq->cont (mq->cont_cls, success, size_payload, physical);
1191 * Check the message list for the given neighbour and if we can
1192 * send a message, do so. This function should only be called
1193 * if the connection is at least generally ready for transmission.
1194 * While we will only send one message at a time, no bandwidth
1195 * quota management is performed here. If a message was given to
1196 * the plugin, the continuation will automatically re-schedule
1197 * the 'master' task once the next message might be transmitted.
1199 * @param n target peer for which to transmit
1202 try_transmission_to_peer (struct NeighbourMapEntry *n)
1204 struct MessageQueue *mq;
1205 struct GNUNET_TIME_Relative timeout;
1207 if (NULL == n->primary_address.address)
1209 /* no address, why are we here? */
1213 if ((0 == n->primary_address.address->address_length) &&
1214 (NULL == n->primary_address.session))
1216 /* no address, why are we here? */
1220 if (NULL != n->is_active)
1222 /* transmission already pending */
1226 /* timeout messages from the queue that are past their due date */
1227 while (NULL != (mq = n->messages_head))
1229 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1230 if (timeout.rel_value_us > 0)
1232 GNUNET_STATISTICS_update (GST_stats,
1234 ("# messages timed out while in transport queue"),
1236 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1238 transmit_send_continuation (mq, &n->id,
1240 mq->message_buf_size, 0); /* timeout */
1243 return; /* no more messages */
1244 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1246 (void) send_with_session (n,
1247 mq->message_buf, mq->message_buf_size,
1248 0 /* priority */, timeout, GNUNET_NO,
1249 &transmit_send_continuation, mq);
1254 * Send keepalive message to the neighbour. Must only be called
1255 * if we are on 'connected' state or while trying to switch addresses.
1256 * Will internally determine if a keepalive is truly needed (so can
1257 * always be called).
1259 * @param n neighbour that went idle and needs a keepalive
1262 send_keepalive (struct NeighbourMapEntry *n)
1264 struct SessionKeepAliveMessage m;
1265 struct GNUNET_TIME_Relative timeout;
1268 GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1269 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1270 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT));
1271 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1272 return; /* no keepalive needed at this time */
1274 nonce = 0; /* 0 indicates 'not set' */
1276 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1279 "Sending keep alive to peer `%s' with nonce %u\n",
1280 GNUNET_i2s (&n->id), nonce);
1282 m.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1283 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1284 m.nonce = htonl (nonce);
1286 timeout = send_with_session (n,
1287 (const void *) &m, sizeof (m),
1288 UINT32_MAX /* priority */,
1289 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1291 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1293 n->primary_address.keep_alive_nonce = nonce;
1294 n->expect_latency_response = GNUNET_YES;
1295 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1296 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1302 * Keep the connection to the given neighbour alive longer,
1303 * we received a KEEPALIVE (or equivalent); send a response.
1305 * @param neighbour neighbour to keep alive (by sending keep alive response)
1306 * @param m the keep alive message containing the nonce to respond to
1309 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1310 const struct GNUNET_MessageHeader *m)
1312 struct NeighbourMapEntry *n;
1313 const struct SessionKeepAliveMessage *msg_in;
1314 struct SessionKeepAliveMessage msg;
1316 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1319 msg_in = (struct SessionKeepAliveMessage *) m;
1320 if (NULL == (n = lookup_neighbour (neighbour)))
1322 GNUNET_STATISTICS_update (GST_stats,
1324 ("# KEEPALIVE messages discarded (peer unknown)"),
1328 if (NULL == n->primary_address.session)
1330 GNUNET_STATISTICS_update (GST_stats,
1332 ("# KEEPALIVE messages discarded (no session)"),
1337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1338 "Received keep alive request from peer `%s' with nonce %u\n",
1339 GNUNET_i2s (&n->id), ntohl (msg_in->nonce));
1341 /* send reply to allow neighbour to measure latency */
1342 msg.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1343 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1344 msg.nonce = msg_in->nonce;
1345 (void) send_with_session(n,
1346 (const void *) &msg, sizeof (struct SessionKeepAliveMessage),
1347 UINT32_MAX /* priority */,
1348 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1354 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1355 * latency to this peer. Pass the updated information (existing ats
1356 * plus calculated latency) to ATS.
1358 * @param neighbour neighbour to keep alive
1359 * @param m the message containing the keep alive response
1362 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1363 const struct GNUNET_MessageHeader *m)
1365 struct NeighbourMapEntry *n;
1366 const struct SessionKeepAliveMessage *msg;
1367 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1369 struct GNUNET_ATS_Information ats;
1371 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1374 msg = (const struct SessionKeepAliveMessage *) m;
1375 if (NULL == (n = lookup_neighbour (neighbour)))
1377 GNUNET_STATISTICS_update (GST_stats,
1379 ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1383 if ( (GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1384 (GNUNET_YES != n->expect_latency_response) )
1386 GNUNET_STATISTICS_update (GST_stats,
1388 ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1392 if (NULL == n->primary_address.address)
1394 GNUNET_STATISTICS_update (GST_stats,
1396 ("# KEEPALIVE_RESPONSE messages discarded (address changed)"),
1400 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1402 GNUNET_STATISTICS_update (GST_stats,
1404 ("# KEEPALIVE_RESPONSE messages discarded (wrong nonce)"),
1410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1411 "Received keep alive response from peer `%s' for session %p\n",
1412 GNUNET_i2s (&n->id), n->primary_address.session);
1416 /* Update session timeout here */
1417 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420 "Updating session for peer `%s' for session %p\n",
1421 GNUNET_i2s (&n->id), n->primary_address.session);
1422 papi->update_session_timeout (papi->cls, &n->id, n->primary_address.session);
1429 n->primary_address.keep_alive_nonce = 0;
1430 n->expect_latency_response = GNUNET_NO;
1431 n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1432 set_timeout (n, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435 "Latency for peer `%s' is %s\n",
1436 GNUNET_i2s (&n->id),
1437 GNUNET_STRINGS_relative_time_to_string (n->latency,
1439 /* append latency */
1440 ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1441 if (n->latency.rel_value_us > UINT32_MAX)
1442 latency = UINT32_MAX;
1444 latency = n->latency.rel_value_us;
1445 ats.value = htonl (latency);
1446 GST_ats_update_metrics (&n->id, n->primary_address.address,
1447 n->primary_address.session, &ats, 1);
1452 * We have received a message from the given sender. How long should
1453 * we delay before receiving more? (Also used to keep the peer marked
1456 * @param sender sender of the message
1457 * @param size size of the message
1458 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1459 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1460 * #GNUNET_SYSERR if the connection is not fully up yet
1461 * @return how long to wait before reading more from this sender
1463 struct GNUNET_TIME_Relative
1464 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1465 *sender, ssize_t size, int *do_forward)
1467 struct NeighbourMapEntry *n;
1468 struct GNUNET_TIME_Relative ret;
1470 if (NULL == neighbours)
1472 *do_forward = GNUNET_NO;
1473 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1475 if (NULL == (n = lookup_neighbour (sender)))
1477 GST_neighbours_try_connect (sender);
1478 if (NULL == (n = lookup_neighbour (sender)))
1480 GNUNET_STATISTICS_update (GST_stats,
1482 ("# messages discarded due to lack of neighbour record"),
1484 *do_forward = GNUNET_NO;
1485 return GNUNET_TIME_UNIT_ZERO;
1488 if (! test_connected (n))
1490 *do_forward = GNUNET_SYSERR;
1491 return GNUNET_TIME_UNIT_ZERO;
1493 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1495 n->quota_violation_count++;
1496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1498 n->in_tracker.available_bytes_per_s__,
1499 n->quota_violation_count);
1500 /* Discount 32k per violation */
1501 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1505 if (n->quota_violation_count > 0)
1507 /* try to add 32k back */
1508 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1509 n->quota_violation_count--;
1512 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1514 GNUNET_STATISTICS_update (GST_stats,
1516 ("# bandwidth quota violations by other peers"),
1518 *do_forward = GNUNET_NO;
1519 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1521 *do_forward = GNUNET_YES;
1522 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1523 if (ret.rel_value_us > 0)
1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1526 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1527 (unsigned long long) n->in_tracker.
1528 consumption_since_last_update__,
1529 (unsigned int) n->in_tracker.available_bytes_per_s__,
1530 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1531 GNUNET_STATISTICS_update (GST_stats,
1532 gettext_noop ("# ms throttling suggested"),
1533 (int64_t) ret.rel_value_us / 1000LL,
1541 * Transmit a message to the given target using the active connection.
1543 * @param target destination
1544 * @param msg message to send
1545 * @param msg_size number of bytes in msg
1546 * @param timeout when to fail with timeout
1547 * @param cont function to call when done
1548 * @param cont_cls closure for 'cont'
1551 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1552 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1553 GST_NeighbourSendContinuation cont, void *cont_cls)
1555 struct NeighbourMapEntry *n;
1556 struct MessageQueue *mq;
1558 /* All ove these cases should never happen; they are all API violations.
1559 But we check anyway, just to be sure. */
1560 if (NULL == (n = lookup_neighbour (target)))
1564 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1567 if (GNUNET_YES != test_connected (n))
1571 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1574 bytes_in_send_queue += msg_size;
1575 GNUNET_STATISTICS_set (GST_stats,
1577 ("# bytes in message queue for other peers"),
1578 bytes_in_send_queue, GNUNET_NO);
1579 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1581 mq->cont_cls = cont_cls;
1582 memcpy (&mq[1], msg, msg_size);
1583 mq->message_buf = (const char *) &mq[1];
1584 mq->message_buf_size = msg_size;
1585 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1586 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1587 if ( (NULL != n->is_active) ||
1588 ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1590 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1591 GNUNET_SCHEDULER_cancel (n->task);
1592 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1597 * Send a SESSION_CONNECT message via the given address.
1599 * @param na address to use
1602 send_session_connect (struct NeighbourAddress *na)
1604 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1605 struct SessionConnectMessage connect_msg;
1607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1608 "Sending SESSION_CONNECT message to peer %s\n",
1609 GNUNET_i2s (&na->address->peer));
1610 if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1615 if (NULL == na->session)
1616 na->session = papi->get_session (papi->cls, na->address);
1617 if (NULL == na->session)
1622 GNUNET_STATISTICS_update (GST_stats,
1624 ("# SESSION_CONNECT messages sent"),
1626 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1627 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1628 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1629 connect_msg.reserved = htonl (0);
1630 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1632 papi->send (papi->cls,
1634 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1636 GNUNET_TIME_UNIT_FOREVER_REL,
1639 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1640 _("Failed to transmit CONNECT message via plugin to %s\n"),
1641 GST_plugins_a2s (na->address));
1643 GST_neighbours_notify_data_sent (&na->address->peer,
1646 sizeof (struct SessionConnectMessage));
1652 * Send a SESSION_CONNECT_ACK message via the given address.
1654 * @param address address to use
1655 * @param session session to use
1656 * @param timestamp timestamp to use for the ACK message
1659 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1660 struct Session *session,
1661 struct GNUNET_TIME_Absolute timestamp)
1663 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1664 struct SessionConnectMessage connect_msg;
1666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1667 "Sending CONNECT_ACK to peer `%s'\n",
1668 GNUNET_i2s (&address->peer));
1669 if (NULL == (papi = GST_plugins_find (address->transport_name)))
1674 if (NULL == session)
1675 session = papi->get_session (papi->cls, address);
1676 if (NULL == session)
1681 GNUNET_STATISTICS_update (GST_stats,
1683 ("# CONNECT_ACK messages sent"),
1685 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1686 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1687 connect_msg.reserved = htonl (0);
1688 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1689 (void) papi->send (papi->cls,
1691 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1693 GNUNET_TIME_UNIT_FOREVER_REL,
1698 struct QuotaNotificationRequest
1700 struct GNUNET_PeerIdentity peer;
1701 struct Session *session;
1705 struct QNR_LookContext
1707 struct GNUNET_PeerIdentity peer;
1708 struct Session *session;
1711 struct QuotaNotificationRequest *res;
1715 find_notification_request (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1717 struct QNR_LookContext *qnr_ctx = cls;
1718 struct QuotaNotificationRequest *qnr = value;
1720 if ((qnr->session == qnr_ctx->session) &&
1721 (0 == memcmp (&qnr->peer, &qnr_ctx->peer, sizeof (struct GNUNET_PeerIdentity))) &&
1722 (0 == strcmp(qnr_ctx->plugin, qnr->plugin)))
1724 qnr_ctx->res = value;
1731 GST_neighbours_register_quota_notification(void *cls,
1732 const struct GNUNET_PeerIdentity *peer, const char *plugin,
1733 struct Session *session)
1735 struct QuotaNotificationRequest *qnr;
1736 struct QNR_LookContext qnr_ctx;
1738 if (NULL == registered_quota_notifications)
1740 return; /* init or shutdown */
1743 qnr_ctx.peer = (*peer);
1744 qnr_ctx.plugin = plugin;
1745 qnr_ctx.session = session;
1748 GNUNET_CONTAINER_multipeermap_get_multiple (registered_quota_notifications,
1749 peer, &find_notification_request, &qnr_ctx);
1750 if (NULL != qnr_ctx.res)
1756 qnr = GNUNET_new (struct QuotaNotificationRequest);
1757 qnr->peer = (*peer);
1758 qnr->plugin = GNUNET_strdup (plugin);
1759 qnr->session = session;
1761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1762 "Adding notification for peer `%s' plugin `%s' session %p \n",
1763 GNUNET_i2s (peer), plugin, session);
1765 GNUNET_CONTAINER_multipeermap_put (registered_quota_notifications, peer,
1766 qnr, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1771 GST_neighbours_unregister_quota_notification(void *cls,
1772 const struct GNUNET_PeerIdentity *peer, const char *plugin, struct Session *session)
1774 struct QNR_LookContext qnr_ctx;
1776 if (NULL == registered_quota_notifications)
1778 return; /* init or shutdown */
1781 qnr_ctx.peer = (*peer);
1782 qnr_ctx.plugin = plugin;
1783 qnr_ctx.session = session;
1786 GNUNET_CONTAINER_multipeermap_iterate (registered_quota_notifications,
1787 &find_notification_request, &qnr_ctx);
1788 if (NULL == qnr_ctx.res)
1794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1795 "Removing notification for peer `%s' plugin `%s' session %p \n",
1796 GNUNET_i2s (peer), plugin, session);
1798 GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, peer,
1800 GNUNET_free (qnr_ctx.res->plugin);
1801 GNUNET_free (qnr_ctx.res);
1805 notification_cb(void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1807 /* struct NeighbourMapEntry *n = cls; */
1808 struct QuotaNotificationRequest *qnr = value;
1809 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1810 struct GNUNET_TIME_Relative delay;
1813 papi = GST_plugins_find(qnr->plugin);
1820 delay = GST_neighbours_calculate_receive_delay (key, 0, &do_forward);
1821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1822 "New inbound delay for peer `%s' is %llu ms\n", GNUNET_i2s (key),
1823 delay.rel_value_us / 1000);
1825 if (NULL != papi->update_inbound_delay)
1826 papi->update_inbound_delay (papi->cls, key, qnr->session, delay);
1832 free_notification_cb(void *cls, const struct GNUNET_PeerIdentity *key,
1835 /* struct NeighbourMapEntry *n = cls; */
1836 struct QuotaNotificationRequest *qnr = value;
1838 GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, key,
1840 GNUNET_free(qnr->plugin);
1847 inbound_bw_tracker_update(void *cls)
1849 struct NeighbourMapEntry *n = cls;
1851 /* Quota was updated, tell plugins to update the time to receive next */
1852 GNUNET_CONTAINER_multipeermap_get_multiple (registered_quota_notifications,
1853 &n->id, ¬ification_cb, n);
1858 * Create a fresh entry in the neighbour map for the given peer
1860 * @param peer peer to create an entry for
1861 * @return new neighbour map entry
1863 static struct NeighbourMapEntry *
1864 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1866 struct NeighbourMapEntry *n;
1868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1869 "Creating new neighbour entry for `%s'\n",
1871 n = GNUNET_new (struct NeighbourMapEntry);
1873 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1874 n->last_util_transmission = GNUNET_TIME_absolute_get();
1875 n->util_payload_bytes_recv = 0;
1876 n->util_payload_bytes_sent = 0;
1877 n->util_total_bytes_recv = 0;
1878 n->util_total_bytes_sent = 0;
1879 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker, &inbound_bw_tracker_update, n,
1880 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1881 MAX_BANDWIDTH_CARRY_S);
1882 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1883 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_NOT_CONNECTED, GNUNET_TIME_UNIT_FOREVER_ABS);
1884 GNUNET_assert (GNUNET_OK ==
1885 GNUNET_CONTAINER_multipeermap_put (neighbours,
1887 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1893 * Check if the two given addresses are the same.
1894 * Actually only checks if the sessions are non-NULL
1895 * (which they should be) and then if they are identical;
1896 * the actual addresses don't matter if the session
1897 * pointers match anyway, and we must have session pointers
1900 * @param a1 first address to compare
1901 * @param a2 other address to compare
1902 * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
1905 address_matches (const struct NeighbourAddress *a1,
1906 const struct NeighbourAddress *a2)
1908 if ( (NULL == a1->session) ||
1909 (NULL == a2->session) )
1914 return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1918 /* We received a address suggestion after requesting an address in
1919 * try_connect or after receiving a connect, switch to address
1922 address_suggest_cont (void *cls,
1923 const struct GNUNET_PeerIdentity *peer,
1924 const struct GNUNET_HELLO_Address *address, struct Session *session,
1925 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
1926 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1927 const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
1929 GST_neighbours_switch_to_address(peer, address, session, ats, ats_count,
1930 bandwidth_in, bandwidth_out);
1934 struct BlacklistCheckSwitchContext
1936 struct BlacklistCheckSwitchContext *prev;
1937 struct BlacklistCheckSwitchContext *next;
1940 struct GST_BlacklistCheck *blc;
1942 struct GNUNET_HELLO_Address *address;
1943 struct Session *session;
1944 struct GNUNET_ATS_Information *ats;
1947 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
1948 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
1952 * Black list check result for try_connect call
1953 * If connection to the peer is allowed request adddress and
1955 * @param cls blc_ctx bl context
1956 * @param peer the peer
1957 * @param result the result
1960 try_connect_bl_check_cont (void *cls,
1961 const struct GNUNET_PeerIdentity *peer, int result)
1963 struct BlacklistCheckSwitchContext *blc_ctx = cls;
1964 struct NeighbourMapEntry *n;
1966 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
1967 GNUNET_free (blc_ctx);
1969 if (GNUNET_OK != result)
1971 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1972 _("Blacklisting disapproved to connect to peer `%s'\n"),
1976 n = setup_neighbour (peer);
1977 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
1978 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1980 GNUNET_ATS_reset_backoff (GST_ats, peer);
1981 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer,
1982 &address_suggest_cont, n);
1988 * Try to create a connection to the given target (eventually).
1990 * @param target peer to try to connect to
1993 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1995 struct NeighbourMapEntry *n;
1996 struct GST_BlacklistCheck *blc;
1997 struct BlacklistCheckSwitchContext *blc_ctx;
1999 if (NULL == neighbours)
2001 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2002 "Asked to connect to peer `%s' during shutdown\n",
2003 GNUNET_i2s (target));
2004 return; /* during shutdown, do nothing */
2006 n = lookup_neighbour (target);
2007 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2008 "Asked to connect to peer `%s' (state: %s)\n",
2009 GNUNET_i2s (target),
2010 (NULL != n) ? GNUNET_TRANSPORT_ps2s(n->state) : "NEW PEER");
2015 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2016 /* this should not be possible */
2018 free_neighbour (n, GNUNET_NO);
2020 case GNUNET_TRANSPORT_PS_INIT_ATS:
2021 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2022 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2023 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2024 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2025 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2026 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2027 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2028 "Ignoring request to try to connect to `%s', already trying!\n",
2029 GNUNET_i2s (target));
2030 return; /* already trying */
2031 case GNUNET_TRANSPORT_PS_CONNECTED:
2032 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2033 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2034 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2035 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2036 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2037 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2038 "Ignoring request to try to connect, already connected to `%s'!\n",
2039 GNUNET_i2s (target));
2040 return; /* already connected */
2041 case GNUNET_TRANSPORT_PS_DISCONNECT:
2042 /* get rid of remains, ready to re-try immediately */
2043 free_neighbour (n, GNUNET_NO);
2045 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2046 /* should not be possible */
2049 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2050 "Unhandled state `%s'\n",
2051 GNUNET_TRANSPORT_ps2s (n->state));
2053 free_neighbour (n, GNUNET_NO);
2058 /* Do blacklist check if connecting to this peer is allowed */
2059 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2060 GNUNET_CONTAINER_DLL_insert (pending_bc_head, pending_bc_tail, blc_ctx);
2062 if (NULL != (blc = GST_blacklist_test_allowed (target, NULL,
2063 &try_connect_bl_check_cont, blc_ctx)))
2071 * Function called with the result of a blacklist check.
2073 * @param cls closure with the `struct BlackListCheckContext`
2074 * @param peer peer this check affects
2075 * @param result #GNUNET_OK if the address is allowed
2078 handle_test_blacklist_cont (void *cls,
2079 const struct GNUNET_PeerIdentity *peer,
2082 struct BlackListCheckContext *bcc = cls;
2083 struct NeighbourMapEntry *n;
2086 GNUNET_CONTAINER_DLL_remove (bc_head,
2089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2090 "Connection to new address of peer `%s' based on blacklist is `%s'\n",
2092 (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
2093 if (GNUNET_OK == result)
2095 GST_ats_add_address (bcc->na.address, bcc->na.session, NULL, 0);
2099 /* Blacklist disagreed on connecting to a peer with this address
2100 * Destroy address because we are not allowed to use it
2102 if (NULL != bcc->na.session)
2103 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, bcc->na.session);
2104 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, NULL);
2106 if (NULL == (n = lookup_neighbour (peer)))
2108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2109 "No neighbor entry for peer `%s', ignoring blacklist result\n",
2111 goto cleanup; /* nobody left to care about new address */
2113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2114 "Received blacklist result for peer `%s' in state %s/%d\n",
2116 GNUNET_TRANSPORT_ps2s (n->state),
2117 n->send_connect_ack);
2120 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2121 /* this should not be possible */
2123 free_neighbour (n, GNUNET_NO);
2125 case GNUNET_TRANSPORT_PS_INIT_ATS:
2126 /* waiting on ATS suggestion; still, pass address to ATS as a
2129 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2130 /* check if the address the blacklist was fine with matches
2131 ATS suggestion, if so, we can move on! */
2132 if ( (GNUNET_OK == result) &&
2133 (1 == n->send_connect_ack) )
2135 n->send_connect_ack = 2;
2136 send_session_connect_ack_message (bcc->na.address,
2138 n->connect_ack_timestamp);
2140 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2143 "Blacklist result for peer %s is for non-primary address, ignored\n",
2145 break; /* result for an address we currently don't care about */
2147 if (GNUNET_OK == result)
2149 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_SENT,
2150 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2151 send_session_connect (&n->primary_address);
2155 free_address (&n->primary_address);
2156 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
2157 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2160 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2161 /* waiting on CONNECT_ACK, send ACK if one is pending */
2162 if ( (GNUNET_OK == result) &&
2163 (1 == n->send_connect_ack) )
2165 n->send_connect_ack = 2;
2166 send_session_connect_ack_message (n->primary_address.address,
2167 n->primary_address.session,
2168 n->connect_ack_timestamp);
2171 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2172 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
2173 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2174 GNUNET_ATS_reset_backoff (GST_ats, peer);
2175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2176 "Suggesting address for peer %s to ATS\n",
2178 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer,
2179 &address_suggest_cont, n);
2181 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2182 /* waiting on ATS suggestion, don't care about blacklist */
2184 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2185 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2188 "Blacklist result ignored, as it is not for our primary address\n");
2189 break; /* result for an address we currently don't care about */
2191 if (GNUNET_OK == result)
2193 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK,
2194 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2195 send_session_connect_ack_message (bcc->na.address,
2197 n->connect_ack_timestamp);
2198 if (1 == n->send_connect_ack)
2199 n->send_connect_ack = 2;
2203 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
2205 plugin = GST_plugins_find (bcc->na.address->transport_name);
2206 if ( (NULL != plugin) &&
2207 (NULL != bcc->na.session) )
2209 plugin->disconnect_session (plugin->cls,
2213 GNUNET_break (NULL != plugin);
2214 free_address (&n->primary_address);
2215 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
2216 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2217 GNUNET_ATS_reset_backoff (GST_ats, peer);
2220 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2221 /* waiting on SESSION_ACK, send ACK if one is pending */
2222 if ( (GNUNET_OK == result) &&
2223 (1 == n->send_connect_ack) )
2225 n->send_connect_ack = 2;
2226 send_session_connect_ack_message (n->primary_address.address,
2227 n->primary_address.session,
2228 n->connect_ack_timestamp);
2231 case GNUNET_TRANSPORT_PS_CONNECTED:
2232 /* already connected, don't care about blacklist */
2234 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2235 /* still waiting on ATS suggestion, don't care about blacklist */
2237 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2238 if ( (GNUNET_OK == result) &&
2239 (1 == n->send_connect_ack) )
2241 n->send_connect_ack = 2;
2242 send_session_connect_ack_message (bcc->na.address,
2244 n->connect_ack_timestamp);
2246 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2249 "Blacklist result ignored, as it is not for our primary address\n");
2250 break; /* result for an address we currently don't care about */
2252 if (GNUNET_OK == result)
2254 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2255 send_session_connect (&n->primary_address);
2259 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2262 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2263 /* waiting on CONNECT_ACK, don't care about blacklist */
2264 if ( (GNUNET_OK == result) &&
2265 (1 == n->send_connect_ack) )
2267 n->send_connect_ack = 2;
2268 send_session_connect_ack_message (n->primary_address.address,
2269 n->primary_address.session,
2270 n->connect_ack_timestamp);
2273 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2274 if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2277 "Blacklist result ignored, as it is not for our primary address\n");
2278 break; /* result for an address we currently don't care about */
2280 if (GNUNET_OK == result)
2282 send_session_connect (&n->alternative_address);
2283 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT);
2287 set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
2288 free_address (&n->alternative_address);
2291 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2292 /* waiting on CONNECT_ACK, don't care about blacklist */
2293 if ( (GNUNET_OK == result) &&
2294 (1 == n->send_connect_ack) )
2296 n->send_connect_ack = 2;
2297 send_session_connect_ack_message (n->primary_address.address,
2298 n->primary_address.session,
2299 n->connect_ack_timestamp);
2302 case GNUNET_TRANSPORT_PS_DISCONNECT:
2303 /* Nothing to do here, ATS will already do what can be done */
2305 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2306 /* should not be possible */
2310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2311 "Unhandled state `%s'\n",
2312 GNUNET_TRANSPORT_ps2s (n->state));
2314 free_neighbour (n, GNUNET_NO);
2318 GNUNET_HELLO_address_free (bcc->na.address);
2324 * We want to know if connecting to a particular peer via
2325 * a particular address is allowed. Check it!
2327 * @param peer identity of the peer to switch the address for
2328 * @param ts time at which the check was initiated
2329 * @param address address of the other peer, NULL if other peer
2331 * @param session session to use (or NULL)
2334 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2335 struct GNUNET_TIME_Absolute ts,
2336 const struct GNUNET_HELLO_Address *address,
2337 struct Session *session)
2339 struct BlackListCheckContext *bcc;
2340 struct GST_BlacklistCheck *bc;
2342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2343 "Checking peer `%s' against blacklist\n",
2345 bcc = GNUNET_new (struct BlackListCheckContext);
2346 bcc->na.address = GNUNET_HELLO_address_copy (address);
2347 bcc->na.session = session;
2348 bcc->na.connect_timestamp = ts;
2349 GNUNET_CONTAINER_DLL_insert (bc_head,
2352 if (NULL != (bc = GST_blacklist_test_allowed (peer,
2353 address->transport_name,
2354 &handle_test_blacklist_cont, bcc)))
2356 /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2357 we must only store 'bc' if 'bc' is non-NULL... */
2362 * We received a 'SESSION_CONNECT' message from the other peer.
2363 * Consider switching to it.
2365 * @param message possibly a 'struct SessionConnectMessage' (check format)
2366 * @param peer identity of the peer to switch the address for
2367 * @param address address of the other peer, NULL if other peer
2369 * @param session session to use (or NULL)
2370 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2373 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2374 const struct GNUNET_PeerIdentity *peer,
2375 const struct GNUNET_HELLO_Address *address,
2376 struct Session *session)
2378 const struct SessionConnectMessage *scm;
2379 struct NeighbourMapEntry *n;
2380 struct GNUNET_TIME_Absolute ts;
2382 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2383 "Received CONNECT message from peer `%s'\n",
2385 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2387 GNUNET_break_op (0);
2388 return GNUNET_SYSERR;
2390 GNUNET_STATISTICS_update (GST_stats,
2392 ("# CONNECT messages received"),
2394 if (NULL == neighbours)
2396 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2397 _("CONNECT request from peer `%s' ignored due impending shutdown\n"),
2399 return GNUNET_OK; /* we're shutting down */
2401 scm = (const struct SessionConnectMessage *) message;
2402 GNUNET_break_op (0 == ntohl (scm->reserved));
2403 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2404 n = lookup_neighbour (peer);
2406 n = setup_neighbour (peer);
2407 n->send_connect_ack = 1;
2408 n->connect_ack_timestamp = ts;
2410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2411 "Received SESSION_CONNECT for peer `%s' in state %s/%d\n",
2413 GNUNET_TRANSPORT_ps2s (n->state),
2414 n->send_connect_ack);
2417 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2418 /* Do a blacklist check for the new address */
2419 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
2420 check_blacklist (peer, ts, address, session);
2422 case GNUNET_TRANSPORT_PS_INIT_ATS:
2423 /* CONNECT message takes priority over us asking ATS for address */
2424 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
2426 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2427 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2428 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2429 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2430 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2431 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2432 /* It can never hurt to have an alternative address in the above cases,
2433 see if it is allowed */
2434 check_blacklist (peer, ts, address, session);
2436 case GNUNET_TRANSPORT_PS_CONNECTED:
2437 /* we are already connected and can thus send the ACK immediately;
2438 still, it can never hurt to have an alternative address, so also
2439 tell ATS about it */
2440 GNUNET_assert (NULL != n->primary_address.address);
2441 GNUNET_assert (NULL != n->primary_address.session);
2442 n->send_connect_ack = 0;
2443 send_session_connect_ack_message (n->primary_address.address,
2444 n->primary_address.session, ts);
2445 check_blacklist (peer, ts, address, session);
2447 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2448 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2449 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2450 /* It can never hurt to have an alternative address in the above cases,
2451 see if it is allowed */
2452 check_blacklist (peer, ts, address, session);
2454 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2455 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2456 /* we are already connected and can thus send the ACK immediately;
2457 still, it can never hurt to have an alternative address, so also
2458 tell ATS about it */
2459 GNUNET_assert (NULL != n->primary_address.address);
2460 GNUNET_assert (NULL != n->primary_address.session);
2461 n->send_connect_ack = 0;
2462 send_session_connect_ack_message (n->primary_address.address,
2463 n->primary_address.session, ts);
2464 check_blacklist (peer, ts, address, session);
2466 case GNUNET_TRANSPORT_PS_DISCONNECT:
2467 /* get rid of remains without terminating sessions, ready to re-try */
2468 free_neighbour (n, GNUNET_YES);
2469 n = setup_neighbour (peer);
2470 set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS);
2471 GNUNET_ATS_reset_backoff (GST_ats, peer);
2473 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2474 /* should not be possible */
2478 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2479 "Unhandled state `%s'\n",
2480 GNUNET_TRANSPORT_ps2s (n->state));
2482 return GNUNET_SYSERR;
2488 switch_address_bl_check_cont (void *cls,
2489 const struct GNUNET_PeerIdentity *peer, int result)
2491 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2492 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2493 struct NeighbourMapEntry *n;
2495 if ( (NULL == (n = lookup_neighbour (peer))) || (result == GNUNET_NO) ||
2496 (NULL == (papi = GST_plugins_find (blc_ctx->address->transport_name))) )
2500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2501 "Peer %s is unknown, suggestion ignored\n",
2504 if (result == GNUNET_NO)
2506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2507 "Blacklist denied to switch to suggested address `%s' sesion %p for peer `%s'\n",
2508 GST_plugins_a2s (blc_ctx->address),
2510 GNUNET_i2s (&blc_ctx->address->peer));
2512 /* Delete address (or session if existing) in ATS */
2513 GNUNET_ATS_address_destroyed (GST_ats, blc_ctx->address, blc_ctx->session);
2515 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
2516 GNUNET_HELLO_address_free(blc_ctx->address);
2517 GNUNET_free_non_null (blc_ctx->ats);
2518 GNUNET_free (blc_ctx);
2522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2523 "Blacklist accepted to switch to suggested address `%s' for peer `%s'\n",
2524 GST_plugins_a2s (blc_ctx->address),
2526 GNUNET_i2s (&blc_ctx->address->peer));
2528 if (NULL == blc_ctx->session)
2530 blc_ctx->session = papi->get_session (papi->cls, blc_ctx->address);
2531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2532 "Obtained new session for peer `%s' and address '%s': %p\n",
2533 GNUNET_i2s (&blc_ctx->address->peer), GST_plugins_a2s (blc_ctx->address), blc_ctx->session);
2535 if (NULL == blc_ctx->session)
2537 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2538 "Failed to obtain new session for peer `%s' and address '%s'\n",
2539 GNUNET_i2s (&blc_ctx->address->peer), GST_plugins_a2s (blc_ctx->address));
2540 GNUNET_ATS_address_destroyed (GST_ats, blc_ctx->address, NULL);
2545 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2547 free_neighbour (n, GNUNET_NO);
2549 case GNUNET_TRANSPORT_PS_INIT_ATS:
2550 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2551 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2552 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_BLACKLIST,
2553 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2554 check_blacklist (&n->id, n->connect_ack_timestamp,
2555 blc_ctx->address, blc_ctx->session);
2557 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2558 /* ATS suggests a different address, switch 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_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2562 check_blacklist (&n->id, n->connect_ack_timestamp,
2563 blc_ctx->address, blc_ctx->session);
2565 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2566 /* ATS suggests a different address, switch again */
2567 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2568 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2569 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_BLACKLIST,
2570 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2571 check_blacklist (&n->id, n->connect_ack_timestamp,
2572 blc_ctx->address, blc_ctx->session);
2574 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
2575 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2576 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2577 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST,
2578 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2579 check_blacklist (&n->id, n->connect_ack_timestamp,
2580 blc_ctx->address, blc_ctx->session);
2582 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
2583 set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2584 check_blacklist (&n->id, n->connect_ack_timestamp,
2585 blc_ctx->address, blc_ctx->session);
2587 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
2588 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
2589 /* ATS asks us to switch while we were trying to connect; switch to new
2590 address and check blacklist again */
2591 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2592 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2593 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST,
2594 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2595 check_blacklist (&n->id, n->connect_ack_timestamp,
2596 blc_ctx->address, blc_ctx->session);
2598 case GNUNET_TRANSPORT_PS_CONNECTED:
2599 GNUNET_assert (NULL != n->primary_address.address);
2600 GNUNET_assert (NULL != n->primary_address.session);
2601 if (n->primary_address.session == blc_ctx->session)
2603 /* not an address change, just a quota change */
2604 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2605 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_YES);
2608 /* ATS asks us to switch a life connection; see if we can get
2609 a CONNECT_ACK on it before we actually do this! */
2610 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
2611 set_alternative_address (n, blc_ctx->address, blc_ctx->session,
2612 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
2613 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2614 blc_ctx->address, blc_ctx->session);
2616 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2617 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2618 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2619 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST,
2620 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2621 check_blacklist (&n->id, n->connect_ack_timestamp,
2622 blc_ctx->address, blc_ctx->session);
2624 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
2625 /* ATS asks us to switch while we were trying to reconnect; switch to new
2626 address and check blacklist again */
2627 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2628 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2629 set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2630 check_blacklist (&n->id, n->connect_ack_timestamp,
2631 blc_ctx->address, blc_ctx->session);
2633 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2634 /* ATS asks us to switch while we were trying to reconnect; switch to new
2635 address and check blacklist again */
2636 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2637 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2638 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST,
2639 GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
2640 check_blacklist (&n->id, n->connect_ack_timestamp,
2641 blc_ctx->address, blc_ctx->session);
2643 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
2644 if (n->primary_address.session == blc_ctx->session)
2646 /* ATS switches back to still-active session */
2647 set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
2648 free_address (&n->alternative_address);
2651 /* ATS asks us to switch a life connection, update blacklist check */
2652 set_primary_address (n, blc_ctx->address, blc_ctx->session,
2653 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
2654 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2655 blc_ctx->address, blc_ctx->session);
2657 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
2658 if (n->primary_address.session == blc_ctx->session)
2660 /* ATS switches back to still-active session */
2661 free_address (&n->alternative_address);
2662 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
2665 /* ATS asks us to switch a life connection, update blacklist check */
2666 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
2667 set_alternative_address (n, blc_ctx->address, blc_ctx->session,
2668 blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
2669 check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
2670 blc_ctx->address, blc_ctx->session);
2672 case GNUNET_TRANSPORT_PS_DISCONNECT:
2673 /* not going to switch addresses while disconnecting */
2675 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2680 "Unhandled state `%s'\n",
2681 GNUNET_TRANSPORT_ps2s (n->state));
2686 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, blc_ctx);
2687 GNUNET_HELLO_address_free(blc_ctx->address);
2688 GNUNET_free_non_null (blc_ctx->ats);
2689 GNUNET_free (blc_ctx);
2695 * For the given peer, switch to this address.
2697 * Before accepting this addresses and actively using it, a blacklist check
2698 * is performed. If this blacklist check fails the address will be destroyed.
2700 * @param peer identity of the peer to switch the address for
2701 * @param address address of the other peer,
2702 * @param session session to use or NULL if transport should initiate a session
2703 * @param ats performance data
2704 * @param ats_count number of entries in ats
2705 * @param bandwidth_in inbound quota to be used when connection is up,
2706 * 0 to disconnect from peer
2707 * @param bandwidth_out outbound quota to be used when connection is up,
2708 * 0 to disconnect from peer
2711 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2712 const struct GNUNET_HELLO_Address *address,
2713 struct Session *session,
2714 const struct GNUNET_ATS_Information *ats,
2716 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2717 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2719 struct NeighbourMapEntry *n;
2720 struct GST_BlacklistCheck *blc;
2721 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2722 struct BlacklistCheckSwitchContext *blc_ctx;
2725 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2726 "ATS has decided on an address for peer %s\n",
2728 GNUNET_assert (NULL != address->transport_name);
2729 if (NULL == (n = lookup_neighbour (peer)))
2731 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2732 "Peer %s is unknown, suggestion ignored\n",
2737 /* Obtain an session for this address from plugin */
2738 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2740 /* we don't have the plugin for this address */
2741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2742 "Plugin `%s' is unknown, suggestion for peer %s ignored\n",
2743 address->transport_name,
2745 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2748 if ((NULL == session) &&
2749 (GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2751 /* This is a inbound address and we do not have a session to use! */
2752 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2753 "Inbound address without session `%s'! Destroying address...\n",
2754 GST_plugins_a2s (address));
2755 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2759 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2760 "ATS tells us to switch to %s address '%s' session %p for "
2761 "peer `%s' in state %s/%d (quota in/out %u %u )\n",
2762 GNUNET_HELLO_address_check_option (address,
2763 GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "",
2764 GST_plugins_a2s (address), session, GNUNET_i2s (peer),
2765 GNUNET_TRANSPORT_ps2s (n->state), n->send_connect_ack,
2766 ntohl (bandwidth_in.value__), ntohl (bandwidth_out.value__));
2768 /* Perform blacklist check */
2769 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2770 blc_ctx->address = GNUNET_HELLO_address_copy (address);
2771 blc_ctx->session = session;
2772 blc_ctx->bandwidth_in = bandwidth_in;
2773 blc_ctx->bandwidth_out = bandwidth_out;
2774 blc_ctx->ats_count = ats_count;
2775 blc_ctx->ats = NULL;
2778 blc_ctx->ats = GNUNET_malloc (ats_count * sizeof (struct GNUNET_ATS_Information));
2779 for (c = 0; c < ats_count; c++)
2781 blc_ctx->ats[c].type = ats[c].type;
2782 blc_ctx->ats[c].value = ats[c].value;
2786 GNUNET_CONTAINER_DLL_insert (pending_bc_head, pending_bc_tail, blc_ctx);
2787 if (NULL != (blc = GST_blacklist_test_allowed (peer, address->transport_name,
2788 &switch_address_bl_check_cont, blc_ctx)))
2796 send_utilization_data (void *cls,
2797 const struct GNUNET_PeerIdentity *key,
2800 struct NeighbourMapEntry *n = value;
2801 struct GNUNET_ATS_Information atsi[4];
2803 uint32_t bps_pl_out;
2806 struct GNUNET_TIME_Relative delta;
2808 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2809 GNUNET_TIME_absolute_get ());
2813 if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2814 bps_pl_in = (1000LL * 1000LL * n->util_payload_bytes_recv) / (delta.rel_value_us);
2816 if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2817 bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2819 "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2824 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2825 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2827 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2828 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2832 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2836 atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2837 atsi[0].value = htonl (bps_out);
2838 atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2839 atsi[1].value = htonl (bps_in);
2841 atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2842 atsi[2].value = htonl (bps_pl_out);
2843 atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2844 atsi[3].value = htonl (bps_pl_in);
2846 GST_ats_update_metrics (key, n->primary_address.address,
2847 n->primary_address.session, atsi, 4);
2848 n->util_payload_bytes_recv = 0;
2849 n->util_payload_bytes_sent = 0;
2850 n->util_total_bytes_recv = 0;
2851 n->util_total_bytes_sent = 0;
2852 n->last_util_transmission = GNUNET_TIME_absolute_get();
2858 * Task transmitting utilization in a regular interval
2860 * @param cls the 'struct NeighbourMapEntry' for which we are running
2861 * @param tc scheduler context (unused)
2864 utilization_transmission (void *cls,
2865 const struct GNUNET_SCHEDULER_TaskContext *tc)
2867 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2869 if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2870 GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2872 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2873 utilization_transmission, NULL);
2879 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2880 const struct GNUNET_HELLO_Address *address,
2881 struct Session *session,
2882 const struct GNUNET_MessageHeader *message)
2884 struct NeighbourMapEntry *n;
2886 n = lookup_neighbour (peer);
2889 n->util_total_bytes_recv += ntohs(message->size);
2894 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2895 const struct GNUNET_HELLO_Address *address,
2896 struct Session *session,
2897 const struct GNUNET_MessageHeader *message)
2899 struct NeighbourMapEntry *n;
2900 n = lookup_neighbour (peer);
2903 n->util_payload_bytes_recv += ntohs(message->size);
2908 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2909 const struct GNUNET_HELLO_Address *address,
2910 struct Session *session,
2913 struct NeighbourMapEntry *n;
2914 n = lookup_neighbour (peer);
2917 if (n->primary_address.session != session)
2919 n->util_total_bytes_sent += size;
2924 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2927 struct NeighbourMapEntry *n;
2928 n = lookup_neighbour (peer);
2931 n->util_payload_bytes_sent += size;
2936 * Master task run for every neighbour. Performs all of the time-related
2937 * activities (keep alive, send next message, disconnect if idle, finish
2938 * clean up after disconnect).
2940 * @param cls the 'struct NeighbourMapEntry' for which we are running
2941 * @param tc scheduler context (unused)
2944 master_task (void *cls,
2945 const struct GNUNET_SCHEDULER_TaskContext *tc)
2947 struct NeighbourMapEntry *n = cls;
2948 struct GNUNET_TIME_Relative delay;
2950 n->task = GNUNET_SCHEDULER_NO_TASK;
2951 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2952 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2953 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2954 GNUNET_i2s (&n->id),
2955 GNUNET_TRANSPORT_ps2s(n->state),
2956 GNUNET_STRINGS_relative_time_to_string (delay,
2960 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2961 /* invalid state for master task, clean up */
2963 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2964 free_neighbour (n, GNUNET_NO);
2966 case GNUNET_TRANSPORT_PS_INIT_ATS:
2967 if (0 == delay.rel_value_us)
2969 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2970 "Connection to `%s' timed out waiting for ATS to provide address\n",
2971 GNUNET_i2s (&n->id));
2972 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2973 free_neighbour (n, GNUNET_NO);
2977 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
2978 if (0 == delay.rel_value_us)
2980 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2981 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2982 GNUNET_i2s (&n->id));
2983 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
2984 free_neighbour (n, GNUNET_NO);
2988 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
2989 if (0 == delay.rel_value_us)
2991 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2992 "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2993 GNUNET_i2s (&n->id));
2994 /* We could not send to this address, delete address and session */
2995 if (NULL != n->primary_address.session)
2996 GNUNET_ATS_address_destroyed (GST_ats,
2997 n->primary_address.address, n->primary_address.session);
2998 GNUNET_ATS_address_destroyed (GST_ats,
2999 n->primary_address.address, NULL);
3000 disconnect_neighbour (n);
3004 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3005 if (0 == delay.rel_value_us)
3007 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3008 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
3009 GNUNET_i2s (&n->id));
3010 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3011 free_neighbour (n, GNUNET_NO);
3015 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3016 if (0 == delay.rel_value_us)
3018 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3019 "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
3020 GNUNET_i2s (&n->id));
3021 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3022 free_neighbour (n, GNUNET_NO);
3026 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3027 if (0 == delay.rel_value_us)
3029 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3030 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
3031 GNUNET_i2s (&n->id));
3032 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3033 free_neighbour (n, GNUNET_NO);
3037 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3038 if (0 == delay.rel_value_us)
3040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3041 "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
3042 GNUNET_i2s (&n->id));
3043 disconnect_neighbour (n);
3047 case GNUNET_TRANSPORT_PS_CONNECTED:
3048 if (0 == delay.rel_value_us)
3050 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3051 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3052 GNUNET_i2s (&n->id));
3053 disconnect_neighbour (n);
3056 try_transmission_to_peer (n);
3059 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3060 if (0 == delay.rel_value_us)
3062 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3063 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3064 GNUNET_i2s (&n->id));
3065 disconnect_neighbour (n);
3069 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3070 if (0 == delay.rel_value_us)
3072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3073 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
3074 GNUNET_i2s (&n->id));
3075 disconnect_neighbour (n);
3079 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3080 if (0 == delay.rel_value_us)
3082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3083 "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
3084 GNUNET_i2s (&n->id));
3085 disconnect_neighbour (n);
3089 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3090 if (0 == delay.rel_value_us)
3092 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3093 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3094 GNUNET_i2s (&n->id));
3095 disconnect_neighbour (n);
3098 try_transmission_to_peer (n);
3101 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3102 if (0 == delay.rel_value_us)
3104 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3105 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
3106 GNUNET_i2s (&n->id));
3107 disconnect_neighbour (n);
3110 try_transmission_to_peer (n);
3113 case GNUNET_TRANSPORT_PS_DISCONNECT:
3114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3115 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3116 GNUNET_i2s (&n->id));
3117 free_neighbour (n, GNUNET_NO);
3119 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3120 /* how did we get here!? */
3124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3125 "Unhandled state `%s'\n",
3126 GNUNET_TRANSPORT_ps2s (n->state));
3130 if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
3131 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3132 (GNUNET_TRANSPORT_PS_CONNECTED == n->state) )
3134 /* if we are *now* in one of these three states, we're sending
3135 keep alive messages, so we need to consider the keepalive
3136 delay, not just the connection timeout */
3137 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
3140 if (GNUNET_SCHEDULER_NO_TASK == n->task)
3141 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3148 * Send a SESSION_ACK message to the neighbour to confirm that we
3149 * got his CONNECT_ACK.
3151 * @param n neighbour to send the SESSION_ACK to
3154 send_session_ack_message (struct NeighbourMapEntry *n)
3156 struct GNUNET_MessageHeader msg;
3158 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
3159 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3160 (void) send_with_session(n,
3161 (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
3162 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_NO,
3168 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
3169 * Consider switching to it.
3171 * @param message possibly a 'struct SessionConnectMessage' (check format)
3172 * @param peer identity of the peer to switch the address for
3173 * @param address address of the other peer, NULL if other peer
3175 * @param session session to use (or NULL)
3176 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3179 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
3180 const struct GNUNET_PeerIdentity *peer,
3181 const struct GNUNET_HELLO_Address *address,
3182 struct Session *session)
3184 const struct SessionConnectMessage *scm;
3185 struct GNUNET_TIME_Absolute ts;
3186 struct NeighbourMapEntry *n;
3188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3189 "Received CONNECT_ACK message from peer `%s'\n",
3192 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
3194 GNUNET_break_op (0);
3195 return GNUNET_SYSERR;
3197 GNUNET_STATISTICS_update (GST_stats,
3199 ("# CONNECT_ACK messages received"),
3201 scm = (const struct SessionConnectMessage *) message;
3202 GNUNET_break_op (ntohl (scm->reserved) == 0);
3203 if (NULL == (n = lookup_neighbour (peer)))
3205 GNUNET_STATISTICS_update (GST_stats,
3207 ("# unexpected CONNECT_ACK messages (no peer)"),
3209 return GNUNET_SYSERR;
3211 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3214 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3216 free_neighbour (n, GNUNET_NO);
3217 return GNUNET_SYSERR;
3218 case GNUNET_TRANSPORT_PS_INIT_ATS:
3219 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3220 GNUNET_STATISTICS_update (GST_stats,
3222 ("# unexpected CONNECT_ACK messages (not ready)"),
3225 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3226 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3228 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3229 "CONNECT_ACK ignored as the timestamp does not match our CONNECT request\n");
3232 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3233 GNUNET_STATISTICS_set (GST_stats,
3234 gettext_noop ("# peers connected"),
3235 ++neighbours_connected,
3237 connect_notify_cb (callback_cls, &n->id,
3238 n->primary_address.bandwidth_in,
3239 n->primary_address.bandwidth_out);
3240 /* Tell ATS that the outbound session we created to send CONNECT was successful */
3241 GST_ats_add_address (n->primary_address.address,
3242 n->primary_address.session,
3244 set_primary_address (n,
3245 n->primary_address.address,
3246 n->primary_address.session,
3247 n->primary_address.bandwidth_in,
3248 n->primary_address.bandwidth_out,
3250 send_session_ack_message (n);
3252 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3253 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3254 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3255 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3256 GNUNET_STATISTICS_update (GST_stats,
3258 ("# unexpected CONNECT_ACK messages (not ready)"),
3261 case GNUNET_TRANSPORT_PS_CONNECTED:
3262 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
3263 send_session_ack_message (n);
3265 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3266 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3267 /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
3268 to give us a new address... */
3269 GNUNET_STATISTICS_update (GST_stats,
3271 ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
3274 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3275 /* new address worked; go back to connected! */
3276 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
3277 send_session_ack_message (n);
3279 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3280 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
3281 send_session_ack_message (n);
3283 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3284 /* new address worked; adopt it and go back to connected! */
3285 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3286 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3288 GST_ats_add_address (n->alternative_address.address,
3289 n->alternative_address.session,
3291 set_primary_address (n, n->alternative_address.address,
3292 n->alternative_address.session, n->alternative_address.bandwidth_in,
3293 n->alternative_address.bandwidth_out, GNUNET_YES);
3295 free_address (&n->alternative_address);
3296 send_session_ack_message (n);
3298 case GNUNET_TRANSPORT_PS_DISCONNECT:
3299 GNUNET_STATISTICS_update (GST_stats,
3301 ("# unexpected CONNECT_ACK messages (disconnecting)"),
3303 return GNUNET_SYSERR;
3304 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3309 "Unhandled state `%s'\n",
3310 GNUNET_TRANSPORT_ps2s (n->state));
3312 return GNUNET_SYSERR;
3319 * A session was terminated. Take note; if needed, try to get
3320 * an alternative address from ATS.
3322 * @param peer identity of the peer where the session died
3323 * @param session session that is gone
3324 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3325 * this session was not in use
3328 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3329 struct Session *session)
3331 struct NeighbourMapEntry *n;
3332 struct BlackListCheckContext *bcc;
3333 struct BlackListCheckContext *bcc_next;
3335 /* make sure to cancel all ongoing blacklist checks involving 'session' */
3337 while (NULL != (bcc = bcc_next))
3339 bcc_next = bcc->next;
3340 if (bcc->na.session == session)
3342 if (NULL != bcc->bc)
3343 GST_blacklist_test_cancel (bcc->bc);
3344 GNUNET_HELLO_address_free (bcc->na.address);
3345 GNUNET_CONTAINER_DLL_remove (bc_head,
3351 if (NULL == (n = lookup_neighbour (peer)))
3352 return GNUNET_NO; /* can't affect us */
3353 if (session != n->primary_address.session)
3355 if (session == n->alternative_address.session)
3357 if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3358 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
3359 set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
3362 free_address (&n->alternative_address);
3364 return GNUNET_NO; /* doesn't affect us further */
3367 n->expect_latency_response = GNUNET_NO;
3370 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3372 free_neighbour (n, GNUNET_NO);
3374 case GNUNET_TRANSPORT_PS_INIT_ATS:
3376 free_neighbour (n, GNUNET_NO);
3378 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3379 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3380 free_address (&n->primary_address);
3381 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3383 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3384 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3385 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3386 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3387 /* error on inbound session; free neighbour entirely */
3388 free_address (&n->primary_address);
3389 free_neighbour (n, GNUNET_NO);
3391 case GNUNET_TRANSPORT_PS_CONNECTED:
3392 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3393 free_address (&n->primary_address);
3395 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3396 /* we don't have an address, how can it go down? */
3399 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3400 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3401 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3403 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3404 /* primary went down while we were checking secondary against
3405 blacklist, adopt secondary as primary */
3406 free_address (&n->primary_address);
3407 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3408 n->primary_address = n->alternative_address;
3409 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3411 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3412 /* primary went down while we were waiting for CONNECT_ACK on secondary;
3413 secondary as primary */
3414 free_address (&n->primary_address);
3415 n->primary_address = n->alternative_address;
3416 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3417 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3419 case GNUNET_TRANSPORT_PS_DISCONNECT:
3420 free_address (&n->primary_address);
3422 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3423 /* neighbour was freed and plugins told to terminate session */
3427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3428 "Unhandled state `%s'\n",
3429 GNUNET_TRANSPORT_ps2s (n->state));
3433 if (GNUNET_SCHEDULER_NO_TASK != n->task)
3434 GNUNET_SCHEDULER_cancel (n->task);
3435 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3441 * We received a 'SESSION_ACK' message from the other peer.
3442 * If we sent a 'CONNECT_ACK' last, this means we are now
3443 * connected. Otherwise, do nothing.
3445 * @param message possibly a 'struct SessionConnectMessage' (check format)
3446 * @param peer identity of the peer to switch the address for
3447 * @param address address of the other peer, NULL if other peer
3449 * @param session session to use (or NULL)
3450 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3453 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3454 const struct GNUNET_PeerIdentity *peer,
3455 const struct GNUNET_HELLO_Address *address,
3456 struct Session *session)
3458 struct NeighbourMapEntry *n;
3460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3461 "Received SESSION_ACK message from peer `%s'\n",
3463 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3465 GNUNET_break_op (0);
3466 return GNUNET_SYSERR;
3468 GNUNET_STATISTICS_update (GST_stats,
3470 ("# SESSION_ACK messages received"),
3472 if (NULL == (n = lookup_neighbour (peer)))
3474 GNUNET_break_op (0);
3475 return GNUNET_SYSERR;
3477 /* check if we are in a plausible state for having sent
3478 a CONNECT_ACK. If not, return, otherwise break */
3479 if ( ( (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state) &&
3480 (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state) ) ||
3481 (2 != n->send_connect_ack) )
3483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3484 "Received SESSION_ACK message from peer `%s' in state %s/%d\n",
3486 GNUNET_TRANSPORT_ps2s (n->state),
3487 n->send_connect_ack);
3488 GNUNET_STATISTICS_update (GST_stats,
3489 gettext_noop ("# unexpected SESSION_ACK messages"), 1,
3493 set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3494 GNUNET_STATISTICS_set (GST_stats,
3495 gettext_noop ("# peers connected"),
3496 ++neighbours_connected,
3498 connect_notify_cb (callback_cls, &n->id,
3499 n->primary_address.bandwidth_in,
3500 n->primary_address.bandwidth_out);
3502 GST_ats_add_address (n->primary_address.address,
3503 n->primary_address.session,
3505 set_primary_address (n,
3506 n->primary_address.address,
3507 n->primary_address.session,
3508 n->primary_address.bandwidth_in,
3509 n->primary_address.bandwidth_out,
3516 * Test if we're connected to the given peer.
3518 * @param target peer to test
3519 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3522 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3524 return test_connected (lookup_neighbour (target));
3528 * Change the incoming quota for the given peer.
3530 * @param neighbour identity of peer to change qutoa for
3531 * @param quota new quota
3534 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3535 struct GNUNET_BANDWIDTH_Value32NBO quota)
3537 struct NeighbourMapEntry *n;
3539 if (NULL == (n = lookup_neighbour (neighbour)))
3541 GNUNET_STATISTICS_update (GST_stats,
3543 ("# SET QUOTA messages ignored (no such peer)"),
3547 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3548 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3549 ntohl (quota.value__), GNUNET_i2s (&n->id));
3550 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3551 if (0 != ntohl (quota.value__))
3553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3554 "Disconnecting peer `%4s' due to SET_QUOTA\n",
3555 GNUNET_i2s (&n->id));
3556 if (GNUNET_YES == test_connected (n))
3557 GNUNET_STATISTICS_update (GST_stats,
3558 gettext_noop ("# disconnects due to quota of 0"),
3560 disconnect_neighbour (n);
3565 * We received a disconnect message from the given peer,
3566 * validate and process.
3568 * @param peer sender of the message
3569 * @param msg the disconnect message
3572 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
3573 const struct GNUNET_MessageHeader *msg)
3575 struct NeighbourMapEntry *n;
3576 const struct SessionDisconnectMessage *sdm;
3578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3579 "Received DISCONNECT message from peer `%s'\n",
3581 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3583 // GNUNET_break_op (0);
3584 GNUNET_STATISTICS_update (GST_stats,
3586 ("# disconnect messages ignored (old format)"), 1,
3590 GNUNET_STATISTICS_update (GST_stats,
3592 ("# DISCONNECT messages received"),
3594 sdm = (const struct SessionDisconnectMessage *) msg;
3595 if (NULL == (n = lookup_neighbour (peer)))
3596 return; /* gone already */
3597 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3599 GNUNET_STATISTICS_update (GST_stats,
3601 ("# disconnect messages ignored (timestamp)"), 1,
3605 if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3607 GNUNET_break_op (0);
3610 if (ntohl (sdm->purpose.size) !=
3611 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3612 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3613 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3615 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3616 "%s message from peer `%s' has invalid size \n",
3619 GNUNET_break_op (0);
3623 GNUNET_CRYPTO_eddsa_verify
3624 (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3625 &sdm->signature, &sdm->public_key))
3627 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3628 "%s message from peer `%s' cannot be verified \n",
3631 GNUNET_break_op (0);
3634 if (GNUNET_YES == test_connected (n))
3635 GNUNET_STATISTICS_update (GST_stats,
3637 ("# other peer asked to disconnect from us"), 1,
3639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3640 "Disconnecting by request from peer %s\n",
3642 disconnect_neighbour (n);
3647 * Closure for the neighbours_iterate function.
3649 struct IteratorContext
3652 * Function to call on each connected neighbour.
3654 GST_NeighbourIterator cb;
3664 * Call the callback from the closure for each neighbour.
3666 * @param cls the `struct IteratorContext`
3667 * @param key the hash of the public key of the neighbour
3668 * @param value the `struct NeighbourMapEntry`
3669 * @return #GNUNET_OK (continue to iterate)
3672 neighbours_iterate (void *cls,
3673 const struct GNUNET_PeerIdentity *key,
3676 struct IteratorContext *ic = cls;
3677 struct NeighbourMapEntry *n = value;
3678 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3679 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3682 if (NULL != n->primary_address.address)
3684 bandwidth_in = n->primary_address.bandwidth_in;
3685 bandwidth_out = n->primary_address.bandwidth_out;
3689 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3690 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3694 n->primary_address.address,
3697 bandwidth_in, bandwidth_out);
3703 * Iterate over all connected neighbours.
3705 * @param cb function to call
3706 * @param cb_cls closure for cb
3709 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3711 struct IteratorContext ic;
3713 if (NULL == neighbours)
3714 return; /* can happen during shutdown */
3717 GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3722 * If we have an active connection to the given target, it must be shutdown.
3724 * @param target peer to disconnect from
3727 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3729 struct NeighbourMapEntry *n;
3731 if (NULL == (n = lookup_neighbour (target)))
3732 return; /* not active */
3733 if (GNUNET_YES == test_connected (n))
3734 GNUNET_STATISTICS_update (GST_stats,
3736 ("# disconnected from peer upon explicit request"), 1,
3738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3739 "Forced disconnect from peer %s\n",
3740 GNUNET_i2s (target));
3741 disconnect_neighbour (n);
3746 * Obtain current latency information for the given neighbour.
3748 * @param peer to get the latency for
3749 * @return observed latency of the address, FOREVER if the
3750 * the connection is not up
3752 struct GNUNET_TIME_Relative
3753 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3755 struct NeighbourMapEntry *n;
3757 n = lookup_neighbour (peer);
3759 return GNUNET_TIME_UNIT_FOREVER_REL;
3762 case GNUNET_TRANSPORT_PS_CONNECTED:
3763 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
3764 case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
3765 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3766 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3767 case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
3769 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3770 case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
3771 case GNUNET_TRANSPORT_PS_INIT_ATS:
3772 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
3773 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
3774 case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
3775 case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
3776 case GNUNET_TRANSPORT_PS_CONNECT_SENT:
3777 case GNUNET_TRANSPORT_PS_DISCONNECT:
3778 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3779 return GNUNET_TIME_UNIT_FOREVER_REL;
3781 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3782 "Unhandled state `%s'\n",
3783 GNUNET_TRANSPORT_ps2s (n->state));
3787 return GNUNET_TIME_UNIT_FOREVER_REL;
3792 * Obtain current address information for the given neighbour.
3795 * @return address currently used
3797 struct GNUNET_HELLO_Address *
3798 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3800 struct NeighbourMapEntry *n;
3802 n = lookup_neighbour (peer);
3805 return n->primary_address.address;
3810 * Initialize the neighbours subsystem.
3812 * @param cls closure for callbacks
3813 * @param connect_cb function to call if we connect to a peer
3814 * @param disconnect_cb function to call if we disconnect from a peer
3815 * @param peer_address_cb function to call if we change an active address
3817 * @param max_fds maximum number of fds to use
3820 GST_neighbours_start (void *cls,
3821 NotifyConnect connect_cb,
3822 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3823 GNUNET_TRANSPORT_NeighbourChangeCallback peer_address_cb,
3824 unsigned int max_fds)
3827 connect_notify_cb = connect_cb;
3828 disconnect_notify_cb = disconnect_cb;
3829 neighbour_change_cb = peer_address_cb;
3830 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3831 registered_quota_notifications = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3832 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3833 utilization_transmission, NULL);
3838 * Disconnect from the given neighbour.
3841 * @param key hash of neighbour's public key (not used)
3842 * @param value the 'struct NeighbourMapEntry' of the neighbour
3843 * @return #GNUNET_OK (continue to iterate)
3846 disconnect_all_neighbours (void *cls,
3847 const struct GNUNET_PeerIdentity *key,
3850 struct NeighbourMapEntry *n = value;
3852 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3853 "Disconnecting peer `%4s', %s\n",
3854 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3855 set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
3856 free_neighbour (n, GNUNET_NO);
3862 * Cleanup the neighbours subsystem.
3865 GST_neighbours_stop ()
3867 struct BlacklistCheckSwitchContext *cur;
3868 struct BlacklistCheckSwitchContext *next;
3870 if (NULL == neighbours)
3872 if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3874 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3875 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3878 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3879 &disconnect_all_neighbours,
3881 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3883 next = pending_bc_head;
3884 for (cur = next; NULL != cur; cur = next )
3887 GNUNET_CONTAINER_DLL_remove (pending_bc_head, pending_bc_tail, cur);
3889 if (NULL != cur->blc)
3891 GST_blacklist_test_cancel (cur->blc);
3894 if (NULL != cur->address)
3895 GNUNET_HELLO_address_free (cur->address);
3896 GNUNET_free_non_null (cur->ats);
3900 GNUNET_CONTAINER_multipeermap_iterate (registered_quota_notifications,
3901 &free_notification_cb, NULL);
3902 GNUNET_CONTAINER_multipeermap_destroy (registered_quota_notifications);
3903 registered_quota_notifications = NULL;
3906 callback_cls = NULL;
3907 connect_notify_cb = NULL;
3908 disconnect_notify_cb = NULL;
3909 neighbour_change_cb = NULL;
3913 /* end of file gnunet-service-transport_neighbours.c */