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