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