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 * - "address_change_cb" is NEVER invoked; when should we call this one exactly?
28 * - TEST, TEST, TEST...
31 #include "gnunet_ats_service.h"
32 #include "gnunet-service-transport_neighbours.h"
33 #include "gnunet-service-transport_plugins.h"
34 #include "gnunet-service-transport_validation.h"
35 #include "gnunet-service-transport_clients.h"
36 #include "gnunet-service-transport.h"
37 #include "gnunet_peerinfo_service.h"
38 #include "gnunet-service-transport_blacklist.h"
39 #include "gnunet_constants.h"
40 #include "transport.h"
45 * Size of the neighbour hash map.
47 #define NEIGHBOUR_TABLE_SIZE 256
50 * Time we give plugin to transmit DISCONNECT message before the
51 * neighbour entry self-destructs.
53 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
56 * How often must a peer violate bandwidth quotas before we start
57 * to simply drop its messages?
59 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
62 * How often do we send KEEPALIVE messages to each of our neighbours and measure
63 * the latency with this neighbour?
64 * (idle timeout is 5 minutes or 300 seconds, so with 100s interval we
65 * send 3 keepalives in each interval, so 3 messages would need to be
66 * lost in a row for a disconnect).
68 #define KEEPALIVE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100)
71 * How long are we willing to wait for a response from ATS before timing out?
73 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5000)
76 * How long are we willing to wait for an ACK from the other peer before
77 * giving up on our connect operation?
79 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
82 * How long are we willing to wait for a successful reconnect if
83 * an existing connection went down? Much shorter than the
84 * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
85 * higher layers about the disconnect during this period.
87 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
90 * How long are we willing to wait for a response from the blacklist
91 * subsystem before timing out?
93 #define BLACKLIST_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
96 * Interval to send utilization data
98 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
100 #define DEBUG_MALLOC GNUNET_NO
106 struct Allocator *prev;
107 struct Allocator *next;
109 unsigned int bytes_alloced;
110 unsigned int max_alloced;
114 struct GNUNET_TIME_Absolute max_alloced_when;
115 struct GNUNET_TIME_Absolute last_alloced_when;
119 struct Allocator *aehead;
120 struct Allocator *aetail;
124 struct Allocation *prev;
125 struct Allocation *next;
127 struct Allocator *alloc;
128 unsigned int bytes_alloced;
133 struct Allocation *ahead;
134 struct Allocation *atail;
136 static int bytes_alloced;
138 static struct Allocator *
139 find_allocator (int line)
141 struct Allocator *cur = aehead;
144 if (line == cur->line)
154 static int start = GNUNET_YES;
155 static struct GNUNET_TIME_Absolute next;
156 static struct GNUNET_TIME_Relative rem;
157 struct Allocator *cur = aehead;
160 next = GNUNET_TIME_UNIT_ZERO_ABS;
163 if (0 == (rem = GNUNET_TIME_absolute_get_remaining(next)).rel_value_us)
165 fprintf (stderr, "Allocated in `%s' total: %5u bytes\n", __FILE__, bytes_alloced);
168 char *last_alloc = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string(cur->max_alloced_when));
169 fprintf (stderr, "Allocated from line %4u :%5u bytes (diff %5i bytes, max alloc: %5u @ %s, last alloc %s)\n",
170 cur->line, cur->bytes_alloced, cur->diff, cur->max_alloced,
172 GNUNET_STRINGS_absolute_time_to_string(cur->last_alloced_when));
173 GNUNET_free (last_alloc);
177 fprintf (stderr, "\n");
178 next = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), GNUNET_TIME_UNIT_SECONDS);
185 MEMDEBUG_add_alloc (void *p, size_t size, int line)
188 struct Allocation *alloc = GNUNET_malloc (sizeof (struct Allocation));
189 struct Allocator *allocator = find_allocator(line);
190 if (NULL == allocator)
192 allocator = GNUNET_malloc (sizeof (struct Allocator));
193 allocator->line = line;
194 GNUNET_CONTAINER_DLL_insert (aehead, aetail, allocator);
196 alloc->alloc = allocator;
199 alloc->bytes_alloced = size;
200 allocator->bytes_alloced += size;
201 allocator->last_alloced_when = GNUNET_TIME_absolute_get();
202 if (allocator->bytes_alloced >= allocator->max_alloced)
204 allocator->max_alloced = allocator->bytes_alloced;
205 allocator->max_alloced_when = allocator->last_alloced_when;
207 allocator->diff += size;
208 GNUNET_CONTAINER_DLL_insert (ahead, atail, alloc);
210 bytes_alloced += size;
216 MEMDEBUG_malloc (size_t size, int line)
220 ret = GNUNET_malloc (size);
223 MEMDEBUG_add_alloc (ret, size, line);
230 MEMDEBUG_free (void * alloc, int line)
233 struct Allocation *cur;
234 struct Allocator *allocator;
244 fprintf (stderr, "Unmonitored free from line %4u\n", line);
248 allocator = cur->alloc;
249 if (NULL == allocator)
253 GNUNET_CONTAINER_DLL_remove (ahead, atail, cur);
254 allocator->bytes_alloced -= cur->bytes_alloced;
255 allocator->diff -= cur->bytes_alloced;
256 GNUNET_assert (allocator->bytes_alloced >= 0);
257 bytes_alloced -= cur->bytes_alloced;
258 GNUNET_assert (bytes_alloced >= 0);
265 MEMDEBUG_free_non_null (void * alloc, int line)
268 MEMDEBUG_free (alloc, line);
272 GNUNET_NETWORK_STRUCT_BEGIN
275 * Message a peer sends to another to indicate that it intends to
276 * setup a connection/session for data exchange. A 'SESSION_CONNECT'
277 * should be answered with a 'SESSION_CONNECT_ACK' with the same body
278 * to confirm. A 'SESSION_CONNECT_ACK' should then be followed with
279 * a 'SESSION_ACK'. Once the 'SESSION_ACK' is received, both peers
280 * should be connected.
282 struct SessionConnectMessage
285 * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT'
286 * or 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK'
288 struct GNUNET_MessageHeader header;
293 uint32_t reserved GNUNET_PACKED;
296 * Absolute time at the sender. Only the most recent connect
297 * message implies which session is preferred by the sender.
299 struct GNUNET_TIME_AbsoluteNBO timestamp;
305 * Message we send to the other peer to notify him that we intentionally
306 * are disconnecting (to reduce timeouts). This is just a friendly
307 * notification, peers must not rely on always receiving disconnect
310 struct SessionDisconnectMessage
313 * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT'
315 struct GNUNET_MessageHeader header;
320 uint32_t reserved GNUNET_PACKED;
323 * Purpose of the signature. Extends over the timestamp.
324 * Purpose should be GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
326 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
329 * Absolute time at the sender. Only the most recent connect
330 * message implies which session is preferred by the sender.
332 struct GNUNET_TIME_AbsoluteNBO timestamp;
335 * Public key of the sender.
337 struct GNUNET_CRYPTO_EddsaPublicKey public_key;
340 * Signature of the peer that sends us the disconnect. Only
341 * valid if the timestamp is AFTER the timestamp from the
342 * corresponding 'CONNECT' message.
344 struct GNUNET_CRYPTO_EddsaSignature signature;
348 GNUNET_NETWORK_STRUCT_END
352 * For each neighbour we keep a list of messages
353 * that we still want to transmit to the neighbour.
359 * This is a doubly linked list.
361 struct MessageQueue *next;
364 * This is a doubly linked list.
366 struct MessageQueue *prev;
369 * Function to call once we're done.
371 GST_NeighbourSendContinuation cont;
379 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
380 * stuck together in memory. Allocated at the end of this struct.
382 const char *message_buf;
385 * Size of the message buf
387 size_t message_buf_size;
390 * At what time should we fail?
392 struct GNUNET_TIME_Absolute timeout;
398 * Possible state of a neighbour. Initially, we are S_NOT_CONNECTED.
400 * Then, there are two main paths. If we receive a CONNECT message, we
401 * first run a check against the blacklist (S_CONNECT_RECV_BLACKLIST_INBOUND).
402 * If this check is successful, we give the inbound address to ATS.
403 * After the check we ask ATS for a suggestion (S_CONNECT_RECV_ATS).
404 * If ATS makes a suggestion, we ALSO give that suggestion to the blacklist
405 * (S_CONNECT_RECV_BLACKLIST). Once the blacklist approves the
406 * address we got from ATS, we send our CONNECT_ACK and go to
407 * S_CONNECT_RECV_ACK. If we receive a SESSION_ACK, we go to
408 * S_CONNECTED (and notify everyone about the new connection). If the
409 * operation times out, we go to S_DISCONNECT.
411 * The other case is where we transmit a CONNECT message first. We
412 * start with S_INIT_ATS. If we get an address, we enter
413 * S_INIT_BLACKLIST and check the blacklist. If the blacklist is OK
414 * with the connection, we actually send the CONNECT message and go to
415 * state S_CONNECT_SENT. Once we receive a CONNECT_ACK, we go to
416 * S_CONNECTED (and notify everyone about the new connection and send
417 * back a SESSION_ACK). If the operation times out, we go to
420 * If the session is in trouble (i.e. transport-level disconnect or
421 * timeout), we go to S_RECONNECT_ATS where we ask ATS for a new
422 * address (we don't notify anyone about the disconnect yet). Once we
423 * have a new address, we go to S_RECONNECT_BLACKLIST to check the new
424 * address against the blacklist. If the blacklist approves, we enter
425 * S_RECONNECT_SENT and send a CONNECT message. If we receive a
426 * CONNECT_ACK, we go to S_CONNECTED and nobody noticed that we had
427 * trouble; we also send a SESSION_ACK at this time just in case. If
428 * the operation times out, we go to S_DISCONNECT (and notify everyone
429 * about the lost connection).
431 * If ATS decides to switch addresses while we have a normal
432 * connection, we go to S_CONNECTED_SWITCHING_BLACKLIST to check the
433 * new address against the blacklist. If the blacklist approves, we
434 * go to S_CONNECTED_SWITCHING_CONNECT_SENT and send a
435 * SESSION_CONNECT. If we get a SESSION_ACK back, we switch the
436 * primary connection to the suggested alternative from ATS, go back
437 * to S_CONNECTED and send a SESSION_ACK to the other peer just to be
438 * sure. If the operation times out (or the blacklist disapproves),
439 * we go to S_CONNECTED (and notify ATS that the given alternative
440 * address is "invalid").
442 * Once a session is in S_DISCONNECT, it is cleaned up and then goes
443 * to (S_DISCONNECT_FINISHED). If we receive an explicit disconnect
444 * request, we can go from any state to S_DISCONNECT, possibly after
445 * generating disconnect notifications.
447 * Note that it is quite possible that while we are in any of these
448 * states, we could receive a 'CONNECT' request from the other peer.
449 * We then enter a 'weird' state where we pursue our own primary state
450 * machine (as described above), but with the 'send_connect_ack' flag
451 * set to 1. If our state machine allows us to send a 'CONNECT_ACK'
452 * (because we have an acceptable address), we send the 'CONNECT_ACK'
453 * and set the 'send_connect_ack' to 2. If we then receive a
454 * 'SESSION_ACK', we go to 'S_CONNECTED' (and reset 'send_connect_ack'
461 * fresh peer or completely disconnected
466 * Asked to initiate connection, trying to get address from ATS
471 * Asked to initiate connection, trying to get address approved
477 * Sent CONNECT message to other peer, waiting for CONNECT_ACK
482 * Received a CONNECT, do a blacklist check for inbound address
484 S_CONNECT_RECV_BLACKLIST_INBOUND,
487 * Received a CONNECT, asking ATS about address suggestions.
492 * Received CONNECT from other peer, got an address, checking with blacklist.
494 S_CONNECT_RECV_BLACKLIST,
497 * CONNECT request from other peer was SESSION_ACK'ed, waiting for
503 * Got our CONNECT_ACK/SESSION_ACK, connection is up.
508 * Connection got into trouble, rest of the system still believes
509 * it to be up, but we're getting a new address from ATS.
514 * Connection got into trouble, rest of the system still believes
515 * it to be up; we are checking the new address against the blacklist.
517 S_RECONNECT_BLACKLIST,
520 * Sent CONNECT over new address (either by ATS telling us to switch
521 * addresses or from RECONNECT_ATS); if this fails, we need to tell
522 * the rest of the system about a disconnect.
527 * We have some primary connection, but ATS suggested we switch
528 * to some alternative; we're now checking the alternative against
531 S_CONNECTED_SWITCHING_BLACKLIST,
534 * We have some primary connection, but ATS suggested we switch
535 * to some alternative; we now sent a CONNECT message for the
536 * alternative session to the other peer and waiting for a
537 * CONNECT_ACK to make this our primary connection.
539 S_CONNECTED_SWITCHING_CONNECT_SENT,
542 * Disconnect in progress (we're sending the DISCONNECT message to the
543 * other peer; after that is finished, the state will be cleaned up).
548 * We're finished with the disconnect; and are cleaning up the state
549 * now! We put the struct into this state when we are really in the
550 * task that calls 'free' on it and are about to remove the record
551 * from the map. We should never find a 'struct NeighbourMapEntry'
552 * in this state in the map. Accessing a 'struct NeighbourMapEntry'
553 * in this state virtually always means using memory that has been
554 * freed (the exception being the cleanup code in 'free_neighbour').
556 S_DISCONNECT_FINISHED
561 * A possible address we could use to communicate with a neighbour.
563 struct NeighbourAddress
567 * Active session for this address.
569 struct Session *session;
572 * Network-level address information.
574 struct GNUNET_HELLO_Address *address;
577 * Timestamp of the 'SESSION_CONNECT' message we sent to the other
578 * peer for this address. Use to check that the ACK is in response
579 * to our most recent 'CONNECT'.
581 struct GNUNET_TIME_Absolute connect_timestamp;
584 * Inbound bandwidth from ATS for this address.
586 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
589 * Outbound bandwidth from ATS for this address.
591 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
594 * Did we tell ATS that this is our 'active' address?
602 * Entry in neighbours.
604 struct NeighbourMapEntry
608 * Head of list of messages we would like to send to this peer;
609 * must contain at most one message per client.
611 struct MessageQueue *messages_head;
614 * Tail of list of messages we would like to send to this peer; must
615 * contain at most one message per client.
617 struct MessageQueue *messages_tail;
620 * Are we currently trying to send a message? If so, which one?
622 struct MessageQueue *is_active;
625 * Primary address we currently use to communicate with the neighbour.
627 struct NeighbourAddress primary_address;
630 * Alternative address currently under consideration for communicating
631 * with the neighbour.
633 struct NeighbourAddress alternative_address;
636 * Identity of this neighbour.
638 struct GNUNET_PeerIdentity id;
641 * Main task that drives this peer (timeouts, keepalives, etc.).
642 * Always runs the 'master_task'.
644 GNUNET_SCHEDULER_TaskIdentifier task;
647 * At what time should we sent the next keep-alive message?
649 struct GNUNET_TIME_Absolute keep_alive_time;
652 * At what time did we sent the last keep-alive message? Used
653 * to calculate round-trip time ("latency").
655 struct GNUNET_TIME_Absolute last_keep_alive_time;
658 * Timestamp we should include in our next CONNECT_ACK message.
659 * (only valid if 'send_connect_ack' is GNUNET_YES). Used to build
660 * our CONNECT_ACK message.
662 struct GNUNET_TIME_Absolute connect_ack_timestamp;
665 * ATS address suggest handle
667 struct GNUNET_ATS_SuggestHandle *suggest_handle;
670 * Time where we should cut the connection (timeout) if we don't
671 * make progress in the state machine (or get a KEEPALIVE_RESPONSE
672 * if we are in S_CONNECTED).
674 struct GNUNET_TIME_Absolute timeout;
677 * Latest calculated latency value
679 struct GNUNET_TIME_Relative latency;
682 * Tracker for inbound bandwidth.
684 struct GNUNET_BANDWIDTH_Tracker in_tracker;
687 * How often has the other peer (recently) violated the inbound
688 * traffic limit? Incremented by 10 per violation, decremented by 1
689 * per non-violation (for each time interval).
691 unsigned int quota_violation_count;
694 * The current state of the peer.
699 * Did we sent an KEEP_ALIVE message and are we expecting a response?
701 int expect_latency_response;
704 * Flag to set if we still need to send a CONNECT_ACK message to the other peer
705 * (once we have an address to use and the peer has been allowed by our
706 * blacklist). Set to 1 if we need to send a CONNECT_ACK. Set to 2 if we
707 * did send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving
708 * a 'SESSION_ACK' (regardless of what our own state machine might say).
710 int send_connect_ack;
713 * Tracking utilization of outbound bandwidth
715 unsigned int util_bytes_sent;
718 * Tracking utilization of inbound bandwidth
720 unsigned int util_bytes_recv;
724 * Date of last utilization transmission
726 struct GNUNET_TIME_Absolute last_util_transmission;
731 * Context for blacklist checks and the 'handle_test_blacklist_cont'
732 * function. Stores information about ongoing blacklist checks.
734 struct BlackListCheckContext
738 * We keep blacklist checks in a DLL.
740 struct BlackListCheckContext *next;
743 * We keep blacklist checks in a DLL.
745 struct BlackListCheckContext *prev;
748 * Address that is being checked.
750 struct NeighbourAddress na;
753 * Handle to the ongoing blacklist check.
755 struct GST_BlacklistCheck *bc;
760 * Hash map from peer identities to the respective 'struct NeighbourMapEntry'.
762 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
765 * We keep blacklist checks in a DLL so that we can find
766 * the 'sessions' in their 'struct NeighbourAddress' if
767 * a session goes down.
769 static struct BlackListCheckContext *bc_head;
772 * We keep blacklist checks in a DLL.
774 static struct BlackListCheckContext *bc_tail;
777 * Closure for connect_notify_cb, disconnect_notify_cb and address_change_cb
779 static void *callback_cls;
782 * Function to call when we connected to a neighbour.
784 static NotifyConnect connect_notify_cb;
787 * Function to call when we disconnected from a neighbour.
789 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
792 * Function to call when we changed an active address of a neighbour.
794 static GNUNET_TRANSPORT_PeerIterateCallback address_change_cb;
797 * counter for connected neighbours
799 static unsigned int neighbours_connected;
802 * Number of bytes we have currently queued for transmission.
804 static unsigned long long bytes_in_send_queue;
807 * Task transmitting utilization data
809 static GNUNET_SCHEDULER_TaskIdentifier util_transmission_tk;
813 * Lookup a neighbour entry in the neighbours hash map.
815 * @param pid identity of the peer to look up
816 * @return the entry, NULL if there is no existing record
818 static struct NeighbourMapEntry *
819 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
821 if (NULL == neighbours)
823 return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
827 print_state (int state)
832 case S_NOT_CONNECTED:
833 return "S_NOT_CONNECTED";
836 case S_INIT_BLACKLIST:
837 return "S_INIT_BLACKLIST";
839 return "S_CONNECT_SENT";
840 case S_CONNECT_RECV_BLACKLIST_INBOUND:
841 return "S_CONNECT_RECV_BLACKLIST_INBOUND";
842 case S_CONNECT_RECV_ATS:
843 return "S_CONNECT_RECV_ATS";
844 case S_CONNECT_RECV_BLACKLIST:
845 return "S_CONNECT_RECV_BLACKLIST";
846 case S_CONNECT_RECV_ACK:
847 return "S_CONNECT_RECV_ACK";
849 return "S_CONNECTED";
850 case S_RECONNECT_ATS:
851 return "S_RECONNECT_ATS";
852 case S_RECONNECT_BLACKLIST:
853 return "S_RECONNECT_BLACKLIST";
854 case S_RECONNECT_SENT:
855 return "S_RECONNECT_SENT";
856 case S_CONNECTED_SWITCHING_BLACKLIST:
857 return "S_CONNECTED_SWITCHING_BLACKLIST";
858 case S_CONNECTED_SWITCHING_CONNECT_SENT:
859 return "S_CONNECTED_SWITCHING_CONNECT_SENT";
861 return "S_DISCONNECT";
862 case S_DISCONNECT_FINISHED:
863 return "S_DISCONNECT_FINISHED";
871 * Test if we're connected to the given peer.
873 * @param n neighbour entry of peer to test
874 * @return GNUNET_YES if we are connected, GNUNET_NO if not
877 test_connected (struct NeighbourMapEntry *n)
883 case S_NOT_CONNECTED:
885 case S_INIT_BLACKLIST:
887 case S_CONNECT_RECV_BLACKLIST_INBOUND:
888 case S_CONNECT_RECV_ATS:
889 case S_CONNECT_RECV_BLACKLIST:
890 case S_CONNECT_RECV_ACK:
893 case S_RECONNECT_ATS:
894 case S_RECONNECT_BLACKLIST:
895 case S_RECONNECT_SENT:
896 case S_CONNECTED_SWITCHING_BLACKLIST:
897 case S_CONNECTED_SWITCHING_CONNECT_SENT:
900 case S_DISCONNECT_FINISHED:
903 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
907 return GNUNET_SYSERR;
911 * Send information about a new outbound quota to our clients.
913 * @param target affected peer
914 * @param quota new quota
917 send_outbound_quota (const struct GNUNET_PeerIdentity *target,
918 struct GNUNET_BANDWIDTH_Value32NBO quota)
920 struct QuotaSetMessage q_msg;
922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923 "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
924 ntohl (quota.value__), GNUNET_i2s (target));
925 q_msg.header.size = htons (sizeof (struct QuotaSetMessage));
926 q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
928 q_msg.peer = (*target);
929 GST_clients_broadcast (&q_msg.header, GNUNET_NO);
934 * We don't need a given neighbour address any more.
935 * Release its resources and give appropriate notifications
936 * to ATS and other subsystems.
938 * @param na address we are done with; 'na' itself must NOT be 'free'd, only the contents!
941 free_address (struct NeighbourAddress *na)
943 if (GNUNET_YES == na->ats_active)
945 GST_validation_set_address_use (na->address, na->session, GNUNET_NO, __LINE__);
946 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
947 address_change_cb (NULL, &na->address->peer, NULL);
950 na->ats_active = GNUNET_NO;
951 if (NULL != na->address)
953 MEMDEBUG_free (na->address, __LINE__);
954 //GNUNET_HELLO_address_free (na->address);
962 * Initialize the 'struct NeighbourAddress'.
964 * @param na neighbour address to initialize
965 * @param address address of the other peer, NULL if other peer
967 * @param session session to use (or NULL, in which case an
968 * address must be setup)
969 * @param bandwidth_in inbound quota to be used when connection is up
970 * @param bandwidth_out outbound quota to be used when connection is up
971 * @param is_active GNUNET_YES to mark this as the active address with ATS
974 set_address (struct NeighbourAddress *na,
975 const struct GNUNET_HELLO_Address *address,
976 struct Session *session,
977 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
978 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
981 struct GNUNET_TRANSPORT_PluginFunctions *papi;
982 if (NULL == (papi = GST_plugins_find (address->transport_name)))
987 if (session == na->session)
989 na->bandwidth_in = bandwidth_in;
990 na->bandwidth_out = bandwidth_out;
991 if (is_active != na->ats_active)
993 na->ats_active = is_active;
994 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, is_active);
995 GST_validation_set_address_use (na->address, na->session, is_active, __LINE__);
997 address_change_cb (NULL, &address->peer, address);
999 if (GNUNET_YES == is_active)
1001 /* FIXME: is this the right place to set quotas? */
1002 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
1003 send_outbound_quota (&address->peer, bandwidth_out);
1008 if (NULL == session)
1009 session = papi->get_session (papi->cls, address);
1010 if (NULL == session)
1012 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1013 "Failed to obtain new session for peer `%s' and address '%s'\n",
1014 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
1015 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
1018 na->address = GNUNET_HELLO_address_copy (address);
1019 MEMDEBUG_add_alloc (na->address, GNUNET_HELLO_address_get_size (na->address), __LINE__);
1020 na->bandwidth_in = bandwidth_in;
1021 na->bandwidth_out = bandwidth_out;
1022 na->session = session;
1023 na->ats_active = is_active;
1024 if (GNUNET_YES == is_active)
1026 /* Telling ATS about new session */
1027 GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_YES);
1028 GST_validation_set_address_use (na->address, na->session, GNUNET_YES, __LINE__);
1029 address_change_cb (NULL, &address->peer, address);
1030 /* FIXME: is this the right place to set quotas? */
1031 GST_neighbours_set_incoming_quota (&address->peer, bandwidth_in);
1032 send_outbound_quota (&address->peer, bandwidth_out);
1038 * Free a neighbour map entry.
1040 * @param n entry to free
1041 * @param keep_sessions GNUNET_NO to tell plugin to terminate sessions,
1042 * GNUNET_YES to keep all sessions
1045 free_neighbour (struct NeighbourMapEntry *n, int keep_sessions)
1047 struct MessageQueue *mq;
1048 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1049 struct GNUNET_HELLO_Address *backup_primary;
1051 n->is_active = NULL; /* always free'd by its own continuation! */
1053 /* fail messages currently in the queue */
1054 while (NULL != (mq = n->messages_head))
1056 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1057 if (NULL != mq->cont)
1058 mq->cont (mq->cont_cls, GNUNET_SYSERR, mq->message_buf_size, 0);
1059 MEMDEBUG_free (mq, __LINE__);
1061 /* It is too late to send other peer disconnect notifications, but at
1062 least internally we need to get clean... */
1063 if (GNUNET_YES == test_connected (n))
1065 GNUNET_STATISTICS_set (GST_stats,
1066 gettext_noop ("# peers connected"),
1067 --neighbours_connected,
1069 disconnect_notify_cb (callback_cls, &n->id);
1072 n->state = S_DISCONNECT_FINISHED;
1074 if (NULL != n->primary_address.address)
1076 backup_primary = GNUNET_HELLO_address_copy(n->primary_address.address);
1077 MEMDEBUG_add_alloc (backup_primary, GNUNET_HELLO_address_get_size(backup_primary), __LINE__);
1080 backup_primary = NULL;
1082 /* free addresses and mark as unused */
1083 free_address (&n->primary_address);
1084 free_address (&n->alternative_address);
1086 /* FIXME-PLUGIN-API: This does not seem to guarantee that all
1087 transport sessions eventually get killed due to inactivity; they
1088 MUST have their own timeout logic (but at least TCP doesn't have
1089 one yet). Are we sure that EVERY 'session' of a plugin is
1090 actually cleaned up this way!? Note that if we are switching
1091 between two TCP sessions to the same peer, the existing plugin
1092 API gives us not even the means to selectively kill only one of
1093 them! Killing all sessions like this seems to be very, very
1096 /* cut transport-level connection */
1097 if ((GNUNET_NO == keep_sessions) &&
1098 (NULL != backup_primary) &&
1099 (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
1100 papi->disconnect (papi->cls, &n->id);
1102 MEMDEBUG_free_non_null (backup_primary, __LINE__);
1104 GNUNET_assert (GNUNET_YES ==
1105 GNUNET_CONTAINER_multipeermap_remove (neighbours,
1108 // FIXME-ATS-API: we might want to be more specific about
1109 // which states we do this from in the future (ATS should
1110 // have given us a 'suggest_address' handle, and if we have
1111 // such a handle, we should cancel the operation here!
1112 if (NULL != n->suggest_handle)
1114 GNUNET_ATS_suggest_address_cancel (GST_ats, &n->id);
1115 n->suggest_handle = NULL;
1118 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1120 GNUNET_SCHEDULER_cancel (n->task);
1121 n->task = GNUNET_SCHEDULER_NO_TASK;
1123 /* free rest of memory */
1124 MEMDEBUG_free (n, __LINE__);
1128 * Transmit a message using the current session of the given
1131 * @param n entry for the recipient
1132 * @param msgbuf buffer to transmit
1133 * @param msgbuf_size number of bytes in buffer
1134 * @param priority transmission priority
1135 * @param timeout transmission timeout
1136 * @param cont continuation to call when finished (can be NULL)
1137 * @param cont_cls closure for cont
1140 send_with_session (struct NeighbourMapEntry *n,
1141 const char *msgbuf, size_t msgbuf_size,
1143 struct GNUNET_TIME_Relative timeout,
1144 GNUNET_TRANSPORT_TransmitContinuation cont,
1147 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1149 GNUNET_assert (n->primary_address.session != NULL);
1150 if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
1151 (-1 == papi->send (papi->cls,
1152 n->primary_address.session,
1153 msgbuf, msgbuf_size,
1156 cont, cont_cls)))) &&
1158 cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
1159 GNUNET_break (NULL != papi);
1164 * Master task run for every neighbour. Performs all of the time-related
1165 * activities (keep alive, send next message, disconnect if idle, finish
1166 * clean up after disconnect).
1168 * @param cls the 'struct NeighbourMapEntry' for which we are running
1169 * @param tc scheduler context (unused)
1172 master_task (void *cls,
1173 const struct GNUNET_SCHEDULER_TaskContext *tc);
1177 * Function called when the 'DISCONNECT' message has been sent by the
1178 * plugin. Frees the neighbour --- if the entry still exists.
1181 * @param target identity of the neighbour that was disconnected
1182 * @param result GNUNET_OK if the disconnect got out successfully
1183 * @param payload bytes payload
1184 * @param physical bytes physical
1187 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
1188 int result, size_t payload, size_t physical)
1190 struct NeighbourMapEntry *n;
1192 n = lookup_neighbour (target);
1194 return; /* already gone */
1195 if (S_DISCONNECT != n->state)
1196 return; /* have created a fresh entry since */
1197 n->state = S_DISCONNECT;
1198 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1199 GNUNET_SCHEDULER_cancel (n->task);
1200 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1205 * Transmit a DISCONNECT message to the other peer.
1207 * @param n neighbour to send DISCONNECT message.
1210 send_disconnect (struct NeighbourMapEntry *n)
1212 struct SessionDisconnectMessage disconnect_msg;
1214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1215 "Sending DISCONNECT message to peer `%4s'\n",
1216 GNUNET_i2s (&n->id));
1217 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1218 disconnect_msg.header.type =
1219 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1220 disconnect_msg.reserved = htonl (0);
1221 disconnect_msg.purpose.size =
1222 htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1223 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1224 sizeof (struct GNUNET_TIME_AbsoluteNBO));
1225 disconnect_msg.purpose.purpose =
1226 htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1227 disconnect_msg.timestamp =
1228 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1229 disconnect_msg.public_key = GST_my_identity.public_key;
1230 GNUNET_assert (GNUNET_OK ==
1231 GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1232 &disconnect_msg.purpose,
1233 &disconnect_msg.signature));
1235 send_with_session (n,
1236 (const char *) &disconnect_msg, sizeof (disconnect_msg),
1237 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1238 &send_disconnect_cont, NULL);
1239 GNUNET_STATISTICS_update (GST_stats,
1241 ("# DISCONNECT messages sent"), 1,
1247 * Disconnect from the given neighbour, clean up the record.
1249 * @param n neighbour to disconnect from
1252 disconnect_neighbour (struct NeighbourMapEntry *n)
1254 /* depending on state, notify neighbour and/or upper layers of this peer
1258 case S_NOT_CONNECTED:
1260 case S_INIT_BLACKLIST:
1261 /* other peer is completely unaware of us, no need to send DISCONNECT */
1262 n->state = S_DISCONNECT_FINISHED;
1263 free_neighbour (n, GNUNET_NO);
1265 case S_CONNECT_SENT:
1266 send_disconnect (n);
1267 n->state = S_DISCONNECT;
1269 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1270 case S_CONNECT_RECV_ATS:
1271 case S_CONNECT_RECV_BLACKLIST:
1272 /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1273 n->state = S_DISCONNECT_FINISHED;
1274 free_neighbour (n, GNUNET_NO);
1276 case S_CONNECT_RECV_ACK:
1277 /* we DID ACK the other peer's request, must send DISCONNECT */
1278 send_disconnect (n);
1279 n->state = S_DISCONNECT;
1282 case S_RECONNECT_BLACKLIST:
1283 case S_RECONNECT_SENT:
1284 case S_CONNECTED_SWITCHING_BLACKLIST:
1285 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1286 /* we are currently connected, need to send disconnect and do
1287 internal notifications and update statistics */
1288 send_disconnect (n);
1289 GNUNET_STATISTICS_set (GST_stats,
1290 gettext_noop ("# peers connected"),
1291 --neighbours_connected,
1293 disconnect_notify_cb (callback_cls, &n->id);
1294 n->state = S_DISCONNECT;
1296 case S_RECONNECT_ATS:
1297 /* ATS address request timeout, disconnect without sending disconnect message */
1298 GNUNET_STATISTICS_set (GST_stats,
1299 gettext_noop ("# peers connected"),
1300 --neighbours_connected,
1302 disconnect_notify_cb (callback_cls, &n->id);
1303 n->state = S_DISCONNECT;
1306 /* already disconnected, ignore */
1308 case S_DISCONNECT_FINISHED:
1309 /* already cleaned up, how did we get here!? */
1313 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
1317 /* schedule timeout to clean up */
1318 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1319 GNUNET_SCHEDULER_cancel (n->task);
1320 n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1326 * We're done with our transmission attempt, continue processing.
1328 * @param cls the 'struct MessageQueue' of the message
1329 * @param receiver intended receiver
1330 * @param success whether it worked or not
1331 * @param size_payload bytes payload sent
1332 * @param physical bytes sent on wire
1335 transmit_send_continuation (void *cls,
1336 const struct GNUNET_PeerIdentity *receiver,
1337 int success, size_t size_payload, size_t physical)
1339 struct MessageQueue *mq = cls;
1340 struct NeighbourMapEntry *n;
1342 if (NULL == (n = lookup_neighbour (receiver)))
1344 MEMDEBUG_free (mq, __LINE__);
1345 return; /* disconnect or other error while transmitting, can happen */
1347 if (n->is_active == mq)
1349 /* this is still "our" neighbour, remove us from its queue
1350 and allow it to send the next message now */
1351 n->is_active = NULL;
1352 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1353 GNUNET_SCHEDULER_cancel (n->task);
1354 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1356 if (bytes_in_send_queue < mq->message_buf_size)
1358 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1359 "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1360 bytes_in_send_queue, mq->message_buf_size,
1361 (GNUNET_OK == success) ? "OK" : "FAIL",
1362 size_payload, physical);
1367 GNUNET_break (size_payload == mq->message_buf_size);
1368 bytes_in_send_queue -= mq->message_buf_size;
1369 GNUNET_STATISTICS_set (GST_stats,
1371 ("# bytes in message queue for other peers"),
1372 bytes_in_send_queue, GNUNET_NO);
1373 if (GNUNET_OK == success)
1375 n->util_bytes_sent += size_payload;
1376 GNUNET_STATISTICS_update (GST_stats,
1378 ("# messages transmitted to other peers"),
1382 GNUNET_STATISTICS_update (GST_stats,
1384 ("# transmission failures for messages to other peers"),
1386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1387 "Sending message to `%s' of type %u was a %s\n",
1388 GNUNET_i2s (receiver),
1389 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1390 (success == GNUNET_OK) ? "success" : "FAILURE");
1391 if (NULL != mq->cont)
1392 mq->cont (mq->cont_cls, success, size_payload, physical);
1393 MEMDEBUG_free (mq, __LINE__);
1398 * Check the message list for the given neighbour and if we can
1399 * send a message, do so. This function should only be called
1400 * if the connection is at least generally ready for transmission.
1401 * While we will only send one message at a time, no bandwidth
1402 * quota management is performed here. If a message was given to
1403 * the plugin, the continuation will automatically re-schedule
1404 * the 'master' task once the next message might be transmitted.
1406 * @param n target peer for which to transmit
1409 try_transmission_to_peer (struct NeighbourMapEntry *n)
1411 struct MessageQueue *mq;
1412 struct GNUNET_TIME_Relative timeout;
1414 if (NULL == n->primary_address.address)
1416 /* no address, why are we here? */
1420 if ((0 == n->primary_address.address->address_length) &&
1421 (NULL == n->primary_address.session))
1423 /* no address, why are we here? */
1427 if (NULL != n->is_active)
1429 /* transmission already pending */
1433 /* timeout messages from the queue that are past their due date */
1434 while (NULL != (mq = n->messages_head))
1436 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1437 if (timeout.rel_value_us > 0)
1439 GNUNET_STATISTICS_update (GST_stats,
1441 ("# messages timed out while in transport queue"),
1443 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1445 transmit_send_continuation (mq, &n->id, GNUNET_SYSERR, mq->message_buf_size, 0); /* timeout */
1448 return; /* no more messages */
1449 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1451 send_with_session (n,
1452 mq->message_buf, mq->message_buf_size,
1453 0 /* priority */, timeout,
1454 &transmit_send_continuation, mq);
1459 * Send keepalive message to the neighbour. Must only be called
1460 * if we are on 'connected' state or while trying to switch addresses.
1461 * Will internally determine if a keepalive is truly needed (so can
1462 * always be called).
1464 * @param n neighbour that went idle and needs a keepalive
1467 send_keepalive (struct NeighbourMapEntry *n)
1469 struct GNUNET_MessageHeader m;
1471 GNUNET_assert ((S_CONNECTED == n->state) ||
1472 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1473 (S_CONNECTED_SWITCHING_CONNECT_SENT));
1474 if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1475 return; /* no keepalive needed at this time */
1476 m.size = htons (sizeof (struct GNUNET_MessageHeader));
1477 m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1478 send_with_session (n,
1479 (const void *) &m, sizeof (m),
1480 UINT32_MAX /* priority */,
1481 KEEPALIVE_FREQUENCY,
1483 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1485 n->expect_latency_response = GNUNET_YES;
1486 n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1487 n->keep_alive_time = GNUNET_TIME_relative_to_absolute (KEEPALIVE_FREQUENCY);
1492 * Keep the connection to the given neighbour alive longer,
1493 * we received a KEEPALIVE (or equivalent); send a response.
1495 * @param neighbour neighbour to keep alive (by sending keep alive response)
1498 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour)
1500 struct NeighbourMapEntry *n;
1501 struct GNUNET_MessageHeader m;
1503 if (NULL == (n = lookup_neighbour (neighbour)))
1505 GNUNET_STATISTICS_update (GST_stats,
1507 ("# KEEPALIVE messages discarded (peer unknown)"),
1511 if (NULL == n->primary_address.session)
1513 GNUNET_STATISTICS_update (GST_stats,
1515 ("# KEEPALIVE messages discarded (no session)"),
1519 /* send reply to allow neighbour to measure latency */
1520 m.size = htons (sizeof (struct GNUNET_MessageHeader));
1521 m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1522 send_with_session(n,
1523 (const void *) &m, sizeof (m),
1524 UINT32_MAX /* priority */,
1525 KEEPALIVE_FREQUENCY,
1531 * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1532 * latency to this peer. Pass the updated information (existing ats
1533 * plus calculated latency) to ATS.
1535 * @param neighbour neighbour to keep alive
1538 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour)
1540 struct NeighbourMapEntry *n;
1542 struct GNUNET_ATS_Information ats;
1544 if (NULL == (n = lookup_neighbour (neighbour)))
1546 GNUNET_STATISTICS_update (GST_stats,
1548 ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1552 if ( (S_CONNECTED != n->state) ||
1553 (GNUNET_YES != n->expect_latency_response) )
1555 GNUNET_STATISTICS_update (GST_stats,
1557 ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1561 n->expect_latency_response = GNUNET_NO;
1562 n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1563 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1565 "Latency for peer `%s' is %s\n",
1566 GNUNET_i2s (&n->id),
1567 GNUNET_STRINGS_relative_time_to_string (n->latency,
1569 /* append latency */
1570 ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1571 if (n->latency.rel_value_us > UINT32_MAX)
1572 latency = UINT32_MAX;
1574 latency = n->latency.rel_value_us;
1575 ats.value = htonl (latency);
1576 GST_ats_update_metrics (&n->id,
1577 n->primary_address.address,
1578 n->primary_address.session,
1584 * We have received a message from the given sender. How long should
1585 * we delay before receiving more? (Also used to keep the peer marked
1588 * @param sender sender of the message
1589 * @param size size of the message
1590 * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1591 * GNUNET_NO if the neighbour is not connected or violates the quota,
1592 * GNUNET_SYSERR if the connection is not fully up yet
1593 * @return how long to wait before reading more from this sender
1595 struct GNUNET_TIME_Relative
1596 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1597 *sender, ssize_t size, int *do_forward)
1599 struct NeighbourMapEntry *n;
1600 struct GNUNET_TIME_Relative ret;
1602 if (NULL == neighbours)
1604 *do_forward = GNUNET_NO;
1605 return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1607 if (NULL == (n = lookup_neighbour (sender)))
1609 GST_neighbours_try_connect (sender);
1610 if (NULL == (n = lookup_neighbour (sender)))
1612 GNUNET_STATISTICS_update (GST_stats,
1614 ("# messages discarded due to lack of neighbour record"),
1616 *do_forward = GNUNET_NO;
1617 return GNUNET_TIME_UNIT_ZERO;
1620 if (! test_connected (n))
1622 *do_forward = GNUNET_SYSERR;
1623 return GNUNET_TIME_UNIT_ZERO;
1625 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1627 n->quota_violation_count++;
1628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1629 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1630 n->in_tracker.available_bytes_per_s__,
1631 n->quota_violation_count);
1632 /* Discount 32k per violation */
1633 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1637 if (n->quota_violation_count > 0)
1639 /* try to add 32k back */
1640 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1641 n->quota_violation_count--;
1644 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1646 GNUNET_STATISTICS_update (GST_stats,
1648 ("# bandwidth quota violations by other peers"),
1650 *do_forward = GNUNET_NO;
1651 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1653 *do_forward = GNUNET_YES;
1654 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1655 if (ret.rel_value_us > 0)
1657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1658 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1659 (unsigned long long) n->in_tracker.
1660 consumption_since_last_update__,
1661 (unsigned int) n->in_tracker.available_bytes_per_s__,
1662 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1663 GNUNET_STATISTICS_update (GST_stats,
1664 gettext_noop ("# ms throttling suggested"),
1665 (int64_t) ret.rel_value_us / 1000LL,
1673 * Transmit a message to the given target using the active connection.
1675 * @param target destination
1676 * @param msg message to send
1677 * @param msg_size number of bytes in msg
1678 * @param timeout when to fail with timeout
1679 * @param cont function to call when done
1680 * @param cont_cls closure for 'cont'
1683 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1684 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1685 GST_NeighbourSendContinuation cont, void *cont_cls)
1687 struct NeighbourMapEntry *n;
1688 struct MessageQueue *mq;
1690 /* All ove these cases should never happen; they are all API violations.
1691 But we check anyway, just to be sure. */
1692 if (NULL == (n = lookup_neighbour (target)))
1696 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1699 if (GNUNET_YES != test_connected (n))
1703 cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1706 bytes_in_send_queue += msg_size;
1707 GNUNET_STATISTICS_set (GST_stats,
1709 ("# bytes in message queue for other peers"),
1710 bytes_in_send_queue, GNUNET_NO);
1711 mq = MEMDEBUG_malloc (sizeof (struct MessageQueue) + msg_size, __LINE__);
1713 mq->cont_cls = cont_cls;
1714 memcpy (&mq[1], msg, msg_size);
1715 mq->message_buf = (const char *) &mq[1];
1716 mq->message_buf_size = msg_size;
1717 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1718 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1719 if ( (NULL != n->is_active) ||
1720 ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1722 if (GNUNET_SCHEDULER_NO_TASK != n->task)
1723 GNUNET_SCHEDULER_cancel (n->task);
1724 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1729 * Send a SESSION_CONNECT message via the given address.
1731 * @param na address to use
1734 send_session_connect (struct NeighbourAddress *na)
1736 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1737 struct SessionConnectMessage connect_msg;
1739 if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1744 if (NULL == na->session)
1745 na->session = papi->get_session (papi->cls, na->address);
1746 if (NULL == na->session)
1751 na->connect_timestamp = GNUNET_TIME_absolute_get ();
1752 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1753 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1754 connect_msg.reserved = htonl (0);
1755 connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1756 (void) papi->send (papi->cls,
1758 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1760 GNUNET_TIME_UNIT_FOREVER_REL,
1767 * Send a SESSION_CONNECT_ACK message via the given address.
1769 * @param address address to use
1770 * @param session session to use
1771 * @param timestamp timestamp to use for the ACK message
1774 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1775 struct Session *session,
1776 struct GNUNET_TIME_Absolute timestamp)
1778 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1779 struct SessionConnectMessage connect_msg;
1781 if (NULL == (papi = GST_plugins_find (address->transport_name)))
1786 if (NULL == session)
1787 session = papi->get_session (papi->cls, address);
1788 if (NULL == session)
1793 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1794 connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1795 connect_msg.reserved = htonl (0);
1796 connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1797 (void) papi->send (papi->cls,
1799 (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1801 GNUNET_TIME_UNIT_FOREVER_REL,
1808 * Create a fresh entry in the neighbour map for the given peer
1810 * @param peer peer to create an entry for
1811 * @return new neighbour map entry
1813 static struct NeighbourMapEntry *
1814 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1816 struct NeighbourMapEntry *n;
1818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1819 "Creating new neighbour entry for `%s'\n",
1821 n = MEMDEBUG_malloc (sizeof (struct NeighbourMapEntry), __LINE__);
1823 n->state = S_NOT_CONNECTED;
1824 n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1825 n->last_util_transmission = GNUNET_TIME_absolute_get();
1826 n->util_bytes_recv = 0;
1827 n->util_bytes_sent = 0;
1828 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1829 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1830 MAX_BANDWIDTH_CARRY_S);
1831 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1832 GNUNET_assert (GNUNET_OK ==
1833 GNUNET_CONTAINER_multipeermap_put (neighbours,
1835 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1841 * Check if the two given addresses are the same.
1842 * Actually only checks if the sessions are non-NULL
1843 * (which they should be) and then if they are identical;
1844 * the actual addresses don't matter if the session
1845 * pointers match anyway, and we must have session pointers
1848 * @param a1 first address to compare
1849 * @param a2 other address to compare
1850 * @return GNUNET_NO if the addresses do not match, GNUNET_YES if they do match
1853 address_matches (const struct NeighbourAddress *a1,
1854 const struct NeighbourAddress *a2)
1856 if ( (NULL == a1->session) ||
1857 (NULL == a2->session) )
1862 return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1867 * Try to create a connection to the given target (eventually).
1869 * @param target peer to try to connect to
1872 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1874 struct NeighbourMapEntry *n;
1876 if (NULL == neighbours)
1878 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1879 "Asked to connect to peer `%s' during shutdown\n",
1880 GNUNET_i2s (target));
1881 return; /* during shutdown, do nothing */
1883 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1884 "Asked to connect to peer `%s'\n",
1885 GNUNET_i2s (target));
1886 if (0 == memcmp (target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1888 /* refuse to connect to myself */
1889 /* FIXME: can this happen? Is this not an API violation? */
1890 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1891 "Refusing to try to connect to myself.\n");
1894 n = lookup_neighbour (target);
1899 case S_NOT_CONNECTED:
1900 /* this should not be possible */
1902 free_neighbour (n, GNUNET_NO);
1905 case S_INIT_BLACKLIST:
1906 case S_CONNECT_SENT:
1907 case S_CONNECT_RECV_BLACKLIST_INBOUND:
1908 case S_CONNECT_RECV_ATS:
1909 case S_CONNECT_RECV_BLACKLIST:
1910 case S_CONNECT_RECV_ACK:
1911 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1912 "Ignoring request to try to connect to `%s', already trying!\n",
1913 GNUNET_i2s (target));
1914 return; /* already trying */
1916 case S_RECONNECT_ATS:
1917 case S_RECONNECT_BLACKLIST:
1918 case S_RECONNECT_SENT:
1919 case S_CONNECTED_SWITCHING_BLACKLIST:
1920 case S_CONNECTED_SWITCHING_CONNECT_SENT:
1921 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1922 "Ignoring request to try to connect, already connected to `%s'!\n",
1923 GNUNET_i2s (target));
1924 return; /* already connected */
1926 /* get rid of remains, ready to re-try immediately */
1927 free_neighbour (n, GNUNET_NO);
1929 case S_DISCONNECT_FINISHED:
1930 /* should not be possible */
1933 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
1935 free_neighbour (n, GNUNET_NO);
1939 n = setup_neighbour (target);
1940 n->state = S_INIT_ATS;
1941 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1943 GNUNET_ATS_reset_backoff (GST_ats, target);
1944 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1949 * Function called with the result of a blacklist check.
1951 * @param cls closure with the 'struct BlackListCheckContext'
1952 * @param peer peer this check affects
1953 * @param result GNUNET_OK if the address is allowed
1956 handle_test_blacklist_cont (void *cls,
1957 const struct GNUNET_PeerIdentity *peer,
1960 struct BlackListCheckContext *bcc = cls;
1961 struct NeighbourMapEntry *n;
1964 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1965 "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1967 (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1968 if (NULL == (n = lookup_neighbour (peer)))
1969 goto cleanup; /* nobody left to care about new address */
1972 case S_NOT_CONNECTED:
1973 /* this should not be possible */
1975 free_neighbour (n, GNUNET_NO);
1978 /* still waiting on ATS suggestion */
1980 case S_INIT_BLACKLIST:
1981 /* check if the address the blacklist was fine with matches
1982 ATS suggestion, if so, we can move on! */
1983 if ( (GNUNET_OK == result) &&
1984 (1 == n->send_connect_ack) )
1986 n->send_connect_ack = 2;
1987 send_session_connect_ack_message (bcc->na.address,
1989 n->connect_ack_timestamp);
1991 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1992 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_SENT;
1997 send_session_connect (&n->primary_address);
2001 // FIXME: should also possibly destroy session with plugin!?
2002 GNUNET_ATS_address_destroyed (GST_ats,
2005 free_address (&n->primary_address);
2006 n->state = S_INIT_ATS;
2007 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2008 // FIXME: do we need to ask ATS again for suggestions?
2009 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2012 case S_CONNECT_SENT:
2013 /* waiting on CONNECT_ACK, send ACK if one is pending */
2014 if ( (GNUNET_OK == result) &&
2015 (1 == n->send_connect_ack) )
2017 n->send_connect_ack = 2;
2018 send_session_connect_ack_message (n->primary_address.address,
2019 n->primary_address.session,
2020 n->connect_ack_timestamp);
2023 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2024 if (GNUNET_OK == result)
2025 GST_ats_add_address (bcc->na.address, bcc->na.session);
2027 n->state = S_CONNECT_RECV_ATS;
2028 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2029 GNUNET_ATS_reset_backoff (GST_ats, peer);
2030 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2032 case S_CONNECT_RECV_ATS:
2033 /* still waiting on ATS suggestion, don't care about blacklist */
2035 case S_CONNECT_RECV_BLACKLIST:
2036 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2037 break; /* result for an address we currently don't care about */
2038 if (GNUNET_OK == result)
2040 n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
2041 n->state = S_CONNECT_RECV_ACK;
2042 send_session_connect_ack_message (bcc->na.address,
2044 n->connect_ack_timestamp);
2045 if (1 == n->send_connect_ack)
2046 n->send_connect_ack = 2;
2050 // FIXME: should also possibly destroy session with plugin!?
2051 GNUNET_ATS_address_destroyed (GST_ats,
2054 free_address (&n->primary_address);
2055 n->state = S_INIT_ATS;
2056 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2057 // FIXME: do we need to ask ATS again for suggestions?
2058 GNUNET_ATS_reset_backoff (GST_ats, peer);
2059 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2062 case S_CONNECT_RECV_ACK:
2063 /* waiting on SESSION_ACK, send ACK if one is pending */
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);
2074 /* already connected, don't care about blacklist */
2076 case S_RECONNECT_ATS:
2077 /* still waiting on ATS suggestion, don't care about blacklist */
2079 case S_RECONNECT_BLACKLIST:
2080 if ( (GNUNET_OK == result) &&
2081 (1 == n->send_connect_ack) )
2083 n->send_connect_ack = 2;
2084 send_session_connect_ack_message (bcc->na.address,
2086 n->connect_ack_timestamp);
2088 if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2089 break; /* result for an address we currently don't care about */
2090 if (GNUNET_OK == result)
2092 send_session_connect (&n->primary_address);
2093 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2094 n->state = S_RECONNECT_SENT;
2098 GNUNET_ATS_address_destroyed (GST_ats,
2101 n->state = S_RECONNECT_ATS;
2102 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2103 // FIXME: do we need to ask ATS again for suggestions?
2104 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2107 case S_RECONNECT_SENT:
2108 /* waiting on CONNECT_ACK, don't care about blacklist */
2109 if ( (GNUNET_OK == result) &&
2110 (1 == n->send_connect_ack) )
2112 n->send_connect_ack = 2;
2113 send_session_connect_ack_message (n->primary_address.address,
2114 n->primary_address.session,
2115 n->connect_ack_timestamp);
2118 case S_CONNECTED_SWITCHING_BLACKLIST:
2119 if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2120 break; /* result for an address we currently don't care about */
2121 if (GNUNET_OK == result)
2123 send_session_connect (&n->alternative_address);
2124 n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
2128 GNUNET_ATS_address_destroyed (GST_ats,
2131 free_address (&n->alternative_address);
2132 n->state = S_CONNECTED;
2135 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2136 /* waiting on CONNECT_ACK, don't care about blacklist */
2137 if ( (GNUNET_OK == result) &&
2138 (1 == n->send_connect_ack) )
2140 n->send_connect_ack = 2;
2141 send_session_connect_ack_message (n->primary_address.address,
2142 n->primary_address.session,
2143 n->connect_ack_timestamp);
2147 /* Nothing to do here, ATS will already do what can be done */
2149 case S_DISCONNECT_FINISHED:
2150 /* should not be possible */
2154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2156 free_neighbour (n, GNUNET_NO);
2160 MEMDEBUG_free (bcc->na.address, __LINE__);
2161 //GNUNET_HELLO_address_free (bcc->na.address);
2162 GNUNET_CONTAINER_DLL_remove (bc_head,
2165 MEMDEBUG_free (bcc, __LINE__);
2170 * We want to know if connecting to a particular peer via
2171 * a particular address is allowed. Check it!
2173 * @param peer identity of the peer to switch the address for
2174 * @param ts time at which the check was initiated
2175 * @param address address of the other peer, NULL if other peer
2177 * @param session session to use (or NULL)
2180 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2181 struct GNUNET_TIME_Absolute ts,
2182 const struct GNUNET_HELLO_Address *address,
2183 struct Session *session)
2185 struct BlackListCheckContext *bcc;
2186 struct GST_BlacklistCheck *bc;
2189 MEMDEBUG_malloc (sizeof (struct BlackListCheckContext), __LINE__);
2190 bcc->na.address = GNUNET_HELLO_address_copy (address);
2191 MEMDEBUG_add_alloc (bcc->na.address, GNUNET_HELLO_address_get_size (address), __LINE__);
2192 bcc->na.session = session;
2193 bcc->na.connect_timestamp = ts;
2194 GNUNET_CONTAINER_DLL_insert (bc_head,
2197 if (NULL != (bc = GST_blacklist_test_allowed (peer,
2198 address->transport_name,
2199 &handle_test_blacklist_cont, bcc)))
2201 /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2202 we must only store 'bc' if 'bc' is non-NULL... */
2207 * We received a 'SESSION_CONNECT' message from the other peer.
2208 * Consider switching to it.
2210 * @param message possibly a 'struct SessionConnectMessage' (check format)
2211 * @param peer identity of the peer to switch the address for
2212 * @param address address of the other peer, NULL if other peer
2214 * @param session session to use (or NULL)
2217 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2218 const struct GNUNET_PeerIdentity *peer,
2219 const struct GNUNET_HELLO_Address *address,
2220 struct Session *session)
2222 const struct SessionConnectMessage *scm;
2223 struct NeighbourMapEntry *n;
2224 struct GNUNET_TIME_Absolute ts;
2226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2227 "Received CONNECT message from peer `%s'\n",
2230 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2232 GNUNET_break_op (0);
2235 if (NULL == neighbours)
2236 return; /* we're shutting down */
2237 scm = (const struct SessionConnectMessage *) message;
2238 GNUNET_break_op (0 == ntohl (scm->reserved));
2239 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2240 n = lookup_neighbour (peer);
2242 n = setup_neighbour (peer);
2243 n->send_connect_ack = 1;
2244 n->connect_ack_timestamp = ts;
2248 case S_NOT_CONNECTED:
2249 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2250 /* Do a blacklist check for the new address */
2251 check_blacklist (peer, ts, address, session);
2254 /* CONNECT message takes priority over us asking ATS for address */
2255 n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2257 case S_INIT_BLACKLIST:
2258 case S_CONNECT_SENT:
2259 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2260 case S_CONNECT_RECV_ATS:
2261 case S_CONNECT_RECV_BLACKLIST:
2262 case S_CONNECT_RECV_ACK:
2263 /* It can never hurt to have an alternative address in the above cases,
2264 see if it is allowed */
2265 check_blacklist (peer, ts, address, session);
2268 /* we are already connected and can thus send the ACK immediately;
2269 still, it can never hurt to have an alternative address, so also
2270 tell ATS about it */
2271 GNUNET_assert (NULL != n->primary_address.address);
2272 GNUNET_assert (NULL != n->primary_address.session);
2273 n->send_connect_ack = 0;
2274 send_session_connect_ack_message (n->primary_address.address,
2275 n->primary_address.session, ts);
2276 check_blacklist (peer, ts, address, session);
2278 case S_RECONNECT_ATS:
2279 case S_RECONNECT_BLACKLIST:
2280 case S_RECONNECT_SENT:
2281 /* It can never hurt to have an alternative address in the above cases,
2282 see if it is allowed */
2283 check_blacklist (peer, ts, address, session);
2285 case S_CONNECTED_SWITCHING_BLACKLIST:
2286 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2287 /* we are already connected and can thus send the ACK immediately;
2288 still, it can never hurt to have an alternative address, so also
2289 tell ATS about it */
2290 GNUNET_assert (NULL != n->primary_address.address);
2291 GNUNET_assert (NULL != n->primary_address.session);
2292 n->send_connect_ack = 0;
2293 send_session_connect_ack_message (n->primary_address.address,
2294 n->primary_address.session, ts);
2295 check_blacklist (peer, ts, address, session);
2298 /* get rid of remains without terminating sessions, ready to re-try */
2299 free_neighbour (n, GNUNET_YES);
2300 n = setup_neighbour (peer);
2301 n->state = S_CONNECT_RECV_ATS;
2302 GNUNET_ATS_reset_backoff (GST_ats, peer);
2303 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2305 case S_DISCONNECT_FINISHED:
2306 /* should not be possible */
2310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2312 free_neighbour (n, GNUNET_NO);
2319 * For an existing neighbour record, set the active connection to
2320 * use the given address.
2322 * @param peer identity of the peer to switch the address for
2323 * @param address address of the other peer, NULL if other peer
2325 * @param session session to use (or NULL)
2326 * @param ats performance data
2327 * @param ats_count number of entries in ats
2328 * @param bandwidth_in inbound quota to be used when connection is up,
2329 * 0 to disconnect from peer
2330 * @param bandwidth_out outbound quota to be used when connection is up,
2331 * 0 to disconnect from peer
2334 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2335 const struct GNUNET_HELLO_Address *address,
2336 struct Session *session,
2337 const struct GNUNET_ATS_Information *ats,
2339 struct GNUNET_BANDWIDTH_Value32NBO
2341 struct GNUNET_BANDWIDTH_Value32NBO
2344 struct NeighbourMapEntry *n;
2345 struct GNUNET_TRANSPORT_PluginFunctions *papi;
2347 GNUNET_assert (address->transport_name != NULL);
2348 if (NULL == (n = lookup_neighbour (peer)))
2351 /* Obtain an session for this address from plugin */
2352 if (NULL == (papi = GST_plugins_find (address->transport_name)))
2354 /* we don't have the plugin for this address */
2355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2356 "2348 : `%s' \n", address->transport_name);
2357 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2360 if ((NULL == session) && (0 == address->address_length))
2363 if (strlen (address->transport_name) > 0)
2364 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2368 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2369 "ATS tells us to switch to address '%s' session %p for "
2370 "peer `%s' in state %s (quota in/out %u %u )\n",
2371 (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2374 print_state (n->state),
2375 ntohl (bandwidth_in.value__),
2376 ntohl (bandwidth_out.value__));
2378 if (NULL == session)
2380 session = papi->get_session (papi->cls, address);
2381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2382 "Obtained new session for peer `%s' and address '%s': %p\n",
2383 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2385 if (NULL == session)
2387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2388 "Failed to obtain new session for peer `%s' and address '%s'\n",
2389 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
2390 GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2395 case S_NOT_CONNECTED:
2397 free_neighbour (n, GNUNET_NO);
2400 set_address (&n->primary_address,
2401 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2402 n->state = S_INIT_BLACKLIST;
2403 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2404 check_blacklist (&n->id,
2405 n->connect_ack_timestamp,
2408 case S_INIT_BLACKLIST:
2409 /* ATS suggests a different address, switch again */
2410 set_address (&n->primary_address,
2411 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2412 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2413 check_blacklist (&n->id,
2414 n->connect_ack_timestamp,
2417 case S_CONNECT_SENT:
2418 /* ATS suggests a different address, switch again */
2419 set_address (&n->primary_address,
2420 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2421 n->state = S_INIT_BLACKLIST;
2422 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2423 check_blacklist (&n->id,
2424 n->connect_ack_timestamp,
2427 case S_CONNECT_RECV_ATS:
2428 set_address (&n->primary_address,
2429 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2430 n->state = S_CONNECT_RECV_BLACKLIST;
2431 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2432 check_blacklist (&n->id,
2433 n->connect_ack_timestamp,
2436 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2437 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2438 check_blacklist (&n->id,
2439 n->connect_ack_timestamp,
2442 case S_CONNECT_RECV_BLACKLIST:
2443 case S_CONNECT_RECV_ACK:
2444 /* ATS asks us to switch while we were trying to connect; switch to new
2445 address and check blacklist again */
2446 set_address (&n->primary_address,
2447 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2448 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2449 check_blacklist (&n->id,
2450 n->connect_ack_timestamp,
2454 GNUNET_assert (NULL != n->primary_address.address);
2455 GNUNET_assert (NULL != n->primary_address.session);
2456 if (n->primary_address.session == session)
2458 /* not an address change, just a quota change */
2459 set_address (&n->primary_address,
2460 address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2463 /* ATS asks us to switch a life connection; see if we can get
2464 a CONNECT_ACK on it before we actually do this! */
2465 set_address (&n->alternative_address,
2466 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2467 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2468 check_blacklist (&n->id,
2469 GNUNET_TIME_absolute_get (),
2472 case S_RECONNECT_ATS:
2473 set_address (&n->primary_address,
2474 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2475 n->state = S_RECONNECT_BLACKLIST;
2476 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2477 check_blacklist (&n->id,
2478 n->connect_ack_timestamp,
2481 case S_RECONNECT_BLACKLIST:
2482 /* ATS asks us to switch while we were trying to reconnect; switch to new
2483 address and check blacklist again */
2484 set_address (&n->primary_address,
2485 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2486 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2487 check_blacklist (&n->id,
2488 n->connect_ack_timestamp,
2491 case S_RECONNECT_SENT:
2492 /* ATS asks us to switch while we were trying to reconnect; switch to new
2493 address and check blacklist again */
2494 set_address (&n->primary_address,
2495 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2496 n->state = S_RECONNECT_BLACKLIST;
2497 n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2498 check_blacklist (&n->id,
2499 n->connect_ack_timestamp,
2502 case S_CONNECTED_SWITCHING_BLACKLIST:
2503 if (n->primary_address.session == session)
2505 /* ATS switches back to still-active session */
2506 free_address (&n->alternative_address);
2507 n->state = S_CONNECTED;
2510 /* ATS asks us to switch a life connection, update blacklist check */
2511 set_address (&n->alternative_address,
2512 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2513 check_blacklist (&n->id,
2514 GNUNET_TIME_absolute_get (),
2517 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2518 if (n->primary_address.session == session)
2520 /* ATS switches back to still-active session */
2521 free_address (&n->alternative_address);
2522 n->state = S_CONNECTED;
2525 /* ATS asks us to switch a life connection, update blacklist check */
2526 set_address (&n->alternative_address,
2527 address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2528 n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2529 check_blacklist (&n->id,
2530 GNUNET_TIME_absolute_get (),
2534 /* not going to switch addresses while disconnecting */
2536 case S_DISCONNECT_FINISHED:
2540 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2548 const struct GNUNET_PeerIdentity *key,
2551 struct NeighbourMapEntry *n = value;
2552 struct GNUNET_ATS_Information atsi[2];
2555 struct GNUNET_TIME_Relative delta;
2557 delta = GNUNET_TIME_absolute_get_difference(n->last_util_transmission, GNUNET_TIME_absolute_get());
2559 if (0 != n->util_bytes_recv)
2560 bps_in = ((1000 * 1000) * n->util_bytes_recv) / (delta.rel_value_us);
2562 if (0 != n->util_bytes_sent)
2563 bps_out = ((1000 * 1000) * n->util_bytes_sent) / (delta.rel_value_us);
2564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "`%s': Bytes sent: %u recv %u in %llu sec.\n",
2565 GNUNET_i2s (key), bps_out, bps_in, delta.rel_value_us / (1000 * 1000));
2566 atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_UP);
2567 atsi[0].value = htonl (bps_out);
2568 atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_DOWN);
2569 atsi[1].value = htonl (bps_in);
2570 GNUNET_ATS_address_update (GST_ats, n->primary_address.address,
2571 n->primary_address.session, atsi, 2);
2572 n->util_bytes_recv = 0;
2573 n->util_bytes_sent = 0;
2574 n->last_util_transmission = GNUNET_TIME_absolute_get();
2579 * Task transmitting utilization in a regular interval
2581 * @param cls the 'struct NeighbourMapEntry' for which we are running
2582 * @param tc scheduler context (unused)
2585 utilization_transmission (void *cls,
2586 const struct GNUNET_SCHEDULER_TaskContext *tc)
2588 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2590 if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2591 GNUNET_CONTAINER_multipeermap_iterate (neighbours, util_it, NULL);
2593 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2594 utilization_transmission, NULL);
2599 GST_neighbours_notify_payload (const struct GNUNET_PeerIdentity *peer,
2600 const struct GNUNET_HELLO_Address *address,
2601 struct Session *session,
2602 const struct GNUNET_MessageHeader *message)
2604 struct NeighbourMapEntry *n;
2605 n = lookup_neighbour (peer);
2611 n->util_bytes_recv += ntohs(message->size);
2616 * Master task run for every neighbour. Performs all of the time-related
2617 * activities (keep alive, send next message, disconnect if idle, finish
2618 * clean up after disconnect).
2620 * @param cls the 'struct NeighbourMapEntry' for which we are running
2621 * @param tc scheduler context (unused)
2624 master_task (void *cls,
2625 const struct GNUNET_SCHEDULER_TaskContext *tc)
2627 struct NeighbourMapEntry *n = cls;
2628 struct GNUNET_TIME_Relative delay;
2630 n->task = GNUNET_SCHEDULER_NO_TASK;
2631 delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2633 "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2634 GNUNET_i2s (&n->id),
2635 print_state(n->state),
2636 GNUNET_STRINGS_relative_time_to_string (delay,
2640 case S_NOT_CONNECTED:
2641 /* invalid state for master task, clean up */
2643 n->state = S_DISCONNECT_FINISHED;
2644 free_neighbour (n, GNUNET_NO);
2647 if (0 == delay.rel_value_us)
2649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2650 "Connection to `%s' timed out waiting for ATS to provide address\n",
2651 GNUNET_i2s (&n->id));
2652 n->state = S_DISCONNECT_FINISHED;
2653 free_neighbour (n, GNUNET_NO);
2657 case S_INIT_BLACKLIST:
2658 if (0 == delay.rel_value_us)
2660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2661 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2662 GNUNET_i2s (&n->id));
2663 n->state = S_DISCONNECT_FINISHED;
2664 free_neighbour (n, GNUNET_NO);
2668 case S_CONNECT_SENT:
2669 if (0 == delay.rel_value_us)
2671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2672 "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2673 GNUNET_i2s (&n->id));
2674 disconnect_neighbour (n);
2678 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2679 if (0 == delay.rel_value_us)
2681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2682 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2683 GNUNET_i2s (&n->id));
2684 n->state = S_DISCONNECT_FINISHED;
2685 free_neighbour (n, GNUNET_NO);
2689 case S_CONNECT_RECV_ATS:
2690 if (0 == delay.rel_value_us)
2692 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2693 "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2694 GNUNET_i2s (&n->id));
2695 n->state = S_DISCONNECT_FINISHED;
2696 free_neighbour (n, GNUNET_NO);
2700 case S_CONNECT_RECV_BLACKLIST:
2701 if (0 == delay.rel_value_us)
2703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2704 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2705 GNUNET_i2s (&n->id));
2706 n->state = S_DISCONNECT_FINISHED;
2707 free_neighbour (n, GNUNET_NO);
2711 case S_CONNECT_RECV_ACK:
2712 if (0 == delay.rel_value_us)
2714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2715 "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2716 GNUNET_i2s (&n->id));
2717 disconnect_neighbour (n);
2722 if (0 == delay.rel_value_us)
2724 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2725 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2726 GNUNET_i2s (&n->id));
2727 disconnect_neighbour (n);
2730 try_transmission_to_peer (n);
2733 case S_RECONNECT_ATS:
2734 if (0 == delay.rel_value_us)
2736 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2737 "Connection to `%s' timed out, waiting for ATS replacement address\n",
2738 GNUNET_i2s (&n->id));
2739 disconnect_neighbour (n);
2743 case S_RECONNECT_BLACKLIST:
2744 if (0 == delay.rel_value_us)
2746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2747 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2748 GNUNET_i2s (&n->id));
2749 disconnect_neighbour (n);
2753 case S_RECONNECT_SENT:
2754 if (0 == delay.rel_value_us)
2756 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2757 "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2758 GNUNET_i2s (&n->id));
2759 disconnect_neighbour (n);
2763 case S_CONNECTED_SWITCHING_BLACKLIST:
2764 if (0 == delay.rel_value_us)
2766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2767 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2768 GNUNET_i2s (&n->id));
2769 disconnect_neighbour (n);
2772 try_transmission_to_peer (n);
2775 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2776 if (0 == delay.rel_value_us)
2778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2779 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2780 GNUNET_i2s (&n->id));
2781 disconnect_neighbour (n);
2784 try_transmission_to_peer (n);
2788 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2789 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2790 GNUNET_i2s (&n->id));
2791 free_neighbour (n, GNUNET_NO);
2793 case S_DISCONNECT_FINISHED:
2794 /* how did we get here!? */
2798 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2802 if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2803 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2804 (S_CONNECTED == n->state) )
2806 /* if we are *now* in one of these three states, we're sending
2807 keep alive messages, so we need to consider the keepalive
2808 delay, not just the connection timeout */
2809 delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2812 if (GNUNET_SCHEDULER_NO_TASK == n->task)
2813 n->task = GNUNET_SCHEDULER_add_delayed (delay,
2820 * Send a SESSION_ACK message to the neighbour to confirm that we
2821 * got his CONNECT_ACK.
2823 * @param n neighbour to send the SESSION_ACK to
2826 send_session_ack_message (struct NeighbourMapEntry *n)
2828 struct GNUNET_MessageHeader msg;
2830 msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2831 msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2832 (void) send_with_session(n,
2833 (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2834 UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2840 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2841 * Consider switching to it.
2843 * @param message possibly a 'struct SessionConnectMessage' (check format)
2844 * @param peer identity of the peer to switch the address for
2845 * @param address address of the other peer, NULL if other peer
2847 * @param session session to use (or NULL)
2850 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2851 const struct GNUNET_PeerIdentity *peer,
2852 const struct GNUNET_HELLO_Address *address,
2853 struct Session *session)
2855 const struct SessionConnectMessage *scm;
2856 struct GNUNET_TIME_Absolute ts;
2857 struct NeighbourMapEntry *n;
2859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2860 "Received CONNECT_ACK message from peer `%s'\n",
2863 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2865 GNUNET_break_op (0);
2868 scm = (const struct SessionConnectMessage *) message;
2869 GNUNET_break_op (ntohl (scm->reserved) == 0);
2870 if (NULL == (n = lookup_neighbour (peer)))
2872 GNUNET_STATISTICS_update (GST_stats,
2874 ("# unexpected CONNECT_ACK messages (no peer)"),
2878 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2881 case S_NOT_CONNECTED:
2883 free_neighbour (n, GNUNET_NO);
2886 case S_INIT_BLACKLIST:
2887 GNUNET_STATISTICS_update (GST_stats,
2889 ("# unexpected CONNECT_ACK messages (not ready)"),
2892 case S_CONNECT_SENT:
2893 if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
2894 break; /* ACK does not match our original CONNECT message */
2895 n->state = S_CONNECTED;
2896 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2897 GNUNET_STATISTICS_set (GST_stats,
2898 gettext_noop ("# peers connected"),
2899 ++neighbours_connected,
2901 connect_notify_cb (callback_cls, &n->id,
2902 n->primary_address.bandwidth_in,
2903 n->primary_address.bandwidth_out);
2904 /* Tell ATS that the outbound session we created to send CONNECT was successfull */
2905 GST_ats_add_address (n->primary_address.address, n->primary_address.session);
2906 set_address (&n->primary_address,
2907 n->primary_address.address,
2908 n->primary_address.session,
2909 n->primary_address.bandwidth_in,
2910 n->primary_address.bandwidth_out,
2912 send_session_ack_message (n);
2914 case S_CONNECT_RECV_BLACKLIST_INBOUND:
2915 case S_CONNECT_RECV_ATS:
2916 case S_CONNECT_RECV_BLACKLIST:
2917 case S_CONNECT_RECV_ACK:
2918 GNUNET_STATISTICS_update (GST_stats,
2920 ("# unexpected CONNECT_ACK messages (not ready)"),
2924 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2925 send_session_ack_message (n);
2927 case S_RECONNECT_ATS:
2928 case S_RECONNECT_BLACKLIST:
2929 /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2930 to give us a new address... */
2931 GNUNET_STATISTICS_update (GST_stats,
2933 ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2936 case S_RECONNECT_SENT:
2937 /* new address worked; go back to connected! */
2938 n->state = S_CONNECTED;
2939 send_session_ack_message (n);
2941 case S_CONNECTED_SWITCHING_BLACKLIST:
2942 /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2943 send_session_ack_message (n);
2945 case S_CONNECTED_SWITCHING_CONNECT_SENT:
2946 /* new address worked; adopt it and go back to connected! */
2947 n->state = S_CONNECTED;
2948 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2949 GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2951 GST_ats_add_address (n->alternative_address.address, n->alternative_address.session);
2952 set_address (&n->primary_address,
2953 n->alternative_address.address,
2954 n->alternative_address.session,
2955 n->alternative_address.bandwidth_in,
2956 n->alternative_address.bandwidth_out,
2958 free_address (&n->alternative_address);
2959 send_session_ack_message (n);
2962 GNUNET_STATISTICS_update (GST_stats,
2964 ("# unexpected CONNECT_ACK messages (disconnecting)"),
2967 case S_DISCONNECT_FINISHED:
2971 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2979 * A session was terminated. Take note; if needed, try to get
2980 * an alternative address from ATS.
2982 * @param peer identity of the peer where the session died
2983 * @param session session that is gone
2984 * @return GNUNET_YES if this was a session used, GNUNET_NO if
2985 * this session was not in use
2988 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2989 struct Session *session)
2991 struct NeighbourMapEntry *n;
2992 struct BlackListCheckContext *bcc;
2993 struct BlackListCheckContext *bcc_next;
2995 /* make sure to cancel all ongoing blacklist checks involving 'session' */
2997 while (NULL != (bcc = bcc_next))
2999 bcc_next = bcc->next;
3000 if (bcc->na.session == session)
3002 GST_blacklist_test_cancel (bcc->bc);
3003 MEMDEBUG_free (bcc->na.address, __LINE__);
3004 //GNUNET_HELLO_address_free (bcc->na.address);
3005 GNUNET_CONTAINER_DLL_remove (bc_head,
3008 MEMDEBUG_free (bcc, __LINE__);
3011 if (NULL == (n = lookup_neighbour (peer)))
3012 return GNUNET_NO; /* can't affect us */
3013 if (session != n->primary_address.session)
3015 if (session == n->alternative_address.session)
3017 free_address (&n->alternative_address);
3018 if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
3019 (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
3020 n->state = S_CONNECTED;
3024 return GNUNET_NO; /* doesn't affect us further */
3027 n->expect_latency_response = GNUNET_NO;
3030 case S_NOT_CONNECTED:
3032 free_neighbour (n, GNUNET_NO);
3036 free_neighbour (n, GNUNET_NO);
3038 case S_INIT_BLACKLIST:
3039 case S_CONNECT_SENT:
3040 free_address (&n->primary_address);
3041 n->state = S_INIT_ATS;
3042 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3043 // FIXME: need to ask ATS for suggestions again?
3044 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3046 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3047 case S_CONNECT_RECV_ATS:
3048 case S_CONNECT_RECV_BLACKLIST:
3049 case S_CONNECT_RECV_ACK:
3050 /* error on inbound session; free neighbour entirely */
3051 free_address (&n->primary_address);
3052 free_neighbour (n, GNUNET_NO);
3055 free_address (&n->primary_address);
3056 n->state = S_RECONNECT_ATS;
3057 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3058 /* FIXME: is this ATS call needed? */
3059 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3061 case S_RECONNECT_ATS:
3062 /* we don't have an address, how can it go down? */
3065 case S_RECONNECT_BLACKLIST:
3066 case S_RECONNECT_SENT:
3067 n->state = S_RECONNECT_ATS;
3068 n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3069 // FIXME: need to ask ATS for suggestions again?
3070 n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3072 case S_CONNECTED_SWITCHING_BLACKLIST:
3073 /* primary went down while we were checking secondary against
3074 blacklist, adopt secondary as primary */
3075 free_address (&n->primary_address);
3076 n->primary_address = n->alternative_address;
3077 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3078 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3079 n->state = S_RECONNECT_BLACKLIST;
3081 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3082 /* primary went down while we were waiting for CONNECT_ACK on secondary;
3083 secondary as primary */
3084 free_address (&n->primary_address);
3085 n->primary_address = n->alternative_address;
3086 memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3087 n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3088 n->state = S_RECONNECT_SENT;
3091 free_address (&n->primary_address);
3093 case S_DISCONNECT_FINISHED:
3094 /* neighbour was freed and plugins told to terminate session */
3098 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
3102 if (GNUNET_SCHEDULER_NO_TASK != n->task)
3103 GNUNET_SCHEDULER_cancel (n->task);
3104 n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3110 * We received a 'SESSION_ACK' message from the other peer.
3111 * If we sent a 'CONNECT_ACK' last, this means we are now
3112 * connected. Otherwise, do nothing.
3114 * @param message possibly a 'struct SessionConnectMessage' (check format)
3115 * @param peer identity of the peer to switch the address for
3116 * @param address address of the other peer, NULL if other peer
3118 * @param session session to use (or NULL)
3121 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3122 const struct GNUNET_PeerIdentity *peer,
3123 const struct GNUNET_HELLO_Address *address,
3124 struct Session *session)
3126 struct NeighbourMapEntry *n;
3128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3129 "Received SESSION_ACK message from peer `%s'\n",
3131 if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3133 GNUNET_break_op (0);
3136 if (NULL == (n = lookup_neighbour (peer)))
3138 /* check if we are in a plausible state for having sent
3139 a CONNECT_ACK. If not, return, otherwise break */
3140 if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3141 (S_CONNECT_SENT != n->state) ) ||
3142 (2 != n->send_connect_ack) )
3144 GNUNET_STATISTICS_update (GST_stats,
3145 gettext_noop ("# unexpected SESSION ACK messages"), 1,
3149 n->state = S_CONNECTED;
3150 n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3151 GNUNET_STATISTICS_set (GST_stats,
3152 gettext_noop ("# peers connected"),
3153 ++neighbours_connected,
3155 connect_notify_cb (callback_cls, &n->id,
3156 n->primary_address.bandwidth_in,
3157 n->primary_address.bandwidth_out);
3159 GST_ats_add_address (n->primary_address.address, n->primary_address.session);
3160 set_address (&n->primary_address,
3161 n->primary_address.address,
3162 n->primary_address.session,
3163 n->primary_address.bandwidth_in,
3164 n->primary_address.bandwidth_out,
3170 * Test if we're connected to the given peer.
3172 * @param target peer to test
3173 * @return GNUNET_YES if we are connected, GNUNET_NO if not
3176 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3178 return test_connected (lookup_neighbour (target));
3183 * Change the incoming quota for the given peer.
3185 * @param neighbour identity of peer to change qutoa for
3186 * @param quota new quota
3189 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3190 struct GNUNET_BANDWIDTH_Value32NBO quota)
3192 struct NeighbourMapEntry *n;
3194 if (NULL == (n = lookup_neighbour (neighbour)))
3196 GNUNET_STATISTICS_update (GST_stats,
3198 ("# SET QUOTA messages ignored (no such peer)"),
3202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3203 "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3204 ntohl (quota.value__), GNUNET_i2s (&n->id));
3205 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3206 if (0 != ntohl (quota.value__))
3208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3209 GNUNET_i2s (&n->id), "SET_QUOTA");
3210 if (GNUNET_YES == test_connected (n))
3211 GNUNET_STATISTICS_update (GST_stats,
3212 gettext_noop ("# disconnects due to quota of 0"),
3214 disconnect_neighbour (n);
3219 * We received a disconnect message from the given peer,
3220 * validate and process.
3222 * @param peer sender of the message
3223 * @param msg the disconnect message
3226 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3228 const struct GNUNET_MessageHeader
3231 struct NeighbourMapEntry *n;
3232 const struct SessionDisconnectMessage *sdm;
3233 struct GNUNET_HashCode hc;
3235 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3236 "Received DISCONNECT message from peer `%s'\n",
3238 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3240 // GNUNET_break_op (0);
3241 GNUNET_STATISTICS_update (GST_stats,
3243 ("# disconnect messages ignored (old format)"), 1,
3247 sdm = (const struct SessionDisconnectMessage *) msg;
3248 if (NULL == (n = lookup_neighbour (peer)))
3249 return; /* gone already */
3250 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3252 GNUNET_STATISTICS_update (GST_stats,
3254 ("# disconnect messages ignored (timestamp)"), 1,
3258 GNUNET_CRYPTO_hash (&sdm->public_key,
3259 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
3261 if (0 != memcmp (peer, &hc, sizeof (struct GNUNET_PeerIdentity)))
3263 GNUNET_break_op (0);
3266 if (ntohl (sdm->purpose.size) !=
3267 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3268 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3269 sizeof (struct GNUNET_TIME_AbsoluteNBO))
3271 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3272 "%s message from peer `%s' has invalid size \n",
3275 GNUNET_break_op (0);
3279 GNUNET_CRYPTO_eddsa_verify
3280 (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3281 &sdm->signature, &sdm->public_key))
3283 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3284 "%s message from peer `%s' cannot be verified \n",
3287 GNUNET_break_op (0);
3290 if (GNUNET_YES == test_connected (n))
3291 GNUNET_STATISTICS_update (GST_stats,
3293 ("# other peer asked to disconnect from us"), 1,
3295 disconnect_neighbour (n);
3300 * Closure for the neighbours_iterate function.
3302 struct IteratorContext
3305 * Function to call on each connected neighbour.
3307 GST_NeighbourIterator cb;
3317 * Call the callback from the closure for each connected neighbour.
3319 * @param cls the 'struct IteratorContext'
3320 * @param key the hash of the public key of the neighbour
3321 * @param value the 'struct NeighbourMapEntry'
3322 * @return GNUNET_OK (continue to iterate)
3325 neighbours_iterate (void *cls, const struct GNUNET_PeerIdentity * key, void *value)
3327 struct IteratorContext *ic = cls;
3328 struct NeighbourMapEntry *n = value;
3330 if (GNUNET_YES == test_connected (n))
3332 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3333 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3335 if (NULL != n->primary_address.address)
3337 bandwidth_in = n->primary_address.bandwidth_in;
3338 bandwidth_out = n->primary_address.bandwidth_out;
3342 bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3343 bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3346 ic->cb (ic->cb_cls, &n->id,
3347 n->primary_address.address,
3348 bandwidth_in, bandwidth_out);
3355 * Iterate over all connected neighbours.
3357 * @param cb function to call
3358 * @param cb_cls closure for cb
3361 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3363 struct IteratorContext ic;
3365 if (NULL == neighbours)
3366 return; /* can happen during shutdown */
3369 GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3374 * If we have an active connection to the given target, it must be shutdown.
3376 * @param target peer to disconnect from
3379 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3381 struct NeighbourMapEntry *n;
3383 if (NULL == (n = lookup_neighbour (target)))
3384 return; /* not active */
3385 if (GNUNET_YES == test_connected (n))
3386 GNUNET_STATISTICS_update (GST_stats,
3388 ("# disconnected from peer upon explicit request"), 1,
3390 disconnect_neighbour (n);
3395 * Obtain current latency information for the given neighbour.
3397 * @param peer to get the latency for
3398 * @return observed latency of the address, FOREVER if the
3399 * the connection is not up
3401 struct GNUNET_TIME_Relative
3402 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3404 struct NeighbourMapEntry *n;
3406 n = lookup_neighbour (peer);
3408 return GNUNET_TIME_UNIT_FOREVER_REL;
3412 case S_CONNECTED_SWITCHING_CONNECT_SENT:
3413 case S_CONNECTED_SWITCHING_BLACKLIST:
3414 case S_RECONNECT_SENT:
3415 case S_RECONNECT_ATS:
3416 case S_RECONNECT_BLACKLIST:
3418 case S_NOT_CONNECTED:
3419 case S_INIT_BLACKLIST:
3421 case S_CONNECT_RECV_BLACKLIST_INBOUND:
3422 case S_CONNECT_RECV_ATS:
3423 case S_CONNECT_RECV_BLACKLIST:
3424 case S_CONNECT_RECV_ACK:
3425 case S_CONNECT_SENT:
3427 case S_DISCONNECT_FINISHED:
3428 return GNUNET_TIME_UNIT_FOREVER_REL;
3430 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
3434 return GNUNET_TIME_UNIT_FOREVER_REL;
3439 * Obtain current address information for the given neighbour.
3442 * @return address currently used
3444 struct GNUNET_HELLO_Address *
3445 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3447 struct NeighbourMapEntry *n;
3449 n = lookup_neighbour (peer);
3452 return n->primary_address.address;
3457 * Initialize the neighbours subsystem.
3459 * @param cls closure for callbacks
3460 * @param connect_cb function to call if we connect to a peer
3461 * @param disconnect_cb function to call if we disconnect from a peer
3462 * @param peer_address_cb function to call if we change an active address
3464 * @param max_fds maximum number of fds to use
3467 GST_neighbours_start (void *cls,
3468 NotifyConnect connect_cb,
3469 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3470 GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3471 unsigned int max_fds)
3474 connect_notify_cb = connect_cb;
3475 disconnect_notify_cb = disconnect_cb;
3476 address_change_cb = peer_address_cb;
3477 neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3478 util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3479 utilization_transmission, NULL);
3484 * Disconnect from the given neighbour.
3487 * @param key hash of neighbour's public key (not used)
3488 * @param value the 'struct NeighbourMapEntry' of the neighbour
3489 * @return GNUNET_OK (continue to iterate)
3492 disconnect_all_neighbours (void *cls,
3493 const struct GNUNET_PeerIdentity *key,
3496 struct NeighbourMapEntry *n = value;
3498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3499 "Disconnecting peer `%4s', %s\n",
3500 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3501 n->state = S_DISCONNECT_FINISHED;
3502 free_neighbour (n, GNUNET_NO);
3508 * Cleanup the neighbours subsystem.
3511 GST_neighbours_stop ()
3513 if (NULL == neighbours)
3515 if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3517 GNUNET_SCHEDULER_cancel (util_transmission_tk);
3518 util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3521 GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3522 &disconnect_all_neighbours,
3524 GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3526 callback_cls = NULL;
3527 connect_notify_cb = NULL;
3528 disconnect_notify_cb = NULL;
3529 address_change_cb = NULL;
3533 /* end of file gnunet-service-transport_neighbours.c */