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