Merge branch 'master' of git+ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2015 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file transport/gnunet-service-transport_neighbours.c
23  * @brief neighbour management
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet-service-transport_ats.h"
29 #include "gnunet-service-transport_neighbours.h"
30 #include "gnunet-service-transport_manipulation.h"
31 #include "gnunet-service-transport_plugins.h"
32 #include "gnunet-service-transport_validation.h"
33 #include "gnunet-service-transport.h"
34 #include "gnunet_peerinfo_service.h"
35 #include "gnunet_constants.h"
36 #include "transport.h"
37
38 /**
39  * Experimental option to ignore SessionQuotaMessages from
40  * the other peer.
41  */
42 #define IGNORE_INBOUND_QUOTA GNUNET_YES
43
44 /**
45  * Size of the neighbour hash map.
46  */
47 #define NEIGHBOUR_TABLE_SIZE 256
48
49 /**
50  * Time we give plugin to transmit DISCONNECT message before the
51  * neighbour entry self-destructs.
52  */
53 #define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply ( \
54     GNUNET_TIME_UNIT_MILLISECONDS, 500)
55
56 /**
57  * How often must a peer violate bandwidth quotas before we start
58  * to simply drop its messages?
59  */
60 #define QUOTA_VIOLATION_DROP_THRESHOLD 10
61
62 /**
63  * How long are we willing to wait for a response from ATS before timing out?
64  */
65 #define ATS_RESPONSE_TIMEOUT GNUNET_TIME_relative_multiply ( \
66     GNUNET_TIME_UNIT_SECONDS, 5)
67
68 /**
69  * How long are we willing to wait for an ACK from the other peer before
70  * giving up on our connect operation?
71  */
72 #define SETUP_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply ( \
73     GNUNET_TIME_UNIT_SECONDS, 15)
74
75 /**
76  * How long are we willing to wait for a successful reconnect if
77  * an existing connection went down?  Much shorter than the
78  * usual SETUP_CONNECTION_TIMEOUT as we do not inform the
79  * higher layers about the disconnect during this period.
80  */
81 #define FAST_RECONNECT_TIMEOUT GNUNET_TIME_UNIT_SECONDS
82
83 /**
84  * Interval to send utilization data
85  */
86 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_UNIT_SECONDS
87
88 /**
89  * State describing which kind a reply this neighbour should send
90  */
91 enum GST_ACK_State
92 {
93   /**
94    * We did not receive a SYN message for this neighbour
95    */
96   ACK_UNDEFINED = 0,
97
98   /**
99    * The neighbour received a SYN message and has to send a SYN_ACK
100    * as reply
101    */
102   ACK_SEND_SYN_ACK = 1,
103
104   /**
105    * The neighbour sent a SYN_ACK message and has to send a ACK
106    * as reply
107    */
108   ACK_SEND_ACK = 2
109 };
110
111
112 GNUNET_NETWORK_STRUCT_BEGIN
113
114 /**
115  * Message a peer sends to another to indicate that it intends to
116  * setup a connection/session for data exchange.  A 'SESSION_SYN'
117  * should be answered with a 'SESSION_SYN_ACK' with the same body
118  * to confirm.  A 'SESSION_SYN_ACK' should then be followed with
119  * a 'ACK'.  Once the 'ACK' is received, both peers
120  * should be connected.
121  */
122 struct TransportSynMessage
123 {
124   /**
125    * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN
126    * or #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK
127    */
128   struct GNUNET_MessageHeader header;
129
130   /**
131    * Always zero.
132    */
133   uint32_t reserved GNUNET_PACKED;
134
135   /**
136    * Absolute time at the sender.  Only the most recent connect
137    * message implies which session is preferred by the sender.
138    */
139   struct GNUNET_TIME_AbsoluteNBO timestamp;
140 };
141
142
143 /**
144  * Message a peer sends to another when connected to indicate that a
145  * session is in use and the peer is still alive or to respond to a keep alive.
146  * A peer sends a message with type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE
147  * to request a message with #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
148  * When the keep alive response with type is received, transport service
149  * will call the respective plugin to update the session timeout
150  */
151 struct GNUNET_ATS_SessionKeepAliveMessage
152 {
153   /**
154    * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE or
155    * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE.
156    */
157   struct GNUNET_MessageHeader header;
158
159   /**
160    * A nonce to identify the session the keep alive is used for
161    */
162   uint32_t nonce GNUNET_PACKED;
163 };
164
165
166 /**
167  * Message a peer sends to another when connected to indicate that
168  * the other peer should limit transmissions to the indicated
169  * quota.
170  */
171 struct GNUNET_ATS_SessionQuotaMessage
172 {
173   /**
174    * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA.
175    */
176   struct GNUNET_MessageHeader header;
177
178   /**
179    * Quota to use (for sending), in bytes per second.
180    */
181   uint32_t quota GNUNET_PACKED;
182 };
183
184
185 /**
186  * Message we send to the other peer to notify it that we intentionally
187  * are disconnecting (to reduce timeouts).  This is just a friendly
188  * notification, peers must not rely on always receiving disconnect
189  * messages.
190  */
191 struct GNUNET_ATS_SessionDisconnectMessage
192 {
193   /**
194    * Header of type #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT
195    */
196   struct GNUNET_MessageHeader header;
197
198   /**
199    * Always zero.
200    */
201   uint32_t reserved GNUNET_PACKED;
202
203   /**
204    * Purpose of the signature.  Extends over the timestamp.
205    * Purpose should be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
206    */
207   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
208
209   /**
210    * Absolute time at the sender.  Only the most recent connect
211    * message implies which session is preferred by the sender.
212    */
213   struct GNUNET_TIME_AbsoluteNBO timestamp;
214
215   /**
216    * Public key of the sender.
217    */
218   struct GNUNET_CRYPTO_EddsaPublicKey public_key;
219
220   /**
221    * Signature of the peer that sends us the disconnect.  Only
222    * valid if the timestamp is AFTER the timestamp from the
223    * corresponding 'SYN' message.
224    */
225   struct GNUNET_CRYPTO_EddsaSignature signature;
226 };
227
228 GNUNET_NETWORK_STRUCT_END
229
230
231 /**
232  * For each neighbour we keep a list of messages
233  * that we still want to transmit to the neighbour.
234  */
235 struct MessageQueue
236 {
237   /**
238    * This is a doubly linked list.
239    */
240   struct MessageQueue *next;
241
242   /**
243    * This is a doubly linked list.
244    */
245   struct MessageQueue *prev;
246
247   /**
248    * Function to call once we're done.
249    */
250   GST_NeighbourSendContinuation cont;
251
252   /**
253    * Closure for @e cont
254    */
255   void *cont_cls;
256
257   /**
258    * The message(s) we want to transmit, GNUNET_MessageHeader(s)
259    * stuck together in memory.  Allocated at the end of this struct.
260    */
261   const char *message_buf;
262
263   /**
264    * Size of the message buf
265    */
266   size_t message_buf_size;
267
268   /**
269    * At what time should we fail?
270    */
271   struct GNUNET_TIME_Absolute timeout;
272 };
273
274
275 /**
276  * A possible address we could use to communicate with a neighbour.
277  */
278 struct NeighbourAddress
279 {
280   /**
281    * Active session for this address.
282    */
283   struct GNUNET_ATS_Session *session;
284
285   /**
286    * Network-level address information.
287    */
288   struct GNUNET_HELLO_Address *address;
289
290   /**
291    * Timestamp of the 'SESSION_CONNECT' message we sent to the other
292    * peer for this address.  Use to check that the ACK is in response
293    * to our most recent 'SYN'.
294    */
295   struct GNUNET_TIME_Absolute connect_timestamp;
296
297   /**
298    * Inbound bandwidth from ATS for this address.
299    */
300   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
301
302   /**
303    * Outbound bandwidth from ATS for this address.
304    */
305   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
306
307   /**
308    * Did we tell ATS that this is our 'active' address?
309    */
310   int ats_active;
311
312   /**
313    * The current nonce sent in the last keep alive messages
314    */
315   uint32_t keep_alive_nonce;
316 };
317
318
319 /**
320  * Entry in neighbours.
321  */
322 struct NeighbourMapEntry
323 {
324   /**
325    * Head of list of messages we would like to send to this peer;
326    * must contain at most one message per client.
327    */
328   struct MessageQueue *messages_head;
329
330   /**
331    * Tail of list of messages we would like to send to this peer; must
332    * contain at most one message per client.
333    */
334   struct MessageQueue *messages_tail;
335
336   /**
337    * Are we currently trying to send a message? If so, which one?
338    */
339   struct MessageQueue *is_active;
340
341   /**
342    * Primary address we currently use to communicate with the neighbour.
343    */
344   struct NeighbourAddress primary_address;
345
346   /**
347    * Alternative address currently under consideration for communicating
348    * with the neighbour.
349    */
350   struct NeighbourAddress alternative_address;
351
352   /**
353    * Identity of this neighbour.
354    */
355   struct GNUNET_PeerIdentity id;
356
357   /**
358    * Main task that drives this peer (timeouts, keepalives, etc.).
359    * Always runs the #master_task().
360    */
361   struct GNUNET_SCHEDULER_Task *task;
362
363   /**
364    * Task to disconnect neighbour after we received a DISCONNECT message
365    */
366   struct GNUNET_SCHEDULER_Task *delayed_disconnect_task;
367
368   /**
369    * At what time should we sent the next keep-alive message?
370    */
371   struct GNUNET_TIME_Absolute keep_alive_time;
372
373   /**
374    * At what time did we sent the last keep-alive message?  Used
375    * to calculate round-trip time ("latency").
376    */
377   struct GNUNET_TIME_Absolute last_keep_alive_time;
378
379   /**
380    * Timestamp we should include in our next SYN_ACK message.
381    * (only valid if 'send_connect_ack' is #GNUNET_YES).  Used to build
382    * our SYN_ACK message.
383    */
384   struct GNUNET_TIME_Absolute connect_ack_timestamp;
385
386   /**
387    * ATS address suggest handle
388    */
389   struct GNUNET_ATS_ConnectivitySuggestHandle *suggest_handle;
390
391   /**
392    * Time where we should cut the connection (timeout) if we don't
393    * make progress in the state machine (or get a KEEPALIVE_RESPONSE
394    * if we are in #GNUNET_TRANSPORT_PS_CONNECTED).
395    */
396   struct GNUNET_TIME_Absolute timeout;
397
398   /**
399    * Tracker for inbound bandwidth.
400    */
401   struct GNUNET_BANDWIDTH_Tracker in_tracker;
402
403   /**
404    * How often has the other peer (recently) violated the inbound
405    * traffic limit?  Incremented by 10 per violation, decremented by 1
406    * per non-violation (for each time interval).
407    */
408   unsigned int quota_violation_count;
409
410   /**
411    * Latest quota the other peer send us in bytes per second.
412    * We should not send more, least the other peer throttle
413    * receiving our traffic.
414    */
415   struct GNUNET_BANDWIDTH_Value32NBO neighbour_receive_quota;
416
417   /**
418    * The current state of the peer.
419    */
420   enum GNUNET_TRANSPORT_PeerState state;
421
422   /**
423    * Did we sent an KEEP_ALIVE message and are we expecting a response?
424    */
425   int expect_latency_response;
426
427   /**
428    * When a peer wants to connect we have to reply to the 1st SYN message
429    * with a SYN_ACK message. But sometime we cannot send this message
430    * immediately since we do not have an address and then we have to remember
431    * to send this message as soon as we have an address.
432    *
433    * Flag to set if we still need to send a SYN_ACK message to the other peer
434    * (once we have an address to use and the peer has been allowed by our
435    * blacklist).  Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_SYN_ACK
436    * if we need to send a SYN_ACK.  Set to #ACK_SEND_ACK if we did
437    * send a SYN_ACK and should go to #S_CONNECTED upon receiving a
438    * 'ACK' (regardless of what our own state machine might say).
439    */
440   enum GST_ACK_State ack_state;
441
442   /**
443    * Tracking utilization of outbound bandwidth
444    */
445   uint32_t util_total_bytes_sent;
446
447   /**
448    * Tracking utilization of inbound bandwidth
449    */
450   uint32_t util_total_bytes_recv;
451
452   /**
453    * Date of last utilization transmission
454    */
455   struct GNUNET_TIME_Absolute last_util_transmission;
456 };
457
458
459 /**
460  * Hash map from peer identities to the respective `struct NeighbourMapEntry`.
461  */
462 static struct GNUNET_CONTAINER_MultiPeerMap *neighbours;
463
464 /**
465  * List of pending blacklist checks: head
466  */
467 static struct BlacklistCheckSwitchContext *pending_bc_head;
468
469 /**
470  * List of pending blacklist checks: tail
471  */
472 static struct BlacklistCheckSwitchContext *pending_bc_tail;
473
474 /**
475  * counter for connected neighbours
476  */
477 static unsigned int neighbours_connected;
478
479 /**
480  * Number of bytes we have currently queued for transmission.
481  */
482 static unsigned long long bytes_in_send_queue;
483
484 /**
485  * Task transmitting utilization data
486  */
487 static struct GNUNET_SCHEDULER_Task *util_transmission_tk;
488
489
490 /**
491  * Convert the given ACK state to a string.
492  *
493  * @param s state
494  * @return corresponding human-readable string
495  */
496 static char *
497 print_ack_state (enum GST_ACK_State s)
498 {
499   switch (s)
500   {
501   case ACK_UNDEFINED:
502     return "UNDEFINED";
503
504   case ACK_SEND_SYN_ACK:
505     return "SEND_SYN_ACK";
506
507   case ACK_SEND_ACK:
508     return "SEND_ACK";
509
510   default:
511     GNUNET_break (0);
512     return "N/A";
513   }
514 }
515
516
517 /**
518  * Send information about a new outbound quota to our clients.
519  * Note that the outbound quota is enforced client-side (i.e.
520  * in libgnunettransport).
521  *
522  * @param n affected peer
523  */
524 static void
525 send_outbound_quota_to_clients (struct NeighbourMapEntry *n)
526 {
527   struct QuotaSetMessage q_msg;
528   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
529
530   if (! GNUNET_TRANSPORT_is_connected (n->state))
531     return;
532 #if IGNORE_INBOUND_QUOTA
533   bandwidth_min = n->primary_address.bandwidth_out;
534 #else
535   bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
536                                               n->neighbour_receive_quota);
537 #endif
538
539   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
540               "Sending outbound quota of %u Bps for peer `%s' to all clients\n",
541               ntohl (bandwidth_min.value__),
542               GNUNET_i2s (&n->id));
543   q_msg.header.size = htons (sizeof(struct QuotaSetMessage));
544   q_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA);
545   q_msg.quota = bandwidth_min;
546   q_msg.peer = n->id;
547   GST_clients_broadcast (&q_msg.header,
548                          GNUNET_NO);
549 }
550
551
552 /**
553  * Notify our clients that another peer connected to us.
554  *
555  * @param n the peer that connected
556  */
557 static void
558 neighbours_connect_notification (struct NeighbourMapEntry *n)
559 {
560   size_t len = sizeof(struct ConnectInfoMessage);
561   char buf[len] GNUNET_ALIGN;
562   struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
563   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_min;
564
565 #if IGNORE_INBOUND_QUOTA
566   bandwidth_min = n->primary_address.bandwidth_out;
567 #else
568   bandwidth_min = GNUNET_BANDWIDTH_value_min (n->primary_address.bandwidth_out,
569                                               n->neighbour_receive_quota);
570 #endif
571   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
572               "We are now connected to peer `%s'\n",
573               GNUNET_i2s (&n->id));
574   connect_msg->header.size = htons (sizeof(buf));
575   connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
576   connect_msg->id = n->id;
577   connect_msg->quota_out = bandwidth_min;
578   GST_clients_broadcast (&connect_msg->header,
579                          GNUNET_NO);
580 }
581
582
583 /**
584  * Notify our clients (and manipulation) that a peer disconnected from
585  * us.
586  *
587  * @param n the peer that disconnected
588  */
589 static void
590 neighbours_disconnect_notification (struct NeighbourMapEntry *n)
591 {
592   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
593               "Peer `%s' disconnected\n",
594               GNUNET_i2s (&n->id));
595   GST_manipulation_peer_disconnect (&n->id);
596   GST_clients_broadcast_disconnect (&n->id);
597 }
598
599
600 /**
601  * Notify transport clients that a neighbour peer changed its active
602  * address.
603  *
604  * @param peer identity of the peer
605  * @param address address possibly NULL if peer is not connected
606  * @param state current state this peer is in
607  * @param state_timeout timeout for the current state of the peer
608  * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
609  * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
610  */
611 static void
612 neighbours_changed_notification (const struct GNUNET_PeerIdentity *peer,
613                                  const struct GNUNET_HELLO_Address *address,
614                                  enum GNUNET_TRANSPORT_PeerState state,
615                                  struct GNUNET_TIME_Absolute state_timeout,
616                                  struct GNUNET_BANDWIDTH_Value32NBO
617                                  bandwidth_in,
618                                  struct GNUNET_BANDWIDTH_Value32NBO
619                                  bandwidth_out)
620 {
621   (void) bandwidth_in;
622   (void) bandwidth_out;
623   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624               "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
625               GNUNET_i2s (peer),
626               GST_plugins_a2s (address),
627               GNUNET_TRANSPORT_ps2s (state),
628               GNUNET_STRINGS_absolute_time_to_string (state_timeout));
629   /* FIXME: include bandwidth in notification! */
630   GST_clients_broadcast_peer_notification (peer,
631                                            address,
632                                            state,
633                                            state_timeout);
634 }
635
636
637 /**
638  * Lookup a neighbour entry in the neighbours hash map.
639  *
640  * @param pid identity of the peer to look up
641  * @return the entry, NULL if there is no existing record
642  */
643 static struct NeighbourMapEntry *
644 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
645 {
646   if (NULL == neighbours)
647     return NULL;
648   return GNUNET_CONTAINER_multipeermap_get (neighbours, pid);
649 }
650
651
652 /**
653  * Test if we're connected to the given peer.
654  *
655  * @param n neighbour entry of peer to test
656  * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
657  */
658 static int
659 test_connected (struct NeighbourMapEntry *n)
660 {
661   if (NULL == n)
662     return GNUNET_NO;
663   return GNUNET_TRANSPORT_is_connected (n->state);
664 }
665
666
667 /**
668  * We don't need a given neighbour address any more.
669  * Release its resources and give appropriate notifications
670  * to ATS and other subsystems.
671  *
672  * @param na address we are done with; @a na itself must NOT be 'free'd, only the contents!
673  */
674 static void
675 free_address (struct NeighbourAddress *na)
676 {
677   if (GNUNET_YES == na->ats_active)
678     GST_validation_set_address_use (na->address,
679                                     GNUNET_NO);
680   if (NULL != na->address)
681   {
682     GST_ats_block_address (na->address,
683                            na->session);
684     GNUNET_HELLO_address_free (na->address);
685     na->address = NULL;
686   }
687   na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
688   na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
689   na->ats_active = GNUNET_NO;
690   na->keep_alive_nonce = 0;
691   na->session = NULL;
692 }
693
694
695 /**
696  * Master task run for every neighbour.  Performs all of the time-related
697  * activities (keep alive, send next message, disconnect if idle, finish
698  * clean up after disconnect).
699  *
700  * @param cls the `struct NeighbourMapEntry` for which we are running
701  */
702 static void
703 master_task (void *cls);
704
705
706 /**
707  * Set net state and state timeout for this neighbour and notify monitoring
708  *
709  * @param n the respective neighbour
710  * @param s the new state
711  * @param timeout the new timeout
712  */
713 static void
714 set_state_and_timeout (struct NeighbourMapEntry *n,
715                        enum GNUNET_TRANSPORT_PeerState s,
716                        struct GNUNET_TIME_Absolute timeout)
717 {
718   if (GNUNET_TRANSPORT_is_connected (s) &&
719       (! GNUNET_TRANSPORT_is_connected (n->state)))
720   {
721     neighbours_connect_notification (n);
722     GNUNET_STATISTICS_set (GST_stats,
723                            gettext_noop ("# peers connected"),
724                            ++neighbours_connected,
725                            GNUNET_NO);
726   }
727   if ((! GNUNET_TRANSPORT_is_connected (s)) &&
728       GNUNET_TRANSPORT_is_connected (n->state))
729   {
730     GNUNET_STATISTICS_set (GST_stats,
731                            gettext_noop ("# peers connected"),
732                            --neighbours_connected,
733                            GNUNET_NO);
734     neighbours_disconnect_notification (n);
735   }
736   n->state = s;
737   if ((timeout.abs_value_us < n->timeout.abs_value_us) &&
738       (NULL != n->task))
739   {
740     /* new timeout is earlier, reschedule master task */
741     GNUNET_SCHEDULER_cancel (n->task);
742     n->task = GNUNET_SCHEDULER_add_at (timeout,
743                                        &master_task,
744                                        n);
745   }
746   n->timeout = timeout;
747   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
748               "Neighbour `%s' changed state to %s with timeout %s\n",
749               GNUNET_i2s (&n->id),
750               GNUNET_TRANSPORT_ps2s (s),
751               GNUNET_STRINGS_absolute_time_to_string (timeout));
752   neighbours_changed_notification (&n->id,
753                                    n->primary_address.address,
754                                    n->state,
755                                    n->timeout,
756                                    n->primary_address.bandwidth_in,
757                                    n->primary_address.bandwidth_out);
758 }
759
760
761 /**
762  * Initialize the alternative address of a neighbour
763  *
764  * @param n the neighbour
765  * @param address address of the other peer, NULL if other peer
766  *                       connected to us
767  * @param session session to use (or NULL, in which case an
768  *        address must be setup)
769  * @param bandwidth_in inbound quota to be used when connection is up
770  * @param bandwidth_out outbound quota to be used when connection is up
771  */
772 static void
773 set_alternative_address (struct NeighbourMapEntry *n,
774                          const struct GNUNET_HELLO_Address *address,
775                          struct GNUNET_ATS_Session *session,
776                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
777                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
778 {
779   struct GNUNET_TRANSPORT_PluginFunctions *papi;
780
781   if (NULL == (papi = GST_plugins_find (address->transport_name)))
782   {
783     GNUNET_break (0);
784     return;
785   }
786   if (session == n->alternative_address.session)
787   {
788     n->alternative_address.bandwidth_in = bandwidth_in;
789     n->alternative_address.bandwidth_out = bandwidth_out;
790     return;
791   }
792   if (NULL != n->alternative_address.address)
793   {
794     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795                 "Replacing existing alternative address with another one\n");
796     free_address (&n->alternative_address);
797   }
798   if (NULL == session)
799     session = papi->get_session (papi->cls,
800                                  address);
801   if (NULL == session)
802   {
803     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
805                 GNUNET_i2s (&address->peer),
806                 GST_plugins_a2s (address));
807     GNUNET_STATISTICS_update (GST_stats,
808                               gettext_noop ("# session creation failed"),
809                               1,
810                               GNUNET_NO);
811     return;
812   }
813   GST_ats_new_session (address,
814                        session);
815   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
816               "Neighbour `%s' configured alternative address %s\n",
817               GNUNET_i2s (&n->id),
818               GST_plugins_a2s (address));
819
820   n->alternative_address.address = GNUNET_HELLO_address_copy (address);
821   n->alternative_address.bandwidth_in = bandwidth_in;
822   n->alternative_address.bandwidth_out = bandwidth_out;
823   n->alternative_address.session = session;
824   n->alternative_address.ats_active = GNUNET_NO;
825   n->alternative_address.keep_alive_nonce = 0;
826   GNUNET_assert (GNUNET_YES ==
827                  GST_ats_is_known (n->alternative_address.address,
828                                    n->alternative_address.session));
829 }
830
831
832 /**
833  * Transmit a message using the current session of the given
834  * neighbour.
835  *
836  * @param n entry for the recipient
837  * @param msgbuf buffer to transmit
838  * @param msgbuf_size number of bytes in @a msgbuf buffer
839  * @param priority transmission priority
840  * @param timeout transmission timeout
841  * @param use_keepalive_timeout #GNUNET_YES to use plugin-specific keep-alive
842  *        timeout (@a timeout is ignored in that case), #GNUNET_NO otherwise
843  * @param cont continuation to call when finished (can be NULL)
844  * @param cont_cls closure for @a cont
845  * @return timeout (copy of @a timeout or a calculated one if
846  *         @a use_keepalive_timeout is #GNUNET_YES.
847  */
848 static struct GNUNET_TIME_Relative
849 send_with_session (struct NeighbourMapEntry *n,
850                    const void *msgbuf,
851                    size_t msgbuf_size,
852                    uint32_t priority,
853                    struct GNUNET_TIME_Relative timeout,
854                    unsigned int use_keepalive_timeout,
855                    GNUNET_TRANSPORT_TransmitContinuation cont,
856                    void *cont_cls)
857 {
858   struct GNUNET_TRANSPORT_PluginFunctions *papi;
859   struct GNUNET_TIME_Relative result = GNUNET_TIME_UNIT_FOREVER_REL;
860
861   GNUNET_assert (NULL != n->primary_address.session);
862   if ((((NULL == (papi = GST_plugins_find (
863                     n->primary_address.address->transport_name))) ||
864         (-1 == papi->send (papi->cls,
865                            n->primary_address.session,
866                            msgbuf,
867                            msgbuf_size,
868                            priority,
869                            (result = (GNUNET_NO == use_keepalive_timeout) ?
870                                      timeout :
871                                      GNUNET_TIME_relative_divide (
872                               GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
873                               papi->
874                               query_keepalive_factor (papi->cls))),
875                            cont,
876                            cont_cls)))) &&
877       (NULL != cont))
878     cont (cont_cls,
879           &n->id,
880           GNUNET_SYSERR,
881           msgbuf_size,
882           0);
883   GST_neighbours_notify_data_sent (n->primary_address.address,
884                                    n->primary_address.session,
885                                    msgbuf_size);
886   GNUNET_break (NULL != papi);
887   return result;
888 }
889
890
891 /**
892  * Clear the primary address of a neighbour since this address is not
893  * valid anymore and notify monitoring about it
894  *
895  * @param n the neighbour
896  */
897 static void
898 unset_primary_address (struct NeighbourMapEntry *n)
899 {
900   /* Notify monitoring about change */
901   if (NULL == n->primary_address.address)
902     return;
903   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
904               "Disabling primary address\n");
905   neighbours_changed_notification (&n->id,
906                                    n->primary_address.address,
907                                    n->state,
908                                    n->timeout,
909                                    GNUNET_BANDWIDTH_value_init (0),
910                                    GNUNET_BANDWIDTH_value_init (0));
911   free_address (&n->primary_address);
912 }
913
914
915 /**
916  * Free a neighbour map entry.
917  *
918  * @param n entry to free
919  */
920 static void
921 free_neighbour (struct NeighbourMapEntry *n)
922 {
923   struct MessageQueue *mq;
924
925   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
926               "Freeing neighbour state of peer `%s'\n",
927               GNUNET_i2s (&n->id));
928   n->is_active = NULL; /* always free'd by its own continuation! */
929
930   /* fail messages currently in the queue */
931   while (NULL != (mq = n->messages_head))
932   {
933     GNUNET_CONTAINER_DLL_remove (n->messages_head,
934                                  n->messages_tail,
935                                  mq);
936     if (NULL != mq->cont)
937       mq->cont (mq->cont_cls,
938                 GNUNET_SYSERR,
939                 mq->message_buf_size,
940                 0);
941     GNUNET_free (mq);
942   }
943   /* Mark peer as disconnected */
944   set_state_and_timeout (n,
945                          GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED,
946                          GNUNET_TIME_UNIT_FOREVER_ABS);
947   /* free addresses and mark as unused */
948   unset_primary_address (n);
949
950   if (NULL != n->alternative_address.address)
951   {
952     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953                 "Cleaning up alternative address\n");
954     free_address (&n->alternative_address);
955   }
956   GNUNET_assert (GNUNET_YES ==
957                  GNUNET_CONTAINER_multipeermap_remove (neighbours,
958                                                        &n->id,
959                                                        n));
960
961   /* Cancel address requests for this peer */
962   if (NULL != n->suggest_handle)
963   {
964     GNUNET_ATS_connectivity_suggest_cancel (n->suggest_handle);
965     n->suggest_handle = NULL;
966   }
967
968   /* Cancel the disconnect task */
969   if (NULL != n->delayed_disconnect_task)
970   {
971     GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task);
972     n->delayed_disconnect_task = NULL;
973   }
974
975   /* Cancel the master task */
976   if (NULL != n->task)
977   {
978     GNUNET_SCHEDULER_cancel (n->task);
979     n->task = NULL;
980   }
981   /* free rest of memory */
982   GNUNET_free (n);
983 }
984
985
986 /**
987  * Function called when the 'DISCONNECT' message has been sent by the
988  * plugin.  Frees the neighbour --- if the entry still exists.
989  *
990  * @param cls NULL
991  * @param target identity of the neighbour that was disconnected
992  * @param result #GNUNET_OK if the disconnect got out successfully
993  * @param payload bytes payload
994  * @param physical bytes on wire
995  */
996 static void
997 send_disconnect_cont (void *cls,
998                       const struct GNUNET_PeerIdentity *target,
999                       int result,
1000                       size_t payload,
1001                       size_t physical)
1002 {
1003   struct NeighbourMapEntry *n;
1004
1005   (void) cls;
1006   (void) result;
1007   (void) payload;
1008   (void) physical;
1009   n = lookup_neighbour (target);
1010   if (NULL == n)
1011     return; /* already gone */
1012   if (GNUNET_TRANSPORT_PS_DISCONNECT != n->state)
1013     return; /* have created a fresh entry since */
1014   if (NULL != n->task)
1015     GNUNET_SCHEDULER_cancel (n->task);
1016   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1017 }
1018
1019
1020 /**
1021  * Transmit a DISCONNECT message to the other peer.
1022  *
1023  * @param n neighbour to send DISCONNECT message.
1024  */
1025 static void
1026 send_disconnect (struct NeighbourMapEntry *n)
1027 {
1028   struct GNUNET_ATS_SessionDisconnectMessage disconnect_msg;
1029
1030   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1031               "Sending DISCONNECT message to peer `%4s'\n",
1032               GNUNET_i2s (&n->id));
1033   disconnect_msg.header.size = htons (sizeof(struct
1034                                              GNUNET_ATS_SessionDisconnectMessage));
1035   disconnect_msg.header.type =
1036     htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1037   disconnect_msg.reserved = htonl (0);
1038   disconnect_msg.purpose.size =
1039     htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
1040            + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
1041            + sizeof(struct GNUNET_TIME_AbsoluteNBO));
1042   disconnect_msg.purpose.purpose =
1043     htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1044   disconnect_msg.timestamp =
1045     GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1046   disconnect_msg.public_key = GST_my_identity.public_key;
1047   GNUNET_assert (GNUNET_OK ==
1048                  GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1049                                            &disconnect_msg.purpose,
1050                                            &disconnect_msg.signature));
1051
1052   (void) send_with_session (n,
1053                             &disconnect_msg,
1054                             sizeof(disconnect_msg),
1055                             UINT32_MAX,
1056                             GNUNET_TIME_UNIT_FOREVER_REL,
1057                             GNUNET_NO,
1058                             &send_disconnect_cont,
1059                             NULL);
1060   GNUNET_STATISTICS_update (GST_stats,
1061                             gettext_noop ("# DISCONNECT messages sent"),
1062                             1,
1063                             GNUNET_NO);
1064 }
1065
1066
1067 /**
1068  * Disconnect from the given neighbour, clean up the record.
1069  *
1070  * @param n neighbour to disconnect from
1071  */
1072 static void
1073 disconnect_neighbour (struct NeighbourMapEntry *n)
1074 {
1075   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1076               "Disconnecting from peer %s in state %s\n",
1077               GNUNET_i2s (&n->id),
1078               GNUNET_TRANSPORT_ps2s (n->state));
1079   /* depending on state, notify neighbour and/or upper layers of this peer
1080      about disconnect */
1081   switch (n->state)
1082   {
1083   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
1084   case GNUNET_TRANSPORT_PS_INIT_ATS:
1085     /* other peer is completely unaware of us, no need to send DISCONNECT */
1086     free_neighbour (n);
1087     return;
1088
1089   case GNUNET_TRANSPORT_PS_SYN_SENT:
1090     send_disconnect (n);
1091     set_state_and_timeout (n,
1092                            GNUNET_TRANSPORT_PS_DISCONNECT,
1093                            GNUNET_TIME_UNIT_FOREVER_ABS);
1094     break;
1095
1096   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
1097     /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1098     free_neighbour (n);
1099     return;
1100
1101   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
1102     /* we DID ACK the other peer's request, must send DISCONNECT */
1103     send_disconnect (n);
1104     set_state_and_timeout (n,
1105                            GNUNET_TRANSPORT_PS_DISCONNECT,
1106                            GNUNET_TIME_UNIT_FOREVER_ABS);
1107     break;
1108
1109   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1110   case GNUNET_TRANSPORT_PS_CONNECTED:
1111   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1112     /* we are currently connected, need to send disconnect and do
1113        internal notifications and update statistics */
1114     send_disconnect (n);
1115     set_state_and_timeout (n,
1116                            GNUNET_TRANSPORT_PS_DISCONNECT,
1117                            GNUNET_TIME_UNIT_FOREVER_ABS);
1118     break;
1119
1120   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
1121     /* Disconnecting while waiting for an ATS address to reconnect,
1122      * cannot send DISCONNECT */
1123     free_neighbour (n);
1124     return;
1125
1126   case GNUNET_TRANSPORT_PS_DISCONNECT:
1127     /* already disconnected, ignore */
1128     break;
1129
1130   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
1131     /* already cleaned up, how did we get here!? */
1132     GNUNET_assert (0);
1133     break;
1134
1135   default:
1136     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1137                 "Unhandled state `%s'\n",
1138                 GNUNET_TRANSPORT_ps2s (n->state));
1139     GNUNET_break (0);
1140     break;
1141   }
1142   /* schedule timeout to clean up */
1143   if (NULL != n->task)
1144     GNUNET_SCHEDULER_cancel (n->task);
1145   n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1146                                           &master_task,
1147                                           n);
1148 }
1149
1150
1151 /**
1152  * Change the incoming quota for the given peer.  Updates
1153  * our own receive rate and informs the neighbour about
1154  * the new quota.
1155  *
1156  * @param n neighbour entry to change quota for
1157  * @param quota new quota
1158  * @return #GNUNET_YES if @a n is still valid, #GNUNET_NO if
1159  *   @a n was freed
1160  */
1161 static int
1162 set_incoming_quota (struct NeighbourMapEntry *n,
1163                     struct GNUNET_BANDWIDTH_Value32NBO quota)
1164 {
1165   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1166               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
1167               ntohl (quota.value__), GNUNET_i2s (&n->id));
1168   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
1169                                          quota);
1170   if (0 != ntohl (quota.value__))
1171   {
1172     struct GNUNET_ATS_SessionQuotaMessage sqm;
1173
1174     sqm.header.size = htons (sizeof(struct GNUNET_ATS_SessionQuotaMessage));
1175     sqm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA);
1176     sqm.quota = quota.value__;
1177     if (NULL != n->primary_address.session)
1178       (void) send_with_session (n,
1179                                 &sqm,
1180                                 sizeof(sqm),
1181                                 UINT32_MAX - 1,
1182                                 GNUNET_TIME_UNIT_FOREVER_REL,
1183                                 GNUNET_NO,
1184                                 NULL, NULL);
1185     return GNUNET_YES;
1186   }
1187   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1188               "Disconnecting peer `%s' due to SET_QUOTA\n",
1189               GNUNET_i2s (&n->id));
1190   if (GNUNET_YES == test_connected (n))
1191     GNUNET_STATISTICS_update (GST_stats,
1192                               gettext_noop ("# disconnects due to quota of 0"),
1193                               1, GNUNET_NO);
1194   disconnect_neighbour (n);
1195   return GNUNET_NO;
1196 }
1197
1198
1199 /**
1200  * Initialize the primary address of a neighbour
1201  *
1202  * @param n the neighbour
1203  * @param address address of the other peer, NULL if other peer
1204  *                       connected to us
1205  * @param session session to use (or NULL, in which case an
1206  *        address must be setup)
1207  * @param bandwidth_in inbound quota to be used when connection is up
1208  * @param bandwidth_out outbound quota to be used when connection is up
1209  */
1210 static void
1211 set_primary_address (struct NeighbourMapEntry *n,
1212                      const struct GNUNET_HELLO_Address *address,
1213                      struct GNUNET_ATS_Session *session,
1214                      struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
1215                      struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
1216 {
1217   if (session == n->primary_address.session)
1218   {
1219     GST_validation_set_address_use (n->primary_address.address,
1220                                     GNUNET_YES);
1221     if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
1222     {
1223       n->primary_address.bandwidth_in = bandwidth_in;
1224       if (GNUNET_YES !=
1225           set_incoming_quota (n,
1226                               bandwidth_in))
1227         return;
1228     }
1229     if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
1230     {
1231       n->primary_address.bandwidth_out = bandwidth_out;
1232       send_outbound_quota_to_clients (n);
1233     }
1234     return;
1235   }
1236   if ((NULL != n->primary_address.address) &&
1237       (0 == GNUNET_HELLO_address_cmp (address,
1238                                       n->primary_address.address)))
1239   {
1240     GNUNET_break (0);
1241     return;
1242   }
1243   if (NULL == session)
1244   {
1245     GNUNET_break (0);
1246     GST_ats_block_address (address,
1247                            session);
1248     return;
1249   }
1250   if (NULL != n->primary_address.address)
1251   {
1252     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1253                 "Replacing existing primary address with another one\n");
1254     free_address (&n->primary_address);
1255   }
1256   n->primary_address.address = GNUNET_HELLO_address_copy (address);
1257   n->primary_address.bandwidth_in = bandwidth_in;
1258   n->primary_address.bandwidth_out = bandwidth_out;
1259   n->primary_address.session = session;
1260   n->primary_address.keep_alive_nonce = 0;
1261   GNUNET_assert (GNUNET_YES ==
1262                  GST_ats_is_known (n->primary_address.address,
1263                                    n->primary_address.session));
1264   /* subsystems about address use */
1265   GST_validation_set_address_use (n->primary_address.address,
1266                                   GNUNET_YES);
1267   if (GNUNET_YES !=
1268       set_incoming_quota (n,
1269                           bandwidth_in))
1270     return;
1271   send_outbound_quota_to_clients (n);
1272   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1273               "Neighbour `%s' switched to address `%s'\n",
1274               GNUNET_i2s (&n->id),
1275               GST_plugins_a2s (address));
1276
1277   neighbours_changed_notification (&n->id,
1278                                    n->primary_address.address,
1279                                    n->state,
1280                                    n->timeout,
1281                                    n->primary_address.bandwidth_in,
1282                                    n->primary_address.bandwidth_out);
1283 }
1284
1285
1286 /**
1287  * We're done with our transmission attempt, continue processing.
1288  *
1289  * @param cls the `struct MessageQueue` of the message
1290  * @param receiver intended receiver
1291  * @param success whether it worked or not
1292  * @param size_payload bytes payload sent
1293  * @param physical bytes sent on wire
1294  */
1295 static void
1296 transmit_send_continuation (void *cls,
1297                             const struct GNUNET_PeerIdentity *receiver,
1298                             int success,
1299                             size_t size_payload,
1300                             size_t physical)
1301 {
1302   struct MessageQueue *mq = cls;
1303   struct NeighbourMapEntry *n;
1304
1305   if (NULL == (n = lookup_neighbour (receiver)))
1306   {
1307     if (NULL != mq->cont)
1308       mq->cont (mq->cont_cls,
1309                 GNUNET_SYSERR /* not connected */,
1310                 size_payload,
1311                 0);
1312     GNUNET_free (mq);
1313     return;   /* disconnect or other error while transmitting, can happen */
1314   }
1315   if (n->is_active == mq)
1316   {
1317     /* this is still "our" neighbour, remove us from its queue
1318        and allow it to send the next message now */
1319     n->is_active = NULL;
1320     if (NULL != n->task)
1321       GNUNET_SCHEDULER_cancel (n->task);
1322     n->task = GNUNET_SCHEDULER_add_now (&master_task,
1323                                         n);
1324   }
1325   if (bytes_in_send_queue < mq->message_buf_size)
1326   {
1327     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1328                 "Bytes_in_send_queue `%llu', Message_size %u, result: %s, payload %u, on wire %u\n",
1329                 bytes_in_send_queue,
1330                 (unsigned int) mq->message_buf_size,
1331                 (GNUNET_OK == success) ? "OK" : "FAIL",
1332                 (unsigned int) size_payload,
1333                 (unsigned int) physical);
1334     GNUNET_break (0);
1335   }
1336
1337   GNUNET_break (size_payload == mq->message_buf_size);
1338   bytes_in_send_queue -= mq->message_buf_size;
1339   GNUNET_STATISTICS_set (GST_stats,
1340                          gettext_noop (
1341                            "# bytes in message queue for other peers"),
1342                          bytes_in_send_queue,
1343                          GNUNET_NO);
1344   if (GNUNET_OK == success)
1345     GNUNET_STATISTICS_update (GST_stats,
1346                               gettext_noop (
1347                                 "# messages transmitted to other peers"),
1348                               1,
1349                               GNUNET_NO);
1350   else
1351     GNUNET_STATISTICS_update (GST_stats,
1352                               gettext_noop
1353                               (
1354                                 "# transmission failures for messages to other peers"),
1355                               1, GNUNET_NO);
1356   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1357               "Sending message to `%s' of type %u with %u bytes was a %s\n",
1358               GNUNET_i2s (receiver),
1359               ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1360               (unsigned int) mq->message_buf_size,
1361               (success == GNUNET_OK) ? "success" : "FAILURE");
1362   if (NULL != mq->cont)
1363     mq->cont (mq->cont_cls,
1364               success,
1365               size_payload,
1366               physical);
1367   GNUNET_free (mq);
1368 }
1369
1370
1371 /**
1372  * Check the message list for the given neighbour and if we can
1373  * send a message, do so.  This function should only be called
1374  * if the connection is at least generally ready for transmission.
1375  * While we will only send one message at a time, no bandwidth
1376  * quota management is performed here.  If a message was given to
1377  * the plugin, the continuation will automatically re-schedule
1378  * the 'master' task once the next message might be transmitted.
1379  *
1380  * @param n target peer for which to transmit
1381  */
1382 static void
1383 try_transmission_to_peer (struct NeighbourMapEntry *n)
1384 {
1385   struct MessageQueue *mq;
1386   struct GNUNET_TIME_Relative timeout;
1387
1388   if (NULL == n->primary_address.address)
1389   {
1390     /* no address, why are we here? */
1391     GNUNET_break (0);
1392     return;
1393   }
1394   if ((0 == n->primary_address.address->address_length) &&
1395       (NULL == n->primary_address.session))
1396   {
1397     /* no address, why are we here? */
1398     GNUNET_break (0);
1399     return;
1400   }
1401   if (NULL != n->is_active)
1402   {
1403     /* transmission already pending */
1404     return;
1405   }
1406
1407   /* timeout messages from the queue that are past their due date */
1408   while (NULL != (mq = n->messages_head))
1409   {
1410     timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1411     if (timeout.rel_value_us > 0)
1412       break;
1413     GNUNET_STATISTICS_update (GST_stats,
1414                               gettext_noop (
1415                                 "# messages timed out while in transport queue"),
1416                               1,
1417                               GNUNET_NO);
1418     GNUNET_CONTAINER_DLL_remove (n->messages_head,
1419                                  n->messages_tail,
1420                                  mq);
1421     n->is_active = mq;
1422     transmit_send_continuation (mq,
1423                                 &n->id,
1424                                 GNUNET_SYSERR,
1425                                 mq->message_buf_size,
1426                                 0);     /* timeout */
1427   }
1428   if (NULL == mq)
1429     return;                     /* no more messages */
1430   if (NULL == n->primary_address.address)
1431   {
1432     /* transmit_send_continuation() caused us to drop session,
1433        can't try transmission anymore. */
1434     return;
1435   }
1436
1437
1438   GNUNET_CONTAINER_DLL_remove (n->messages_head,
1439                                n->messages_tail,
1440                                mq);
1441   n->is_active = mq;
1442
1443   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1444               "Giving message with %u bytes to plugin session %p\n",
1445               (unsigned int) mq->message_buf_size,
1446               n->primary_address.session);
1447   (void) send_with_session (n,
1448                             mq->message_buf,
1449                             mq->message_buf_size,
1450                             0 /* priority */,
1451                             timeout,
1452                             GNUNET_NO,
1453                             &transmit_send_continuation,
1454                             mq);
1455 }
1456
1457
1458 /**
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).
1463  *
1464  * @param n neighbour that went idle and needs a keepalive
1465  */
1466 static void
1467 send_keepalive (struct NeighbourMapEntry *n)
1468 {
1469   struct GNUNET_ATS_SessionKeepAliveMessage m;
1470   struct GNUNET_TIME_Relative timeout;
1471   uint32_t nonce;
1472
1473   GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
1474                  (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state));
1475   if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1476     return; /* no keepalive needed at this time */
1477
1478   nonce = 0; /* 0 indicates 'not set' */
1479   while (0 == nonce)
1480     nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1481                                       UINT32_MAX);
1482
1483   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1484               "Sending KEEPALIVE to peer `%s' with nonce %u\n",
1485               GNUNET_i2s (&n->id),
1486               nonce);
1487   m.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1488   m.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1489   m.nonce = htonl (nonce);
1490
1491   timeout = send_with_session (n,
1492                                &m,
1493                                sizeof(m),
1494                                UINT32_MAX /* priority */,
1495                                GNUNET_TIME_UNIT_FOREVER_REL,
1496                                GNUNET_YES,
1497                                NULL, NULL);
1498   GNUNET_STATISTICS_update (GST_stats,
1499                             gettext_noop ("# KEEPALIVES sent"),
1500                             1,
1501                             GNUNET_NO);
1502   n->primary_address.keep_alive_nonce = nonce;
1503   n->expect_latency_response = GNUNET_YES;
1504   n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1505   n->keep_alive_time = GNUNET_TIME_relative_to_absolute (timeout);
1506 }
1507
1508
1509 /**
1510  * Keep the connection to the given neighbour alive longer,
1511  * we received a KEEPALIVE (or equivalent); send a response.
1512  *
1513  * @param neighbour neighbour to keep alive (by sending keep alive response)
1514  * @param m the keep alive message containing the nonce to respond to
1515  */
1516 void
1517 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour,
1518                           const struct GNUNET_MessageHeader *m)
1519 {
1520   struct NeighbourMapEntry *n;
1521   const struct GNUNET_ATS_SessionKeepAliveMessage *msg_in;
1522   struct GNUNET_ATS_SessionKeepAliveMessage msg;
1523
1524   if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1525   {
1526     GNUNET_break_op (0);
1527     return;
1528   }
1529
1530   msg_in = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1531   if (NULL == (n = lookup_neighbour (neighbour)))
1532   {
1533     GNUNET_STATISTICS_update (GST_stats,
1534                               gettext_noop
1535                                 ("# KEEPALIVE messages discarded (peer unknown)"),
1536                               1, GNUNET_NO);
1537     return;
1538   }
1539   if (NULL == n->primary_address.session)
1540   {
1541     GNUNET_STATISTICS_update (GST_stats,
1542                               gettext_noop
1543                                 ("# KEEPALIVE messages discarded (no session)"),
1544                               1, GNUNET_NO);
1545     return;
1546   }
1547
1548   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1549               "Received KEEPALIVE request from peer `%s' with nonce %u\n",
1550               GNUNET_i2s (&n->id),
1551               ntohl (msg_in->nonce));
1552   GNUNET_STATISTICS_update (GST_stats,
1553                             gettext_noop (
1554                               "# KEEPALIVES received in good order"),
1555                             1,
1556                             GNUNET_NO);
1557
1558   /* send reply to allow neighbour to measure latency */
1559   msg.header.size = htons (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage));
1560   msg.header.type = htons (
1561     GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1562   msg.nonce = msg_in->nonce;
1563   (void) send_with_session (n,
1564                             &msg,
1565                             sizeof(struct GNUNET_ATS_SessionKeepAliveMessage),
1566                             UINT32_MAX /* priority */,
1567                             GNUNET_TIME_UNIT_FOREVER_REL,
1568                             GNUNET_YES,
1569                             NULL, NULL);
1570 }
1571
1572
1573 /**
1574  * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1575  * latency to this peer.  Pass the updated information (existing ats
1576  * plus calculated latency) to ATS.
1577  *
1578  * @param neighbour neighbour to keep alive
1579  * @param m the message containing the keep alive response
1580  */
1581 void
1582 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour,
1583                                    const struct GNUNET_MessageHeader *m)
1584 {
1585   struct NeighbourMapEntry *n;
1586   const struct GNUNET_ATS_SessionKeepAliveMessage *msg;
1587   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1588   struct GNUNET_TIME_Relative latency;
1589
1590   if (sizeof(struct GNUNET_ATS_SessionKeepAliveMessage) != ntohs (m->size))
1591   {
1592     GNUNET_break_op (0);
1593     return;
1594   }
1595
1596   msg = (const struct GNUNET_ATS_SessionKeepAliveMessage *) m;
1597   if (NULL == (n = lookup_neighbour (neighbour)))
1598   {
1599     GNUNET_STATISTICS_update (GST_stats,
1600                               gettext_noop (
1601                                 "# KEEPALIVE_RESPONSEs discarded (not connected)"),
1602                               1,
1603                               GNUNET_NO);
1604     return;
1605   }
1606   if ((GNUNET_TRANSPORT_PS_CONNECTED != n->state) ||
1607       (GNUNET_YES != n->expect_latency_response))
1608   {
1609     GNUNET_STATISTICS_update (GST_stats,
1610                               gettext_noop (
1611                                 "# KEEPALIVE_RESPONSEs discarded (not expected)"),
1612                               1,
1613                               GNUNET_NO);
1614     return;
1615   }
1616   if (NULL == n->primary_address.address)
1617   {
1618     GNUNET_STATISTICS_update (GST_stats,
1619                               gettext_noop (
1620                                 "# KEEPALIVE_RESPONSEs discarded (address changed)"),
1621                               1,
1622                               GNUNET_NO);
1623     return;
1624   }
1625   if (n->primary_address.keep_alive_nonce != ntohl (msg->nonce))
1626   {
1627     if (0 == n->primary_address.keep_alive_nonce)
1628       GNUNET_STATISTICS_update (GST_stats,
1629                                 gettext_noop (
1630                                   "# KEEPALIVE_RESPONSEs discarded (no nonce)"),
1631                                 1,
1632                                 GNUNET_NO);
1633     else
1634       GNUNET_STATISTICS_update (GST_stats,
1635                                 gettext_noop (
1636                                   "# KEEPALIVE_RESPONSEs discarded (bad nonce)"),
1637                                 1,
1638                                 GNUNET_NO);
1639     return;
1640   }
1641   GNUNET_STATISTICS_update (GST_stats,
1642                             gettext_noop (
1643                               "# KEEPALIVE_RESPONSEs received (OK)"),
1644                             1,
1645                             GNUNET_NO);
1646
1647
1648   /* Update session timeout here */
1649   if (NULL != (papi = GST_plugins_find (
1650                  n->primary_address.address->transport_name)))
1651   {
1652     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1653                 "Updating session for peer `%s' for session %p\n",
1654                 GNUNET_i2s (&n->id),
1655                 n->primary_address.session);
1656     papi->update_session_timeout (papi->cls,
1657                                   &n->id,
1658                                   n->primary_address.session);
1659   }
1660   else
1661   {
1662     GNUNET_break (0);
1663   }
1664
1665   n->primary_address.keep_alive_nonce = 0;
1666   n->expect_latency_response = GNUNET_NO;
1667   set_state_and_timeout (n,
1668                          n->state,
1669                          GNUNET_TIME_relative_to_absolute (
1670                            GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
1671
1672   latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1673   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1674               "Received KEEPALIVE_RESPONSE from peer `%s', latency is %s\n",
1675               GNUNET_i2s (&n->id),
1676               GNUNET_STRINGS_relative_time_to_string (latency,
1677                                                       GNUNET_YES));
1678   GST_ats_update_delay (n->primary_address.address,
1679                         GNUNET_TIME_relative_divide (latency,
1680                                                      2));
1681 }
1682
1683
1684 /**
1685  * We have received a message from the given sender.  How long should
1686  * we delay before receiving more?  (Also used to keep the peer marked
1687  * as live).
1688  *
1689  * @param sender sender of the message
1690  * @param size size of the message
1691  * @param do_forward set to #GNUNET_YES if the message should be forwarded to clients
1692  *                   #GNUNET_NO if the neighbour is not connected or violates the quota,
1693  *                   #GNUNET_SYSERR if the connection is not fully up yet
1694  * @return how long to wait before reading more from this sender
1695  */
1696 struct GNUNET_TIME_Relative
1697 GST_neighbours_calculate_receive_delay (const struct
1698                                         GNUNET_PeerIdentity *sender,
1699                                         ssize_t size,
1700                                         int *do_forward)
1701 {
1702   struct NeighbourMapEntry *n;
1703   struct GNUNET_TIME_Relative ret;
1704
1705   if (NULL == neighbours)
1706   {
1707     *do_forward = GNUNET_NO;
1708     return GNUNET_TIME_UNIT_FOREVER_REL;   /* This can happen during shutdown */
1709   }
1710   if (NULL == (n = lookup_neighbour (sender)))
1711   {
1712     GNUNET_STATISTICS_update (GST_stats,
1713                               gettext_noop (
1714                                 "# messages discarded due to lack of neighbour record"),
1715                               1,
1716                               GNUNET_NO);
1717     *do_forward = GNUNET_NO;
1718     return GNUNET_TIME_UNIT_ZERO;
1719   }
1720   if (! test_connected (n))
1721   {
1722     *do_forward = GNUNET_SYSERR;
1723     return GNUNET_TIME_UNIT_ZERO;
1724   }
1725   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1726   {
1727     n->quota_violation_count++;
1728     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1729                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1730                 n->in_tracker.available_bytes_per_s__,
1731                 n->quota_violation_count);
1732     /* Discount 32k per violation */
1733     GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1734   }
1735   else
1736   {
1737     if (n->quota_violation_count > 0)
1738     {
1739       /* try to add 32k back */
1740       GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1741       n->quota_violation_count--;
1742     }
1743   }
1744   if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1745   {
1746     GNUNET_STATISTICS_update (GST_stats,
1747                               gettext_noop
1748                                 ("# bandwidth quota violations by other peers"),
1749                               1, GNUNET_NO);
1750     *do_forward = GNUNET_NO;
1751     return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1752   }
1753   *do_forward = GNUNET_YES;
1754   ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1755   if (ret.rel_value_us > 0)
1756   {
1757     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1758                 "Throttling read (%lld bytes excess at %u b/s), waiting %s before reading more.\n",
1759                 (long long) n->in_tracker.consumption_since_last_update__,
1760                 (unsigned int) n->in_tracker.available_bytes_per_s__,
1761                 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1762     GNUNET_STATISTICS_update (GST_stats,
1763                               gettext_noop ("# ms throttling suggested"),
1764                               (int64_t) ret.rel_value_us / 1000LL,
1765                               GNUNET_NO);
1766   }
1767   return ret;
1768 }
1769
1770
1771 /**
1772  * Transmit a message to the given target using the active connection.
1773  *
1774  * @param target destination
1775  * @param msg message to send
1776  * @param msg_size number of bytes in msg
1777  * @param timeout when to fail with timeout
1778  * @param cont function to call when done
1779  * @param cont_cls closure for @a cont
1780  */
1781 void
1782 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
1783                      const void *msg,
1784                      size_t msg_size,
1785                      struct GNUNET_TIME_Relative timeout,
1786                      GST_NeighbourSendContinuation cont,
1787                      void *cont_cls)
1788 {
1789   struct NeighbourMapEntry *n;
1790   struct MessageQueue *mq;
1791
1792   /* All ove these cases should never happen; they are all API violations.
1793      But we check anyway, just to be sure. */
1794   if (NULL == (n = lookup_neighbour (target)))
1795   {
1796     GNUNET_break (0);
1797     if (NULL != cont)
1798       cont (cont_cls,
1799             GNUNET_SYSERR,
1800             msg_size,
1801             0);
1802     return;
1803   }
1804   if (GNUNET_YES != test_connected (n))
1805   {
1806     GNUNET_break (0);
1807     if (NULL != cont)
1808       cont (cont_cls,
1809             GNUNET_SYSERR,
1810             msg_size,
1811             0);
1812     return;
1813   }
1814   bytes_in_send_queue += msg_size;
1815   GNUNET_STATISTICS_set (GST_stats,
1816                          gettext_noop
1817                            ("# bytes in message queue for other peers"),
1818                          bytes_in_send_queue, GNUNET_NO);
1819   mq = GNUNET_malloc (sizeof(struct MessageQueue) + msg_size);
1820   mq->cont = cont;
1821   mq->cont_cls = cont_cls;
1822   GNUNET_memcpy (&mq[1], msg, msg_size);
1823   mq->message_buf = (const char *) &mq[1];
1824   mq->message_buf_size = msg_size;
1825   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1826
1827   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1828               "Enqueueing %u bytes to send to peer %s\n",
1829               (unsigned int) msg_size,
1830               GNUNET_i2s (target));
1831   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
1832                                     n->messages_tail,
1833                                     mq);
1834   if (NULL != n->task)
1835     GNUNET_SCHEDULER_cancel (n->task);
1836   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1837 }
1838
1839
1840 /**
1841  * Continuation called from our attempt to transmitted our
1842  * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN to the specified @a
1843  * target.  Continue processing based on the @a result.  Specifically,
1844  * if we failed to transmit, discard the address we used.
1845  *
1846  * @param cls NULL
1847  * @param target which peer received the transmission
1848  * @param result #GNUNET_OK if sending worked
1849  * @param size_payload how many bytes of payload were sent (ignored)
1850  * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
1851  */
1852 static void
1853 send_session_syn_cont (void *cls,
1854                        const struct GNUNET_PeerIdentity *target,
1855                        int result,
1856                        size_t size_payload,
1857                        size_t size_on_wire)
1858 {
1859   struct NeighbourMapEntry *n;
1860
1861   (void) cls;
1862   (void) size_payload;
1863   (void) size_on_wire;
1864   n = lookup_neighbour (target);
1865   if (NULL == n)
1866   {
1867     /* SYN continuation was called after neighbor was freed,
1868      * for example due to a time out for the state or the session
1869      * used was already terminated: nothing to do here... */
1870     return;
1871   }
1872
1873   if ((GNUNET_TRANSPORT_PS_SYN_SENT != n->state) &&
1874       (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
1875       (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT != n->state))
1876   {
1877     /* SYN continuation was called after neighbor changed state,
1878      * for example due to a time out for the state or the session
1879      * used was already terminated: nothing to do here... */
1880     return;
1881   }
1882   if (GNUNET_OK == result)
1883     return;
1884
1885   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1886               _ ("Failed to send SYN message to peer `%s'\n"),
1887               GNUNET_i2s (target));
1888   switch (n->state)
1889   {
1890   case GNUNET_TRANSPORT_PS_SYN_SENT:
1891     /* Remove address and request an additional one */
1892     unset_primary_address (n);
1893     set_state_and_timeout (n,
1894                            GNUNET_TRANSPORT_PS_INIT_ATS,
1895                            GNUNET_TIME_relative_to_absolute (
1896                              FAST_RECONNECT_TIMEOUT));
1897     break;
1898
1899   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1900     /* Remove address and request an additional one */
1901     unset_primary_address (n);
1902     set_state_and_timeout (n,
1903                            GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1904                            GNUNET_TIME_relative_to_absolute (
1905                              ATS_RESPONSE_TIMEOUT));
1906     break;
1907
1908   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
1909     /* Remove address and request and go back to primary address */
1910     GNUNET_STATISTICS_update (GST_stats,
1911                               gettext_noop (
1912                                 "# Failed attempts to switch addresses (failed to send SYN CONT)"),
1913                               1,
1914                               GNUNET_NO);
1915     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1916                 "Switch failed, cleaning up alternative address\n");
1917     free_address (&n->alternative_address);
1918     set_state_and_timeout (n,
1919                            GNUNET_TRANSPORT_PS_CONNECTED,
1920                            GNUNET_TIME_relative_to_absolute (
1921                              ATS_RESPONSE_TIMEOUT));
1922     break;
1923
1924   default:
1925     disconnect_neighbour (n);
1926     break;
1927   }
1928 }
1929
1930
1931 /**
1932  * Send a SYN message via the given address.
1933  *
1934  * @param na address to use
1935  */
1936 static void
1937 send_syn (struct NeighbourAddress *na)
1938 {
1939   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1940   struct TransportSynMessage connect_msg;
1941   struct NeighbourMapEntry *n;
1942
1943   GNUNET_assert (NULL != na->session);
1944   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1945               "Sending SYN message to peer `%s' at %s\n",
1946               GNUNET_i2s (&na->address->peer),
1947               GST_plugins_a2s (na->address));
1948
1949   papi = GST_plugins_find (na->address->transport_name);
1950   GNUNET_assert (NULL != papi);
1951   GNUNET_STATISTICS_update (GST_stats,
1952                             gettext_noop
1953                               ("# SYN messages sent"),
1954                             1, GNUNET_NO);
1955   na->connect_timestamp = GNUNET_TIME_absolute_get ();
1956   connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
1957   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN);
1958   connect_msg.reserved = htonl (0);
1959   connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1960   if (-1 ==
1961       papi->send (papi->cls,
1962                   na->session,
1963                   (const char *) &connect_msg,
1964                   sizeof(struct TransportSynMessage),
1965                   UINT_MAX,
1966                   SETUP_CONNECTION_TIMEOUT,
1967                   &send_session_syn_cont, NULL))
1968   {
1969     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1970                 _ ("Failed to transmit SYN message to %s\n"),
1971                 GST_plugins_a2s (na->address));
1972     n = lookup_neighbour (&na->address->peer);
1973     if (NULL == n)
1974     {
1975       GNUNET_break (0);
1976       return;
1977     }
1978     switch (n->state)
1979     {
1980     case GNUNET_TRANSPORT_PS_SYN_SENT:
1981       /* Remove address and request and additional one */
1982       GNUNET_assert (na == &n->primary_address);
1983       unset_primary_address (n);
1984       set_state_and_timeout (n,
1985                              GNUNET_TRANSPORT_PS_INIT_ATS,
1986                              GNUNET_TIME_relative_to_absolute (
1987                                FAST_RECONNECT_TIMEOUT));
1988       /* Hard failure to send the SYN message with this address:
1989          Destroy address and session */
1990       break;
1991
1992     case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
1993       /* Remove address and request an additional one */
1994       GNUNET_assert (na == &n->primary_address);
1995       unset_primary_address (n);
1996       set_state_and_timeout (n,
1997                              GNUNET_TRANSPORT_PS_RECONNECT_ATS,
1998                              GNUNET_TIME_relative_to_absolute (
1999                                ATS_RESPONSE_TIMEOUT));
2000       break;
2001
2002     case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2003       GNUNET_assert (na == &n->alternative_address);
2004       GNUNET_STATISTICS_update (GST_stats,
2005                                 gettext_noop (
2006                                   "# Failed attempts to switch addresses (failed to send SYN)"),
2007                                 1,
2008                                 GNUNET_NO);
2009       /* Remove address and request an additional one */
2010       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2011                   "Switch failed, cleaning up alternative address\n");
2012       free_address (&n->alternative_address);
2013       set_state_and_timeout (n,
2014                              GNUNET_TRANSPORT_PS_CONNECTED,
2015                              GNUNET_TIME_relative_to_absolute (
2016                                ATS_RESPONSE_TIMEOUT));
2017       break;
2018
2019     default:
2020       GNUNET_break (0);
2021       disconnect_neighbour (n);
2022       break;
2023     }
2024     return;
2025   }
2026   GST_neighbours_notify_data_sent (na->address,
2027                                    na->session,
2028                                    sizeof(struct TransportSynMessage));
2029 }
2030
2031
2032 /**
2033  * Continuation called from our attempt to transmitted our
2034  * #GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK to the specified @a
2035  * target.  Continue processing based on the @a result.  Specifically,
2036  * if we failed to transmit, discard the address we used.
2037  *
2038  * @param cls NULL
2039  * @param target which peer received the transmission
2040  * @param result #GNUNET_OK if sending worked
2041  * @param size_payload how many bytes of payload were sent (ignored)
2042  * @param size_on_wire how much bandwidth was consumed on the wire (ignored)
2043  */
2044 static void
2045 send_session_syn_ack_cont (void *cls,
2046                            const struct GNUNET_PeerIdentity *target,
2047                            int result,
2048                            size_t size_payload,
2049                            size_t size_on_wire)
2050 {
2051   struct NeighbourMapEntry *n;
2052
2053   (void) cls;
2054   (void) size_payload;
2055   (void) size_on_wire;
2056   n = lookup_neighbour (target);
2057   if (NULL == n)
2058   {
2059     /* SYN_ACK continuation was called after neighbor was freed,
2060      * for example due to a time out for the state or the session
2061      * used was already terminated: nothing to do here... */
2062     return;
2063   }
2064
2065   if (GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state)
2066   {
2067     /* SYN_ACK continuation was called after neighbor changed state,
2068      * for example due to a time out for the state or the session
2069      * used was already terminated: nothing to do here... */
2070     return;
2071   }
2072   if (GNUNET_OK == result)
2073     return;
2074
2075   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2076               _ (
2077                 "Failed to send SYN_ACK message to peer `%s' using address `%s'\n"),
2078               GNUNET_i2s (target),
2079               GST_plugins_a2s (n->primary_address.address));
2080
2081   /* Remove address and request and additional one */
2082   /* FIXME: what if the neighbour's primary address
2083      changed in the meantime? Might want to instead
2084      pass "something" around in closure to be sure. */
2085   unset_primary_address (n);
2086   n->ack_state = ACK_SEND_SYN_ACK;
2087   set_state_and_timeout (n,
2088                          GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2089                          GNUNET_TIME_relative_to_absolute (
2090                            ATS_RESPONSE_TIMEOUT));
2091 }
2092
2093
2094 /**
2095  * Send a SYN_ACK message via the given address.
2096  *
2097  * @param na address and session to use
2098  * @param timestamp timestamp to use for the ACK message
2099  * @return #GNUNET_SYSERR if sending immediately failed, #GNUNET_OK otherwise
2100  */
2101 static void
2102 send_syn_ack_message (struct NeighbourAddress *na,
2103                       struct GNUNET_TIME_Absolute timestamp)
2104 {
2105   const struct GNUNET_HELLO_Address *address = na->address;
2106   struct GNUNET_ATS_Session *session = na->session;
2107   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2108   struct TransportSynMessage connect_msg;
2109   struct NeighbourMapEntry *n;
2110
2111   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2112               "Sending SYN_ACK to peer `%s'\n",
2113               GNUNET_i2s (&address->peer));
2114
2115   if (NULL == (papi = GST_plugins_find (address->transport_name)))
2116   {
2117     GNUNET_break (0);
2118     return;
2119   }
2120   if (NULL == session)
2121     session = papi->get_session (papi->cls,
2122                                  address);
2123   if (NULL == session)
2124   {
2125     GNUNET_break (0);
2126     return;
2127   }
2128   GST_ats_new_session (address,
2129                        session);
2130   GNUNET_STATISTICS_update (GST_stats,
2131                             gettext_noop
2132                               ("# SYN_ACK messages sent"),
2133                             1, GNUNET_NO);
2134   connect_msg.header.size = htons (sizeof(struct TransportSynMessage));
2135   connect_msg.header.type = htons (
2136     GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK);
2137   connect_msg.reserved = htonl (0);
2138   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
2139
2140   if (GNUNET_SYSERR ==
2141       papi->send (papi->cls,
2142                   session,
2143                   (const char *) &connect_msg,
2144                   sizeof(struct TransportSynMessage),
2145                   UINT_MAX,
2146                   GNUNET_TIME_UNIT_FOREVER_REL,
2147                   &send_session_syn_ack_cont, NULL))
2148   {
2149     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2150                 _ ("Failed to transmit SYN_ACK message to %s\n"),
2151                 GST_plugins_a2s (address));
2152
2153     n = lookup_neighbour (&address->peer);
2154     if (NULL == n)
2155     {
2156       GNUNET_break (0);
2157       return;
2158     }
2159     /* Remove address and request and additional one */
2160     unset_primary_address (n);
2161     n->ack_state = ACK_SEND_SYN_ACK;
2162     set_state_and_timeout (n,
2163                            GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2164                            GNUNET_TIME_relative_to_absolute (
2165                              ATS_RESPONSE_TIMEOUT));
2166     return;
2167   }
2168 }
2169
2170
2171 /**
2172  * Function called by the bandwidth tracker for a peer whenever
2173  * the tracker's state changed such that we need to recalculate
2174  * the delay for flow control.  We calculate the latest delay
2175  * and inform the plugin (if applicable).
2176  *
2177  * @param cls the `struct NeighbourMapEntry` to update calculations for
2178  */
2179 static void
2180 inbound_bw_tracker_update (void *cls)
2181 {
2182   struct NeighbourMapEntry *n = cls;
2183   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2184   struct GNUNET_TIME_Relative delay;
2185   int do_forward;
2186
2187   if (NULL == n->primary_address.address)
2188     return; /* not active, ignore */
2189   papi = GST_plugins_find (n->primary_address.address->transport_name);
2190   GNUNET_assert (NULL != papi);
2191   if (NULL == papi->update_inbound_delay)
2192     return;
2193   delay = GST_neighbours_calculate_receive_delay (&n->id,
2194                                                   0,
2195                                                   &do_forward);
2196   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197               "New inbound delay for peer `%s' is %llu ms\n",
2198               GNUNET_i2s (&n->id),
2199               (unsigned long long) delay.rel_value_us / 1000LL);
2200   if (NULL == n->primary_address.session)
2201     return;
2202   papi->update_inbound_delay (papi->cls,
2203                               &n->id,
2204                               n->primary_address.session,
2205                               delay);
2206 }
2207
2208
2209 /**
2210  * Create a fresh entry in the neighbour map for the given peer
2211  *
2212  * @param peer peer to create an entry for
2213  * @return new neighbour map entry
2214  */
2215 static struct NeighbourMapEntry *
2216 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
2217 {
2218   struct NeighbourMapEntry *n;
2219
2220   if (0 ==
2221       memcmp (&GST_my_identity,
2222               peer,
2223               sizeof(struct GNUNET_PeerIdentity)))
2224   {
2225     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2226                 "Cowardly refusing to consider myself my neighbour!\n");
2227     return NULL;
2228   }
2229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2230               "Creating new neighbour entry for `%s'\n",
2231               GNUNET_i2s (peer));
2232   n = GNUNET_new (struct NeighbourMapEntry);
2233   n->id = *peer;
2234   n->ack_state = ACK_UNDEFINED;
2235   n->last_util_transmission = GNUNET_TIME_absolute_get ();
2236   n->neighbour_receive_quota = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
2237   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
2238                                  &inbound_bw_tracker_update,
2239                                  n,
2240                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
2241                                  MAX_BANDWIDTH_CARRY_S);
2242   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
2243   set_state_and_timeout (n,
2244                          GNUNET_TRANSPORT_PS_NOT_CONNECTED,
2245                          GNUNET_TIME_UNIT_FOREVER_ABS);
2246   GNUNET_assert (GNUNET_OK ==
2247                  GNUNET_CONTAINER_multipeermap_put (neighbours,
2248                                                     &n->id,
2249                                                     n,
2250                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2251   n->suggest_handle = GNUNET_ATS_connectivity_suggest (GST_ats_connect,
2252                                                        peer,
2253                                                        0);
2254
2255   return n;
2256 }
2257
2258
2259 /**
2260  * Entry in a DLL we use to keep track of pending blacklist checks.
2261  */
2262 struct BlacklistCheckSwitchContext
2263 {
2264   /**
2265    * DLL prev pointer.
2266    */
2267   struct BlacklistCheckSwitchContext *prev;
2268
2269   /**
2270    * DLL next pointer.
2271    */
2272   struct BlacklistCheckSwitchContext *next;
2273
2274   /**
2275    * Handle to the blacklist check we are performing.
2276    */
2277   struct GST_BlacklistCheck *blc;
2278
2279   /**
2280    * Inbound bandwidth that was assigned to @e address.
2281    */
2282   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
2283
2284   /**
2285    * Outbound bandwidth that was assigned to @e address.
2286    */
2287   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
2288 };
2289
2290
2291 /**
2292  * We received a 'SYN' message from the other peer.
2293  * Consider switching to it.
2294  *
2295  * @param message possibly a `struct TransportSynMessage` (check format)
2296  * @param peer identity of the peer to switch the address for
2297  * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
2298  */
2299 int
2300 GST_neighbours_handle_session_syn (const struct GNUNET_MessageHeader *message,
2301                                    const struct GNUNET_PeerIdentity *peer)
2302 {
2303   const struct TransportSynMessage *scm;
2304   struct NeighbourMapEntry *n;
2305   struct GNUNET_TIME_Absolute ts;
2306
2307   if (ntohs (message->size) != sizeof(struct TransportSynMessage))
2308   {
2309     GNUNET_break_op (0);
2310     return GNUNET_SYSERR;
2311   }
2312   GNUNET_STATISTICS_update (GST_stats,
2313                             gettext_noop
2314                               ("# SYN messages received"),
2315                             1, GNUNET_NO);
2316   if (NULL == neighbours)
2317   {
2318     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2319                 _ (
2320                   "SYN request from peer `%s' ignored due impending shutdown\n"),
2321                 GNUNET_i2s (peer));
2322     return GNUNET_OK;   /* we're shutting down */
2323   }
2324   scm = (const struct TransportSynMessage *) message;
2325   GNUNET_break_op (0 == ntohl (scm->reserved));
2326   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2327   if (0 ==
2328       memcmp (&GST_my_identity,
2329               peer,
2330               sizeof(struct GNUNET_PeerIdentity)))
2331   {
2332     /* loopback connection-to-self, ignore */
2333     return GNUNET_SYSERR;
2334   }
2335   n = lookup_neighbour (peer);
2336   if (NULL == n)
2337   {
2338     /* This is a new neighbour and set to not connected */
2339     n = setup_neighbour (peer);
2340     GNUNET_assert (NULL != n);
2341   }
2342
2343   /* Remember this SYN message in neighbour */
2344   n->ack_state = ACK_SEND_SYN_ACK;
2345   n->connect_ack_timestamp = ts;
2346
2347   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2348               "Received SYN for peer `%s' in state %s/%s\n",
2349               GNUNET_i2s (peer),
2350               GNUNET_TRANSPORT_ps2s (n->state),
2351               print_ack_state (n->ack_state));
2352
2353   switch (n->state)
2354   {
2355   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2356     /* Request an address from ATS to send SYN_ACK to this peer */
2357     set_state_and_timeout (n,
2358                            GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2359                            GNUNET_TIME_relative_to_absolute (
2360                              ATS_RESPONSE_TIMEOUT));
2361     break;
2362
2363   case GNUNET_TRANSPORT_PS_INIT_ATS:
2364     /* SYN message takes priority over us asking ATS for address:
2365      * Wait for ATS to suggest an address and send SYN_ACK */
2366     set_state_and_timeout (n,
2367                            GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2368                            GNUNET_TIME_relative_to_absolute (
2369                              ATS_RESPONSE_TIMEOUT));
2370     break;
2371
2372   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2373     /* We already wait for an address to send an SYN_ACK */
2374     break;
2375
2376   case GNUNET_TRANSPORT_PS_SYN_SENT:
2377   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2378     /* Send ACK immediately */
2379     n->ack_state = ACK_SEND_ACK;
2380     send_syn_ack_message (&n->primary_address,
2381                           ts);
2382     break;
2383
2384   case GNUNET_TRANSPORT_PS_CONNECTED:
2385     /* we are already connected and can thus send the ACK immediately */
2386     GNUNET_assert (NULL != n->primary_address.address);
2387     GNUNET_assert (NULL != n->primary_address.session);
2388     n->ack_state = ACK_SEND_ACK;
2389     send_syn_ack_message (&n->primary_address,
2390                           ts);
2391     break;
2392
2393   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2394     /* We wait for ATS address suggestion */
2395     break;
2396
2397   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2398     /* We received a SYN message while waiting for a SYN_ACK in fast
2399      * reconnect. Send SYN_ACK immediately */
2400     n->ack_state = ACK_SEND_ACK;
2401     send_syn_ack_message (&n->primary_address,
2402                           n->connect_ack_timestamp);
2403     break;
2404
2405   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2406     /* We are already connected and can thus send the ACK immediately;
2407        still, it can never hurt to have an alternative address, so also
2408        tell ATS  about it */
2409     GNUNET_assert (NULL != n->primary_address.address);
2410     GNUNET_assert (NULL != n->primary_address.session);
2411     n->ack_state = ACK_SEND_ACK;
2412     send_syn_ack_message (&n->primary_address,
2413                           ts);
2414     break;
2415
2416   case GNUNET_TRANSPORT_PS_DISCONNECT:
2417     /* Get rid of remains and re-try */
2418     free_neighbour (n);
2419     n = setup_neighbour (peer);
2420     GNUNET_assert (NULL != n);
2421     /* Remember the SYN time stamp for ACK message */
2422     n->ack_state = ACK_SEND_SYN_ACK;
2423     n->connect_ack_timestamp = ts;
2424     /* Request an address for the peer */
2425     set_state_and_timeout (n,
2426                            GNUNET_TRANSPORT_PS_SYN_RECV_ATS,
2427                            GNUNET_TIME_relative_to_absolute (
2428                              ATS_RESPONSE_TIMEOUT));
2429     break;
2430
2431   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2432     /* should not be possible */
2433     GNUNET_assert (0);
2434     break;
2435
2436   default:
2437     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2438                 "Unhandled state `%s'\n",
2439                 GNUNET_TRANSPORT_ps2s (n->state));
2440     GNUNET_break (0);
2441     return GNUNET_SYSERR;
2442   }
2443   return GNUNET_OK;
2444 }
2445
2446
2447 /**
2448  * Check if the given @a address is the same that we are already
2449  * using for the respective neighbour. If so, update the bandwidth
2450  * assignment and possibly the session and return #GNUNET_OK.
2451  * If the new address is different from what the neighbour is
2452  * using right now, return #GNUNET_NO.
2453  *
2454  * @param address address of the other peer,
2455  * @param session session to use or NULL if transport should initiate a session
2456  * @param bandwidth_in inbound quota to be used when connection is up,
2457  *      0 to disconnect from peer
2458  * @param bandwidth_out outbound quota to be used when connection is up,
2459  *      0 to disconnect from peer
2460  * @return #GNUNET_OK if we were able to just update the bandwidth and session,
2461  *         #GNUNET_NO if more extensive changes are required (address changed)
2462  */
2463 static int
2464 try_run_fast_ats_update (const struct GNUNET_HELLO_Address *address,
2465                          struct GNUNET_ATS_Session *session,
2466                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
2467                          struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
2468 {
2469   struct NeighbourMapEntry *n;
2470
2471   n = lookup_neighbour (&address->peer);
2472   if ((NULL == n) ||
2473       (NULL == n->primary_address.address) ||
2474       (0 != GNUNET_HELLO_address_cmp (address,
2475                                       n->primary_address.address)))
2476     return GNUNET_NO;
2477   /* We are not really switching addresses, but merely adjusting
2478      session and/or bandwidth, can do fast ATS update! */
2479   if (session != n->primary_address.session)
2480   {
2481     /* switch to a different session, but keeping same address; could
2482        happen if there is a 2nd inbound connection */
2483     n->primary_address.session = session;
2484     GNUNET_assert (GNUNET_YES ==
2485                    GST_ats_is_known (n->primary_address.address,
2486                                      n->primary_address.session));
2487   }
2488   if (n->primary_address.bandwidth_in.value__ != bandwidth_in.value__)
2489   {
2490     n->primary_address.bandwidth_in = bandwidth_in;
2491     if (GNUNET_YES !=
2492         set_incoming_quota (n,
2493                             bandwidth_in))
2494       return GNUNET_NO;
2495   }
2496   if (n->primary_address.bandwidth_out.value__ != bandwidth_out.value__)
2497   {
2498     n->primary_address.bandwidth_out = bandwidth_out;
2499     send_outbound_quota_to_clients (n);
2500   }
2501   return GNUNET_OK;
2502 }
2503
2504
2505 /**
2506  * We've been asked to switch addresses, and just now got the result
2507  * from the blacklist check to see if this is allowed.
2508  *
2509  * @param cls the `struct BlacklistCheckSwitchContext` with
2510  *        the information about the future address
2511  * @param peer the peer we may switch addresses on
2512  * @param address address associated with the request
2513  * @param session session associated with the request
2514  * @param result #GNUNET_OK if the connection is allowed,
2515  *               #GNUNET_NO if not,
2516  *               #GNUNET_SYSERR if operation was aborted
2517  */
2518 static void
2519 switch_address_bl_check_cont (void *cls,
2520                               const struct GNUNET_PeerIdentity *peer,
2521                               const struct GNUNET_HELLO_Address *address,
2522                               struct GNUNET_ATS_Session *session,
2523                               int result)
2524 {
2525   struct BlacklistCheckSwitchContext *blc_ctx = cls;
2526   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2527   struct NeighbourMapEntry *n;
2528
2529   if (GNUNET_SYSERR == result)
2530     goto cleanup;
2531
2532   papi = GST_plugins_find (address->transport_name);
2533   if (NULL == papi)
2534   {
2535     /* This can happen during shutdown. */
2536     goto cleanup;
2537   }
2538
2539   if (GNUNET_NO == result)
2540   {
2541     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2542                 "Blacklist denied to switch to suggested address `%s' session %p for peer `%s'\n",
2543                 GST_plugins_a2s (address),
2544                 session,
2545                 GNUNET_i2s (peer));
2546     GNUNET_STATISTICS_update (GST_stats,
2547                               "# ATS suggestions ignored (blacklist denied)",
2548                               1,
2549                               GNUNET_NO);
2550     if (NULL != session)
2551       papi->disconnect_session (papi->cls,
2552                                 session);
2553     if (GNUNET_YES !=
2554         GNUNET_HELLO_address_check_option (address,
2555                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND))
2556       GST_ats_block_address (address,
2557                              NULL);
2558     goto cleanup;
2559   }
2560
2561
2562   if (NULL == session)
2563   {
2564     /* need to create a session, ATS only gave us an address */
2565     session = papi->get_session (papi->cls,
2566                                  address);
2567     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568                 "Obtained new session for peer `%s' and  address '%s': %p\n",
2569                 GNUNET_i2s (&address->peer),
2570                 GST_plugins_a2s (address),
2571                 session);
2572     if (NULL != session)
2573       GST_ats_new_session (address,
2574                            session);
2575   }
2576   if (NULL == session)
2577   {
2578     /* session creation failed, bad!, fail! */
2579     GNUNET_STATISTICS_update (GST_stats,
2580                               "# ATS suggestions ignored (failed to create session)",
2581                               1,
2582                               GNUNET_NO);
2583     /* No session could be obtained, remove blacklist check and clean up */
2584     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2585                 "Failed to obtain new session for peer `%s' and address '%s'\n",
2586                 GNUNET_i2s (&address->peer),
2587                 GST_plugins_a2s (address));
2588     GST_ats_block_address (address,
2589                            session);
2590     goto cleanup;
2591   }
2592
2593   /* We did this check already before going into blacklist, but
2594      it is theoretically possible that the situation changed in
2595      the meantime, hence we check again here */
2596   if (GNUNET_OK ==
2597       try_run_fast_ats_update (address,
2598                                session,
2599                                blc_ctx->bandwidth_in,
2600                                blc_ctx->bandwidth_out))
2601     goto cleanup; /* was just a minor update, we're done */
2602
2603   /* check if we also need to setup the neighbour entry */
2604   if (NULL == (n = lookup_neighbour (peer)))
2605   {
2606     n = setup_neighbour (peer);
2607     if (NULL == n)
2608     {
2609       /* not sure how this can happen... */
2610       GNUNET_break (0);
2611       goto cleanup;
2612     }
2613     n->state = GNUNET_TRANSPORT_PS_INIT_ATS;
2614   }
2615
2616   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2617               "Peer `%s' switches to address `%s'\n",
2618               GNUNET_i2s (&address->peer),
2619               GST_plugins_a2s (address));
2620
2621   switch (n->state)
2622   {
2623   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
2624     GNUNET_break (0);
2625     GST_ats_block_address (address,
2626                            session);
2627     free_neighbour (n);
2628     return;
2629
2630   case GNUNET_TRANSPORT_PS_INIT_ATS:
2631     /* We requested an address and ATS suggests one:
2632      * set primary address and send SYN message*/
2633     set_primary_address (n,
2634                          address,
2635                          session,
2636                          blc_ctx->bandwidth_in,
2637                          blc_ctx->bandwidth_out);
2638     if (ACK_SEND_SYN_ACK == n->ack_state)
2639     {
2640       /* Send pending SYN_ACK message */
2641       n->ack_state = ACK_SEND_ACK;
2642       send_syn_ack_message (&n->primary_address,
2643                             n->connect_ack_timestamp);
2644     }
2645     set_state_and_timeout (n,
2646                            GNUNET_TRANSPORT_PS_SYN_SENT,
2647                            GNUNET_TIME_relative_to_absolute (
2648                              SETUP_CONNECTION_TIMEOUT));
2649     send_syn (&n->primary_address);
2650     break;
2651
2652   case GNUNET_TRANSPORT_PS_SYN_SENT:
2653     /* ATS suggested a new address while waiting for an SYN_ACK:
2654      * Switch and send new SYN */
2655     /* ATS suggests a different address, switch again */
2656     set_primary_address (n,
2657                          address,
2658                          session,
2659                          blc_ctx->bandwidth_in,
2660                          blc_ctx->bandwidth_out);
2661     if (ACK_SEND_SYN_ACK == n->ack_state)
2662     {
2663       /* Send pending SYN_ACK message */
2664       n->ack_state = ACK_SEND_ACK;
2665       send_syn_ack_message (&n->primary_address,
2666                             n->connect_ack_timestamp);
2667     }
2668     set_state_and_timeout (n,
2669                            GNUNET_TRANSPORT_PS_SYN_SENT,
2670                            GNUNET_TIME_relative_to_absolute (
2671                              SETUP_CONNECTION_TIMEOUT));
2672     send_syn (&n->primary_address);
2673     break;
2674
2675   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
2676     /* We requested an address and ATS suggests one:
2677     * set primary address and send SYN_ACK message*/
2678     set_primary_address (n,
2679                          address,
2680                          session,
2681                          blc_ctx->bandwidth_in,
2682                          blc_ctx->bandwidth_out);
2683     /* Send an ACK message as a response to the SYN msg */
2684     set_state_and_timeout (n,
2685                            GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2686                            GNUNET_TIME_relative_to_absolute (
2687                              SETUP_CONNECTION_TIMEOUT));
2688     send_syn_ack_message (&n->primary_address,
2689                           n->connect_ack_timestamp);
2690     if ((ACK_SEND_SYN_ACK == n->ack_state) ||
2691         (ACK_UNDEFINED == n->ack_state))
2692       n->ack_state = ACK_SEND_ACK;
2693     break;
2694
2695   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
2696     /* ATS asks us to switch while we were trying to connect; switch to new
2697        address and check blacklist again */
2698     if ((ACK_SEND_SYN_ACK == n->ack_state))
2699     {
2700       n->ack_state = ACK_SEND_ACK;
2701       send_syn_ack_message (&n->primary_address,
2702                             n->connect_ack_timestamp);
2703     }
2704     set_primary_address (n,
2705                          address,
2706                          session,
2707                          blc_ctx->bandwidth_in,
2708                          blc_ctx->bandwidth_out);
2709     set_state_and_timeout (n,
2710                            GNUNET_TRANSPORT_PS_SYN_RECV_ACK,
2711                            GNUNET_TIME_relative_to_absolute (
2712                              SETUP_CONNECTION_TIMEOUT));
2713     break;
2714
2715   case GNUNET_TRANSPORT_PS_CONNECTED:
2716     GNUNET_assert (NULL != n->primary_address.address);
2717     GNUNET_assert (NULL != n->primary_address.session);
2718     GNUNET_break (n->primary_address.session != session);
2719     /* ATS asks us to switch a life connection; see if we can get
2720        a SYN_ACK on it before we actually do this! */
2721     set_alternative_address (n,
2722                              address,
2723                              session,
2724                              blc_ctx->bandwidth_in,
2725                              blc_ctx->bandwidth_out);
2726     set_state_and_timeout (n,
2727                            GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2728                            GNUNET_TIME_relative_to_absolute (
2729                              SETUP_CONNECTION_TIMEOUT));
2730     GNUNET_STATISTICS_update (GST_stats,
2731                               gettext_noop ("# Attempts to switch addresses"),
2732                               1,
2733                               GNUNET_NO);
2734     send_syn (&n->alternative_address);
2735     break;
2736
2737   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
2738     set_primary_address (n,
2739                          address,
2740                          session,
2741                          blc_ctx->bandwidth_in,
2742                          blc_ctx->bandwidth_out);
2743     if (ACK_SEND_SYN_ACK == n->ack_state)
2744     {
2745       /* Send pending SYN_ACK message */
2746       n->ack_state = ACK_SEND_ACK;
2747       send_syn_ack_message (&n->primary_address,
2748                             n->connect_ack_timestamp);
2749     }
2750     set_state_and_timeout (n,
2751                            GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2752                            GNUNET_TIME_relative_to_absolute (
2753                              FAST_RECONNECT_TIMEOUT));
2754     send_syn (&n->primary_address);
2755     break;
2756
2757   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
2758     /* ATS asks us to switch while we were trying to reconnect; switch to new
2759        address and send SYN again */
2760     set_primary_address (n,
2761                          address,
2762                          session,
2763                          blc_ctx->bandwidth_in,
2764                          blc_ctx->bandwidth_out);
2765     set_state_and_timeout (n,
2766                            GNUNET_TRANSPORT_PS_RECONNECT_SENT,
2767                            GNUNET_TIME_relative_to_absolute (
2768                              FAST_RECONNECT_TIMEOUT));
2769     send_syn (&n->primary_address);
2770     break;
2771
2772   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
2773     if ((0 == GNUNET_HELLO_address_cmp (n->primary_address.address,
2774                                         address)) &&
2775         (n->primary_address.session == session))
2776     {
2777       /* ATS switches back to still-active session */
2778       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2779                   "ATS double-switched, cleaning up alternative address\n");
2780       free_address (&n->alternative_address);
2781       set_state_and_timeout (n,
2782                              GNUNET_TRANSPORT_PS_CONNECTED,
2783                              n->timeout);
2784       break;
2785     }
2786     /* ATS asks us to switch a life connection, send */
2787     set_alternative_address (n,
2788                              address,
2789                              session,
2790                              blc_ctx->bandwidth_in,
2791                              blc_ctx->bandwidth_out);
2792     set_state_and_timeout (n,
2793                            GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT,
2794                            GNUNET_TIME_relative_to_absolute (
2795                              SETUP_CONNECTION_TIMEOUT));
2796     send_syn (&n->alternative_address);
2797     break;
2798
2799   case GNUNET_TRANSPORT_PS_DISCONNECT:
2800     /* not going to switch addresses while disconnecting */
2801     GNUNET_STATISTICS_update (GST_stats,
2802                               "# ATS suggestion ignored (disconnecting)",
2803                               1,
2804                               GNUNET_NO);
2805     return;
2806
2807   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
2808     GNUNET_assert (0);
2809     break;
2810
2811   default:
2812     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2813                 "Unhandled state `%s'\n",
2814                 GNUNET_TRANSPORT_ps2s (n->state));
2815     GNUNET_break (0);
2816     break;
2817   }
2818 cleanup:
2819   GNUNET_CONTAINER_DLL_remove (pending_bc_head,
2820                                pending_bc_tail,
2821                                blc_ctx);
2822   GNUNET_free (blc_ctx);
2823 }
2824
2825
2826 /**
2827  * For the given peer, switch to this address.
2828  *
2829  * Before accepting this addresses and actively using it, a blacklist check
2830  * is performed.
2831  *
2832  * If any check fails or the suggestion can somehow not be followed, we
2833  * MUST call #GST_ats_block_address() to tell ATS that the suggestion
2834  * could not be satisfied and force ATS to do something else.
2835  *
2836  * @param address address of the other peer,
2837  * @param session session to use or NULL if transport should initiate a session
2838  * @param bandwidth_in inbound quota to be used when connection is up,
2839  *      0 to disconnect from peer
2840  * @param bandwidth_out outbound quota to be used when connection is up,
2841  *      0 to disconnect from peer
2842  */
2843 void
2844 GST_neighbours_switch_to_address (const struct GNUNET_HELLO_Address *address,
2845                                   struct GNUNET_ATS_Session *session,
2846                                   struct GNUNET_BANDWIDTH_Value32NBO
2847                                   bandwidth_in,
2848                                   struct GNUNET_BANDWIDTH_Value32NBO
2849                                   bandwidth_out)
2850 {
2851   struct GST_BlacklistCheck *blc;
2852   struct BlacklistCheckSwitchContext *blc_ctx;
2853
2854   GNUNET_assert (NULL != address->transport_name);
2855   if (GNUNET_OK ==
2856       try_run_fast_ats_update (address,
2857                                session,
2858                                bandwidth_in,
2859                                bandwidth_out))
2860     return;
2861
2862   /* Check if plugin is available */
2863   if (NULL == (GST_plugins_find (address->transport_name)))
2864   {
2865     /* we don't have the plugin for this address */
2866     GNUNET_break (0);
2867     GST_ats_block_address (address,
2868                            session);
2869     return;
2870   }
2871   if ((NULL == session) &&
2872       (GNUNET_HELLO_address_check_option (address,
2873                                           GNUNET_HELLO_ADDRESS_INFO_INBOUND)))
2874   {
2875     /* This is a inbound address and we do not have a session to use! */
2876     GNUNET_break (0);
2877     GST_ats_block_address (address,
2878                            session);
2879     return;
2880   }
2881
2882   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2883               "ATS suggests address '%s' for peer `%s' at %u/%u speed\n",
2884               GST_plugins_a2s (address),
2885               GNUNET_i2s (&address->peer),
2886               (unsigned int) ntohl (bandwidth_in.value__),
2887               (unsigned int) ntohl (bandwidth_out.value__));
2888
2889   /* Perform blacklist check */
2890   blc_ctx = GNUNET_new (struct BlacklistCheckSwitchContext);
2891   blc_ctx->bandwidth_in = bandwidth_in;
2892   blc_ctx->bandwidth_out = bandwidth_out;
2893   GNUNET_CONTAINER_DLL_insert (pending_bc_head,
2894                                pending_bc_tail,
2895                                blc_ctx);
2896   if (NULL != (blc = GST_blacklist_test_allowed (&address->peer,
2897                                                  address->transport_name,
2898                                                  &switch_address_bl_check_cont,
2899                                                  blc_ctx,
2900                                                  address,
2901                                                  session)))
2902   {
2903     blc_ctx->blc = blc;
2904   }
2905 }
2906
2907
2908 /**
2909  * Function called to send network utilization data to ATS for
2910  * each active connection.
2911  *
2912  * @param cls NULL
2913  * @param key peer we send utilization data for
2914  * @param value the `struct NeighbourMapEntry *` with data to send
2915  * @return #GNUNET_OK (continue to iterate)
2916  */
2917 static int
2918 send_utilization_data (void *cls,
2919                        const struct GNUNET_PeerIdentity *key,
2920                        void *value)
2921 {
2922   struct NeighbourMapEntry *n = value;
2923   uint32_t bps_in;
2924   uint32_t bps_out;
2925   struct GNUNET_TIME_Relative delta;
2926
2927   (void) cls;
2928   if ((GNUNET_YES != test_connected (n)) ||
2929       (NULL == n->primary_address.address))
2930     return GNUNET_OK;
2931   delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2932                                                GNUNET_TIME_absolute_get ());
2933   bps_in = 0;
2934   if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2935     bps_in = (1000LL * 1000LL * n->util_total_bytes_recv)
2936              / (delta.rel_value_us);
2937   bps_out = 0;
2938   if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2939     bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2940
2941   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2942               "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2943               GNUNET_i2s (key),
2944               bps_in,
2945               bps_out);
2946   GST_ats_update_utilization (n->primary_address.address,
2947                               bps_in,
2948                               bps_out);
2949   n->util_total_bytes_recv = 0;
2950   n->util_total_bytes_sent = 0;
2951   n->last_util_transmission = GNUNET_TIME_absolute_get ();
2952   return GNUNET_OK;
2953 }
2954
2955
2956 /**
2957  * Task transmitting utilization in a regular interval
2958  *
2959  * @param cls the `struct NeighbourMapEntry` for which we are running
2960  */
2961 static void
2962 utilization_transmission (void *cls)
2963 {
2964   (void) cls;
2965   util_transmission_tk = NULL;
2966   GNUNET_CONTAINER_multipeermap_iterate (neighbours,
2967                                          &send_utilization_data,
2968                                          NULL);
2969   util_transmission_tk
2970     = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2971                                     &utilization_transmission,
2972                                     NULL);
2973 }
2974
2975
2976 /**
2977  * Track information about data we received from the
2978  * given address (used to notify ATS about our utilization
2979  * of allocated resources).
2980  *
2981  * @param address the address we got data from
2982  * @param message the message we received (really only the size is used)
2983  */
2984 void
2985 GST_neighbours_notify_data_recv (const struct GNUNET_HELLO_Address *address,
2986                                  const struct GNUNET_MessageHeader *message)
2987 {
2988   struct NeighbourMapEntry *n;
2989
2990   n = lookup_neighbour (&address->peer);
2991   if (NULL == n)
2992     return;
2993   n->util_total_bytes_recv += ntohs (message->size);
2994 }
2995
2996
2997 /**
2998  * Track information about data we transmitted using the given @a
2999  * address and @a session (used to notify ATS about our utilization of
3000  * allocated resources).
3001  *
3002  * @param address the address we transmitted data to
3003  * @param session session we used to transmit data
3004  * @param message the message we sent (really only the size is used)
3005  */
3006 void
3007 GST_neighbours_notify_data_sent (const struct GNUNET_HELLO_Address *address,
3008                                  struct GNUNET_ATS_Session *session,
3009                                  size_t size)
3010 {
3011   struct NeighbourMapEntry *n;
3012
3013   n = lookup_neighbour (&address->peer);
3014   if (NULL == n)
3015     return;
3016   if (n->primary_address.session != session)
3017     return;
3018   n->util_total_bytes_sent += size;
3019 }
3020
3021
3022 /**
3023  * Master task run for every neighbour.  Performs all of the time-related
3024  * activities (keep alive, send next message, disconnect if idle, finish
3025  * clean up after disconnect).
3026  *
3027  * @param cls the 'struct NeighbourMapEntry' for which we are running
3028  */
3029 static void
3030 master_task (void *cls)
3031 {
3032   struct NeighbourMapEntry *n = cls;
3033   struct GNUNET_TIME_Relative delay;
3034
3035   n->task = NULL;
3036   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3037   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3038               "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
3039               GNUNET_i2s (&n->id),
3040               GNUNET_TRANSPORT_ps2s (n->state),
3041               GNUNET_STRINGS_relative_time_to_string (delay,
3042                                                       GNUNET_YES));
3043   switch (n->state)
3044   {
3045   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3046     /* invalid state for master task, clean up */
3047     GNUNET_break (0);
3048     free_neighbour (n);
3049     return;
3050
3051   case GNUNET_TRANSPORT_PS_INIT_ATS:
3052     if (0 == delay.rel_value_us)
3053     {
3054       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3055                   "Connection to `%s' timed out waiting for ATS to provide address\n",
3056                   GNUNET_i2s (&n->id));
3057       free_neighbour (n);
3058       return;
3059     }
3060     break;
3061
3062   case GNUNET_TRANSPORT_PS_SYN_SENT:
3063     if (0 == delay.rel_value_us)
3064     {
3065       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3066                   "Connection to `%s' timed out waiting for other peer to send SYN_ACK\n",
3067                   GNUNET_i2s (&n->id));
3068       /* Remove address and request and additional one */
3069       unset_primary_address (n);
3070       set_state_and_timeout (n,
3071                              GNUNET_TRANSPORT_PS_INIT_ATS,
3072                              GNUNET_TIME_relative_to_absolute (
3073                                ATS_RESPONSE_TIMEOUT));
3074       return;
3075     }
3076     break;
3077
3078   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3079     if (0 == delay.rel_value_us)
3080     {
3081       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3082                   "Connection to `%s' timed out waiting ATS to provide address to use for SYN_ACK\n",
3083                   GNUNET_i2s (&n->id));
3084       free_neighbour (n);
3085       return;
3086     }
3087     break;
3088
3089   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3090     if (0 == delay.rel_value_us)
3091     {
3092       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3093                   "Connection to `%s' timed out waiting for other peer to send ACK\n",
3094                   GNUNET_i2s (&n->id));
3095       disconnect_neighbour (n);
3096       return;
3097     }
3098     break;
3099
3100   case GNUNET_TRANSPORT_PS_CONNECTED:
3101     if (0 == delay.rel_value_us)
3102     {
3103       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3104                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
3105                   GNUNET_i2s (&n->id));
3106       disconnect_neighbour (n);
3107       return;
3108     }
3109     try_transmission_to_peer (n);
3110     send_keepalive (n);
3111     break;
3112
3113   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3114     if (0 == delay.rel_value_us)
3115     {
3116       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3117                   "Connection to `%s' timed out, waiting for ATS replacement address\n",
3118                   GNUNET_i2s (&n->id));
3119       disconnect_neighbour (n);
3120       return;
3121     }
3122     break;
3123
3124   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3125     if (0 == delay.rel_value_us)
3126     {
3127       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3128                   "Connection to `%s' timed out, waiting for other peer to SYN_ACK replacement address\n",
3129                   GNUNET_i2s (&n->id));
3130       disconnect_neighbour (n);
3131       return;
3132     }
3133     break;
3134
3135   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3136     if (0 == delay.rel_value_us)
3137     {
3138       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3139                   "Switch failed, cleaning up alternative address\n");
3140       free_address (&n->alternative_address);
3141       set_state_and_timeout (n,
3142                              GNUNET_TRANSPORT_PS_CONNECTED,
3143                              GNUNET_TIME_relative_to_absolute (
3144                                SETUP_CONNECTION_TIMEOUT));
3145     }
3146     try_transmission_to_peer (n);
3147     send_keepalive (n);
3148     break;
3149
3150   case GNUNET_TRANSPORT_PS_DISCONNECT:
3151     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3152                 "Cleaning up connection to `%s' after sending DISCONNECT\n",
3153                 GNUNET_i2s (&n->id));
3154     free_neighbour (n);
3155     return;
3156
3157   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3158     /* how did we get here!? */
3159     GNUNET_assert (0);
3160     break;
3161
3162   default:
3163     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3164                 "Unhandled state `%s'\n",
3165                 GNUNET_TRANSPORT_ps2s (n->state));
3166     GNUNET_break (0);
3167     break;
3168   }
3169   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
3170   if ((GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state) ||
3171       (GNUNET_TRANSPORT_PS_CONNECTED == n->state))
3172   {
3173     /* if we are *now* in one of the two states, we're sending
3174        keep alive messages, so we need to consider the keepalive
3175        delay, not just the connection timeout */
3176     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (
3177                                         n->keep_alive_time),
3178                                       delay);
3179   }
3180   if (NULL == n->task)
3181     n->task = GNUNET_SCHEDULER_add_delayed (delay,
3182                                             &master_task,
3183                                             n);
3184 }
3185
3186
3187 /**
3188  * Send a ACK message to the neighbour to confirm that we
3189  * got its SYN_ACK.
3190  *
3191  * @param n neighbour to send the ACK to
3192  */
3193 static void
3194 send_session_ack_message (struct NeighbourMapEntry *n)
3195 {
3196   struct GNUNET_MessageHeader msg;
3197
3198   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3199               "Sending ACK message to peer `%s'\n",
3200               GNUNET_i2s (&n->id));
3201
3202   msg.size = htons (sizeof(struct GNUNET_MessageHeader));
3203   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
3204   (void) send_with_session (n,
3205                             &msg,
3206                             sizeof(struct GNUNET_MessageHeader),
3207                             UINT32_MAX,
3208                             GNUNET_TIME_UNIT_FOREVER_REL,
3209                             GNUNET_NO,
3210                             NULL, NULL);
3211 }
3212
3213
3214 /**
3215  * We received a 'SESSION_SYN_ACK' message from the other peer.
3216  * Consider switching to it.
3217  *
3218  * @param message possibly a `struct GNUNET_ATS_SessionConnectMessage` (check format)
3219  * @param peer identity of the peer to switch the address for
3220  * @param address address of the other peer, NULL if other peer
3221  *                       connected to us
3222  * @param session session to use (or NULL)
3223  * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3224  */
3225 int
3226 GST_neighbours_handle_session_syn_ack (const struct
3227                                        GNUNET_MessageHeader *message,
3228                                        const struct
3229                                        GNUNET_HELLO_Address *address,
3230                                        struct GNUNET_ATS_Session *session)
3231 {
3232   const struct TransportSynMessage *scm;
3233   struct GNUNET_TIME_Absolute ts;
3234   struct NeighbourMapEntry *n;
3235
3236   (void) session;
3237   if (ntohs (message->size) != sizeof(struct TransportSynMessage))
3238   {
3239     GNUNET_break_op (0);
3240     return GNUNET_SYSERR;
3241   }
3242   GNUNET_STATISTICS_update (GST_stats,
3243                             gettext_noop
3244                               ("# SYN_ACK messages received"),
3245                             1, GNUNET_NO);
3246   scm = (const struct TransportSynMessage *) message;
3247   GNUNET_break_op (ntohl (scm->reserved) == 0);
3248   if (NULL == (n = lookup_neighbour (&address->peer)))
3249   {
3250     GNUNET_STATISTICS_update (GST_stats,
3251                               gettext_noop
3252                                 ("# unexpected SYN_ACK messages (no peer)"),
3253                               1, GNUNET_NO);
3254     return GNUNET_SYSERR;
3255   }
3256   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3257               "Received SYN_ACK message from peer `%s' in state %s/%s\n",
3258               GNUNET_i2s (&address->peer),
3259               GNUNET_TRANSPORT_ps2s (n->state),
3260               print_ack_state (n->ack_state));
3261   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
3262   switch (n->state)
3263   {
3264   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3265     GNUNET_break (0);
3266     free_neighbour (n);
3267     return GNUNET_SYSERR;
3268
3269   case GNUNET_TRANSPORT_PS_INIT_ATS:
3270     GNUNET_STATISTICS_update (GST_stats,
3271                               gettext_noop (
3272                                 "# unexpected SYN_ACK messages (not ready)"),
3273                               1,
3274                               GNUNET_NO);
3275     break;
3276
3277   case GNUNET_TRANSPORT_PS_SYN_SENT:
3278     if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
3279     {
3280       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3281                   "SYN_ACK ignored as the timestamp does not match our SYN request\n");
3282       return GNUNET_OK;
3283     }
3284     set_state_and_timeout (n,
3285                            GNUNET_TRANSPORT_PS_CONNECTED,
3286                            GNUNET_TIME_relative_to_absolute (
3287                              GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3288     set_primary_address (n,
3289                          n->primary_address.address,
3290                          n->primary_address.session,
3291                          n->primary_address.bandwidth_in,
3292                          n->primary_address.bandwidth_out);
3293     send_session_ack_message (n);
3294     break;
3295
3296   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3297   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3298     GNUNET_STATISTICS_update (GST_stats,
3299                               gettext_noop (
3300                                 "# unexpected SYN_ACK messages (not ready)"),
3301                               1,
3302                               GNUNET_NO);
3303     break;
3304
3305   case GNUNET_TRANSPORT_PS_CONNECTED:
3306     /* duplicate SYN_ACK, let's answer by duplicate ACK just in case */
3307     send_session_ack_message (n);
3308     break;
3309
3310   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3311     /* we didn't expect any SYN_ACK, as we are waiting for ATS
3312        to give us a new address... */
3313     GNUNET_STATISTICS_update (GST_stats,
3314                               gettext_noop (
3315                                 "# unexpected SYN_ACK messages (waiting on ATS)"),
3316                               1,
3317                               GNUNET_NO);
3318     break;
3319
3320   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3321     /* Reconnecting with new address address worked; go back to connected! */
3322     set_state_and_timeout (n,
3323                            GNUNET_TRANSPORT_PS_CONNECTED,
3324                            GNUNET_TIME_relative_to_absolute (
3325                              GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3326     send_session_ack_message (n);
3327     break;
3328
3329   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3330     /* new address worked; adopt it and go back to connected! */
3331     set_state_and_timeout (n,
3332                            GNUNET_TRANSPORT_PS_CONNECTED,
3333                            GNUNET_TIME_relative_to_absolute (
3334                              GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3335     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
3336
3337     /* Set primary addresses */
3338     set_primary_address (n,
3339                          n->alternative_address.address,
3340                          n->alternative_address.session,
3341                          n->alternative_address.bandwidth_in,
3342                          n->alternative_address.bandwidth_out);
3343     GNUNET_STATISTICS_update (GST_stats,
3344                               gettext_noop (
3345                                 "# Successful attempts to switch addresses"),
3346                               1,
3347                               GNUNET_NO);
3348
3349     GNUNET_HELLO_address_free (n->alternative_address.address);
3350     memset (&n->alternative_address,
3351             0,
3352             sizeof(n->alternative_address));
3353     send_session_ack_message (n);
3354     break;
3355
3356   case GNUNET_TRANSPORT_PS_DISCONNECT:
3357     GNUNET_STATISTICS_update (GST_stats,
3358                               gettext_noop
3359                                 ("# unexpected SYN_ACK messages (disconnecting)"),
3360                               1, GNUNET_NO);
3361     return GNUNET_SYSERR;
3362
3363   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3364     GNUNET_assert (0);
3365     break;
3366
3367   default:
3368     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3369                 "Unhandled state `%s'\n",
3370                 GNUNET_TRANSPORT_ps2s (n->state));
3371     GNUNET_break (0);
3372     return GNUNET_SYSERR;
3373   }
3374   return GNUNET_OK;
3375 }
3376
3377
3378 /**
3379  * A session was terminated. Take note; if needed, try to get
3380  * an alternative address from ATS.
3381  *
3382  * @param peer identity of the peer where the session died
3383  * @param session session that is gone
3384  * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
3385  *        this session was not in use
3386  */
3387 int
3388 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
3389                                    struct GNUNET_ATS_Session *session)
3390 {
3391   struct NeighbourMapEntry *n;
3392
3393   if (NULL == (n = lookup_neighbour (peer)))
3394     return GNUNET_NO; /* can't affect us */
3395   if (session != n->primary_address.session)
3396   {
3397     /* Free alternative address */
3398     if (session == n->alternative_address.session)
3399     {
3400       if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3401         set_state_and_timeout (n,
3402                                GNUNET_TRANSPORT_PS_CONNECTED,
3403                                n->timeout);
3404       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3405                   "Session died, cleaning up alternative address\n");
3406       free_address (&n->alternative_address);
3407     }
3408     return GNUNET_NO;   /* doesn't affect us further */
3409   }
3410
3411   n->expect_latency_response = GNUNET_NO;
3412   /* The session for neighbour's primary address died */
3413   switch (n->state)
3414   {
3415   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
3416     GNUNET_break (0);
3417     free_neighbour (n);
3418     return GNUNET_YES;
3419
3420   case GNUNET_TRANSPORT_PS_INIT_ATS:
3421     GNUNET_break (0);
3422     free_neighbour (n);
3423     return GNUNET_YES;
3424
3425   case GNUNET_TRANSPORT_PS_SYN_SENT:
3426     /* The session used to send the SYN terminated:
3427      * this implies a connect error*/
3428     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3429                 "Failed to send SYN in CONNECT_SENT with `%s' %p: session terminated\n",
3430                 GST_plugins_a2s (n->primary_address.address),
3431                 n->primary_address.session);
3432
3433     /* Destroy the address since it cannot be used */
3434     unset_primary_address (n);
3435     set_state_and_timeout (n,
3436                            GNUNET_TRANSPORT_PS_INIT_ATS,
3437                            GNUNET_TIME_relative_to_absolute (
3438                              ATS_RESPONSE_TIMEOUT));
3439     break;
3440
3441   case GNUNET_TRANSPORT_PS_SYN_RECV_ATS:
3442   case GNUNET_TRANSPORT_PS_SYN_RECV_ACK:
3443     /* error on inbound session; free neighbour entirely */
3444     free_neighbour (n);
3445     return GNUNET_YES;
3446
3447   case GNUNET_TRANSPORT_PS_CONNECTED:
3448     /* Our primary connection died, try a fast reconnect */
3449     unset_primary_address (n);
3450     set_state_and_timeout (n,
3451                            GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3452                            GNUNET_TIME_relative_to_absolute (
3453                              ATS_RESPONSE_TIMEOUT));
3454     break;
3455
3456   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
3457     /* we don't have an address, how can it go down? */
3458     GNUNET_break (0);
3459     break;
3460
3461   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
3462     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3463                 "Failed to send SYN in RECONNECT_SENT with `%s' %p: session terminated\n",
3464                 GST_plugins_a2s (n->primary_address.address),
3465                 n->primary_address.session);
3466     /* Destroy the address since it cannot be used */
3467     unset_primary_address (n);
3468     set_state_and_timeout (n,
3469                            GNUNET_TRANSPORT_PS_RECONNECT_ATS,
3470                            GNUNET_TIME_relative_to_absolute (
3471                              ATS_RESPONSE_TIMEOUT));
3472     break;
3473
3474   case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT:
3475     /* primary went down while we were waiting for SYN_ACK on secondary;
3476        secondary as primary */
3477
3478     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3479                 "Connection `%s' %p to peer `%s' was terminated while switching, "
3480                 "switching to alternative address `%s' %p\n",
3481                 GST_plugins_a2s (n->primary_address.address),
3482                 n->primary_address.session,
3483                 GNUNET_i2s (peer),
3484                 GST_plugins_a2s (n->alternative_address.address),
3485                 n->alternative_address.session);
3486
3487     /* Destroy the inbound address since it cannot be used */
3488     free_address (&n->primary_address);
3489     n->primary_address = n->alternative_address;
3490     GNUNET_assert (GNUNET_YES ==
3491                    GST_ats_is_known (n->primary_address.address,
3492                                      n->primary_address.session));
3493     memset (&n->alternative_address,
3494             0,
3495             sizeof(struct NeighbourAddress));
3496     set_state_and_timeout (n,
3497                            GNUNET_TRANSPORT_PS_RECONNECT_SENT,
3498                            GNUNET_TIME_relative_to_absolute (
3499                              FAST_RECONNECT_TIMEOUT));
3500     break;
3501
3502   case GNUNET_TRANSPORT_PS_DISCONNECT:
3503     unset_primary_address (n);
3504     break;
3505
3506   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
3507     /* neighbour was freed and plugins told to terminate session */
3508     return GNUNET_NO;
3509
3510   default:
3511     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3512                 "Unhandled state `%s'\n",
3513                 GNUNET_TRANSPORT_ps2s (n->state));
3514     GNUNET_break (0);
3515     break;
3516   }
3517   if (NULL != n->task)
3518     GNUNET_SCHEDULER_cancel (n->task);
3519   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3520   return GNUNET_YES;
3521 }
3522
3523
3524 /**
3525  * We received a 'ACK' message from the other peer.
3526  * If we sent a 'SYN_ACK' last, this means we are now
3527  * connected.  Otherwise, do nothing.
3528  *
3529  * @param message possibly a 'struct GNUNET_ATS_SessionConnectMessage' (check format)
3530  * @param address address of the other peer
3531  * @param session session to use (or NULL)
3532  * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
3533  */
3534 int
3535 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3536                                    const struct GNUNET_HELLO_Address *address,
3537                                    struct GNUNET_ATS_Session *session)
3538 {
3539   struct NeighbourMapEntry *n;
3540
3541   (void) session;
3542   if (ntohs (message->size) != sizeof(struct GNUNET_MessageHeader))
3543   {
3544     GNUNET_break_op (0);
3545     return GNUNET_SYSERR;
3546   }
3547   GNUNET_STATISTICS_update (GST_stats,
3548                             gettext_noop ("# ACK messages received"),
3549                             1,
3550                             GNUNET_NO);
3551   if (NULL == (n = lookup_neighbour (&address->peer)))
3552   {
3553     GNUNET_break_op (0);
3554     return GNUNET_SYSERR;
3555   }
3556   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3557               "Received ACK for peer `%s' in state %s/%s\n",
3558               GNUNET_i2s (&address->peer),
3559               GNUNET_TRANSPORT_ps2s (n->state),
3560               print_ack_state (n->ack_state));
3561
3562   /* Check if we are in a plausible state for having sent
3563      a SYN_ACK.  If not, return, otherwise break.
3564
3565      The remote peers sends a ACK as a response for a SYN_ACK
3566      message.
3567
3568      We expect a ACK:
3569      - If a remote peer has sent a SYN, we responded with a SYN_ACK and
3570      now wait for the ACK to finally be connected
3571      - If we sent a SYN_ACK to this peer before */if (((GNUNET_TRANSPORT_PS_SYN_RECV_ACK != n->state) &&
3572        (ACK_SEND_ACK != n->ack_state)) ||
3573       (NULL == n->primary_address.address))
3574   {
3575     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3576                 "Received unexpected ACK message from peer `%s' in state %s/%s\n",
3577                 GNUNET_i2s (&address->peer),
3578                 GNUNET_TRANSPORT_ps2s (n->state),
3579                 print_ack_state (n->ack_state));
3580
3581     GNUNET_STATISTICS_update (GST_stats,
3582                               gettext_noop ("# unexpected ACK messages"),
3583                               1,
3584                               GNUNET_NO);
3585     return GNUNET_OK;
3586   }
3587   if (GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT == n->state)
3588   {
3589     /* We tried to switch addresses while being connect. We explicitly wait
3590      * for a SYN_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
3591      * so we do not want to set the address as in use! */
3592     return GNUNET_OK;
3593   }
3594   set_state_and_timeout (n,
3595                          GNUNET_TRANSPORT_PS_CONNECTED,
3596                          GNUNET_TIME_relative_to_absolute (
3597                            GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
3598
3599   if (NULL == n->primary_address.address)
3600   {
3601     /* See issue #3693.
3602      * We are in state = PSY_SYN_RECV_ACK or ack_state = ACK_SEND_ACK, which
3603      * really means we did try (and succeed) to send a SYN and are waiting for
3604      * an ACK.
3605      * That suggests that the primary_address used to be non-NULL, but maybe it
3606      * got reset to NULL without the state being changed appropriately?
3607      */GNUNET_break (0);
3608     return GNUNET_OK;
3609   }
3610
3611   /* Reset backoff for primary address */
3612   GST_ats_block_reset (n->primary_address.address,
3613                        n->primary_address.session);
3614   return GNUNET_OK;
3615 }
3616
3617
3618 /**
3619  * Test if we're connected to the given peer.
3620  *
3621  * @param target peer to test
3622  * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3623  */
3624 int
3625 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3626 {
3627   return test_connected (lookup_neighbour (target));
3628 }
3629
3630
3631 /**
3632  * Task to asynchronously run #free_neighbour().
3633  *
3634  * @param cls the `struct NeighbourMapEntry` to free
3635  */
3636 static void
3637 delayed_disconnect (void *cls)
3638 {
3639   struct NeighbourMapEntry *n = cls;
3640
3641   n->delayed_disconnect_task = NULL;
3642   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3643               "Disconnecting by request from peer %s\n",
3644               GNUNET_i2s (&n->id));
3645   free_neighbour (n);
3646 }
3647
3648
3649 /**
3650  * We received a quota message from the given peer,
3651  * validate and process.
3652  *
3653  * @param peer sender of the message
3654  * @param msg the quota message
3655  */
3656 void
3657 GST_neighbours_handle_quota_message (const struct GNUNET_PeerIdentity *peer,
3658                                      const struct GNUNET_MessageHeader *msg)
3659 {
3660   struct NeighbourMapEntry *n;
3661   const struct GNUNET_ATS_SessionQuotaMessage *sqm;
3662   struct GNUNET_BANDWIDTH_Value32NBO last;
3663
3664   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3665               "Received QUOTA message from peer `%s'\n",
3666               GNUNET_i2s (peer));
3667   if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionQuotaMessage))
3668   {
3669     GNUNET_break_op (0);
3670     GNUNET_STATISTICS_update (GST_stats,
3671                               gettext_noop (
3672                                 "# quota messages ignored (malformed)"),
3673                               1,
3674                               GNUNET_NO);
3675     return;
3676   }
3677   GNUNET_STATISTICS_update (GST_stats,
3678                             gettext_noop
3679                               ("# QUOTA messages received"),
3680                             1, GNUNET_NO);
3681   sqm = (const struct GNUNET_ATS_SessionQuotaMessage *) msg;
3682   if (NULL == (n = lookup_neighbour (peer)))
3683   {
3684     /* gone already */
3685     return;
3686   }
3687   last = GNUNET_BANDWIDTH_value_max (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
3688                                      GNUNET_BANDWIDTH_value_init (ntohl (
3689                                                                     sqm->quota)));
3690   if (last.value__ != n->neighbour_receive_quota.value__)
3691   {
3692     n->neighbour_receive_quota = last;
3693     send_outbound_quota_to_clients (n);
3694   }
3695 }
3696
3697
3698 /**
3699  * We received a disconnect message from the given peer,
3700  * validate and process.
3701  *
3702  * @param peer sender of the message
3703  * @param msg the disconnect message
3704  */
3705 void
3706 GST_neighbours_handle_disconnect_message (const struct
3707                                           GNUNET_PeerIdentity *peer,
3708                                           const struct
3709                                           GNUNET_MessageHeader *msg)
3710 {
3711   struct NeighbourMapEntry *n;
3712   const struct GNUNET_ATS_SessionDisconnectMessage *sdm;
3713
3714   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3715               "Received DISCONNECT message from peer `%s'\n",
3716               GNUNET_i2s (peer));
3717   if (ntohs (msg->size) != sizeof(struct GNUNET_ATS_SessionDisconnectMessage))
3718   {
3719     GNUNET_break_op (0);
3720     GNUNET_STATISTICS_update (GST_stats,
3721                               gettext_noop
3722                                 ("# disconnect messages ignored (malformed)"),
3723                               1,
3724                               GNUNET_NO);
3725     return;
3726   }
3727   GNUNET_STATISTICS_update (GST_stats,
3728                             gettext_noop
3729                               ("# DISCONNECT messages received"),
3730                             1, GNUNET_NO);
3731   sdm = (const struct GNUNET_ATS_SessionDisconnectMessage *) msg;
3732   if (NULL == (n = lookup_neighbour (peer)))
3733   {
3734     /* gone already */
3735     return;
3736   }
3737   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <=
3738       n->connect_ack_timestamp.abs_value_us)
3739   {
3740     GNUNET_STATISTICS_update (GST_stats,
3741                               gettext_noop (
3742                                 "# disconnect messages ignored (timestamp)"),
3743                               1,
3744                               GNUNET_NO);
3745     return;
3746   }
3747   if (0 != memcmp (peer,
3748                    &sdm->public_key,
3749                    sizeof(struct GNUNET_PeerIdentity)))
3750   {
3751     GNUNET_break_op (0);
3752     return;
3753   }
3754   if (ntohl (sdm->purpose.size) !=
3755       sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
3756       + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)
3757       + sizeof(struct GNUNET_TIME_AbsoluteNBO))
3758   {
3759     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3760                 "DISCONNECT message from peer `%s' has invalid size\n",
3761                 GNUNET_i2s (peer));
3762     GNUNET_break_op (0);
3763     return;
3764   }
3765   if (GNUNET_OK !=
3766       GNUNET_CRYPTO_eddsa_verify (
3767         GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
3768         &sdm->purpose,
3769         &sdm->signature,
3770         &sdm->public_key))
3771   {
3772     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3773                 "DISCONNECT message from peer `%s' cannot be verified \n",
3774                 GNUNET_i2s (peer));
3775     GNUNET_break_op (0);
3776     return;
3777   }
3778   if (NULL == n->delayed_disconnect_task)
3779   {
3780     n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect,
3781                                                            n);
3782   }
3783 }
3784
3785
3786 /**
3787  * Closure for the #neighbours_iterate() function.
3788  */
3789 struct IteratorContext
3790 {
3791   /**
3792    * Function to call on each connected neighbour.
3793    */
3794   GST_NeighbourIterator cb;
3795
3796   /**
3797    * Closure for @e cb.
3798    */
3799   void *cb_cls;
3800 };
3801
3802
3803 /**
3804  * Call the callback from the closure for each neighbour.
3805  *
3806  * @param cls the `struct IteratorContext`
3807  * @param key the hash of the public key of the neighbour
3808  * @param value the `struct NeighbourMapEntry`
3809  * @return #GNUNET_OK (continue to iterate)
3810  */
3811 static int
3812 neighbours_iterate (void *cls,
3813                     const struct GNUNET_PeerIdentity *key,
3814                     void *value)
3815 {
3816   struct IteratorContext *ic = cls;
3817   struct NeighbourMapEntry *n = value;
3818   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3819   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3820
3821   (void) key;
3822   if (NULL != n->primary_address.address)
3823   {
3824     bandwidth_in = n->primary_address.bandwidth_in;
3825     bandwidth_out = n->primary_address.bandwidth_out;
3826   }
3827   else
3828   {
3829     bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3830     bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3831   }
3832   ic->cb (ic->cb_cls,
3833           &n->id,
3834           n->primary_address.address,
3835           n->state,
3836           n->timeout,
3837           bandwidth_in, bandwidth_out);
3838   return GNUNET_OK;
3839 }
3840
3841
3842 /**
3843  * Iterate over all connected neighbours.
3844  *
3845  * @param cb function to call
3846  * @param cb_cls closure for @a cb
3847  */
3848 void
3849 GST_neighbours_iterate (GST_NeighbourIterator cb,
3850                         void *cb_cls)
3851 {
3852   struct IteratorContext ic;
3853
3854   if (NULL == neighbours)
3855     return; /* can happen during shutdown */
3856   ic.cb = cb;
3857   ic.cb_cls = cb_cls;
3858   GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3859                                          &neighbours_iterate,
3860                                          &ic);
3861 }
3862
3863
3864 /**
3865  * If we have an active connection to the given target, it must be shutdown.
3866  *
3867  * @param target peer to disconnect from
3868  */
3869 void
3870 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3871 {
3872   struct NeighbourMapEntry *n;
3873
3874   if (NULL == (n = lookup_neighbour (target)))
3875     return;  /* not active */
3876   if (GNUNET_YES == test_connected (n))
3877     GNUNET_STATISTICS_update (GST_stats,
3878                               gettext_noop (
3879                                 "# disconnected from peer upon explicit request"),
3880                               1,
3881                               GNUNET_NO);
3882   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3883               "Forced disconnect from peer %s\n",
3884               GNUNET_i2s (target));
3885   disconnect_neighbour (n);
3886 }
3887
3888
3889 /**
3890  * Obtain current address information for the given neighbour.
3891  *
3892  * @param peer
3893  * @return address currently used
3894  */
3895 const struct GNUNET_HELLO_Address *
3896 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3897 {
3898   struct NeighbourMapEntry *n;
3899
3900   n = lookup_neighbour (peer);
3901   if (NULL == n)
3902     return NULL;
3903   return n->primary_address.address;
3904 }
3905
3906
3907 /**
3908  * Initialize the neighbours subsystem.
3909  *
3910  * @param max_fds maximum number of fds to use
3911  */
3912 void
3913 GST_neighbours_start (unsigned int max_fds)
3914 {
3915   (void) max_fds;
3916   neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE,
3917                                                      GNUNET_NO);
3918   util_transmission_tk = GNUNET_SCHEDULER_add_delayed (
3919     UTIL_TRANSMISSION_INTERVAL,
3920     &utilization_transmission,
3921     NULL);
3922 }
3923
3924
3925 /**
3926  * Disconnect from the given neighbour.
3927  *
3928  * @param cls unused
3929  * @param key hash of neighbour's public key (not used)
3930  * @param value the `struct NeighbourMapEntry` of the neighbour
3931  * @return #GNUNET_OK (continue to iterate)
3932  */
3933 static int
3934 disconnect_all_neighbours (void *cls,
3935                            const struct GNUNET_PeerIdentity *key,
3936                            void *value)
3937 {
3938   struct NeighbourMapEntry *n = value;
3939
3940   (void) cls;
3941   (void) key;
3942   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3943               "Disconnecting peer `%4s' during shutdown\n",
3944               GNUNET_i2s (&n->id));
3945   free_neighbour (n);
3946   return GNUNET_OK;
3947 }
3948
3949
3950 /**
3951  * Cleanup the neighbours subsystem.
3952  */
3953 void
3954 GST_neighbours_stop ()
3955 {
3956   if (NULL == neighbours)
3957     return;
3958   if (NULL != util_transmission_tk)
3959   {
3960     GNUNET_SCHEDULER_cancel (util_transmission_tk);
3961     util_transmission_tk = NULL;
3962   }
3963   GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3964                                          &disconnect_all_neighbours,
3965                                          NULL);
3966   GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3967   neighbours = NULL;
3968 }
3969
3970
3971 /* end of file gnunet-service-transport_neighbours.c */