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