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