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