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;
235 * Possible state of a neighbour. Initially, we are #S_NOT_CONNECTED.
237 * Then, there are two main paths. If we receive a CONNECT message, we
238 * first run a check against the blacklist (#S_CONNECT_RECV_BLACKLIST_INBOUND).
239 * If this check is successful, we give the inbound address to ATS.
240 * After the check we ask ATS for a suggestion (S_CONNECT_RECV_ATS).
241 * If ATS makes a suggestion, we ALSO give that suggestion to the blacklist
242 * (#S_CONNECT_RECV_BLACKLIST). Once the blacklist approves the
243 * address we got from ATS, we send our CONNECT_ACK and go to
244 * #S_CONNECT_RECV_ACK. If we receive a SESSION_ACK, we go to
245 * #S_CONNECTED (and notify everyone about the new connection). If the
246 * operation times out, we go to #S_DISCONNECT.
248 * The other case is where we transmit a CONNECT message first. We
249 * start with #S_INIT_ATS. If we get an address, we enter
250 * #S_INIT_BLACKLIST and check the blacklist. If the blacklist is OK
251 * with the connection, we actually send the CONNECT message and go to
252 * state S_CONNECT_SENT. Once we receive a CONNECT_ACK, we go to
253 * #S_CONNECTED (and notify everyone about the new connection and send
254 * back a SESSION_ACK). If the operation times out, we go to
257 * If the session is in trouble (i.e. transport-level disconnect or
258 * timeout), we go to #S_RECONNECT_ATS where we ask ATS for a new
259 * address (we don't notify anyone about the disconnect yet). Once we
260 * have a new address, we go to #S_RECONNECT_BLACKLIST to check the new
261 * address against the blacklist. If the blacklist approves, we enter
262 * #S_RECONNECT_SENT and send a CONNECT message. If we receive a
263 * CONNECT_ACK, we go to #S_CONNECTED and nobody noticed that we had
264 * trouble; we also send a SESSION_ACK at this time just in case. If
265 * the operation times out, we go to S_DISCONNECT (and notify everyone
266 * about the lost connection).
268 * If ATS decides to switch addresses while we have a normal
269 * connection, we go to #S_CONNECTED_SWITCHING_BLACKLIST to check the
270 * new address against the blacklist. If the blacklist approves, we
271 * go to #S_CONNECTED_SWITCHING_CONNECT_SENT and send a
272 * SESSION_CONNECT. If we get a SESSION_ACK back, we switch the
273 * primary connection to the suggested alternative from ATS, go back
274 * to #S_CONNECTED and send a SESSION_ACK to the other peer just to be
275 * sure. If the operation times out (or the blacklist disapproves),
276 * we go to #S_CONNECTED (and notify ATS that the given alternative
277 * address is "invalid").
279 * Once a session is in #S_DISCONNECT, it is cleaned up and then goes
280 * to (#S_DISCONNECT_FINISHED). If we receive an explicit disconnect
281 * request, we can go from any state to #S_DISCONNECT, possibly after
282 * generating disconnect notifications.
284 * Note that it is quite possible that while we are in any of these
285 * states, we could receive a 'CONNECT' request from the other peer.
286 * We then enter a 'weird' state where we pursue our own primary state
287 * machine (as described above), but with the 'send_connect_ack' flag
288 * set to 1. If our state machine allows us to send a 'CONNECT_ACK'
289 * (because we have an acceptable address), we send the 'CONNECT_ACK'
290 * and set the 'send_connect_ack' to 2. If we then receive a
291 * 'SESSION_ACK', we go to #S_CONNECTED (and reset 'send_connect_ack'
298 * fresh peer or completely disconnected
303 * Asked to initiate connection, trying to get address from ATS
308 * Asked to initiate connection, trying to get address approved
314 * Sent CONNECT message to other peer, waiting for CONNECT_ACK
319 * Received a CONNECT, do a blacklist check for inbound address
321 S_CONNECT_RECV_BLACKLIST_INBOUND,
324 * Received a CONNECT, asking ATS about address suggestions.
329 * Received CONNECT from other peer, got an address, checking with blacklist.
331 S_CONNECT_RECV_BLACKLIST,
334 * CONNECT request from other peer was SESSION_ACK'ed, waiting for
340 * Got our CONNECT_ACK/SESSION_ACK, connection is up.
345 * Connection got into trouble, rest of the system still believes
346 * it to be up, but we're getting a new address from ATS.
351 * Connection got into trouble, rest of the system still believes
352 * it to be up; we are checking the new address against the blacklist.
354 S_RECONNECT_BLACKLIST,
357 * Sent CONNECT over new address (either by ATS telling us to switch
358 * addresses or from RECONNECT_ATS); if this fails, we need to tell
359 * the rest of the system about a disconnect.
364 * We have some primary connection, but ATS suggested we switch
365 * to some alternative; we're now checking the alternative against
368 S_CONNECTED_SWITCHING_BLACKLIST,
371 * We have some primary connection, but ATS suggested we switch
372 * to some alternative; we now sent a CONNECT message for the
373 * alternative session to the other peer and waiting for a
374 * CONNECT_ACK to make this our primary connection.
376 S_CONNECTED_SWITCHING_CONNECT_SENT,
379 * Disconnect in progress (we're sending the DISCONNECT message to the
380 * other peer; after that is finished, the state will be cleaned up).
385 * We're finished with the disconnect; and are cleaning up the state
386 * now! We put the struct into this state when we are really in the
387 * task that calls 'free' on it and are about to remove the record
388 * from the map. We should never find a 'struct NeighbourMapEntry'
389 * in this state in the map. Accessing a 'struct NeighbourMapEntry'
390 * in this state virtually always means using memory that has been
391 * freed (the exception being the cleanup code in #free_neighbour()).
393 S_DISCONNECT_FINISHED
398 * A possible address we could use to communicate with a neighbour.
400 struct NeighbourAddress
404 * Active session for this address.
406 struct Session *session;
409 * Network-level address information.
411 struct GNUNET_HELLO_Address *address;
414 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
415 * peer for this address. Use to check that the ACK is in response
416 * to our most recent 'CONNECT'.
418 struct GNUNET_TIME_Absolute connect_timestamp;
421 * Inbound bandwidth from ATS for this address.
423 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
426 * Outbound bandwidth from ATS for this address.
428 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
431 * Did we tell ATS that this is our 'active' address?
436 * The current nonce sent in the last keep alive messages
438 uint32_t keep_alive_nonce;
443 * Entry in neighbours.
445 struct NeighbourMapEntry
449 * Head of list of messages we would like to send to this peer;
450 * must contain at most one message per client.
452 struct MessageQueue *messages_head;
455 * Tail of list of messages we would like to send to this peer; must
456 * contain at most one message per client.
458 struct MessageQueue *messages_tail;
461 * Are we currently trying to send a message? If so, which one?
463 struct MessageQueue *is_active;
466 * Primary address we currently use to communicate with the neighbour.
468 struct NeighbourAddress primary_address;
471 * Alternative address currently under consideration for communicating
472 * with the neighbour.
474 struct NeighbourAddress alternative_address;
477 * Identity of this neighbour.
479 struct GNUNET_PeerIdentity id;
482 * Main task that drives this peer (timeouts, keepalives, etc.).
483 * Always runs the 'master_task'.
485 GNUNET_SCHEDULER_TaskIdentifier task;
488 * At what time should we sent the next keep-alive message?
490 struct GNUNET_TIME_Absolute keep_alive_time;
493 * At what time did we sent the last keep-alive message? Used
494 * to calculate round-trip time ("latency").
496 struct GNUNET_TIME_Absolute last_keep_alive_time;
499 * Timestamp we should include in our next CONNECT_ACK message.
500 * (only valid if 'send_connect_ack' is GNUNET_YES). Used to build
501 * our CONNECT_ACK message.
503 struct GNUNET_TIME_Absolute connect_ack_timestamp;
506 * ATS address suggest handle
508 struct GNUNET_ATS_SuggestHandle *suggest_handle;
511 * Time where we should cut the connection (timeout) if we don't
512 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
513 * if we are in S_CONNECTED).
515 struct GNUNET_TIME_Absolute timeout;
518 * Latest calculated latency value
520 struct GNUNET_TIME_Relative latency;
523 * Tracker for inbound bandwidth.
525 struct GNUNET_BANDWIDTH_Tracker in_tracker;
528 * How often has the other peer (recently) violated the inbound
529 * traffic limit? Incremented by 10 per violation, decremented by 1
530 * per non-violation (for each time interval).
532 unsigned int quota_violation_count;
535 * The current state of the peer.
540 * Did we sent an KEEP_ALIVE message and are we expecting a response?
542 int expect_latency_response;
545 * Flag to set if we still need to send a CONNECT_ACK message to the other peer
546 * (once we have an address to use and the peer has been allowed by our
547 * blacklist). Set to 1 if we need to send a CONNECT_ACK. Set to 2 if we
548 * did send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving
549 * a 'SESSION_ACK' (regardless of what our own state machine might say).
551 int send_connect_ack;
554 * Tracking utilization of outbound bandwidth
556 uint32_t util_payload_bytes_sent;
559 * Tracking utilization of inbound bandwidth
561 uint32_t util_payload_bytes_recv;
564 * Tracking utilization of outbound bandwidth
566 uint32_t util_total_bytes_sent;
569 * Tracking utilization of inbound bandwidth
571 uint32_t util_total_bytes_recv;
574 * Date of last utilization transmission
576 struct GNUNET_TIME_Absolute last_util_transmission;
581 * Context for blacklist checks and the 'handle_test_blacklist_cont'
582 * function. Stores information about ongoing blacklist checks.
584 struct BlackListCheckContext
588 * We keep blacklist checks in a DLL.
590 struct BlackListCheckContext *next;
593 * We keep blacklist checks in a DLL.
595 struct BlackListCheckContext *prev;
598 * Address that is being checked.
600 struct NeighbourAddress na;
603 * Handle to the ongoing blacklist check.
605 struct GST_BlacklistCheck *bc;
610 * Hash map from peer identities to the respective 'struct NeighbourMapEntry'.
612 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
615 * We keep blacklist checks in a DLL so that we can find
616 * the 'sessions' in their 'struct NeighbourAddress' if
617 * a session goes down.
619 static struct BlackListCheckContext *bc_head;
622 * We keep blacklist checks in a DLL.
624 static struct BlackListCheckContext *bc_tail;
627 * Closure for #connect_notify_cb, #disconnect_notify_cb and #address_change_cb
629 static void *callback_cls;
632 * Function to call when we connected to a neighbour.
634 static NotifyConnect connect_notify_cb;
637 * Function to call when we disconnected from a neighbour.
639 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
642 * Function to call when we changed an active address of a neighbour.
644 static GNUNET_TRANSPORT_PeerIterateCallback address_change_cb;
647 * counter for connected neighbours
649 static unsigned int neighbours_connected;
652 * Number of bytes we have currently queued for transmission.
654 static unsigned long long bytes_in_send_queue;
657 * Task transmitting utilization data
659 static GNUNET_SCHEDULER_TaskIdentifier util_transmission_tk;
663 * Lookup a neighbour entry in the neighbours hash map.
665 * @param pid identity of the peer to look up
666 * @return the entry, NULL if there is no existing record
668 static struct NeighbourMapEntry *
669 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
671 if (NULL == neighbours)
673 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
678 print_state (int state)
683 case S_NOT_CONNECTED:
684 return "S_NOT_CONNECTED";
687 case S_INIT_BLACKLIST:
688 return "S_INIT_BLACKLIST";
690 return "S_CONNECT_SENT";
691 case S_CONNECT_RECV_BLACKLIST_INBOUND:
692 return "S_CONNECT_RECV_BLACKLIST_INBOUND";
693 case S_CONNECT_RECV_ATS:
694 return "S_CONNECT_RECV_ATS";
695 case S_CONNECT_RECV_BLACKLIST:
696 return "S_CONNECT_RECV_BLACKLIST";
697 case S_CONNECT_RECV_ACK:
698 return "S_CONNECT_RECV_ACK";
700 return "S_CONNECTED";
701 case S_RECONNECT_ATS:
702 return "S_RECONNECT_ATS";
703 case S_RECONNECT_BLACKLIST:
704 return "S_RECONNECT_BLACKLIST";
705 case S_RECONNECT_SENT:
706 return "S_RECONNECT_SENT";
707 case S_CONNECTED_SWITCHING_BLACKLIST:
708 return "S_CONNECTED_SWITCHING_BLACKLIST";
709 case S_CONNECTED_SWITCHING_CONNECT_SENT:
710 return "S_CONNECTED_SWITCHING_CONNECT_SENT";
712 return "S_DISCONNECT";
713 case S_DISCONNECT_FINISHED:
714 return "S_DISCONNECT_FINISHED";
722 * Test if we're connected to the given peer.
724 * @param n neighbour entry of peer to test
725 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
728 test_connected (struct NeighbourMapEntry *n)
734 case S_NOT_CONNECTED:
736 case S_INIT_BLACKLIST:
738 case S_CONNECT_RECV_BLACKLIST_INBOUND:
739 case S_CONNECT_RECV_ATS:
740 case S_CONNECT_RECV_BLACKLIST:
741 case S_CONNECT_RECV_ACK:
744 case S_RECONNECT_ATS:
745 case S_RECONNECT_BLACKLIST:
746 case S_RECONNECT_SENT:
747 case S_CONNECTED_SWITCHING_BLACKLIST:
748 case S_CONNECTED_SWITCHING_CONNECT_SENT:
751 case S_DISCONNECT_FINISHED:
754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755 "Unhandled state `%s' \n",
756 print_state (n->state));
760 return GNUNET_SYSERR;
764 * Send information about a new outbound quota to our clients.
766 * @param target affected peer
767 * @param quota new quota
770 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
771 struct GNUNET_BANDWIDTH_Value32NBO quota)
773 struct QuotaSetMessage q_msg;
775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
776 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
777 ntohl (quota.value__), GNUNET_i2s (target));
778 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
779 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
781 q_msg.peer = (*target);
782 GST_clients_broadcast (&q_msg.header, GNUNET_NO);
787 * We don't need a given neighbour address any more.
788 * Release its resources and give appropriate notifications
789 * to ATS and other subsystems.
791 * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
794 free_address (struct NeighbourAddress *na)
796 if (GNUNET_YES == na->ats_active)
798 GST_validation_set_address_use (na->address, na->session, GNUNET_NO, __LINE__);
799 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
800 address_change_cb (callback_cls, &na->address->peer, NULL);
803 na->ats_active = GNUNET_NO;
804 na->keep_alive_nonce = 0;
805 if (NULL != na->address)
807 GNUNET_HELLO_address_free (na->address);
815 * Initialize the 'struct NeighbourAddress'.
817 * @param na neighbour address to initialize
818 * @param address address of the other peer, NULL if other peer
820 * @param session session to use (or NULL, in which case an
821 * address must be setup)
822 * @param bandwidth_in inbound quota to be used when connection is up
823 * @param bandwidth_out outbound quota to be used when connection is up
824 * @param is_active #GNUNET_YES to mark this as the active address with ATS
827 set_address (struct NeighbourAddress *na,
828 const struct GNUNET_HELLO_Address *address,
829 struct Session *session,
830 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
831 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
834 struct GNUNET_TRANSPORT_PluginFunctions *papi;
836 if (NULL == (papi = GST_plugins_find (address->transport_name)))
841 if (session == na->session)
843 na->bandwidth_in = bandwidth_in;
844 na->bandwidth_out = bandwidth_out;
845 if (is_active != na->ats_active)
847 na->ats_active = is_active;
848 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, is_active);
849 GST_validation_set_address_use (na->address, na->session, is_active, __LINE__);
851 address_change_cb (callback_cls, &address->peer, address);
853 if (GNUNET_YES == is_active)
855 /* FIXME: is this the right place to set quotas? */
856 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
857 send_outbound_quota (&address->peer, bandwidth_out);
863 session = papi->get_session (papi->cls, address);
866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867 "Failed to obtain new session for peer `%s' and address '%s'\n",
868 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
869 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
872 na->address = GNUNET_HELLO_address_copy (address);
873 na->bandwidth_in = bandwidth_in;
874 na->bandwidth_out = bandwidth_out;
875 na->session = session;
876 na->ats_active = is_active;
877 na->keep_alive_nonce = 0;
878 if (GNUNET_YES == is_active)
880 /* Telling ATS about new session */
881 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_YES);
882 GST_validation_set_address_use (na->address, na->session, GNUNET_YES, __LINE__);
883 address_change_cb (callback_cls, &address->peer, address);
884 /* FIXME: is this the right place to set quotas? */
885 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
886 send_outbound_quota (&address->peer, bandwidth_out);
892 * Free a neighbour map entry.
894 * @param n entry to free
895 * @param keep_sessions #GNUNET_NO to tell plugin to terminate sessions,
896 * #GNUNET_YES to keep all sessions
899 free_neighbour (struct NeighbourMapEntry *n,
902 struct MessageQueue *mq;
903 struct GNUNET_TRANSPORT_PluginFunctions *papi;
904 struct GNUNET_HELLO_Address *backup_primary;
906 n->is_active = NULL; /* always free'd by its own continuation! */
908 /* fail messages currently in the queue */
909 while (NULL != (mq = n->messages_head))
911 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
912 if (NULL != mq->cont)
913 mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
916 /* It is too late to send other peer disconnect notifications, but at
917 least internally we need to get clean... */
918 if (GNUNET_YES == test_connected (n))
920 GNUNET_STATISTICS_set (GST_stats,
921 gettext_noop ("# peers connected"),
922 --neighbours_connected,
924 disconnect_notify_cb (callback_cls, &n->id);
926 n->state = S_DISCONNECT_FINISHED;
928 if (NULL != n->primary_address.address)
930 backup_primary = GNUNET_HELLO_address_copy (n->primary_address.address);
933 backup_primary = NULL;
935 /* free addresses and mark as unused */
936 free_address (&n->primary_address);
937 free_address (&n->alternative_address);
939 /* FIXME: Note that if we are switching between two TCP sessions to
940 the same peer, we might want to selectively kill only one of
941 them! Killing all sessions like this seems to be very, very
944 /* cut transport-level connection */
945 if ((GNUNET_NO == keep_sessions) &&
946 (NULL != backup_primary) &&
947 (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
948 papi->disconnect_peer (papi->cls, &n->id);
950 GNUNET_free_non_null (backup_primary);
952 GNUNET_assert (GNUNET_YES ==
953 GNUNET_CONTAINER_multipeermap_remove (neighbours,
956 // FIXME-ATS-API: we might want to be more specific about
957 // which states we do this from in the future (ATS should
958 // have given us a 'suggest_address' handle, and if we have
959 // such a handle, we should cancel the operation here!
960 if (NULL != n->suggest_handle)
962 GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
963 n->suggest_handle = NULL;
966 if (GNUNET_SCHEDULER_NO_TASK != n->task)
968 GNUNET_SCHEDULER_cancel (n->task);
969 n->task = GNUNET_SCHEDULER_NO_TASK;
971 /* free rest of memory */
977 * Transmit a message using the current session of the given
980 * @param n entry for the recipient
981 * @param msgbuf buffer to transmit
982 * @param msgbuf_size number of bytes in @a msgbuf buffer
983 * @param priority transmission priority
984 * @param timeout transmission timeout
985 * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
986 * timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
987 * @param cont continuation to call when finished (can be NULL)
988 * @param cont_cls closure for @a cont
989 * @return timeout (copy of @a timeout or a calculated one if
990 * @a use_keepalive_timeout is #GNUNET_YES.
992 static struct GNUNET_TIME_Relative
993 send_with_session (struct NeighbourMapEntry *n,
994 const char *msgbuf, size_t msgbuf_size,
996 struct GNUNET_TIME_Relative timeout,
997 unsigned int use_keepalive_timeout,
998 GNUNET_TRANSPORT_TransmitContinuation cont,
1001 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1002 struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
1004 GNUNET_assert (n->primary_address.session != NULL);
1005 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
1006 (-1 == papi->send (papi->cls,
1007 n->primary_address.session,
1008 msgbuf, msgbuf_size,
1010 (result = (GNUNET_NO == use_keepalive_timeout) ? timeout :
1011 GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
1012 papi->query_keepalive_factor (papi->cls))),
1013 cont, cont_cls)))) &&
1015 cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
1016 GST_neighbours_notify_data_sent (&n->id,
1017 n->primary_address.address, n->primary_address.session, msgbuf_size);
1018 GNUNET_break (NULL != papi);
1024 * Master task run for every neighbour. Performs all of the time-related
1025 * activities (keep alive, send next message, disconnect if idle, finish
1026 * clean up after disconnect).
1028 * @param cls the `struct NeighbourMapEntry` for which we are running
1029 * @param tc scheduler context (unused)
1032 master_task (void *cls,
1033 const struct GNUNET_SCHEDULER_TaskContext *tc);
1037 * Function called when the 'DISCONNECT' message has been sent by the
1038 * plugin. Frees the neighbour --- if the entry still exists.
1041 * @param target identity of the neighbour that was disconnected
1042 * @param result #GNUNET_OK if the disconnect got out successfully
1043 * @param payload bytes payload
1044 * @param physical bytes physical
1047 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
1048 int result, size_t payload, size_t physical)
1050 struct NeighbourMapEntry *n;
1052 n = lookup_neighbour (target);
1054 return; /* already gone */
1055 if (S_DISCONNECT != n->state)
1056 return; /* have created a fresh entry since */
1057 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1058 GNUNET_SCHEDULER_cancel (n->task);
1059 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1064 * Transmit a DISCONNECT message to the other peer.
1066 * @param n neighbour to send DISCONNECT message.
1069 send_disconnect (struct NeighbourMapEntry *n)
1071 struct SessionDisconnectMessage disconnect_msg;
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "Sending DISCONNECT message to peer `%4s'\n",
1075 GNUNET_i2s (&n->id));
1076 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1077 disconnect_msg.header.type =
1078 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1079 disconnect_msg.reserved = htonl (0);
1080 disconnect_msg.purpose.size =
1081 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1082 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1083 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1084 disconnect_msg.purpose.purpose =
1085 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1086 disconnect_msg.timestamp =
1087 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1088 disconnect_msg.public_key = GST_my_identity.public_key;
1089 GNUNET_assert (GNUNET_OK ==
1090 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1091 &disconnect_msg.purpose,
1092 &disconnect_msg.signature));
1094 (void) send_with_session (n,
1095 (const char *) &disconnect_msg, sizeof (disconnect_msg),
1096 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1097 GNUNET_NO, &send_disconnect_cont, NULL);
1098 GNUNET_STATISTICS_update (GST_stats,
1100 ("# DISCONNECT messages sent"), 1,
1106 * Disconnect from the given neighbour, clean up the record.
1108 * @param n neighbour to disconnect from
1111 disconnect_neighbour (struct NeighbourMapEntry *n)
1113 /* depending on state, notify neighbour and/or upper layers of this peer
1117 case S_NOT_CONNECTED:
1119 case S_INIT_BLACKLIST:
1120 /* other peer is completely unaware of us, no need to send DISCONNECT */
1121 n->state = S_DISCONNECT_FINISHED;
1122 free_neighbour (n, GNUNET_NO);
1124 case S_CONNECT_SENT:
1125 send_disconnect (n);
1126 n->state = S_DISCONNECT;
1128 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1129 case S_CONNECT_RECV_ATS:
1130 case S_CONNECT_RECV_BLACKLIST:
1131 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1132 n->state = S_DISCONNECT_FINISHED;
1133 free_neighbour (n, GNUNET_NO);
1135 case S_CONNECT_RECV_ACK:
1136 /* we DID ACK the other peer's request, must send DISCONNECT */
1137 send_disconnect (n);
1138 n->state = S_DISCONNECT;
1141 case S_RECONNECT_BLACKLIST:
1142 case S_RECONNECT_SENT:
1143 case S_CONNECTED_SWITCHING_BLACKLIST:
1144 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1145 /* we are currently connected, need to send disconnect and do
1146 internal notifications and update statistics */
1147 send_disconnect (n);
1148 GNUNET_STATISTICS_set (GST_stats,
1149 gettext_noop ("# peers connected"),
1150 --neighbours_connected,
1152 disconnect_notify_cb (callback_cls, &n->id);
1153 n->state = S_DISCONNECT;
1155 case S_RECONNECT_ATS:
1156 /* ATS address request timeout, disconnect without sending disconnect message */
1157 GNUNET_STATISTICS_set (GST_stats,
1158 gettext_noop ("# peers connected"),
1159 --neighbours_connected,
1161 disconnect_notify_cb (callback_cls, &n->id);
1162 n->state = S_DISCONNECT;
1165 /* already disconnected, ignore */
1167 case S_DISCONNECT_FINISHED:
1168 /* already cleaned up, how did we get here!? */
1172 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1173 "Unhandled state `%s'\n",
1174 print_state (n->state));
1178 /* schedule timeout to clean up */
1179 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1180 GNUNET_SCHEDULER_cancel (n->task);
1181 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1187 * We're done with our transmission attempt, continue processing.
1189 * @param cls the `struct MessageQueue` of the message
1190 * @param receiver intended receiver
1191 * @param success whether it worked or not
1192 * @param size_payload bytes payload sent
1193 * @param physical bytes sent on wire
1196 transmit_send_continuation (void *cls,
1197 const struct GNUNET_PeerIdentity *receiver,
1198 int success, size_t size_payload, size_t physical)
1200 struct MessageQueue *mq = cls;
1201 struct NeighbourMapEntry *n;
1203 if (NULL == (n = lookup_neighbour (receiver)))
1206 return; /* disconnect or other error while transmitting, can happen */
1208 if (n->is_active == mq)
1210 /* this is still "our" neighbour, remove us from its queue
1211 and allow it to send the next message now */
1212 n->is_active = NULL;
1213 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1214 GNUNET_SCHEDULER_cancel (n->task);
1215 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1217 if (bytes_in_send_queue < mq->message_buf_size)
1219 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1220 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1221 bytes_in_send_queue, mq->message_buf_size,
1222 (GNUNET_OK == success) ? "OK" : "FAIL",
1223 size_payload, physical);
1228 GNUNET_break (size_payload == mq->message_buf_size);
1229 bytes_in_send_queue -= mq->message_buf_size;
1230 GNUNET_STATISTICS_set (GST_stats,
1232 ("# bytes in message queue for other peers"),
1233 bytes_in_send_queue, GNUNET_NO);
1234 if (GNUNET_OK == success)
1235 GNUNET_STATISTICS_update (GST_stats,
1237 ("# messages transmitted to other peers"),
1240 GNUNET_STATISTICS_update (GST_stats,
1242 ("# transmission failures for messages to other peers"),
1244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1245 "Sending message to `%s' of type %u was a %s\n",
1246 GNUNET_i2s (receiver),
1247 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1248 (success == GNUNET_OK) ? "success" : "FAILURE");
1249 if (NULL != mq->cont)
1250 mq->cont (mq->cont_cls, success, size_payload, physical);
1256 * Check the message list for the given neighbour and if we can
1257 * send a message, do so. This function should only be called
1258 * if the connection is at least generally ready for transmission.
1259 * While we will only send one message at a time, no bandwidth
1260 * quota management is performed here. If a message was given to
1261 * the plugin, the continuation will automatically re-schedule
1262 * the 'master' task once the next message might be transmitted.
1264 * @param n target peer for which to transmit
1267 try_transmission_to_peer (struct NeighbourMapEntry *n)
1269 struct MessageQueue *mq;
1270 struct GNUNET_TIME_Relative timeout;
1272 if (NULL == n->primary_address.address)
1274 /* no address, why are we here? */
1278 if ((0 == n->primary_address.address->address_length) &&
1279 (NULL == n->primary_address.session))
1281 /* no address, why are we here? */
1285 if (NULL != n->is_active)
1287 /* transmission already pending */
1291 /* timeout messages from the queue that are past their due date */
1292 while (NULL != (mq = n->messages_head))
1294 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1295 if (timeout.rel_value_us > 0)
1297 GNUNET_STATISTICS_update (GST_stats,
1299 ("# messages timed out while in transport queue"),
1301 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1303 transmit_send_continuation (mq, &n->id, GNUNET_SYSERR, mq->message_buf_size, 0); /* timeout */
1306 return; /* no more messages */
1307 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1309 (void) send_with_session (n,
1310 mq->message_buf, mq->message_buf_size,
1311 0 /* priority */, timeout, GNUNET_NO,
1312 &transmit_send_continuation, mq);
1317 * Send keepalive message to the neighbour. Must only be called
1318 * if we are on 'connected' state or while trying to switch addresses.
1319 * Will internally determine if a keepalive is truly needed (so can
1320 * always be called).
1322 * @param n neighbour that went idle and needs a keepalive
1325 send_keepalive (struct NeighbourMapEntry *n)
1327 struct SessionKeepAliveMessage m;
1328 struct GNUNET_TIME_Relative timeout;
1331 GNUNET_assert ((S_CONNECTED == n->state) ||
1332 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1333 (S_CONNECTED_SWITCHING_CONNECT_SENT));
1334 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1335 return; /* no keepalive needed at this time */
1337 nonce = 0; /* 0 indicates 'not set' */
1339 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1342 "Sending keep alive response to peer `%s' with nonce %u\n",
1343 GNUNET_i2s (&n->id), nonce);
1345 m.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1346 m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1347 m.nonce = htonl (nonce);
1349 timeout = send_with_session (n,
1350 (const void *) &m, sizeof (m),
1351 UINT32_MAX /* priority */,
1352 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1354 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1356 n->primary_address.keep_alive_nonce = nonce;
1357 n->expect_latency_response = GNUNET_YES;
1358 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1359 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1365 * Keep the connection to the given neighbour alive longer,
1366 * we received a KEEPALIVE (or equivalent); send a response.
1368 * @param neighbour neighbour to keep alive (by sending keep alive response)
1369 * @param m the keep alive message containing the nonce to respond to
1372 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1373 const struct GNUNET_MessageHeader *m)
1375 struct NeighbourMapEntry *n;
1376 const struct SessionKeepAliveMessage *msg_in;
1377 struct SessionKeepAliveMessage msg;
1379 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1382 msg_in = (struct SessionKeepAliveMessage *) m;
1383 if (NULL == (n = lookup_neighbour (neighbour)))
1385 GNUNET_STATISTICS_update (GST_stats,
1387 ("# KEEPALIVE messages discarded (peer unknown)"),
1391 if (NULL == n->primary_address.session)
1393 GNUNET_STATISTICS_update (GST_stats,
1395 ("# KEEPALIVE messages discarded (no session)"),
1400 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1401 "Received keep alive request from peer `%s' with nonce %u\n",
1402 GNUNET_i2s (&n->id), ntohl (msg_in->nonce));
1404 /* send reply to allow neighbour to measure latency */
1405 msg.header.size = htons (sizeof (struct SessionKeepAliveMessage));
1406 msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1407 msg.nonce = msg_in->nonce;
1408 (void) send_with_session(n,
1409 (const void *) &msg, sizeof (struct SessionKeepAliveMessage),
1410 UINT32_MAX /* priority */,
1411 GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES,
1417 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1418 * latency to this peer. Pass the updated information (existing ats
1419 * plus calculated latency) to ATS.
1421 * @param neighbour neighbour to keep alive
1422 * @param m the message containing the keep alive response
1425 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1426 const struct GNUNET_MessageHeader *m)
1428 struct NeighbourMapEntry *n;
1429 const struct SessionKeepAliveMessage *msg;
1430 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1432 struct GNUNET_ATS_Information ats;
1434 if (sizeof (struct SessionKeepAliveMessage) != ntohs (m->size))
1437 msg = (const struct SessionKeepAliveMessage *) m;
1438 if (NULL == (n = lookup_neighbour (neighbour)))
1440 GNUNET_STATISTICS_update (GST_stats,
1442 ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1446 if ( (S_CONNECTED != n->state) ||
1447 (GNUNET_YES != n->expect_latency_response) )
1449 GNUNET_STATISTICS_update (GST_stats,
1451 ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1455 if (NULL == n->primary_address.address)
1457 GNUNET_STATISTICS_update (GST_stats,
1459 ("# KEEPALIVE_RESPONSE messages discarded (address changed)"),
1463 if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1465 GNUNET_STATISTICS_update (GST_stats,
1467 ("# KEEPALIVE_RESPONSE messages discarded (wrong nonce)"),
1473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1474 "Received keep alive response from peer `%s' for session %p\n",
1475 GNUNET_i2s (&n->id), n->primary_address.session);
1479 /* Update session timeout here */
1480 if (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name)))
1482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1483 "Updating session for peer `%s' for session %p\n",
1484 GNUNET_i2s (&n->id), n->primary_address.session);
1485 papi->update_session_timeout (papi->cls, &n->id, n->primary_address.session);
1492 n->primary_address.keep_alive_nonce = 0;
1493 n->expect_latency_response = GNUNET_NO;
1494 n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1495 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1497 "Latency for peer `%s' is %s\n",
1498 GNUNET_i2s (&n->id),
1499 GNUNET_STRINGS_relative_time_to_string (n->latency,
1501 /* append latency */
1502 ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1503 if (n->latency.rel_value_us > UINT32_MAX)
1504 latency = UINT32_MAX;
1506 latency = n->latency.rel_value_us;
1507 ats.value = htonl (latency);
1508 GST_ats_update_metrics (&n->id, n->primary_address.address,
1509 n->primary_address.session, &ats, 1);
1514 * We have received a message from the given sender. How long should
1515 * we delay before receiving more? (Also used to keep the peer marked
1518 * @param sender sender of the message
1519 * @param size size of the message
1520 * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1521 * GNUNET_NO if the neighbour is not connected or violates the quota,
1522 * GNUNET_SYSERR if the connection is not fully up yet
1523 * @return how long to wait before reading more from this sender
1525 struct GNUNET_TIME_Relative
1526 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1527 *sender, ssize_t size, int *do_forward)
1529 struct NeighbourMapEntry *n;
1530 struct GNUNET_TIME_Relative ret;
1532 if (NULL == neighbours)
1534 *do_forward = GNUNET_NO;
1535 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1537 if (NULL == (n = lookup_neighbour (sender)))
1539 GST_neighbours_try_connect (sender);
1540 if (NULL == (n = lookup_neighbour (sender)))
1542 GNUNET_STATISTICS_update (GST_stats,
1544 ("# messages discarded due to lack of neighbour record"),
1546 *do_forward = GNUNET_NO;
1547 return GNUNET_TIME_UNIT_ZERO;
1550 if (! test_connected (n))
1552 *do_forward = GNUNET_SYSERR;
1553 return GNUNET_TIME_UNIT_ZERO;
1555 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1557 n->quota_violation_count++;
1558 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1559 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1560 n->in_tracker.available_bytes_per_s__,
1561 n->quota_violation_count);
1562 /* Discount 32k per violation */
1563 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1567 if (n->quota_violation_count > 0)
1569 /* try to add 32k back */
1570 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1571 n->quota_violation_count--;
1574 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1576 GNUNET_STATISTICS_update (GST_stats,
1578 ("# bandwidth quota violations by other peers"),
1580 *do_forward = GNUNET_NO;
1581 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1583 *do_forward = GNUNET_YES;
1584 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1585 if (ret.rel_value_us > 0)
1587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1588 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1589 (unsigned long long) n->in_tracker.
1590 consumption_since_last_update__,
1591 (unsigned int) n->in_tracker.available_bytes_per_s__,
1592 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1593 GNUNET_STATISTICS_update (GST_stats,
1594 gettext_noop ("# ms throttling suggested"),
1595 (int64_t) ret.rel_value_us / 1000LL,
1603 * Transmit a message to the given target using the active connection.
1605 * @param target destination
1606 * @param msg message to send
1607 * @param msg_size number of bytes in msg
1608 * @param timeout when to fail with timeout
1609 * @param cont function to call when done
1610 * @param cont_cls closure for 'cont'
1613 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1614 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1615 GST_NeighbourSendContinuation cont, void *cont_cls)
1617 struct NeighbourMapEntry *n;
1618 struct MessageQueue *mq;
1620 /* All ove these cases should never happen; they are all API violations.
1621 But we check anyway, just to be sure. */
1622 if (NULL == (n = lookup_neighbour (target)))
1626 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1629 if (GNUNET_YES != test_connected (n))
1633 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1636 bytes_in_send_queue += msg_size;
1637 GNUNET_STATISTICS_set (GST_stats,
1639 ("# bytes in message queue for other peers"),
1640 bytes_in_send_queue, GNUNET_NO);
1641 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1643 mq->cont_cls = cont_cls;
1644 memcpy (&mq[1], msg, msg_size);
1645 mq->message_buf = (const char *) &mq[1];
1646 mq->message_buf_size = msg_size;
1647 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1648 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1649 if ( (NULL != n->is_active) ||
1650 ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1652 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1653 GNUNET_SCHEDULER_cancel (n->task);
1654 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1659 * Send a SESSION_CONNECT message via the given address.
1661 * @param na address to use
1664 send_session_connect (struct NeighbourAddress *na)
1666 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1667 struct SessionConnectMessage connect_msg;
1669 if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1674 if (NULL == na->session)
1675 na->session = papi->get_session (papi->cls, na->address);
1676 if (NULL == na->session)
1681 GNUNET_STATISTICS_update (GST_stats,
1683 ("# SESSION_CONNECT messages sent"),
1685 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1686 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1687 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1688 connect_msg.reserved = htonl (0);
1689 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1691 papi->send (papi->cls,
1693 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1695 GNUNET_TIME_UNIT_FOREVER_REL,
1698 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1699 _("Failed to transmit CONNECT message via plugin to %s\n"),
1700 GST_plugins_a2s (na->address));
1702 GST_neighbours_notify_data_sent (&na->address->peer,
1705 sizeof (struct SessionConnectMessage));
1711 * Send a SESSION_CONNECT_ACK message via the given address.
1713 * @param address address to use
1714 * @param session session to use
1715 * @param timestamp timestamp to use for the ACK message
1718 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1719 struct Session *session,
1720 struct GNUNET_TIME_Absolute timestamp)
1722 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1723 struct SessionConnectMessage connect_msg;
1725 if (NULL == (papi = GST_plugins_find (address->transport_name)))
1730 if (NULL == session)
1731 session = papi->get_session (papi->cls, address);
1732 if (NULL == session)
1737 GNUNET_STATISTICS_update (GST_stats,
1739 ("# CONNECT_ACK messages sent"),
1741 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1742 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1743 connect_msg.reserved = htonl (0);
1744 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1745 (void) papi->send (papi->cls,
1747 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1749 GNUNET_TIME_UNIT_FOREVER_REL,
1756 * Create a fresh entry in the neighbour map for the given peer
1758 * @param peer peer to create an entry for
1759 * @return new neighbour map entry
1761 static struct NeighbourMapEntry *
1762 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1764 struct NeighbourMapEntry *n;
1766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1767 "Creating new neighbour entry for `%s'\n",
1769 n = GNUNET_new (struct NeighbourMapEntry);
1771 n->state = S_NOT_CONNECTED;
1772 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1773 n->last_util_transmission = GNUNET_TIME_absolute_get();
1774 n->util_payload_bytes_recv = 0;
1775 n->util_payload_bytes_sent = 0;
1776 n->util_total_bytes_recv = 0;
1777 n->util_total_bytes_sent = 0;
1778 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1779 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1780 MAX_BANDWIDTH_CARRY_S);
1781 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1782 GNUNET_assert (GNUNET_OK ==
1783 GNUNET_CONTAINER_multipeermap_put (neighbours,
1785 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1791 * Check if the two given addresses are the same.
1792 * Actually only checks if the sessions are non-NULL
1793 * (which they should be) and then if they are identical;
1794 * the actual addresses don't matter if the session
1795 * pointers match anyway, and we must have session pointers
1798 * @param a1 first address to compare
1799 * @param a2 other address to compare
1800 * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
1803 address_matches (const struct NeighbourAddress *a1,
1804 const struct NeighbourAddress *a2)
1806 if ( (NULL == a1->session) ||
1807 (NULL == a2->session) )
1812 return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1817 * Try to create a connection to the given target (eventually).
1819 * @param target peer to try to connect to
1822 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1824 struct NeighbourMapEntry *n;
1826 if (NULL == neighbours)
1828 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1829 "Asked to connect to peer `%s' during shutdown\n",
1830 GNUNET_i2s (target));
1831 return; /* during shutdown, do nothing */
1833 n = lookup_neighbour (target);
1834 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1835 "Asked to connect to peer `%s' (state: %s)\n",
1836 GNUNET_i2s (target),
1837 (NULL != n) ? print_state(n->state) : "NEW PEER");
1842 case S_NOT_CONNECTED:
1843 /* this should not be possible */
1845 free_neighbour (n, GNUNET_NO);
1848 case S_INIT_BLACKLIST:
1849 case S_CONNECT_SENT:
1850 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1851 case S_CONNECT_RECV_ATS:
1852 case S_CONNECT_RECV_BLACKLIST:
1853 case S_CONNECT_RECV_ACK:
1854 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1855 "Ignoring request to try to connect to `%s', already trying!\n",
1856 GNUNET_i2s (target));
1857 return; /* already trying */
1859 case S_RECONNECT_ATS:
1860 case S_RECONNECT_BLACKLIST:
1861 case S_RECONNECT_SENT:
1862 case S_CONNECTED_SWITCHING_BLACKLIST:
1863 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1864 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1865 "Ignoring request to try to connect, already connected to `%s'!\n",
1866 GNUNET_i2s (target));
1867 return; /* already connected */
1869 /* get rid of remains, ready to re-try immediately */
1870 free_neighbour (n, GNUNET_NO);
1872 case S_DISCONNECT_FINISHED:
1873 /* should not be possible */
1876 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1877 "Unhandled state `%s'\n",
1878 print_state (n->state));
1880 free_neighbour (n, GNUNET_NO);
1884 n = setup_neighbour (target);
1885 n->state = S_INIT_ATS;
1886 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1888 GNUNET_ATS_reset_backoff (GST_ats, target);
1889 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1894 * Function called with the result of a blacklist check.
1896 * @param cls closure with the 'struct BlackListCheckContext'
1897 * @param peer peer this check affects
1898 * @param result #GNUNET_OK if the address is allowed
1901 handle_test_blacklist_cont (void *cls,
1902 const struct GNUNET_PeerIdentity *peer,
1905 struct BlackListCheckContext *bcc = cls;
1906 struct NeighbourMapEntry *n;
1909 GNUNET_CONTAINER_DLL_remove (bc_head,
1912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913 "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1915 (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1916 if (GNUNET_OK == result)
1917 GST_ats_add_address (bcc->na.address, bcc->na.session, NULL, 0);
1920 /* Blacklist disagreed on connecting to a peer with this address
1921 * Destroy address because we are not allowed to use it
1923 if (NULL != bcc->na.session)
1924 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, bcc->na.session);
1925 GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, NULL);
1927 if (NULL == (n = lookup_neighbour (peer)))
1928 goto cleanup; /* nobody left to care about new address */
1931 case S_NOT_CONNECTED:
1932 /* this should not be possible */
1934 free_neighbour (n, GNUNET_NO);
1937 /* waiting on ATS suggestion; still, pass address to ATS as a
1940 case S_INIT_BLACKLIST:
1941 /* check if the address the blacklist was fine with matches
1942 ATS suggestion, if so, we can move on! */
1943 if ( (GNUNET_OK == result) &&
1944 (1 == n->send_connect_ack) )
1946 n->send_connect_ack = 2;
1947 send_session_connect_ack_message (bcc->na.address,
1949 n->connect_ack_timestamp);
1951 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1952 break; /* result for an address we currently don't care about */
1953 if (GNUNET_OK == result)
1955 n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1956 n->state = S_CONNECT_SENT;
1957 send_session_connect (&n->primary_address);
1961 free_address (&n->primary_address);
1962 n->state = S_INIT_ATS;
1963 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1966 case S_CONNECT_SENT:
1967 /* waiting on CONNECT_ACK, send ACK if one is pending */
1968 if ( (GNUNET_OK == result) &&
1969 (1 == n->send_connect_ack) )
1971 n->send_connect_ack = 2;
1972 send_session_connect_ack_message (n->primary_address.address,
1973 n->primary_address.session,
1974 n->connect_ack_timestamp);
1977 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1978 n->state = S_CONNECT_RECV_ATS;
1979 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1980 GNUNET_ATS_reset_backoff (GST_ats, peer);
1981 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
1983 case S_CONNECT_RECV_ATS:
1984 /* waiting on ATS suggestion, don't care about blacklist */
1986 case S_CONNECT_RECV_BLACKLIST:
1987 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1990 "Blacklist result ignored, as it is not for our primary address\n");
1991 break; /* result for an address we currently don't care about */
1993 if (GNUNET_OK == result)
1995 n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1996 n->state = S_CONNECT_RECV_ACK;
1997 send_session_connect_ack_message (bcc->na.address,
1999 n->connect_ack_timestamp);
2000 if (1 == n->send_connect_ack)
2001 n->send_connect_ack = 2;
2005 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
2007 plugin = GST_plugins_find (bcc->na.address->transport_name);
2008 if ( (NULL != plugin) &&
2009 (NULL != bcc->na.session) )
2011 plugin->disconnect_session (plugin->cls,
2015 GNUNET_break (NULL != plugin);
2016 free_address (&n->primary_address);
2017 n->state = S_INIT_ATS;
2018 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2019 GNUNET_ATS_reset_backoff (GST_ats, peer);
2022 case S_CONNECT_RECV_ACK:
2023 /* waiting on SESSION_ACK, send ACK if one is pending */
2024 if ( (GNUNET_OK == result) &&
2025 (1 == n->send_connect_ack) )
2027 n->send_connect_ack = 2;
2028 send_session_connect_ack_message (n->primary_address.address,
2029 n->primary_address.session,
2030 n->connect_ack_timestamp);
2034 /* already connected, don't care about blacklist */
2036 case S_RECONNECT_ATS:
2037 /* still waiting on ATS suggestion, don't care about blacklist */
2039 case S_RECONNECT_BLACKLIST:
2040 if ( (GNUNET_OK == result) &&
2041 (1 == n->send_connect_ack) )
2043 n->send_connect_ack = 2;
2044 send_session_connect_ack_message (bcc->na.address,
2046 n->connect_ack_timestamp);
2048 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2049 break; /* result for an address we currently don't care about */
2050 if (GNUNET_OK == result)
2052 n->state = S_RECONNECT_SENT;
2053 send_session_connect (&n->primary_address);
2054 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2058 n->state = S_RECONNECT_ATS;
2059 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2062 case S_RECONNECT_SENT:
2063 /* waiting on CONNECT_ACK, don't care about blacklist */
2064 if ( (GNUNET_OK == result) &&
2065 (1 == n->send_connect_ack) )
2067 n->send_connect_ack = 2;
2068 send_session_connect_ack_message (n->primary_address.address,
2069 n->primary_address.session,
2070 n->connect_ack_timestamp);
2073 case S_CONNECTED_SWITCHING_BLACKLIST:
2074 if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2075 break; /* result for an address we currently don't care about */
2076 if (GNUNET_OK == result)
2078 send_session_connect (&n->alternative_address);
2079 n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
2083 n->state = S_CONNECTED;
2084 free_address (&n->alternative_address);
2087 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2088 /* waiting on CONNECT_ACK, don't care about blacklist */
2089 if ( (GNUNET_OK == result) &&
2090 (1 == n->send_connect_ack) )
2092 n->send_connect_ack = 2;
2093 send_session_connect_ack_message (n->primary_address.address,
2094 n->primary_address.session,
2095 n->connect_ack_timestamp);
2099 /* Nothing to do here, ATS will already do what can be done */
2101 case S_DISCONNECT_FINISHED:
2102 /* should not be possible */
2106 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2107 "Unhandled state `%s'\n",
2108 print_state (n->state));
2110 free_neighbour (n, GNUNET_NO);
2114 GNUNET_HELLO_address_free (bcc->na.address);
2120 * We want to know if connecting to a particular peer via
2121 * a particular address is allowed. Check it!
2123 * @param peer identity of the peer to switch the address for
2124 * @param ts time at which the check was initiated
2125 * @param address address of the other peer, NULL if other peer
2127 * @param session session to use (or NULL)
2130 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2131 struct GNUNET_TIME_Absolute ts,
2132 const struct GNUNET_HELLO_Address *address,
2133 struct Session *session)
2135 struct BlackListCheckContext *bcc;
2136 struct GST_BlacklistCheck *bc;
2138 bcc = GNUNET_new (struct BlackListCheckContext);
2139 bcc->na.address = GNUNET_HELLO_address_copy (address);
2140 bcc->na.session = session;
2141 bcc->na.connect_timestamp = ts;
2142 GNUNET_CONTAINER_DLL_insert (bc_head,
2145 if (NULL != (bc = GST_blacklist_test_allowed (peer,
2146 address->transport_name,
2147 &handle_test_blacklist_cont, bcc)))
2149 /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2150 we must only store 'bc' if 'bc' is non-NULL... */
2155 * We received a 'SESSION_CONNECT' message from the other peer.
2156 * Consider switching to it.
2158 * @param message possibly a 'struct SessionConnectMessage' (check format)
2159 * @param peer identity of the peer to switch the address for
2160 * @param address address of the other peer, NULL if other peer
2162 * @param session session to use (or NULL)
2163 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2166 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2167 const struct GNUNET_PeerIdentity *peer,
2168 const struct GNUNET_HELLO_Address *address,
2169 struct Session *session)
2171 const struct SessionConnectMessage *scm;
2172 struct NeighbourMapEntry *n;
2173 struct GNUNET_TIME_Absolute ts;
2175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2176 "Received CONNECT message from peer `%s'\n",
2178 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2180 GNUNET_break_op (0);
2181 return GNUNET_SYSERR;
2183 GNUNET_STATISTICS_update (GST_stats,
2185 ("# CONNECT messages received"),
2187 if (NULL == neighbours)
2189 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2190 _("CONNECT request from peer `%s' ignored due impending shutdown\n"),
2192 return GNUNET_OK; /* we're shutting down */
2194 scm = (const struct SessionConnectMessage *) message;
2195 GNUNET_break_op (0 == ntohl (scm->reserved));
2196 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2197 n = lookup_neighbour (peer);
2199 n = setup_neighbour (peer);
2200 n->send_connect_ack = 1;
2201 n->connect_ack_timestamp = ts;
2205 case S_NOT_CONNECTED:
2206 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2207 /* Do a blacklist check for the new address */
2208 check_blacklist (peer, ts, address, session);
2211 /* CONNECT message takes priority over us asking ATS for address */
2212 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2214 case S_INIT_BLACKLIST:
2215 case S_CONNECT_SENT:
2216 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2217 case S_CONNECT_RECV_ATS:
2218 case S_CONNECT_RECV_BLACKLIST:
2219 case S_CONNECT_RECV_ACK:
2220 /* It can never hurt to have an alternative address in the above cases,
2221 see if it is allowed */
2222 check_blacklist (peer, ts, address, session);
2225 /* we are already connected and can thus send the ACK immediately;
2226 still, it can never hurt to have an alternative address, so also
2227 tell ATS about it */
2228 GNUNET_assert (NULL != n->primary_address.address);
2229 GNUNET_assert (NULL != n->primary_address.session);
2230 n->send_connect_ack = 0;
2231 send_session_connect_ack_message (n->primary_address.address,
2232 n->primary_address.session, ts);
2233 check_blacklist (peer, ts, address, session);
2235 case S_RECONNECT_ATS:
2236 case S_RECONNECT_BLACKLIST:
2237 case S_RECONNECT_SENT:
2238 /* It can never hurt to have an alternative address in the above cases,
2239 see if it is allowed */
2240 check_blacklist (peer, ts, address, session);
2242 case S_CONNECTED_SWITCHING_BLACKLIST:
2243 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2244 /* we are already connected and can thus send the ACK immediately;
2245 still, it can never hurt to have an alternative address, so also
2246 tell ATS about it */
2247 GNUNET_assert (NULL != n->primary_address.address);
2248 GNUNET_assert (NULL != n->primary_address.session);
2249 n->send_connect_ack = 0;
2250 send_session_connect_ack_message (n->primary_address.address,
2251 n->primary_address.session, ts);
2252 check_blacklist (peer, ts, address, session);
2255 /* get rid of remains without terminating sessions, ready to re-try */
2256 free_neighbour (n, GNUNET_YES);
2257 n = setup_neighbour (peer);
2258 n->state = S_CONNECT_RECV_ATS;
2259 GNUNET_ATS_reset_backoff (GST_ats, peer);
2260 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2262 case S_DISCONNECT_FINISHED:
2263 /* should not be possible */
2267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2268 "Unhandled state `%s'\n",
2269 print_state (n->state));
2271 return GNUNET_SYSERR;
2278 * For an existing neighbour record, set the active connection to
2279 * use the given address.
2281 * @param peer identity of the peer to switch the address for
2282 * @param address address of the other peer, NULL if other peer
2284 * @param session session to use (or NULL)
2285 * @param ats performance data
2286 * @param ats_count number of entries in ats
2287 * @param bandwidth_in inbound quota to be used when connection is up,
2288 * 0 to disconnect from peer
2289 * @param bandwidth_out outbound quota to be used when connection is up,
2290 * 0 to disconnect from peer
2293 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2294 const struct GNUNET_HELLO_Address *address,
2295 struct Session *session,
2296 const struct GNUNET_ATS_Information *ats,
2298 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2299 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2301 struct NeighbourMapEntry *n;
2302 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2304 GNUNET_assert (address->transport_name != NULL);
2305 if (NULL == (n = lookup_neighbour (peer)))
2308 /* Obtain an session for this address from plugin */
2309 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2311 /* we don't have the plugin for this address */
2312 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2315 if ((NULL == session) && (0 == address->address_length))
2318 if (strlen (address->transport_name) > 0)
2319 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2323 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2324 "ATS tells us to switch to address '%s' session %p for "
2325 "peer `%s' in state %s/%d (quota in/out %u %u )\n",
2326 (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2329 print_state (n->state),
2330 n->send_connect_ack,
2331 ntohl (bandwidth_in.value__),
2332 ntohl (bandwidth_out.value__));
2334 if (NULL == session)
2336 session = papi->get_session (papi->cls, address);
2337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2338 "Obtained new session for peer `%s' and address '%s': %p\n",
2339 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2341 if (NULL == session)
2343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2344 "Failed to obtain new session for peer `%s' and address '%s'\n",
2345 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
2346 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2351 case S_NOT_CONNECTED:
2353 free_neighbour (n, GNUNET_NO);
2356 set_address (&n->primary_address,
2357 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2358 n->state = S_INIT_BLACKLIST;
2359 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2360 check_blacklist (&n->id,
2361 n->connect_ack_timestamp,
2364 case S_INIT_BLACKLIST:
2365 /* ATS suggests a different address, switch again */
2366 set_address (&n->primary_address,
2367 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2368 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2369 check_blacklist (&n->id,
2370 n->connect_ack_timestamp,
2373 case S_CONNECT_SENT:
2374 /* ATS suggests a different address, switch again */
2375 set_address (&n->primary_address,
2376 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2377 n->state = S_INIT_BLACKLIST;
2378 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2379 check_blacklist (&n->id,
2380 n->connect_ack_timestamp,
2383 case S_CONNECT_RECV_ATS:
2384 set_address (&n->primary_address,
2385 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2386 n->state = S_CONNECT_RECV_BLACKLIST;
2387 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2388 check_blacklist (&n->id,
2389 n->connect_ack_timestamp,
2392 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2393 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2394 check_blacklist (&n->id,
2395 n->connect_ack_timestamp,
2398 case S_CONNECT_RECV_BLACKLIST:
2399 case S_CONNECT_RECV_ACK:
2400 /* ATS asks us to switch while we were trying to connect; switch to new
2401 address and check blacklist again */
2402 set_address (&n->primary_address,
2403 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2404 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2405 check_blacklist (&n->id,
2406 n->connect_ack_timestamp,
2410 GNUNET_assert (NULL != n->primary_address.address);
2411 GNUNET_assert (NULL != n->primary_address.session);
2412 if (n->primary_address.session == session)
2414 /* not an address change, just a quota change */
2415 set_address (&n->primary_address,
2416 address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2419 /* ATS asks us to switch a life connection; see if we can get
2420 a CONNECT_ACK on it before we actually do this! */
2421 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2422 set_address (&n->alternative_address,
2423 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2424 check_blacklist (&n->id,
2425 GNUNET_TIME_absolute_get (),
2428 case S_RECONNECT_ATS:
2429 n->state = S_RECONNECT_BLACKLIST;
2430 set_address (&n->primary_address,
2431 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2432 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2433 check_blacklist (&n->id,
2434 n->connect_ack_timestamp,
2437 case S_RECONNECT_BLACKLIST:
2438 /* ATS asks us to switch while we were trying to reconnect; switch to new
2439 address and check blacklist again */
2440 set_address (&n->primary_address,
2441 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2442 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2443 check_blacklist (&n->id,
2444 n->connect_ack_timestamp,
2447 case S_RECONNECT_SENT:
2448 /* ATS asks us to switch while we were trying to reconnect; switch to new
2449 address and check blacklist again */
2450 n->state = S_RECONNECT_BLACKLIST;
2451 set_address (&n->primary_address,
2452 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2453 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2454 check_blacklist (&n->id,
2455 n->connect_ack_timestamp,
2458 case S_CONNECTED_SWITCHING_BLACKLIST:
2459 if (n->primary_address.session == session)
2461 /* ATS switches back to still-active session */
2462 n->state = S_CONNECTED;
2463 free_address (&n->alternative_address);
2466 /* ATS asks us to switch a life connection, update blacklist check */
2467 set_address (&n->alternative_address,
2468 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2469 check_blacklist (&n->id,
2470 GNUNET_TIME_absolute_get (),
2473 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2474 if (n->primary_address.session == session)
2476 /* ATS switches back to still-active session */
2477 free_address (&n->alternative_address);
2478 n->state = S_CONNECTED;
2481 /* ATS asks us to switch a life connection, update blacklist check */
2482 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2483 set_address (&n->alternative_address,
2484 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2485 check_blacklist (&n->id,
2486 GNUNET_TIME_absolute_get (),
2490 /* not going to switch addresses while disconnecting */
2492 case S_DISCONNECT_FINISHED:
2496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2497 "Unhandled state `%s'\n",
2498 print_state (n->state));
2506 send_utilization_data (void *cls,
2507 const struct GNUNET_PeerIdentity *key,
2510 struct NeighbourMapEntry *n = value;
2511 struct GNUNET_ATS_Information atsi[4];
2513 uint32_t bps_pl_out;
2516 struct GNUNET_TIME_Relative delta;
2518 delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2519 GNUNET_TIME_absolute_get ());
2523 if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2524 bps_pl_in = (1000LL * 1000LL * n->util_payload_bytes_recv) / (delta.rel_value_us);
2526 if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2527 bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2528 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2529 "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2534 if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2535 bps_in = (1000LL * 1000LL * n->util_total_bytes_recv) / (delta.rel_value_us);
2537 if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2538 bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2542 "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2546 atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2547 atsi[0].value = htonl (bps_out);
2548 atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2549 atsi[1].value = htonl (bps_in);
2551 atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2552 atsi[2].value = htonl (bps_pl_out);
2553 atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2554 atsi[3].value = htonl (bps_pl_in);
2556 GST_ats_update_metrics (key, n->primary_address.address,
2557 n->primary_address.session, atsi, 4);
2558 n->util_payload_bytes_recv = 0;
2559 n->util_payload_bytes_sent = 0;
2560 n->util_total_bytes_recv = 0;
2561 n->util_total_bytes_sent = 0;
2562 n->last_util_transmission = GNUNET_TIME_absolute_get();
2568 * Task transmitting utilization in a regular interval
2570 * @param cls the 'struct NeighbourMapEntry' for which we are running
2571 * @param tc scheduler context (unused)
2574 utilization_transmission (void *cls,
2575 const struct GNUNET_SCHEDULER_TaskContext *tc)
2577 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2579 if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2580 GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2582 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2583 utilization_transmission, NULL);
2589 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2590 const struct GNUNET_HELLO_Address *address,
2591 struct Session *session,
2592 const struct GNUNET_MessageHeader *message)
2594 struct NeighbourMapEntry *n;
2596 n = lookup_neighbour (peer);
2599 n->util_total_bytes_recv += ntohs(message->size);
2604 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2605 const struct GNUNET_HELLO_Address *address,
2606 struct Session *session,
2607 const struct GNUNET_MessageHeader *message)
2609 struct NeighbourMapEntry *n;
2610 n = lookup_neighbour (peer);
2613 n->util_payload_bytes_recv += ntohs(message->size);
2618 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2619 const struct GNUNET_HELLO_Address *address,
2620 struct Session *session,
2623 struct NeighbourMapEntry *n;
2624 n = lookup_neighbour (peer);
2627 if (n->primary_address.session != session)
2629 n->util_total_bytes_sent += size;
2634 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2637 struct NeighbourMapEntry *n;
2638 n = lookup_neighbour (peer);
2641 n->util_payload_bytes_sent += size;
2646 * Master task run for every neighbour. Performs all of the time-related
2647 * activities (keep alive, send next message, disconnect if idle, finish
2648 * clean up after disconnect).
2650 * @param cls the 'struct NeighbourMapEntry' for which we are running
2651 * @param tc scheduler context (unused)
2654 master_task (void *cls,
2655 const struct GNUNET_SCHEDULER_TaskContext *tc)
2657 struct NeighbourMapEntry *n = cls;
2658 struct GNUNET_TIME_Relative delay;
2660 n->task = GNUNET_SCHEDULER_NO_TASK;
2661 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2662 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2663 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2664 GNUNET_i2s (&n->id),
2665 print_state(n->state),
2666 GNUNET_STRINGS_relative_time_to_string (delay,
2670 case S_NOT_CONNECTED:
2671 /* invalid state for master task, clean up */
2673 n->state = S_DISCONNECT_FINISHED;
2674 free_neighbour (n, GNUNET_NO);
2677 if (0 == delay.rel_value_us)
2679 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2680 "Connection to `%s' timed out waiting for ATS to provide address\n",
2681 GNUNET_i2s (&n->id));
2682 n->state = S_DISCONNECT_FINISHED;
2683 free_neighbour (n, GNUNET_NO);
2687 case S_INIT_BLACKLIST:
2688 if (0 == delay.rel_value_us)
2690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2692 GNUNET_i2s (&n->id));
2693 n->state = S_DISCONNECT_FINISHED;
2694 free_neighbour (n, GNUNET_NO);
2698 case S_CONNECT_SENT:
2699 if (0 == delay.rel_value_us)
2701 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2702 "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2703 GNUNET_i2s (&n->id));
2704 /* We could not send to this address, delete address and session */
2705 if (NULL != n->primary_address.session)
2706 GNUNET_ATS_address_destroyed (GST_ats,
2707 n->primary_address.address, n->primary_address.session);
2708 GNUNET_ATS_address_destroyed (GST_ats,
2709 n->primary_address.address, NULL);
2710 disconnect_neighbour (n);
2714 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2715 if (0 == delay.rel_value_us)
2717 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2718 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2719 GNUNET_i2s (&n->id));
2720 n->state = S_DISCONNECT_FINISHED;
2721 free_neighbour (n, GNUNET_NO);
2725 case S_CONNECT_RECV_ATS:
2726 if (0 == delay.rel_value_us)
2728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2729 "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2730 GNUNET_i2s (&n->id));
2731 n->state = S_DISCONNECT_FINISHED;
2732 free_neighbour (n, GNUNET_NO);
2736 case S_CONNECT_RECV_BLACKLIST:
2737 if (0 == delay.rel_value_us)
2739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2740 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2741 GNUNET_i2s (&n->id));
2742 n->state = S_DISCONNECT_FINISHED;
2743 free_neighbour (n, GNUNET_NO);
2747 case S_CONNECT_RECV_ACK:
2748 if (0 == delay.rel_value_us)
2750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2751 "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2752 GNUNET_i2s (&n->id));
2753 disconnect_neighbour (n);
2758 if (0 == delay.rel_value_us)
2760 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2761 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2762 GNUNET_i2s (&n->id));
2763 disconnect_neighbour (n);
2766 try_transmission_to_peer (n);
2769 case S_RECONNECT_ATS:
2770 if (0 == delay.rel_value_us)
2772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2773 "Connection to `%s' timed out, waiting for ATS replacement address\n",
2774 GNUNET_i2s (&n->id));
2775 disconnect_neighbour (n);
2779 case S_RECONNECT_BLACKLIST:
2780 if (0 == delay.rel_value_us)
2782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2783 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2784 GNUNET_i2s (&n->id));
2785 disconnect_neighbour (n);
2789 case S_RECONNECT_SENT:
2790 if (0 == delay.rel_value_us)
2792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2793 "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2794 GNUNET_i2s (&n->id));
2795 disconnect_neighbour (n);
2799 case S_CONNECTED_SWITCHING_BLACKLIST:
2800 if (0 == delay.rel_value_us)
2802 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2803 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2804 GNUNET_i2s (&n->id));
2805 disconnect_neighbour (n);
2808 try_transmission_to_peer (n);
2811 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2812 if (0 == delay.rel_value_us)
2814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2815 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2816 GNUNET_i2s (&n->id));
2817 disconnect_neighbour (n);
2820 try_transmission_to_peer (n);
2824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2825 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2826 GNUNET_i2s (&n->id));
2827 free_neighbour (n, GNUNET_NO);
2829 case S_DISCONNECT_FINISHED:
2830 /* how did we get here!? */
2834 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2835 "Unhandled state `%s'\n",
2836 print_state (n->state));
2840 if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2841 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2842 (S_CONNECTED == n->state) )
2844 /* if we are *now* in one of these three states, we're sending
2845 keep alive messages, so we need to consider the keepalive
2846 delay, not just the connection timeout */
2847 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2850 if (GNUNET_SCHEDULER_NO_TASK == n->task)
2851 n->task = GNUNET_SCHEDULER_add_delayed (delay,
2858 * Send a SESSION_ACK message to the neighbour to confirm that we
2859 * got his CONNECT_ACK.
2861 * @param n neighbour to send the SESSION_ACK to
2864 send_session_ack_message (struct NeighbourMapEntry *n)
2866 struct GNUNET_MessageHeader msg;
2868 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2869 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2870 (void) send_with_session(n,
2871 (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2872 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_NO,
2878 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2879 * Consider switching to it.
2881 * @param message possibly a 'struct SessionConnectMessage' (check format)
2882 * @param peer identity of the peer to switch the address for
2883 * @param address address of the other peer, NULL if other peer
2885 * @param session session to use (or NULL)
2886 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2889 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2890 const struct GNUNET_PeerIdentity *peer,
2891 const struct GNUNET_HELLO_Address *address,
2892 struct Session *session)
2894 const struct SessionConnectMessage *scm;
2895 struct GNUNET_TIME_Absolute ts;
2896 struct NeighbourMapEntry *n;
2898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2899 "Received CONNECT_ACK message from peer `%s'\n",
2902 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2904 GNUNET_break_op (0);
2905 return GNUNET_SYSERR;
2907 GNUNET_STATISTICS_update (GST_stats,
2909 ("# CONNECT_ACK messages received"),
2911 scm = (const struct SessionConnectMessage *) message;
2912 GNUNET_break_op (ntohl (scm->reserved) == 0);
2913 if (NULL == (n = lookup_neighbour (peer)))
2915 GNUNET_STATISTICS_update (GST_stats,
2917 ("# unexpected CONNECT_ACK messages (no peer)"),
2919 return GNUNET_SYSERR;
2921 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2924 case S_NOT_CONNECTED:
2926 free_neighbour (n, GNUNET_NO);
2927 return GNUNET_SYSERR;
2929 case S_INIT_BLACKLIST:
2930 GNUNET_STATISTICS_update (GST_stats,
2932 ("# unexpected CONNECT_ACK messages (not ready)"),
2935 case S_CONNECT_SENT:
2936 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
2938 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2939 "CONNECT_ACK ignored as the timestamp does not match our CONNECT request\n");
2942 n->state = S_CONNECTED;
2943 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2944 GNUNET_STATISTICS_set (GST_stats,
2945 gettext_noop ("# peers connected"),
2946 ++neighbours_connected,
2948 connect_notify_cb (callback_cls, &n->id,
2949 n->primary_address.bandwidth_in,
2950 n->primary_address.bandwidth_out);
2951 /* Tell ATS that the outbound session we created to send CONNECT was successful */
2952 GST_ats_add_address (n->primary_address.address,
2953 n->primary_address.session,
2955 set_address (&n->primary_address,
2956 n->primary_address.address,
2957 n->primary_address.session,
2958 n->primary_address.bandwidth_in,
2959 n->primary_address.bandwidth_out,
2961 send_session_ack_message (n);
2963 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2964 case S_CONNECT_RECV_ATS:
2965 case S_CONNECT_RECV_BLACKLIST:
2966 case S_CONNECT_RECV_ACK:
2967 GNUNET_STATISTICS_update (GST_stats,
2969 ("# unexpected CONNECT_ACK messages (not ready)"),
2973 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2974 send_session_ack_message (n);
2976 case S_RECONNECT_ATS:
2977 case S_RECONNECT_BLACKLIST:
2978 /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2979 to give us a new address... */
2980 GNUNET_STATISTICS_update (GST_stats,
2982 ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2985 case S_RECONNECT_SENT:
2986 /* new address worked; go back to connected! */
2987 n->state = S_CONNECTED;
2988 send_session_ack_message (n);
2990 case S_CONNECTED_SWITCHING_BLACKLIST:
2991 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2992 send_session_ack_message (n);
2994 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2995 /* new address worked; adopt it and go back to connected! */
2996 n->state = S_CONNECTED;
2997 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2998 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3000 GST_ats_add_address (n->alternative_address.address,
3001 n->alternative_address.session,
3003 set_address (&n->primary_address,
3004 n->alternative_address.address,
3005 n->alternative_address.session,
3006 n->alternative_address.bandwidth_in,
3007 n->alternative_address.bandwidth_out,
3009 free_address (&n->alternative_address);
3010 send_session_ack_message (n);
3013 GNUNET_STATISTICS_update (GST_stats,
3015 ("# unexpected CONNECT_ACK messages (disconnecting)"),
3017 return GNUNET_SYSERR;
3018 case S_DISCONNECT_FINISHED:
3022 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3023 "Unhandled state `%s'\n",
3024 print_state (n->state));
3026 return GNUNET_SYSERR;
3033 * A session was terminated. Take note; if needed, try to get
3034 * an alternative address from ATS.
3036 * @param peer identity of the peer where the session died
3037 * @param session session that is gone
3038 * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3039 * this session was not in use
3042 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3043 struct Session *session)
3045 struct NeighbourMapEntry *n;
3046 struct BlackListCheckContext *bcc;
3047 struct BlackListCheckContext *bcc_next;
3049 /* make sure to cancel all ongoing blacklist checks involving 'session' */
3051 while (NULL != (bcc = bcc_next))
3053 bcc_next = bcc->next;
3054 if (bcc->na.session == session)
3056 if (NULL != bcc->bc)
3057 GST_blacklist_test_cancel (bcc->bc);
3058 GNUNET_HELLO_address_free (bcc->na.address);
3059 GNUNET_CONTAINER_DLL_remove (bc_head,
3065 if (NULL == (n = lookup_neighbour (peer)))
3066 return GNUNET_NO; /* can't affect us */
3067 if (session != n->primary_address.session)
3069 if (session == n->alternative_address.session)
3071 if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3072 (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
3073 n->state = S_CONNECTED;
3076 free_address (&n->alternative_address);
3078 return GNUNET_NO; /* doesn't affect us further */
3081 n->expect_latency_response = GNUNET_NO;
3084 case S_NOT_CONNECTED:
3086 free_neighbour (n, GNUNET_NO);
3090 free_neighbour (n, GNUNET_NO);
3092 case S_INIT_BLACKLIST:
3093 case S_CONNECT_SENT:
3094 free_address (&n->primary_address);
3095 n->state = S_INIT_ATS;
3096 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3097 // FIXME: need to ask ATS for suggestions again?
3098 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3100 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3101 case S_CONNECT_RECV_ATS:
3102 case S_CONNECT_RECV_BLACKLIST:
3103 case S_CONNECT_RECV_ACK:
3104 /* error on inbound session; free neighbour entirely */
3105 free_address (&n->primary_address);
3106 free_neighbour (n, GNUNET_NO);
3109 n->state = S_RECONNECT_ATS;
3110 free_address (&n->primary_address);
3111 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3112 /* FIXME: is this ATS call needed? */
3113 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3115 case S_RECONNECT_ATS:
3116 /* we don't have an address, how can it go down? */
3119 case S_RECONNECT_BLACKLIST:
3120 case S_RECONNECT_SENT:
3121 n->state = S_RECONNECT_ATS;
3122 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3123 // FIXME: need to ask ATS for suggestions again?
3124 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3126 case S_CONNECTED_SWITCHING_BLACKLIST:
3127 /* primary went down while we were checking secondary against
3128 blacklist, adopt secondary as primary */
3129 n->state = S_RECONNECT_BLACKLIST;
3130 free_address (&n->primary_address);
3131 n->primary_address = n->alternative_address;
3132 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3133 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3135 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3136 /* primary went down while we were waiting for CONNECT_ACK on secondary;
3137 secondary as primary */
3138 n->state = S_RECONNECT_SENT;
3139 free_address (&n->primary_address);
3140 n->primary_address = n->alternative_address;
3141 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3142 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3145 free_address (&n->primary_address);
3147 case S_DISCONNECT_FINISHED:
3148 /* neighbour was freed and plugins told to terminate session */
3152 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3153 "Unhandled state `%s'\n",
3154 print_state (n->state));
3158 if (GNUNET_SCHEDULER_NO_TASK != n->task)
3159 GNUNET_SCHEDULER_cancel (n->task);
3160 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3166 * We received a 'SESSION_ACK' message from the other peer.
3167 * If we sent a 'CONNECT_ACK' last, this means we are now
3168 * connected. Otherwise, do nothing.
3170 * @param message possibly a 'struct SessionConnectMessage' (check format)
3171 * @param peer identity of the peer to switch the address for
3172 * @param address address of the other peer, NULL if other peer
3174 * @param session session to use (or NULL)
3175 * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3178 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3179 const struct GNUNET_PeerIdentity *peer,
3180 const struct GNUNET_HELLO_Address *address,
3181 struct Session *session)
3183 struct NeighbourMapEntry *n;
3185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3186 "Received SESSION_ACK message from peer `%s'\n",
3188 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3190 GNUNET_break_op (0);
3191 return GNUNET_SYSERR;
3193 GNUNET_STATISTICS_update (GST_stats,
3195 ("# SESSION_ACK messages received"),
3197 if (NULL == (n = lookup_neighbour (peer)))
3199 GNUNET_break_op (0);
3200 return GNUNET_SYSERR;
3202 /* check if we are in a plausible state for having sent
3203 a CONNECT_ACK. If not, return, otherwise break */
3204 if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3205 (S_CONNECT_SENT != n->state) ) ||
3206 (2 != n->send_connect_ack) )
3208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3209 "Received SESSION_ACK message from peer `%s' in state %s/%d\n",
3211 print_state (n->state),
3212 n->send_connect_ack);
3213 GNUNET_STATISTICS_update (GST_stats,
3214 gettext_noop ("# unexpected SESSION_ACK messages"), 1,
3218 n->state = S_CONNECTED;
3219 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3220 GNUNET_STATISTICS_set (GST_stats,
3221 gettext_noop ("# peers connected"),
3222 ++neighbours_connected,
3224 connect_notify_cb (callback_cls, &n->id,
3225 n->primary_address.bandwidth_in,
3226 n->primary_address.bandwidth_out);
3228 GST_ats_add_address (n->primary_address.address,
3229 n->primary_address.session,
3231 set_address (&n->primary_address,
3232 n->primary_address.address,
3233 n->primary_address.session,
3234 n->primary_address.bandwidth_in,
3235 n->primary_address.bandwidth_out,
3242 * Test if we're connected to the given peer.
3244 * @param target peer to test
3245 * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3248 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3250 return test_connected (lookup_neighbour (target));
3255 * Change the incoming quota for the given peer.
3257 * @param neighbour identity of peer to change qutoa for
3258 * @param quota new quota
3261 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3262 struct GNUNET_BANDWIDTH_Value32NBO quota)
3264 struct NeighbourMapEntry *n;
3266 if (NULL == (n = lookup_neighbour (neighbour)))
3268 GNUNET_STATISTICS_update (GST_stats,
3270 ("# SET QUOTA messages ignored (no such peer)"),
3274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3275 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3276 ntohl (quota.value__), GNUNET_i2s (&n->id));
3277 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3278 if (0 != ntohl (quota.value__))
3280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3281 GNUNET_i2s (&n->id), "SET_QUOTA");
3282 if (GNUNET_YES == test_connected (n))
3283 GNUNET_STATISTICS_update (GST_stats,
3284 gettext_noop ("# disconnects due to quota of 0"),
3286 disconnect_neighbour (n);
3291 * We received a disconnect message from the given peer,
3292 * validate and process.
3294 * @param peer sender of the message
3295 * @param msg the disconnect message
3298 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3300 const struct GNUNET_MessageHeader
3303 struct NeighbourMapEntry *n;
3304 const struct SessionDisconnectMessage *sdm;
3306 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3307 "Received DISCONNECT message from peer `%s'\n",
3309 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3311 // GNUNET_break_op (0);
3312 GNUNET_STATISTICS_update (GST_stats,
3314 ("# disconnect messages ignored (old format)"), 1,
3318 GNUNET_STATISTICS_update (GST_stats,
3320 ("# DISCONNECT messages received"),
3322 sdm = (const struct SessionDisconnectMessage *) msg;
3323 if (NULL == (n = lookup_neighbour (peer)))
3324 return; /* gone already */
3325 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3327 GNUNET_STATISTICS_update (GST_stats,
3329 ("# disconnect messages ignored (timestamp)"), 1,
3333 if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3335 GNUNET_break_op (0);
3338 if (ntohl (sdm->purpose.size) !=
3339 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3340 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3341 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3343 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3344 "%s message from peer `%s' has invalid size \n",
3347 GNUNET_break_op (0);
3351 GNUNET_CRYPTO_eddsa_verify
3352 (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3353 &sdm->signature, &sdm->public_key))
3355 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3356 "%s message from peer `%s' cannot be verified \n",
3359 GNUNET_break_op (0);
3362 if (GNUNET_YES == test_connected (n))
3363 GNUNET_STATISTICS_update (GST_stats,
3365 ("# other peer asked to disconnect from us"), 1,
3367 disconnect_neighbour (n);
3372 * Closure for the neighbours_iterate function.
3374 struct IteratorContext
3377 * Function to call on each connected neighbour.
3379 GST_NeighbourIterator cb;
3389 * Call the callback from the closure for each connected neighbour.
3391 * @param cls the `struct IteratorContext`
3392 * @param key the hash of the public key of the neighbour
3393 * @param value the `struct NeighbourMapEntry`
3394 * @return #GNUNET_OK (continue to iterate)
3397 neighbours_iterate (void *cls,
3398 const struct GNUNET_PeerIdentity *key,
3401 struct IteratorContext *ic = cls;
3402 struct NeighbourMapEntry *n = value;
3403 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3404 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3406 if (GNUNET_YES != test_connected (n))
3409 if (NULL != n->primary_address.address)
3411 bandwidth_in = n->primary_address.bandwidth_in;
3412 bandwidth_out = n->primary_address.bandwidth_out;
3416 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3417 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3419 ic->cb (ic->cb_cls, &n->id,
3420 n->primary_address.address,
3421 bandwidth_in, bandwidth_out);
3427 * Iterate over all connected neighbours.
3429 * @param cb function to call
3430 * @param cb_cls closure for cb
3433 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3435 struct IteratorContext ic;
3437 if (NULL == neighbours)
3438 return; /* can happen during shutdown */
3441 GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3446 * If we have an active connection to the given target, it must be shutdown.
3448 * @param target peer to disconnect from
3451 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3453 struct NeighbourMapEntry *n;
3455 if (NULL == (n = lookup_neighbour (target)))
3456 return; /* not active */
3457 if (GNUNET_YES == test_connected (n))
3458 GNUNET_STATISTICS_update (GST_stats,
3460 ("# disconnected from peer upon explicit request"), 1,
3462 disconnect_neighbour (n);
3467 * Obtain current latency information for the given neighbour.
3469 * @param peer to get the latency for
3470 * @return observed latency of the address, FOREVER if the
3471 * the connection is not up
3473 struct GNUNET_TIME_Relative
3474 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3476 struct NeighbourMapEntry *n;
3478 n = lookup_neighbour (peer);
3480 return GNUNET_TIME_UNIT_FOREVER_REL;
3484 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3485 case S_CONNECTED_SWITCHING_BLACKLIST:
3486 case S_RECONNECT_SENT:
3487 case S_RECONNECT_ATS:
3488 case S_RECONNECT_BLACKLIST:
3490 case S_NOT_CONNECTED:
3491 case S_INIT_BLACKLIST:
3493 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3494 case S_CONNECT_RECV_ATS:
3495 case S_CONNECT_RECV_BLACKLIST:
3496 case S_CONNECT_RECV_ACK:
3497 case S_CONNECT_SENT:
3499 case S_DISCONNECT_FINISHED:
3500 return GNUNET_TIME_UNIT_FOREVER_REL;
3502 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3503 "Unhandled state `%s'\n",
3504 print_state (n->state));
3508 return GNUNET_TIME_UNIT_FOREVER_REL;
3513 * Obtain current address information for the given neighbour.
3516 * @return address currently used
3518 struct GNUNET_HELLO_Address *
3519 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3521 struct NeighbourMapEntry *n;
3523 n = lookup_neighbour (peer);
3526 return n->primary_address.address;
3531 * Initialize the neighbours subsystem.
3533 * @param cls closure for callbacks
3534 * @param connect_cb function to call if we connect to a peer
3535 * @param disconnect_cb function to call if we disconnect from a peer
3536 * @param peer_address_cb function to call if we change an active address
3538 * @param max_fds maximum number of fds to use
3541 GST_neighbours_start (void *cls,
3542 NotifyConnect connect_cb,
3543 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3544 GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3545 unsigned int max_fds)
3548 connect_notify_cb = connect_cb;
3549 disconnect_notify_cb = disconnect_cb;
3550 address_change_cb = peer_address_cb;
3551 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3552 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3553 utilization_transmission, NULL);
3558 * Disconnect from the given neighbour.
3561 * @param key hash of neighbour's public key (not used)
3562 * @param value the 'struct NeighbourMapEntry' of the neighbour
3563 * @return #GNUNET_OK (continue to iterate)
3566 disconnect_all_neighbours (void *cls,
3567 const struct GNUNET_PeerIdentity *key,
3570 struct NeighbourMapEntry *n = value;
3572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3573 "Disconnecting peer `%4s', %s\n",
3574 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3575 n->state = S_DISCONNECT_FINISHED;
3576 free_neighbour (n, GNUNET_NO);
3582 * Cleanup the neighbours subsystem.
3585 GST_neighbours_stop ()
3587 if (NULL == neighbours)
3589 if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3591 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3592 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3595 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3596 &disconnect_all_neighbours,
3598 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3600 callback_cls = NULL;
3601 connect_notify_cb = NULL;
3602 disconnect_notify_cb = NULL;
3603 address_change_cb = NULL;
3607 /* end of file gnunet-service-transport_neighbours.c */