2 This file is part of GNUnet.
3 (C) 2010-2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file transport/gnunet-service-transport_neighbours.c
23 * @brief neighbour management
24 * @author Christian Grothoff
27 #include "gnunet_ats_service.h"
28 #include "gnunet-service-transport_neighbours.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet-service-transport_validation.h"
31 #include "gnunet-service-transport_clients.h"
32 #include "gnunet-service-transport.h"
33 #include "gnunet_peerinfo_service.h"
34 #include "gnunet-service-transport_blacklist.h"
35 #include "gnunet_constants.h"
36 #include "transport.h"
41 * Size of the neighbour hash map.
43 #define NEIGHBOUR_TABLE_SIZE 256
46 * Time we give plugin to transmit DISCONNECT message before the
47 * neighbour entry self-destructs.
49 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
52 * How often must a peer violate bandwidth quotas before we start
53 * to simply drop its messages?
55 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
58 * How long are we willing to wait for a response from ATS before timing out?
60 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5000)
63 * How long are we willing to wait for an ACK from the other peer before
64 * giving up on our connect operation?
66 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
69 * How long are we willing to wait for a successful reconnect if
70 * an existing connection went down? Much shorter than the
71 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
72 * higher layers about the disconnect during this period.
74 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
77 * How long are we willing to wait for a response from the blacklist
78 * subsystem before timing out?
80 #define BLACKLIST_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
83 * Interval to send utilization data
85 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
87 GNUNET_NETWORK_STRUCT_BEGIN
90 * Message a peer sends to another to indicate that it intends to
91 * setup a connection/session for data exchange. A 'SESSION_CONNECT'
92 * should be answered with a 'SESSION_CONNECT_ACK' with the same body
93 * to confirm. A 'SESSION_CONNECT_ACK' should then be followed with
94 * a 'SESSION_ACK'. Once the 'SESSION_ACK' is received, both peers
95 * should be connected.
97 struct SessionConnectMessage
100 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT
101 * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK
103 struct GNUNET_MessageHeader header;
108 uint32_t reserved GNUNET_PACKED;
111 * Absolute time at the sender. Only the most recent connect
112 * message implies which session is preferred by the sender.
114 struct GNUNET_TIME_AbsoluteNBO timestamp;
120 * Message a peer sends to another when connected to indicate that a
121 * session is in use and the peer is still alive or to respond to a keep alive.
122 * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
123 * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
124 * When the keep alive response with type is received, transport service
125 * will call the respective plugin to update the session timeout
127 struct SessionKeepAliveMessage
130 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
131 * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
133 struct GNUNET_MessageHeader header;
136 * A nonce to identify the session the keep alive is used for
138 uint32_t nonce GNUNET_PACKED;
142 * Message we send to the other peer to notify him that we intentionally
143 * are disconnecting (to reduce timeouts). This is just a friendly
144 * notification, peers must not rely on always receiving disconnect
147 struct SessionDisconnectMessage
150 * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
152 struct GNUNET_MessageHeader header;
157 uint32_t reserved GNUNET_PACKED;
160 * Purpose of the signature. Extends over the timestamp.
161 * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
163 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
166 * Absolute time at the sender. Only the most recent connect
167 * message implies which session is preferred by the sender.
169 struct GNUNET_TIME_AbsoluteNBO timestamp;
172 * Public key of the sender.
174 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
177 * Signature of the peer that sends us the disconnect. Only
178 * valid if the timestamp is AFTER the timestamp from the
179 * corresponding 'CONNECT' message.
181 struct GNUNET_CRYPTO_EddsaSignature signature;
185 GNUNET_NETWORK_STRUCT_END
189 * For each neighbour we keep a list of messages
190 * that we still want to transmit to the neighbour.
196 * This is a doubly linked list.
198 struct MessageQueue *next;
201 * This is a doubly linked list.
203 struct MessageQueue *prev;
206 * Function to call once we're done.
208 GST_NeighbourSendContinuation cont;
211 * Closure for @e cont
216 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
217 * stuck together in memory. Allocated at the end of this struct.
219 const char *message_buf;
222 * Size of the message buf
224 size_t message_buf_size;
227 * At what time should we fail?
229 struct GNUNET_TIME_Absolute timeout;
237 * A possible address we could use to communicate with a neighbour.
239 struct NeighbourAddress
243 * Active session for this address.
245 struct Session *session;
248 * Network-level address information.
250 struct GNUNET_HELLO_Address *address;
253 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
254 * peer for this address. Use to check that the ACK is in response
255 * to our most recent 'CONNECT'.
257 struct GNUNET_TIME_Absolute connect_timestamp;
260 * Inbound bandwidth from ATS for this address.
262 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
265 * Outbound bandwidth from ATS for this address.
267 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
270 * Did we tell ATS that this is our 'active' address?
275 * The current nonce sent in the last keep alive messages
277 uint32_t keep_alive_nonce;
282 * Entry in neighbours.
284 struct NeighbourMapEntry
288 * Head of list of messages we would like to send to this peer;
289 * must contain at most one message per client.
291 struct MessageQueue *messages_head;
294 * Tail of list of messages we would like to send to this peer; must
295 * contain at most one message per client.
297 struct MessageQueue *messages_tail;
300 * Are we currently trying to send a message? If so, which one?
302 struct MessageQueue *is_active;
305 * Primary address we currently use to communicate with the neighbour.
307 struct NeighbourAddress primary_address;
310 * Alternative address currently under consideration for communicating
311 * with the neighbour.
313 struct NeighbourAddress alternative_address;
316 * Identity of this neighbour.
318 struct GNUNET_PeerIdentity id;
321 * Main task that drives this peer (timeouts, keepalives, etc.).
322 * Always runs the 'master_task'.
324 GNUNET_SCHEDULER_TaskIdentifier task;
327 * At what time should we sent the next keep-alive message?
329 struct GNUNET_TIME_Absolute keep_alive_time;
332 * At what time did we sent the last keep-alive message? Used
333 * to calculate round-trip time ("latency").
335 struct GNUNET_TIME_Absolute last_keep_alive_time;
338 * Timestamp we should include in our next CONNECT_ACK message.
339 * (only valid if 'send_connect_ack' is #GNUNET_YES). Used to build
340 * our CONNECT_ACK message.
342 struct GNUNET_TIME_Absolute connect_ack_timestamp;
345 * ATS address suggest handle
347 struct GNUNET_ATS_SuggestHandle *suggest_handle;
350 * Time where we should cut the connection (timeout) if we don't
351 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
352 * if we are in S_CONNECTED).
354 struct GNUNET_TIME_Absolute timeout;
357 * Latest calculated latency value
359 struct GNUNET_TIME_Relative latency;
362 * Tracker for inbound bandwidth.
364 struct GNUNET_BANDWIDTH_Tracker in_tracker;
367 * How often has the other peer (recently) violated the inbound
368 * traffic limit? Incremented by 10 per violation, decremented by 1
369 * per non-violation (for each time interval).
371 unsigned int quota_violation_count;
374 * The current state of the peer.
376 enum GNUNET_TRANSPORT_PeerState state;
379 * Did we sent an KEEP_ALIVE message and are we expecting a response?
381 int expect_latency_response;
384 * Flag to set if we still need to send a CONNECT_ACK message to the other peer
385 * (once we have an address to use and the peer has been allowed by our
386 * blacklist). Set to 1 if we need to send a CONNECT_ACK. Set to 2 if we
387 * did send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving
388 * a 'SESSION_ACK' (regardless of what our own state machine might say).
390 int send_connect_ack;
393 * Tracking utilization of outbound bandwidth
395 uint32_t util_payload_bytes_sent;
398 * Tracking utilization of inbound bandwidth
400 uint32_t util_payload_bytes_recv;
403 * Tracking utilization of outbound bandwidth
405 uint32_t util_total_bytes_sent;
408 * Tracking utilization of inbound bandwidth
410 uint32_t util_total_bytes_recv;
413 * Date of last utilization transmission
415 struct GNUNET_TIME_Absolute last_util_transmission;
420 * Context for blacklist checks and the #handle_test_blacklist_cont()
421 * function. Stores information about ongoing blacklist checks.
423 struct BlackListCheckContext
427 * We keep blacklist checks in a DLL.
429 struct BlackListCheckContext *next;
432 * We keep blacklist checks in a DLL.
434 struct BlackListCheckContext *prev;
437 * Address that is being checked.
439 struct NeighbourAddress na;
442 * Handle to the ongoing blacklist check.
444 struct GST_BlacklistCheck *bc;
449 * Hash map from peer identities to the respective 'struct NeighbourMapEntry'.
451 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
454 * We keep blacklist checks in a DLL so that we can find
455 * the 'sessions' in their 'struct NeighbourAddress' if
456 * a session goes down.
458 static struct BlackListCheckContext *bc_head;
461 * We keep blacklist checks in a DLL.
463 static struct BlackListCheckContext *bc_tail;
466 * Closure for #connect_notify_cb, #disconnect_notify_cb and #address_change_cb
468 static void *callback_cls;
471 * Function to call when we connected to a neighbour.
473 static NotifyConnect connect_notify_cb;
476 * Function to call when we disconnected from a neighbour.
478 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
481 * Function to call when we changed an active address of a neighbour.
483 static GNUNET_TRANSPORT_AddressChangeCallback address_change_cb;
486 * counter for connected neighbours
488 static unsigned int neighbours_connected;
491 * Number of bytes we have currently queued for transmission.
493 static unsigned long long bytes_in_send_queue;
496 * Task transmitting utilization data
498 static GNUNET_SCHEDULER_TaskIdentifier util_transmission_tk;
502 * Lookup a neighbour entry in the neighbours hash map.
504 * @param pid identity of the peer to look up
505 * @return the entry, NULL if there is no existing record
507 static struct NeighbourMapEntry *
508 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
510 if (NULL == neighbours)
512 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
517 * Convert state to human-readable string.
519 * @param state the state value
520 * @return corresponding string
523 print_state (enum GNUNET_TRANSPORT_PeerState state)
527 case S_NOT_CONNECTED:
528 return "S_NOT_CONNECTED";
531 case S_INIT_BLACKLIST:
532 return "S_INIT_BLACKLIST";
534 return "S_CONNECT_SENT";
535 case S_CONNECT_RECV_BLACKLIST_INBOUND:
536 return "S_CONNECT_RECV_BLACKLIST_INBOUND";
537 case S_CONNECT_RECV_ATS:
538 return "S_CONNECT_RECV_ATS";
539 case S_CONNECT_RECV_BLACKLIST:
540 return "S_CONNECT_RECV_BLACKLIST";
541 case S_CONNECT_RECV_ACK:
542 return "S_CONNECT_RECV_ACK";
544 return "S_CONNECTED";
545 case S_RECONNECT_ATS:
546 return "S_RECONNECT_ATS";
547 case S_RECONNECT_BLACKLIST:
548 return "S_RECONNECT_BLACKLIST";
549 case S_RECONNECT_SENT:
550 return "S_RECONNECT_SENT";
551 case S_CONNECTED_SWITCHING_BLACKLIST:
552 return "S_CONNECTED_SWITCHING_BLACKLIST";
553 case S_CONNECTED_SWITCHING_CONNECT_SENT:
554 return "S_CONNECTED_SWITCHING_CONNECT_SENT";
556 return "S_DISCONNECT";
557 case S_DISCONNECT_FINISHED:
558 return "S_DISCONNECT_FINISHED";
566 * Test if we're connected to the given peer.
568 * @param n neighbour entry of peer to test
569 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
572 test_connected (struct NeighbourMapEntry *n)
578 case S_NOT_CONNECTED:
580 case S_INIT_BLACKLIST:
582 case S_CONNECT_RECV_BLACKLIST_INBOUND:
583 case S_CONNECT_RECV_ATS:
584 case S_CONNECT_RECV_BLACKLIST:
585 case S_CONNECT_RECV_ACK:
588 case S_RECONNECT_ATS:
589 case S_RECONNECT_BLACKLIST:
590 case S_RECONNECT_SENT:
591 case S_CONNECTED_SWITCHING_BLACKLIST:
592 case S_CONNECTED_SWITCHING_CONNECT_SENT:
595 case S_DISCONNECT_FINISHED:
598 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
599 "Unhandled state `%s' \n",
600 print_state (n->state));
604 return GNUNET_SYSERR;
608 * Send information about a new outbound quota to our clients.
610 * @param target affected peer
611 * @param quota new quota
614 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
615 struct GNUNET_BANDWIDTH_Value32NBO quota)
617 struct QuotaSetMessage q_msg;
619 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
621 ntohl (quota.value__), GNUNET_i2s (target));
622 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
623 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
625 q_msg.peer = (*target);
626 GST_clients_broadcast (&q_msg.header, GNUNET_NO);
631 * We don't need a given neighbour address any more.
632 * Release its resources and give appropriate notifications
633 * to ATS and other subsystems.
635 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
638 free_address (struct NeighbourAddress *na)
640 if (GNUNET_YES == na->ats_active)
642 GST_validation_set_address_use (na->address, na->session, GNUNET_NO, __LINE__);
643 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
644 address_change_cb (callback_cls, &na->address->peer, NULL);
647 na->ats_active = GNUNET_NO;
648 na->keep_alive_nonce = 0;
649 if (NULL != na->address)
651 GNUNET_HELLO_address_free (na->address);
659 * Initialize the 'struct NeighbourAddress'.
661 * @param na neighbour address to initialize
662 * @param address address of the other peer, NULL if other peer
664 * @param session session to use (or NULL, in which case an
665 * address must be setup)
666 * @param bandwidth_in inbound quota to be used when connection is up
667 * @param bandwidth_out outbound quota to be used when connection is up
668 * @param is_active #GNUNET_YES to mark this as the active address with ATS
671 set_address (struct NeighbourAddress *na,
672 const struct GNUNET_HELLO_Address *address,
673 struct Session *session,
674 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
675 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
678 struct GNUNET_TRANSPORT_PluginFunctions *papi;
680 if (NULL == (papi = GST_plugins_find (address->transport_name)))
685 if (session == na->session)
687 na->bandwidth_in = bandwidth_in;
688 na->bandwidth_out = bandwidth_out;
689 if (is_active != na->ats_active)
691 na->ats_active = is_active;
692 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, is_active);
693 GST_validation_set_address_use (na->address, na->session, is_active, __LINE__);
695 address_change_cb (callback_cls, &address->peer, address);
697 if (GNUNET_YES == is_active)
699 /* FIXME: is this the right place to set quotas? */
700 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
701 send_outbound_quota (&address->peer, bandwidth_out);
707 session = papi->get_session (papi->cls, address);
710 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
711 "Failed to obtain new session for peer `%s' and address '%s'\n",
712 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
713 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
716 na->address = GNUNET_HELLO_address_copy (address);
717 na->bandwidth_in = bandwidth_in;
718 na->bandwidth_out = bandwidth_out;
719 na->session = session;
720 na->ats_active = is_active;
721 na->keep_alive_nonce = 0;
722 if (GNUNET_YES == is_active)
724 /* Telling ATS about new session */
725 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_YES);
726 GST_validation_set_address_use (na->address, na->session, GNUNET_YES, __LINE__);
727 address_change_cb (callback_cls, &address->peer, address);
728 /* FIXME: is this the right place to set quotas? */
729 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
730 send_outbound_quota (&address->peer, bandwidth_out);
736 * Free a neighbour map entry.
738 * @param n entry to free
739 * @param keep_sessions #GNUNET_NO to tell plugin to terminate sessions,
740 * #GNUNET_YES to keep all sessions
743 free_neighbour (struct NeighbourMapEntry *n,
746 struct MessageQueue *mq;
747 struct GNUNET_TRANSPORT_PluginFunctions *papi;
748 struct GNUNET_HELLO_Address *backup_primary;
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751 "Freeing neighbur state of peer `%s'\n",
752 GNUNET_i2s (&n->id));
753 n->is_active = NULL; /* always free'd by its own continuation! */
755 /* fail messages currently in the queue */
756 while (NULL != (mq = n->messages_head))
758 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
759 if (NULL != mq->cont)
760 mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
763 /* It is too late to send other peer disconnect notifications, but at
764 least internally we need to get clean... */
765 if (GNUNET_YES == test_connected (n))
767 GNUNET_STATISTICS_set (GST_stats,
768 gettext_noop ("# peers connected"),
769 --neighbours_connected,
771 disconnect_notify_cb (callback_cls, &n->id);
773 n->state = S_DISCONNECT_FINISHED;
775 if (NULL != n->primary_address.address)
777 backup_primary = GNUNET_HELLO_address_copy (n->primary_address.address);
780 backup_primary = NULL;
782 /* free addresses and mark as unused */
783 free_address (&n->primary_address);
784 free_address (&n->alternative_address);
786 /* FIXME: Note that if we are switching between two TCP sessions to
787 the same peer, we might want to selectively kill only one of
788 them! Killing all sessions like this seems to be very, very
791 /* cut transport-level connection */
792 if ((GNUNET_NO == keep_sessions) &&
793 (NULL != backup_primary) &&
794 (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
795 papi->disconnect_peer (papi->cls, &n->id);
797 GNUNET_free_non_null (backup_primary);
799 GNUNET_assert (GNUNET_YES ==
800 GNUNET_CONTAINER_multipeermap_remove (neighbours,
803 // FIXME-ATS-API: we might want to be more specific about
804 // which states we do this from in the future (ATS should
805 // have given us a 'suggest_address' handle, and if we have
806 // such a handle, we should cancel the operation here!
807 if (NULL != n->suggest_handle)
809 GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
810 n->suggest_handle = NULL;
813 if (GNUNET_SCHEDULER_NO_TASK != n->task)
815 GNUNET_SCHEDULER_cancel (n->task);
816 n->task = GNUNET_SCHEDULER_NO_TASK;
818 /* free rest of memory */
824 * Transmit a message using the current session of the given
827 * @param n entry for the recipient
828 * @param msgbuf buffer to transmit
829 * @param msgbuf_size number of bytes in @a msgbuf buffer
830 * @param priority transmission priority
831 * @param timeout transmission timeout
832 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
833 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
834 * @param cont continuation to call when finished (can be NULL)
835 * @param cont_cls closure for @a cont
836 * @return timeout (copy of @a timeout or a calculated one if
837 * @a use_keepalive_timeout is #GNUNET_YES.
839 static struct GNUNET_TIME_Relative
840 send_with_session (struct NeighbourMapEntry *n,
841 const char *msgbuf, size_t msgbuf_size,
843 struct GNUNET_TIME_Relative timeout,
844 unsigned int use_keepalive_timeout,
845 GNUNET_TRANSPORT_TransmitContinuation cont,
848 struct GNUNET_TRANSPORT_PluginFunctions *papi;
849 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
851 GNUNET_assert (n->primary_address.session != NULL);
852 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
853 (-1 == papi->send (papi->cls,
854 n->primary_address.session,
857 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
858 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
859 papi->query_keepalive_factor (papi->cls))),
860 cont, cont_cls)))) &&
862 cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
863 GST_neighbours_notify_data_sent (&n->id,
864 n->primary_address.address, n->primary_address.session, msgbuf_size);
865 GNUNET_break (NULL != papi);
871 * Master task run for every neighbour. Performs all of the time-related
872 * activities (keep alive, send next message, disconnect if idle, finish
873 * clean up after disconnect).
875 * @param cls the `struct NeighbourMapEntry` for which we are running
876 * @param tc scheduler context (unused)
879 master_task (void *cls,
880 const struct GNUNET_SCHEDULER_TaskContext *tc);
884 * Function called when the 'DISCONNECT' message has been sent by the
885 * plugin. Frees the neighbour --- if the entry still exists.
888 * @param target identity of the neighbour that was disconnected
889 * @param result #GNUNET_OK if the disconnect got out successfully
890 * @param payload bytes payload
891 * @param physical bytes physical
894 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
895 int result, size_t payload, size_t physical)
897 struct NeighbourMapEntry *n;
899 n = lookup_neighbour (target);
901 return; /* already gone */
902 if (S_DISCONNECT != n->state)
903 return; /* have created a fresh entry since */
904 if (GNUNET_SCHEDULER_NO_TASK != n->task)
905 GNUNET_SCHEDULER_cancel (n->task);
906 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
911 * Transmit a DISCONNECT message to the other peer.
913 * @param n neighbour to send DISCONNECT message.
916 send_disconnect (struct NeighbourMapEntry *n)
918 struct SessionDisconnectMessage disconnect_msg;
920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
921 "Sending DISCONNECT message to peer `%4s'\n",
922 GNUNET_i2s (&n->id));
923 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
924 disconnect_msg.header.type =
925 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
926 disconnect_msg.reserved = htonl (0);
927 disconnect_msg.purpose.size =
928 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
929 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
930 sizeof (struct GNUNET_TIME_AbsoluteNBO));
931 disconnect_msg.purpose.purpose =
932 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
933 disconnect_msg.timestamp =
934 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
935 disconnect_msg.public_key = GST_my_identity.public_key;
936 GNUNET_assert (GNUNET_OK ==
937 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
938 &disconnect_msg.purpose,
939 &disconnect_msg.signature));
941 (void) send_with_session (n,
942 (const char *) &disconnect_msg, sizeof (disconnect_msg),
943 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
944 GNUNET_NO, &send_disconnect_cont, NULL);
945 GNUNET_STATISTICS_update (GST_stats,
947 ("# DISCONNECT messages sent"), 1,
953 * Disconnect from the given neighbour, clean up the record.
955 * @param n neighbour to disconnect from
958 disconnect_neighbour (struct NeighbourMapEntry *n)
960 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961 "Disconnecting from peer %s in state %s\n",
963 print_state (n->state));
964 /* depending on state, notify neighbour and/or upper layers of this peer
968 case S_NOT_CONNECTED:
970 case S_INIT_BLACKLIST:
971 /* other peer is completely unaware of us, no need to send DISCONNECT */
972 n->state = S_DISCONNECT_FINISHED;
973 free_neighbour (n, GNUNET_NO);
977 n->state = S_DISCONNECT;
979 case S_CONNECT_RECV_BLACKLIST_INBOUND:
980 case S_CONNECT_RECV_ATS:
981 case S_CONNECT_RECV_BLACKLIST:
982 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
983 n->state = S_DISCONNECT_FINISHED;
984 free_neighbour (n, GNUNET_NO);
986 case S_CONNECT_RECV_ACK:
987 /* we DID ACK the other peer's request, must send DISCONNECT */
989 n->state = S_DISCONNECT;
992 case S_RECONNECT_BLACKLIST:
993 case S_RECONNECT_SENT:
994 case S_CONNECTED_SWITCHING_BLACKLIST:
995 case S_CONNECTED_SWITCHING_CONNECT_SENT:
996 /* we are currently connected, need to send disconnect and do
997 internal notifications and update statistics */
999 GNUNET_STATISTICS_set (GST_stats,
1000 gettext_noop ("# peers connected"),
1001 --neighbours_connected,
1003 disconnect_notify_cb (callback_cls, &n->id);
1004 n->state = S_DISCONNECT;
1006 case S_RECONNECT_ATS:
1007 /* ATS address request timeout, disconnect without sending disconnect message */
1008 GNUNET_STATISTICS_set (GST_stats,
1009 gettext_noop ("# peers connected"),
1010 --neighbours_connected,
1012 disconnect_notify_cb (callback_cls, &n->id);
1013 n->state = S_DISCONNECT;
1016 /* already disconnected, ignore */
1018 case S_DISCONNECT_FINISHED:
1019 /* already cleaned up, how did we get here!? */
1023 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1024 "Unhandled state `%s'\n",
1025 print_state (n->state));
1029 /* schedule timeout to clean up */
1030 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1031 GNUNET_SCHEDULER_cancel (n->task);
1032 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1038 * We're done with our transmission attempt, continue processing.
1040 * @param cls the `struct MessageQueue` of the message
1041 * @param receiver intended receiver
1042 * @param success whether it worked or not
1043 * @param size_payload bytes payload sent
1044 * @param physical bytes sent on wire
1047 transmit_send_continuation (void *cls,
1048 const struct GNUNET_PeerIdentity *receiver,
1049 int success, size_t size_payload, size_t physical)
1051 struct MessageQueue *mq = cls;
1052 struct NeighbourMapEntry *n;
1054 if (NULL == (n = lookup_neighbour (receiver)))
1057 return; /* disconnect or other error while transmitting, can happen */
1059 if (n->is_active == mq)
1061 /* this is still "our" neighbour, remove us from its queue
1062 and allow it to send the next message now */
1063 n->is_active = NULL;
1064 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1065 GNUNET_SCHEDULER_cancel (n->task);
1066 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1068 if (bytes_in_send_queue < mq->message_buf_size)
1070 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1071 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1072 bytes_in_send_queue, mq->message_buf_size,
1073 (GNUNET_OK == success) ? "OK" : "FAIL",
1074 size_payload, physical);
1079 GNUNET_break (size_payload == mq->message_buf_size);
1080 bytes_in_send_queue -= mq->message_buf_size;
1081 GNUNET_STATISTICS_set (GST_stats,
1083 ("# bytes in message queue for other peers"),
1084 bytes_in_send_queue, GNUNET_NO);
1085 if (GNUNET_OK == success)
1086 GNUNET_STATISTICS_update (GST_stats,
1088 ("# messages transmitted to other peers"),
1091 GNUNET_STATISTICS_update (GST_stats,
1093 ("# transmission failures for messages to other peers"),
1095 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096 "Sending message to `%s' of type %u was a %s\n",
1097 GNUNET_i2s (receiver),
1098 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1099 (success == GNUNET_OK) ? "success" : "FAILURE");
1100 if (NULL != mq->cont)
1101 mq->cont (mq->cont_cls, success, size_payload, physical);
1107 * Check the message list for the given neighbour and if we can
1108 * send a message, do so. This function should only be called
1109 * if the connection is at least generally ready for transmission.
1110 * While we will only send one message at a time, no bandwidth
1111 * quota management is performed here. If a message was given to
1112 * the plugin, the continuation will automatically re-schedule
1113 * the 'master' task once the next message might be transmitted.
1115 * @param n target peer for which to transmit
1118 try_transmission_to_peer (struct NeighbourMapEntry *n)
1120 struct MessageQueue *mq;
1121 struct GNUNET_TIME_Relative timeout;
1123 if (NULL == n->primary_address.address)
1125 /* no address, why are we here? */
1129 if ((0 == n->primary_address.address->address_length) &&
1130 (NULL == n->primary_address.session))
1132 /* no address, why are we here? */
1136 if (NULL != n->is_active)
1138 /* transmission already pending */
1142 /* timeout messages from the queue that are past their due date */
1143 while (NULL != (mq = n->messages_head))
1145 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1146 if (timeout.rel_value_us > 0)
1148 GNUNET_STATISTICS_update (GST_stats,
1150 ("# messages timed out while in transport queue"),
1152 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1154 transmit_send_continuation (mq, &n->id,
1156 mq->message_buf_size, 0); /* timeout */
1159 return; /* no more messages */
1160 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1162 (void) send_with_session (n,
1163 mq->message_buf, mq->message_buf_size,
1164 0 /* priority */, timeout, GNUNET_NO,
1165 &transmit_send_continuation, mq);
1170 * Send keepalive message to the neighbour. Must only be called
1171 * if we are on 'connected' state or while trying to switch addresses.
1172 * Will internally determine if a keepalive is truly needed (so can
1173 * always be called).
1175 * @param n neighbour that went idle and needs a keepalive
1178 send_keepalive (struct NeighbourMapEntry *n)
1180 struct SessionKeepAliveMessage m;
1181 struct GNUNET_TIME_Relative timeout;
1184 GNUNET_assert ((S_CONNECTED == n->state) ||
1185 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1186 (S_CONNECTED_SWITCHING_CONNECT_SENT));
1187 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1188 return; /* no keepalive needed at this time */
1190 nonce = 0; /* 0 indicates 'not set' */
1192 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1195 "Sending keep alive to peer `%s' with nonce %u\n",
1196 GNUNET_i2s (&n->id), nonce);
1198 m.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1199 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1200 m.nonce = htonl (nonce);
1202 timeout = send_with_session (n,
1203 (const void *) &m, sizeof (m),
1204 UINT32_MAX /* priority */,
1205 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1207 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1209 n->primary_address.keep_alive_nonce = nonce;
1210 n->expect_latency_response = GNUNET_YES;
1211 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1212 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1218 * Keep the connection to the given neighbour alive longer,
1219 * we received a KEEPALIVE (or equivalent); send a response.
1221 * @param neighbour neighbour to keep alive (by sending keep alive response)
1222 * @param m the keep alive message containing the nonce to respond to
1225 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1226 const struct GNUNET_MessageHeader *m)
1228 struct NeighbourMapEntry *n;
1229 const struct SessionKeepAliveMessage *msg_in;
1230 struct SessionKeepAliveMessage msg;
1232 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1235 msg_in = (struct SessionKeepAliveMessage *) m;
1236 if (NULL == (n = lookup_neighbour (neighbour)))
1238 GNUNET_STATISTICS_update (GST_stats,
1240 ("# KEEPALIVE messages discarded (peer unknown)"),
1244 if (NULL == n->primary_address.session)
1246 GNUNET_STATISTICS_update (GST_stats,
1248 ("# KEEPALIVE messages discarded (no session)"),
1253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1254 "Received keep alive request from peer `%s' with nonce %u\n",
1255 GNUNET_i2s (&n->id), ntohl (msg_in->nonce));
1257 /* send reply to allow neighbour to measure latency */
1258 msg.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1259 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1260 msg.nonce = msg_in->nonce;
1261 (void) send_with_session(n,
1262 (const void *) &msg, sizeof (struct SessionKeepAliveMessage),
1263 UINT32_MAX /* priority */,
1264 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1270 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1271 * latency to this peer. Pass the updated information (existing ats
1272 * plus calculated latency) to ATS.
1274 * @param neighbour neighbour to keep alive
1275 * @param m the message containing the keep alive response
1278 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1279 const struct GNUNET_MessageHeader *m)
1281 struct NeighbourMapEntry *n;
1282 const struct SessionKeepAliveMessage *msg;
1283 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1285 struct GNUNET_ATS_Information ats;
1287 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1290 msg = (const struct SessionKeepAliveMessage *) m;
1291 if (NULL == (n = lookup_neighbour (neighbour)))
1293 GNUNET_STATISTICS_update (GST_stats,
1295 ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1299 if ( (S_CONNECTED != n->state) ||
1300 (GNUNET_YES != n->expect_latency_response) )
1302 GNUNET_STATISTICS_update (GST_stats,
1304 ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1308 if (NULL == n->primary_address.address)
1310 GNUNET_STATISTICS_update (GST_stats,
1312 ("# KEEPALIVE_RESPONSE messages discarded (address changed)"),
1316 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1318 GNUNET_STATISTICS_update (GST_stats,
1320 ("# KEEPALIVE_RESPONSE messages discarded (wrong nonce)"),
1326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1327 "Received keep alive response from peer `%s' for session %p\n",
1328 GNUNET_i2s (&n->id), n->primary_address.session);
1332 /* Update session timeout here */
1333 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1336 "Updating session for peer `%s' for session %p\n",
1337 GNUNET_i2s (&n->id), n->primary_address.session);
1338 papi->update_session_timeout (papi->cls, &n->id, n->primary_address.session);
1345 n->primary_address.keep_alive_nonce = 0;
1346 n->expect_latency_response = GNUNET_NO;
1347 n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1348 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1350 "Latency for peer `%s' is %s\n",
1351 GNUNET_i2s (&n->id),
1352 GNUNET_STRINGS_relative_time_to_string (n->latency,
1354 /* append latency */
1355 ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1356 if (n->latency.rel_value_us > UINT32_MAX)
1357 latency = UINT32_MAX;
1359 latency = n->latency.rel_value_us;
1360 ats.value = htonl (latency);
1361 GST_ats_update_metrics (&n->id, n->primary_address.address,
1362 n->primary_address.session, &ats, 1);
1367 * We have received a message from the given sender. How long should
1368 * we delay before receiving more? (Also used to keep the peer marked
1371 * @param sender sender of the message
1372 * @param size size of the message
1373 * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1374 * #GNUNET_NO if the neighbour is not connected or violates the quota,
1375 * #GNUNET_SYSERR if the connection is not fully up yet
1376 * @return how long to wait before reading more from this sender
1378 struct GNUNET_TIME_Relative
1379 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1380 *sender, ssize_t size, int *do_forward)
1382 struct NeighbourMapEntry *n;
1383 struct GNUNET_TIME_Relative ret;
1385 if (NULL == neighbours)
1387 *do_forward = GNUNET_NO;
1388 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1390 if (NULL == (n = lookup_neighbour (sender)))
1392 GST_neighbours_try_connect (sender);
1393 if (NULL == (n = lookup_neighbour (sender)))
1395 GNUNET_STATISTICS_update (GST_stats,
1397 ("# messages discarded due to lack of neighbour record"),
1399 *do_forward = GNUNET_NO;
1400 return GNUNET_TIME_UNIT_ZERO;
1403 if (! test_connected (n))
1405 *do_forward = GNUNET_SYSERR;
1406 return GNUNET_TIME_UNIT_ZERO;
1408 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1410 n->quota_violation_count++;
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1413 n->in_tracker.available_bytes_per_s__,
1414 n->quota_violation_count);
1415 /* Discount 32k per violation */
1416 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1420 if (n->quota_violation_count > 0)
1422 /* try to add 32k back */
1423 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1424 n->quota_violation_count--;
1427 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1429 GNUNET_STATISTICS_update (GST_stats,
1431 ("# bandwidth quota violations by other peers"),
1433 *do_forward = GNUNET_NO;
1434 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1436 *do_forward = GNUNET_YES;
1437 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1438 if (ret.rel_value_us > 0)
1440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1441 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1442 (unsigned long long) n->in_tracker.
1443 consumption_since_last_update__,
1444 (unsigned int) n->in_tracker.available_bytes_per_s__,
1445 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1446 GNUNET_STATISTICS_update (GST_stats,
1447 gettext_noop ("# ms throttling suggested"),
1448 (int64_t) ret.rel_value_us / 1000LL,
1456 * Transmit a message to the given target using the active connection.
1458 * @param target destination
1459 * @param msg message to send
1460 * @param msg_size number of bytes in msg
1461 * @param timeout when to fail with timeout
1462 * @param cont function to call when done
1463 * @param cont_cls closure for 'cont'
1466 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1467 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1468 GST_NeighbourSendContinuation cont, void *cont_cls)
1470 struct NeighbourMapEntry *n;
1471 struct MessageQueue *mq;
1473 /* All ove these cases should never happen; they are all API violations.
1474 But we check anyway, just to be sure. */
1475 if (NULL == (n = lookup_neighbour (target)))
1479 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1482 if (GNUNET_YES != test_connected (n))
1486 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1489 bytes_in_send_queue += msg_size;
1490 GNUNET_STATISTICS_set (GST_stats,
1492 ("# bytes in message queue for other peers"),
1493 bytes_in_send_queue, GNUNET_NO);
1494 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1496 mq->cont_cls = cont_cls;
1497 memcpy (&mq[1], msg, msg_size);
1498 mq->message_buf = (const char *) &mq[1];
1499 mq->message_buf_size = msg_size;
1500 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1501 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1502 if ( (NULL != n->is_active) ||
1503 ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1505 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1506 GNUNET_SCHEDULER_cancel (n->task);
1507 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1512 * Send a SESSION_CONNECT message via the given address.
1514 * @param na address to use
1517 send_session_connect (struct NeighbourAddress *na)
1519 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1520 struct SessionConnectMessage connect_msg;
1522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1523 "Sending SESSION_CONNECT message to peer %s\n",
1524 GNUNET_i2s (&na->address->peer));
1525 if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1530 if (NULL == na->session)
1531 na->session = papi->get_session (papi->cls, na->address);
1532 if (NULL == na->session)
1537 GNUNET_STATISTICS_update (GST_stats,
1539 ("# SESSION_CONNECT messages sent"),
1541 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1542 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1543 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1544 connect_msg.reserved = htonl (0);
1545 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1547 papi->send (papi->cls,
1549 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1551 GNUNET_TIME_UNIT_FOREVER_REL,
1554 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1555 _("Failed to transmit CONNECT message via plugin to %s\n"),
1556 GST_plugins_a2s (na->address));
1558 GST_neighbours_notify_data_sent (&na->address->peer,
1561 sizeof (struct SessionConnectMessage));
1567 * Send a SESSION_CONNECT_ACK message via the given address.
1569 * @param address address to use
1570 * @param session session to use
1571 * @param timestamp timestamp to use for the ACK message
1574 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1575 struct Session *session,
1576 struct GNUNET_TIME_Absolute timestamp)
1578 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1579 struct SessionConnectMessage connect_msg;
1581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1582 "Sending CONNECT_ACK to peer `%s'\n",
1583 GNUNET_i2s (&address->peer));
1584 if (NULL == (papi = GST_plugins_find (address->transport_name)))
1589 if (NULL == session)
1590 session = papi->get_session (papi->cls, address);
1591 if (NULL == session)
1596 GNUNET_STATISTICS_update (GST_stats,
1598 ("# CONNECT_ACK messages sent"),
1600 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1601 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1602 connect_msg.reserved = htonl (0);
1603 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1604 (void) papi->send (papi->cls,
1606 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1608 GNUNET_TIME_UNIT_FOREVER_REL,
1615 * Create a fresh entry in the neighbour map for the given peer
1617 * @param peer peer to create an entry for
1618 * @return new neighbour map entry
1620 static struct NeighbourMapEntry *
1621 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1623 struct NeighbourMapEntry *n;
1625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1626 "Creating new neighbour entry for `%s'\n",
1628 n = GNUNET_new (struct NeighbourMapEntry);
1630 n->state = S_NOT_CONNECTED;
1631 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1632 n->last_util_transmission = GNUNET_TIME_absolute_get();
1633 n->util_payload_bytes_recv = 0;
1634 n->util_payload_bytes_sent = 0;
1635 n->util_total_bytes_recv = 0;
1636 n->util_total_bytes_sent = 0;
1637 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1638 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1639 MAX_BANDWIDTH_CARRY_S);
1640 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1641 GNUNET_assert (GNUNET_OK ==
1642 GNUNET_CONTAINER_multipeermap_put (neighbours,
1644 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1650 * Check if the two given addresses are the same.
1651 * Actually only checks if the sessions are non-NULL
1652 * (which they should be) and then if they are identical;
1653 * the actual addresses don't matter if the session
1654 * pointers match anyway, and we must have session pointers
1657 * @param a1 first address to compare
1658 * @param a2 other address to compare
1659 * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
1662 address_matches (const struct NeighbourAddress *a1,
1663 const struct NeighbourAddress *a2)
1665 if ( (NULL == a1->session) ||
1666 (NULL == a2->session) )
1671 return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1676 * Try to create a connection to the given target (eventually).
1678 * @param target peer to try to connect to
1681 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1683 struct NeighbourMapEntry *n;
1685 if (NULL == neighbours)
1687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1688 "Asked to connect to peer `%s' during shutdown\n",
1689 GNUNET_i2s (target));
1690 return; /* during shutdown, do nothing */
1692 n = lookup_neighbour (target);
1693 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1694 "Asked to connect to peer `%s' (state: %s)\n",
1695 GNUNET_i2s (target),
1696 (NULL != n) ? print_state(n->state) : "NEW PEER");
1701 case S_NOT_CONNECTED:
1702 /* this should not be possible */
1704 free_neighbour (n, GNUNET_NO);
1707 case S_INIT_BLACKLIST:
1708 case S_CONNECT_SENT:
1709 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1710 case S_CONNECT_RECV_ATS:
1711 case S_CONNECT_RECV_BLACKLIST:
1712 case S_CONNECT_RECV_ACK:
1713 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1714 "Ignoring request to try to connect to `%s', already trying!\n",
1715 GNUNET_i2s (target));
1716 return; /* already trying */
1718 case S_RECONNECT_ATS:
1719 case S_RECONNECT_BLACKLIST:
1720 case S_RECONNECT_SENT:
1721 case S_CONNECTED_SWITCHING_BLACKLIST:
1722 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1723 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1724 "Ignoring request to try to connect, already connected to `%s'!\n",
1725 GNUNET_i2s (target));
1726 return; /* already connected */
1728 /* get rid of remains, ready to re-try immediately */
1729 free_neighbour (n, GNUNET_NO);
1731 case S_DISCONNECT_FINISHED:
1732 /* should not be possible */
1735 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1736 "Unhandled state `%s'\n",
1737 print_state (n->state));
1739 free_neighbour (n, GNUNET_NO);
1743 n = setup_neighbour (target);
1744 n->state = S_INIT_ATS;
1745 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1747 GNUNET_ATS_reset_backoff (GST_ats, target);
1748 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1753 * Function called with the result of a blacklist check.
1755 * @param cls closure with the `struct BlackListCheckContext`
1756 * @param peer peer this check affects
1757 * @param result #GNUNET_OK if the address is allowed
1760 handle_test_blacklist_cont (void *cls,
1761 const struct GNUNET_PeerIdentity *peer,
1764 struct BlackListCheckContext *bcc = cls;
1765 struct NeighbourMapEntry *n;
1768 GNUNET_CONTAINER_DLL_remove (bc_head,
1771 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1772 "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1774 (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1775 if (GNUNET_OK == result)
1777 GST_ats_add_address (bcc->na.address, bcc->na.session, NULL, 0);
1781 /* Blacklist disagreed on connecting to a peer with this address
1782 * Destroy address because we are not allowed to use it
1784 if (NULL != bcc->na.session)
1785 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, bcc->na.session);
1786 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, NULL);
1788 if (NULL == (n = lookup_neighbour (peer)))
1790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1791 "No neighbor entry for peer `%s', ignoring blacklist result\n",
1793 goto cleanup; /* nobody left to care about new address */
1795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1796 "Received blacklist result for peer `%s' in state %s/%d\n",
1798 print_state (n->state),
1799 n->send_connect_ack);
1802 case S_NOT_CONNECTED:
1803 /* this should not be possible */
1805 free_neighbour (n, GNUNET_NO);
1808 /* waiting on ATS suggestion; still, pass address to ATS as a
1811 case S_INIT_BLACKLIST:
1812 /* check if the address the blacklist was fine with matches
1813 ATS suggestion, if so, we can move on! */
1814 if ( (GNUNET_OK == result) &&
1815 (1 == n->send_connect_ack) )
1817 n->send_connect_ack = 2;
1818 send_session_connect_ack_message (bcc->na.address,
1820 n->connect_ack_timestamp);
1822 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1825 "Blacklist result for peer %s is for non-primary address, ignored\n",
1827 break; /* result for an address we currently don't care about */
1829 if (GNUNET_OK == result)
1831 n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1832 n->state = S_CONNECT_SENT;
1833 send_session_connect (&n->primary_address);
1837 free_address (&n->primary_address);
1838 n->state = S_INIT_ATS;
1839 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1842 case S_CONNECT_SENT:
1843 /* waiting on CONNECT_ACK, send ACK if one is pending */
1844 if ( (GNUNET_OK == result) &&
1845 (1 == n->send_connect_ack) )
1847 n->send_connect_ack = 2;
1848 send_session_connect_ack_message (n->primary_address.address,
1849 n->primary_address.session,
1850 n->connect_ack_timestamp);
1853 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1854 n->state = S_CONNECT_RECV_ATS;
1855 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1856 GNUNET_ATS_reset_backoff (GST_ats, peer);
1857 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1858 "Suggesting address for peer %s to ATS\n",
1860 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
1862 case S_CONNECT_RECV_ATS:
1863 /* waiting on ATS suggestion, don't care about blacklist */
1865 case S_CONNECT_RECV_BLACKLIST:
1866 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1869 "Blacklist result ignored, as it is not for our primary address\n");
1870 break; /* result for an address we currently don't care about */
1872 if (GNUNET_OK == result)
1874 n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1875 n->state = S_CONNECT_RECV_ACK;
1876 send_session_connect_ack_message (bcc->na.address,
1878 n->connect_ack_timestamp);
1879 if (1 == n->send_connect_ack)
1880 n->send_connect_ack = 2;
1884 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
1886 plugin = GST_plugins_find (bcc->na.address->transport_name);
1887 if ( (NULL != plugin) &&
1888 (NULL != bcc->na.session) )
1890 plugin->disconnect_session (plugin->cls,
1894 GNUNET_break (NULL != plugin);
1895 free_address (&n->primary_address);
1896 n->state = S_INIT_ATS;
1897 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1898 GNUNET_ATS_reset_backoff (GST_ats, peer);
1901 case S_CONNECT_RECV_ACK:
1902 /* waiting on SESSION_ACK, send ACK if one is pending */
1903 if ( (GNUNET_OK == result) &&
1904 (1 == n->send_connect_ack) )
1906 n->send_connect_ack = 2;
1907 send_session_connect_ack_message (n->primary_address.address,
1908 n->primary_address.session,
1909 n->connect_ack_timestamp);
1913 /* already connected, don't care about blacklist */
1915 case S_RECONNECT_ATS:
1916 /* still waiting on ATS suggestion, don't care about blacklist */
1918 case S_RECONNECT_BLACKLIST:
1919 if ( (GNUNET_OK == result) &&
1920 (1 == n->send_connect_ack) )
1922 n->send_connect_ack = 2;
1923 send_session_connect_ack_message (bcc->na.address,
1925 n->connect_ack_timestamp);
1927 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1930 "Blacklist result ignored, as it is not for our primary address\n");
1931 break; /* result for an address we currently don't care about */
1933 if (GNUNET_OK == result)
1935 n->state = S_RECONNECT_SENT;
1936 send_session_connect (&n->primary_address);
1937 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
1941 n->state = S_RECONNECT_ATS;
1942 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1945 case S_RECONNECT_SENT:
1946 /* waiting on CONNECT_ACK, don't care about blacklist */
1947 if ( (GNUNET_OK == result) &&
1948 (1 == n->send_connect_ack) )
1950 n->send_connect_ack = 2;
1951 send_session_connect_ack_message (n->primary_address.address,
1952 n->primary_address.session,
1953 n->connect_ack_timestamp);
1956 case S_CONNECTED_SWITCHING_BLACKLIST:
1957 if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
1959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1960 "Blacklist result ignored, as it is not for our primary address\n");
1961 break; /* result for an address we currently don't care about */
1963 if (GNUNET_OK == result)
1965 send_session_connect (&n->alternative_address);
1966 n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
1970 n->state = S_CONNECTED;
1971 free_address (&n->alternative_address);
1974 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1975 /* waiting on CONNECT_ACK, don't care about blacklist */
1976 if ( (GNUNET_OK == result) &&
1977 (1 == n->send_connect_ack) )
1979 n->send_connect_ack = 2;
1980 send_session_connect_ack_message (n->primary_address.address,
1981 n->primary_address.session,
1982 n->connect_ack_timestamp);
1986 /* Nothing to do here, ATS will already do what can be done */
1988 case S_DISCONNECT_FINISHED:
1989 /* should not be possible */
1993 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1994 "Unhandled state `%s'\n",
1995 print_state (n->state));
1997 free_neighbour (n, GNUNET_NO);
2001 GNUNET_HELLO_address_free (bcc->na.address);
2007 * We want to know if connecting to a particular peer via
2008 * a particular address is allowed. Check it!
2010 * @param peer identity of the peer to switch the address for
2011 * @param ts time at which the check was initiated
2012 * @param address address of the other peer, NULL if other peer
2014 * @param session session to use (or NULL)
2017 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2018 struct GNUNET_TIME_Absolute ts,
2019 const struct GNUNET_HELLO_Address *address,
2020 struct Session *session)
2022 struct BlackListCheckContext *bcc;
2023 struct GST_BlacklistCheck *bc;
2025 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2026 "Checking peer `%s' against blacklist\n",
2028 bcc = GNUNET_new (struct BlackListCheckContext);
2029 bcc->na.address = GNUNET_HELLO_address_copy (address);
2030 bcc->na.session = session;
2031 bcc->na.connect_timestamp = ts;
2032 GNUNET_CONTAINER_DLL_insert (bc_head,
2035 if (NULL != (bc = GST_blacklist_test_allowed (peer,
2036 address->transport_name,
2037 &handle_test_blacklist_cont, bcc)))
2039 /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2040 we must only store 'bc' if 'bc' is non-NULL... */
2045 * We received a 'SESSION_CONNECT' message from the other peer.
2046 * Consider switching to it.
2048 * @param message possibly a 'struct SessionConnectMessage' (check format)
2049 * @param peer identity of the peer to switch the address for
2050 * @param address address of the other peer, NULL if other peer
2052 * @param session session to use (or NULL)
2053 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2056 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2057 const struct GNUNET_PeerIdentity *peer,
2058 const struct GNUNET_HELLO_Address *address,
2059 struct Session *session)
2061 const struct SessionConnectMessage *scm;
2062 struct NeighbourMapEntry *n;
2063 struct GNUNET_TIME_Absolute ts;
2065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2066 "Received CONNECT message from peer `%s'\n",
2068 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2070 GNUNET_break_op (0);
2071 return GNUNET_SYSERR;
2073 GNUNET_STATISTICS_update (GST_stats,
2075 ("# CONNECT messages received"),
2077 if (NULL == neighbours)
2079 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2080 _("CONNECT request from peer `%s' ignored due impending shutdown\n"),
2082 return GNUNET_OK; /* we're shutting down */
2084 scm = (const struct SessionConnectMessage *) message;
2085 GNUNET_break_op (0 == ntohl (scm->reserved));
2086 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2087 n = lookup_neighbour (peer);
2089 n = setup_neighbour (peer);
2090 n->send_connect_ack = 1;
2091 n->connect_ack_timestamp = ts;
2093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2094 "Received SESSION_CONNECT for peer `%s' in state %s/%d\n",
2096 print_state (n->state),
2097 n->send_connect_ack);
2100 case S_NOT_CONNECTED:
2101 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2102 /* Do a blacklist check for the new address */
2103 check_blacklist (peer, ts, address, session);
2106 /* CONNECT message takes priority over us asking ATS for address */
2107 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2109 case S_INIT_BLACKLIST:
2110 case S_CONNECT_SENT:
2111 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2112 case S_CONNECT_RECV_ATS:
2113 case S_CONNECT_RECV_BLACKLIST:
2114 case S_CONNECT_RECV_ACK:
2115 /* It can never hurt to have an alternative address in the above cases,
2116 see if it is allowed */
2117 check_blacklist (peer, ts, address, session);
2120 /* we are already connected and can thus send the ACK immediately;
2121 still, it can never hurt to have an alternative address, so also
2122 tell ATS about it */
2123 GNUNET_assert (NULL != n->primary_address.address);
2124 GNUNET_assert (NULL != n->primary_address.session);
2125 n->send_connect_ack = 0;
2126 send_session_connect_ack_message (n->primary_address.address,
2127 n->primary_address.session, ts);
2128 check_blacklist (peer, ts, address, session);
2130 case S_RECONNECT_ATS:
2131 case S_RECONNECT_BLACKLIST:
2132 case S_RECONNECT_SENT:
2133 /* It can never hurt to have an alternative address in the above cases,
2134 see if it is allowed */
2135 check_blacklist (peer, ts, address, session);
2137 case S_CONNECTED_SWITCHING_BLACKLIST:
2138 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2139 /* we are already connected and can thus send the ACK immediately;
2140 still, it can never hurt to have an alternative address, so also
2141 tell ATS about it */
2142 GNUNET_assert (NULL != n->primary_address.address);
2143 GNUNET_assert (NULL != n->primary_address.session);
2144 n->send_connect_ack = 0;
2145 send_session_connect_ack_message (n->primary_address.address,
2146 n->primary_address.session, ts);
2147 check_blacklist (peer, ts, address, session);
2150 /* get rid of remains without terminating sessions, ready to re-try */
2151 free_neighbour (n, GNUNET_YES);
2152 n = setup_neighbour (peer);
2153 n->state = S_CONNECT_RECV_ATS;
2154 GNUNET_ATS_reset_backoff (GST_ats, peer);
2155 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2157 case S_DISCONNECT_FINISHED:
2158 /* should not be possible */
2162 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2163 "Unhandled state `%s'\n",
2164 print_state (n->state));
2166 return GNUNET_SYSERR;
2173 * For an existing neighbour record, set the active connection to
2174 * use the given address.
2176 * @param peer identity of the peer to switch the address for
2177 * @param address address of the other peer, NULL if other peer
2179 * @param session session to use (or NULL)
2180 * @param ats performance data
2181 * @param ats_count number of entries in ats
2182 * @param bandwidth_in inbound quota to be used when connection is up,
2183 * 0 to disconnect from peer
2184 * @param bandwidth_out outbound quota to be used when connection is up,
2185 * 0 to disconnect from peer
2188 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2189 const struct GNUNET_HELLO_Address *address,
2190 struct Session *session,
2191 const struct GNUNET_ATS_Information *ats,
2193 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2194 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2196 struct NeighbourMapEntry *n;
2197 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2200 "ATS has decided on an address for peer %s\n",
2202 GNUNET_assert (NULL != address->transport_name);
2203 if (NULL == (n = lookup_neighbour (peer)))
2205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2206 "Peer %s is unknown, suggestion ignored\n",
2211 /* Obtain an session for this address from plugin */
2212 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2214 /* we don't have the plugin for this address */
2215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2216 "Plugin `%s' is unknown, suggestion for peer %s ignored\n",
2217 address->transport_name,
2219 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2222 if ((NULL == session) && (0 == address->address_length))
2225 if (strlen (address->transport_name) > 0)
2226 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2231 "ATS tells us to switch to address '%s/%s' session %p for "
2232 "peer `%s' in state %s/%d (quota in/out %u %u )\n",
2233 (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2234 address->transport_name,
2237 print_state (n->state),
2238 n->send_connect_ack,
2239 ntohl (bandwidth_in.value__),
2240 ntohl (bandwidth_out.value__));
2242 if (NULL == session)
2244 session = papi->get_session (papi->cls, address);
2245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2246 "Obtained new session for peer `%s' and address '%s': %p\n",
2247 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2249 if (NULL == session)
2251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2252 "Failed to obtain new session for peer `%s' and address '%s'\n",
2253 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
2254 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2259 case S_NOT_CONNECTED:
2261 free_neighbour (n, GNUNET_NO);
2264 set_address (&n->primary_address,
2265 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2266 n->state = S_INIT_BLACKLIST;
2267 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2268 check_blacklist (&n->id,
2269 n->connect_ack_timestamp,
2272 case S_INIT_BLACKLIST:
2273 /* ATS suggests a different address, switch again */
2274 set_address (&n->primary_address,
2275 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2276 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2277 check_blacklist (&n->id,
2278 n->connect_ack_timestamp,
2281 case S_CONNECT_SENT:
2282 /* ATS suggests a different address, switch again */
2283 set_address (&n->primary_address,
2284 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2285 n->state = S_INIT_BLACKLIST;
2286 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2287 check_blacklist (&n->id,
2288 n->connect_ack_timestamp,
2291 case S_CONNECT_RECV_ATS:
2292 set_address (&n->primary_address,
2293 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2294 n->state = S_CONNECT_RECV_BLACKLIST;
2295 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2296 check_blacklist (&n->id,
2297 n->connect_ack_timestamp,
2300 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2301 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2302 check_blacklist (&n->id,
2303 n->connect_ack_timestamp,
2306 case S_CONNECT_RECV_BLACKLIST:
2307 case S_CONNECT_RECV_ACK:
2308 /* ATS asks us to switch while we were trying to connect; switch to new
2309 address and check blacklist again */
2310 set_address (&n->primary_address,
2311 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2312 n->state = S_CONNECT_RECV_BLACKLIST;
2313 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2314 check_blacklist (&n->id,
2315 n->connect_ack_timestamp,
2319 GNUNET_assert (NULL != n->primary_address.address);
2320 GNUNET_assert (NULL != n->primary_address.session);
2321 if (n->primary_address.session == session)
2323 /* not an address change, just a quota change */
2324 set_address (&n->primary_address,
2325 address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2328 /* ATS asks us to switch a life connection; see if we can get
2329 a CONNECT_ACK on it before we actually do this! */
2330 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2331 set_address (&n->alternative_address,
2332 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2333 check_blacklist (&n->id,
2334 GNUNET_TIME_absolute_get (),
2337 case S_RECONNECT_ATS:
2338 n->state = S_RECONNECT_BLACKLIST;
2339 set_address (&n->primary_address,
2340 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2341 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2342 check_blacklist (&n->id,
2343 n->connect_ack_timestamp,
2346 case S_RECONNECT_BLACKLIST:
2347 /* ATS asks us to switch while we were trying to reconnect; switch to new
2348 address and check blacklist again */
2349 set_address (&n->primary_address,
2350 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2351 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2352 check_blacklist (&n->id,
2353 n->connect_ack_timestamp,
2356 case S_RECONNECT_SENT:
2357 /* ATS asks us to switch while we were trying to reconnect; switch to new
2358 address and check blacklist again */
2359 n->state = S_RECONNECT_BLACKLIST;
2360 set_address (&n->primary_address,
2361 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2362 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2363 check_blacklist (&n->id,
2364 n->connect_ack_timestamp,
2367 case S_CONNECTED_SWITCHING_BLACKLIST:
2368 if (n->primary_address.session == session)
2370 /* ATS switches back to still-active session */
2371 n->state = S_CONNECTED;
2372 free_address (&n->alternative_address);
2375 /* ATS asks us to switch a life connection, update blacklist check */
2376 set_address (&n->alternative_address,
2377 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2378 check_blacklist (&n->id,
2379 GNUNET_TIME_absolute_get (),
2382 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2383 if (n->primary_address.session == session)
2385 /* ATS switches back to still-active session */
2386 free_address (&n->alternative_address);
2387 n->state = S_CONNECTED;
2390 /* ATS asks us to switch a life connection, update blacklist check */
2391 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2392 set_address (&n->alternative_address,
2393 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2394 check_blacklist (&n->id,
2395 GNUNET_TIME_absolute_get (),
2399 /* not going to switch addresses while disconnecting */
2401 case S_DISCONNECT_FINISHED:
2405 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2406 "Unhandled state `%s'\n",
2407 print_state (n->state));
2415 send_utilization_data (void *cls,
2416 const struct GNUNET_PeerIdentity *key,
2419 struct NeighbourMapEntry *n = value;
2420 struct GNUNET_ATS_Information atsi[4];
2422 uint32_t bps_pl_out;
2425 struct GNUNET_TIME_Relative delta;
2427 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2428 GNUNET_TIME_absolute_get ());
2432 if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2433 bps_pl_in = (1000LL * 1000LL * n->util_payload_bytes_recv) / (delta.rel_value_us);
2435 if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2436 bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2438 "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2443 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2444 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2446 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2447 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2450 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2451 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2455 atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2456 atsi[0].value = htonl (bps_out);
2457 atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2458 atsi[1].value = htonl (bps_in);
2460 atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2461 atsi[2].value = htonl (bps_pl_out);
2462 atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2463 atsi[3].value = htonl (bps_pl_in);
2465 GST_ats_update_metrics (key, n->primary_address.address,
2466 n->primary_address.session, atsi, 4);
2467 n->util_payload_bytes_recv = 0;
2468 n->util_payload_bytes_sent = 0;
2469 n->util_total_bytes_recv = 0;
2470 n->util_total_bytes_sent = 0;
2471 n->last_util_transmission = GNUNET_TIME_absolute_get();
2477 * Task transmitting utilization in a regular interval
2479 * @param cls the 'struct NeighbourMapEntry' for which we are running
2480 * @param tc scheduler context (unused)
2483 utilization_transmission (void *cls,
2484 const struct GNUNET_SCHEDULER_TaskContext *tc)
2486 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2488 if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2489 GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2491 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2492 utilization_transmission, NULL);
2498 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2499 const struct GNUNET_HELLO_Address *address,
2500 struct Session *session,
2501 const struct GNUNET_MessageHeader *message)
2503 struct NeighbourMapEntry *n;
2505 n = lookup_neighbour (peer);
2508 n->util_total_bytes_recv += ntohs(message->size);
2513 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2514 const struct GNUNET_HELLO_Address *address,
2515 struct Session *session,
2516 const struct GNUNET_MessageHeader *message)
2518 struct NeighbourMapEntry *n;
2519 n = lookup_neighbour (peer);
2522 n->util_payload_bytes_recv += ntohs(message->size);
2527 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2528 const struct GNUNET_HELLO_Address *address,
2529 struct Session *session,
2532 struct NeighbourMapEntry *n;
2533 n = lookup_neighbour (peer);
2536 if (n->primary_address.session != session)
2538 n->util_total_bytes_sent += size;
2543 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2546 struct NeighbourMapEntry *n;
2547 n = lookup_neighbour (peer);
2550 n->util_payload_bytes_sent += size;
2555 * Master task run for every neighbour. Performs all of the time-related
2556 * activities (keep alive, send next message, disconnect if idle, finish
2557 * clean up after disconnect).
2559 * @param cls the 'struct NeighbourMapEntry' for which we are running
2560 * @param tc scheduler context (unused)
2563 master_task (void *cls,
2564 const struct GNUNET_SCHEDULER_TaskContext *tc)
2566 struct NeighbourMapEntry *n = cls;
2567 struct GNUNET_TIME_Relative delay;
2569 n->task = GNUNET_SCHEDULER_NO_TASK;
2570 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2572 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2573 GNUNET_i2s (&n->id),
2574 print_state(n->state),
2575 GNUNET_STRINGS_relative_time_to_string (delay,
2579 case S_NOT_CONNECTED:
2580 /* invalid state for master task, clean up */
2582 n->state = S_DISCONNECT_FINISHED;
2583 free_neighbour (n, GNUNET_NO);
2586 if (0 == delay.rel_value_us)
2588 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2589 "Connection to `%s' timed out waiting for ATS to provide address\n",
2590 GNUNET_i2s (&n->id));
2591 n->state = S_DISCONNECT_FINISHED;
2592 free_neighbour (n, GNUNET_NO);
2596 case S_INIT_BLACKLIST:
2597 if (0 == delay.rel_value_us)
2599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2600 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2601 GNUNET_i2s (&n->id));
2602 n->state = S_DISCONNECT_FINISHED;
2603 free_neighbour (n, GNUNET_NO);
2607 case S_CONNECT_SENT:
2608 if (0 == delay.rel_value_us)
2610 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2611 "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2612 GNUNET_i2s (&n->id));
2613 /* We could not send to this address, delete address and session */
2614 if (NULL != n->primary_address.session)
2615 GNUNET_ATS_address_destroyed (GST_ats,
2616 n->primary_address.address, n->primary_address.session);
2617 GNUNET_ATS_address_destroyed (GST_ats,
2618 n->primary_address.address, NULL);
2619 disconnect_neighbour (n);
2623 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2624 if (0 == delay.rel_value_us)
2626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2627 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2628 GNUNET_i2s (&n->id));
2629 n->state = S_DISCONNECT_FINISHED;
2630 free_neighbour (n, GNUNET_NO);
2634 case S_CONNECT_RECV_ATS:
2635 if (0 == delay.rel_value_us)
2637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2638 "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2639 GNUNET_i2s (&n->id));
2640 n->state = S_DISCONNECT_FINISHED;
2641 free_neighbour (n, GNUNET_NO);
2645 case S_CONNECT_RECV_BLACKLIST:
2646 if (0 == delay.rel_value_us)
2648 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2649 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2650 GNUNET_i2s (&n->id));
2651 n->state = S_DISCONNECT_FINISHED;
2652 free_neighbour (n, GNUNET_NO);
2656 case S_CONNECT_RECV_ACK:
2657 if (0 == delay.rel_value_us)
2659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2660 "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2661 GNUNET_i2s (&n->id));
2662 disconnect_neighbour (n);
2667 if (0 == delay.rel_value_us)
2669 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2670 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2671 GNUNET_i2s (&n->id));
2672 disconnect_neighbour (n);
2675 try_transmission_to_peer (n);
2678 case S_RECONNECT_ATS:
2679 if (0 == delay.rel_value_us)
2681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2682 "Connection to `%s' timed out, waiting for ATS replacement address\n",
2683 GNUNET_i2s (&n->id));
2684 disconnect_neighbour (n);
2688 case S_RECONNECT_BLACKLIST:
2689 if (0 == delay.rel_value_us)
2691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2692 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2693 GNUNET_i2s (&n->id));
2694 disconnect_neighbour (n);
2698 case S_RECONNECT_SENT:
2699 if (0 == delay.rel_value_us)
2701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2702 "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2703 GNUNET_i2s (&n->id));
2704 disconnect_neighbour (n);
2708 case S_CONNECTED_SWITCHING_BLACKLIST:
2709 if (0 == delay.rel_value_us)
2711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2712 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2713 GNUNET_i2s (&n->id));
2714 disconnect_neighbour (n);
2717 try_transmission_to_peer (n);
2720 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2721 if (0 == delay.rel_value_us)
2723 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2724 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2725 GNUNET_i2s (&n->id));
2726 disconnect_neighbour (n);
2729 try_transmission_to_peer (n);
2733 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2734 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2735 GNUNET_i2s (&n->id));
2736 free_neighbour (n, GNUNET_NO);
2738 case S_DISCONNECT_FINISHED:
2739 /* how did we get here!? */
2743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2744 "Unhandled state `%s'\n",
2745 print_state (n->state));
2749 if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2750 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2751 (S_CONNECTED == n->state) )
2753 /* if we are *now* in one of these three states, we're sending
2754 keep alive messages, so we need to consider the keepalive
2755 delay, not just the connection timeout */
2756 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2759 if (GNUNET_SCHEDULER_NO_TASK == n->task)
2760 n->task = GNUNET_SCHEDULER_add_delayed (delay,
2767 * Send a SESSION_ACK message to the neighbour to confirm that we
2768 * got his CONNECT_ACK.
2770 * @param n neighbour to send the SESSION_ACK to
2773 send_session_ack_message (struct NeighbourMapEntry *n)
2775 struct GNUNET_MessageHeader msg;
2777 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2778 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2779 (void) send_with_session(n,
2780 (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2781 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_NO,
2787 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2788 * Consider switching to it.
2790 * @param message possibly a 'struct SessionConnectMessage' (check format)
2791 * @param peer identity of the peer to switch the address for
2792 * @param address address of the other peer, NULL if other peer
2794 * @param session session to use (or NULL)
2795 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2798 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2799 const struct GNUNET_PeerIdentity *peer,
2800 const struct GNUNET_HELLO_Address *address,
2801 struct Session *session)
2803 const struct SessionConnectMessage *scm;
2804 struct GNUNET_TIME_Absolute ts;
2805 struct NeighbourMapEntry *n;
2807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2808 "Received CONNECT_ACK message from peer `%s'\n",
2811 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2813 GNUNET_break_op (0);
2814 return GNUNET_SYSERR;
2816 GNUNET_STATISTICS_update (GST_stats,
2818 ("# CONNECT_ACK messages received"),
2820 scm = (const struct SessionConnectMessage *) message;
2821 GNUNET_break_op (ntohl (scm->reserved) == 0);
2822 if (NULL == (n = lookup_neighbour (peer)))
2824 GNUNET_STATISTICS_update (GST_stats,
2826 ("# unexpected CONNECT_ACK messages (no peer)"),
2828 return GNUNET_SYSERR;
2830 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2833 case S_NOT_CONNECTED:
2835 free_neighbour (n, GNUNET_NO);
2836 return GNUNET_SYSERR;
2838 case S_INIT_BLACKLIST:
2839 GNUNET_STATISTICS_update (GST_stats,
2841 ("# unexpected CONNECT_ACK messages (not ready)"),
2844 case S_CONNECT_SENT:
2845 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
2847 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2848 "CONNECT_ACK ignored as the timestamp does not match our CONNECT request\n");
2851 n->state = S_CONNECTED;
2852 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2853 GNUNET_STATISTICS_set (GST_stats,
2854 gettext_noop ("# peers connected"),
2855 ++neighbours_connected,
2857 connect_notify_cb (callback_cls, &n->id,
2858 n->primary_address.bandwidth_in,
2859 n->primary_address.bandwidth_out);
2860 /* Tell ATS that the outbound session we created to send CONNECT was successful */
2861 GST_ats_add_address (n->primary_address.address,
2862 n->primary_address.session,
2864 set_address (&n->primary_address,
2865 n->primary_address.address,
2866 n->primary_address.session,
2867 n->primary_address.bandwidth_in,
2868 n->primary_address.bandwidth_out,
2870 send_session_ack_message (n);
2872 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2873 case S_CONNECT_RECV_ATS:
2874 case S_CONNECT_RECV_BLACKLIST:
2875 case S_CONNECT_RECV_ACK:
2876 GNUNET_STATISTICS_update (GST_stats,
2878 ("# unexpected CONNECT_ACK messages (not ready)"),
2882 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2883 send_session_ack_message (n);
2885 case S_RECONNECT_ATS:
2886 case S_RECONNECT_BLACKLIST:
2887 /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2888 to give us a new address... */
2889 GNUNET_STATISTICS_update (GST_stats,
2891 ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2894 case S_RECONNECT_SENT:
2895 /* new address worked; go back to connected! */
2896 n->state = S_CONNECTED;
2897 send_session_ack_message (n);
2899 case S_CONNECTED_SWITCHING_BLACKLIST:
2900 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2901 send_session_ack_message (n);
2903 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2904 /* new address worked; adopt it and go back to connected! */
2905 n->state = S_CONNECTED;
2906 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2907 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2909 GST_ats_add_address (n->alternative_address.address,
2910 n->alternative_address.session,
2912 set_address (&n->primary_address,
2913 n->alternative_address.address,
2914 n->alternative_address.session,
2915 n->alternative_address.bandwidth_in,
2916 n->alternative_address.bandwidth_out,
2918 free_address (&n->alternative_address);
2919 send_session_ack_message (n);
2922 GNUNET_STATISTICS_update (GST_stats,
2924 ("# unexpected CONNECT_ACK messages (disconnecting)"),
2926 return GNUNET_SYSERR;
2927 case S_DISCONNECT_FINISHED:
2931 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2932 "Unhandled state `%s'\n",
2933 print_state (n->state));
2935 return GNUNET_SYSERR;
2942 * A session was terminated. Take note; if needed, try to get
2943 * an alternative address from ATS.
2945 * @param peer identity of the peer where the session died
2946 * @param session session that is gone
2947 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
2948 * this session was not in use
2951 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2952 struct Session *session)
2954 struct NeighbourMapEntry *n;
2955 struct BlackListCheckContext *bcc;
2956 struct BlackListCheckContext *bcc_next;
2958 /* make sure to cancel all ongoing blacklist checks involving 'session' */
2960 while (NULL != (bcc = bcc_next))
2962 bcc_next = bcc->next;
2963 if (bcc->na.session == session)
2965 if (NULL != bcc->bc)
2966 GST_blacklist_test_cancel (bcc->bc);
2967 GNUNET_HELLO_address_free (bcc->na.address);
2968 GNUNET_CONTAINER_DLL_remove (bc_head,
2974 if (NULL == (n = lookup_neighbour (peer)))
2975 return GNUNET_NO; /* can't affect us */
2976 if (session != n->primary_address.session)
2978 if (session == n->alternative_address.session)
2980 if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2981 (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
2982 n->state = S_CONNECTED;
2985 free_address (&n->alternative_address);
2987 return GNUNET_NO; /* doesn't affect us further */
2990 n->expect_latency_response = GNUNET_NO;
2993 case S_NOT_CONNECTED:
2995 free_neighbour (n, GNUNET_NO);
2999 free_neighbour (n, GNUNET_NO);
3001 case S_INIT_BLACKLIST:
3002 case S_CONNECT_SENT:
3003 free_address (&n->primary_address);
3004 n->state = S_INIT_ATS;
3005 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3006 // FIXME: need to ask ATS for suggestions again?
3007 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3009 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3010 case S_CONNECT_RECV_ATS:
3011 case S_CONNECT_RECV_BLACKLIST:
3012 case S_CONNECT_RECV_ACK:
3013 /* error on inbound session; free neighbour entirely */
3014 free_address (&n->primary_address);
3015 free_neighbour (n, GNUNET_NO);
3018 n->state = S_RECONNECT_ATS;
3019 free_address (&n->primary_address);
3020 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3021 /* FIXME: is this ATS call needed? */
3022 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3024 case S_RECONNECT_ATS:
3025 /* we don't have an address, how can it go down? */
3028 case S_RECONNECT_BLACKLIST:
3029 case S_RECONNECT_SENT:
3030 n->state = S_RECONNECT_ATS;
3031 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3032 // FIXME: need to ask ATS for suggestions again?
3033 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3035 case S_CONNECTED_SWITCHING_BLACKLIST:
3036 /* primary went down while we were checking secondary against
3037 blacklist, adopt secondary as primary */
3038 n->state = S_RECONNECT_BLACKLIST;
3039 free_address (&n->primary_address);
3040 n->primary_address = n->alternative_address;
3041 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3042 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3044 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3045 /* primary went down while we were waiting for CONNECT_ACK on secondary;
3046 secondary as primary */
3047 n->state = S_RECONNECT_SENT;
3048 free_address (&n->primary_address);
3049 n->primary_address = n->alternative_address;
3050 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3051 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3054 free_address (&n->primary_address);
3056 case S_DISCONNECT_FINISHED:
3057 /* neighbour was freed and plugins told to terminate session */
3061 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3062 "Unhandled state `%s'\n",
3063 print_state (n->state));
3067 if (GNUNET_SCHEDULER_NO_TASK != n->task)
3068 GNUNET_SCHEDULER_cancel (n->task);
3069 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3075 * We received a 'SESSION_ACK' message from the other peer.
3076 * If we sent a 'CONNECT_ACK' last, this means we are now
3077 * connected. Otherwise, do nothing.
3079 * @param message possibly a 'struct SessionConnectMessage' (check format)
3080 * @param peer identity of the peer to switch the address for
3081 * @param address address of the other peer, NULL if other peer
3083 * @param session session to use (or NULL)
3084 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3087 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3088 const struct GNUNET_PeerIdentity *peer,
3089 const struct GNUNET_HELLO_Address *address,
3090 struct Session *session)
3092 struct NeighbourMapEntry *n;
3094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3095 "Received SESSION_ACK message from peer `%s'\n",
3097 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3099 GNUNET_break_op (0);
3100 return GNUNET_SYSERR;
3102 GNUNET_STATISTICS_update (GST_stats,
3104 ("# SESSION_ACK messages received"),
3106 if (NULL == (n = lookup_neighbour (peer)))
3108 GNUNET_break_op (0);
3109 return GNUNET_SYSERR;
3111 /* check if we are in a plausible state for having sent
3112 a CONNECT_ACK. If not, return, otherwise break */
3113 if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3114 (S_CONNECT_SENT != n->state) ) ||
3115 (2 != n->send_connect_ack) )
3117 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3118 "Received SESSION_ACK message from peer `%s' in state %s/%d\n",
3120 print_state (n->state),
3121 n->send_connect_ack);
3122 GNUNET_STATISTICS_update (GST_stats,
3123 gettext_noop ("# unexpected SESSION_ACK messages"), 1,
3127 n->state = S_CONNECTED;
3128 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3129 GNUNET_STATISTICS_set (GST_stats,
3130 gettext_noop ("# peers connected"),
3131 ++neighbours_connected,
3133 connect_notify_cb (callback_cls, &n->id,
3134 n->primary_address.bandwidth_in,
3135 n->primary_address.bandwidth_out);
3137 GST_ats_add_address (n->primary_address.address,
3138 n->primary_address.session,
3140 set_address (&n->primary_address,
3141 n->primary_address.address,
3142 n->primary_address.session,
3143 n->primary_address.bandwidth_in,
3144 n->primary_address.bandwidth_out,
3151 * Test if we're connected to the given peer.
3153 * @param target peer to test
3154 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3157 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3159 return test_connected (lookup_neighbour (target));
3164 * Change the incoming quota for the given peer.
3166 * @param neighbour identity of peer to change qutoa for
3167 * @param quota new quota
3170 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3171 struct GNUNET_BANDWIDTH_Value32NBO quota)
3173 struct NeighbourMapEntry *n;
3175 if (NULL == (n = lookup_neighbour (neighbour)))
3177 GNUNET_STATISTICS_update (GST_stats,
3179 ("# SET QUOTA messages ignored (no such peer)"),
3183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3184 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3185 ntohl (quota.value__), GNUNET_i2s (&n->id));
3186 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3187 if (0 != ntohl (quota.value__))
3189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3190 "Disconnecting peer `%4s' due to SET_QUOTA\n",
3191 GNUNET_i2s (&n->id));
3192 if (GNUNET_YES == test_connected (n))
3193 GNUNET_STATISTICS_update (GST_stats,
3194 gettext_noop ("# disconnects due to quota of 0"),
3196 disconnect_neighbour (n);
3201 * We received a disconnect message from the given peer,
3202 * validate and process.
3204 * @param peer sender of the message
3205 * @param msg the disconnect message
3208 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
3209 const struct GNUNET_MessageHeader *msg)
3211 struct NeighbourMapEntry *n;
3212 const struct SessionDisconnectMessage *sdm;
3214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3215 "Received DISCONNECT message from peer `%s'\n",
3217 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3219 // GNUNET_break_op (0);
3220 GNUNET_STATISTICS_update (GST_stats,
3222 ("# disconnect messages ignored (old format)"), 1,
3226 GNUNET_STATISTICS_update (GST_stats,
3228 ("# DISCONNECT messages received"),
3230 sdm = (const struct SessionDisconnectMessage *) msg;
3231 if (NULL == (n = lookup_neighbour (peer)))
3232 return; /* gone already */
3233 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3235 GNUNET_STATISTICS_update (GST_stats,
3237 ("# disconnect messages ignored (timestamp)"), 1,
3241 if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3243 GNUNET_break_op (0);
3246 if (ntohl (sdm->purpose.size) !=
3247 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3248 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3249 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3251 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3252 "%s message from peer `%s' has invalid size \n",
3255 GNUNET_break_op (0);
3259 GNUNET_CRYPTO_eddsa_verify
3260 (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3261 &sdm->signature, &sdm->public_key))
3263 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3264 "%s message from peer `%s' cannot be verified \n",
3267 GNUNET_break_op (0);
3270 if (GNUNET_YES == test_connected (n))
3271 GNUNET_STATISTICS_update (GST_stats,
3273 ("# other peer asked to disconnect from us"), 1,
3275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3276 "Disconnecting by request from peer %s\n",
3278 disconnect_neighbour (n);
3283 * Closure for the neighbours_iterate function.
3285 struct IteratorContext
3288 * Function to call on each connected neighbour.
3290 GST_NeighbourIterator cb;
3300 * Call the callback from the closure for each connected neighbour.
3302 * @param cls the `struct IteratorContext`
3303 * @param key the hash of the public key of the neighbour
3304 * @param value the `struct NeighbourMapEntry`
3305 * @return #GNUNET_OK (continue to iterate)
3308 neighbours_iterate (void *cls,
3309 const struct GNUNET_PeerIdentity *key,
3312 struct IteratorContext *ic = cls;
3313 struct NeighbourMapEntry *n = value;
3314 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3315 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3317 if (GNUNET_YES != test_connected (n))
3320 if (NULL != n->primary_address.address)
3322 bandwidth_in = n->primary_address.bandwidth_in;
3323 bandwidth_out = n->primary_address.bandwidth_out;
3327 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3328 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3330 ic->cb (ic->cb_cls, &n->id,
3331 n->primary_address.address,
3332 bandwidth_in, bandwidth_out);
3338 * Iterate over all connected neighbours.
3340 * @param cb function to call
3341 * @param cb_cls closure for cb
3344 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3346 struct IteratorContext ic;
3348 if (NULL == neighbours)
3349 return; /* can happen during shutdown */
3352 GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3357 * If we have an active connection to the given target, it must be shutdown.
3359 * @param target peer to disconnect from
3362 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3364 struct NeighbourMapEntry *n;
3366 if (NULL == (n = lookup_neighbour (target)))
3367 return; /* not active */
3368 if (GNUNET_YES == test_connected (n))
3369 GNUNET_STATISTICS_update (GST_stats,
3371 ("# disconnected from peer upon explicit request"), 1,
3373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3374 "Forced disconnect from peer %s\n",
3375 GNUNET_i2s (target));
3376 disconnect_neighbour (n);
3381 * Obtain current latency information for the given neighbour.
3383 * @param peer to get the latency for
3384 * @return observed latency of the address, FOREVER if the
3385 * the connection is not up
3387 struct GNUNET_TIME_Relative
3388 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3390 struct NeighbourMapEntry *n;
3392 n = lookup_neighbour (peer);
3394 return GNUNET_TIME_UNIT_FOREVER_REL;
3398 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3399 case S_CONNECTED_SWITCHING_BLACKLIST:
3400 case S_RECONNECT_SENT:
3401 case S_RECONNECT_ATS:
3402 case S_RECONNECT_BLACKLIST:
3404 case S_NOT_CONNECTED:
3405 case S_INIT_BLACKLIST:
3407 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3408 case S_CONNECT_RECV_ATS:
3409 case S_CONNECT_RECV_BLACKLIST:
3410 case S_CONNECT_RECV_ACK:
3411 case S_CONNECT_SENT:
3413 case S_DISCONNECT_FINISHED:
3414 return GNUNET_TIME_UNIT_FOREVER_REL;
3416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3417 "Unhandled state `%s'\n",
3418 print_state (n->state));
3422 return GNUNET_TIME_UNIT_FOREVER_REL;
3427 * Obtain current address information for the given neighbour.
3430 * @return address currently used
3432 struct GNUNET_HELLO_Address *
3433 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3435 struct NeighbourMapEntry *n;
3437 n = lookup_neighbour (peer);
3440 return n->primary_address.address;
3445 * Initialize the neighbours subsystem.
3447 * @param cls closure for callbacks
3448 * @param connect_cb function to call if we connect to a peer
3449 * @param disconnect_cb function to call if we disconnect from a peer
3450 * @param peer_address_cb function to call if we change an active address
3452 * @param max_fds maximum number of fds to use
3455 GST_neighbours_start (void *cls,
3456 NotifyConnect connect_cb,
3457 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3458 GNUNET_TRANSPORT_AddressChangeCallback peer_address_cb,
3459 unsigned int max_fds)
3462 connect_notify_cb = connect_cb;
3463 disconnect_notify_cb = disconnect_cb;
3464 address_change_cb = peer_address_cb;
3465 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3466 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3467 utilization_transmission, NULL);
3472 * Disconnect from the given neighbour.
3475 * @param key hash of neighbour's public key (not used)
3476 * @param value the 'struct NeighbourMapEntry' of the neighbour
3477 * @return #GNUNET_OK (continue to iterate)
3480 disconnect_all_neighbours (void *cls,
3481 const struct GNUNET_PeerIdentity *key,
3484 struct NeighbourMapEntry *n = value;
3486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3487 "Disconnecting peer `%4s', %s\n",
3488 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3489 n->state = S_DISCONNECT_FINISHED;
3490 free_neighbour (n, GNUNET_NO);
3496 * Cleanup the neighbours subsystem.
3499 GST_neighbours_stop ()
3501 if (NULL == neighbours)
3503 if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3505 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3506 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3509 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3510 &disconnect_all_neighbours,
3512 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3514 callback_cls = NULL;
3515 connect_notify_cb = NULL;
3516 disconnect_notify_cb = NULL;
3517 address_change_cb = NULL;
3521 /* end of file gnunet-service-transport_neighbours.c */