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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file transport/gnunet-service-transport_neighbours.c
23 * @brief neighbour management
24 * @author Christian Grothoff
27 #include "gnunet_ats_service.h"
28 #include "gnunet-service-transport_ats.h"
29 #include "gnunet-service-transport_neighbours.h"
30 #include "gnunet-service-transport_manipulation.h"
31 #include "gnunet-service-transport_plugins.h"
32 #include "gnunet-service-transport_validation.h"
33 #include "gnunet-service-transport.h"
34 #include "gnunet_peerinfo_service.h"
35 #include "gnunet_constants.h"
36 #include "transport.h"
39 * Experimental option to ignore SessionQuotaMessages from
42 #define IGNORE_INBOUND_QUOTA GNUNET_YES
45 * Size of the neighbour hash map.
47 #define NEIGHBOUR_TABLE_SIZE 256
50 * Time we give plugin to transmit DISCONNECT message before the
51 * neighbour entry self-destructs.
53 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 500)
56 * How often must a peer violate bandwidth quotas before we start
57 * to simply drop its messages?
59 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
62 * How long are we willing to wait for a response from ATS before timing out?
64 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
67 * How long are we willing to wait for an ACK from the other peer before
68 * giving up on our connect operation?
70 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 15)
73 * How long are we willing to wait for a successful reconnect if
74 * an existing connection went down? Much shorter than the
75 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
76 * higher layers about the disconnect during this period.
78 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS
81 * Interval to send utilization data
83 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS
86 * State describing which kind a reply this neighbour should send
90 * We did not receive a SYN message for this neighbour
95 * The neighbour received a SYN message and has to send a SYN_ACK
101 * The neighbour sent a SYN_ACK message and has to send a ACK
108 GNUNET_NETWORK_STRUCT_BEGIN
111 * Message a peer sends to another to indicate that it intends to
112 * setup a connection/session for data exchange. A 'SESSION_SYN'
113 * should be answered with a 'SESSION_SYN_ACK' with the same body
114 * to confirm. A 'SESSION_SYN_ACK' should then be followed with
115 * a 'ACK'. Once the 'ACK' is received, both peers
116 * should be connected.
118 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;
139 * Message a peer sends to another when connected to indicate that a
140 * session is in use and the peer is still alive or to respond to a keep alive.
141 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
142 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
143 * When the keep alive response with type is received, transport service
144 * will call the respective plugin to update the session timeout
146 struct GNUNET_ATS_SessionKeepAliveMessage {
148 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
149 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
151 struct GNUNET_MessageHeader header;
154 * A nonce to identify the session the keep alive is used for
156 uint32_t nonce GNUNET_PACKED;
161 * Message a peer sends to another when connected to indicate that
162 * the other peer should limit transmissions to the indicated
165 struct GNUNET_ATS_SessionQuotaMessage {
167 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA.
169 struct GNUNET_MessageHeader header;
172 * Quota to use (for sending), in bytes per second.
174 uint32_t quota GNUNET_PACKED;
179 * Message we send to the other peer to notify it that we intentionally
180 * are disconnecting (to reduce timeouts). This is just a friendly
181 * notification, peers must not rely on always receiving disconnect
184 struct GNUNET_ATS_SessionDisconnectMessage {
186 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
188 struct GNUNET_MessageHeader header;
193 uint32_t reserved GNUNET_PACKED;
196 * Purpose of the signature. Extends over the timestamp.
197 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
199 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
202 * Absolute time at the sender. Only the most recent connect
203 * message implies which session is preferred by the sender.
205 struct GNUNET_TIME_AbsoluteNBO timestamp;
208 * Public key of the sender.
210 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
213 * Signature of the peer that sends us the disconnect. Only
214 * valid if the timestamp is AFTER the timestamp from the
215 * corresponding 'SYN' message.
217 struct GNUNET_CRYPTO_EddsaSignature signature;
220 GNUNET_NETWORK_STRUCT_END
224 * For each neighbour we keep a list of messages
225 * that we still want to transmit to the neighbour.
227 struct MessageQueue {
229 * This is a doubly linked list.
231 struct MessageQueue *next;
234 * This is a doubly linked list.
236 struct MessageQueue *prev;
239 * Function to call once we're done.
241 GST_NeighbourSendContinuation cont;
244 * Closure for @e cont
249 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
250 * stuck together in memory. Allocated at the end of this struct.
252 const char *message_buf;
255 * Size of the message buf
257 size_t message_buf_size;
260 * At what time should we fail?
262 struct GNUNET_TIME_Absolute timeout;
267 * A possible address we could use to communicate with a neighbour.
269 struct NeighbourAddress {
271 * Active session for this address.
273 struct GNUNET_ATS_Session *session;
276 * Network-level address information.
278 struct GNUNET_HELLO_Address *address;
281 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
282 * peer for this address. Use to check that the ACK is in response
283 * to our most recent 'SYN'.
285 struct GNUNET_TIME_Absolute connect_timestamp;
288 * Inbound bandwidth from ATS for this address.
290 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
293 * Outbound bandwidth from ATS for this address.
295 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
298 * Did we tell ATS that this is our 'active' address?
303 * The current nonce sent in the last keep alive messages
305 uint32_t keep_alive_nonce;
310 * Entry in neighbours.
312 struct NeighbourMapEntry {
314 * Head of list of messages we would like to send to this peer;
315 * must contain at most one message per client.
317 struct MessageQueue *messages_head;
320 * Tail of list of messages we would like to send to this peer; must
321 * contain at most one message per client.
323 struct MessageQueue *messages_tail;
326 * Are we currently trying to send a message? If so, which one?
328 struct MessageQueue *is_active;
331 * Primary address we currently use to communicate with the neighbour.
333 struct NeighbourAddress primary_address;
336 * Alternative address currently under consideration for communicating
337 * with the neighbour.
339 struct NeighbourAddress alternative_address;
342 * Identity of this neighbour.
344 struct GNUNET_PeerIdentity id;
347 * Main task that drives this peer (timeouts, keepalives, etc.).
348 * Always runs the #master_task().
350 struct GNUNET_SCHEDULER_Task *task;
353 * Task to disconnect neighbour after we received a DISCONNECT message
355 struct GNUNET_SCHEDULER_Task *delayed_disconnect_task;
358 * At what time should we sent the next keep-alive message?
360 struct GNUNET_TIME_Absolute keep_alive_time;
363 * At what time did we sent the last keep-alive message? Used
364 * to calculate round-trip time ("latency").
366 struct GNUNET_TIME_Absolute last_keep_alive_time;
369 * Timestamp we should include in our next SYN_ACK message.
370 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
371 * our SYN_ACK message.
373 struct GNUNET_TIME_Absolute connect_ack_timestamp;
376 * ATS address suggest handle
378 struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle;
381 * Time where we should cut the connection (timeout) if we don't
382 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
383 * if we are in #GNUNET_TRANSPORT_PS_CONNECTED).
385 struct GNUNET_TIME_Absolute timeout;
388 * Tracker for inbound bandwidth.
390 struct GNUNET_BANDWIDTH_Tracker in_tracker;
393 * How often has the other peer (recently) violated the inbound
394 * traffic limit? Incremented by 10 per violation, decremented by 1
395 * per non-violation (for each time interval).
397 unsigned int quota_violation_count;
400 * Latest quota the other peer send us in bytes per second.
401 * We should not send more, least the other peer throttle
402 * receiving our traffic.
404 struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota;
407 * The current state of the peer.
409 enum GNUNET_TRANSPORT_PeerState state;
412 * Did we sent an KEEP_ALIVE message and are we expecting a response?
414 int expect_latency_response;
417 * When a peer wants to connect we have to reply to the 1st SYN message
418 * with a SYN_ACK message. But sometime we cannot send this message
419 * immediately since we do not have an address and then we have to remember
420 * to send this message as soon as we have an address.
422 * Flag to set if we still need to send a SYN_ACK message to the other peer
423 * (once we have an address to use and the peer has been allowed by our
424 * blacklist). Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK
425 * if we need to send a SYN_ACK. Set to #ACK_SEND_ACK if we did
426 * send a SYN_ACK and should go to #S_CONNECTED upon receiving a
427 * 'ACK' (regardless of what our own state machine might say).
429 enum GST_ACK_State ack_state;
432 * Tracking utilization of outbound bandwidth
434 uint32_t util_total_bytes_sent;
437 * Tracking utilization of inbound bandwidth
439 uint32_t util_total_bytes_recv;
442 * Date of last utilization transmission
444 struct GNUNET_TIME_Absolute last_util_transmission;
449 * Hash map from peer identities to the respective `struct NeighbourMapEntry`.
451 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
454 * List of pending blacklist checks: head
456 static struct BlacklistCheckSwitchContext *pending_bc_head;
459 * List of pending blacklist checks: tail
461 static struct BlacklistCheckSwitchContext *pending_bc_tail;
464 * counter for connected neighbours
466 static unsigned int neighbours_connected;
469 * Number of bytes we have currently queued for transmission.
471 static unsigned long long bytes_in_send_queue;
474 * Task transmitting utilization data
476 static struct GNUNET_SCHEDULER_Task *util_transmission_tk;
480 * Convert the given ACK state to a string.
483 * @return corresponding human-readable string
486 print_ack_state(enum GST_ACK_State s)
493 case ACK_SEND_SYN_ACK:
494 return "SEND_SYN_ACK";
507 * Send information about a new outbound quota to our clients.
508 * Note that the outbound quota is enforced client-side (i.e.
509 * in libgnunettransport).
511 * @param n affected peer
514 send_outbound_quota_to_clients(struct NeighbourMapEntry *n)
516 struct QuotaSetMessage q_msg;
517 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
519 if (!GNUNET_TRANSPORT_is_connected(n->state))
521 #if IGNORE_INBOUND_QUOTA
522 bandwidth_min = n->primary_address.bandwidth_out;
524 bandwidth_min = GNUNET_BANDWIDTH_value_min(n->primary_address.bandwidth_out,
525 n->neighbour_receive_quota);
528 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
529 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
530 ntohl(bandwidth_min.value__),
532 q_msg.header.size = htons(sizeof(struct QuotaSetMessage));
533 q_msg.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
534 q_msg.quota = bandwidth_min;
536 GST_clients_broadcast(&q_msg.header,
542 * Notify our clients that another peer connected to us.
544 * @param n the peer that connected
547 neighbours_connect_notification(struct NeighbourMapEntry *n)
549 size_t len = sizeof(struct ConnectInfoMessage);
550 char buf[len] GNUNET_ALIGN;
551 struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *)buf;
552 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
554 #if IGNORE_INBOUND_QUOTA
555 bandwidth_min = n->primary_address.bandwidth_out;
557 bandwidth_min = GNUNET_BANDWIDTH_value_min(n->primary_address.bandwidth_out,
558 n->neighbour_receive_quota);
560 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
561 "We are now connected to peer `%s'\n",
563 connect_msg->header.size = htons(sizeof(buf));
564 connect_msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
565 connect_msg->id = n->id;
566 connect_msg->quota_out = bandwidth_min;
567 GST_clients_broadcast(&connect_msg->header,
573 * Notify our clients (and manipulation) that a peer disconnected from
576 * @param n the peer that disconnected
579 neighbours_disconnect_notification(struct NeighbourMapEntry *n)
581 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
582 "Peer `%s' disconnected\n",
584 GST_manipulation_peer_disconnect(&n->id);
585 GST_clients_broadcast_disconnect(&n->id);
590 * Notify transport clients that a neighbour peer changed its active
593 * @param peer identity of the peer
594 * @param address address possibly NULL if peer is not connected
595 * @param state current state this peer is in
596 * @param state_timeout timeout for the current state of the peer
597 * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
598 * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
601 neighbours_changed_notification(const struct GNUNET_PeerIdentity *peer,
602 const struct GNUNET_HELLO_Address *address,
603 enum GNUNET_TRANSPORT_PeerState state,
604 struct GNUNET_TIME_Absolute state_timeout,
605 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
606 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
610 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
611 "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
613 GST_plugins_a2s(address),
614 GNUNET_TRANSPORT_ps2s(state),
615 GNUNET_STRINGS_absolute_time_to_string(state_timeout));
616 /* FIXME: include bandwidth in notification! */
617 GST_clients_broadcast_peer_notification(peer,
625 * Lookup a neighbour entry in the neighbours hash map.
627 * @param pid identity of the peer to look up
628 * @return the entry, NULL if there is no existing record
630 static struct NeighbourMapEntry *
631 lookup_neighbour(const struct GNUNET_PeerIdentity *pid)
633 if (NULL == neighbours)
635 return GNUNET_CONTAINER_multipeermap_get(neighbours, pid);
640 * Test if we're connected to the given peer.
642 * @param n neighbour entry of peer to test
643 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
646 test_connected(struct NeighbourMapEntry *n)
650 return GNUNET_TRANSPORT_is_connected(n->state);
655 * We don't need a given neighbour address any more.
656 * Release its resources and give appropriate notifications
657 * to ATS and other subsystems.
659 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
662 free_address(struct NeighbourAddress *na)
664 if (GNUNET_YES == na->ats_active)
665 GST_validation_set_address_use(na->address,
667 if (NULL != na->address)
669 GST_ats_block_address(na->address,
671 GNUNET_HELLO_address_free(na->address);
674 na->bandwidth_in = GNUNET_BANDWIDTH_value_init(0);
675 na->bandwidth_out = GNUNET_BANDWIDTH_value_init(0);
676 na->ats_active = GNUNET_NO;
677 na->keep_alive_nonce = 0;
683 * Master task run for every neighbour. Performs all of the time-related
684 * activities (keep alive, send next message, disconnect if idle, finish
685 * clean up after disconnect).
687 * @param cls the `struct NeighbourMapEntry` for which we are running
690 master_task(void *cls);
694 * Set net state and state timeout for this neighbour and notify monitoring
696 * @param n the respective neighbour
697 * @param s the new state
698 * @param timeout the new timeout
701 set_state_and_timeout(struct NeighbourMapEntry *n,
702 enum GNUNET_TRANSPORT_PeerState s,
703 struct GNUNET_TIME_Absolute timeout)
705 if (GNUNET_TRANSPORT_is_connected(s) &&
706 (!GNUNET_TRANSPORT_is_connected(n->state)))
708 neighbours_connect_notification(n);
709 GNUNET_STATISTICS_set(GST_stats,
710 gettext_noop("# peers connected"),
711 ++neighbours_connected,
714 if ((!GNUNET_TRANSPORT_is_connected(s)) &&
715 GNUNET_TRANSPORT_is_connected(n->state))
717 GNUNET_STATISTICS_set(GST_stats,
718 gettext_noop("# peers connected"),
719 --neighbours_connected,
721 neighbours_disconnect_notification(n);
724 if ((timeout.abs_value_us < n->timeout.abs_value_us) &&
727 /* new timeout is earlier, reschedule master task */
728 GNUNET_SCHEDULER_cancel(n->task);
729 n->task = GNUNET_SCHEDULER_add_at(timeout,
733 n->timeout = timeout;
734 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
735 "Neighbour `%s' changed state to %s with timeout %s\n",
737 GNUNET_TRANSPORT_ps2s(s),
738 GNUNET_STRINGS_absolute_time_to_string(timeout));
739 neighbours_changed_notification(&n->id,
740 n->primary_address.address,
743 n->primary_address.bandwidth_in,
744 n->primary_address.bandwidth_out);
749 * Initialize the alternative address of a neighbour
751 * @param n the neighbour
752 * @param address address of the other peer, NULL if other peer
754 * @param session session to use (or NULL, in which case an
755 * address must be setup)
756 * @param bandwidth_in inbound quota to be used when connection is up
757 * @param bandwidth_out outbound quota to be used when connection is up
760 set_alternative_address(struct NeighbourMapEntry *n,
761 const struct GNUNET_HELLO_Address *address,
762 struct GNUNET_ATS_Session *session,
763 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
764 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
766 struct GNUNET_TRANSPORT_PluginFunctions *papi;
768 if (NULL == (papi = GST_plugins_find(address->transport_name)))
773 if (session == n->alternative_address.session)
775 n->alternative_address.bandwidth_in = bandwidth_in;
776 n->alternative_address.bandwidth_out = bandwidth_out;
779 if (NULL != n->alternative_address.address)
781 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
782 "Replacing existing alternative address with another one\n");
783 free_address(&n->alternative_address);
786 session = papi->get_session(papi->cls,
790 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
791 "Failed to obtain new session for peer `%s' and address '%s'\n",
792 GNUNET_i2s(&address->peer),
793 GST_plugins_a2s(address));
794 GNUNET_STATISTICS_update(GST_stats,
795 gettext_noop("# session creation failed"),
800 GST_ats_new_session(address,
802 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
803 "Neighbour `%s' configured alternative address %s\n",
805 GST_plugins_a2s(address));
807 n->alternative_address.address = GNUNET_HELLO_address_copy(address);
808 n->alternative_address.bandwidth_in = bandwidth_in;
809 n->alternative_address.bandwidth_out = bandwidth_out;
810 n->alternative_address.session = session;
811 n->alternative_address.ats_active = GNUNET_NO;
812 n->alternative_address.keep_alive_nonce = 0;
813 GNUNET_assert(GNUNET_YES ==
814 GST_ats_is_known(n->alternative_address.address,
815 n->alternative_address.session));
820 * Transmit a message using the current session of the given
823 * @param n entry for the recipient
824 * @param msgbuf buffer to transmit
825 * @param msgbuf_size number of bytes in @a msgbuf buffer
826 * @param priority transmission priority
827 * @param timeout transmission timeout
828 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
829 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
830 * @param cont continuation to call when finished (can be NULL)
831 * @param cont_cls closure for @a cont
832 * @return timeout (copy of @a timeout or a calculated one if
833 * @a use_keepalive_timeout is #GNUNET_YES.
835 static struct GNUNET_TIME_Relative
836 send_with_session(struct NeighbourMapEntry *n,
840 struct GNUNET_TIME_Relative timeout,
841 unsigned int use_keepalive_timeout,
842 GNUNET_TRANSPORT_TransmitContinuation cont,
845 struct GNUNET_TRANSPORT_PluginFunctions *papi;
846 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
848 GNUNET_assert(NULL != n->primary_address.session);
849 if (((NULL == (papi = GST_plugins_find(n->primary_address.address->transport_name)) ||
850 (-1 == papi->send(papi->cls,
851 n->primary_address.session,
855 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
856 GNUNET_TIME_relative_divide(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
857 papi->query_keepalive_factor(papi->cls))),
866 GST_neighbours_notify_data_sent(n->primary_address.address,
867 n->primary_address.session,
869 GNUNET_break(NULL != papi);
875 * Clear the primary address of a neighbour since this address is not
876 * valid anymore and notify monitoring about it
878 * @param n the neighbour
881 unset_primary_address(struct NeighbourMapEntry *n)
883 /* Notify monitoring about change */
884 if (NULL == n->primary_address.address)
886 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
887 "Disabling primary address\n");
888 neighbours_changed_notification(&n->id,
889 n->primary_address.address,
892 GNUNET_BANDWIDTH_value_init(0),
893 GNUNET_BANDWIDTH_value_init(0));
894 free_address(&n->primary_address);
899 * Free a neighbour map entry.
901 * @param n entry to free
904 free_neighbour(struct NeighbourMapEntry *n)
906 struct MessageQueue *mq;
908 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
909 "Freeing neighbour state of peer `%s'\n",
911 n->is_active = NULL; /* always free'd by its own continuation! */
913 /* fail messages currently in the queue */
914 while (NULL != (mq = n->messages_head))
916 GNUNET_CONTAINER_DLL_remove(n->messages_head,
919 if (NULL != mq->cont)
920 mq->cont(mq->cont_cls,
922 mq->message_buf_size,
926 /* Mark peer as disconnected */
927 set_state_and_timeout(n,
928 GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED,
929 GNUNET_TIME_UNIT_FOREVER_ABS);
930 /* free addresses and mark as unused */
931 unset_primary_address(n);
933 if (NULL != n->alternative_address.address)
935 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
936 "Cleaning up alternative address\n");
937 free_address(&n->alternative_address);
939 GNUNET_assert(GNUNET_YES ==
940 GNUNET_CONTAINER_multipeermap_remove(neighbours,
944 /* Cancel address requests for this peer */
945 if (NULL != n->suggest_handle)
947 GNUNET_ATS_connectivity_suggest_cancel(n->suggest_handle);
948 n->suggest_handle = NULL;
951 /* Cancel the disconnect task */
952 if (NULL != n->delayed_disconnect_task)
954 GNUNET_SCHEDULER_cancel(n->delayed_disconnect_task);
955 n->delayed_disconnect_task = NULL;
958 /* Cancel the master task */
961 GNUNET_SCHEDULER_cancel(n->task);
964 /* free rest of memory */
970 * Function called when the 'DISCONNECT' message has been sent by the
971 * plugin. Frees the neighbour --- if the entry still exists.
974 * @param target identity of the neighbour that was disconnected
975 * @param result #GNUNET_OK if the disconnect got out successfully
976 * @param payload bytes payload
977 * @param physical bytes on wire
980 send_disconnect_cont(void *cls,
981 const struct GNUNET_PeerIdentity *target,
986 struct NeighbourMapEntry *n;
992 n = lookup_neighbour(target);
994 return; /* already gone */
995 if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
996 return; /* have created a fresh entry since */
998 GNUNET_SCHEDULER_cancel(n->task);
999 n->task = GNUNET_SCHEDULER_add_now(&master_task, n);
1004 * Transmit a DISCONNECT message to the other peer.
1006 * @param n neighbour to send DISCONNECT message.
1009 send_disconnect(struct NeighbourMapEntry *n)
1011 struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg;
1013 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1014 "Sending DISCONNECT message to peer `%4s'\n",
1015 GNUNET_i2s(&n->id));
1016 disconnect_msg.header.size = htons(sizeof(struct GNUNET_ATS_SessionDisconnectMessage));
1017 disconnect_msg.header.type =
1018 htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1019 disconnect_msg.reserved = htonl(0);
1020 disconnect_msg.purpose.size =
1021 htonl(sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) +
1022 sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) +
1023 sizeof(struct GNUNET_TIME_AbsoluteNBO));
1024 disconnect_msg.purpose.purpose =
1025 htonl(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1026 disconnect_msg.timestamp =
1027 GNUNET_TIME_absolute_hton(GNUNET_TIME_absolute_get());
1028 disconnect_msg.public_key = GST_my_identity.public_key;
1029 GNUNET_assert(GNUNET_OK ==
1030 GNUNET_CRYPTO_eddsa_sign(GST_my_private_key,
1031 &disconnect_msg.purpose,
1032 &disconnect_msg.signature));
1034 (void)send_with_session(n,
1036 sizeof(disconnect_msg),
1038 GNUNET_TIME_UNIT_FOREVER_REL,
1040 &send_disconnect_cont,
1042 GNUNET_STATISTICS_update(GST_stats,
1043 gettext_noop("# DISCONNECT messages sent"),
1050 * Disconnect from the given neighbour, clean up the record.
1052 * @param n neighbour to disconnect from
1055 disconnect_neighbour(struct NeighbourMapEntry *n)
1057 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1058 "Disconnecting from peer %s in state %s\n",
1060 GNUNET_TRANSPORT_ps2s(n->state));
1061 /* depending on state, notify neighbour and/or upper layers of this peer
1065 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1066 case GNUNET_TRANSPORT_PS_INIT_ATS:
1067 /* other peer is completely unaware of us, no need to send DISCONNECT */
1071 case GNUNET_TRANSPORT_PS_SYN_SENT:
1073 set_state_and_timeout(n,
1074 GNUNET_TRANSPORT_PS_DISCONNECT,
1075 GNUNET_TIME_UNIT_FOREVER_ABS);
1078 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
1079 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1083 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
1084 /* we DID ACK the other peer's request, must send DISCONNECT */
1086 set_state_and_timeout(n,
1087 GNUNET_TRANSPORT_PS_DISCONNECT,
1088 GNUNET_TIME_UNIT_FOREVER_ABS);
1091 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1092 case GNUNET_TRANSPORT_PS_CONNECTED:
1093 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1094 /* we are currently connected, need to send disconnect and do
1095 internal notifications and update statistics */
1097 set_state_and_timeout(n,
1098 GNUNET_TRANSPORT_PS_DISCONNECT,
1099 GNUNET_TIME_UNIT_FOREVER_ABS);
1102 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1103 /* Disconnecting while waiting for an ATS address to reconnect,
1104 * cannot send DISCONNECT */
1108 case GNUNET_TRANSPORT_PS_DISCONNECT:
1109 /* already disconnected, ignore */
1112 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1113 /* already cleaned up, how did we get here!? */
1118 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1119 "Unhandled state `%s'\n",
1120 GNUNET_TRANSPORT_ps2s(n->state));
1124 /* schedule timeout to clean up */
1125 if (NULL != n->task)
1126 GNUNET_SCHEDULER_cancel(n->task);
1127 n->task = GNUNET_SCHEDULER_add_delayed(DISCONNECT_SENT_TIMEOUT,
1134 * Change the incoming quota for the given peer. Updates
1135 * our own receive rate and informs the neighbour about
1138 * @param n neighbour entry to change quota for
1139 * @param quota new quota
1140 * @return #GNUNET_YES if @a n is still valid, #GNUNET_NO if
1144 set_incoming_quota(struct NeighbourMapEntry *n,
1145 struct GNUNET_BANDWIDTH_Value32NBO quota)
1147 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1148 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
1149 ntohl(quota.value__), GNUNET_i2s(&n->id));
1150 GNUNET_BANDWIDTH_tracker_update_quota(&n->in_tracker,
1152 if (0 != ntohl(quota.value__))
1154 struct GNUNET_ATS_SessionQuotaMessage sqm;
1156 sqm.header.size = htons(sizeof(struct GNUNET_ATS_SessionQuotaMessage));
1157 sqm.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA);
1158 sqm.quota = quota.value__;
1159 if (NULL != n->primary_address.session)
1160 (void)send_with_session(n,
1164 GNUNET_TIME_UNIT_FOREVER_REL,
1169 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1170 "Disconnecting peer `%s' due to SET_QUOTA\n",
1171 GNUNET_i2s(&n->id));
1172 if (GNUNET_YES == test_connected(n))
1173 GNUNET_STATISTICS_update(GST_stats,
1174 gettext_noop("# disconnects due to quota of 0"),
1176 disconnect_neighbour(n);
1182 * Initialize the primary address of a neighbour
1184 * @param n the neighbour
1185 * @param address address of the other peer, NULL if other peer
1187 * @param session session to use (or NULL, in which case an
1188 * address must be setup)
1189 * @param bandwidth_in inbound quota to be used when connection is up
1190 * @param bandwidth_out outbound quota to be used when connection is up
1193 set_primary_address(struct NeighbourMapEntry *n,
1194 const struct GNUNET_HELLO_Address *address,
1195 struct GNUNET_ATS_Session *session,
1196 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1197 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
1199 if (session == n->primary_address.session)
1201 GST_validation_set_address_use(n->primary_address.address,
1203 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
1205 n->primary_address.bandwidth_in = bandwidth_in;
1207 set_incoming_quota(n,
1211 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
1213 n->primary_address.bandwidth_out = bandwidth_out;
1214 send_outbound_quota_to_clients(n);
1218 if ((NULL != n->primary_address.address) &&
1219 (0 == GNUNET_HELLO_address_cmp(address,
1220 n->primary_address.address)))
1225 if (NULL == session)
1228 GST_ats_block_address(address,
1232 if (NULL != n->primary_address.address)
1234 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1235 "Replacing existing primary address with another one\n");
1236 free_address(&n->primary_address);
1238 n->primary_address.address = GNUNET_HELLO_address_copy(address);
1239 n->primary_address.bandwidth_in = bandwidth_in;
1240 n->primary_address.bandwidth_out = bandwidth_out;
1241 n->primary_address.session = session;
1242 n->primary_address.keep_alive_nonce = 0;
1243 GNUNET_assert(GNUNET_YES ==
1244 GST_ats_is_known(n->primary_address.address,
1245 n->primary_address.session));
1246 /* subsystems about address use */
1247 GST_validation_set_address_use(n->primary_address.address,
1250 set_incoming_quota(n,
1253 send_outbound_quota_to_clients(n);
1254 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1255 "Neighbour `%s' switched to address `%s'\n",
1257 GST_plugins_a2s(address));
1259 neighbours_changed_notification(&n->id,
1260 n->primary_address.address,
1263 n->primary_address.bandwidth_in,
1264 n->primary_address.bandwidth_out);
1269 * We're done with our transmission attempt, continue processing.
1271 * @param cls the `struct MessageQueue` of the message
1272 * @param receiver intended receiver
1273 * @param success whether it worked or not
1274 * @param size_payload bytes payload sent
1275 * @param physical bytes sent on wire
1278 transmit_send_continuation(void *cls,
1279 const struct GNUNET_PeerIdentity *receiver,
1281 size_t size_payload,
1284 struct MessageQueue *mq = cls;
1285 struct NeighbourMapEntry *n;
1287 if (NULL == (n = lookup_neighbour(receiver)))
1289 if (NULL != mq->cont)
1290 mq->cont(mq->cont_cls,
1291 GNUNET_SYSERR /* not connected */,
1295 return; /* disconnect or other error while transmitting, can happen */
1297 if (n->is_active == mq)
1299 /* this is still "our" neighbour, remove us from its queue
1300 and allow it to send the next message now */
1301 n->is_active = NULL;
1302 if (NULL != n->task)
1303 GNUNET_SCHEDULER_cancel(n->task);
1304 n->task = GNUNET_SCHEDULER_add_now(&master_task,
1307 if (bytes_in_send_queue < mq->message_buf_size)
1309 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1310 "Bytes_in_send_queue `%llu', Message_size %u, result: %s, payload %u, on wire %u\n",
1311 bytes_in_send_queue,
1312 (unsigned int)mq->message_buf_size,
1313 (GNUNET_OK == success) ? "OK" : "FAIL",
1314 (unsigned int)size_payload,
1315 (unsigned int)physical);
1319 GNUNET_break(size_payload == mq->message_buf_size);
1320 bytes_in_send_queue -= mq->message_buf_size;
1321 GNUNET_STATISTICS_set(GST_stats,
1322 gettext_noop("# bytes in message queue for other peers"),
1323 bytes_in_send_queue,
1325 if (GNUNET_OK == success)
1326 GNUNET_STATISTICS_update(GST_stats,
1327 gettext_noop("# messages transmitted to other peers"),
1331 GNUNET_STATISTICS_update(GST_stats,
1333 ("# transmission failures for messages to other peers"),
1335 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1336 "Sending message to `%s' of type %u with %u bytes was a %s\n",
1337 GNUNET_i2s(receiver),
1338 ntohs(((struct GNUNET_MessageHeader *)mq->message_buf)->type),
1339 (unsigned int)mq->message_buf_size,
1340 (success == GNUNET_OK) ? "success" : "FAILURE");
1341 if (NULL != mq->cont)
1342 mq->cont(mq->cont_cls,
1351 * Check the message list for the given neighbour and if we can
1352 * send a message, do so. This function should only be called
1353 * if the connection is at least generally ready for transmission.
1354 * While we will only send one message at a time, no bandwidth
1355 * quota management is performed here. If a message was given to
1356 * the plugin, the continuation will automatically re-schedule
1357 * the 'master' task once the next message might be transmitted.
1359 * @param n target peer for which to transmit
1362 try_transmission_to_peer(struct NeighbourMapEntry *n)
1364 struct MessageQueue *mq;
1365 struct GNUNET_TIME_Relative timeout;
1367 if (NULL == n->primary_address.address)
1369 /* no address, why are we here? */
1373 if ((0 == n->primary_address.address->address_length) &&
1374 (NULL == n->primary_address.session))
1376 /* no address, why are we here? */
1380 if (NULL != n->is_active)
1382 /* transmission already pending */
1386 /* timeout messages from the queue that are past their due date */
1387 while (NULL != (mq = n->messages_head))
1389 timeout = GNUNET_TIME_absolute_get_remaining(mq->timeout);
1390 if (timeout.rel_value_us > 0)
1392 GNUNET_STATISTICS_update(GST_stats,
1393 gettext_noop("# messages timed out while in transport queue"),
1396 GNUNET_CONTAINER_DLL_remove(n->messages_head,
1400 transmit_send_continuation(mq,
1403 mq->message_buf_size,
1407 return; /* no more messages */
1408 if (NULL == n->primary_address.address)
1410 /* transmit_send_continuation() caused us to drop session,
1411 can't try transmission anymore. */
1416 GNUNET_CONTAINER_DLL_remove(n->messages_head,
1421 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1422 "Giving message with %u bytes to plugin session %p\n",
1423 (unsigned int)mq->message_buf_size,
1424 n->primary_address.session);
1425 (void)send_with_session(n,
1427 mq->message_buf_size,
1431 &transmit_send_continuation,
1437 * Send keepalive message to the neighbour. Must only be called
1438 * if we are on 'connected' state or while trying to switch addresses.
1439 * Will internally determine if a keepalive is truly needed (so can
1440 * always be called).
1442 * @param n neighbour that went idle and needs a keepalive
1445 send_keepalive(struct NeighbourMapEntry *n)
1447 struct GNUNET_ATS_SessionKeepAliveMessage m;
1448 struct GNUNET_TIME_Relative timeout;
1451 GNUNET_assert((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1452 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state));
1453 if (GNUNET_TIME_absolute_get_remaining(n->keep_alive_time).rel_value_us > 0)
1454 return; /* no keepalive needed at this time */
1456 nonce = 0; /* 0 indicates 'not set' */
1458 nonce = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_NONCE,
1461 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1462 "Sending KEEPALIVE to peer `%s' with nonce %u\n",
1465 m.header.size = htons(sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1466 m.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1467 m.nonce = htonl(nonce);
1469 timeout = send_with_session(n,
1472 UINT32_MAX /* priority */,
1473 GNUNET_TIME_UNIT_FOREVER_REL,
1476 GNUNET_STATISTICS_update(GST_stats,
1477 gettext_noop("# KEEPALIVES sent"),
1480 n->primary_address.keep_alive_nonce = nonce;
1481 n->expect_latency_response = GNUNET_YES;
1482 n->last_keep_alive_time = GNUNET_TIME_absolute_get();
1483 n->keep_alive_time = GNUNET_TIME_relative_to_absolute(timeout);
1488 * Keep the connection to the given neighbour alive longer,
1489 * we received a KEEPALIVE (or equivalent); send a response.
1491 * @param neighbour neighbour to keep alive (by sending keep alive response)
1492 * @param m the keep alive message containing the nonce to respond to
1495 GST_neighbours_keepalive(const struct GNUNET_PeerIdentity *neighbour,
1496 const struct GNUNET_MessageHeader *m)
1498 struct NeighbourMapEntry *n;
1499 const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in;
1500 struct GNUNET_ATS_SessionKeepAliveMessage msg;
1502 if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs(m->size))
1508 msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *)m;
1509 if (NULL == (n = lookup_neighbour(neighbour)))
1511 GNUNET_STATISTICS_update(GST_stats,
1513 ("# KEEPALIVE messages discarded (peer unknown)"),
1517 if (NULL == n->primary_address.session)
1519 GNUNET_STATISTICS_update(GST_stats,
1521 ("# KEEPALIVE messages discarded (no session)"),
1526 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1527 "Received KEEPALIVE request from peer `%s' with nonce %u\n",
1529 ntohl(msg_in->nonce));
1530 GNUNET_STATISTICS_update(GST_stats,
1531 gettext_noop("# KEEPALIVES received in good order"),
1535 /* send reply to allow neighbour to measure latency */
1536 msg.header.size = htons(sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1537 msg.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1538 msg.nonce = msg_in->nonce;
1539 (void)send_with_session(n,
1541 sizeof(struct GNUNET_ATS_SessionKeepAliveMessage),
1542 UINT32_MAX /* priority */,
1543 GNUNET_TIME_UNIT_FOREVER_REL,
1550 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1551 * latency to this peer. Pass the updated information (existing ats
1552 * plus calculated latency) to ATS.
1554 * @param neighbour neighbour to keep alive
1555 * @param m the message containing the keep alive response
1558 GST_neighbours_keepalive_response(const struct GNUNET_PeerIdentity *neighbour,
1559 const struct GNUNET_MessageHeader *m)
1561 struct NeighbourMapEntry *n;
1562 const struct GNUNET_ATS_SessionKeepAliveMessage *msg;
1563 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1564 struct GNUNET_TIME_Relative latency;
1566 if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs(m->size))
1572 msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *)m;
1573 if (NULL == (n = lookup_neighbour(neighbour)))
1575 GNUNET_STATISTICS_update(GST_stats,
1576 gettext_noop("# KEEPALIVE_RESPONSEs discarded (not connected)"),
1581 if ((GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1582 (GNUNET_YES != n->expect_latency_response))
1584 GNUNET_STATISTICS_update(GST_stats,
1585 gettext_noop("# KEEPALIVE_RESPONSEs discarded (not expected)"),
1590 if (NULL == n->primary_address.address)
1592 GNUNET_STATISTICS_update(GST_stats,
1593 gettext_noop("# KEEPALIVE_RESPONSEs discarded (address changed)"),
1598 if (n->primary_address.keep_alive_nonce != ntohl(msg->nonce))
1600 if (0 == n->primary_address.keep_alive_nonce)
1601 GNUNET_STATISTICS_update(GST_stats,
1602 gettext_noop("# KEEPALIVE_RESPONSEs discarded (no nonce)"),
1606 GNUNET_STATISTICS_update(GST_stats,
1607 gettext_noop("# KEEPALIVE_RESPONSEs discarded (bad nonce)"),
1612 GNUNET_STATISTICS_update(GST_stats,
1613 gettext_noop("# KEEPALIVE_RESPONSEs received (OK)"),
1618 /* Update session timeout here */
1619 if (NULL != (papi = GST_plugins_find(n->primary_address.address->transport_name)))
1621 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1622 "Updating session for peer `%s' for session %p\n",
1624 n->primary_address.session);
1625 papi->update_session_timeout(papi->cls,
1627 n->primary_address.session);
1634 n->primary_address.keep_alive_nonce = 0;
1635 n->expect_latency_response = GNUNET_NO;
1636 set_state_and_timeout(n,
1638 GNUNET_TIME_relative_to_absolute(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1640 latency = GNUNET_TIME_absolute_get_duration(n->last_keep_alive_time);
1641 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1642 "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n",
1644 GNUNET_STRINGS_relative_time_to_string(latency,
1646 GST_ats_update_delay(n->primary_address.address,
1647 GNUNET_TIME_relative_divide(latency,
1653 * We have received a message from the given sender. How long should
1654 * we delay before receiving more? (Also used to keep the peer marked
1657 * @param sender sender of the message
1658 * @param size size of the message
1659 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1660 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1661 * #GNUNET_SYSERR if the connection is not fully up yet
1662 * @return how long to wait before reading more from this sender
1664 struct GNUNET_TIME_Relative
1665 GST_neighbours_calculate_receive_delay(const struct GNUNET_PeerIdentity *sender,
1669 struct NeighbourMapEntry *n;
1670 struct GNUNET_TIME_Relative ret;
1672 if (NULL == neighbours)
1674 *do_forward = GNUNET_NO;
1675 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1677 if (NULL == (n = lookup_neighbour(sender)))
1679 GNUNET_STATISTICS_update(GST_stats,
1680 gettext_noop("# messages discarded due to lack of neighbour record"),
1683 *do_forward = GNUNET_NO;
1684 return GNUNET_TIME_UNIT_ZERO;
1686 if (!test_connected(n))
1688 *do_forward = GNUNET_SYSERR;
1689 return GNUNET_TIME_UNIT_ZERO;
1691 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume(&n->in_tracker, size))
1693 n->quota_violation_count++;
1694 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1695 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1696 n->in_tracker.available_bytes_per_s__,
1697 n->quota_violation_count);
1698 /* Discount 32k per violation */
1699 GNUNET_BANDWIDTH_tracker_consume(&n->in_tracker, -32 * 1024);
1703 if (n->quota_violation_count > 0)
1705 /* try to add 32k back */
1706 GNUNET_BANDWIDTH_tracker_consume(&n->in_tracker, 32 * 1024);
1707 n->quota_violation_count--;
1710 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1712 GNUNET_STATISTICS_update(GST_stats,
1714 ("# bandwidth quota violations by other peers"),
1716 *do_forward = GNUNET_NO;
1717 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1719 *do_forward = GNUNET_YES;
1720 ret = GNUNET_BANDWIDTH_tracker_get_delay(&n->in_tracker, 32 * 1024);
1721 if (ret.rel_value_us > 0)
1723 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1724 "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n",
1725 (long long)n->in_tracker.consumption_since_last_update__,
1726 (unsigned int)n->in_tracker.available_bytes_per_s__,
1727 GNUNET_STRINGS_relative_time_to_string(ret, GNUNET_YES));
1728 GNUNET_STATISTICS_update(GST_stats,
1729 gettext_noop("# ms throttling suggested"),
1730 (int64_t)ret.rel_value_us / 1000LL,
1738 * Transmit a message to the given target using the active connection.
1740 * @param target destination
1741 * @param msg message to send
1742 * @param msg_size number of bytes in msg
1743 * @param timeout when to fail with timeout
1744 * @param cont function to call when done
1745 * @param cont_cls closure for @a cont
1748 GST_neighbours_send(const struct GNUNET_PeerIdentity *target,
1751 struct GNUNET_TIME_Relative timeout,
1752 GST_NeighbourSendContinuation cont,
1755 struct NeighbourMapEntry *n;
1756 struct MessageQueue *mq;
1758 /* All ove these cases should never happen; they are all API violations.
1759 But we check anyway, just to be sure. */
1760 if (NULL == (n = lookup_neighbour(target)))
1770 if (GNUNET_YES != test_connected(n))
1780 bytes_in_send_queue += msg_size;
1781 GNUNET_STATISTICS_set(GST_stats,
1783 ("# bytes in message queue for other peers"),
1784 bytes_in_send_queue, GNUNET_NO);
1785 mq = GNUNET_malloc(sizeof(struct MessageQueue) + msg_size);
1787 mq->cont_cls = cont_cls;
1788 GNUNET_memcpy(&mq[1], msg, msg_size);
1789 mq->message_buf = (const char *)&mq[1];
1790 mq->message_buf_size = msg_size;
1791 mq->timeout = GNUNET_TIME_relative_to_absolute(timeout);
1793 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1794 "Enqueueing %u bytes to send to peer %s\n",
1795 (unsigned int)msg_size,
1796 GNUNET_i2s(target));
1797 GNUNET_CONTAINER_DLL_insert_tail(n->messages_head,
1800 if (NULL != n->task)
1801 GNUNET_SCHEDULER_cancel(n->task);
1802 n->task = GNUNET_SCHEDULER_add_now(&master_task, n);
1807 * Continuation called from our attempt to transmitted our
1808 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1809 * target. Continue processing based on the @a result. Specifically,
1810 * if we failed to transmit, discard the address we used.
1813 * @param target which peer received the transmission
1814 * @param result #GNUNET_OK if sending worked
1815 * @param size_payload how many bytes of payload were sent (ignored)
1816 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1819 send_session_syn_cont(void *cls,
1820 const struct GNUNET_PeerIdentity *target,
1822 size_t size_payload,
1823 size_t size_on_wire)
1825 struct NeighbourMapEntry *n;
1830 n = lookup_neighbour(target);
1833 /* SYN continuation was called after neighbor was freed,
1834 * for example due to a time out for the state or the session
1835 * used was already terminated: nothing to do here... */
1839 if ((GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1840 (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1841 (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1843 /* SYN continuation was called after neighbor changed state,
1844 * for example due to a time out for the state or the session
1845 * used was already terminated: nothing to do here... */
1848 if (GNUNET_OK == result)
1851 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1852 _("Failed to send SYN message to peer `%s'\n"),
1853 GNUNET_i2s(target));
1856 case GNUNET_TRANSPORT_PS_SYN_SENT:
1857 /* Remove address and request an additional one */
1858 unset_primary_address(n);
1859 set_state_and_timeout(n,
1860 GNUNET_TRANSPORT_PS_INIT_ATS,
1861 GNUNET_TIME_relative_to_absolute(FAST_RECONNECT_TIMEOUT));
1864 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1865 /* Remove address and request an additional one */
1866 unset_primary_address(n);
1867 set_state_and_timeout(n,
1868 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1869 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
1872 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1873 /* Remove address and request and go back to primary address */
1874 GNUNET_STATISTICS_update(GST_stats,
1875 gettext_noop("# Failed attempts to switch addresses (failed to send SYN CONT)"),
1878 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1879 "Switch failed, cleaning up alternative address\n");
1880 free_address(&n->alternative_address);
1881 set_state_and_timeout(n,
1882 GNUNET_TRANSPORT_PS_CONNECTED,
1883 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
1887 disconnect_neighbour(n);
1894 * Send a SYN message via the given address.
1896 * @param na address to use
1899 send_syn(struct NeighbourAddress *na)
1901 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1902 struct TransportSynMessage connect_msg;
1903 struct NeighbourMapEntry *n;
1905 GNUNET_assert(NULL != na->session);
1906 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1907 "Sending SYN message to peer `%s' at %s\n",
1908 GNUNET_i2s(&na->address->peer),
1909 GST_plugins_a2s(na->address));
1911 papi = GST_plugins_find(na->address->transport_name);
1912 GNUNET_assert(NULL != papi);
1913 GNUNET_STATISTICS_update(GST_stats,
1915 ("# SYN messages sent"),
1917 na->connect_timestamp = GNUNET_TIME_absolute_get();
1918 connect_msg.header.size = htons(sizeof(struct TransportSynMessage));
1919 connect_msg.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1920 connect_msg.reserved = htonl(0);
1921 connect_msg.timestamp = GNUNET_TIME_absolute_hton(na->connect_timestamp);
1923 papi->send(papi->cls,
1925 (const char *)&connect_msg,
1926 sizeof(struct TransportSynMessage),
1928 SETUP_CONNECTION_TIMEOUT,
1929 &send_session_syn_cont, NULL))
1931 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
1932 _("Failed to transmit SYN message to %s\n"),
1933 GST_plugins_a2s(na->address));
1934 n = lookup_neighbour(&na->address->peer);
1942 case GNUNET_TRANSPORT_PS_SYN_SENT:
1943 /* Remove address and request and additional one */
1944 GNUNET_assert(na == &n->primary_address);
1945 unset_primary_address(n);
1946 set_state_and_timeout(n,
1947 GNUNET_TRANSPORT_PS_INIT_ATS,
1948 GNUNET_TIME_relative_to_absolute(FAST_RECONNECT_TIMEOUT));
1949 /* Hard failure to send the SYN message with this address:
1950 Destroy address and session */
1953 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1954 /* Remove address and request an additional one */
1955 GNUNET_assert(na == &n->primary_address);
1956 unset_primary_address(n);
1957 set_state_and_timeout(n,
1958 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1959 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
1962 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1963 GNUNET_assert(na == &n->alternative_address);
1964 GNUNET_STATISTICS_update(GST_stats,
1965 gettext_noop("# Failed attempts to switch addresses (failed to send SYN)"),
1968 /* Remove address and request an additional one */
1969 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1970 "Switch failed, cleaning up alternative address\n");
1971 free_address(&n->alternative_address);
1972 set_state_and_timeout(n,
1973 GNUNET_TRANSPORT_PS_CONNECTED,
1974 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
1979 disconnect_neighbour(n);
1984 GST_neighbours_notify_data_sent(na->address,
1986 sizeof(struct TransportSynMessage));
1991 * Continuation called from our attempt to transmitted our
1992 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
1993 * target. Continue processing based on the @a result. Specifically,
1994 * if we failed to transmit, discard the address we used.
1997 * @param target which peer received the transmission
1998 * @param result #GNUNET_OK if sending worked
1999 * @param size_payload how many bytes of payload were sent (ignored)
2000 * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
2003 send_session_syn_ack_cont(void *cls,
2004 const struct GNUNET_PeerIdentity *target,
2006 size_t size_payload,
2007 size_t size_on_wire)
2009 struct NeighbourMapEntry *n;
2014 n = lookup_neighbour(target);
2017 /* SYN_ACK continuation was called after neighbor was freed,
2018 * for example due to a time out for the state or the session
2019 * used was already terminated: nothing to do here... */
2023 if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2025 /* SYN_ACK continuation was called after neighbor changed state,
2026 * for example due to a time out for the state or the session
2027 * used was already terminated: nothing to do here... */
2030 if (GNUNET_OK == result)
2033 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2034 _("Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2036 GST_plugins_a2s(n->primary_address.address));
2038 /* Remove address and request and additional one */
2039 /* FIXME: what if the neighbour's primary address
2040 changed in the meantime? Might want to instead
2041 pass "something" around in closure to be sure. */
2042 unset_primary_address(n);
2043 n->ack_state = ACK_SEND_SYN_ACK;
2044 set_state_and_timeout(n,
2045 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2046 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
2051 * Send a SYN_ACK message via the given address.
2053 * @param na address and session to use
2054 * @param timestamp timestamp to use for the ACK message
2055 * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2058 send_syn_ack_message(struct NeighbourAddress *na,
2059 struct GNUNET_TIME_Absolute timestamp)
2061 const struct GNUNET_HELLO_Address *address = na->address;
2062 struct GNUNET_ATS_Session *session = na->session;
2063 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2064 struct TransportSynMessage connect_msg;
2065 struct NeighbourMapEntry *n;
2067 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2068 "Sending SYN_ACK to peer `%s'\n",
2069 GNUNET_i2s(&address->peer));
2071 if (NULL == (papi = GST_plugins_find(address->transport_name)))
2076 if (NULL == session)
2077 session = papi->get_session(papi->cls,
2079 if (NULL == session)
2084 GST_ats_new_session(address,
2086 GNUNET_STATISTICS_update(GST_stats,
2088 ("# SYN_ACK messages sent"),
2090 connect_msg.header.size = htons(sizeof(struct TransportSynMessage));
2091 connect_msg.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2092 connect_msg.reserved = htonl(0);
2093 connect_msg.timestamp = GNUNET_TIME_absolute_hton(timestamp);
2095 if (GNUNET_SYSERR ==
2096 papi->send(papi->cls,
2098 (const char *)&connect_msg,
2099 sizeof(struct TransportSynMessage),
2101 GNUNET_TIME_UNIT_FOREVER_REL,
2102 &send_session_syn_ack_cont, NULL))
2104 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
2105 _("Failed to transmit SYN_ACK message to %s\n"),
2106 GST_plugins_a2s(address));
2108 n = lookup_neighbour(&address->peer);
2114 /* Remove address and request and additional one */
2115 unset_primary_address(n);
2116 n->ack_state = ACK_SEND_SYN_ACK;
2117 set_state_and_timeout(n,
2118 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2119 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
2126 * Function called by the bandwidth tracker for a peer whenever
2127 * the tracker's state changed such that we need to recalculate
2128 * the delay for flow control. We calculate the latest delay
2129 * and inform the plugin (if applicable).
2131 * @param cls the `struct NeighbourMapEntry` to update calculations for
2134 inbound_bw_tracker_update(void *cls)
2136 struct NeighbourMapEntry *n = cls;
2137 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2138 struct GNUNET_TIME_Relative delay;
2141 if (NULL == n->primary_address.address)
2142 return; /* not active, ignore */
2143 papi = GST_plugins_find(n->primary_address.address->transport_name);
2144 GNUNET_assert(NULL != papi);
2145 if (NULL == papi->update_inbound_delay)
2147 delay = GST_neighbours_calculate_receive_delay(&n->id,
2150 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2151 "New inbound delay for peer `%s' is %llu ms\n",
2153 (unsigned long long)delay.rel_value_us / 1000LL);
2154 if (NULL == n->primary_address.session)
2156 papi->update_inbound_delay(papi->cls,
2158 n->primary_address.session,
2164 * Create a fresh entry in the neighbour map for the given peer
2166 * @param peer peer to create an entry for
2167 * @return new neighbour map entry
2169 static struct NeighbourMapEntry *
2170 setup_neighbour(const struct GNUNET_PeerIdentity *peer)
2172 struct NeighbourMapEntry *n;
2175 memcmp(&GST_my_identity,
2177 sizeof(struct GNUNET_PeerIdentity)))
2179 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2180 "Cowardly refusing to consider myself my neighbour!\n");
2183 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2184 "Creating new neighbour entry for `%s'\n",
2186 n = GNUNET_new(struct NeighbourMapEntry);
2188 n->ack_state = ACK_UNDEFINED;
2189 n->last_util_transmission = GNUNET_TIME_absolute_get();
2190 n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2191 GNUNET_BANDWIDTH_tracker_init(&n->in_tracker,
2192 &inbound_bw_tracker_update,
2194 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2195 MAX_BANDWIDTH_CARRY_S);
2196 n->task = GNUNET_SCHEDULER_add_now(&master_task, n);
2197 set_state_and_timeout(n,
2198 GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2199 GNUNET_TIME_UNIT_FOREVER_ABS);
2200 GNUNET_assert(GNUNET_OK ==
2201 GNUNET_CONTAINER_multipeermap_put(neighbours,
2204 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2205 n->suggest_handle = GNUNET_ATS_connectivity_suggest(GST_ats_connect,
2214 * Entry in a DLL we use to keep track of pending blacklist checks.
2216 struct BlacklistCheckSwitchContext {
2220 struct BlacklistCheckSwitchContext *prev;
2225 struct BlacklistCheckSwitchContext *next;
2228 * Handle to the blacklist check we are performing.
2230 struct GST_BlacklistCheck *blc;
2233 * Inbound bandwidth that was assigned to @e address.
2235 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2238 * Outbound bandwidth that was assigned to @e address.
2240 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2245 * We received a 'SYN' message from the other peer.
2246 * Consider switching to it.
2248 * @param message possibly a `struct TransportSynMessage` (check format)
2249 * @param peer identity of the peer to switch the address for
2250 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2253 GST_neighbours_handle_session_syn(const struct GNUNET_MessageHeader *message,
2254 const struct GNUNET_PeerIdentity *peer)
2256 const struct TransportSynMessage *scm;
2257 struct NeighbourMapEntry *n;
2258 struct GNUNET_TIME_Absolute ts;
2260 if (ntohs(message->size) != sizeof(struct TransportSynMessage))
2263 return GNUNET_SYSERR;
2265 GNUNET_STATISTICS_update(GST_stats,
2267 ("# SYN messages received"),
2269 if (NULL == neighbours)
2271 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2272 _("SYN request from peer `%s' ignored due impending shutdown\n"),
2274 return GNUNET_OK; /* we're shutting down */
2276 scm = (const struct TransportSynMessage *)message;
2277 GNUNET_break_op(0 == ntohl(scm->reserved));
2278 ts = GNUNET_TIME_absolute_ntoh(scm->timestamp);
2280 memcmp(&GST_my_identity,
2282 sizeof(struct GNUNET_PeerIdentity)))
2284 /* loopback connection-to-self, ignore */
2285 return GNUNET_SYSERR;
2287 n = lookup_neighbour(peer);
2290 /* This is a new neighbour and set to not connected */
2291 n = setup_neighbour(peer);
2292 GNUNET_assert(NULL != n);
2295 /* Remember this SYN message in neighbour */
2296 n->ack_state = ACK_SEND_SYN_ACK;
2297 n->connect_ack_timestamp = ts;
2299 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2300 "Received SYN for peer `%s' in state %s/%s\n",
2302 GNUNET_TRANSPORT_ps2s(n->state),
2303 print_ack_state(n->ack_state));
2307 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2308 /* Request an address from ATS to send SYN_ACK to this peer */
2309 set_state_and_timeout(n,
2310 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2311 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
2314 case GNUNET_TRANSPORT_PS_INIT_ATS:
2315 /* SYN message takes priority over us asking ATS for address:
2316 * Wait for ATS to suggest an address and send SYN_ACK */
2317 set_state_and_timeout(n,
2318 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2319 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
2322 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2323 /* We already wait for an address to send an SYN_ACK */
2326 case GNUNET_TRANSPORT_PS_SYN_SENT:
2327 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2328 /* Send ACK immediately */
2329 n->ack_state = ACK_SEND_ACK;
2330 send_syn_ack_message(&n->primary_address,
2334 case GNUNET_TRANSPORT_PS_CONNECTED:
2335 /* we are already connected and can thus send the ACK immediately */
2336 GNUNET_assert(NULL != n->primary_address.address);
2337 GNUNET_assert(NULL != n->primary_address.session);
2338 n->ack_state = ACK_SEND_ACK;
2339 send_syn_ack_message(&n->primary_address,
2343 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2344 /* We wait for ATS address suggestion */
2347 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2348 /* We received a SYN message while waiting for a SYN_ACK in fast
2349 * reconnect. Send SYN_ACK immediately */
2350 n->ack_state = ACK_SEND_ACK;
2351 send_syn_ack_message(&n->primary_address,
2352 n->connect_ack_timestamp);
2355 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2356 /* We are already connected and can thus send the ACK immediately;
2357 still, it can never hurt to have an alternative address, so also
2358 tell ATS about it */
2359 GNUNET_assert(NULL != n->primary_address.address);
2360 GNUNET_assert(NULL != n->primary_address.session);
2361 n->ack_state = ACK_SEND_ACK;
2362 send_syn_ack_message(&n->primary_address,
2366 case GNUNET_TRANSPORT_PS_DISCONNECT:
2367 /* Get rid of remains and re-try */
2369 n = setup_neighbour(peer);
2370 GNUNET_assert(NULL != n);
2371 /* Remember the SYN time stamp for ACK message */
2372 n->ack_state = ACK_SEND_SYN_ACK;
2373 n->connect_ack_timestamp = ts;
2374 /* Request an address for the peer */
2375 set_state_and_timeout(n,
2376 GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2377 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
2380 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2381 /* should not be possible */
2386 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
2387 "Unhandled state `%s'\n",
2388 GNUNET_TRANSPORT_ps2s(n->state));
2390 return GNUNET_SYSERR;
2397 * Check if the given @a address is the same that we are already
2398 * using for the respective neighbour. If so, update the bandwidth
2399 * assignment and possibly the session and return #GNUNET_OK.
2400 * If the new address is different from what the neighbour is
2401 * using right now, return #GNUNET_NO.
2403 * @param address address of the other peer,
2404 * @param session session to use or NULL if transport should initiate a session
2405 * @param bandwidth_in inbound quota to be used when connection is up,
2406 * 0 to disconnect from peer
2407 * @param bandwidth_out outbound quota to be used when connection is up,
2408 * 0 to disconnect from peer
2409 * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2410 * #GNUNET_NO if more extensive changes are required (address changed)
2413 try_run_fast_ats_update(const struct GNUNET_HELLO_Address *address,
2414 struct GNUNET_ATS_Session *session,
2415 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2416 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2418 struct NeighbourMapEntry *n;
2420 n = lookup_neighbour(&address->peer);
2422 (NULL == n->primary_address.address) ||
2423 (0 != GNUNET_HELLO_address_cmp(address,
2424 n->primary_address.address)))
2426 /* We are not really switching addresses, but merely adjusting
2427 session and/or bandwidth, can do fast ATS update! */
2428 if (session != n->primary_address.session)
2430 /* switch to a different session, but keeping same address; could
2431 happen if there is a 2nd inbound connection */
2432 n->primary_address.session = session;
2433 GNUNET_assert(GNUNET_YES ==
2434 GST_ats_is_known(n->primary_address.address,
2435 n->primary_address.session));
2437 if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2439 n->primary_address.bandwidth_in = bandwidth_in;
2441 set_incoming_quota(n,
2445 if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2447 n->primary_address.bandwidth_out = bandwidth_out;
2448 send_outbound_quota_to_clients(n);
2455 * We've been asked to switch addresses, and just now got the result
2456 * from the blacklist check to see if this is allowed.
2458 * @param cls the `struct BlacklistCheckSwitchContext` with
2459 * the information about the future address
2460 * @param peer the peer we may switch addresses on
2461 * @param address address associated with the request
2462 * @param session session associated with the request
2463 * @param result #GNUNET_OK if the connection is allowed,
2464 * #GNUNET_NO if not,
2465 * #GNUNET_SYSERR if operation was aborted
2468 switch_address_bl_check_cont(void *cls,
2469 const struct GNUNET_PeerIdentity *peer,
2470 const struct GNUNET_HELLO_Address *address,
2471 struct GNUNET_ATS_Session *session,
2474 struct BlacklistCheckSwitchContext *blc_ctx = cls;
2475 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2476 struct NeighbourMapEntry *n;
2478 if (GNUNET_SYSERR == result)
2481 papi = GST_plugins_find(address->transport_name);
2484 /* This can happen during shutdown. */
2488 if (GNUNET_NO == result)
2490 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2491 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2492 GST_plugins_a2s(address),
2495 GNUNET_STATISTICS_update(GST_stats,
2496 "# ATS suggestions ignored (blacklist denied)",
2499 if (NULL != session)
2500 papi->disconnect_session(papi->cls,
2503 GNUNET_HELLO_address_check_option(address,
2504 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2505 GST_ats_block_address(address,
2511 if (NULL == session)
2513 /* need to create a session, ATS only gave us an address */
2514 session = papi->get_session(papi->cls,
2516 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2517 "Obtained new session for peer `%s' and address '%s': %p\n",
2518 GNUNET_i2s(&address->peer),
2519 GST_plugins_a2s(address),
2521 if (NULL != session)
2522 GST_ats_new_session(address,
2525 if (NULL == session)
2527 /* session creation failed, bad!, fail! */
2528 GNUNET_STATISTICS_update(GST_stats,
2529 "# ATS suggestions ignored (failed to create session)",
2532 /* No session could be obtained, remove blacklist check and clean up */
2533 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2534 "Failed to obtain new session for peer `%s' and address '%s'\n",
2535 GNUNET_i2s(&address->peer),
2536 GST_plugins_a2s(address));
2537 GST_ats_block_address(address,
2542 /* We did this check already before going into blacklist, but
2543 it is theoretically possible that the situation changed in
2544 the meantime, hence we check again here */
2546 try_run_fast_ats_update(address,
2548 blc_ctx->bandwidth_in,
2549 blc_ctx->bandwidth_out))
2550 goto cleanup; /* was just a minor update, we're done */
2552 /* check if we also need to setup the neighbour entry */
2553 if (NULL == (n = lookup_neighbour(peer)))
2555 n = setup_neighbour(peer);
2558 /* not sure how this can happen... */
2562 n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2565 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2566 "Peer `%s' switches to address `%s'\n",
2567 GNUNET_i2s(&address->peer),
2568 GST_plugins_a2s(address));
2572 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2574 GST_ats_block_address(address,
2579 case GNUNET_TRANSPORT_PS_INIT_ATS:
2580 /* We requested an address and ATS suggests one:
2581 * set primary address and send SYN message*/
2582 set_primary_address(n,
2585 blc_ctx->bandwidth_in,
2586 blc_ctx->bandwidth_out);
2587 if (ACK_SEND_SYN_ACK == n->ack_state)
2589 /* Send pending SYN_ACK message */
2590 n->ack_state = ACK_SEND_ACK;
2591 send_syn_ack_message(&n->primary_address,
2592 n->connect_ack_timestamp);
2594 set_state_and_timeout(n,
2595 GNUNET_TRANSPORT_PS_SYN_SENT,
2596 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2597 send_syn(&n->primary_address);
2600 case GNUNET_TRANSPORT_PS_SYN_SENT:
2601 /* ATS suggested a new address while waiting for an SYN_ACK:
2602 * Switch and send new SYN */
2603 /* ATS suggests a different address, switch again */
2604 set_primary_address(n,
2607 blc_ctx->bandwidth_in,
2608 blc_ctx->bandwidth_out);
2609 if (ACK_SEND_SYN_ACK == n->ack_state)
2611 /* Send pending SYN_ACK message */
2612 n->ack_state = ACK_SEND_ACK;
2613 send_syn_ack_message(&n->primary_address,
2614 n->connect_ack_timestamp);
2616 set_state_and_timeout(n,
2617 GNUNET_TRANSPORT_PS_SYN_SENT,
2618 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2619 send_syn(&n->primary_address);
2622 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2623 /* We requested an address and ATS suggests one:
2624 * set primary address and send SYN_ACK message*/
2625 set_primary_address(n,
2628 blc_ctx->bandwidth_in,
2629 blc_ctx->bandwidth_out);
2630 /* Send an ACK message as a response to the SYN msg */
2631 set_state_and_timeout(n,
2632 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2633 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2634 send_syn_ack_message(&n->primary_address,
2635 n->connect_ack_timestamp);
2636 if ((ACK_SEND_SYN_ACK == n->ack_state) ||
2637 (ACK_UNDEFINED == n->ack_state))
2638 n->ack_state = ACK_SEND_ACK;
2641 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2642 /* ATS asks us to switch while we were trying to connect; switch to new
2643 address and check blacklist again */
2644 if ((ACK_SEND_SYN_ACK == n->ack_state))
2646 n->ack_state = ACK_SEND_ACK;
2647 send_syn_ack_message(&n->primary_address,
2648 n->connect_ack_timestamp);
2650 set_primary_address(n,
2653 blc_ctx->bandwidth_in,
2654 blc_ctx->bandwidth_out);
2655 set_state_and_timeout(n,
2656 GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2657 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2660 case GNUNET_TRANSPORT_PS_CONNECTED:
2661 GNUNET_assert(NULL != n->primary_address.address);
2662 GNUNET_assert(NULL != n->primary_address.session);
2663 GNUNET_break(n->primary_address.session != session);
2664 /* ATS asks us to switch a life connection; see if we can get
2665 a SYN_ACK on it before we actually do this! */
2666 set_alternative_address(n,
2669 blc_ctx->bandwidth_in,
2670 blc_ctx->bandwidth_out);
2671 set_state_and_timeout(n,
2672 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2673 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2674 GNUNET_STATISTICS_update(GST_stats,
2675 gettext_noop("# Attempts to switch addresses"),
2678 send_syn(&n->alternative_address);
2681 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2682 set_primary_address(n,
2685 blc_ctx->bandwidth_in,
2686 blc_ctx->bandwidth_out);
2687 if (ACK_SEND_SYN_ACK == n->ack_state)
2689 /* Send pending SYN_ACK message */
2690 n->ack_state = ACK_SEND_ACK;
2691 send_syn_ack_message(&n->primary_address,
2692 n->connect_ack_timestamp);
2694 set_state_and_timeout(n,
2695 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2696 GNUNET_TIME_relative_to_absolute(FAST_RECONNECT_TIMEOUT));
2697 send_syn(&n->primary_address);
2700 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2701 /* ATS asks us to switch while we were trying to reconnect; switch to new
2702 address and send SYN again */
2703 set_primary_address(n,
2706 blc_ctx->bandwidth_in,
2707 blc_ctx->bandwidth_out);
2708 set_state_and_timeout(n,
2709 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2710 GNUNET_TIME_relative_to_absolute(FAST_RECONNECT_TIMEOUT));
2711 send_syn(&n->primary_address);
2714 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2715 if ((0 == GNUNET_HELLO_address_cmp(n->primary_address.address,
2717 (n->primary_address.session == session))
2719 /* ATS switches back to still-active session */
2720 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2721 "ATS double-switched, cleaning up alternative address\n");
2722 free_address(&n->alternative_address);
2723 set_state_and_timeout(n,
2724 GNUNET_TRANSPORT_PS_CONNECTED,
2728 /* ATS asks us to switch a life connection, send */
2729 set_alternative_address(n,
2732 blc_ctx->bandwidth_in,
2733 blc_ctx->bandwidth_out);
2734 set_state_and_timeout(n,
2735 GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2736 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
2737 send_syn(&n->alternative_address);
2740 case GNUNET_TRANSPORT_PS_DISCONNECT:
2741 /* not going to switch addresses while disconnecting */
2742 GNUNET_STATISTICS_update(GST_stats,
2743 "# ATS suggestion ignored (disconnecting)",
2748 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2753 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
2754 "Unhandled state `%s'\n",
2755 GNUNET_TRANSPORT_ps2s(n->state));
2760 GNUNET_CONTAINER_DLL_remove(pending_bc_head,
2763 GNUNET_free(blc_ctx);
2768 * For the given peer, switch to this address.
2770 * Before accepting this addresses and actively using it, a blacklist check
2773 * If any check fails or the suggestion can somehow not be followed, we
2774 * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2775 * could not be satisfied and force ATS to do something else.
2777 * @param address address of the other peer,
2778 * @param session session to use or NULL if transport should initiate a session
2779 * @param bandwidth_in inbound quota to be used when connection is up,
2780 * 0 to disconnect from peer
2781 * @param bandwidth_out outbound quota to be used when connection is up,
2782 * 0 to disconnect from peer
2785 GST_neighbours_switch_to_address(const struct GNUNET_HELLO_Address *address,
2786 struct GNUNET_ATS_Session *session,
2787 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2788 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2790 struct GST_BlacklistCheck *blc;
2791 struct BlacklistCheckSwitchContext *blc_ctx;
2793 GNUNET_assert(NULL != address->transport_name);
2795 try_run_fast_ats_update(address,
2801 /* Check if plugin is available */
2802 if (NULL == (GST_plugins_find(address->transport_name)))
2804 /* we don't have the plugin for this address */
2806 GST_ats_block_address(address,
2810 if ((NULL == session) &&
2811 (GNUNET_HELLO_address_check_option(address,
2812 GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2814 /* This is a inbound address and we do not have a session to use! */
2816 GST_ats_block_address(address,
2821 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2822 "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2823 GST_plugins_a2s(address),
2824 GNUNET_i2s(&address->peer),
2825 (unsigned int)ntohl(bandwidth_in.value__),
2826 (unsigned int)ntohl(bandwidth_out.value__));
2828 /* Perform blacklist check */
2829 blc_ctx = GNUNET_new(struct BlacklistCheckSwitchContext);
2830 blc_ctx->bandwidth_in = bandwidth_in;
2831 blc_ctx->bandwidth_out = bandwidth_out;
2832 GNUNET_CONTAINER_DLL_insert(pending_bc_head,
2835 if (NULL != (blc = GST_blacklist_test_allowed(&address->peer,
2836 address->transport_name,
2837 &switch_address_bl_check_cont,
2848 * Function called to send network utilization data to ATS for
2849 * each active connection.
2852 * @param key peer we send utilization data for
2853 * @param value the `struct NeighbourMapEntry *` with data to send
2854 * @return #GNUNET_OK (continue to iterate)
2857 send_utilization_data(void *cls,
2858 const struct GNUNET_PeerIdentity *key,
2861 struct NeighbourMapEntry *n = value;
2864 struct GNUNET_TIME_Relative delta;
2867 if ((GNUNET_YES != test_connected(n)) ||
2868 (NULL == n->primary_address.address))
2870 delta = GNUNET_TIME_absolute_get_difference(n->last_util_transmission,
2871 GNUNET_TIME_absolute_get());
2873 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2874 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2876 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2877 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2879 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2880 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2884 GST_ats_update_utilization(n->primary_address.address,
2887 n->util_total_bytes_recv = 0;
2888 n->util_total_bytes_sent = 0;
2889 n->last_util_transmission = GNUNET_TIME_absolute_get();
2895 * Task transmitting utilization in a regular interval
2897 * @param cls the `struct NeighbourMapEntry` for which we are running
2900 utilization_transmission(void *cls)
2903 util_transmission_tk = NULL;
2904 GNUNET_CONTAINER_multipeermap_iterate(neighbours,
2905 &send_utilization_data,
2907 util_transmission_tk
2908 = GNUNET_SCHEDULER_add_delayed(UTIL_TRANSMISSION_INTERVAL,
2909 &utilization_transmission,
2915 * Track information about data we received from the
2916 * given address (used to notify ATS about our utilization
2917 * of allocated resources).
2919 * @param address the address we got data from
2920 * @param message the message we received (really only the size is used)
2923 GST_neighbours_notify_data_recv(const struct GNUNET_HELLO_Address *address,
2924 const struct GNUNET_MessageHeader *message)
2926 struct NeighbourMapEntry *n;
2928 n = lookup_neighbour(&address->peer);
2931 n->util_total_bytes_recv += ntohs(message->size);
2936 * Track information about data we transmitted using the given @a
2937 * address and @a session (used to notify ATS about our utilization of
2938 * allocated resources).
2940 * @param address the address we transmitted data to
2941 * @param session session we used to transmit data
2942 * @param message the message we sent (really only the size is used)
2945 GST_neighbours_notify_data_sent(const struct GNUNET_HELLO_Address *address,
2946 struct GNUNET_ATS_Session *session,
2949 struct NeighbourMapEntry *n;
2951 n = lookup_neighbour(&address->peer);
2954 if (n->primary_address.session != session)
2956 n->util_total_bytes_sent += size;
2961 * Master task run for every neighbour. Performs all of the time-related
2962 * activities (keep alive, send next message, disconnect if idle, finish
2963 * clean up after disconnect).
2965 * @param cls the 'struct NeighbourMapEntry' for which we are running
2968 master_task(void *cls)
2970 struct NeighbourMapEntry *n = cls;
2971 struct GNUNET_TIME_Relative delay;
2974 delay = GNUNET_TIME_absolute_get_remaining(n->timeout);
2975 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
2976 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2978 GNUNET_TRANSPORT_ps2s(n->state),
2979 GNUNET_STRINGS_relative_time_to_string(delay,
2983 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2984 /* invalid state for master task, clean up */
2989 case GNUNET_TRANSPORT_PS_INIT_ATS:
2990 if (0 == delay.rel_value_us)
2992 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
2993 "Connection to `%s' timed out waiting for ATS to provide address\n",
2994 GNUNET_i2s(&n->id));
3000 case GNUNET_TRANSPORT_PS_SYN_SENT:
3001 if (0 == delay.rel_value_us)
3003 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3004 "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
3005 GNUNET_i2s(&n->id));
3006 /* Remove address and request and additional one */
3007 unset_primary_address(n);
3008 set_state_and_timeout(n,
3009 GNUNET_TRANSPORT_PS_INIT_ATS,
3010 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
3015 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3016 if (0 == delay.rel_value_us)
3018 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3019 "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
3020 GNUNET_i2s(&n->id));
3026 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3027 if (0 == delay.rel_value_us)
3029 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3030 "Connection to `%s' timed out waiting for other peer to send ACK\n",
3031 GNUNET_i2s(&n->id));
3032 disconnect_neighbour(n);
3037 case GNUNET_TRANSPORT_PS_CONNECTED:
3038 if (0 == delay.rel_value_us)
3040 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3041 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3042 GNUNET_i2s(&n->id));
3043 disconnect_neighbour(n);
3046 try_transmission_to_peer(n);
3050 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3051 if (0 == delay.rel_value_us)
3053 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3054 "Connection to `%s' timed out, waiting for ATS replacement address\n",
3055 GNUNET_i2s(&n->id));
3056 disconnect_neighbour(n);
3061 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3062 if (0 == delay.rel_value_us)
3064 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3065 "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3066 GNUNET_i2s(&n->id));
3067 disconnect_neighbour(n);
3072 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3073 if (0 == delay.rel_value_us)
3075 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3076 "Switch failed, cleaning up alternative address\n");
3077 free_address(&n->alternative_address);
3078 set_state_and_timeout(n,
3079 GNUNET_TRANSPORT_PS_CONNECTED,
3080 GNUNET_TIME_relative_to_absolute(SETUP_CONNECTION_TIMEOUT));
3082 try_transmission_to_peer(n);
3086 case GNUNET_TRANSPORT_PS_DISCONNECT:
3087 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3088 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3089 GNUNET_i2s(&n->id));
3093 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3094 /* how did we get here!? */
3099 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
3100 "Unhandled state `%s'\n",
3101 GNUNET_TRANSPORT_ps2s(n->state));
3105 delay = GNUNET_TIME_absolute_get_remaining(n->timeout);
3106 if ((GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3107 (GNUNET_TRANSPORT_PS_CONNECTED == n->state))
3109 /* if we are *now* in one of the two states, we're sending
3110 keep alive messages, so we need to consider the keepalive
3111 delay, not just the connection timeout */
3112 delay = GNUNET_TIME_relative_min(GNUNET_TIME_absolute_get_remaining(n->keep_alive_time),
3115 if (NULL == n->task)
3116 n->task = GNUNET_SCHEDULER_add_delayed(delay,
3123 * Send a ACK message to the neighbour to confirm that we
3126 * @param n neighbour to send the ACK to
3129 send_session_ack_message(struct NeighbourMapEntry *n)
3131 struct GNUNET_MessageHeader msg;
3133 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3134 "Sending ACK message to peer `%s'\n",
3135 GNUNET_i2s(&n->id));
3137 msg.size = htons(sizeof(struct GNUNET_MessageHeader));
3138 msg.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3139 (void)send_with_session(n,
3141 sizeof(struct GNUNET_MessageHeader),
3143 GNUNET_TIME_UNIT_FOREVER_REL,
3150 * We received a 'SESSION_SYN_ACK' message from the other peer.
3151 * Consider switching to it.
3153 * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3154 * @param peer identity of the peer to switch the address for
3155 * @param address address of the other peer, NULL if other peer
3157 * @param session session to use (or NULL)
3158 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3161 GST_neighbours_handle_session_syn_ack(const struct GNUNET_MessageHeader *message,
3162 const struct GNUNET_HELLO_Address *address,
3163 struct GNUNET_ATS_Session *session)
3165 const struct TransportSynMessage *scm;
3166 struct GNUNET_TIME_Absolute ts;
3167 struct NeighbourMapEntry *n;
3170 if (ntohs(message->size) != sizeof(struct TransportSynMessage))
3173 return GNUNET_SYSERR;
3175 GNUNET_STATISTICS_update(GST_stats,
3177 ("# SYN_ACK messages received"),
3179 scm = (const struct TransportSynMessage *)message;
3180 GNUNET_break_op(ntohl(scm->reserved) == 0);
3181 if (NULL == (n = lookup_neighbour(&address->peer)))
3183 GNUNET_STATISTICS_update(GST_stats,
3185 ("# unexpected SYN_ACK messages (no peer)"),
3187 return GNUNET_SYSERR;
3189 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3190 "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3191 GNUNET_i2s(&address->peer),
3192 GNUNET_TRANSPORT_ps2s(n->state),
3193 print_ack_state(n->ack_state));
3194 ts = GNUNET_TIME_absolute_ntoh(scm->timestamp);
3197 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3200 return GNUNET_SYSERR;
3202 case GNUNET_TRANSPORT_PS_INIT_ATS:
3203 GNUNET_STATISTICS_update(GST_stats,
3204 gettext_noop("# unexpected SYN_ACK messages (not ready)"),
3209 case GNUNET_TRANSPORT_PS_SYN_SENT:
3210 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3212 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3213 "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3216 set_state_and_timeout(n,
3217 GNUNET_TRANSPORT_PS_CONNECTED,
3218 GNUNET_TIME_relative_to_absolute(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3219 set_primary_address(n,
3220 n->primary_address.address,
3221 n->primary_address.session,
3222 n->primary_address.bandwidth_in,
3223 n->primary_address.bandwidth_out);
3224 send_session_ack_message(n);
3227 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3228 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3229 GNUNET_STATISTICS_update(GST_stats,
3230 gettext_noop("# unexpected SYN_ACK messages (not ready)"),
3235 case GNUNET_TRANSPORT_PS_CONNECTED:
3236 /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3237 send_session_ack_message(n);
3240 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3241 /* we didn't expect any SYN_ACK, as we are waiting for ATS
3242 to give us a new address... */
3243 GNUNET_STATISTICS_update(GST_stats,
3244 gettext_noop("# unexpected SYN_ACK messages (waiting on ATS)"),
3249 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3250 /* Reconnecting with new address address worked; go back to connected! */
3251 set_state_and_timeout(n,
3252 GNUNET_TRANSPORT_PS_CONNECTED,
3253 GNUNET_TIME_relative_to_absolute(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3254 send_session_ack_message(n);
3257 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3258 /* new address worked; adopt it and go back to connected! */
3259 set_state_and_timeout(n,
3260 GNUNET_TRANSPORT_PS_CONNECTED,
3261 GNUNET_TIME_relative_to_absolute(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3262 GNUNET_break(GNUNET_NO == n->alternative_address.ats_active);
3264 /* Set primary addresses */
3265 set_primary_address(n,
3266 n->alternative_address.address,
3267 n->alternative_address.session,
3268 n->alternative_address.bandwidth_in,
3269 n->alternative_address.bandwidth_out);
3270 GNUNET_STATISTICS_update(GST_stats,
3271 gettext_noop("# Successful attempts to switch addresses"),
3275 GNUNET_HELLO_address_free(n->alternative_address.address);
3276 memset(&n->alternative_address,
3278 sizeof(n->alternative_address));
3279 send_session_ack_message(n);
3282 case GNUNET_TRANSPORT_PS_DISCONNECT:
3283 GNUNET_STATISTICS_update(GST_stats,
3285 ("# unexpected SYN_ACK messages (disconnecting)"),
3287 return GNUNET_SYSERR;
3289 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3294 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
3295 "Unhandled state `%s'\n",
3296 GNUNET_TRANSPORT_ps2s(n->state));
3298 return GNUNET_SYSERR;
3305 * A session was terminated. Take note; if needed, try to get
3306 * an alternative address from ATS.
3308 * @param peer identity of the peer where the session died
3309 * @param session session that is gone
3310 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3311 * this session was not in use
3314 GST_neighbours_session_terminated(const struct GNUNET_PeerIdentity *peer,
3315 struct GNUNET_ATS_Session *session)
3317 struct NeighbourMapEntry *n;
3319 if (NULL == (n = lookup_neighbour(peer)))
3320 return GNUNET_NO; /* can't affect us */
3321 if (session != n->primary_address.session)
3323 /* Free alternative address */
3324 if (session == n->alternative_address.session)
3326 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3327 set_state_and_timeout(n,
3328 GNUNET_TRANSPORT_PS_CONNECTED,
3330 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3331 "Session died, cleaning up alternative address\n");
3332 free_address(&n->alternative_address);
3334 return GNUNET_NO; /* doesn't affect us further */
3337 n->expect_latency_response = GNUNET_NO;
3338 /* The session for neighbour's primary address died */
3341 case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3346 case GNUNET_TRANSPORT_PS_INIT_ATS:
3351 case GNUNET_TRANSPORT_PS_SYN_SENT:
3352 /* The session used to send the SYN terminated:
3353 * this implies a connect error*/
3354 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3355 "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n",
3356 GST_plugins_a2s(n->primary_address.address),
3357 n->primary_address.session);
3359 /* Destroy the address since it cannot be used */
3360 unset_primary_address(n);
3361 set_state_and_timeout(n,
3362 GNUNET_TRANSPORT_PS_INIT_ATS,
3363 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
3366 case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3367 case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3368 /* error on inbound session; free neighbour entirely */
3372 case GNUNET_TRANSPORT_PS_CONNECTED:
3373 /* Our primary connection died, try a fast reconnect */
3374 unset_primary_address(n);
3375 set_state_and_timeout(n,
3376 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3377 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
3380 case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3381 /* we don't have an address, how can it go down? */
3385 case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3386 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3387 "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n",
3388 GST_plugins_a2s(n->primary_address.address),
3389 n->primary_address.session);
3390 /* Destroy the address since it cannot be used */
3391 unset_primary_address(n);
3392 set_state_and_timeout(n,
3393 GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3394 GNUNET_TIME_relative_to_absolute(ATS_RESPONSE_TIMEOUT));
3397 case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3398 /* primary went down while we were waiting for SYN_ACK on secondary;
3399 secondary as primary */
3401 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3402 "Connection `%s' %p to peer `%s' was terminated while switching, "
3403 "switching to alternative address `%s' %p\n",
3404 GST_plugins_a2s(n->primary_address.address),
3405 n->primary_address.session,
3407 GST_plugins_a2s(n->alternative_address.address),
3408 n->alternative_address.session);
3410 /* Destroy the inbound address since it cannot be used */
3411 free_address(&n->primary_address);
3412 n->primary_address = n->alternative_address;
3413 GNUNET_assert(GNUNET_YES ==
3414 GST_ats_is_known(n->primary_address.address,
3415 n->primary_address.session));
3416 memset(&n->alternative_address,
3418 sizeof(struct NeighbourAddress));
3419 set_state_and_timeout(n,
3420 GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3421 GNUNET_TIME_relative_to_absolute(FAST_RECONNECT_TIMEOUT));
3424 case GNUNET_TRANSPORT_PS_DISCONNECT:
3425 unset_primary_address(n);
3428 case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3429 /* neighbour was freed and plugins told to terminate session */
3433 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
3434 "Unhandled state `%s'\n",
3435 GNUNET_TRANSPORT_ps2s(n->state));
3439 if (NULL != n->task)
3440 GNUNET_SCHEDULER_cancel(n->task);
3441 n->task = GNUNET_SCHEDULER_add_now(&master_task, n);
3447 * We received a 'ACK' message from the other peer.
3448 * If we sent a 'SYN_ACK' last, this means we are now
3449 * connected. Otherwise, do nothing.
3451 * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3452 * @param address address of the other peer
3453 * @param session session to use (or NULL)
3454 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3457 GST_neighbours_handle_session_ack(const struct GNUNET_MessageHeader *message,
3458 const struct GNUNET_HELLO_Address *address,
3459 struct GNUNET_ATS_Session *session)
3461 struct NeighbourMapEntry *n;
3464 if (ntohs(message->size) != sizeof(struct GNUNET_MessageHeader))
3467 return GNUNET_SYSERR;
3469 GNUNET_STATISTICS_update(GST_stats,
3470 gettext_noop("# ACK messages received"),
3473 if (NULL == (n = lookup_neighbour(&address->peer)))
3476 return GNUNET_SYSERR;
3478 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3479 "Received ACK for peer `%s' in state %s/%s\n",
3480 GNUNET_i2s(&address->peer),
3481 GNUNET_TRANSPORT_ps2s(n->state),
3482 print_ack_state(n->ack_state));
3484 /* Check if we are in a plausible state for having sent
3485 a SYN_ACK. If not, return, otherwise break.
3487 The remote peers sends a ACK as a response for a SYN_ACK
3491 - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3492 now wait for the ACK to finally be connected
3493 - If we sent a SYN_ACK to this peer before */
3495 if (((GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3496 (ACK_SEND_ACK != n->ack_state)) ||
3497 (NULL == n->primary_address.address))
3499 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
3500 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3501 GNUNET_i2s(&address->peer),
3502 GNUNET_TRANSPORT_ps2s(n->state),
3503 print_ack_state(n->ack_state));
3505 GNUNET_STATISTICS_update(GST_stats,
3506 gettext_noop("# unexpected ACK messages"),
3511 if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3513 /* We tried to switch addresses while being connect. We explicitly wait
3514 * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3515 * so we do not want to set the address as in use! */
3518 set_state_and_timeout(n,
3519 GNUNET_TRANSPORT_PS_CONNECTED,
3520 GNUNET_TIME_relative_to_absolute(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3522 if (NULL == n->primary_address.address)
3525 * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3526 * really means we did try (and succeed) to send a SYN and are waiting for
3528 * That suggests that the primary_address used to be non-NULL, but maybe it
3529 * got reset to NULL without the state being changed appropriately?
3535 /* Reset backoff for primary address */
3536 GST_ats_block_reset(n->primary_address.address,
3537 n->primary_address.session);
3543 * Test if we're connected to the given peer.
3545 * @param target peer to test
3546 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3549 GST_neighbours_test_connected(const struct GNUNET_PeerIdentity *target)
3551 return test_connected(lookup_neighbour(target));
3556 * Task to asynchronously run #free_neighbour().
3558 * @param cls the `struct NeighbourMapEntry` to free
3561 delayed_disconnect(void *cls)
3563 struct NeighbourMapEntry *n = cls;
3565 n->delayed_disconnect_task = NULL;
3566 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3567 "Disconnecting by request from peer %s\n",
3568 GNUNET_i2s(&n->id));
3574 * We received a quota message from the given peer,
3575 * validate and process.
3577 * @param peer sender of the message
3578 * @param msg the quota message
3581 GST_neighbours_handle_quota_message(const struct GNUNET_PeerIdentity *peer,
3582 const struct GNUNET_MessageHeader *msg)
3584 struct NeighbourMapEntry *n;
3585 const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3586 struct GNUNET_BANDWIDTH_Value32NBO last;
3588 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3589 "Received QUOTA message from peer `%s'\n",
3591 if (ntohs(msg->size) != sizeof(struct GNUNET_ATS_SessionQuotaMessage))
3594 GNUNET_STATISTICS_update(GST_stats,
3595 gettext_noop("# quota messages ignored (malformed)"),
3600 GNUNET_STATISTICS_update(GST_stats,
3602 ("# QUOTA messages received"),
3604 sqm = (const struct GNUNET_ATS_SessionQuotaMessage *)msg;
3605 if (NULL == (n = lookup_neighbour(peer)))
3610 last = GNUNET_BANDWIDTH_value_max(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3611 GNUNET_BANDWIDTH_value_init(ntohl(sqm->quota)));
3612 if (last.value__ != n->neighbour_receive_quota.value__)
3614 n->neighbour_receive_quota = last;
3615 send_outbound_quota_to_clients(n);
3621 * We received a disconnect message from the given peer,
3622 * validate and process.
3624 * @param peer sender of the message
3625 * @param msg the disconnect message
3628 GST_neighbours_handle_disconnect_message(const struct GNUNET_PeerIdentity *peer,
3629 const struct GNUNET_MessageHeader *msg)
3631 struct NeighbourMapEntry *n;
3632 const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3634 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3635 "Received DISCONNECT message from peer `%s'\n",
3637 if (ntohs(msg->size) != sizeof(struct GNUNET_ATS_SessionDisconnectMessage))
3640 GNUNET_STATISTICS_update(GST_stats,
3642 ("# disconnect messages ignored (malformed)"),
3647 GNUNET_STATISTICS_update(GST_stats,
3649 ("# DISCONNECT messages received"),
3651 sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *)msg;
3652 if (NULL == (n = lookup_neighbour(peer)))
3657 if (GNUNET_TIME_absolute_ntoh(sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3659 GNUNET_STATISTICS_update(GST_stats,
3660 gettext_noop("# disconnect messages ignored (timestamp)"),
3665 if (0 != memcmp(peer,
3667 sizeof(struct GNUNET_PeerIdentity)))
3672 if (ntohl(sdm->purpose.size) !=
3673 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) +
3674 sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) +
3675 sizeof(struct GNUNET_TIME_AbsoluteNBO))
3677 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3678 "DISCONNECT message from peer `%s' has invalid size\n",
3684 GNUNET_CRYPTO_eddsa_verify(GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3689 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3690 "DISCONNECT message from peer `%s' cannot be verified \n",
3695 if (NULL == n->delayed_disconnect_task)
3697 n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now(&delayed_disconnect,
3704 * Closure for the #neighbours_iterate() function.
3706 struct IteratorContext {
3708 * Function to call on each connected neighbour.
3710 GST_NeighbourIterator cb;
3713 * Closure for @e cb.
3720 * Call the callback from the closure for each neighbour.
3722 * @param cls the `struct IteratorContext`
3723 * @param key the hash of the public key of the neighbour
3724 * @param value the `struct NeighbourMapEntry`
3725 * @return #GNUNET_OK (continue to iterate)
3728 neighbours_iterate(void *cls,
3729 const struct GNUNET_PeerIdentity *key,
3732 struct IteratorContext *ic = cls;
3733 struct NeighbourMapEntry *n = value;
3734 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3735 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3738 if (NULL != n->primary_address.address)
3740 bandwidth_in = n->primary_address.bandwidth_in;
3741 bandwidth_out = n->primary_address.bandwidth_out;
3745 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3746 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3750 n->primary_address.address,
3753 bandwidth_in, bandwidth_out);
3759 * Iterate over all connected neighbours.
3761 * @param cb function to call
3762 * @param cb_cls closure for @a cb
3765 GST_neighbours_iterate(GST_NeighbourIterator cb,
3768 struct IteratorContext ic;
3770 if (NULL == neighbours)
3771 return; /* can happen during shutdown */
3774 GNUNET_CONTAINER_multipeermap_iterate(neighbours,
3775 &neighbours_iterate,
3781 * If we have an active connection to the given target, it must be shutdown.
3783 * @param target peer to disconnect from
3786 GST_neighbours_force_disconnect(const struct GNUNET_PeerIdentity *target)
3788 struct NeighbourMapEntry *n;
3790 if (NULL == (n = lookup_neighbour(target)))
3791 return; /* not active */
3792 if (GNUNET_YES == test_connected(n))
3793 GNUNET_STATISTICS_update(GST_stats,
3794 gettext_noop("# disconnected from peer upon explicit request"),
3797 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
3798 "Forced disconnect from peer %s\n",
3799 GNUNET_i2s(target));
3800 disconnect_neighbour(n);
3805 * Obtain current address information for the given neighbour.
3808 * @return address currently used
3810 const struct GNUNET_HELLO_Address *
3811 GST_neighbour_get_current_address(const struct GNUNET_PeerIdentity *peer)
3813 struct NeighbourMapEntry *n;
3815 n = lookup_neighbour(peer);
3818 return n->primary_address.address;
3823 * Initialize the neighbours subsystem.
3825 * @param max_fds maximum number of fds to use
3828 GST_neighbours_start(unsigned int max_fds)
3831 neighbours = GNUNET_CONTAINER_multipeermap_create(NEIGHBOUR_TABLE_SIZE,
3833 util_transmission_tk = GNUNET_SCHEDULER_add_delayed(UTIL_TRANSMISSION_INTERVAL,
3834 &utilization_transmission,
3840 * Disconnect from the given neighbour.
3843 * @param key hash of neighbour's public key (not used)
3844 * @param value the `struct NeighbourMapEntry` of the neighbour
3845 * @return #GNUNET_OK (continue to iterate)
3848 disconnect_all_neighbours(void *cls,
3849 const struct GNUNET_PeerIdentity *key,
3852 struct NeighbourMapEntry *n = value;
3856 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
3857 "Disconnecting peer `%4s' during shutdown\n",
3858 GNUNET_i2s(&n->id));
3865 * Cleanup the neighbours subsystem.
3868 GST_neighbours_stop()
3870 if (NULL == neighbours)
3872 if (NULL != util_transmission_tk)
3874 GNUNET_SCHEDULER_cancel(util_transmission_tk);
3875 util_transmission_tk = NULL;
3877 GNUNET_CONTAINER_multipeermap_iterate(neighbours,
3878 &disconnect_all_neighbours,
3880 GNUNET_CONTAINER_multipeermap_destroy(neighbours);
3885 /* end of file gnunet-service-transport_neighbours.c */