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