2 This file is part of GNUnet.
3 Copyright (C) 2010-2015 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file transport/gnunet-service-transport_neighbours.c
21 * @brief neighbour management
22 * @author Christian Grothoff
25 #include "gnunet_ats_service.h"
26 #include "gnunet-service-transport_ats.h"
27 #include "gnunet-service-transport_neighbours.h"
28 #include "gnunet-service-transport_manipulation.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet-service-transport_validation.h"
31 #include "gnunet-service-transport.h"
32 #include "gnunet_peerinfo_service.h"
33 #include "gnunet_constants.h"
34 #include "transport.h"
37 * Experimental option to ignore SessionQuotaMessages from
40 #define IGNORE_INBOUND_QUOTA GNUNET_YES
43 * Size of the neighbour hash map.
45 #define NEIGHBOUR_TABLE_SIZE 256
48 * Time we give plugin to transmit DISCONNECT message before the
49 * neighbour entry self-destructs.
51 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
54 * How often must a peer violate bandwidth quotas before we start
55 * to simply drop its messages?
57 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
60 * How long are we willing to wait for a response from ATS before timing out?
62 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
65 * How long are we willing to wait for an ACK from the other peer before
66 * giving up on our connect operation?
68 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
71 * How long are we willing to wait for a successful reconnect if
72 * an existing connection went down? Much shorter than the
73 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
74 * higher layers about the disconnect during this period.
76 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS
79 * Interval to send utilization data
81 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS
84 * State describing which kind a reply this neighbour should send
89 * We did not receive a SYN message for this neighbour
94 * The neighbour received a SYN message and has to send a SYN_ACK
100 * The neighbour sent a SYN_ACK message and has to send a ACK
107 GNUNET_NETWORK_STRUCT_BEGIN
110 * Message a peer sends to another to indicate that it intends to
111 * setup a connection/session for data exchange. A 'SESSION_SYN'
112 * should be answered with a 'SESSION_SYN_ACK' with the same body
113 * to confirm. A 'SESSION_SYN_ACK' should then be followed with
114 * a 'ACK'. Once the 'ACK' is received, both peers
115 * should be connected.
117 struct TransportSynMessage
120 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN
121 * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK
123 struct GNUNET_MessageHeader header;
128 uint32_t reserved GNUNET_PACKED;
131 * Absolute time at the sender. Only the most recent connect
132 * message implies which session is preferred by the sender.
134 struct GNUNET_TIME_AbsoluteNBO timestamp;
140 * Message a peer sends to another when connected to indicate that a
141 * session is in use and the peer is still alive or to respond to a keep alive.
142 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
143 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
144 * When the keep alive response with type is received, transport service
145 * will call the respective plugin to update the session timeout
147 struct GNUNET_ATS_SessionKeepAliveMessage
150 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
151 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
153 struct GNUNET_MessageHeader header;
156 * A nonce to identify the session the keep alive is used for
158 uint32_t nonce GNUNET_PACKED;
163 * Message a peer sends to another when connected to indicate that
164 * the other peer should limit transmissions to the indicated
167 struct GNUNET_ATS_SessionQuotaMessage
170 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA.
172 struct GNUNET_MessageHeader header;
175 * Quota to use (for sending), in bytes per second.
177 uint32_t quota GNUNET_PACKED;
182 * Message we send to the other peer to notify it that we intentionally
183 * are disconnecting (to reduce timeouts). This is just a friendly
184 * notification, peers must not rely on always receiving disconnect
187 struct GNUNET_ATS_SessionDisconnectMessage
190 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
192 struct GNUNET_MessageHeader header;
197 uint32_t reserved GNUNET_PACKED;
200 * Purpose of the signature. Extends over the timestamp.
201 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
203 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
206 * Absolute time at the sender. Only the most recent connect
207 * message implies which session is preferred by the sender.
209 struct GNUNET_TIME_AbsoluteNBO timestamp;
212 * Public key of the sender.
214 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
217 * Signature of the peer that sends us the disconnect. Only
218 * valid if the timestamp is AFTER the timestamp from the
219 * corresponding 'SYN' message.
221 struct GNUNET_CRYPTO_EddsaSignature signature;
225 GNUNET_NETWORK_STRUCT_END
229 * For each neighbour we keep a list of messages
230 * that we still want to transmit to the neighbour.
236 * This is a doubly linked list.
238 struct MessageQueue *next;
241 * This is a doubly linked list.
243 struct MessageQueue *prev;
246 * Function to call once we're done.
248 GST_NeighbourSendContinuation cont;
251 * Closure for @e cont
256 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
257 * stuck together in memory. Allocated at the end of this struct.
259 const char *message_buf;
262 * Size of the message buf
264 size_t message_buf_size;
267 * At what time should we fail?
269 struct GNUNET_TIME_Absolute timeout;
275 * A possible address we could use to communicate with a neighbour.
277 struct NeighbourAddress
281 * Active session for this address.
283 struct GNUNET_ATS_Session *session;
286 * Network-level address information.
288 struct GNUNET_HELLO_Address *address;
291 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
292 * peer for this address. Use to check that the ACK is in response
293 * to our most recent 'SYN'.
295 struct GNUNET_TIME_Absolute connect_timestamp;
298 * Inbound bandwidth from ATS for this address.
300 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
303 * Outbound bandwidth from ATS for this address.
305 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
308 * Did we tell ATS that this is our 'active' address?
313 * The current nonce sent in the last keep alive messages
315 uint32_t keep_alive_nonce;
320 * Entry in neighbours.
322 struct NeighbourMapEntry
326 * Head of list of messages we would like to send to this peer;
327 * must contain at most one message per client.
329 struct MessageQueue *messages_head;
332 * Tail of list of messages we would like to send to this peer; must
333 * contain at most one message per client.
335 struct MessageQueue *messages_tail;
338 * Are we currently trying to send a message? If so, which one?
340 struct MessageQueue *is_active;
343 * Primary address we currently use to communicate with the neighbour.
345 struct NeighbourAddress primary_address;
348 * Alternative address currently under consideration for communicating
349 * with the neighbour.
351 struct NeighbourAddress alternative_address;
354 * Identity of this neighbour.
356 struct GNUNET_PeerIdentity id;
359 * Main task that drives this peer (timeouts, keepalives, etc.).
360 * Always runs the #master_task().
362 struct GNUNET_SCHEDULER_Task *task;
365 * Task to disconnect neighbour after we received a DISCONNECT message
367 struct GNUNET_SCHEDULER_Task *delayed_disconnect_task;
370 * At what time should we sent the next keep-alive message?
372 struct GNUNET_TIME_Absolute keep_alive_time;
375 * At what time did we sent the last keep-alive message? Used
376 * to calculate round-trip time ("latency").
378 struct GNUNET_TIME_Absolute last_keep_alive_time;
381 * Timestamp we should include in our next SYN_ACK message.
382 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
383 * our SYN_ACK message.
385 struct GNUNET_TIME_Absolute connect_ack_timestamp;
388 * ATS address suggest handle
390 struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle;
393 * Time where we should cut the connection (timeout) if we don't
394 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
395 * if we are in #GNUNET_TRANSPORT_PS_CONNECTED).
397 struct GNUNET_TIME_Absolute timeout;
400 * Tracker for inbound bandwidth.
402 struct GNUNET_BANDWIDTH_Tracker in_tracker;
405 * How often has the other peer (recently) violated the inbound
406 * traffic limit? Incremented by 10 per violation, decremented by 1
407 * per non-violation (for each time interval).
409 unsigned int quota_violation_count;
412 * Latest quota the other peer send us in bytes per second.
413 * We should not send more, least the other peer throttle
414 * receiving our traffic.
416 struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota;
419 * The current state of the peer.
421 enum GNUNET_TRANSPORT_PeerState state;
424 * Did we sent an KEEP_ALIVE message and are we expecting a response?
426 int expect_latency_response;
429 * When a peer wants to connect we have to reply to the 1st SYN message
430 * with a SYN_ACK message. But sometime we cannot send this message
431 * immediately since we do not have an address and then we have to remember
432 * to send this message as soon as we have an address.
434 * Flag to set if we still need to send a SYN_ACK message to the other peer
435 * (once we have an address to use and the peer has been allowed by our
436 * blacklist). Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK
437 * if we need to send a SYN_ACK. Set to #ACK_SEND_ACK if we did
438 * send a SYN_ACK and should go to #S_CONNECTED upon receiving a
439 * 'ACK' (regardless of what our own state machine might say).
441 enum GST_ACK_State ack_state;
444 * Tracking utilization of outbound bandwidth
446 uint32_t util_total_bytes_sent;
449 * Tracking utilization of inbound bandwidth
451 uint32_t util_total_bytes_recv;
454 * Date of last utilization transmission
456 struct GNUNET_TIME_Absolute last_util_transmission;
461 * Hash map from peer identities to the respective `struct NeighbourMapEntry`.
463 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
466 * List of pending blacklist checks: head
468 static struct BlacklistCheckSwitchContext *pending_bc_head;
471 * List of pending blacklist checks: tail
473 static struct BlacklistCheckSwitchContext *pending_bc_tail;
476 * counter for connected neighbours
478 static unsigned int neighbours_connected;
481 * Number of bytes we have currently queued for transmission.
483 static unsigned long long bytes_in_send_queue;
486 * Task transmitting utilization data
488 static struct GNUNET_SCHEDULER_Task *util_transmission_tk;
492 * Convert the given ACK state to a string.
495 * @return corresponding human-readable string
498 print_ack_state (enum GST_ACK_State s)
503 case ACK_SEND_SYN_ACK:
504 return "SEND_SYN_ACK";
515 * Send information about a new outbound quota to our clients.
516 * Note that the outbound quota is enforced client-side (i.e.
517 * in libgnunettransport).
519 * @param n affected peer
522 send_outbound_quota_to_clients (struct NeighbourMapEntry *n)
524 struct QuotaSetMessage q_msg;
525 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
527 if (! GNUNET_TRANSPORT_is_connected (n->state))
529 #if IGNORE_INBOUND_QUOTA
530 bandwidth_min = n->primary_address.bandwidth_out;
532 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
533 n->neighbour_receive_quota);
536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
538 ntohl (bandwidth_min.value__),
539 GNUNET_i2s (&n->id));
540 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
541 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
542 q_msg.quota = bandwidth_min;
544 GST_clients_broadcast (&q_msg.header,
550 * Notify our clients that another peer connected to us.
552 * @param n the peer that connected
555 neighbours_connect_notification (struct NeighbourMapEntry *n)
557 size_t len = sizeof(struct ConnectInfoMessage);
558 char buf[len] GNUNET_ALIGN;
559 struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
560 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
562 #if IGNORE_INBOUND_QUOTA
563 bandwidth_min = n->primary_address.bandwidth_out;
565 bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
566 n->neighbour_receive_quota);
568 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
569 "We are now connected to peer `%s'\n",
570 GNUNET_i2s (&n->id));
571 connect_msg->header.size = htons (sizeof(buf));
572 connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
573 connect_msg->id = n->id;
574 connect_msg->quota_out = bandwidth_min;
575 GST_clients_broadcast (&connect_msg->header,
581 * Notify our clients (and manipulation) that a peer disconnected from
584 * @param n the peer that disconnected
587 neighbours_disconnect_notification (struct NeighbourMapEntry *n)
589 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590 "Peer `%s' disconnected\n",
591 GNUNET_i2s (&n->id));
592 GST_manipulation_peer_disconnect (&n->id);
593 GST_clients_broadcast_disconnect (&n->id);
598 * Notify transport clients that a neighbour peer changed its active
601 * @param peer identity of the peer
602 * @param address address possibly NULL if peer is not connected
603 * @param state current state this peer is in
604 * @param state_timeout timeout for the current state of the peer
605 * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
606 * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
609 neighbours_changed_notification (const struct GNUNET_PeerIdentity *peer,
610 const struct GNUNET_HELLO_Address *address,
611 enum GNUNET_TRANSPORT_PeerState state,
612 struct GNUNET_TIME_Absolute state_timeout,
613 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
614 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
617 (void) bandwidth_out;
618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619 "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
621 GST_plugins_a2s (address),
622 GNUNET_TRANSPORT_ps2s (state),
623 GNUNET_STRINGS_absolute_time_to_string (state_timeout));
624 /* FIXME: include bandwidth in notification! */
625 GST_clients_broadcast_peer_notification (peer,
633 * Lookup a neighbour entry in the neighbours hash map.
635 * @param pid identity of the peer to look up
636 * @return the entry, NULL if there is no existing record
638 static struct NeighbourMapEntry *
639 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
641 if (NULL == neighbours)
643 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
648 * Test if we're connected to the given peer.
650 * @param n neighbour entry of peer to test
651 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
654 test_connected (struct NeighbourMapEntry *n)
658 return GNUNET_TRANSPORT_is_connected (n->state);
663 * We don't need a given neighbour address any more.
664 * Release its resources and give appropriate notifications
665 * to ATS and other subsystems.
667 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
670 free_address (struct NeighbourAddress *na)
672 if (GNUNET_YES == na->ats_active)
673 GST_validation_set_address_use (na->address,
675 if (NULL != na->address)
677 GST_ats_block_address (na->address,
679 GNUNET_HELLO_address_free (na->address);
682 na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
683 na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
684 na->ats_active = GNUNET_NO;
685 na->keep_alive_nonce = 0;
691 * Master task run for every neighbour. Performs all of the time-related
692 * activities (keep alive, send next message, disconnect if idle, finish
693 * clean up after disconnect).
695 * @param cls the `struct NeighbourMapEntry` for which we are running
698 master_task (void *cls);
702 * Set net state and state timeout for this neighbour and notify monitoring
704 * @param n the respective neighbour
705 * @param s the new state
706 * @param timeout the new timeout
709 set_state_and_timeout (struct NeighbourMapEntry *n,
710 enum GNUNET_TRANSPORT_PeerState s,
711 struct GNUNET_TIME_Absolute timeout)
713 if (GNUNET_TRANSPORT_is_connected (s) &&
714 ! GNUNET_TRANSPORT_is_connected (n->state) )
716 neighbours_connect_notification (n);
717 GNUNET_STATISTICS_set (GST_stats,
718 gettext_noop ("# peers connected"),
719 ++neighbours_connected,
722 if (! GNUNET_TRANSPORT_is_connected (s) &&
723 GNUNET_TRANSPORT_is_connected (n->state) )
725 GNUNET_STATISTICS_set (GST_stats,
726 gettext_noop ("# peers connected"),
727 --neighbours_connected,
729 neighbours_disconnect_notification (n);
732 if ( (timeout.abs_value_us < n->timeout.abs_value_us) &&
735 /* new timeout is earlier, reschedule master task */
736 GNUNET_SCHEDULER_cancel (n->task);
737 n->task = GNUNET_SCHEDULER_add_at (timeout,
741 n->timeout = timeout;
742 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
743 "Neighbour `%s' changed state to %s with timeout %s\n",
745 GNUNET_TRANSPORT_ps2s(s),
746 GNUNET_STRINGS_absolute_time_to_string (timeout));
747 neighbours_changed_notification (&n->id,
748 n->primary_address.address,
751 n->primary_address.bandwidth_in,
752 n->primary_address.bandwidth_out);
757 * Initialize the alternative address of a neighbour
759 * @param n the neighbour
760 * @param address address of the other peer, NULL if other peer
762 * @param session session to use (or NULL, in which case an
763 * address must be setup)
764 * @param bandwidth_in inbound quota to be used when connection is up
765 * @param bandwidth_out outbound quota to be used when connection is up
768 set_alternative_address (struct NeighbourMapEntry *n,
769 const struct GNUNET_HELLO_Address *address,
770 struct GNUNET_ATS_Session *session,
771 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
772 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
774 struct GNUNET_TRANSPORT_PluginFunctions *papi;
776 if (NULL == (papi = GST_plugins_find (address->transport_name)))
781 if (session == n->alternative_address.session)
783 n->alternative_address.bandwidth_in = bandwidth_in;
784 n->alternative_address.bandwidth_out = bandwidth_out;
787 if (NULL != n->alternative_address.address)
789 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
790 "Replacing existing alternative address with another one\n");
791 free_address (&n->alternative_address);
794 session = papi->get_session (papi->cls,
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Failed to obtain new session for peer `%s' and address '%s'\n",
800 GNUNET_i2s (&address->peer),
801 GST_plugins_a2s (address));
802 GNUNET_STATISTICS_update (GST_stats,
803 gettext_noop ("# session creation failed"),
808 GST_ats_new_session (address,
810 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
811 "Neighbour `%s' configured alternative address %s\n",
813 GST_plugins_a2s(address));
815 n->alternative_address.address = GNUNET_HELLO_address_copy (address);
816 n->alternative_address.bandwidth_in = bandwidth_in;
817 n->alternative_address.bandwidth_out = bandwidth_out;
818 n->alternative_address.session = session;
819 n->alternative_address.ats_active = GNUNET_NO;
820 n->alternative_address.keep_alive_nonce = 0;
821 GNUNET_assert (GNUNET_YES ==
822 GST_ats_is_known (n->alternative_address.address,
823 n->alternative_address.session));
828 * Transmit a message using the current session of the given
831 * @param n entry for the recipient
832 * @param msgbuf buffer to transmit
833 * @param msgbuf_size number of bytes in @a msgbuf buffer
834 * @param priority transmission priority
835 * @param timeout transmission timeout
836 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
837 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
838 * @param cont continuation to call when finished (can be NULL)
839 * @param cont_cls closure for @a cont
840 * @return timeout (copy of @a timeout or a calculated one if
841 * @a use_keepalive_timeout is #GNUNET_YES.
843 static struct GNUNET_TIME_Relative
844 send_with_session (struct NeighbourMapEntry *n,
848 struct GNUNET_TIME_Relative timeout,
849 unsigned int use_keepalive_timeout,
850 GNUNET_TRANSPORT_TransmitContinuation cont,
853 struct GNUNET_TRANSPORT_PluginFunctions *papi;
854 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
856 GNUNET_assert (NULL != n->primary_address.session);
857 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
858 (-1 == papi->send (papi->cls,
859 n->primary_address.session,
863 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
864 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
865 papi->query_keepalive_factor (papi->cls))),
874 GST_neighbours_notify_data_sent (n->primary_address.address,
875 n->primary_address.session,
877 GNUNET_break (NULL != papi);
883 * Clear the primary address of a neighbour since this address is not
884 * valid anymore and notify monitoring about it
886 * @param n the neighbour
889 unset_primary_address (struct NeighbourMapEntry *n)
891 /* Notify monitoring about change */
892 if (NULL == n->primary_address.address)
894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895 "Disabling primary address\n");
896 neighbours_changed_notification (&n->id,
897 n->primary_address.address,
900 GNUNET_BANDWIDTH_value_init (0),
901 GNUNET_BANDWIDTH_value_init (0));
902 free_address (&n->primary_address);
907 * Free a neighbour map entry.
909 * @param n entry to free
912 free_neighbour (struct NeighbourMapEntry *n)
914 struct MessageQueue *mq;
916 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
917 "Freeing neighbour state of peer `%s'\n",
918 GNUNET_i2s (&n->id));
919 n->is_active = NULL; /* always free'd by its own continuation! */
921 /* fail messages currently in the queue */
922 while (NULL != (mq = n->messages_head))
924 GNUNET_CONTAINER_DLL_remove (n->messages_head,
927 if (NULL != mq->cont)
928 mq->cont (mq->cont_cls,
930 mq->message_buf_size,
934 /* Mark peer as disconnected */
935 set_state_and_timeout (n,
936 GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED,
937 GNUNET_TIME_UNIT_FOREVER_ABS);
938 /* free addresses and mark as unused */
939 unset_primary_address (n);
941 if (NULL != n->alternative_address.address)
943 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
944 "Cleaning up alternative address\n");
945 free_address (&n->alternative_address);
947 GNUNET_assert (GNUNET_YES ==
948 GNUNET_CONTAINER_multipeermap_remove (neighbours,
951 /* Cancel address requests for this peer */
952 if (NULL != n->suggest_handle)
954 GNUNET_ATS_connectivity_suggest_cancel (n->suggest_handle);
955 n->suggest_handle = NULL;
958 /* Cancel the disconnect task */
959 if (NULL != n->delayed_disconnect_task)
961 GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task);
962 n->delayed_disconnect_task = NULL;
965 /* Cancel the master task */
968 GNUNET_SCHEDULER_cancel (n->task);
971 /* free rest of memory */
977 * Function called when the 'DISCONNECT' message has been sent by the
978 * plugin. Frees the neighbour --- if the entry still exists.
981 * @param target identity of the neighbour that was disconnected
982 * @param result #GNUNET_OK if the disconnect got out successfully
983 * @param payload bytes payload
984 * @param physical bytes on wire
987 send_disconnect_cont (void *cls,
988 const struct GNUNET_PeerIdentity *target,
993 struct NeighbourMapEntry *n;
999 n = lookup_neighbour (target);
1001 return; /* already gone */
1002 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
1003 return; /* have created a fresh entry since */
1004 if (NULL != n->task)
1005 GNUNET_SCHEDULER_cancel (n->task);
1006 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1011 * Transmit a DISCONNECT message to the other peer.
1013 * @param n neighbour to send DISCONNECT message.
1016 send_disconnect (struct NeighbourMapEntry *n)
1018 struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg;
1020 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1021 "Sending DISCONNECT message to peer `%4s'\n",
1022 GNUNET_i2s (&n->id));
1023 disconnect_msg.header.size = htons (sizeof (struct GNUNET_ATS_SessionDisconnectMessage));
1024 disconnect_msg.header.type =
1025 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1026 disconnect_msg.reserved = htonl (0);
1027 disconnect_msg.purpose.size =
1028 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1029 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1030 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1031 disconnect_msg.purpose.purpose =
1032 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1033 disconnect_msg.timestamp =
1034 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1035 disconnect_msg.public_key = GST_my_identity.public_key;
1036 GNUNET_assert (GNUNET_OK ==
1037 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1038 &disconnect_msg.purpose,
1039 &disconnect_msg.signature));
1041 (void) send_with_session (n,
1043 sizeof (disconnect_msg),
1045 GNUNET_TIME_UNIT_FOREVER_REL,
1047 &send_disconnect_cont,
1049 GNUNET_STATISTICS_update (GST_stats,
1050 gettext_noop ("# DISCONNECT messages sent"),
1057 * Disconnect from the given neighbour, clean up the record.
1059 * @param n neighbour to disconnect from
1062 disconnect_neighbour (struct NeighbourMapEntry *n)
1064 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1065 "Disconnecting from peer %s in state %s\n",
1066 GNUNET_i2s (&n->id),
1067 GNUNET_TRANSPORT_ps2s (n->state));
1068 /* depending on state, notify neighbour and/or upper layers of this peer
1072 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1073 case GNUNET_TRANSPORT_PS_INIT_ATS:
1074 /* other peer is completely unaware of us, no need to send DISCONNECT */
1077 case GNUNET_TRANSPORT_PS_SYN_SENT:
1078 send_disconnect (n);
1079 set_state_and_timeout (n,
1080 GNUNET_TRANSPORT_PS_DISCONNECT,
1081 GNUNET_TIME_UNIT_FOREVER_ABS);
1083 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
1084 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1087 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
1088 /* we DID ACK the other peer's request, must send DISCONNECT */
1089 send_disconnect (n);
1090 set_state_and_timeout (n,
1091 GNUNET_TRANSPORT_PS_DISCONNECT,
1092 GNUNET_TIME_UNIT_FOREVER_ABS);
1094 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1095 case GNUNET_TRANSPORT_PS_CONNECTED:
1096 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1097 /* we are currently connected, need to send disconnect and do
1098 internal notifications and update statistics */
1099 send_disconnect (n);
1100 set_state_and_timeout (n,
1101 GNUNET_TRANSPORT_PS_DISCONNECT,
1102 GNUNET_TIME_UNIT_FOREVER_ABS);
1104 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1105 /* Disconnecting while waiting for an ATS address to reconnect,
1106 * cannot send DISCONNECT */
1109 case GNUNET_TRANSPORT_PS_DISCONNECT:
1110 /* already disconnected, ignore */
1112 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1113 /* already cleaned up, how did we get here!? */
1117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1118 "Unhandled state `%s'\n",
1119 GNUNET_TRANSPORT_ps2s (n->state));
1123 /* schedule timeout to clean up */
1124 if (NULL != n->task)
1125 GNUNET_SCHEDULER_cancel (n->task);
1126 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1133 * Change the incoming quota for the given peer. Updates
1134 * our own receive rate and informs the neighbour about
1137 * @param n neighbour entry to change quota for
1138 * @param quota new quota
1139 * @return #GNUNET_YES if @a n is still valid, #GNUNET_NO if
1143 set_incoming_quota (struct NeighbourMapEntry *n,
1144 struct GNUNET_BANDWIDTH_Value32NBO quota)
1146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
1148 ntohl (quota.value__), GNUNET_i2s (&n->id));
1149 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
1151 if (0 != ntohl (quota.value__))
1153 struct GNUNET_ATS_SessionQuotaMessage sqm;
1155 sqm.header.size = htons (sizeof (struct GNUNET_ATS_SessionQuotaMessage));
1156 sqm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA);
1157 sqm.quota = quota.value__;
1158 if (NULL != n->primary_address.session)
1159 (void) send_with_session (n,
1163 GNUNET_TIME_UNIT_FOREVER_REL,
1168 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1169 "Disconnecting peer `%s' due to SET_QUOTA\n",
1170 GNUNET_i2s (&n->id));
1171 if (GNUNET_YES == test_connected (n))
1172 GNUNET_STATISTICS_update (GST_stats,
1173 gettext_noop ("# disconnects due to quota of 0"),
1175 disconnect_neighbour (n);
1181 * Initialize the primary address of a neighbour
1183 * @param n the neighbour
1184 * @param address address of the other peer, NULL if other peer
1186 * @param session session to use (or NULL, in which case an
1187 * address must be setup)
1188 * @param bandwidth_in inbound quota to be used when connection is up
1189 * @param bandwidth_out outbound quota to be used when connection is up
1192 set_primary_address (struct NeighbourMapEntry *n,
1193 const struct GNUNET_HELLO_Address *address,
1194 struct GNUNET_ATS_Session *session,
1195 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1196 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
1198 if (session == n->primary_address.session)
1200 GST_validation_set_address_use (n->primary_address.address,
1202 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
1204 n->primary_address.bandwidth_in = bandwidth_in;
1206 set_incoming_quota (n,
1210 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
1212 n->primary_address.bandwidth_out = bandwidth_out;
1213 send_outbound_quota_to_clients (n);
1217 if ( (NULL != n->primary_address.address) &&
1218 (0 == GNUNET_HELLO_address_cmp (address,
1219 n->primary_address.address)) )
1224 if (NULL == session)
1227 GST_ats_block_address (address,
1231 if (NULL != n->primary_address.address)
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1234 "Replacing existing primary address with another one\n");
1235 free_address (&n->primary_address);
1237 n->primary_address.address = GNUNET_HELLO_address_copy (address);
1238 n->primary_address.bandwidth_in = bandwidth_in;
1239 n->primary_address.bandwidth_out = bandwidth_out;
1240 n->primary_address.session = session;
1241 n->primary_address.keep_alive_nonce = 0;
1242 GNUNET_assert (GNUNET_YES ==
1243 GST_ats_is_known (n->primary_address.address,
1244 n->primary_address.session));
1245 /* subsystems about address use */
1246 GST_validation_set_address_use (n->primary_address.address,
1249 set_incoming_quota (n,
1252 send_outbound_quota_to_clients (n);
1253 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1254 "Neighbour `%s' switched to address `%s'\n",
1255 GNUNET_i2s (&n->id),
1256 GST_plugins_a2s(address));
1258 neighbours_changed_notification (&n->id,
1259 n->primary_address.address,
1262 n->primary_address.bandwidth_in,
1263 n->primary_address.bandwidth_out);
1268 * We're done with our transmission attempt, continue processing.
1270 * @param cls the `struct MessageQueue` of the message
1271 * @param receiver intended receiver
1272 * @param success whether it worked or not
1273 * @param size_payload bytes payload sent
1274 * @param physical bytes sent on wire
1277 transmit_send_continuation (void *cls,
1278 const struct GNUNET_PeerIdentity *receiver,
1280 size_t size_payload,
1283 struct MessageQueue *mq = cls;
1284 struct NeighbourMapEntry *n;
1286 if (NULL == (n = lookup_neighbour (receiver)))
1288 if (NULL != mq->cont)
1289 mq->cont (mq->cont_cls,
1290 GNUNET_SYSERR /* not connected */,
1294 return; /* disconnect or other error while transmitting, can happen */
1296 if (n->is_active == mq)
1298 /* this is still "our" neighbour, remove us from its queue
1299 and allow it to send the next message now */
1300 n->is_active = NULL;
1301 if (NULL != n->task)
1302 GNUNET_SCHEDULER_cancel (n->task);
1303 n->task = GNUNET_SCHEDULER_add_now (&master_task,
1306 if (bytes_in_send_queue < mq->message_buf_size)
1308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1309 "Bytes_in_send_queue `%llu', Message_size %u, result: %s, payload %u, on wire %u\n",
1310 bytes_in_send_queue,
1311 (unsigned int) mq->message_buf_size,
1312 (GNUNET_OK == success) ? "OK" : "FAIL",
1313 (unsigned int) size_payload,
1314 (unsigned int) physical);
1318 GNUNET_break (size_payload == mq->message_buf_size);
1319 bytes_in_send_queue -= mq->message_buf_size;
1320 GNUNET_STATISTICS_set (GST_stats,
1321 gettext_noop ("# bytes in message queue for other peers"),
1322 bytes_in_send_queue,
1324 if (GNUNET_OK == success)
1325 GNUNET_STATISTICS_update (GST_stats,
1326 gettext_noop ("# messages transmitted to other peers"),
1330 GNUNET_STATISTICS_update (GST_stats,
1332 ("# transmission failures for messages to other peers"),
1334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1335 "Sending message to `%s' of type %u with %u bytes was a %s\n",
1336 GNUNET_i2s (receiver),
1337 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1338 (unsigned int) mq->message_buf_size,
1339 (success == GNUNET_OK) ? "success" : "FAILURE");
1340 if (NULL != mq->cont)
1341 mq->cont (mq->cont_cls,
1350 * Check the message list for the given neighbour and if we can
1351 * send a message, do so. This function should only be called
1352 * if the connection is at least generally ready for transmission.
1353 * While we will only send one message at a time, no bandwidth
1354 * quota management is performed here. If a message was given to
1355 * the plugin, the continuation will automatically re-schedule
1356 * the 'master' task once the next message might be transmitted.
1358 * @param n target peer for which to transmit
1361 try_transmission_to_peer (struct NeighbourMapEntry *n)
1363 struct MessageQueue *mq;
1364 struct GNUNET_TIME_Relative timeout;
1366 if (NULL == n->primary_address.address)
1368 /* no address, why are we here? */
1372 if ((0 == n->primary_address.address->address_length) &&
1373 (NULL == n->primary_address.session))
1375 /* no address, why are we here? */
1379 if (NULL != n->is_active)
1381 /* transmission already pending */
1385 /* timeout messages from the queue that are past their due date */
1386 while (NULL != (mq = n->messages_head))
1388 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1389 if (timeout.rel_value_us > 0)
1391 GNUNET_STATISTICS_update (GST_stats,
1392 gettext_noop ("# messages timed out while in transport queue"),
1395 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1399 transmit_send_continuation (mq,
1402 mq->message_buf_size,
1406 return; /* no more messages */
1407 if (NULL == n->primary_address.address)
1409 /* transmit_send_continuation() caused us to drop session,
1410 can't try transmission anymore. */
1415 GNUNET_CONTAINER_DLL_remove (n->messages_head,
1420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421 "Giving message with %u bytes to plugin session %p\n",
1422 (unsigned int) mq->message_buf_size,
1423 n->primary_address.session);
1424 (void) send_with_session (n,
1426 mq->message_buf_size,
1430 &transmit_send_continuation,
1436 * Send keepalive message to the neighbour. Must only be called
1437 * if we are on 'connected' state or while trying to switch addresses.
1438 * Will internally determine if a keepalive is truly needed (so can
1439 * always be called).
1441 * @param n neighbour that went idle and needs a keepalive
1444 send_keepalive (struct NeighbourMapEntry *n)
1446 struct GNUNET_ATS_SessionKeepAliveMessage m;
1447 struct GNUNET_TIME_Relative timeout;
1450 GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1451 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state));
1452 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1453 return; /* no keepalive needed at this time */
1455 nonce = 0; /* 0 indicates 'not set' */
1457 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1461 "Sending KEEPALIVE to peer `%s' with nonce %u\n",
1462 GNUNET_i2s (&n->id),
1464 m.header.size = htons (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage));
1465 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1466 m.nonce = htonl (nonce);
1468 timeout = send_with_session (n,
1471 UINT32_MAX /* priority */,
1472 GNUNET_TIME_UNIT_FOREVER_REL,
1475 GNUNET_STATISTICS_update (GST_stats,
1476 gettext_noop ("# KEEPALIVES sent"),
1479 n->primary_address.keep_alive_nonce = nonce;
1480 n->expect_latency_response = GNUNET_YES;
1481 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1482 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1487 * Keep the connection to the given neighbour alive longer,
1488 * we received a KEEPALIVE (or equivalent); send a response.
1490 * @param neighbour neighbour to keep alive (by sending keep alive response)
1491 * @param m the keep alive message containing the nonce to respond to
1494 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1495 const struct GNUNET_MessageHeader *m)
1497 struct NeighbourMapEntry *n;
1498 const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in;
1499 struct GNUNET_ATS_SessionKeepAliveMessage msg;
1501 if (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1503 GNUNET_break_op (0);
1507 msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1508 if (NULL == (n = lookup_neighbour (neighbour)))
1510 GNUNET_STATISTICS_update (GST_stats,
1512 ("# KEEPALIVE messages discarded (peer unknown)"),
1516 if (NULL == n->primary_address.session)
1518 GNUNET_STATISTICS_update (GST_stats,
1520 ("# KEEPALIVE messages discarded (no session)"),
1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1526 "Received KEEPALIVE request from peer `%s' with nonce %u\n",
1527 GNUNET_i2s (&n->id),
1528 ntohl (msg_in->nonce));
1529 GNUNET_STATISTICS_update (GST_stats,
1530 gettext_noop ("# KEEPALIVES received in good order"),
1534 /* send reply to allow neighbour to measure latency */
1535 msg.header.size = htons (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage));
1536 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1537 msg.nonce = msg_in->nonce;
1538 (void) send_with_session (n,
1540 sizeof (struct GNUNET_ATS_SessionKeepAliveMessage),
1541 UINT32_MAX /* priority */,
1542 GNUNET_TIME_UNIT_FOREVER_REL,
1549 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1550 * latency to this peer. Pass the updated information (existing ats
1551 * plus calculated latency) to ATS.
1553 * @param neighbour neighbour to keep alive
1554 * @param m the message containing the keep alive response
1557 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1558 const struct GNUNET_MessageHeader *m)
1560 struct NeighbourMapEntry *n;
1561 const struct GNUNET_ATS_SessionKeepAliveMessage *msg;
1562 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1563 struct GNUNET_TIME_Relative latency;
1565 if (sizeof (struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1567 GNUNET_break_op (0);
1571 msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1572 if (NULL == (n = lookup_neighbour (neighbour)))
1574 GNUNET_STATISTICS_update (GST_stats,
1575 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (not connected)"),
1580 if ( (GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1581 (GNUNET_YES != n->expect_latency_response) )
1583 GNUNET_STATISTICS_update (GST_stats,
1584 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (not expected)"),
1589 if (NULL == n->primary_address.address)
1591 GNUNET_STATISTICS_update (GST_stats,
1592 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (address changed)"),
1597 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1599 if (0 == n->primary_address.keep_alive_nonce)
1600 GNUNET_STATISTICS_update (GST_stats,
1601 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (no nonce)"),
1605 GNUNET_STATISTICS_update (GST_stats,
1606 gettext_noop ("# KEEPALIVE_RESPONSEs discarded (bad nonce)"),
1611 GNUNET_STATISTICS_update (GST_stats,
1612 gettext_noop ("# KEEPALIVE_RESPONSEs received (OK)"),
1617 /* Update session timeout here */
1618 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1621 "Updating session for peer `%s' for session %p\n",
1622 GNUNET_i2s (&n->id),
1623 n->primary_address.session);
1624 papi->update_session_timeout (papi->cls,
1626 n->primary_address.session);
1633 n->primary_address.keep_alive_nonce = 0;
1634 n->expect_latency_response = GNUNET_NO;
1635 set_state_and_timeout (n,
1637 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1639 latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1641 "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n",
1642 GNUNET_i2s (&n->id),
1643 GNUNET_STRINGS_relative_time_to_string (latency,
1645 GST_ats_update_delay (n->primary_address.address,
1646 GNUNET_TIME_relative_divide (latency,
1652 * We have received a message from the given sender. How long should
1653 * we delay before receiving more? (Also used to keep the peer marked
1656 * @param sender sender of the message
1657 * @param size size of the message
1658 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1659 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1660 * #GNUNET_SYSERR if the connection is not fully up yet
1661 * @return how long to wait before reading more from this sender
1663 struct GNUNET_TIME_Relative
1664 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity *sender,
1668 struct NeighbourMapEntry *n;
1669 struct GNUNET_TIME_Relative ret;
1671 if (NULL == neighbours)
1673 *do_forward = GNUNET_NO;
1674 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1676 if (NULL == (n = lookup_neighbour (sender)))
1678 GNUNET_STATISTICS_update (GST_stats,
1679 gettext_noop ("# messages discarded due to lack of neighbour record"),
1682 *do_forward = GNUNET_NO;
1683 return GNUNET_TIME_UNIT_ZERO;
1685 if (! test_connected (n))
1687 *do_forward = GNUNET_SYSERR;
1688 return GNUNET_TIME_UNIT_ZERO;
1690 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1692 n->quota_violation_count++;
1693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1694 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1695 n->in_tracker.available_bytes_per_s__,
1696 n->quota_violation_count);
1697 /* Discount 32k per violation */
1698 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1702 if (n->quota_violation_count > 0)
1704 /* try to add 32k back */
1705 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1706 n->quota_violation_count--;
1709 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1711 GNUNET_STATISTICS_update (GST_stats,
1713 ("# bandwidth quota violations by other peers"),
1715 *do_forward = GNUNET_NO;
1716 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1718 *do_forward = GNUNET_YES;
1719 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1720 if (ret.rel_value_us > 0)
1722 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1723 "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n",
1724 (long long) n->in_tracker.consumption_since_last_update__,
1725 (unsigned int) n->in_tracker.available_bytes_per_s__,
1726 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1727 GNUNET_STATISTICS_update (GST_stats,
1728 gettext_noop ("# ms throttling suggested"),
1729 (int64_t) ret.rel_value_us / 1000LL,
1737 * Transmit a message to the given target using the active connection.
1739 * @param target destination
1740 * @param msg message to send
1741 * @param msg_size number of bytes in msg
1742 * @param timeout when to fail with timeout
1743 * @param cont function to call when done
1744 * @param cont_cls closure for @a cont
1747 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
1750 struct GNUNET_TIME_Relative timeout,
1751 GST_NeighbourSendContinuation cont,
1754 struct NeighbourMapEntry *n;
1755 struct MessageQueue *mq;
1757 /* All ove these cases should never happen; they are all API violations.
1758 But we check anyway, just to be sure. */
1759 if (NULL == (n = lookup_neighbour (target)))
1769 if (GNUNET_YES != test_connected (n))
1779 bytes_in_send_queue += msg_size;
1780 GNUNET_STATISTICS_set (GST_stats,
1782 ("# bytes in message queue for other peers"),
1783 bytes_in_send_queue, GNUNET_NO);
1784 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1786 mq->cont_cls = cont_cls;
1787 GNUNET_memcpy (&mq[1], msg, msg_size);
1788 mq->message_buf = (const char *) &mq[1];
1789 mq->message_buf_size = msg_size;
1790 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1793 "Enqueueing %u bytes to send to peer %s\n",
1794 (unsigned int) msg_size,
1795 GNUNET_i2s (target));
1796 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
1799 if (NULL != n->task)
1800 GNUNET_SCHEDULER_cancel (n->task);
1801 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1806 * Continuation called from our attempt to transmitted our
1807 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1808 * target. Continue processing based on the @a result. Specifically,
1809 * if we failed to transmit, discard the address we used.
1812 * @param target which peer received the transmission
1813 * @param result #GNUNET_OK if sending worked
1814 * @param size_payload how many bytes of payload were sent (ignored)
1815 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1818 send_session_syn_cont (void *cls,
1819 const struct GNUNET_PeerIdentity *target,
1821 size_t size_payload,
1822 size_t size_on_wire)
1824 struct NeighbourMapEntry *n;
1827 (void) size_payload;
1828 (void) size_on_wire;
1829 n = lookup_neighbour (target);
1832 /* SYN continuation was called after neighbor was freed,
1833 * for example due to a time out for the state or the session
1834 * used was already terminated: nothing to do here... */
1838 if ( (GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1839 (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1840 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1842 /* SYN continuation was called after neighbor changed state,
1843 * for example due to a time out for the state or the session
1844 * used was already terminated: nothing to do here... */
1847 if (GNUNET_OK == result)
1850 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1851 _("Failed to send SYN message to peer `%s'\n"),
1852 GNUNET_i2s (target));
1854 case GNUNET_TRANSPORT_PS_SYN_SENT:
1855 /* Remove address and request an additional one */
1856 unset_primary_address (n);
1857 set_state_and_timeout (n,
1858 GNUNET_TRANSPORT_PS_INIT_ATS,
1859 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
1861 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1862 /* Remove address and request an additional one */
1863 unset_primary_address (n);
1864 set_state_and_timeout (n,
1865 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1866 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1868 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1869 /* Remove address and request and go back to primary address */
1870 GNUNET_STATISTICS_update (GST_stats,
1871 gettext_noop ("# Failed attempts to switch addresses (failed to send SYN CONT)"),
1874 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1875 "Switch failed, cleaning up alternative address\n");
1876 free_address (&n->alternative_address);
1877 set_state_and_timeout (n,
1878 GNUNET_TRANSPORT_PS_CONNECTED,
1879 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1882 disconnect_neighbour (n);
1889 * Send a SYN message via the given address.
1891 * @param na address to use
1894 send_syn (struct NeighbourAddress *na)
1896 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1897 struct TransportSynMessage connect_msg;
1898 struct NeighbourMapEntry *n;
1900 GNUNET_assert (NULL != na->session);
1901 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1902 "Sending SYN message to peer `%s' at %s\n",
1903 GNUNET_i2s (&na->address->peer),
1904 GST_plugins_a2s (na->address));
1906 papi = GST_plugins_find (na->address->transport_name);
1907 GNUNET_assert (NULL != papi);
1908 GNUNET_STATISTICS_update (GST_stats,
1910 ("# SYN messages sent"),
1912 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1913 connect_msg.header.size = htons (sizeof (struct TransportSynMessage));
1914 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1915 connect_msg.reserved = htonl (0);
1916 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1918 papi->send (papi->cls,
1920 (const char *) &connect_msg,
1921 sizeof (struct TransportSynMessage),
1923 SETUP_CONNECTION_TIMEOUT,
1924 &send_session_syn_cont, NULL))
1926 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1927 _("Failed to transmit SYN message to %s\n"),
1928 GST_plugins_a2s (na->address));
1929 n = lookup_neighbour (&na->address->peer);
1936 case GNUNET_TRANSPORT_PS_SYN_SENT:
1937 /* Remove address and request and additional one */
1938 GNUNET_assert (na == &n->primary_address);
1939 unset_primary_address (n);
1940 set_state_and_timeout (n,
1941 GNUNET_TRANSPORT_PS_INIT_ATS,
1942 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
1943 /* Hard failure to send the SYN message with this address:
1944 Destroy address and session */
1946 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1947 /* Remove address and request an additional one */
1948 GNUNET_assert (na == &n->primary_address);
1949 unset_primary_address (n);
1950 set_state_and_timeout (n,
1951 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1952 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1954 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1955 GNUNET_assert (na == &n->alternative_address);
1956 GNUNET_STATISTICS_update (GST_stats,
1957 gettext_noop ("# Failed attempts to switch addresses (failed to send SYN)"),
1960 /* Remove address and request an additional one */
1961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1962 "Switch failed, cleaning up alternative address\n");
1963 free_address (&n->alternative_address);
1964 set_state_and_timeout (n,
1965 GNUNET_TRANSPORT_PS_CONNECTED,
1966 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
1970 disconnect_neighbour (n);
1975 GST_neighbours_notify_data_sent (na->address,
1977 sizeof (struct TransportSynMessage));
1982 * Continuation called from our attempt to transmitted our
1983 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
1984 * target. Continue processing based on the @a result. Specifically,
1985 * if we failed to transmit, discard the address we used.
1988 * @param target which peer received the transmission
1989 * @param result #GNUNET_OK if sending worked
1990 * @param size_payload how many bytes of payload were sent (ignored)
1991 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1994 send_session_syn_ack_cont (void *cls,
1995 const struct GNUNET_PeerIdentity *target,
1997 size_t size_payload,
1998 size_t size_on_wire)
2000 struct NeighbourMapEntry *n;
2003 (void) size_payload;
2004 (void) size_on_wire;
2005 n = lookup_neighbour (target);
2008 /* SYN_ACK continuation was called after neighbor was freed,
2009 * for example due to a time out for the state or the session
2010 * used was already terminated: nothing to do here... */
2014 if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2016 /* SYN_ACK continuation was called after neighbor changed state,
2017 * for example due to a time out for the state or the session
2018 * used was already terminated: nothing to do here... */
2021 if (GNUNET_OK == result)
2024 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2025 _("Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2026 GNUNET_i2s (target),
2027 GST_plugins_a2s (n->primary_address.address));
2029 /* Remove address and request and additional one */
2030 /* FIXME: what if the neighbour's primary address
2031 changed in the meantime? Might want to instead
2032 pass "something" around in closure to be sure. */
2033 unset_primary_address (n);
2034 n->ack_state = ACK_SEND_SYN_ACK;
2035 set_state_and_timeout (n,
2036 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2037 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2042 * Send a SYN_ACK message via the given address.
2044 * @param na address and session to use
2045 * @param timestamp timestamp to use for the ACK message
2046 * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2049 send_syn_ack_message (struct NeighbourAddress *na,
2050 struct GNUNET_TIME_Absolute timestamp)
2052 const struct GNUNET_HELLO_Address *address = na->address;
2053 struct GNUNET_ATS_Session *session = na->session;
2054 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2055 struct TransportSynMessage connect_msg;
2056 struct NeighbourMapEntry *n;
2058 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2059 "Sending SYN_ACK to peer `%s'\n",
2060 GNUNET_i2s (&address->peer));
2062 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2067 if (NULL == session)
2068 session = papi->get_session (papi->cls,
2070 if (NULL == session)
2075 GST_ats_new_session (address,
2077 GNUNET_STATISTICS_update (GST_stats,
2079 ("# SYN_ACK messages sent"),
2081 connect_msg.header.size = htons (sizeof (struct TransportSynMessage));
2082 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2083 connect_msg.reserved = htonl (0);
2084 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
2086 if (GNUNET_SYSERR ==
2087 papi->send (papi->cls,
2089 (const char *) &connect_msg,
2090 sizeof (struct TransportSynMessage),
2092 GNUNET_TIME_UNIT_FOREVER_REL,
2093 &send_session_syn_ack_cont, NULL))
2095 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2096 _("Failed to transmit SYN_ACK message to %s\n"),
2097 GST_plugins_a2s (address));
2099 n = lookup_neighbour (&address->peer);
2105 /* Remove address and request and additional one */
2106 unset_primary_address (n);
2107 n->ack_state = ACK_SEND_SYN_ACK;
2108 set_state_and_timeout (n,
2109 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2110 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2117 * Function called by the bandwidth tracker for a peer whenever
2118 * the tracker's state changed such that we need to recalculate
2119 * the delay for flow control. We calculate the latest delay
2120 * and inform the plugin (if applicable).
2122 * @param cls the `struct NeighbourMapEntry` to update calculations for
2125 inbound_bw_tracker_update (void *cls)
2127 struct NeighbourMapEntry *n = cls;
2128 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2129 struct GNUNET_TIME_Relative delay;
2132 if (NULL == n->primary_address.address)
2133 return; /* not active, ignore */
2134 papi = GST_plugins_find (n->primary_address.address->transport_name);
2135 GNUNET_assert (NULL != papi);
2136 if (NULL == papi->update_inbound_delay)
2138 delay = GST_neighbours_calculate_receive_delay (&n->id,
2141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2142 "New inbound delay for peer `%s' is %llu ms\n",
2143 GNUNET_i2s (&n->id),
2144 (unsigned long long) delay.rel_value_us / 1000LL);
2145 if (NULL == n->primary_address.session)
2147 papi->update_inbound_delay (papi->cls,
2149 n->primary_address.session,
2155 * Create a fresh entry in the neighbour map for the given peer
2157 * @param peer peer to create an entry for
2158 * @return new neighbour map entry
2160 static struct NeighbourMapEntry *
2161 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
2163 struct NeighbourMapEntry *n;
2166 memcmp (&GST_my_identity,
2168 sizeof (struct GNUNET_PeerIdentity)))
2170 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2171 "Cowardly refusing to consider myself my neighbour!\n");
2174 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2175 "Creating new neighbour entry for `%s'\n",
2177 n = GNUNET_new (struct NeighbourMapEntry);
2179 n->ack_state = ACK_UNDEFINED;
2180 n->last_util_transmission = GNUNET_TIME_absolute_get();
2181 n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2182 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
2183 &inbound_bw_tracker_update,
2185 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2186 MAX_BANDWIDTH_CARRY_S);
2187 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
2188 set_state_and_timeout (n,
2189 GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2190 GNUNET_TIME_UNIT_FOREVER_ABS);
2191 GNUNET_assert (GNUNET_OK ==
2192 GNUNET_CONTAINER_multipeermap_put (neighbours,
2195 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2196 n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect,
2205 * Entry in a DLL we use to keep track of pending blacklist checks.
2207 struct BlacklistCheckSwitchContext
2212 struct BlacklistCheckSwitchContext *prev;
2217 struct BlacklistCheckSwitchContext *next;
2220 * Handle to the blacklist check we are performing.
2222 struct GST_BlacklistCheck *blc;
2225 * Inbound bandwidth that was assigned to @e address.
2227 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2230 * Outbound bandwidth that was assigned to @e address.
2232 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2237 * We received a 'SYN' message from the other peer.
2238 * Consider switching to it.
2240 * @param message possibly a `struct TransportSynMessage` (check format)
2241 * @param peer identity of the peer to switch the address for
2242 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2245 GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message,
2246 const struct GNUNET_PeerIdentity *peer)
2248 const struct TransportSynMessage *scm;
2249 struct NeighbourMapEntry *n;
2250 struct GNUNET_TIME_Absolute ts;
2252 if (ntohs (message->size) != sizeof (struct TransportSynMessage))
2254 GNUNET_break_op (0);
2255 return GNUNET_SYSERR;
2257 GNUNET_STATISTICS_update (GST_stats,
2259 ("# SYN messages received"),
2261 if (NULL == neighbours)
2263 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2264 _("SYN request from peer `%s' ignored due impending shutdown\n"),
2266 return GNUNET_OK; /* we're shutting down */
2268 scm = (const struct TransportSynMessage *) message;
2269 GNUNET_break_op (0 == ntohl (scm->reserved));
2270 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2272 memcmp (&GST_my_identity,
2274 sizeof (struct GNUNET_PeerIdentity)))
2276 /* loopback connection-to-self, ignore */
2277 return GNUNET_SYSERR;
2279 n = lookup_neighbour (peer);
2282 /* This is a new neighbour and set to not connected */
2283 n = setup_neighbour (peer);
2284 GNUNET_assert (NULL != n);
2287 /* Remember this SYN message in neighbour */
2288 n->ack_state = ACK_SEND_SYN_ACK;
2289 n->connect_ack_timestamp = ts;
2291 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2292 "Received SYN for peer `%s' in state %s/%s\n",
2294 GNUNET_TRANSPORT_ps2s (n->state),
2295 print_ack_state (n->ack_state));
2299 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2300 /* Request an address from ATS to send SYN_ACK to this peer */
2301 set_state_and_timeout (n,
2302 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2303 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2305 case GNUNET_TRANSPORT_PS_INIT_ATS:
2306 /* SYN message takes priority over us asking ATS for address:
2307 * Wait for ATS to suggest an address and send SYN_ACK */
2308 set_state_and_timeout (n,
2309 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2310 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2312 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2313 /* We already wait for an address to send an SYN_ACK */
2315 case GNUNET_TRANSPORT_PS_SYN_SENT:
2316 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2317 /* Send ACK immediately */
2318 n->ack_state = ACK_SEND_ACK;
2319 send_syn_ack_message (&n->primary_address,
2322 case GNUNET_TRANSPORT_PS_CONNECTED:
2323 /* we are already connected and can thus send the ACK immediately */
2324 GNUNET_assert (NULL != n->primary_address.address);
2325 GNUNET_assert (NULL != n->primary_address.session);
2326 n->ack_state = ACK_SEND_ACK;
2327 send_syn_ack_message (&n->primary_address,
2330 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2331 /* We wait for ATS address suggestion */
2333 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2334 /* We received a SYN message while waiting for a SYN_ACK in fast
2335 * reconnect. Send SYN_ACK immediately */
2336 n->ack_state = ACK_SEND_ACK;
2337 send_syn_ack_message (&n->primary_address,
2338 n->connect_ack_timestamp);
2340 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2341 /* We are already connected and can thus send the ACK immediately;
2342 still, it can never hurt to have an alternative address, so also
2343 tell ATS about it */
2344 GNUNET_assert (NULL != n->primary_address.address);
2345 GNUNET_assert (NULL != n->primary_address.session);
2346 n->ack_state = ACK_SEND_ACK;
2347 send_syn_ack_message (&n->primary_address,
2350 case GNUNET_TRANSPORT_PS_DISCONNECT:
2351 /* Get rid of remains and re-try */
2353 n = setup_neighbour (peer);
2354 GNUNET_assert (NULL != n);
2355 /* Remember the SYN time stamp for ACK message */
2356 n->ack_state = ACK_SEND_SYN_ACK;
2357 n->connect_ack_timestamp = ts;
2358 /* Request an address for the peer */
2359 set_state_and_timeout (n,
2360 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2361 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2363 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2364 /* should not be possible */
2368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2369 "Unhandled state `%s'\n",
2370 GNUNET_TRANSPORT_ps2s (n->state));
2372 return GNUNET_SYSERR;
2379 * Check if the given @a address is the same that we are already
2380 * using for the respective neighbour. If so, update the bandwidth
2381 * assignment and possibly the session and return #GNUNET_OK.
2382 * If the new address is different from what the neighbour is
2383 * using right now, return #GNUNET_NO.
2385 * @param address address of the other peer,
2386 * @param session session to use or NULL if transport should initiate a session
2387 * @param bandwidth_in inbound quota to be used when connection is up,
2388 * 0 to disconnect from peer
2389 * @param bandwidth_out outbound quota to be used when connection is up,
2390 * 0 to disconnect from peer
2391 * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2392 * #GNUNET_NO if more extensive changes are required (address changed)
2395 try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address,
2396 struct GNUNET_ATS_Session *session,
2397 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2398 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2400 struct NeighbourMapEntry *n;
2402 n = lookup_neighbour (&address->peer);
2404 (NULL == n->primary_address.address) ||
2405 (0 != GNUNET_HELLO_address_cmp (address,
2406 n->primary_address.address)) )
2408 /* We are not really switching addresses, but merely adjusting
2409 session and/or bandwidth, can do fast ATS update! */
2410 if (session != n->primary_address.session)
2412 /* switch to a different session, but keeping same address; could
2413 happen if there is a 2nd inbound connection */
2414 n->primary_address.session = session;
2415 GNUNET_assert (GNUNET_YES ==
2416 GST_ats_is_known (n->primary_address.address,
2417 n->primary_address.session));
2419 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2421 n->primary_address.bandwidth_in = bandwidth_in;
2423 set_incoming_quota (n,
2427 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2429 n->primary_address.bandwidth_out = bandwidth_out;
2430 send_outbound_quota_to_clients (n);
2437 * We've been asked to switch addresses, and just now got the result
2438 * from the blacklist check to see if this is allowed.
2440 * @param cls the `struct BlacklistCheckSwitchContext` with
2441 * the information about the future address
2442 * @param peer the peer we may switch addresses on
2443 * @param address address associated with the request
2444 * @param session session associated with the request
2445 * @param result #GNUNET_OK if the connection is allowed,
2446 * #GNUNET_NO if not,
2447 * #GNUNET_SYSERR if operation was aborted
2450 switch_address_bl_check_cont (void *cls,
2451 const struct GNUNET_PeerIdentity *peer,
2452 const struct GNUNET_HELLO_Address *address,
2453 struct GNUNET_ATS_Session *session,
2456 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2457 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2458 struct NeighbourMapEntry *n;
2460 if (GNUNET_SYSERR == result)
2463 papi = GST_plugins_find (address->transport_name);
2465 /* This can happen during shutdown. */
2469 if (GNUNET_NO == result)
2471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2472 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2473 GST_plugins_a2s (address),
2476 GNUNET_STATISTICS_update (GST_stats,
2477 "# ATS suggestions ignored (blacklist denied)",
2480 if (NULL != session)
2481 papi->disconnect_session (papi->cls,
2484 GNUNET_HELLO_address_check_option (address,
2485 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2486 GST_ats_block_address (address,
2492 if (NULL == session)
2494 /* need to create a session, ATS only gave us an address */
2495 session = papi->get_session (papi->cls,
2497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2498 "Obtained new session for peer `%s' and address '%s': %p\n",
2499 GNUNET_i2s (&address->peer),
2500 GST_plugins_a2s (address),
2502 if (NULL != session)
2503 GST_ats_new_session (address,
2506 if (NULL == session)
2508 /* session creation failed, bad!, fail! */
2509 GNUNET_STATISTICS_update (GST_stats,
2510 "# ATS suggestions ignored (failed to create session)",
2513 /* No session could be obtained, remove blacklist check and clean up */
2514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2515 "Failed to obtain new session for peer `%s' and address '%s'\n",
2516 GNUNET_i2s (&address->peer),
2517 GST_plugins_a2s (address));
2518 GST_ats_block_address (address,
2523 /* We did this check already before going into blacklist, but
2524 it is theoretically possible that the situation changed in
2525 the meantime, hence we check again here */
2527 try_run_fast_ats_update (address,
2529 blc_ctx->bandwidth_in,
2530 blc_ctx->bandwidth_out))
2531 goto cleanup; /* was just a minor update, we're done */
2533 /* check if we also need to setup the neighbour entry */
2534 if (NULL == (n = lookup_neighbour (peer)))
2536 n = setup_neighbour (peer);
2539 /* not sure how this can happen... */
2543 n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2546 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2547 "Peer `%s' switches to address `%s'\n",
2548 GNUNET_i2s (&address->peer),
2549 GST_plugins_a2s (address));
2553 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2555 GST_ats_block_address (address,
2559 case GNUNET_TRANSPORT_PS_INIT_ATS:
2560 /* We requested an address and ATS suggests one:
2561 * set primary address and send SYN message*/
2562 set_primary_address (n,
2565 blc_ctx->bandwidth_in,
2566 blc_ctx->bandwidth_out);
2567 if (ACK_SEND_SYN_ACK == n->ack_state)
2569 /* Send pending SYN_ACK message */
2570 n->ack_state = ACK_SEND_ACK;
2571 send_syn_ack_message (&n->primary_address,
2572 n->connect_ack_timestamp);
2574 set_state_and_timeout (n,
2575 GNUNET_TRANSPORT_PS_SYN_SENT,
2576 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2577 send_syn (&n->primary_address);
2579 case GNUNET_TRANSPORT_PS_SYN_SENT:
2580 /* ATS suggested a new address while waiting for an SYN_ACK:
2581 * Switch and send new SYN */
2582 /* ATS suggests a different address, switch again */
2583 set_primary_address (n,
2586 blc_ctx->bandwidth_in,
2587 blc_ctx->bandwidth_out);
2588 if (ACK_SEND_SYN_ACK == n->ack_state)
2590 /* Send pending SYN_ACK message */
2591 n->ack_state = ACK_SEND_ACK;
2592 send_syn_ack_message (&n->primary_address,
2593 n->connect_ack_timestamp);
2595 set_state_and_timeout (n,
2596 GNUNET_TRANSPORT_PS_SYN_SENT,
2597 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2598 send_syn (&n->primary_address);
2600 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2601 /* We requested an address and ATS suggests one:
2602 * set primary address and send SYN_ACK message*/
2603 set_primary_address (n,
2606 blc_ctx->bandwidth_in,
2607 blc_ctx->bandwidth_out);
2608 /* Send an ACK message as a response to the SYN msg */
2609 set_state_and_timeout (n,
2610 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2611 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2612 send_syn_ack_message (&n->primary_address,
2613 n->connect_ack_timestamp);
2614 if ( (ACK_SEND_SYN_ACK == n->ack_state) ||
2615 (ACK_UNDEFINED == n->ack_state) )
2616 n->ack_state = ACK_SEND_ACK;
2618 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2619 /* ATS asks us to switch while we were trying to connect; switch to new
2620 address and check blacklist again */
2621 if ( (ACK_SEND_SYN_ACK == n->ack_state) )
2623 n->ack_state = ACK_SEND_ACK;
2624 send_syn_ack_message (&n->primary_address,
2625 n->connect_ack_timestamp);
2627 set_primary_address (n,
2630 blc_ctx->bandwidth_in,
2631 blc_ctx->bandwidth_out);
2632 set_state_and_timeout (n,
2633 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2634 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2636 case GNUNET_TRANSPORT_PS_CONNECTED:
2637 GNUNET_assert (NULL != n->primary_address.address);
2638 GNUNET_assert (NULL != n->primary_address.session);
2639 GNUNET_break (n->primary_address.session != session);
2640 /* ATS asks us to switch a life connection; see if we can get
2641 a SYN_ACK on it before we actually do this! */
2642 set_alternative_address (n,
2645 blc_ctx->bandwidth_in,
2646 blc_ctx->bandwidth_out);
2647 set_state_and_timeout (n,
2648 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2649 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2650 GNUNET_STATISTICS_update (GST_stats,
2651 gettext_noop ("# Attempts to switch addresses"),
2654 send_syn (&n->alternative_address);
2656 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2657 set_primary_address (n,
2660 blc_ctx->bandwidth_in,
2661 blc_ctx->bandwidth_out);
2662 if (ACK_SEND_SYN_ACK == n->ack_state)
2664 /* Send pending SYN_ACK message */
2665 n->ack_state = ACK_SEND_ACK;
2666 send_syn_ack_message (&n->primary_address,
2667 n->connect_ack_timestamp);
2669 set_state_and_timeout (n,
2670 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2671 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2672 send_syn (&n->primary_address);
2674 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2675 /* ATS asks us to switch while we were trying to reconnect; switch to new
2676 address and send SYN again */
2677 set_primary_address (n,
2680 blc_ctx->bandwidth_in,
2681 blc_ctx->bandwidth_out);
2682 set_state_and_timeout (n,
2683 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2684 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
2685 send_syn (&n->primary_address);
2687 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2688 if ( (0 == GNUNET_HELLO_address_cmp (n->primary_address.address,
2690 (n->primary_address.session == session) )
2692 /* ATS switches back to still-active session */
2693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2694 "ATS double-switched, cleaning up alternative address\n");
2695 free_address (&n->alternative_address);
2696 set_state_and_timeout (n,
2697 GNUNET_TRANSPORT_PS_CONNECTED,
2701 /* ATS asks us to switch a life connection, send */
2702 set_alternative_address (n,
2705 blc_ctx->bandwidth_in,
2706 blc_ctx->bandwidth_out);
2707 set_state_and_timeout (n,
2708 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2709 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
2710 send_syn (&n->alternative_address);
2712 case GNUNET_TRANSPORT_PS_DISCONNECT:
2713 /* not going to switch addresses while disconnecting */
2714 GNUNET_STATISTICS_update (GST_stats,
2715 "# ATS suggestion ignored (disconnecting)",
2719 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2723 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2724 "Unhandled state `%s'\n",
2725 GNUNET_TRANSPORT_ps2s (n->state));
2730 GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2733 GNUNET_free (blc_ctx);
2738 * For the given peer, switch to this address.
2740 * Before accepting this addresses and actively using it, a blacklist check
2743 * If any check fails or the suggestion can somehow not be followed, we
2744 * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2745 * could not be satisfied and force ATS to do something else.
2747 * @param address address of the other peer,
2748 * @param session session to use or NULL if transport should initiate a session
2749 * @param bandwidth_in inbound quota to be used when connection is up,
2750 * 0 to disconnect from peer
2751 * @param bandwidth_out outbound quota to be used when connection is up,
2752 * 0 to disconnect from peer
2755 GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address,
2756 struct GNUNET_ATS_Session *session,
2757 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2758 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2760 struct GST_BlacklistCheck *blc;
2761 struct BlacklistCheckSwitchContext *blc_ctx;
2763 GNUNET_assert (NULL != address->transport_name);
2765 try_run_fast_ats_update (address,
2771 /* Check if plugin is available */
2772 if (NULL == (GST_plugins_find (address->transport_name)))
2774 /* we don't have the plugin for this address */
2776 GST_ats_block_address (address,
2780 if ((NULL == session) &&
2781 (GNUNET_HELLO_address_check_option (address,
2782 GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2784 /* This is a inbound address and we do not have a session to use! */
2786 GST_ats_block_address (address,
2791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2792 "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2793 GST_plugins_a2s (address),
2794 GNUNET_i2s (&address->peer),
2795 (unsigned int) ntohl (bandwidth_in.value__),
2796 (unsigned int) ntohl (bandwidth_out.value__));
2798 /* Perform blacklist check */
2799 blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2800 blc_ctx->bandwidth_in = bandwidth_in;
2801 blc_ctx->bandwidth_out = bandwidth_out;
2802 GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2805 if (NULL != (blc = GST_blacklist_test_allowed (&address->peer,
2806 address->transport_name,
2807 &switch_address_bl_check_cont,
2818 * Function called to send network utilization data to ATS for
2819 * each active connection.
2822 * @param key peer we send utilization data for
2823 * @param value the `struct NeighbourMapEntry *` with data to send
2824 * @return #GNUNET_OK (continue to iterate)
2827 send_utilization_data (void *cls,
2828 const struct GNUNET_PeerIdentity *key,
2831 struct NeighbourMapEntry *n = value;
2834 struct GNUNET_TIME_Relative delta;
2837 if ( (GNUNET_YES != test_connected (n)) ||
2838 (NULL == n->primary_address.address) )
2840 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2841 GNUNET_TIME_absolute_get ());
2843 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2844 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2846 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2847 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2850 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2854 GST_ats_update_utilization (n->primary_address.address,
2857 n->util_total_bytes_recv = 0;
2858 n->util_total_bytes_sent = 0;
2859 n->last_util_transmission = GNUNET_TIME_absolute_get ();
2865 * Task transmitting utilization in a regular interval
2867 * @param cls the `struct NeighbourMapEntry` for which we are running
2870 utilization_transmission (void *cls)
2873 util_transmission_tk = NULL;
2874 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
2875 &send_utilization_data,
2877 util_transmission_tk
2878 = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2879 &utilization_transmission,
2885 * Track information about data we received from the
2886 * given address (used to notify ATS about our utilization
2887 * of allocated resources).
2889 * @param address the address we got data from
2890 * @param message the message we received (really only the size is used)
2893 GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address,
2894 const struct GNUNET_MessageHeader *message)
2896 struct NeighbourMapEntry *n;
2898 n = lookup_neighbour (&address->peer);
2901 n->util_total_bytes_recv += ntohs (message->size);
2906 * Track information about data we transmitted using the given @a
2907 * address and @a session (used to notify ATS about our utilization of
2908 * allocated resources).
2910 * @param address the address we transmitted data to
2911 * @param session session we used to transmit data
2912 * @param message the message we sent (really only the size is used)
2915 GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address,
2916 struct GNUNET_ATS_Session *session,
2919 struct NeighbourMapEntry *n;
2921 n = lookup_neighbour (&address->peer);
2924 if (n->primary_address.session != session)
2926 n->util_total_bytes_sent += size;
2931 * Master task run for every neighbour. Performs all of the time-related
2932 * activities (keep alive, send next message, disconnect if idle, finish
2933 * clean up after disconnect).
2935 * @param cls the 'struct NeighbourMapEntry' for which we are running
2938 master_task (void *cls)
2940 struct NeighbourMapEntry *n = cls;
2941 struct GNUNET_TIME_Relative delay;
2944 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2945 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2946 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2947 GNUNET_i2s (&n->id),
2948 GNUNET_TRANSPORT_ps2s(n->state),
2949 GNUNET_STRINGS_relative_time_to_string (delay,
2953 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2954 /* invalid state for master task, clean up */
2958 case GNUNET_TRANSPORT_PS_INIT_ATS:
2959 if (0 == delay.rel_value_us)
2961 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2962 "Connection to `%s' timed out waiting for ATS to provide address\n",
2963 GNUNET_i2s (&n->id));
2968 case GNUNET_TRANSPORT_PS_SYN_SENT:
2969 if (0 == delay.rel_value_us)
2971 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2972 "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
2973 GNUNET_i2s (&n->id));
2974 /* Remove address and request and additional one */
2975 unset_primary_address (n);
2976 set_state_and_timeout (n,
2977 GNUNET_TRANSPORT_PS_INIT_ATS,
2978 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
2982 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2983 if (0 == delay.rel_value_us)
2985 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2986 "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
2987 GNUNET_i2s (&n->id));
2992 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2993 if (0 == delay.rel_value_us)
2995 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2996 "Connection to `%s' timed out waiting for other peer to send ACK\n",
2997 GNUNET_i2s (&n->id));
2998 disconnect_neighbour (n);
3002 case GNUNET_TRANSPORT_PS_CONNECTED:
3003 if (0 == delay.rel_value_us)
3005 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3006 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3007 GNUNET_i2s (&n->id));
3008 disconnect_neighbour (n);
3011 try_transmission_to_peer (n);
3014 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3015 if (0 == delay.rel_value_us)
3017 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3018 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3019 GNUNET_i2s (&n->id));
3020 disconnect_neighbour (n);
3024 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3025 if (0 == delay.rel_value_us)
3027 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3028 "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3029 GNUNET_i2s (&n->id));
3030 disconnect_neighbour (n);
3034 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3035 if (0 == delay.rel_value_us)
3037 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3038 "Switch failed, cleaning up alternative address\n");
3039 free_address (&n->alternative_address);
3040 set_state_and_timeout (n,
3041 GNUNET_TRANSPORT_PS_CONNECTED,
3042 GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
3044 try_transmission_to_peer (n);
3047 case GNUNET_TRANSPORT_PS_DISCONNECT:
3048 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3049 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3050 GNUNET_i2s (&n->id));
3053 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3054 /* how did we get here!? */
3058 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3059 "Unhandled state `%s'\n",
3060 GNUNET_TRANSPORT_ps2s (n->state));
3064 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3065 if ( (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3066 (GNUNET_TRANSPORT_PS_CONNECTED == n->state) )
3068 /* if we are *now* in one of the two states, we're sending
3069 keep alive messages, so we need to consider the keepalive
3070 delay, not just the connection timeout */
3071 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
3074 if (NULL == n->task)
3075 n->task = GNUNET_SCHEDULER_add_delayed (delay,
3082 * Send a ACK message to the neighbour to confirm that we
3085 * @param n neighbour to send the ACK to
3088 send_session_ack_message (struct NeighbourMapEntry *n)
3090 struct GNUNET_MessageHeader msg;
3092 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3093 "Sending ACK message to peer `%s'\n",
3094 GNUNET_i2s (&n->id));
3096 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
3097 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3098 (void) send_with_session (n,
3100 sizeof (struct GNUNET_MessageHeader),
3102 GNUNET_TIME_UNIT_FOREVER_REL,
3109 * We received a 'SESSION_SYN_ACK' message from the other peer.
3110 * Consider switching to it.
3112 * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3113 * @param peer identity of the peer to switch the address for
3114 * @param address address of the other peer, NULL if other peer
3116 * @param session session to use (or NULL)
3117 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3120 GST_neighbours_handle_session_syn_ack (const struct GNUNET_MessageHeader *message,
3121 const struct GNUNET_HELLO_Address *address,
3122 struct GNUNET_ATS_Session *session)
3124 const struct TransportSynMessage *scm;
3125 struct GNUNET_TIME_Absolute ts;
3126 struct NeighbourMapEntry *n;
3129 if (ntohs (message->size) != sizeof (struct TransportSynMessage))
3131 GNUNET_break_op (0);
3132 return GNUNET_SYSERR;
3134 GNUNET_STATISTICS_update (GST_stats,
3136 ("# SYN_ACK messages received"),
3138 scm = (const struct TransportSynMessage *) message;
3139 GNUNET_break_op (ntohl (scm->reserved) == 0);
3140 if (NULL == (n = lookup_neighbour (&address->peer)))
3142 GNUNET_STATISTICS_update (GST_stats,
3144 ("# unexpected SYN_ACK messages (no peer)"),
3146 return GNUNET_SYSERR;
3148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3149 "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3150 GNUNET_i2s (&address->peer),
3151 GNUNET_TRANSPORT_ps2s (n->state),
3152 print_ack_state (n->ack_state));
3153 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3156 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3159 return GNUNET_SYSERR;
3160 case GNUNET_TRANSPORT_PS_INIT_ATS:
3161 GNUNET_STATISTICS_update (GST_stats,
3162 gettext_noop ("# unexpected SYN_ACK messages (not ready)"),
3166 case GNUNET_TRANSPORT_PS_SYN_SENT:
3167 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3169 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3170 "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3173 set_state_and_timeout (n,
3174 GNUNET_TRANSPORT_PS_CONNECTED,
3175 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3176 set_primary_address (n,
3177 n->primary_address.address,
3178 n->primary_address.session,
3179 n->primary_address.bandwidth_in,
3180 n->primary_address.bandwidth_out);
3181 send_session_ack_message (n);
3183 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3184 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3185 GNUNET_STATISTICS_update (GST_stats,
3186 gettext_noop ("# unexpected SYN_ACK messages (not ready)"),
3190 case GNUNET_TRANSPORT_PS_CONNECTED:
3191 /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3192 send_session_ack_message (n);
3194 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3195 /* we didn't expect any SYN_ACK, as we are waiting for ATS
3196 to give us a new address... */
3197 GNUNET_STATISTICS_update (GST_stats,
3198 gettext_noop ("# unexpected SYN_ACK messages (waiting on ATS)"),
3202 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3203 /* Reconnecting with new address address worked; go back to connected! */
3204 set_state_and_timeout (n,
3205 GNUNET_TRANSPORT_PS_CONNECTED,
3206 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3207 send_session_ack_message (n);
3209 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3210 /* new address worked; adopt it and go back to connected! */
3211 set_state_and_timeout (n,
3212 GNUNET_TRANSPORT_PS_CONNECTED,
3213 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3214 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3216 /* Set primary addresses */
3217 set_primary_address (n,
3218 n->alternative_address.address,
3219 n->alternative_address.session,
3220 n->alternative_address.bandwidth_in,
3221 n->alternative_address.bandwidth_out);
3222 GNUNET_STATISTICS_update (GST_stats,
3223 gettext_noop ("# Successful attempts to switch addresses"),
3227 GNUNET_HELLO_address_free (n->alternative_address.address);
3228 memset (&n->alternative_address,
3230 sizeof (n->alternative_address));
3231 send_session_ack_message (n);
3233 case GNUNET_TRANSPORT_PS_DISCONNECT:
3234 GNUNET_STATISTICS_update (GST_stats,
3236 ("# unexpected SYN_ACK messages (disconnecting)"),
3238 return GNUNET_SYSERR;
3239 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3243 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3244 "Unhandled state `%s'\n",
3245 GNUNET_TRANSPORT_ps2s (n->state));
3247 return GNUNET_SYSERR;
3254 * A session was terminated. Take note; if needed, try to get
3255 * an alternative address from ATS.
3257 * @param peer identity of the peer where the session died
3258 * @param session session that is gone
3259 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3260 * this session was not in use
3263 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3264 struct GNUNET_ATS_Session *session)
3266 struct NeighbourMapEntry *n;
3268 if (NULL == (n = lookup_neighbour (peer)))
3269 return GNUNET_NO; /* can't affect us */
3270 if (session != n->primary_address.session)
3272 /* Free alternative address */
3273 if (session == n->alternative_address.session)
3275 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3276 set_state_and_timeout (n,
3277 GNUNET_TRANSPORT_PS_CONNECTED,
3279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3280 "Session died, cleaning up alternative address\n");
3281 free_address (&n->alternative_address);
3283 return GNUNET_NO; /* doesn't affect us further */
3286 n->expect_latency_response = GNUNET_NO;
3287 /* The session for neighbour's primary address died */
3290 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3294 case GNUNET_TRANSPORT_PS_INIT_ATS:
3298 case GNUNET_TRANSPORT_PS_SYN_SENT:
3299 /* The session used to send the SYN terminated:
3300 * this implies a connect error*/
3301 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3302 "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n",
3303 GST_plugins_a2s (n->primary_address.address),
3304 n->primary_address.session);
3306 /* Destroy the address since it cannot be used */
3307 unset_primary_address (n);
3308 set_state_and_timeout (n,
3309 GNUNET_TRANSPORT_PS_INIT_ATS,
3310 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3312 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3313 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3314 /* error on inbound session; free neighbour entirely */
3317 case GNUNET_TRANSPORT_PS_CONNECTED:
3318 /* Our primary connection died, try a fast reconnect */
3319 unset_primary_address (n);
3320 set_state_and_timeout (n,
3321 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3322 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3324 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3325 /* we don't have an address, how can it go down? */
3328 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3329 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3330 "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n",
3331 GST_plugins_a2s (n->primary_address.address),
3332 n->primary_address.session);
3333 /* Destroy the address since it cannot be used */
3334 unset_primary_address (n);
3335 set_state_and_timeout (n,
3336 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3337 GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
3339 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3340 /* primary went down while we were waiting for SYN_ACK on secondary;
3341 secondary as primary */
3343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3344 "Connection `%s' %p to peer `%s' was terminated while switching, "
3345 "switching to alternative address `%s' %p\n",
3346 GST_plugins_a2s (n->primary_address.address),
3347 n->primary_address.session,
3349 GST_plugins_a2s (n->alternative_address.address),
3350 n->alternative_address.session);
3352 /* Destroy the inbound address since it cannot be used */
3353 free_address (&n->primary_address);
3354 n->primary_address = n->alternative_address;
3355 GNUNET_assert (GNUNET_YES ==
3356 GST_ats_is_known (n->primary_address.address,
3357 n->primary_address.session));
3358 memset (&n->alternative_address,
3360 sizeof (struct NeighbourAddress));
3361 set_state_and_timeout (n,
3362 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3363 GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
3365 case GNUNET_TRANSPORT_PS_DISCONNECT:
3366 unset_primary_address (n);
3368 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3369 /* neighbour was freed and plugins told to terminate session */
3372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3373 "Unhandled state `%s'\n",
3374 GNUNET_TRANSPORT_ps2s (n->state));
3378 if (NULL != n->task)
3379 GNUNET_SCHEDULER_cancel (n->task);
3380 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3386 * We received a 'ACK' message from the other peer.
3387 * If we sent a 'SYN_ACK' last, this means we are now
3388 * connected. Otherwise, do nothing.
3390 * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3391 * @param address address of the other peer
3392 * @param session session to use (or NULL)
3393 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3396 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3397 const struct GNUNET_HELLO_Address *address,
3398 struct GNUNET_ATS_Session *session)
3400 struct NeighbourMapEntry *n;
3403 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3405 GNUNET_break_op (0);
3406 return GNUNET_SYSERR;
3408 GNUNET_STATISTICS_update (GST_stats,
3409 gettext_noop ("# ACK messages received"),
3412 if (NULL == (n = lookup_neighbour (&address->peer)))
3414 GNUNET_break_op (0);
3415 return GNUNET_SYSERR;
3417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3418 "Received ACK for peer `%s' in state %s/%s\n",
3419 GNUNET_i2s (&address->peer),
3420 GNUNET_TRANSPORT_ps2s (n->state),
3421 print_ack_state (n->ack_state));
3423 /* Check if we are in a plausible state for having sent
3424 a SYN_ACK. If not, return, otherwise break.
3426 The remote peers sends a ACK as a response for a SYN_ACK
3430 - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3431 now wait for the ACK to finally be connected
3432 - If we sent a SYN_ACK to this peer before */
3434 if ( ( (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3435 (ACK_SEND_ACK != n->ack_state) ) ||
3436 (NULL == n->primary_address.address) )
3438 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3439 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3440 GNUNET_i2s (&address->peer),
3441 GNUNET_TRANSPORT_ps2s (n->state),
3442 print_ack_state (n->ack_state));
3444 GNUNET_STATISTICS_update (GST_stats,
3445 gettext_noop ("# unexpected ACK messages"),
3450 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3452 /* We tried to switch addresses while being connect. We explicitly wait
3453 * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3454 * so we do not want to set the address as in use! */
3457 set_state_and_timeout (n,
3458 GNUNET_TRANSPORT_PS_CONNECTED,
3459 GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3461 if (NULL == n->primary_address.address) {
3463 * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3464 * really means we did try (and succeed) to send a SYN and are waiting for
3466 * That suggests that the primary_address used to be non-NULL, but maybe it
3467 * got reset to NULL without the state being changed appropriately?
3473 /* Reset backoff for primary address */
3474 GST_ats_block_reset (n->primary_address.address,
3475 n->primary_address.session);
3481 * Test if we're connected to the given peer.
3483 * @param target peer to test
3484 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3487 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3489 return test_connected (lookup_neighbour (target));
3494 * Task to asynchronously run #free_neighbour().
3496 * @param cls the `struct NeighbourMapEntry` to free
3499 delayed_disconnect (void *cls)
3501 struct NeighbourMapEntry *n = cls;
3503 n->delayed_disconnect_task = NULL;
3504 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3505 "Disconnecting by request from peer %s\n",
3506 GNUNET_i2s (&n->id));
3512 * We received a quota message from the given peer,
3513 * validate and process.
3515 * @param peer sender of the message
3516 * @param msg the quota message
3519 GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer,
3520 const struct GNUNET_MessageHeader *msg)
3522 struct NeighbourMapEntry *n;
3523 const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3524 struct GNUNET_BANDWIDTH_Value32NBO last;
3526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3527 "Received QUOTA message from peer `%s'\n",
3529 if (ntohs (msg->size) != sizeof (struct GNUNET_ATS_SessionQuotaMessage))
3531 GNUNET_break_op (0);
3532 GNUNET_STATISTICS_update (GST_stats,
3533 gettext_noop ("# quota messages ignored (malformed)"),
3538 GNUNET_STATISTICS_update (GST_stats,
3540 ("# QUOTA messages received"),
3542 sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg;
3543 if (NULL == (n = lookup_neighbour (peer)))
3548 last = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3549 GNUNET_BANDWIDTH_value_init (ntohl (sqm->quota)));
3550 if (last.value__ != n->neighbour_receive_quota.value__)
3552 n->neighbour_receive_quota = last;
3553 send_outbound_quota_to_clients (n);
3559 * We received a disconnect message from the given peer,
3560 * validate and process.
3562 * @param peer sender of the message
3563 * @param msg the disconnect message
3566 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
3567 const struct GNUNET_MessageHeader *msg)
3569 struct NeighbourMapEntry *n;
3570 const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3573 "Received DISCONNECT message from peer `%s'\n",
3575 if (ntohs (msg->size) != sizeof (struct GNUNET_ATS_SessionDisconnectMessage))
3577 GNUNET_break_op (0);
3578 GNUNET_STATISTICS_update (GST_stats,
3580 ("# disconnect messages ignored (malformed)"),
3585 GNUNET_STATISTICS_update (GST_stats,
3587 ("# DISCONNECT messages received"),
3589 sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg;
3590 if (NULL == (n = lookup_neighbour (peer)))
3595 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3597 GNUNET_STATISTICS_update (GST_stats,
3598 gettext_noop ("# disconnect messages ignored (timestamp)"),
3603 if (0 != memcmp (peer,
3605 sizeof (struct GNUNET_PeerIdentity)))
3607 GNUNET_break_op (0);
3610 if (ntohl (sdm->purpose.size) !=
3611 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3612 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3613 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3616 "DISCONNECT message from peer `%s' has invalid size\n",
3618 GNUNET_break_op (0);
3622 GNUNET_CRYPTO_eddsa_verify (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3628 "DISCONNECT message from peer `%s' cannot be verified \n",
3630 GNUNET_break_op (0);
3633 if (NULL == n->delayed_disconnect_task)
3635 n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect,
3642 * Closure for the #neighbours_iterate() function.
3644 struct IteratorContext
3647 * Function to call on each connected neighbour.
3649 GST_NeighbourIterator cb;
3652 * Closure for @e cb.
3659 * Call the callback from the closure for each neighbour.
3661 * @param cls the `struct IteratorContext`
3662 * @param key the hash of the public key of the neighbour
3663 * @param value the `struct NeighbourMapEntry`
3664 * @return #GNUNET_OK (continue to iterate)
3667 neighbours_iterate (void *cls,
3668 const struct GNUNET_PeerIdentity *key,
3671 struct IteratorContext *ic = cls;
3672 struct NeighbourMapEntry *n = value;
3673 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3674 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3677 if (NULL != n->primary_address.address)
3679 bandwidth_in = n->primary_address.bandwidth_in;
3680 bandwidth_out = n->primary_address.bandwidth_out;
3684 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3685 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3689 n->primary_address.address,
3692 bandwidth_in, bandwidth_out);
3698 * Iterate over all connected neighbours.
3700 * @param cb function to call
3701 * @param cb_cls closure for @a cb
3704 GST_neighbours_iterate (GST_NeighbourIterator cb,
3707 struct IteratorContext ic;
3709 if (NULL == neighbours)
3710 return; /* can happen during shutdown */
3713 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3714 &neighbours_iterate,
3720 * If we have an active connection to the given target, it must be shutdown.
3722 * @param target peer to disconnect from
3725 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3727 struct NeighbourMapEntry *n;
3729 if (NULL == (n = lookup_neighbour (target)))
3730 return; /* not active */
3731 if (GNUNET_YES == test_connected (n))
3732 GNUNET_STATISTICS_update (GST_stats,
3733 gettext_noop ("# disconnected from peer upon explicit request"),
3736 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3737 "Forced disconnect from peer %s\n",
3738 GNUNET_i2s (target));
3739 disconnect_neighbour (n);
3744 * Obtain current address information for the given neighbour.
3747 * @return address currently used
3749 const struct GNUNET_HELLO_Address *
3750 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3752 struct NeighbourMapEntry *n;
3754 n = lookup_neighbour (peer);
3757 return n->primary_address.address;
3762 * Initialize the neighbours subsystem.
3764 * @param max_fds maximum number of fds to use
3767 GST_neighbours_start (unsigned int max_fds)
3770 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE,
3772 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3773 &utilization_transmission,
3779 * Disconnect from the given neighbour.
3782 * @param key hash of neighbour's public key (not used)
3783 * @param value the `struct NeighbourMapEntry` of the neighbour
3784 * @return #GNUNET_OK (continue to iterate)
3787 disconnect_all_neighbours (void *cls,
3788 const struct GNUNET_PeerIdentity *key,
3791 struct NeighbourMapEntry *n = value;
3795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3796 "Disconnecting peer `%4s' during shutdown\n",
3797 GNUNET_i2s (&n->id));
3804 * Cleanup the neighbours subsystem.
3807 GST_neighbours_stop ()
3809 if (NULL == neighbours)
3811 if (NULL != util_transmission_tk)
3813 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3814 util_transmission_tk = NULL;
3816 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3817 &disconnect_all_neighbours,
3819 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3824 /* end of file gnunet-service-transport_neighbours.c */