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