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