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