2 This file is part of GNUnet.
3 Copyright (C) 2010-2015 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, 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_ats.h"
29 #include "gnunet-service-transport_blacklist.h"
30 #include "gnunet-service-transport_clients.h"
31 #include "gnunet-service-transport_neighbours.h"
32 #include "gnunet-service-transport_manipulation.h"
33 #include "gnunet-service-transport_plugins.h"
34 #include "gnunet-service-transport_validation.h"
35 #include "gnunet-service-transport.h"
36 #include "gnunet_peerinfo_service.h"
37 #include "gnunet_constants.h"
38 #include "transport.h"
41 * Experimental option to ignore SessionQuotaMessages from
44 #define IGNORE_INBOUND_QUOTA GNUNET_NO
47 * Size of the neighbour hash map.
49 #define NEIGHBOUR_TABLE_SIZE 256
52 * Time we give plugin to transmit DISCONNECT message before the
53 * neighbour entry self-destructs.
55 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
58 * How often must a peer violate bandwidth quotas before we start
59 * to simply drop its messages?
61 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
64 * How long are we willing to wait for a response from ATS before timing out?
66 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
69 * How long are we willing to wait for an ACK from the other peer before
70 * giving up on our connect operation?
72 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
75 * How long are we willing to wait for a successful reconnect if
76 * an existing connection went down? Much shorter than the
77 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
78 * higher layers about the disconnect during this period.
80 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS
83 * Interval to send utilization data
85 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS
88 * State describing which kind a reply this neighbour should send
93 * We did not receive a SYN message for this neighbour
98 * The neighbour received a SYN message and has to send a SYN_ACK
101 ACK_SEND_SYN_ACK = 1,
104 * The neighbour sent a SYN_ACK message and has to send a ACK
111 GNUNET_NETWORK_STRUCT_BEGIN
114 * Message a peer sends to another to indicate that it intends to
115 * setup a connection/session for data exchange. A 'SESSION_SYN'
116 * should be answered with a 'SESSION_SYN_ACK' with the same body
117 * to confirm. A 'SESSION_SYN_ACK' should then be followed with
118 * a 'ACK'. Once the 'ACK' is received, both peers
119 * should be connected.
121 struct TransportSynMessage
124 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN
125 * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK
127 struct GNUNET_MessageHeader header;
132 uint32_t reserved GNUNET_PACKED;
135 * Absolute time at the sender. Only the most recent connect
136 * message implies which session is preferred by the sender.
138 struct GNUNET_TIME_AbsoluteNBO timestamp;
144 * Message a peer sends to another when connected to indicate that a
145 * session is in use and the peer is still alive or to respond to a keep alive.
146 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
147 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
148 * When the keep alive response with type is received, transport service
149 * will call the respective plugin to update the session timeout
151 struct GNUNET_ATS_SessionKeepAliveMessage
154 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
155 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
157 struct GNUNET_MessageHeader header;
160 * A nonce to identify the session the keep alive is used for
162 uint32_t nonce GNUNET_PACKED;
167 * Message a peer sends to another when connected to indicate that
168 * the other peer should limit transmissions to the indicated
171 struct GNUNET_ATS_SessionQuotaMessage
174 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA.
176 struct GNUNET_MessageHeader header;
179 * Quota to use (for sending), in bytes per second.
181 uint32_t quota GNUNET_PACKED;
186 * Message we send to the other peer to notify him that we intentionally
187 * are disconnecting (to reduce timeouts). This is just a friendly
188 * notification, peers must not rely on always receiving disconnect
191 struct GNUNET_ATS_SessionDisconnectMessage
194 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
196 struct GNUNET_MessageHeader header;
201 uint32_t reserved GNUNET_PACKED;
204 * Purpose of the signature. Extends over the timestamp.
205 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
207 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
210 * Absolute time at the sender. Only the most recent connect
211 * message implies which session is preferred by the sender.
213 struct GNUNET_TIME_AbsoluteNBO timestamp;
216 * Public key of the sender.
218 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
221 * Signature of the peer that sends us the disconnect. Only
222 * valid if the timestamp is AFTER the timestamp from the
223 * corresponding 'SYN' message.
225 struct GNUNET_CRYPTO_EddsaSignature signature;
229 GNUNET_NETWORK_STRUCT_END
233 * For each neighbour we keep a list of messages
234 * that we still want to transmit to the neighbour.
240 * This is a doubly linked list.
242 struct MessageQueue *next;
245 * This is a doubly linked list.
247 struct MessageQueue *prev;
250 * Function to call once we're done.
252 GST_NeighbourSendContinuation cont;
255 * Closure for @e cont
260 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
261 * stuck together in memory. Allocated at the end of this struct.
263 const char *message_buf;
266 * Size of the message buf
268 size_t message_buf_size;
271 * At what time should we fail?
273 struct GNUNET_TIME_Absolute timeout;
279 * A possible address we could use to communicate with a neighbour.
281 struct NeighbourAddress
285 * Active session for this address.
287 struct GNUNET_ATS_Session *session;
290 * Network-level address information.
292 struct GNUNET_HELLO_Address *address;
295 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
296 * peer for this address. Use to check that the ACK is in response
297 * to our most recent 'SYN'.
299 struct GNUNET_TIME_Absolute connect_timestamp;
302 * Inbound bandwidth from ATS for this address.
304 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
307 * Outbound bandwidth from ATS for this address.
309 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
312 * Did we tell ATS that this is our 'active' address?
317 * The current nonce sent in the last keep alive messages
319 uint32_t keep_alive_nonce;
324 * Entry in neighbours.
326 struct NeighbourMapEntry
330 * Head of list of messages we would like to send to this peer;
331 * must contain at most one message per client.
333 struct MessageQueue *messages_head;
336 * Tail of list of messages we would like to send to this peer; must
337 * contain at most one message per client.
339 struct MessageQueue *messages_tail;
342 * Are we currently trying to send a message? If so, which one?
344 struct MessageQueue *is_active;
347 * Primary address we currently use to communicate with the neighbour.
349 struct NeighbourAddress primary_address;
352 * Alternative address currently under consideration for communicating
353 * with the neighbour.
355 struct NeighbourAddress alternative_address;
358 * Identity of this neighbour.
360 struct GNUNET_PeerIdentity id;
363 * Main task that drives this peer (timeouts, keepalives, etc.).
364 * Always runs the #master_task().
366 struct GNUNET_SCHEDULER_Task *task;
369 * Task to disconnect neighbour after we received a DISCONNECT message
371 struct GNUNET_SCHEDULER_Task *delayed_disconnect_task;
374 * At what time should we sent the next keep-alive message?
376 struct GNUNET_TIME_Absolute keep_alive_time;
379 * At what time did we sent the last keep-alive message? Used
380 * to calculate round-trip time ("latency").
382 struct GNUNET_TIME_Absolute last_keep_alive_time;
385 * Timestamp we should include in our next SYN_ACK message.
386 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
387 * our SYN_ACK message.
389 struct GNUNET_TIME_Absolute connect_ack_timestamp;
392 * ATS address suggest handle
394 struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle;
397 * Time where we should cut the connection (timeout) if we don't
398 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
399 * if we are in #GNUNET_TRANSPORT_PS_CONNECTED).
401 struct GNUNET_TIME_Absolute timeout;
404 * Tracker for inbound bandwidth.
406 struct GNUNET_BANDWIDTH_Tracker in_tracker;
409 * How often has the other peer (recently) violated the inbound
410 * traffic limit? Incremented by 10 per violation, decremented by 1
411 * per non-violation (for each time interval).
413 unsigned int quota_violation_count;
416 * Latest quota the other peer send us in bytes per second.
417 * We should not send more, least the other peer throttle
418 * receiving our traffic.
420 struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota;
423 * The current state of the peer.
425 enum GNUNET_TRANSPORT_PeerState state;
428 * Did we sent an KEEP_ALIVE message and are we expecting a response?
430 int expect_latency_response;
433 * When a peer wants to connect we have to reply to the 1st SYN message
434 * with a SYN_ACK message. But sometime we cannot send this message
435 * immediately since we do not have an address and then we have to remember
436 * to send this message as soon as we have an address.
438 * Flag to set if we still need to send a SYN_ACK message to the other peer
439 * (once we have an address to use and the peer has been allowed by our
440 * blacklist). Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK
441 * if we need to send a SYN_ACK. Set to #ACK_SEND_ACK if we did
442 * send a SYN_ACK and should go to #S_CONNECTED upon receiving a
443 * 'ACK' (regardless of what our own state machine might say).
445 enum GST_ACK_State ack_state;
448 * Tracking utilization of outbound bandwidth
450 uint32_t util_total_bytes_sent;
453 * Tracking utilization of inbound bandwidth
455 uint32_t util_total_bytes_recv;
458 * Date of last utilization transmission
460 struct GNUNET_TIME_Absolute last_util_transmission;
465 * Context for blacklist checks and the #try_connect_bl_check_cont()
466 * function. Stores information about ongoing blacklist checks.
468 struct BlackListCheckContext
472 * We keep blacklist checks in a DLL.
474 struct BlackListCheckContext *next;
477 * We keep blacklist checks in a DLL.
479 struct BlackListCheckContext *prev;
482 * Address that is being checked.
484 struct NeighbourAddress na;
487 * Handle to the ongoing blacklist check.
489 struct GST_BlacklistCheck *bc;
494 * Hash map from peer identities to the respective `struct NeighbourMapEntry`.
496 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
499 * We keep blacklist checks in a DLL so that we can find
500 * the 'sessions' in their 'struct NeighbourAddress' if
501 * a session goes down.
503 static struct BlackListCheckContext *bc_head;
506 * We keep blacklist checks in a DLL.
508 static struct BlackListCheckContext *bc_tail;
511 * List of pending blacklist checks: head
513 static struct BlacklistCheckSwitchContext *pending_bc_head;
516 * List of pending blacklist checks: tail
518 static struct BlacklistCheckSwitchContext *pending_bc_tail;
521 * counter for connected neighbours
523 static unsigned int neighbours_connected;
526 * Number of bytes we have currently queued for transmission.
528 static unsigned long long bytes_in_send_queue;
531 * Task transmitting utilization data
533 static struct GNUNET_SCHEDULER_Task *util_transmission_tk;
537 * Convert the given ACK state to a string.
540 * @return corresponding human-readable string
543 print_ack_state (enum GST_ACK_State s)
548 case ACK_SEND_SYN_ACK:
549 return "SEND_SYN_ACK";
560 * Notify our clients that another peer connected to us.
562 * @param n the peer that connected
565 neighbours_connect_notification (struct NeighbourMapEntry *n)
567 size_t len = sizeof(struct ConnectInfoMessage);
568 char buf[len] GNUNET_ALIGN;
569 struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
570 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
572 #if IGNORE_INBOUND_QUOTA
573 bandwidth_min = n->primary_address.bandwidth_out;
575 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
576 n->neighbour_receive_quota);
578 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579 "We are now connected to peer `%s'\n",
580 GNUNET_i2s (&n->id));
581 connect_msg->header.size = htons (sizeof(buf));
582 connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
583 connect_msg->id = n->id;
584 connect_msg->quota_in = n->primary_address.bandwidth_in;
585 connect_msg->quota_out = bandwidth_min;
586 GST_clients_broadcast (&connect_msg->header,
592 * Notify our clients (and manipulation) that a peer disconnected from
595 * @param n the peer that disconnected
598 neighbours_disconnect_notification (struct NeighbourMapEntry *n)
600 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
601 "Peer `%s' disconnected\n",
602 GNUNET_i2s (&n->id));
603 GST_manipulation_peer_disconnect (&n->id);
604 GST_clients_broadcast_disconnect (&n->id);
609 * Notify transport clients that a neighbour peer changed its active
612 * @param peer identity of the peer
613 * @param address address possibly NULL if peer is not connected
614 * @param state current state this peer is in
615 * @param state_timeout timeout for the current state of the peer
616 * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
617 * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
620 neighbours_changed_notification (const struct GNUNET_PeerIdentity *peer,
621 const struct GNUNET_HELLO_Address *address,
622 enum GNUNET_TRANSPORT_PeerState state,
623 struct GNUNET_TIME_Absolute state_timeout,
624 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
625 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628 "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
630 GST_plugins_a2s (address),
631 GNUNET_TRANSPORT_ps2s (state),
632 GNUNET_STRINGS_absolute_time_to_string (state_timeout));
633 /* FIXME: include bandwidth in notification! */
634 GST_clients_broadcast_peer_notification (peer,
642 * Lookup a neighbour entry in the neighbours hash map.
644 * @param pid identity of the peer to look up
645 * @return the entry, NULL if there is no existing record
647 static struct NeighbourMapEntry *
648 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
650 if (NULL == neighbours)
652 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
657 * Test if we're connected to the given peer.
659 * @param n neighbour entry of peer to test
660 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
663 test_connected (struct NeighbourMapEntry *n)
667 return GNUNET_TRANSPORT_is_connected (n->state);
672 * Send information about a new outbound quota to our clients.
673 * Note that the outbound quota is enforced client-side (i.e.
674 * in libgnunettransport).
676 * @param n affected peer
679 send_outbound_quota_to_clients (struct NeighbourMapEntry *n)
681 struct QuotaSetMessage q_msg;
682 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
684 if (! GNUNET_TRANSPORT_is_connected (n->state))
686 #if IGNORE_INBOUND_QUOTA
687 bandwidth_min = n->primary_address.bandwidth_out;
689 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
690 n->neighbour_receive_quota);
693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
694 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
695 ntohl (bandwidth_min.value__),
696 GNUNET_i2s (&n->id));
697 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
698 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
699 q_msg.quota = bandwidth_min;
701 GST_clients_broadcast (&q_msg.header, GNUNET_NO);
706 * We don't need a given neighbour address any more.
707 * Release its resources and give appropriate notifications
708 * to ATS and other subsystems.
710 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
713 free_address (struct NeighbourAddress *na)
715 if (GNUNET_YES == na->ats_active)
716 GST_validation_set_address_use (na->address,
718 if (NULL != na->address)
720 GST_ats_block_address (na->address,
722 GNUNET_HELLO_address_free (na->address);
725 na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
726 na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
727 na->ats_active = GNUNET_NO;
728 na->keep_alive_nonce = 0;
734 * Master task run for every neighbour. Performs all of the time-related
735 * activities (keep alive, send next message, disconnect if idle, finish
736 * clean up after disconnect).
738 * @param cls the `struct NeighbourMapEntry` for which we are running
739 * @param tc scheduler context (unused)
742 master_task (void *cls,
743 const struct GNUNET_SCHEDULER_TaskContext *tc);
747 * Set net state and state timeout for this neighbour and notify monitoring
749 * @param n the respective neighbour
750 * @param s the new state
751 * @param timeout the new timeout
754 set_state_and_timeout (struct NeighbourMapEntry *n,
755 enum GNUNET_TRANSPORT_PeerState s,
756 struct GNUNET_TIME_Absolute timeout)
758 if (GNUNET_TRANSPORT_is_connected (s) &&
759 ! GNUNET_TRANSPORT_is_connected (n->state) )
761 neighbours_connect_notification (n);
762 GNUNET_STATISTICS_set (GST_stats,
763 gettext_noop ("# peers connected"),
764 ++neighbours_connected,
767 if (! GNUNET_TRANSPORT_is_connected (s) &&
768 GNUNET_TRANSPORT_is_connected (n->state) )
770 GNUNET_STATISTICS_set (GST_stats,
771 gettext_noop ("# peers connected"),
772 --neighbours_connected,
774 neighbours_disconnect_notification (n);
777 if ( (timeout.abs_value_us < n->timeout.abs_value_us) &&
780 /* new timeout is earlier, reschedule master task */
781 GNUNET_SCHEDULER_cancel (n->task);
782 n->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (timeout),
786 n->timeout = timeout;
787 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
788 "Neighbour `%s' changed state to %s with timeout %s\n",
790 GNUNET_TRANSPORT_ps2s(s),
791 GNUNET_STRINGS_absolute_time_to_string (timeout));
792 neighbours_changed_notification (&n->id,
793 n->primary_address.address,
796 n->primary_address.bandwidth_in,
797 n->primary_address.bandwidth_out);
802 * Initialize the alternative address of a neighbour
804 * @param n the neighbour
805 * @param address address of the other peer, NULL if other peer
807 * @param session session to use (or NULL, in which case an
808 * address must be setup)
809 * @param bandwidth_in inbound quota to be used when connection is up
810 * @param bandwidth_out outbound quota to be used when connection is up
813 set_alternative_address (struct NeighbourMapEntry *n,
814 const struct GNUNET_HELLO_Address *address,
815 struct GNUNET_ATS_Session *session,
816 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
817 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
819 struct GNUNET_TRANSPORT_PluginFunctions *papi;
821 if (NULL == (papi = GST_plugins_find (address->transport_name)))
826 if (session == n->alternative_address.session)
828 n->alternative_address.bandwidth_in = bandwidth_in;
829 n->alternative_address.bandwidth_out = bandwidth_out;
832 if (NULL != n->alternative_address.address)
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835 "Replacing existing alternative address with another one\n");
836 free_address (&n->alternative_address);
839 session = papi->get_session (papi->cls,
843 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844 "Failed to obtain new session for peer `%s' and address '%s'\n",
845 GNUNET_i2s (&address->peer),
846 GST_plugins_a2s (address));
847 GNUNET_STATISTICS_update (GST_stats,
848 gettext_noop ("# session creation failed"),
853 GST_ats_new_session (address,
855 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
856 "Neighbour `%s' configured alternative address %s\n",
858 GST_plugins_a2s(address));
860 n->alternative_address.address = GNUNET_HELLO_address_copy (address);
861 n->alternative_address.bandwidth_in = bandwidth_in;
862 n->alternative_address.bandwidth_out = bandwidth_out;
863 n->alternative_address.session = session;
864 n->alternative_address.ats_active = GNUNET_NO;
865 n->alternative_address.keep_alive_nonce = 0;
866 GNUNET_assert (GNUNET_YES ==
867 GST_ats_is_known (n->alternative_address.address,
868 n->alternative_address.session));
873 * Transmit a message using the current session of the given
876 * @param n entry for the recipient
877 * @param msgbuf buffer to transmit
878 * @param msgbuf_size number of bytes in @a msgbuf buffer
879 * @param priority transmission priority
880 * @param timeout transmission timeout
881 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
882 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
883 * @param cont continuation to call when finished (can be NULL)
884 * @param cont_cls closure for @a cont
885 * @return timeout (copy of @a timeout or a calculated one if
886 * @a use_keepalive_timeout is #GNUNET_YES.
888 static struct GNUNET_TIME_Relative
889 send_with_session (struct NeighbourMapEntry *n,
893 struct GNUNET_TIME_Relative timeout,
894 unsigned int use_keepalive_timeout,
895 GNUNET_TRANSPORT_TransmitContinuation cont,
898 struct GNUNET_TRANSPORT_PluginFunctions *papi;
899 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
901 GNUNET_assert (NULL != n->primary_address.session);
902 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
903 (-1 == papi->send (papi->cls,
904 n->primary_address.session,
908 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
909 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
910 papi->query_keepalive_factor (papi->cls))),
919 GST_neighbours_notify_data_sent (n->primary_address.address,
920 n->primary_address.session,
922 GNUNET_break (NULL != papi);
928 * Clear the primary address of a neighbour since this address is not
929 * valid anymore and notify monitoring about it
931 * @param n the neighbour
934 unset_primary_address (struct NeighbourMapEntry *n)
936 /* Notify monitoring about change */
937 if (NULL == n->primary_address.address)
939 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
940 "Disabling primary address\n");
941 neighbours_changed_notification (&n->id,
942 n->primary_address.address,
945 GNUNET_BANDWIDTH_value_init (0),
946 GNUNET_BANDWIDTH_value_init (0));
947 free_address (&n->primary_address);
952 * Free a neighbour map entry.
954 * @param n entry to free
957 free_neighbour (struct NeighbourMapEntry *n)
959 struct MessageQueue *mq;
961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 "Freeing neighbour state of peer `%s'\n",
963 GNUNET_i2s (&n->id));
964 n->is_active = NULL; /* always free'd by its own continuation! */
966 /* fail messages currently in the queue */
967 while (NULL != (mq = n->messages_head))
969 GNUNET_CONTAINER_DLL_remove (n->messages_head,
972 if (NULL != mq->cont)
973 mq->cont (mq->cont_cls,
975 mq->message_buf_size,
979 /* Mark peer as disconnected */
980 set_state_and_timeout (n,
981 GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED,
982 GNUNET_TIME_UNIT_FOREVER_ABS);
983 /* free addresses and mark as unused */
984 unset_primary_address (n);
986 if (NULL != n->alternative_address.address)
988 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
989 "Cleaning up alternative address\n");
990 free_address (&n->alternative_address);
992 GNUNET_assert (GNUNET_YES ==
993 GNUNET_CONTAINER_multipeermap_remove (neighbours,
996 /* Cancel address requests for this peer */
997 if (NULL != n->suggest_handle)
999 GNUNET_ATS_connectivity_suggest_cancel (n->suggest_handle);
1000 n->suggest_handle = NULL;
1003 /* Cancel the disconnect task */
1004 if (NULL != n->delayed_disconnect_task)
1006 GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task);
1007 n->delayed_disconnect_task = NULL;
1010 /* Cancel the master task */
1011 if (NULL != n->task)
1013 GNUNET_SCHEDULER_cancel (n->task);
1016 /* free rest of memory */
1022 * Function called when the 'DISCONNECT' message has been sent by the
1023 * plugin. Frees the neighbour --- if the entry still exists.
1026 * @param target identity of the neighbour that was disconnected
1027 * @param result #GNUNET_OK if the disconnect got out successfully
1028 * @param payload bytes payload
1029 * @param physical bytes on wire
1032 send_disconnect_cont (void *cls,
1033 const struct GNUNET_PeerIdentity *target,
1038 struct NeighbourMapEntry *n;
1040 n = lookup_neighbour (target);
1042 return; /* already gone */
1043 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
1044 return; /* have created a fresh entry since */
1045 if (NULL != n->task)
1046 GNUNET_SCHEDULER_cancel (n->task);
1047 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1052 * Transmit a DISCONNECT message to the other peer.
1054 * @param n neighbour to send DISCONNECT message.
1057 send_disconnect (struct NeighbourMapEntry *n)
1059 struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg;
1061 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1062 "Sending DISCONNECT message to peer `%4s'\n",
1063 GNUNET_i2s (&n->id));
1064 disconnect_msg.header.size = htons (sizeof (struct GNUNET_ATS_SessionDisconnectMessage));
1065 disconnect_msg.header.type =
1066 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1067 disconnect_msg.reserved = htonl (0);
1068 disconnect_msg.purpose.size =
1069 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1070 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1071 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1072 disconnect_msg.purpose.purpose =
1073 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1074 disconnect_msg.timestamp =
1075 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1076 disconnect_msg.public_key = GST_my_identity.public_key;
1077 GNUNET_assert (GNUNET_OK ==
1078 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1079 &disconnect_msg.purpose,
1080 &disconnect_msg.signature));
1082 (void) send_with_session (n,
1084 sizeof (disconnect_msg),
1086 GNUNET_TIME_UNIT_FOREVER_REL,
1088 &send_disconnect_cont,
1090 GNUNET_STATISTICS_update (GST_stats,
1091 gettext_noop ("# DISCONNECT messages sent"),
1098 * Disconnect from the given neighbour, clean up the record.
1100 * @param n neighbour to disconnect from
1103 disconnect_neighbour (struct NeighbourMapEntry *n)
1105 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1106 "Disconnecting from peer %s in state %s\n",
1107 GNUNET_i2s (&n->id),
1108 GNUNET_TRANSPORT_ps2s (n->state));
1109 /* depending on state, notify neighbour and/or upper layers of this peer
1113 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1114 case GNUNET_TRANSPORT_PS_INIT_ATS:
1115 /* other peer is completely unaware of us, no need to send DISCONNECT */
1118 case GNUNET_TRANSPORT_PS_SYN_SENT:
1119 send_disconnect (n);
1120 set_state_and_timeout (n,
1121 GNUNET_TRANSPORT_PS_DISCONNECT,
1122 GNUNET_TIME_UNIT_FOREVER_ABS);
1124 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
1125 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1128 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
1129 /* we DID ACK the other peer's request, must send DISCONNECT */
1130 send_disconnect (n);
1131 set_state_and_timeout (n,
1132 GNUNET_TRANSPORT_PS_DISCONNECT,
1133 GNUNET_TIME_UNIT_FOREVER_ABS);
1135 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1136 case GNUNET_TRANSPORT_PS_CONNECTED:
1137 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1138 /* we are currently connected, need to send disconnect and do
1139 internal notifications and update statistics */
1140 send_disconnect (n);
1141 set_state_and_timeout (n,
1142 GNUNET_TRANSPORT_PS_DISCONNECT,
1143 GNUNET_TIME_UNIT_FOREVER_ABS);
1145 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1146 /* Disconnecting while waiting for an ATS address to reconnect,
1147 * cannot send DISCONNECT */
1150 case GNUNET_TRANSPORT_PS_DISCONNECT:
1151 /* already disconnected, ignore */
1153 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1154 /* already cleaned up, how did we get here!? */
1158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1159 "Unhandled state `%s'\n",
1160 GNUNET_TRANSPORT_ps2s (n->state));
1164 /* schedule timeout to clean up */
1165 if (NULL != n->task)
1166 GNUNET_SCHEDULER_cancel (n->task);
1167 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1174 * Change the incoming quota for the given peer. Updates
1175 * our own receive rate and informs the neighbour about
1178 * @param n neighbour entry to change qutoa for
1179 * @param quota new quota
1182 set_incoming_quota (struct NeighbourMapEntry *n,
1183 struct GNUNET_BANDWIDTH_Value32NBO quota)
1185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1186 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
1187 ntohl (quota.value__), GNUNET_i2s (&n->id));
1188 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
1190 if (0 != ntohl (quota.value__))
1192 struct GNUNET_ATS_SessionQuotaMessage sqm;
1194 sqm.header.size = htons (sizeof (struct GNUNET_ATS_SessionQuotaMessage));
1195 sqm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA);
1196 sqm.quota = quota.value__;
1197 (void) send_with_session (n,
1201 GNUNET_TIME_UNIT_FOREVER_REL,
1206 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1207 "Disconnecting peer `%4s' due to SET_QUOTA\n",
1208 GNUNET_i2s (&n->id));
1209 if (GNUNET_YES == test_connected (n))
1210 GNUNET_STATISTICS_update (GST_stats,
1211 gettext_noop ("# disconnects due to quota of 0"),
1213 disconnect_neighbour (n);
1218 * Initialize the primary address of a neighbour
1220 * @param n the neighbour
1221 * @param address address of the other peer, NULL if other peer
1223 * @param session session to use (or NULL, in which case an
1224 * address must be setup)
1225 * @param bandwidth_in inbound quota to be used when connection is up
1226 * @param bandwidth_out outbound quota to be used when connection is up
1229 set_primary_address (struct NeighbourMapEntry *n,
1230 const struct GNUNET_HELLO_Address *address,
1231 struct GNUNET_ATS_Session *session,
1232 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1233 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
1235 if (session == n->primary_address.session)
1237 GST_validation_set_address_use (n->primary_address.address,
1239 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
1241 n->primary_address.bandwidth_in = bandwidth_in;
1242 set_incoming_quota (n,
1245 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
1247 n->primary_address.bandwidth_out = bandwidth_out;
1248 send_outbound_quota_to_clients (n);
1252 if ( (NULL != n->primary_address.address) &&
1253 (0 == GNUNET_HELLO_address_cmp (address,
1254 n->primary_address.address)) )
1259 if (NULL == session)
1262 GST_ats_block_address (address,
1266 if (NULL != n->primary_address.address)
1268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269 "Replacing existing primary address with another one\n");
1270 free_address (&n->primary_address);
1272 n->primary_address.address = GNUNET_HELLO_address_copy (address);
1273 n->primary_address.bandwidth_in = bandwidth_in;
1274 n->primary_address.bandwidth_out = bandwidth_out;
1275 n->primary_address.session = session;
1276 n->primary_address.keep_alive_nonce = 0;
1277 GNUNET_assert (GNUNET_YES ==
1278 GST_ats_is_known (n->primary_address.address,
1279 n->primary_address.session));
1280 /* subsystems about address use */
1281 GST_validation_set_address_use (n->primary_address.address,
1283 set_incoming_quota (n,
1285 send_outbound_quota_to_clients (n);
1286 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1287 "Neighbour `%s' switched to address `%s'\n",
1288 GNUNET_i2s (&n->id),
1289 GST_plugins_a2s(address));
1291 neighbours_changed_notification (&n->id,
1292 n->primary_address.address,
1295 n->primary_address.bandwidth_in,
1296 n->primary_address.bandwidth_out);
1301 * We're done with our transmission attempt, continue processing.
1303 * @param cls the `struct MessageQueue` of the message
1304 * @param receiver intended receiver
1305 * @param success whether it worked or not
1306 * @param size_payload bytes payload sent
1307 * @param physical bytes sent on wire
1310 transmit_send_continuation (void *cls,
1311 const struct GNUNET_PeerIdentity *receiver,
1313 size_t size_payload,
1316 struct MessageQueue *mq = cls;
1317 struct NeighbourMapEntry *n;
1319 if (NULL == (n = lookup_neighbour (receiver)))
1321 if (NULL != mq->cont)
1322 mq->cont (mq->cont_cls,
1323 GNUNET_SYSERR /* not connected */,
1327 return; /* disconnect or other error while transmitting, can happen */
1329 if (n->is_active == mq)
1331 /* this is still "our" neighbour, remove us from its queue
1332 and allow it to send the next message now */
1333 n->is_active = NULL;
1334 if (NULL != n->task)
1335 GNUNET_SCHEDULER_cancel (n->task);
1336 n->task = GNUNET_SCHEDULER_add_now (&master_task,
1339 if (bytes_in_send_queue < mq->message_buf_size)
1341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1342 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1343 bytes_in_send_queue,
1344 mq->message_buf_size,
1345 (GNUNET_OK == success) ? "OK" : "FAIL",
1351 GNUNET_break (size_payload == mq->message_buf_size);
1352 bytes_in_send_queue -= mq->message_buf_size;
1353 GNUNET_STATISTICS_set (GST_stats,
1354 gettext_noop ("# bytes in message queue for other peers"),
1355 bytes_in_send_queue,
1357 if (GNUNET_OK == success)
1358 GNUNET_STATISTICS_update (GST_stats,
1359 gettext_noop ("# messages transmitted to other peers"),
1363 GNUNET_STATISTICS_update (GST_stats,
1365 ("# transmission failures for messages to other peers"),
1367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1368 "Sending message to `%s' of type %u with %u bytes was a %s\n",
1369 GNUNET_i2s (receiver),
1370 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1371 mq->message_buf_size,
1372 (success == GNUNET_OK) ? "success" : "FAILURE");
1373 if (NULL != mq->cont)
1374 mq->cont (mq->cont_cls,
1383 * Check the message list for the given neighbour and if we can
1384 * send a message, do so. This function should only be called
1385 * if the connection is at least generally ready for transmission.
1386 * While we will only send one message at a time, no bandwidth
1387 * quota management is performed here. If a message was given to
1388 * the plugin, the continuation will automatically re-schedule
1389 * the 'master' task once the next message might be transmitted.
1391 * @param n target peer for which to transmit
1394 try_transmission_to_peer (struct NeighbourMapEntry *n)
1396 struct MessageQueue *mq;
1397 struct GNUNET_TIME_Relative timeout;
1399 if (NULL == n->primary_address.address)
1401 /* no address, why are we here? */
1405 if ((0 == n->primary_address.address->address_length) &&
1406 (NULL == n->primary_address.session))
1408 /* no address, why are we here? */
1412 if (NULL != n->is_active)
1414 /* transmission already pending */
1418 /* timeout messages from the queue that are past their due date */
1419 while (NULL != (mq = n->messages_head))
1421 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1422 if (timeout.rel_value_us > 0)
1424 GNUNET_STATISTICS_update (GST_stats,
1425 gettext_noop ("# messages timed out while in transport queue"),
1428 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1432 transmit_send_continuation (mq,
1435 mq->message_buf_size,
1439 return; /* no more messages */
1440 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1446 "Giving message with %u bytes to plugin session %p\n",
1447 mq->message_buf_size,
1448 n->primary_address.session);
1449 (void) send_with_session (n,
1451 mq->message_buf_size,
1455 &transmit_send_continuation,
1461 * Send keepalive message to the neighbour. Must only be called
1462 * if we are on 'connected' state or while trying to switch addresses.
1463 * Will internally determine if a keepalive is truly needed (so can
1464 * always be called).
1466 * @param n neighbour that went idle and needs a keepalive
1469 send_keepalive (struct NeighbourMapEntry *n)
1471 struct GNUNET_ATS_SessionKeepAliveMessage m;
1472 struct GNUNET_TIME_Relative timeout;
1475 GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1476 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state));
1477 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1478 return; /* no keepalive needed at this time */
1480 nonce = 0; /* 0 indicates 'not set' */
1482 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1486 "Sending KEEPALIVE to peer `%s' with nonce %u\n",
1487 GNUNET_i2s (&n->id),
1489 m.header.size = htons (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage));
1490 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1491 m.nonce = htonl (nonce);
1493 timeout = send_with_session (n,
1496 UINT32_MAX /* priority */,
1497 GNUNET_TIME_UNIT_FOREVER_REL,
1500 GNUNET_STATISTICS_update (GST_stats,
1501 gettext_noop ("# KEEPALIVES sent"),
1504 n->primary_address.keep_alive_nonce = nonce;
1505 n->expect_latency_response = GNUNET_YES;
1506 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1507 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1512 * Keep the connection to the given neighbour alive longer,
1513 * we received a KEEPALIVE (or equivalent); send a response.
1515 * @param neighbour neighbour to keep alive (by sending keep alive response)
1516 * @param m the keep alive message containing the nonce to respond to
1519 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1520 const struct GNUNET_MessageHeader *m)
1522 struct NeighbourMapEntry *n;
1523 const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in;
1524 struct GNUNET_ATS_SessionKeepAliveMessage msg;
1526 if (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1528 GNUNET_break_op (0);
1532 msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1533 if (NULL == (n = lookup_neighbour (neighbour)))
1535 GNUNET_STATISTICS_update (GST_stats,
1537 ("# KEEPALIVE messages discarded (peer unknown)"),
1541 if (NULL == n->primary_address.session)
1543 GNUNET_STATISTICS_update (GST_stats,
1545 ("# KEEPALIVE messages discarded (no session)"),
1550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1551 "Received KEEPALIVE request from peer `%s' with nonce %u\n",
1552 GNUNET_i2s (&n->id),
1553 ntohl (msg_in->nonce));
1554 GNUNET_STATISTICS_update (GST_stats,
1555 gettext_noop ("# KEEPALIVES received in good order"),
1559 /* send reply to allow neighbour to measure latency */
1560 msg.header.size = htons (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage));
1561 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1562 msg.nonce = msg_in->nonce;
1563 (void) send_with_session (n,
1565 sizeof (struct GNUNET_ATS_SessionKeepAliveMessage),
1566 UINT32_MAX /* priority */,
1567 GNUNET_TIME_UNIT_FOREVER_REL,
1574 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1575 * latency to this peer. Pass the updated information (existing ats
1576 * plus calculated latency) to ATS.
1578 * @param neighbour neighbour to keep alive
1579 * @param m the message containing the keep alive response
1582 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1583 const struct GNUNET_MessageHeader *m)
1585 struct NeighbourMapEntry *n;
1586 const struct GNUNET_ATS_SessionKeepAliveMessage *msg;
1587 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1588 struct GNUNET_TIME_Relative latency;
1590 if (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1592 GNUNET_break_op (0);
1596 msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1597 if (NULL == (n = lookup_neighbour (neighbour)))
1599 GNUNET_STATISTICS_update (GST_stats,
1600 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (not connected)"),
1605 if ( (GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1606 (GNUNET_YES != n->expect_latency_response) )
1608 GNUNET_STATISTICS_update (GST_stats,
1609 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (not expected)"),
1614 if (NULL == n->primary_address.address)
1616 GNUNET_STATISTICS_update (GST_stats,
1617 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (address changed)"),
1622 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1624 if (0 == n->primary_address.keep_alive_nonce)
1625 GNUNET_STATISTICS_update (GST_stats,
1626 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (no nonce)"),
1630 GNUNET_STATISTICS_update (GST_stats,
1631 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (bad nonce)"),
1636 GNUNET_STATISTICS_update (GST_stats,
1637 gettext_noop ("# KEEPALIVE_RESPONSEs received (OK)"),
1642 /* Update session timeout here */
1643 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1645 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1646 "Updating session for peer `%s' for session %p\n",
1647 GNUNET_i2s (&n->id),
1648 n->primary_address.session);
1649 papi->update_session_timeout (papi->cls,
1651 n->primary_address.session);
1658 n->primary_address.keep_alive_nonce = 0;
1659 n->expect_latency_response = GNUNET_NO;
1660 set_state_and_timeout (n,
1662 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1664 latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1665 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1666 "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n",
1667 GNUNET_i2s (&n->id),
1668 GNUNET_STRINGS_relative_time_to_string (latency,
1670 GST_ats_update_delay (n->primary_address.address,
1671 GNUNET_TIME_relative_divide (latency,
1677 * We have received a message from the given sender. How long should
1678 * we delay before receiving more? (Also used to keep the peer marked
1681 * @param sender sender of the message
1682 * @param size size of the message
1683 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1684 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1685 * #GNUNET_SYSERR if the connection is not fully up yet
1686 * @return how long to wait before reading more from this sender
1688 struct GNUNET_TIME_Relative
1689 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity *sender,
1693 struct NeighbourMapEntry *n;
1694 struct GNUNET_TIME_Relative ret;
1696 if (NULL == neighbours)
1698 *do_forward = GNUNET_NO;
1699 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1701 if (NULL == (n = lookup_neighbour (sender)))
1703 GST_neighbours_try_connect (sender);
1704 if (NULL == (n = lookup_neighbour (sender)))
1706 GNUNET_STATISTICS_update (GST_stats,
1708 ("# messages discarded due to lack of neighbour record"),
1710 *do_forward = GNUNET_NO;
1711 return GNUNET_TIME_UNIT_ZERO;
1714 if (! test_connected (n))
1716 *do_forward = GNUNET_SYSERR;
1717 return GNUNET_TIME_UNIT_ZERO;
1719 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1721 n->quota_violation_count++;
1722 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1723 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1724 n->in_tracker.available_bytes_per_s__,
1725 n->quota_violation_count);
1726 /* Discount 32k per violation */
1727 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1731 if (n->quota_violation_count > 0)
1733 /* try to add 32k back */
1734 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1735 n->quota_violation_count--;
1738 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1740 GNUNET_STATISTICS_update (GST_stats,
1742 ("# bandwidth quota violations by other peers"),
1744 *do_forward = GNUNET_NO;
1745 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1747 *do_forward = GNUNET_YES;
1748 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1749 if (ret.rel_value_us > 0)
1751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1752 "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n",
1753 (long long) n->in_tracker.consumption_since_last_update__,
1754 (unsigned int) n->in_tracker.available_bytes_per_s__,
1755 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1756 GNUNET_STATISTICS_update (GST_stats,
1757 gettext_noop ("# ms throttling suggested"),
1758 (int64_t) ret.rel_value_us / 1000LL,
1766 * Transmit a message to the given target using the active connection.
1768 * @param target destination
1769 * @param msg message to send
1770 * @param msg_size number of bytes in msg
1771 * @param timeout when to fail with timeout
1772 * @param cont function to call when done
1773 * @param cont_cls closure for @a cont
1776 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
1779 struct GNUNET_TIME_Relative timeout,
1780 GST_NeighbourSendContinuation cont,
1783 struct NeighbourMapEntry *n;
1784 struct MessageQueue *mq;
1786 /* All ove these cases should never happen; they are all API violations.
1787 But we check anyway, just to be sure. */
1788 if (NULL == (n = lookup_neighbour (target)))
1798 if (GNUNET_YES != test_connected (n))
1808 bytes_in_send_queue += msg_size;
1809 GNUNET_STATISTICS_set (GST_stats,
1811 ("# bytes in message queue for other peers"),
1812 bytes_in_send_queue, GNUNET_NO);
1813 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1815 mq->cont_cls = cont_cls;
1816 memcpy (&mq[1], msg, msg_size);
1817 mq->message_buf = (const char *) &mq[1];
1818 mq->message_buf_size = msg_size;
1819 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1822 "Enqueueing %u bytes to send to peer %s\n",
1824 GNUNET_i2s (target));
1825 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
1828 if (NULL != n->task)
1829 GNUNET_SCHEDULER_cancel (n->task);
1830 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1835 * Continuation called from our attempt to transmitted our
1836 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1837 * target. Continue processing based on the @a result. Specifically,
1838 * if we failed to transmit, discard the address we used.
1841 * @param target which peer received the transmission
1842 * @param result #GNUNET_OK if sending worked
1843 * @param size_payload how many bytes of payload were sent (ignored)
1844 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1847 send_session_syn_cont (void *cls,
1848 const struct GNUNET_PeerIdentity *target,
1850 size_t size_payload,
1851 size_t size_on_wire)
1853 struct NeighbourMapEntry *n;
1855 n = lookup_neighbour (target);
1858 /* SYN continuation was called after neighbor was freed,
1859 * for example due to a time out for the state or the session
1860 * used was already terminated: nothing to do here... */
1864 if ( (GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1865 (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1866 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1868 /* SYN continuation was called after neighbor changed state,
1869 * for example due to a time out for the state or the session
1870 * used was already terminated: nothing to do here... */
1873 if (GNUNET_OK == result)
1876 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1877 _("Failed to send SYN message to peer `%s'\n"),
1878 GNUNET_i2s (target));
1880 case GNUNET_TRANSPORT_PS_SYN_SENT:
1881 /* Remove address and request an additional one */
1882 unset_primary_address (n);
1883 set_state_and_timeout (n,
1884 GNUNET_TRANSPORT_PS_INIT_ATS,
1885 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
1887 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1888 /* Remove address and request an additional one */
1889 unset_primary_address (n);
1890 set_state_and_timeout (n,
1891 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1892 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1894 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1895 /* Remove address and request and go back to primary address */
1896 GNUNET_STATISTICS_update (GST_stats,
1897 gettext_noop ("# Failed attempts to switch addresses (failed to send SYN CONT)"),
1900 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1901 "Switch failed, cleaning up alternative address\n");
1902 free_address (&n->alternative_address);
1903 set_state_and_timeout (n,
1904 GNUNET_TRANSPORT_PS_CONNECTED,
1905 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1908 disconnect_neighbour (n);
1915 * Send a SYN message via the given address.
1917 * @param na address to use
1920 send_syn (struct NeighbourAddress *na)
1922 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1923 struct TransportSynMessage connect_msg;
1924 struct NeighbourMapEntry *n;
1926 GNUNET_assert (NULL != na->session);
1927 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1928 "Sending SYN message to peer `%s' at %s\n",
1929 GNUNET_i2s (&na->address->peer),
1930 GST_plugins_a2s (na->address));
1932 papi = GST_plugins_find (na->address->transport_name);
1933 GNUNET_assert (NULL != papi);
1934 GNUNET_STATISTICS_update (GST_stats,
1936 ("# SYN messages sent"),
1938 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1939 connect_msg.header.size = htons (sizeof (struct TransportSynMessage));
1940 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1941 connect_msg.reserved = htonl (0);
1942 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1944 papi->send (papi->cls,
1946 (const char *) &connect_msg,
1947 sizeof (struct TransportSynMessage),
1949 SETUP_CONNECTION_TIMEOUT,
1950 &send_session_syn_cont, NULL))
1952 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1953 _("Failed to transmit SYN message to %s\n"),
1954 GST_plugins_a2s (na->address));
1955 n = lookup_neighbour (&na->address->peer);
1962 case GNUNET_TRANSPORT_PS_SYN_SENT:
1963 /* Remove address and request and additional one */
1964 GNUNET_assert (na == &n->primary_address);
1965 unset_primary_address (n);
1966 set_state_and_timeout (n,
1967 GNUNET_TRANSPORT_PS_INIT_ATS,
1968 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
1969 /* Hard failure to send the SYN message with this address:
1970 Destroy address and session */
1972 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1973 /* Remove address and request an additional one */
1974 GNUNET_assert (na == &n->primary_address);
1975 unset_primary_address (n);
1976 set_state_and_timeout (n,
1977 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1978 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1980 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1981 GNUNET_assert (na == &n->alternative_address);
1982 GNUNET_STATISTICS_update (GST_stats,
1983 gettext_noop ("# Failed attempts to switch addresses (failed to send SYN)"),
1986 /* Remove address and request an additional one */
1987 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1988 "Switch failed, cleaning up alternative address\n");
1989 free_address (&n->alternative_address);
1990 set_state_and_timeout (n,
1991 GNUNET_TRANSPORT_PS_CONNECTED,
1992 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1996 disconnect_neighbour (n);
2001 GST_neighbours_notify_data_sent (na->address,
2003 sizeof (struct TransportSynMessage));
2008 * Continuation called from our attempt to transmitted our
2009 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
2010 * target. Continue processing based on the @a result. Specifically,
2011 * if we failed to transmit, discard the address we used.
2014 * @param target which peer received the transmission
2015 * @param result #GNUNET_OK if sending worked
2016 * @param size_payload how many bytes of payload were sent (ignored)
2017 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
2020 send_session_syn_ack_cont (void *cls,
2021 const struct GNUNET_PeerIdentity *target,
2023 size_t size_payload,
2024 size_t size_on_wire)
2026 struct NeighbourMapEntry *n;
2028 n = lookup_neighbour (target);
2031 /* SYN_ACK continuation was called after neighbor was freed,
2032 * for example due to a time out for the state or the session
2033 * used was already terminated: nothing to do here... */
2037 if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2039 /* SYN_ACK continuation was called after neighbor changed state,
2040 * for example due to a time out for the state or the session
2041 * used was already terminated: nothing to do here... */
2044 if (GNUNET_OK == result)
2047 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2048 _("Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2049 GNUNET_i2s (target),
2050 GST_plugins_a2s (n->primary_address.address));
2052 /* Remove address and request and additional one */
2053 /* FIXME: what if the neighbour's primary address
2054 changed in the meantime? Might want to instead
2055 pass "something" around in closure to be sure. */
2056 unset_primary_address (n);
2057 n->ack_state = ACK_SEND_SYN_ACK;
2058 set_state_and_timeout (n,
2059 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2060 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2065 * Send a SYN_ACK message via the given address.
2067 * @param na address and session to use
2068 * @param timestamp timestamp to use for the ACK message
2069 * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2072 send_syn_ack_message (struct NeighbourAddress *na,
2073 struct GNUNET_TIME_Absolute timestamp)
2075 const struct GNUNET_HELLO_Address *address = na->address;
2076 struct GNUNET_ATS_Session *session = na->session;
2077 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2078 struct TransportSynMessage connect_msg;
2079 struct NeighbourMapEntry *n;
2081 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2082 "Sending SYN_ACK to peer `%s'\n",
2083 GNUNET_i2s (&address->peer));
2085 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2090 if (NULL == session)
2091 session = papi->get_session (papi->cls,
2093 if (NULL == session)
2098 GST_ats_new_session (address,
2100 GNUNET_STATISTICS_update (GST_stats,
2102 ("# SYN_ACK messages sent"),
2104 connect_msg.header.size = htons (sizeof (struct TransportSynMessage));
2105 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2106 connect_msg.reserved = htonl (0);
2107 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
2109 if (GNUNET_SYSERR ==
2110 papi->send (papi->cls,
2112 (const char *) &connect_msg,
2113 sizeof (struct TransportSynMessage),
2115 GNUNET_TIME_UNIT_FOREVER_REL,
2116 &send_session_syn_ack_cont, NULL))
2118 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2119 _("Failed to transmit SYN_ACK message to %s\n"),
2120 GST_plugins_a2s (address));
2122 n = lookup_neighbour (&address->peer);
2128 /* Remove address and request and additional one */
2129 unset_primary_address (n);
2130 n->ack_state = ACK_SEND_SYN_ACK;
2131 set_state_and_timeout (n,
2132 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2133 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2140 * Function called by the bandwidth tracker for a peer whenever
2141 * the tracker's state changed such that we need to recalculate
2142 * the delay for flow control. We calculate the latest delay
2143 * and inform the plugin (if applicable).
2145 * @param cls the `struct NeighbourMapEntry` to update calculations for
2148 inbound_bw_tracker_update (void *cls)
2150 struct NeighbourMapEntry *n = cls;
2151 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2152 struct GNUNET_TIME_Relative delay;
2155 if (NULL == n->primary_address.address)
2156 return; /* not active, ignore */
2157 papi = GST_plugins_find (n->primary_address.address->transport_name);
2158 GNUNET_assert (NULL != papi);
2159 if (NULL == papi->update_inbound_delay)
2161 delay = GST_neighbours_calculate_receive_delay (&n->id,
2164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2165 "New inbound delay for peer `%s' is %llu ms\n",
2166 GNUNET_i2s (&n->id),
2167 delay.rel_value_us / 1000);
2168 papi->update_inbound_delay (papi->cls,
2170 n->primary_address.session,
2176 * Create a fresh entry in the neighbour map for the given peer
2178 * @param peer peer to create an entry for
2179 * @return new neighbour map entry
2181 static struct NeighbourMapEntry *
2182 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
2184 struct NeighbourMapEntry *n;
2186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2187 "Creating new neighbour entry for `%s'\n",
2190 memcmp (&GST_my_identity,
2192 sizeof (struct GNUNET_PeerIdentity)));
2193 n = GNUNET_new (struct NeighbourMapEntry);
2195 n->ack_state = ACK_UNDEFINED;
2196 n->last_util_transmission = GNUNET_TIME_absolute_get();
2197 n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2198 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
2199 &inbound_bw_tracker_update,
2201 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2202 MAX_BANDWIDTH_CARRY_S);
2203 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
2204 set_state_and_timeout (n,
2205 GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2206 GNUNET_TIME_UNIT_FOREVER_ABS);
2207 GNUNET_assert (GNUNET_OK ==
2208 GNUNET_CONTAINER_multipeermap_put (neighbours,
2211 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2212 n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect,
2221 * Entry in a DLL we use to keep track of pending blacklist checks.
2223 struct BlacklistCheckSwitchContext
2228 struct BlacklistCheckSwitchContext *prev;
2233 struct BlacklistCheckSwitchContext *next;
2236 * Handle to the blacklist check we are performing.
2238 struct GST_BlacklistCheck *blc;
2241 * Inbound bandwidth that was assigned to @e address.
2243 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2246 * Outbound bandwidth that was assigned to @e address.
2248 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2253 * Black list check result for try_connect call
2254 * If connection to the peer is allowed request adddress and
2256 * @param cls blc_ctx bl context
2257 * @param peer the peer
2258 * @param address address associated with the request
2259 * @param session session associated with the request
2260 * @param result #GNUNET_OK if the connection is allowed,
2261 * #GNUNET_NO if not,
2262 * #GNUNET_SYSERR if operation was aborted
2265 try_connect_bl_check_cont (void *cls,
2266 const struct GNUNET_PeerIdentity *peer,
2267 const struct GNUNET_HELLO_Address *address,
2268 struct GNUNET_ATS_Session *session,
2271 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2272 struct NeighbourMapEntry *n;
2274 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2277 GNUNET_free (blc_ctx);
2278 if (GNUNET_OK != result)
2280 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2281 _("Blacklisting disapproved to connect to peer `%s'\n"),
2286 /* Setup a new neighbour */
2287 if (NULL != lookup_neighbour(peer))
2288 return; /* The neighbor was created in the meantime while waited for BL clients */
2290 n = setup_neighbour (peer);
2292 /* Request address suggestions for this peer */
2293 set_state_and_timeout (n,
2294 GNUNET_TRANSPORT_PS_INIT_ATS,
2295 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2300 * Try to create a connection to the given target (eventually).
2302 * @param target peer to try to connect to
2305 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
2307 struct NeighbourMapEntry *n;
2308 struct GST_BlacklistCheck *blc;
2309 struct BlacklistCheckSwitchContext *blc_ctx;
2311 if (NULL == neighbours)
2313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2314 "Asked to connect to peer `%s' during shutdown\n",
2315 GNUNET_i2s (target));
2316 return; /* during shutdown, do nothing */
2318 n = lookup_neighbour (target);
2319 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2320 "Asked to connect to peer `%s' (state: %s)\n",
2321 GNUNET_i2s (target),
2322 (NULL != n) ? GNUNET_TRANSPORT_ps2s(n->state) : "NEW PEER");
2327 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2328 /* this should not be possible */
2332 case GNUNET_TRANSPORT_PS_INIT_ATS:
2333 case GNUNET_TRANSPORT_PS_SYN_SENT:
2334 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2335 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2336 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2337 "Ignoring request to try to connect to `%s', already trying!\n",
2338 GNUNET_i2s (target));
2339 return; /* already trying */
2340 case GNUNET_TRANSPORT_PS_CONNECTED:
2341 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2342 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2343 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2344 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2345 "Ignoring request to try to connect, already connected to `%s'!\n",
2346 GNUNET_i2s (target));
2347 return; /* already connected */
2348 case GNUNET_TRANSPORT_PS_DISCONNECT:
2349 /* get rid of remains, ready to re-try immediately */
2352 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2353 /* should not be possible */
2357 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2358 "Unhandled state `%s'\n",
2359 GNUNET_TRANSPORT_ps2s (n->state));
2366 /* Do blacklist check if connecting to this peer is allowed */
2367 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2368 GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2373 (blc = GST_blacklist_test_allowed (target,
2375 &try_connect_bl_check_cont,
2386 * We received a 'SYN' message from the other peer.
2387 * Consider switching to it.
2389 * @param message possibly a `struct TransportSynMessage` (check format)
2390 * @param peer identity of the peer to switch the address for
2391 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2394 GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message,
2395 const struct GNUNET_PeerIdentity *peer)
2397 const struct TransportSynMessage *scm;
2398 struct NeighbourMapEntry *n;
2399 struct GNUNET_TIME_Absolute ts;
2401 if (ntohs (message->size) != sizeof (struct TransportSynMessage))
2403 GNUNET_break_op (0);
2404 return GNUNET_SYSERR;
2406 GNUNET_STATISTICS_update (GST_stats,
2408 ("# SYN messages received"),
2410 if (NULL == neighbours)
2412 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2413 _("SYN request from peer `%s' ignored due impending shutdown\n"),
2415 return GNUNET_OK; /* we're shutting down */
2417 scm = (const struct TransportSynMessage *) message;
2418 GNUNET_break_op (0 == ntohl (scm->reserved));
2419 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2420 n = lookup_neighbour (peer);
2423 /* This is a new neighbour and set to not connected */
2424 n = setup_neighbour (peer);
2427 /* Remember this SYN message in neighbour */
2428 n->ack_state = ACK_SEND_SYN_ACK;
2429 n->connect_ack_timestamp = ts;
2431 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2432 "Received SYN for peer `%s' in state %s/%s\n",
2434 GNUNET_TRANSPORT_ps2s (n->state),
2435 print_ack_state (n->ack_state));
2439 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2440 /* Request an address from ATS to send SYN_ACK to this peer */
2441 set_state_and_timeout (n,
2442 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2443 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2445 case GNUNET_TRANSPORT_PS_INIT_ATS:
2446 /* SYN message takes priority over us asking ATS for address:
2447 * Wait for ATS to suggest an address and send SYN_ACK */
2448 set_state_and_timeout (n,
2449 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2450 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2452 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2453 /* We already wait for an address to send an SYN_ACK */
2455 case GNUNET_TRANSPORT_PS_SYN_SENT:
2456 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2457 /* Send ACK immediately */
2458 n->ack_state = ACK_SEND_ACK;
2459 send_syn_ack_message (&n->primary_address,
2462 case GNUNET_TRANSPORT_PS_CONNECTED:
2463 /* we are already connected and can thus send the ACK immediately */
2464 GNUNET_assert (NULL != n->primary_address.address);
2465 GNUNET_assert (NULL != n->primary_address.session);
2466 n->ack_state = ACK_SEND_ACK;
2467 send_syn_ack_message (&n->primary_address,
2470 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2471 /* We wait for ATS address suggestion */
2473 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2474 /* We received a SYN message while waiting for a SYN_ACK in fast
2475 * reconnect. Send SYN_ACK immediately */
2476 n->ack_state = ACK_SEND_ACK;
2477 send_syn_ack_message (&n->primary_address,
2478 n->connect_ack_timestamp);
2480 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2481 /* We are already connected and can thus send the ACK immediately;
2482 still, it can never hurt to have an alternative address, so also
2483 tell ATS about it */
2484 GNUNET_assert (NULL != n->primary_address.address);
2485 GNUNET_assert (NULL != n->primary_address.session);
2486 n->ack_state = ACK_SEND_ACK;
2487 send_syn_ack_message (&n->primary_address,
2490 case GNUNET_TRANSPORT_PS_DISCONNECT:
2491 /* Get rid of remains and re-try */
2493 n = setup_neighbour (peer);
2494 /* Remember the SYN time stamp for ACK message */
2495 n->ack_state = ACK_SEND_SYN_ACK;
2496 n->connect_ack_timestamp = ts;
2497 /* Request an address for the peer */
2498 set_state_and_timeout (n,
2499 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2500 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2502 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2503 /* should not be possible */
2507 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2508 "Unhandled state `%s'\n",
2509 GNUNET_TRANSPORT_ps2s (n->state));
2511 return GNUNET_SYSERR;
2518 * Check if the given @a address is the same that we are already
2519 * using for the respective neighbour. If so, update the bandwidth
2520 * assignment and possibly the session and return #GNUNET_OK.
2521 * If the new address is different from what the neighbour is
2522 * using right now, return #GNUNET_NO.
2524 * @param address address of the other peer,
2525 * @param session session to use or NULL if transport should initiate a session
2526 * @param bandwidth_in inbound quota to be used when connection is up,
2527 * 0 to disconnect from peer
2528 * @param bandwidth_out outbound quota to be used when connection is up,
2529 * 0 to disconnect from peer
2530 * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2531 * #GNUNET_NO if more extensive changes are required (address changed)
2534 try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address,
2535 struct GNUNET_ATS_Session *session,
2536 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2537 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2539 struct NeighbourMapEntry *n;
2541 n = lookup_neighbour (&address->peer);
2543 (NULL == n->primary_address.address) ||
2544 (0 != GNUNET_HELLO_address_cmp (address,
2545 n->primary_address.address)) )
2547 /* We are not really switching addresses, but merely adjusting
2548 session and/or bandwidth, can do fast ATS update! */
2549 if (session != n->primary_address.session)
2551 /* switch to a different session, but keeping same address; could
2552 happen if there is a 2nd inbound connection */
2553 n->primary_address.session = session;
2554 GNUNET_assert (GNUNET_YES ==
2555 GST_ats_is_known (n->primary_address.address,
2556 n->primary_address.session));
2558 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2560 n->primary_address.bandwidth_in = bandwidth_in;
2561 set_incoming_quota (n,
2564 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2566 n->primary_address.bandwidth_out = bandwidth_out;
2567 send_outbound_quota_to_clients (n);
2574 * We've been asked to switch addresses, and just now got the result
2575 * from the blacklist check to see if this is allowed.
2577 * @param cls the `struct BlacklistCheckSwitchContext` with
2578 * the information about the future address
2579 * @param peer the peer we may switch addresses on
2580 * @param address address associated with the request
2581 * @param session session associated with the request
2582 * @param result #GNUNET_OK if the connection is allowed,
2583 * #GNUNET_NO if not,
2584 * #GNUNET_SYSERR if operation was aborted
2587 switch_address_bl_check_cont (void *cls,
2588 const struct GNUNET_PeerIdentity *peer,
2589 const struct GNUNET_HELLO_Address *address,
2590 struct GNUNET_ATS_Session *session,
2593 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2594 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2595 struct NeighbourMapEntry *n;
2597 if (GNUNET_SYSERR == result)
2600 papi = GST_plugins_find (address->transport_name);
2601 GNUNET_assert (NULL != papi);
2603 if (GNUNET_NO == result)
2605 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2606 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2607 GST_plugins_a2s (address),
2610 GNUNET_STATISTICS_update (GST_stats,
2611 "# ATS suggestions ignored (blacklist denied)",
2614 if (NULL != session)
2615 papi->disconnect_session (papi->cls,
2618 GNUNET_HELLO_address_check_option (address,
2619 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2620 GST_ats_block_address (address,
2626 if (NULL == session)
2628 /* need to create a session, ATS only gave us an address */
2629 session = papi->get_session (papi->cls,
2631 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2632 "Obtained new session for peer `%s' and address '%s': %p\n",
2633 GNUNET_i2s (&address->peer),
2634 GST_plugins_a2s (address),
2636 if (NULL != session)
2637 GST_ats_new_session (address,
2640 if (NULL == session)
2642 /* session creation failed, bad!, fail! */
2643 GNUNET_STATISTICS_update (GST_stats,
2644 "# ATS suggestions ignored (failed to create session)",
2647 /* No session could be obtained, remove blacklist check and clean up */
2648 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2649 "Failed to obtain new session for peer `%s' and address '%s'\n",
2650 GNUNET_i2s (&address->peer),
2651 GST_plugins_a2s (address));
2652 GST_ats_block_address (address,
2657 /* We did this check already before going into blacklist, but
2658 it is theoretically possible that the situation changed in
2659 the meantime, hence we check again here */
2661 try_run_fast_ats_update (address,
2663 blc_ctx->bandwidth_in,
2664 blc_ctx->bandwidth_out))
2665 goto cleanup; /* was just a minor update, we're done */
2667 /* check if we also need to setup the neighbour entry */
2668 if (NULL == (n = lookup_neighbour (peer)))
2670 n = setup_neighbour (peer);
2671 n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2674 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2675 "Peer `%s' switches to address `%s'\n",
2676 GNUNET_i2s (&address->peer),
2677 GST_plugins_a2s (address));
2681 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2683 GST_ats_block_address (address,
2687 case GNUNET_TRANSPORT_PS_INIT_ATS:
2688 /* We requested an address and ATS suggests one:
2689 * set primary address and send SYN message*/
2690 set_primary_address (n,
2693 blc_ctx->bandwidth_in,
2694 blc_ctx->bandwidth_out);
2695 if (ACK_SEND_SYN_ACK == n->ack_state)
2697 /* Send pending SYN_ACK message */
2698 n->ack_state = ACK_SEND_ACK;
2699 send_syn_ack_message (&n->primary_address,
2700 n->connect_ack_timestamp);
2702 set_state_and_timeout (n,
2703 GNUNET_TRANSPORT_PS_SYN_SENT,
2704 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2705 send_syn (&n->primary_address);
2707 case GNUNET_TRANSPORT_PS_SYN_SENT:
2708 /* ATS suggested a new address while waiting for an SYN_ACK:
2709 * Switch and send new SYN */
2710 /* ATS suggests a different address, switch again */
2711 set_primary_address (n,
2714 blc_ctx->bandwidth_in,
2715 blc_ctx->bandwidth_out);
2716 if (ACK_SEND_SYN_ACK == n->ack_state)
2718 /* Send pending SYN_ACK message */
2719 n->ack_state = ACK_SEND_ACK;
2720 send_syn_ack_message (&n->primary_address,
2721 n->connect_ack_timestamp);
2723 set_state_and_timeout (n,
2724 GNUNET_TRANSPORT_PS_SYN_SENT,
2725 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2726 send_syn (&n->primary_address);
2728 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2729 /* We requested an address and ATS suggests one:
2730 * set primary address and send SYN_ACK message*/
2731 set_primary_address (n,
2734 blc_ctx->bandwidth_in,
2735 blc_ctx->bandwidth_out);
2736 /* Send an ACK message as a response to the SYN msg */
2737 set_state_and_timeout (n,
2738 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2739 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2740 send_syn_ack_message (&n->primary_address,
2741 n->connect_ack_timestamp);
2742 if ( (ACK_SEND_SYN_ACK == n->ack_state) ||
2743 (ACK_UNDEFINED == n->ack_state) )
2744 n->ack_state = ACK_SEND_ACK;
2746 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2747 /* ATS asks us to switch while we were trying to connect; switch to new
2748 address and check blacklist again */
2749 if ( (ACK_SEND_SYN_ACK == n->ack_state) )
2751 n->ack_state = ACK_SEND_ACK;
2752 send_syn_ack_message (&n->primary_address,
2753 n->connect_ack_timestamp);
2755 set_primary_address (n,
2758 blc_ctx->bandwidth_in,
2759 blc_ctx->bandwidth_out);
2760 set_state_and_timeout (n,
2761 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2762 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2764 case GNUNET_TRANSPORT_PS_CONNECTED:
2765 GNUNET_assert (NULL != n->primary_address.address);
2766 GNUNET_assert (NULL != n->primary_address.session);
2767 GNUNET_break (n->primary_address.session != session);
2768 /* ATS asks us to switch a life connection; see if we can get
2769 a SYN_ACK on it before we actually do this! */
2770 set_alternative_address (n,
2773 blc_ctx->bandwidth_in,
2774 blc_ctx->bandwidth_out);
2775 set_state_and_timeout (n,
2776 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2777 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2778 GNUNET_STATISTICS_update (GST_stats,
2779 gettext_noop ("# Attempts to switch addresses"),
2782 send_syn (&n->alternative_address);
2784 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2785 set_primary_address (n,
2788 blc_ctx->bandwidth_in,
2789 blc_ctx->bandwidth_out);
2790 if (ACK_SEND_SYN_ACK == n->ack_state)
2792 /* Send pending SYN_ACK message */
2793 n->ack_state = ACK_SEND_ACK;
2794 send_syn_ack_message (&n->primary_address,
2795 n->connect_ack_timestamp);
2797 set_state_and_timeout (n,
2798 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2799 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2800 send_syn (&n->primary_address);
2802 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2803 /* ATS asks us to switch while we were trying to reconnect; switch to new
2804 address and send SYN again */
2805 set_primary_address (n,
2808 blc_ctx->bandwidth_in,
2809 blc_ctx->bandwidth_out);
2810 set_state_and_timeout (n,
2811 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2812 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2813 send_syn (&n->primary_address);
2815 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2816 if ( (0 == GNUNET_HELLO_address_cmp (n->primary_address.address,
2818 (n->primary_address.session == session) )
2820 /* ATS switches back to still-active session */
2821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2822 "ATS double-switched, cleaning up alternative address\n");
2823 free_address (&n->alternative_address);
2824 set_state_and_timeout (n,
2825 GNUNET_TRANSPORT_PS_CONNECTED,
2829 /* ATS asks us to switch a life connection, send */
2830 set_alternative_address (n,
2833 blc_ctx->bandwidth_in,
2834 blc_ctx->bandwidth_out);
2835 set_state_and_timeout (n,
2836 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2837 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2838 send_syn (&n->alternative_address);
2840 case GNUNET_TRANSPORT_PS_DISCONNECT:
2841 /* not going to switch addresses while disconnecting */
2842 GNUNET_STATISTICS_update (GST_stats,
2843 "# ATS suggestion ignored (disconnecting)",
2847 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2851 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2852 "Unhandled state `%s'\n",
2853 GNUNET_TRANSPORT_ps2s (n->state));
2858 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2861 GNUNET_free (blc_ctx);
2866 * For the given peer, switch to this address.
2868 * Before accepting this addresses and actively using it, a blacklist check
2871 * If any check fails or the suggestion can somehow not be followed, we
2872 * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2873 * could not be satisfied and force ATS to do something else.
2875 * @param address address of the other peer,
2876 * @param session session to use or NULL if transport should initiate a session
2877 * @param bandwidth_in inbound quota to be used when connection is up,
2878 * 0 to disconnect from peer
2879 * @param bandwidth_out outbound quota to be used when connection is up,
2880 * 0 to disconnect from peer
2883 GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address,
2884 struct GNUNET_ATS_Session *session,
2885 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2886 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2888 struct GST_BlacklistCheck *blc;
2889 struct BlacklistCheckSwitchContext *blc_ctx;
2891 GNUNET_assert (NULL != address->transport_name);
2893 try_run_fast_ats_update (address,
2899 /* Check if plugin is available */
2900 if (NULL == (GST_plugins_find (address->transport_name)))
2902 /* we don't have the plugin for this address */
2904 GST_ats_block_address (address,
2908 if ((NULL == session) &&
2909 (GNUNET_HELLO_address_check_option (address,
2910 GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2912 /* This is a inbound address and we do not have a session to use! */
2914 GST_ats_block_address (address,
2919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2920 "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2921 GST_plugins_a2s (address),
2922 GNUNET_i2s (&address->peer),
2923 (unsigned int) ntohl (bandwidth_in.value__),
2924 (unsigned int) ntohl (bandwidth_out.value__));
2926 /* Perform blacklist check */
2927 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2928 blc_ctx->bandwidth_in = bandwidth_in;
2929 blc_ctx->bandwidth_out = bandwidth_out;
2930 GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2933 if (NULL != (blc = GST_blacklist_test_allowed (&address->peer,
2934 address->transport_name,
2935 &switch_address_bl_check_cont,
2946 * Function called to send network utilization data to ATS for
2947 * each active connection.
2950 * @param key peer we send utilization data for
2951 * @param value the `struct NeighbourMapEntry *` with data to send
2952 * @return #GNUNET_OK (continue to iterate)
2955 send_utilization_data (void *cls,
2956 const struct GNUNET_PeerIdentity *key,
2959 struct NeighbourMapEntry *n = value;
2962 struct GNUNET_TIME_Relative delta;
2964 if ( (GNUNET_YES != test_connected (n)) ||
2965 (NULL == n->primary_address.address) )
2967 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2968 GNUNET_TIME_absolute_get ());
2970 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2971 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2973 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2974 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2976 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2977 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2981 GST_ats_update_utilization (n->primary_address.address,
2984 n->util_total_bytes_recv = 0;
2985 n->util_total_bytes_sent = 0;
2986 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2992 * Task transmitting utilization in a regular interval
2994 * @param cls the 'struct NeighbourMapEntry' for which we are running
2995 * @param tc scheduler context (unused)
2998 utilization_transmission (void *cls,
2999 const struct GNUNET_SCHEDULER_TaskContext *tc)
3001 util_transmission_tk = NULL;
3002 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3003 &send_utilization_data,
3005 util_transmission_tk
3006 = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3007 &utilization_transmission,
3013 * Track information about data we received from the
3014 * given address (used to notify ATS about our utilization
3015 * of allocated resources).
3017 * @param address the address we got data from
3018 * @param message the message we received (really only the size is used)
3021 GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address,
3022 const struct GNUNET_MessageHeader *message)
3024 struct NeighbourMapEntry *n;
3026 n = lookup_neighbour (&address->peer);
3029 n->util_total_bytes_recv += ntohs (message->size);
3034 * Track information about data we transmitted using the given @a
3035 * address and @a session (used to notify ATS about our utilization of
3036 * allocated resources).
3038 * @param address the address we transmitted data to
3039 * @param session session we used to transmit data
3040 * @param message the message we sent (really only the size is used)
3043 GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address,
3044 struct GNUNET_ATS_Session *session,
3047 struct NeighbourMapEntry *n;
3049 n = lookup_neighbour (&address->peer);
3052 if (n->primary_address.session != session)
3054 n->util_total_bytes_sent += size;
3059 * Master task run for every neighbour. Performs all of the time-related
3060 * activities (keep alive, send next message, disconnect if idle, finish
3061 * clean up after disconnect).
3063 * @param cls the 'struct NeighbourMapEntry' for which we are running
3064 * @param tc scheduler context (unused)
3067 master_task (void *cls,
3068 const struct GNUNET_SCHEDULER_TaskContext *tc)
3070 struct NeighbourMapEntry *n = cls;
3071 struct GNUNET_TIME_Relative delay;
3074 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3075 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3076 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
3077 GNUNET_i2s (&n->id),
3078 GNUNET_TRANSPORT_ps2s(n->state),
3079 GNUNET_STRINGS_relative_time_to_string (delay,
3083 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3084 /* invalid state for master task, clean up */
3088 case GNUNET_TRANSPORT_PS_INIT_ATS:
3089 if (0 == delay.rel_value_us)
3091 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3092 "Connection to `%s' timed out waiting for ATS to provide address\n",
3093 GNUNET_i2s (&n->id));
3098 case GNUNET_TRANSPORT_PS_SYN_SENT:
3099 if (0 == delay.rel_value_us)
3101 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3102 "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
3103 GNUNET_i2s (&n->id));
3104 /* Remove address and request and additional one */
3105 unset_primary_address (n);
3106 set_state_and_timeout (n,
3107 GNUNET_TRANSPORT_PS_INIT_ATS,
3108 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3112 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3113 if (0 == delay.rel_value_us)
3115 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3116 "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
3117 GNUNET_i2s (&n->id));
3122 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3123 if (0 == delay.rel_value_us)
3125 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3126 "Connection to `%s' timed out waiting for other peer to send ACK\n",
3127 GNUNET_i2s (&n->id));
3128 disconnect_neighbour (n);
3132 case GNUNET_TRANSPORT_PS_CONNECTED:
3133 if (0 == delay.rel_value_us)
3135 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3136 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3137 GNUNET_i2s (&n->id));
3138 disconnect_neighbour (n);
3141 try_transmission_to_peer (n);
3144 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3145 if (0 == delay.rel_value_us)
3147 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3148 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3149 GNUNET_i2s (&n->id));
3150 disconnect_neighbour (n);
3154 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3155 if (0 == delay.rel_value_us)
3157 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3158 "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3159 GNUNET_i2s (&n->id));
3160 disconnect_neighbour (n);
3164 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3165 if (0 == delay.rel_value_us)
3167 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3168 "Switch failed, cleaning up alternative address\n");
3169 free_address (&n->alternative_address);
3170 set_state_and_timeout (n,
3171 GNUNET_TRANSPORT_PS_CONNECTED,
3172 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
3174 try_transmission_to_peer (n);
3177 case GNUNET_TRANSPORT_PS_DISCONNECT:
3178 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3179 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3180 GNUNET_i2s (&n->id));
3183 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3184 /* how did we get here!? */
3188 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3189 "Unhandled state `%s'\n",
3190 GNUNET_TRANSPORT_ps2s (n->state));
3194 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3195 if ( (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3196 (GNUNET_TRANSPORT_PS_CONNECTED == n->state) )
3198 /* if we are *now* in one of the two states, we're sending
3199 keep alive messages, so we need to consider the keepalive
3200 delay, not just the connection timeout */
3201 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
3204 if (NULL == n->task)
3205 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3212 * Send a ACK message to the neighbour to confirm that we
3215 * @param n neighbour to send the ACK to
3218 send_session_ack_message (struct NeighbourMapEntry *n)
3220 struct GNUNET_MessageHeader msg;
3222 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3223 "Sending ACK message to peer `%s'\n",
3224 GNUNET_i2s (&n->id));
3226 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
3227 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3228 (void) send_with_session (n,
3230 sizeof (struct GNUNET_MessageHeader),
3232 GNUNET_TIME_UNIT_FOREVER_REL,
3239 * We received a 'SESSION_SYN_ACK' message from the other peer.
3240 * Consider switching to it.
3242 * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3243 * @param peer identity of the peer to switch the address for
3244 * @param address address of the other peer, NULL if other peer
3246 * @param session session to use (or NULL)
3247 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3250 GST_neighbours_handle_session_syn_ack (const struct GNUNET_MessageHeader *message,
3251 const struct GNUNET_HELLO_Address *address,
3252 struct GNUNET_ATS_Session *session)
3254 const struct TransportSynMessage *scm;
3255 struct GNUNET_TIME_Absolute ts;
3256 struct NeighbourMapEntry *n;
3258 if (ntohs (message->size) != sizeof (struct TransportSynMessage))
3260 GNUNET_break_op (0);
3261 return GNUNET_SYSERR;
3263 GNUNET_STATISTICS_update (GST_stats,
3265 ("# SYN_ACK messages received"),
3267 scm = (const struct TransportSynMessage *) message;
3268 GNUNET_break_op (ntohl (scm->reserved) == 0);
3269 if (NULL == (n = lookup_neighbour (&address->peer)))
3271 GNUNET_STATISTICS_update (GST_stats,
3273 ("# unexpected SYN_ACK messages (no peer)"),
3275 return GNUNET_SYSERR;
3277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3278 "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3279 GNUNET_i2s (&address->peer),
3280 GNUNET_TRANSPORT_ps2s (n->state),
3281 print_ack_state (n->ack_state));
3282 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3285 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3288 return GNUNET_SYSERR;
3289 case GNUNET_TRANSPORT_PS_INIT_ATS:
3290 GNUNET_STATISTICS_update (GST_stats,
3291 gettext_noop ("# unexpected SYN_ACK messages (not ready)"),
3295 case GNUNET_TRANSPORT_PS_SYN_SENT:
3296 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3298 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3299 "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3302 set_state_and_timeout (n,
3303 GNUNET_TRANSPORT_PS_CONNECTED,
3304 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3305 set_primary_address (n,
3306 n->primary_address.address,
3307 n->primary_address.session,
3308 n->primary_address.bandwidth_in,
3309 n->primary_address.bandwidth_out);
3310 send_session_ack_message (n);
3312 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3313 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3314 GNUNET_STATISTICS_update (GST_stats,
3315 gettext_noop ("# unexpected SYN_ACK messages (not ready)"),
3319 case GNUNET_TRANSPORT_PS_CONNECTED:
3320 /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3321 send_session_ack_message (n);
3323 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3324 /* we didn't expect any SYN_ACK, as we are waiting for ATS
3325 to give us a new address... */
3326 GNUNET_STATISTICS_update (GST_stats,
3327 gettext_noop ("# unexpected SYN_ACK messages (waiting on ATS)"),
3331 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3332 /* Reconnecting with new address address worked; go back to connected! */
3333 set_state_and_timeout (n,
3334 GNUNET_TRANSPORT_PS_CONNECTED,
3335 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3336 send_session_ack_message (n);
3338 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3339 /* new address worked; adopt it and go back to connected! */
3340 set_state_and_timeout (n,
3341 GNUNET_TRANSPORT_PS_CONNECTED,
3342 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3343 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3345 /* Set primary addresses */
3346 set_primary_address (n,
3347 n->alternative_address.address,
3348 n->alternative_address.session,
3349 n->alternative_address.bandwidth_in,
3350 n->alternative_address.bandwidth_out);
3351 GNUNET_STATISTICS_update (GST_stats,
3352 gettext_noop ("# Successful attempts to switch addresses"),
3356 GNUNET_HELLO_address_free (n->alternative_address.address);
3357 memset (&n->alternative_address,
3359 sizeof (n->alternative_address));
3360 send_session_ack_message (n);
3362 case GNUNET_TRANSPORT_PS_DISCONNECT:
3363 GNUNET_STATISTICS_update (GST_stats,
3365 ("# unexpected SYN_ACK messages (disconnecting)"),
3367 return GNUNET_SYSERR;
3368 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3373 "Unhandled state `%s'\n",
3374 GNUNET_TRANSPORT_ps2s (n->state));
3376 return GNUNET_SYSERR;
3383 * A session was terminated. Take note; if needed, try to get
3384 * an alternative address from ATS.
3386 * @param peer identity of the peer where the session died
3387 * @param session session that is gone
3388 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3389 * this session was not in use
3392 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3393 struct GNUNET_ATS_Session *session)
3395 struct NeighbourMapEntry *n;
3396 struct BlackListCheckContext *bcc;
3397 struct BlackListCheckContext *bcc_next;
3399 /* make sure to cancel all ongoing blacklist checks involving 'session' */
3401 while (NULL != (bcc = bcc_next))
3403 bcc_next = bcc->next;
3404 if (bcc->na.session == session)
3406 if (NULL != bcc->bc)
3407 GST_blacklist_test_cancel (bcc->bc);
3408 GNUNET_HELLO_address_free (bcc->na.address);
3409 GNUNET_CONTAINER_DLL_remove (bc_head,
3415 if (NULL == (n = lookup_neighbour (peer)))
3416 return GNUNET_NO; /* can't affect us */
3417 if (session != n->primary_address.session)
3419 /* Free alternative address */
3420 if (session == n->alternative_address.session)
3422 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3423 set_state_and_timeout (n,
3424 GNUNET_TRANSPORT_PS_CONNECTED,
3426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3427 "Session died, cleaning up alternative address\n");
3428 free_address (&n->alternative_address);
3430 return GNUNET_NO; /* doesn't affect us further */
3433 n->expect_latency_response = GNUNET_NO;
3434 /* The session for neighbour's primary address died */
3437 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3441 case GNUNET_TRANSPORT_PS_INIT_ATS:
3445 case GNUNET_TRANSPORT_PS_SYN_SENT:
3446 /* The session used to send the SYN terminated:
3447 * this implies a connect error*/
3448 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3449 "Failed to send SYN in %s with `%s' %p: session terminated\n",
3451 GST_plugins_a2s (n->primary_address.address),
3452 n->primary_address.session,
3455 /* Destroy the address since it cannot be used */
3456 unset_primary_address (n);
3457 set_state_and_timeout (n,
3458 GNUNET_TRANSPORT_PS_INIT_ATS,
3459 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3461 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3462 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3463 /* error on inbound session; free neighbour entirely */
3466 case GNUNET_TRANSPORT_PS_CONNECTED:
3467 /* Our primary connection died, try a fast reconnect */
3468 unset_primary_address (n);
3469 set_state_and_timeout (n,
3470 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3471 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3473 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3474 /* we don't have an address, how can it go down? */
3477 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3478 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3479 "Failed to send SYN in %s with `%s' %p: session terminated\n",
3481 GST_plugins_a2s (n->primary_address.address),
3482 n->primary_address.session,
3484 /* Destroy the address since it cannot be used */
3485 unset_primary_address (n);
3486 set_state_and_timeout (n,
3487 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3488 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3490 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3491 /* primary went down while we were waiting for SYN_ACK on secondary;
3492 secondary as primary */
3494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3495 "Connection `%s' %p to peer `%s' was terminated while switching, "
3496 "switching to alternative address `%s' %p\n",
3497 GST_plugins_a2s (n->primary_address.address),
3498 n->primary_address.session,
3500 GST_plugins_a2s (n->alternative_address.address),
3501 n->alternative_address.session);
3503 /* Destroy the inbound address since it cannot be used */
3504 free_address (&n->primary_address);
3505 n->primary_address = n->alternative_address;
3506 GNUNET_assert (GNUNET_YES ==
3507 GST_ats_is_known (n->primary_address.address,
3508 n->primary_address.session));
3509 memset (&n->alternative_address,
3511 sizeof (struct NeighbourAddress));
3512 set_state_and_timeout (n,
3513 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3514 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3516 case GNUNET_TRANSPORT_PS_DISCONNECT:
3517 unset_primary_address (n);
3519 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3520 /* neighbour was freed and plugins told to terminate session */
3523 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3524 "Unhandled state `%s'\n",
3525 GNUNET_TRANSPORT_ps2s (n->state));
3529 if (NULL != n->task)
3530 GNUNET_SCHEDULER_cancel (n->task);
3531 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3537 * We received a 'ACK' message from the other peer.
3538 * If we sent a 'SYN_ACK' last, this means we are now
3539 * connected. Otherwise, do nothing.
3541 * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3542 * @param address address of the other peer
3543 * @param session session to use (or NULL)
3544 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3547 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3548 const struct GNUNET_HELLO_Address *address,
3549 struct GNUNET_ATS_Session *session)
3551 struct NeighbourMapEntry *n;
3553 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3555 GNUNET_break_op (0);
3556 return GNUNET_SYSERR;
3558 GNUNET_STATISTICS_update (GST_stats,
3559 gettext_noop ("# ACK messages received"),
3562 if (NULL == (n = lookup_neighbour (&address->peer)))
3564 GNUNET_break_op (0);
3565 return GNUNET_SYSERR;
3567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3568 "Received ACK for peer `%s' in state %s/%s\n",
3569 GNUNET_i2s (&address->peer),
3570 GNUNET_TRANSPORT_ps2s (n->state),
3571 print_ack_state (n->ack_state));
3573 /* Check if we are in a plausible state for having sent
3574 a SYN_ACK. If not, return, otherwise break.
3576 The remote peers sends a ACK as a response for a SYN_ACK
3580 - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3581 now wait for the ACK to finally be connected
3582 - If we sent a SYN_ACK to this peer before */
3584 if ( ( (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3585 (ACK_SEND_ACK != n->ack_state) ) ||
3586 (NULL == n->primary_address.address) )
3588 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3589 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3590 GNUNET_i2s (&address->peer),
3591 GNUNET_TRANSPORT_ps2s (n->state),
3592 print_ack_state (n->ack_state));
3594 GNUNET_STATISTICS_update (GST_stats,
3595 gettext_noop ("# unexpected ACK messages"),
3600 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3602 /* We tried to switch addresses while being connect. We explicitly wait
3603 * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3604 * so we do not want to set the address as in use! */
3607 set_state_and_timeout (n,
3608 GNUNET_TRANSPORT_PS_CONNECTED,
3609 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3611 if (NULL == n->primary_address.address) {
3613 * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3614 * really means we did try (and succeed) to send a SYN and are waiting for
3616 * That suggests that the primary_address used to be non-NULL, but maybe it
3617 * got reset to NULL without the state being changed appropriately?
3623 /* Reset backoff for primary address */
3624 GST_ats_block_reset (n->primary_address.address,
3625 n->primary_address.session);
3631 * Test if we're connected to the given peer.
3633 * @param target peer to test
3634 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3637 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3639 return test_connected (lookup_neighbour (target));
3644 * Task to asynchronously run #free_neighbour().
3646 * @param cls the `struct NeighbourMapEntry` to free
3650 delayed_disconnect (void *cls,
3651 const struct GNUNET_SCHEDULER_TaskContext* tc)
3653 struct NeighbourMapEntry *n = cls;
3655 n->delayed_disconnect_task = NULL;
3656 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3657 "Disconnecting by request from peer %s\n",
3658 GNUNET_i2s (&n->id));
3664 * We received a quota message from the given peer,
3665 * validate and process.
3667 * @param peer sender of the message
3668 * @param msg the quota message
3671 GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer,
3672 const struct GNUNET_MessageHeader *msg)
3674 struct NeighbourMapEntry *n;
3675 const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3678 "Received QUOTA message from peer `%s'\n",
3680 if (ntohs (msg->size) != sizeof (struct GNUNET_ATS_SessionQuotaMessage))
3682 GNUNET_break_op (0);
3683 GNUNET_STATISTICS_update (GST_stats,
3684 gettext_noop ("# quota messages ignored (malformed)"),
3689 GNUNET_STATISTICS_update (GST_stats,
3691 ("# QUOTA messages received"),
3693 sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg;
3694 if (NULL == (n = lookup_neighbour (peer)))
3699 n->neighbour_receive_quota
3700 = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3701 GNUNET_BANDWIDTH_value_init (ntohl (sqm->quota)));
3702 send_outbound_quota_to_clients (n);
3707 * We received a disconnect message from the given peer,
3708 * validate and process.
3710 * @param peer sender of the message
3711 * @param msg the disconnect message
3714 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
3715 const struct GNUNET_MessageHeader *msg)
3717 struct NeighbourMapEntry *n;
3718 const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3720 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3721 "Received DISCONNECT message from peer `%s'\n",
3723 if (ntohs (msg->size) != sizeof (struct GNUNET_ATS_SessionDisconnectMessage))
3725 GNUNET_break_op (0);
3726 GNUNET_STATISTICS_update (GST_stats,
3728 ("# disconnect messages ignored (malformed)"),
3733 GNUNET_STATISTICS_update (GST_stats,
3735 ("# DISCONNECT messages received"),
3737 sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg;
3738 if (NULL == (n = lookup_neighbour (peer)))
3743 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3745 GNUNET_STATISTICS_update (GST_stats,
3746 gettext_noop ("# disconnect messages ignored (timestamp)"),
3751 if (0 != memcmp (peer,
3753 sizeof (struct GNUNET_PeerIdentity)))
3755 GNUNET_break_op (0);
3758 if (ntohl (sdm->purpose.size) !=
3759 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3760 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3761 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3764 "DISCONNECT message from peer `%s' has invalid size\n",
3766 GNUNET_break_op (0);
3770 GNUNET_CRYPTO_eddsa_verify (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3776 "DISCONNECT message from peer `%s' cannot be verified \n",
3778 GNUNET_break_op (0);
3781 if (NULL == n->delayed_disconnect_task)
3783 n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect,
3790 * Closure for the #neighbours_iterate() function.
3792 struct IteratorContext
3795 * Function to call on each connected neighbour.
3797 GST_NeighbourIterator cb;
3800 * Closure for @e cb.
3807 * Call the callback from the closure for each neighbour.
3809 * @param cls the `struct IteratorContext`
3810 * @param key the hash of the public key of the neighbour
3811 * @param value the `struct NeighbourMapEntry`
3812 * @return #GNUNET_OK (continue to iterate)
3815 neighbours_iterate (void *cls,
3816 const struct GNUNET_PeerIdentity *key,
3819 struct IteratorContext *ic = cls;
3820 struct NeighbourMapEntry *n = value;
3821 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3822 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3824 if (NULL != n->primary_address.address)
3826 bandwidth_in = n->primary_address.bandwidth_in;
3827 bandwidth_out = n->primary_address.bandwidth_out;
3831 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3832 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3836 n->primary_address.address,
3839 bandwidth_in, bandwidth_out);
3845 * Iterate over all connected neighbours.
3847 * @param cb function to call
3848 * @param cb_cls closure for @a cb
3851 GST_neighbours_iterate (GST_NeighbourIterator cb,
3854 struct IteratorContext ic;
3856 if (NULL == neighbours)
3857 return; /* can happen during shutdown */
3860 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3861 &neighbours_iterate,
3867 * If we have an active connection to the given target, it must be shutdown.
3869 * @param target peer to disconnect from
3872 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3874 struct NeighbourMapEntry *n;
3876 if (NULL == (n = lookup_neighbour (target)))
3877 return; /* not active */
3878 if (GNUNET_YES == test_connected (n))
3879 GNUNET_STATISTICS_update (GST_stats,
3880 gettext_noop ("# disconnected from peer upon explicit request"),
3883 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3884 "Forced disconnect from peer %s\n",
3885 GNUNET_i2s (target));
3886 disconnect_neighbour (n);
3891 * Obtain current address information for the given neighbour.
3894 * @return address currently used
3896 const struct GNUNET_HELLO_Address *
3897 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3899 struct NeighbourMapEntry *n;
3901 n = lookup_neighbour (peer);
3904 return n->primary_address.address;
3909 * Initialize the neighbours subsystem.
3911 * @param max_fds maximum number of fds to use
3914 GST_neighbours_start (unsigned int max_fds)
3916 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE,
3918 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3919 &utilization_transmission,
3925 * Disconnect from the given neighbour.
3928 * @param key hash of neighbour's public key (not used)
3929 * @param value the `struct NeighbourMapEntry` of the neighbour
3930 * @return #GNUNET_OK (continue to iterate)
3933 disconnect_all_neighbours (void *cls,
3934 const struct GNUNET_PeerIdentity *key,
3937 struct NeighbourMapEntry *n = value;
3939 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3940 "Disconnecting peer `%4s' during shutdown\n",
3941 GNUNET_i2s (&n->id));
3948 * Cleanup the neighbours subsystem.
3951 GST_neighbours_stop ()
3953 struct BlacklistCheckSwitchContext *cur;
3954 struct BlacklistCheckSwitchContext *next;
3956 if (NULL == neighbours)
3958 if (NULL != util_transmission_tk)
3960 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3961 util_transmission_tk = NULL;
3963 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3964 &disconnect_all_neighbours,
3966 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3968 next = pending_bc_head;
3969 for (cur = next; NULL != cur; cur = next)
3972 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
3976 if (NULL != cur->blc)
3978 GST_blacklist_test_cancel (cur->blc);
3986 /* end of file gnunet-service-transport_neighbours.c */