Extending the testcases to use bluetooth
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011,2012 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, 500)
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)
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_EccPublicKeyBinaryEncoded 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_EccSignature 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_MultiHashMap *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_multihashmap_get (neighbours, &pid->hashPubKey);
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_multihashmap_remove (neighbours,
1081                                                        &n->id.hashPubKey, 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_EccPublicKeyBinaryEncoded) +
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_public_key;
1205   GNUNET_assert (GNUNET_OK ==
1206                  GNUNET_CRYPTO_ecc_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 > 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 > 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 %llu ms\n",
1538               GNUNET_i2s (&n->id), n->latency.rel_value);
1539   /* append latency */
1540   ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1541   if (n->latency.rel_value > UINT32_MAX)
1542     latency = UINT32_MAX;
1543   else
1544     latency = n->latency.rel_value;
1545   ats.value = htonl (latency);
1546   GST_ats_update_metrics (&n->id,
1547                                                                                           n->primary_address.address,
1548                                                                                         n->primary_address.session,
1549                                                                                         &ats, 1);
1550 }
1551
1552
1553 /**
1554  * We have received a message from the given sender.  How long should
1555  * we delay before receiving more?  (Also used to keep the peer marked
1556  * as live).
1557  *
1558  * @param sender sender of the message
1559  * @param size size of the message
1560  * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1561  *                   GNUNET_NO if the neighbour is not connected or violates the quota,
1562  *                   GNUNET_SYSERR if the connection is not fully up yet
1563  * @return how long to wait before reading more from this sender
1564  */
1565 struct GNUNET_TIME_Relative
1566 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1567                                         *sender, ssize_t size, int *do_forward)
1568 {
1569   struct NeighbourMapEntry *n;
1570   struct GNUNET_TIME_Relative ret;
1571   
1572   if (NULL == neighbours)
1573   {
1574     *do_forward = GNUNET_NO;
1575     return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1576   }
1577   if (NULL == (n = lookup_neighbour (sender)))
1578   {
1579     GST_neighbours_try_connect (sender);
1580     if (NULL == (n = lookup_neighbour (sender)))
1581     {
1582       GNUNET_STATISTICS_update (GST_stats,
1583                                 gettext_noop
1584                                 ("# messages discarded due to lack of neighbour record"),
1585                                 1, GNUNET_NO);
1586       *do_forward = GNUNET_NO;
1587       return GNUNET_TIME_UNIT_ZERO;
1588     }
1589   }
1590   if (! test_connected (n))
1591   {
1592     *do_forward = GNUNET_SYSERR;
1593     return GNUNET_TIME_UNIT_ZERO;
1594   }
1595   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1596   {
1597     n->quota_violation_count++;
1598     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1599                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1600                 n->in_tracker.available_bytes_per_s__,
1601                 n->quota_violation_count);
1602     /* Discount 32k per violation */
1603     GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1604   }
1605   else
1606   {
1607     if (n->quota_violation_count > 0)
1608     {
1609       /* try to add 32k back */
1610       GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1611       n->quota_violation_count--;
1612     }
1613   }
1614   if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1615   {
1616     GNUNET_STATISTICS_update (GST_stats,
1617                               gettext_noop
1618                               ("# bandwidth quota violations by other peers"),
1619                               1, GNUNET_NO);
1620     *do_forward = GNUNET_NO;
1621     return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1622   }
1623   *do_forward = GNUNET_YES;
1624   ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1625   if (ret.rel_value > 0)
1626   {
1627     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1628                 "Throttling read (%llu bytes excess at %u b/s), waiting %llu ms before reading more.\n",
1629                 (unsigned long long) n->in_tracker.
1630                 consumption_since_last_update__,
1631                 (unsigned int) n->in_tracker.available_bytes_per_s__,
1632                 (unsigned long long) ret.rel_value);
1633     GNUNET_STATISTICS_update (GST_stats,
1634                               gettext_noop ("# ms throttling suggested"),
1635                               (int64_t) ret.rel_value, GNUNET_NO);
1636   }
1637   return ret;
1638 }
1639
1640
1641 /**
1642  * Transmit a message to the given target using the active connection.
1643  *
1644  * @param target destination
1645  * @param msg message to send
1646  * @param msg_size number of bytes in msg
1647  * @param timeout when to fail with timeout
1648  * @param cont function to call when done
1649  * @param cont_cls closure for 'cont'
1650  */
1651 void
1652 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1653                      size_t msg_size, struct GNUNET_TIME_Relative timeout,
1654                      GST_NeighbourSendContinuation cont, void *cont_cls)
1655 {
1656   struct NeighbourMapEntry *n;
1657   struct MessageQueue *mq;
1658
1659   /* All ove these cases should never happen; they are all API violations.
1660      But we check anyway, just to be sure. */
1661   if (NULL == (n = lookup_neighbour (target)))
1662   {
1663     GNUNET_break (0);
1664     if (NULL != cont)
1665       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1666     return;
1667   }
1668   if (GNUNET_YES != test_connected (n))
1669   {
1670     GNUNET_break (0);
1671     if (NULL != cont)
1672       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1673     return;
1674   }
1675   bytes_in_send_queue += msg_size;
1676   GNUNET_STATISTICS_set (GST_stats,
1677                          gettext_noop
1678                          ("# bytes in message queue for other peers"),
1679                          bytes_in_send_queue, GNUNET_NO);
1680   mq = MEMDEBUG_malloc (sizeof (struct MessageQueue) + msg_size, __LINE__);
1681   mq->cont = cont;
1682   mq->cont_cls = cont_cls;
1683   memcpy (&mq[1], msg, msg_size);
1684   mq->message_buf = (const char *) &mq[1];
1685   mq->message_buf_size = msg_size;
1686   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1687   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1688   if ( (NULL != n->is_active) ||
1689        ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1690     return;
1691   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1692     GNUNET_SCHEDULER_cancel (n->task);
1693   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1694 }
1695
1696
1697 /**
1698  * Send a SESSION_CONNECT message via the given address.
1699  *
1700  * @param na address to use
1701  */
1702 static void
1703 send_session_connect (struct NeighbourAddress *na)
1704 {
1705   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1706   struct SessionConnectMessage connect_msg;
1707
1708   if (NULL == (papi = GST_plugins_find (na->address->transport_name)))  
1709   {
1710     GNUNET_break (0);
1711     return;
1712   }
1713   if (NULL == na->session)
1714     na->session = papi->get_session (papi->cls, na->address);    
1715   if (NULL == na->session)
1716   {
1717     GNUNET_break (0);
1718     return;
1719   }
1720   na->connect_timestamp = GNUNET_TIME_absolute_get ();
1721   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1722   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1723   connect_msg.reserved = htonl (0);
1724   connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1725   (void) papi->send (papi->cls,
1726                      na->session,
1727                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1728                      UINT_MAX,
1729                      GNUNET_TIME_UNIT_FOREVER_REL,
1730                      NULL, NULL);
1731
1732 }
1733
1734
1735 /**
1736  * Send a SESSION_CONNECT_ACK message via the given address.
1737  *
1738  * @param address address to use
1739  * @param session session to use
1740  * @param timestamp timestamp to use for the ACK message
1741  */
1742 static void
1743 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1744                                   struct Session *session,
1745                                   struct GNUNET_TIME_Absolute timestamp)
1746 {
1747   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1748   struct SessionConnectMessage connect_msg;
1749
1750   if (NULL == (papi = GST_plugins_find (address->transport_name)))  
1751   {
1752     GNUNET_break (0);
1753     return;
1754   }
1755   if (NULL == session)
1756     session = papi->get_session (papi->cls, address);    
1757   if (NULL == session)
1758   {
1759     GNUNET_break (0);
1760     return;
1761   }
1762   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1763   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1764   connect_msg.reserved = htonl (0);
1765   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1766   (void) papi->send (papi->cls,
1767                      session,
1768                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1769                      UINT_MAX,
1770                      GNUNET_TIME_UNIT_FOREVER_REL,
1771                      NULL, NULL);
1772
1773 }
1774
1775
1776 /**
1777  * Create a fresh entry in the neighbour map for the given peer
1778  *
1779  * @param peer peer to create an entry for
1780  * @return new neighbour map entry
1781  */
1782 static struct NeighbourMapEntry *
1783 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1784 {
1785   struct NeighbourMapEntry *n;
1786
1787   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1788               "Creating new neighbour entry for `%s'\n", 
1789               GNUNET_i2s (peer));
1790   n = MEMDEBUG_malloc (sizeof (struct NeighbourMapEntry), __LINE__);
1791   n->id = *peer;
1792   n->state = S_NOT_CONNECTED;
1793   n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1794   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1795                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1796                                  MAX_BANDWIDTH_CARRY_S);
1797   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1798   GNUNET_assert (GNUNET_OK ==
1799                  GNUNET_CONTAINER_multihashmap_put (neighbours,
1800                                                     &n->id.hashPubKey, n,
1801                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1802   return n;
1803 }
1804
1805
1806 /**
1807  * Check if the two given addresses are the same.
1808  * Actually only checks if the sessions are non-NULL
1809  * (which they should be) and then if they are identical;
1810  * the actual addresses don't matter if the session
1811  * pointers match anyway, and we must have session pointers
1812  * at this time.
1813  *
1814  * @param a1 first address to compare
1815  * @param a2 other address to compare
1816  * @return GNUNET_NO if the addresses do not match, GNUNET_YES if they do match
1817  */
1818 static int
1819 address_matches (const struct NeighbourAddress *a1,
1820                  const struct NeighbourAddress *a2)
1821 {
1822   if ( (NULL == a1->session) ||
1823        (NULL == a2->session) )
1824   {
1825     GNUNET_break (0);
1826     return 0;
1827   }
1828   return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1829 }
1830
1831
1832 /**
1833  * Try to create a connection to the given target (eventually).
1834  *
1835  * @param target peer to try to connect to
1836  */
1837 void
1838 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1839 {
1840   struct NeighbourMapEntry *n;
1841
1842   if (NULL == neighbours)  
1843   {
1844           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1845                       "Asked to connect to peer `%s' during shutdown\n",
1846                       GNUNET_i2s (target));
1847                 return; /* during shutdown, do nothing */
1848   }
1849   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
1850               "Asked to connect to peer `%s'\n",
1851               GNUNET_i2s (target));
1852   if (0 == memcmp (target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1853   {
1854     /* refuse to connect to myself */
1855     /* FIXME: can this happen? Is this not an API violation? */
1856     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1857                 "Refusing to try to connect to myself.\n");
1858     return;
1859   }
1860   n = lookup_neighbour (target);
1861   if (NULL != n)
1862   {
1863     switch (n->state)
1864     {
1865     case S_NOT_CONNECTED:
1866       /* this should not be possible */
1867       GNUNET_break (0);
1868       free_neighbour (n, GNUNET_NO);
1869       break;
1870     case S_INIT_ATS:
1871     case S_INIT_BLACKLIST:
1872     case S_CONNECT_SENT:
1873     case S_CONNECT_RECV_BLACKLIST_INBOUND:
1874     case S_CONNECT_RECV_ATS:
1875     case S_CONNECT_RECV_BLACKLIST:
1876     case S_CONNECT_RECV_ACK:
1877       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1878                   "Ignoring request to try to connect to `%s', already trying!\n",
1879                   GNUNET_i2s (target));
1880       return; /* already trying */
1881     case S_CONNECTED:      
1882     case S_RECONNECT_ATS:
1883     case S_RECONNECT_BLACKLIST:
1884     case S_RECONNECT_SENT:
1885     case S_CONNECTED_SWITCHING_BLACKLIST:
1886     case S_CONNECTED_SWITCHING_CONNECT_SENT:
1887       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1888                   "Ignoring request to try to connect, already connected to `%s'!\n",
1889                   GNUNET_i2s (target));
1890       return; /* already connected */
1891     case S_DISCONNECT:
1892       /* get rid of remains, ready to re-try immediately */
1893       free_neighbour (n, GNUNET_NO);
1894       break;
1895     case S_DISCONNECT_FINISHED:
1896       /* should not be possible */      
1897       GNUNET_assert (0); 
1898     default:
1899       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
1900       GNUNET_break (0);
1901       free_neighbour (n, GNUNET_NO);
1902       break;
1903     }
1904   }
1905   n = setup_neighbour (target);  
1906   n->state = S_INIT_ATS; 
1907   n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1908
1909   GNUNET_ATS_reset_backoff (GST_ats, target);
1910   n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1911 }
1912
1913
1914 /**
1915  * Function called with the result of a blacklist check.
1916  *
1917  * @param cls closure with the 'struct BlackListCheckContext'
1918  * @param peer peer this check affects
1919  * @param result GNUNET_OK if the address is allowed
1920  */
1921 static void
1922 handle_test_blacklist_cont (void *cls,
1923                             const struct GNUNET_PeerIdentity *peer,
1924                             int result)
1925 {
1926   struct BlackListCheckContext *bcc = cls;
1927   struct NeighbourMapEntry *n;
1928
1929   bcc->bc = NULL;
1930   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1931               "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1932               GNUNET_i2s (peer),
1933               (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1934   if (NULL == (n = lookup_neighbour (peer)))
1935     goto cleanup; /* nobody left to care about new address */
1936   switch (n->state)
1937   {
1938   case S_NOT_CONNECTED:
1939     /* this should not be possible */
1940     GNUNET_break (0);
1941     free_neighbour (n, GNUNET_NO);
1942     break;
1943   case S_INIT_ATS:
1944     /* still waiting on ATS suggestion */
1945     break;
1946   case S_INIT_BLACKLIST:
1947     /* check if the address the blacklist was fine with matches
1948        ATS suggestion, if so, we can move on! */
1949     if ( (GNUNET_OK == result) &&
1950          (1 == n->send_connect_ack) )
1951     {
1952       n->send_connect_ack = 2;
1953       send_session_connect_ack_message (bcc->na.address,
1954                                         bcc->na.session,
1955                                         n->connect_ack_timestamp);
1956     }
1957     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1958       break; /* result for an address we currently don't care about */
1959     if (GNUNET_OK == result)
1960     {
1961       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1962       n->state = S_CONNECT_SENT;
1963       send_session_connect (&n->primary_address);
1964     }
1965     else
1966     {
1967       // FIXME: should also possibly destroy session with plugin!?
1968       GNUNET_ATS_address_destroyed (GST_ats,
1969                                     bcc->na.address,
1970                                     NULL);
1971       free_address (&n->primary_address);
1972       n->state = S_INIT_ATS;
1973       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1974       // FIXME: do we need to ask ATS again for suggestions?
1975       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1976     }
1977     break;
1978   case S_CONNECT_SENT:
1979     /* waiting on CONNECT_ACK, send ACK if one is pending */
1980     if ( (GNUNET_OK == result) &&
1981          (1 == n->send_connect_ack) )
1982     {
1983       n->send_connect_ack = 2;
1984       send_session_connect_ack_message (n->primary_address.address,
1985                                         n->primary_address.session,
1986                                         n->connect_ack_timestamp);
1987     }
1988     break; 
1989   case S_CONNECT_RECV_BLACKLIST_INBOUND:
1990     if (GNUNET_OK == result)
1991         GST_ats_add_address (bcc->na.address, bcc->na.session);
1992
1993     n->state = S_CONNECT_RECV_ATS;
1994     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1995     GNUNET_ATS_reset_backoff (GST_ats, peer);
1996     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
1997     break;
1998   case S_CONNECT_RECV_ATS:
1999     /* still waiting on ATS suggestion, don't care about blacklist */
2000     break;
2001   case S_CONNECT_RECV_BLACKLIST:
2002     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2003       break; /* result for an address we currently don't care about */
2004     if (GNUNET_OK == result)
2005     {
2006       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
2007       n->state = S_CONNECT_RECV_ACK;
2008       send_session_connect_ack_message (bcc->na.address,
2009                                         bcc->na.session,
2010                                         n->connect_ack_timestamp);
2011       if (1 == n->send_connect_ack) 
2012         n->send_connect_ack = 2;
2013     }
2014     else
2015     {
2016       // FIXME: should also possibly destroy session with plugin!?
2017       GNUNET_ATS_address_destroyed (GST_ats,
2018                                     bcc->na.address,
2019                                     NULL);
2020       free_address (&n->primary_address);
2021       n->state = S_INIT_ATS;
2022       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2023       // FIXME: do we need to ask ATS again for suggestions?
2024       GNUNET_ATS_reset_backoff (GST_ats, peer);
2025       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2026     }
2027     break;
2028   case S_CONNECT_RECV_ACK:
2029     /* waiting on SESSION_ACK, send ACK if one is pending */
2030     if ( (GNUNET_OK == result) &&
2031          (1 == n->send_connect_ack) )
2032     {
2033       n->send_connect_ack = 2;
2034       send_session_connect_ack_message (n->primary_address.address,
2035                                         n->primary_address.session,
2036                                         n->connect_ack_timestamp);
2037     }
2038     break; 
2039   case S_CONNECTED:
2040     /* already connected, don't care about blacklist */
2041     break;
2042   case S_RECONNECT_ATS:
2043     /* still waiting on ATS suggestion, don't care about blacklist */
2044     break;     
2045   case S_RECONNECT_BLACKLIST:
2046     if ( (GNUNET_OK == result) &&
2047          (1 == n->send_connect_ack) )
2048     {
2049       n->send_connect_ack = 2;
2050       send_session_connect_ack_message (bcc->na.address,
2051                                         bcc->na.session,
2052                                         n->connect_ack_timestamp);
2053     }
2054     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
2055       break; /* result for an address we currently don't care about */
2056     if (GNUNET_OK == result)
2057     {
2058       send_session_connect (&n->primary_address);
2059       n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2060       n->state = S_RECONNECT_SENT;
2061     }
2062     else
2063     {
2064       GNUNET_ATS_address_destroyed (GST_ats,
2065                                     bcc->na.address,
2066                                     NULL);
2067       n->state = S_RECONNECT_ATS;
2068       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2069       // FIXME: do we need to ask ATS again for suggestions?
2070       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2071     }
2072     break;
2073   case S_RECONNECT_SENT:
2074     /* waiting on CONNECT_ACK, don't care about blacklist */
2075     if ( (GNUNET_OK == result) &&
2076          (1 == n->send_connect_ack) )
2077     {
2078       n->send_connect_ack = 2;
2079       send_session_connect_ack_message (n->primary_address.address,
2080                                         n->primary_address.session,
2081                                         n->connect_ack_timestamp);
2082     }
2083     break;     
2084   case S_CONNECTED_SWITCHING_BLACKLIST:
2085     if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
2086       break; /* result for an address we currently don't care about */
2087     if (GNUNET_OK == result)
2088     {
2089       send_session_connect (&n->alternative_address);
2090       n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
2091     }
2092     else
2093     {
2094       GNUNET_ATS_address_destroyed (GST_ats,
2095                                     bcc->na.address,
2096                                     NULL);
2097       free_address (&n->alternative_address);
2098       n->state = S_CONNECTED;
2099     }
2100     break;
2101   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2102     /* waiting on CONNECT_ACK, don't care about blacklist */
2103     if ( (GNUNET_OK == result) &&
2104          (1 == n->send_connect_ack) )
2105     {
2106       n->send_connect_ack = 2;
2107       send_session_connect_ack_message (n->primary_address.address,
2108                                         n->primary_address.session,
2109                                         n->connect_ack_timestamp);
2110     }
2111     break;     
2112   case S_DISCONNECT:
2113     /* Nothing to do here, ATS will already do what can be done */
2114     break;
2115   case S_DISCONNECT_FINISHED:
2116     /* should not be possible */
2117     GNUNET_assert (0);
2118     break;
2119   default:
2120     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2121     GNUNET_break (0);
2122     free_neighbour (n, GNUNET_NO);
2123     break;
2124   }
2125  cleanup:
2126   MEMDEBUG_free (bcc->na.address, __LINE__);
2127   //GNUNET_HELLO_address_free (bcc->na.address);
2128   GNUNET_CONTAINER_DLL_remove (bc_head,
2129                                bc_tail,
2130                                bcc);
2131   MEMDEBUG_free (bcc, __LINE__);
2132 }
2133
2134
2135 /**
2136  * We want to know if connecting to a particular peer via
2137  * a particular address is allowed.  Check it!
2138  *
2139  * @param peer identity of the peer to switch the address for
2140  * @param ts time at which the check was initiated
2141  * @param address address of the other peer, NULL if other peer
2142  *                       connected to us
2143  * @param session session to use (or NULL)
2144  */
2145 static void
2146 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2147                  struct GNUNET_TIME_Absolute ts,
2148                  const struct GNUNET_HELLO_Address *address,
2149                  struct Session *session)
2150 {
2151   struct BlackListCheckContext *bcc;
2152   struct GST_BlacklistCheck *bc;
2153
2154   bcc =
2155       MEMDEBUG_malloc (sizeof (struct BlackListCheckContext), __LINE__);
2156   bcc->na.address = GNUNET_HELLO_address_copy (address);
2157   MEMDEBUG_add_alloc (bcc->na.address, GNUNET_HELLO_address_get_size (address), __LINE__);
2158   bcc->na.session = session;
2159   bcc->na.connect_timestamp = ts;
2160   GNUNET_CONTAINER_DLL_insert (bc_head,
2161                                bc_tail,
2162                                bcc);
2163   if (NULL != (bc = GST_blacklist_test_allowed (peer, 
2164                                                 address->transport_name,
2165                                                 &handle_test_blacklist_cont, bcc)))
2166     bcc->bc = bc; 
2167   /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2168      we must only store 'bc' if 'bc' is non-NULL... */
2169 }
2170
2171
2172 /**
2173  * We received a 'SESSION_CONNECT' message from the other peer.
2174  * Consider switching to it.
2175  *
2176  * @param message possibly a 'struct SessionConnectMessage' (check format)
2177  * @param peer identity of the peer to switch the address for
2178  * @param address address of the other peer, NULL if other peer
2179  *                       connected to us
2180  * @param session session to use (or NULL)
2181  */
2182 void
2183 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2184                                const struct GNUNET_PeerIdentity *peer,
2185                                const struct GNUNET_HELLO_Address *address,
2186                                struct Session *session)
2187 {
2188   const struct SessionConnectMessage *scm;
2189   struct NeighbourMapEntry *n;
2190   struct GNUNET_TIME_Absolute ts;
2191
2192   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2193               "Received CONNECT message from peer `%s'\n", 
2194               GNUNET_i2s (peer));
2195
2196   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2197   {
2198     GNUNET_break_op (0);
2199     return;
2200   }
2201   if (NULL == neighbours)
2202     return; /* we're shutting down */
2203   scm = (const struct SessionConnectMessage *) message;
2204   GNUNET_break_op (0 == ntohl (scm->reserved));
2205   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2206   n = lookup_neighbour (peer);
2207   if (NULL == n)
2208     n = setup_neighbour (peer);
2209   n->send_connect_ack = 1;
2210   n->connect_ack_timestamp = ts;
2211
2212   switch (n->state)
2213   {  
2214   case S_NOT_CONNECTED:
2215     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2216     /* Do a blacklist check for the new address */
2217     check_blacklist (peer, ts, address, session);
2218     break;
2219   case S_INIT_ATS:
2220     /* CONNECT message takes priority over us asking ATS for address */
2221     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2222     /* fallthrough */
2223   case S_INIT_BLACKLIST:
2224   case S_CONNECT_SENT:
2225   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2226   case S_CONNECT_RECV_ATS:
2227   case S_CONNECT_RECV_BLACKLIST:
2228   case S_CONNECT_RECV_ACK:
2229     /* It can never hurt to have an alternative address in the above cases, 
2230        see if it is allowed */
2231     check_blacklist (peer, ts, address, session);
2232     break;
2233   case S_CONNECTED:
2234     /* we are already connected and can thus send the ACK immediately;
2235        still, it can never hurt to have an alternative address, so also
2236        tell ATS  about it */
2237     GNUNET_assert (NULL != n->primary_address.address);
2238     GNUNET_assert (NULL != n->primary_address.session);
2239     n->send_connect_ack = 0;
2240     send_session_connect_ack_message (n->primary_address.address,
2241                                       n->primary_address.session, ts);
2242     check_blacklist (peer, ts, address, session);
2243     break;
2244   case S_RECONNECT_ATS:
2245   case S_RECONNECT_BLACKLIST:
2246   case S_RECONNECT_SENT:
2247     /* It can never hurt to have an alternative address in the above cases, 
2248        see if it is allowed */
2249     check_blacklist (peer, ts, address, session);
2250     break;
2251   case S_CONNECTED_SWITCHING_BLACKLIST:
2252   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2253     /* we are already connected and can thus send the ACK immediately;
2254        still, it can never hurt to have an alternative address, so also
2255        tell ATS  about it */
2256     GNUNET_assert (NULL != n->primary_address.address);
2257     GNUNET_assert (NULL != n->primary_address.session);
2258     n->send_connect_ack = 0;
2259     send_session_connect_ack_message (n->primary_address.address,
2260                                       n->primary_address.session, ts);
2261     check_blacklist (peer, ts, address, session);
2262     break;
2263   case S_DISCONNECT:
2264     /* get rid of remains without terminating sessions, ready to re-try */
2265     free_neighbour (n, GNUNET_YES);
2266     n = setup_neighbour (peer);
2267     n->state = S_CONNECT_RECV_ATS;
2268     GNUNET_ATS_reset_backoff (GST_ats, peer);
2269     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2270     break;
2271   case S_DISCONNECT_FINISHED:
2272     /* should not be possible */
2273     GNUNET_assert (0);
2274     break;
2275   default:
2276     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2277     GNUNET_break (0);
2278     free_neighbour (n, GNUNET_NO);
2279     break;
2280   }
2281 }
2282
2283
2284 /**
2285  * For an existing neighbour record, set the active connection to
2286  * use the given address.  
2287  *
2288  * @param peer identity of the peer to switch the address for
2289  * @param address address of the other peer, NULL if other peer
2290  *                       connected to us
2291  * @param session session to use (or NULL)
2292  * @param ats performance data
2293  * @param ats_count number of entries in ats
2294  * @param bandwidth_in inbound quota to be used when connection is up
2295  * @param bandwidth_out outbound quota to be used when connection is up
2296  */
2297 void
2298 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2299                                   const struct GNUNET_HELLO_Address *address,
2300                                   struct Session *session,
2301                                   const struct GNUNET_ATS_Information *ats,
2302                                   uint32_t ats_count,
2303                                   struct GNUNET_BANDWIDTH_Value32NBO
2304                                   bandwidth_in,
2305                                   struct GNUNET_BANDWIDTH_Value32NBO
2306                                   bandwidth_out)
2307 {
2308   struct NeighbourMapEntry *n;
2309   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2310
2311   GNUNET_assert (address->transport_name != NULL);
2312   if (NULL == (n = lookup_neighbour (peer)))
2313     return;
2314
2315   /* Obtain an session for this address from plugin */
2316   if (NULL == (papi = GST_plugins_find (address->transport_name)))
2317   {
2318     /* we don't have the plugin for this address */
2319           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2320                       "2348 : `%s' \n", address->transport_name);
2321     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2322     return;
2323   }
2324   if ((NULL == session) && (0 == address->address_length))
2325   {
2326     GNUNET_break (0);
2327     if (strlen (address->transport_name) > 0)
2328       GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2329     return;
2330   }
2331
2332   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2333               "ATS tells us to switch to address '%s' session %p for "
2334               "peer `%s' in state %s (quota in/out %u %u )\n",
2335               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2336               session,
2337               GNUNET_i2s (peer),
2338               print_state (n->state),
2339               ntohl (bandwidth_in.value__),
2340               ntohl (bandwidth_out.value__));
2341
2342   if (NULL == session)
2343   {
2344     session = papi->get_session (papi->cls, address);
2345     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2346                 "Obtained new session for peer `%s' and  address '%s': %p\n",
2347                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2348   }
2349   if (NULL == session)
2350   {
2351     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2352                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
2353                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));    
2354     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2355     return;
2356   }
2357   switch (n->state)
2358   {
2359   case S_NOT_CONNECTED:
2360     GNUNET_break (0);
2361     free_neighbour (n, GNUNET_NO);
2362     return;
2363   case S_INIT_ATS:
2364     set_address (&n->primary_address,
2365                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2366     n->state = S_INIT_BLACKLIST;
2367     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2368     check_blacklist (&n->id,
2369                      n->connect_ack_timestamp,
2370                      address, session);
2371     break;
2372   case S_INIT_BLACKLIST:
2373     /* ATS suggests a different address, switch again */
2374     set_address (&n->primary_address,
2375                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2376     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2377     check_blacklist (&n->id,
2378                      n->connect_ack_timestamp,
2379                      address, session);
2380     break;
2381   case S_CONNECT_SENT:
2382     /* ATS suggests a different address, switch again */
2383     set_address (&n->primary_address,
2384                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2385     n->state = S_INIT_BLACKLIST;
2386     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2387     check_blacklist (&n->id,
2388                      n->connect_ack_timestamp,
2389                      address, session);
2390     break;
2391   case S_CONNECT_RECV_ATS:
2392     set_address (&n->primary_address,
2393                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2394     n->state = S_CONNECT_RECV_BLACKLIST;
2395     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2396     check_blacklist (&n->id,
2397                      n->connect_ack_timestamp,
2398                      address, session);
2399     break;
2400   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2401     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2402     check_blacklist (&n->id,
2403                      n->connect_ack_timestamp,
2404                      address, session);
2405     break;
2406   case S_CONNECT_RECV_BLACKLIST:
2407   case S_CONNECT_RECV_ACK:
2408     /* ATS asks us to switch while we were trying to connect; switch to new
2409        address and check blacklist again */
2410     set_address (&n->primary_address,
2411                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2412     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2413     check_blacklist (&n->id,
2414                      n->connect_ack_timestamp,
2415                      address, session);
2416     break;
2417   case S_CONNECTED:
2418     GNUNET_assert (NULL != n->primary_address.address);
2419     GNUNET_assert (NULL != n->primary_address.session);
2420     if (n->primary_address.session == session)
2421     {
2422       /* not an address change, just a quota change */
2423       set_address (&n->primary_address,
2424                    address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2425       break;
2426     }
2427     /* ATS asks us to switch a life connection; see if we can get
2428        a CONNECT_ACK on it before we actually do this! */
2429     set_address (&n->alternative_address,
2430                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2431     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2432     check_blacklist (&n->id,
2433                      GNUNET_TIME_absolute_get (),
2434                      address, session);
2435     break;
2436   case S_RECONNECT_ATS:
2437     set_address (&n->primary_address,
2438                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2439     n->state = S_RECONNECT_BLACKLIST;
2440     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2441     check_blacklist (&n->id,
2442                      n->connect_ack_timestamp,
2443                      address, session);
2444     break;
2445   case S_RECONNECT_BLACKLIST:
2446     /* ATS asks us to switch while we were trying to reconnect; switch to new
2447        address and check blacklist again */
2448     set_address (&n->primary_address,
2449                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2450     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2451     check_blacklist (&n->id,
2452                      n->connect_ack_timestamp,
2453                      address, session);
2454     break;
2455   case S_RECONNECT_SENT:
2456     /* ATS asks us to switch while we were trying to reconnect; switch to new
2457        address and check blacklist again */
2458     set_address (&n->primary_address,
2459                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2460     n->state = S_RECONNECT_BLACKLIST;
2461     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2462     check_blacklist (&n->id,
2463                      n->connect_ack_timestamp,
2464                      address, session);
2465     break;
2466   case S_CONNECTED_SWITCHING_BLACKLIST:
2467     if (n->primary_address.session == session)
2468     {
2469       /* ATS switches back to still-active session */
2470       free_address (&n->alternative_address);
2471       n->state = S_CONNECTED;
2472       break;
2473     }
2474     /* ATS asks us to switch a life connection, update blacklist check */
2475     set_address (&n->alternative_address,
2476                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2477     check_blacklist (&n->id,
2478                      GNUNET_TIME_absolute_get (),
2479                      address, session);
2480     break;
2481   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2482     if (n->primary_address.session == session)
2483     {
2484       /* ATS switches back to still-active session */
2485       free_address (&n->alternative_address);
2486       n->state = S_CONNECTED;
2487       break;
2488     }
2489     /* ATS asks us to switch a life connection, update blacklist check */
2490     set_address (&n->alternative_address,
2491                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2492     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2493     check_blacklist (&n->id,
2494                      GNUNET_TIME_absolute_get (),
2495                      address, session);
2496     break;
2497   case S_DISCONNECT:
2498     /* not going to switch addresses while disconnecting */
2499     return;
2500   case S_DISCONNECT_FINISHED:
2501     GNUNET_assert (0);
2502     break;
2503   default:
2504     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2505     GNUNET_break (0);
2506     break;
2507   }
2508 }
2509
2510
2511 /**
2512  * Master task run for every neighbour.  Performs all of the time-related
2513  * activities (keep alive, send next message, disconnect if idle, finish
2514  * clean up after disconnect).
2515  *
2516  * @param cls the 'struct NeighbourMapEntry' for which we are running
2517  * @param tc scheduler context (unused)
2518  */
2519 static void
2520 master_task (void *cls,
2521              const struct GNUNET_SCHEDULER_TaskContext *tc)
2522 {
2523   struct NeighbourMapEntry *n = cls;
2524   struct GNUNET_TIME_Relative delay;
2525
2526   n->task = GNUNET_SCHEDULER_NO_TASK;
2527   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);  
2528   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2529               "Master task runs for neighbour `%s' in state %s with timeout in %llu ms\n",
2530               GNUNET_i2s (&n->id),
2531               print_state(n->state),
2532               (unsigned long long) delay.rel_value);
2533   switch (n->state)
2534   {
2535   case S_NOT_CONNECTED:
2536     /* invalid state for master task, clean up */
2537     GNUNET_break (0);
2538     n->state = S_DISCONNECT_FINISHED;
2539     free_neighbour (n, GNUNET_NO);
2540     return;
2541   case S_INIT_ATS:
2542     if (0 == delay.rel_value)
2543     {
2544       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2545                   "Connection to `%s' timed out waiting for ATS to provide address\n",
2546                   GNUNET_i2s (&n->id));
2547       n->state = S_DISCONNECT_FINISHED;
2548       free_neighbour (n, GNUNET_NO);
2549       return;
2550     }
2551     break;
2552   case S_INIT_BLACKLIST:
2553     if (0 == delay.rel_value)
2554     {
2555       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2556                   "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2557                   GNUNET_i2s (&n->id));
2558       n->state = S_DISCONNECT_FINISHED;
2559       free_neighbour (n, GNUNET_NO);
2560       return;
2561     }
2562     break;
2563   case S_CONNECT_SENT:
2564     if (0 == delay.rel_value)
2565     {
2566       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2567                   "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2568                   GNUNET_i2s (&n->id));
2569       disconnect_neighbour (n);
2570       return;
2571     }
2572     break;
2573   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2574     if (0 == delay.rel_value)
2575     {
2576       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2577                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2578                   GNUNET_i2s (&n->id));
2579       n->state = S_DISCONNECT_FINISHED;
2580       free_neighbour (n, GNUNET_NO);
2581       return;
2582     }
2583     break;
2584   case S_CONNECT_RECV_ATS:
2585     if (0 == delay.rel_value)
2586     {
2587       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2588                   "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2589                   GNUNET_i2s (&n->id));
2590       n->state = S_DISCONNECT_FINISHED;
2591       free_neighbour (n, GNUNET_NO);
2592       return;
2593     }
2594     break;
2595   case S_CONNECT_RECV_BLACKLIST:
2596     if (0 == delay.rel_value)
2597     {
2598       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2599                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2600                   GNUNET_i2s (&n->id));
2601       n->state = S_DISCONNECT_FINISHED;
2602       free_neighbour (n, GNUNET_NO);
2603       return;
2604     }
2605     break;
2606   case S_CONNECT_RECV_ACK:
2607     if (0 == delay.rel_value)
2608     {
2609       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2610                   "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2611                   GNUNET_i2s (&n->id));
2612       disconnect_neighbour (n);
2613       return;
2614     }
2615     break;
2616   case S_CONNECTED:
2617     if (0 == delay.rel_value)
2618     {
2619       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2620                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2621                   GNUNET_i2s (&n->id));
2622       disconnect_neighbour (n);
2623       return;
2624     }
2625     try_transmission_to_peer (n);
2626     send_keepalive (n);
2627     break;
2628   case S_RECONNECT_ATS:
2629     if (0 == delay.rel_value)
2630     {
2631       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2632                   "Connection to `%s' timed out, waiting for ATS replacement address\n",
2633                   GNUNET_i2s (&n->id));
2634       disconnect_neighbour (n);
2635       return;
2636     }
2637     break;
2638   case S_RECONNECT_BLACKLIST:
2639     if (0 == delay.rel_value)
2640     {
2641       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2642                   "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2643                   GNUNET_i2s (&n->id));
2644       disconnect_neighbour (n);
2645       return;
2646     }
2647     break;
2648   case S_RECONNECT_SENT:
2649     if (0 == delay.rel_value)
2650     {
2651       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2652                   "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2653                   GNUNET_i2s (&n->id));
2654       disconnect_neighbour (n);
2655       return;
2656     }
2657     break;
2658   case S_CONNECTED_SWITCHING_BLACKLIST:
2659     if (0 == delay.rel_value)
2660     {
2661       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2662                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2663                   GNUNET_i2s (&n->id));
2664       disconnect_neighbour (n);
2665       return;
2666     }
2667     try_transmission_to_peer (n);
2668     send_keepalive (n);
2669     break;
2670   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2671     if (0 == delay.rel_value)
2672     {
2673       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2674                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2675                   GNUNET_i2s (&n->id));
2676       disconnect_neighbour (n);
2677       return;
2678     }
2679     try_transmission_to_peer (n);
2680     send_keepalive (n);
2681     break;
2682   case S_DISCONNECT:
2683     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2684                 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2685                 GNUNET_i2s (&n->id));
2686     free_neighbour (n, GNUNET_NO);
2687     return;
2688   case S_DISCONNECT_FINISHED:
2689     /* how did we get here!? */
2690     GNUNET_assert (0);
2691     break;
2692   default:
2693     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2694     GNUNET_break (0);
2695     break;  
2696   }
2697   if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2698        (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2699        (S_CONNECTED == n->state) )    
2700   {
2701     /* if we are *now* in one of these three states, we're sending
2702        keep alive messages, so we need to consider the keepalive
2703        delay, not just the connection timeout */
2704     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2705                                       delay);
2706   }
2707   if (GNUNET_SCHEDULER_NO_TASK == n->task)
2708     n->task = GNUNET_SCHEDULER_add_delayed (delay,
2709                                             &master_task,
2710                                             n);
2711 }
2712
2713
2714 /**
2715  * Send a SESSION_ACK message to the neighbour to confirm that we
2716  * got his CONNECT_ACK.
2717  *
2718  * @param n neighbour to send the SESSION_ACK to
2719  */
2720 static void
2721 send_session_ack_message (struct NeighbourMapEntry *n)
2722 {
2723   struct GNUNET_MessageHeader msg;
2724
2725   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2726   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2727   (void) send_with_session(n,
2728                            (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2729                            UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2730                            NULL, NULL);
2731 }
2732
2733
2734 /**
2735  * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2736  * Consider switching to it.
2737  *
2738  * @param message possibly a 'struct SessionConnectMessage' (check format)
2739  * @param peer identity of the peer to switch the address for
2740  * @param address address of the other peer, NULL if other peer
2741  *                       connected to us
2742  * @param session session to use (or NULL)
2743  */
2744 void
2745 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2746                                    const struct GNUNET_PeerIdentity *peer,
2747                                    const struct GNUNET_HELLO_Address *address,
2748                                    struct Session *session)
2749 {
2750   const struct SessionConnectMessage *scm;
2751   struct GNUNET_TIME_Absolute ts;
2752   struct NeighbourMapEntry *n;
2753
2754   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2755               "Received CONNECT_ACK message from peer `%s'\n",
2756               GNUNET_i2s (peer));
2757
2758   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2759   {
2760     GNUNET_break_op (0);
2761     return;
2762   }
2763   scm = (const struct SessionConnectMessage *) message;
2764   GNUNET_break_op (ntohl (scm->reserved) == 0);
2765   if (NULL == (n = lookup_neighbour (peer)))
2766   {
2767     GNUNET_STATISTICS_update (GST_stats,
2768                               gettext_noop
2769                               ("# unexpected CONNECT_ACK messages (no peer)"),
2770                               1, GNUNET_NO);
2771     return;
2772   }
2773   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2774   switch (n->state)
2775   {
2776   case S_NOT_CONNECTED:
2777     GNUNET_break (0);
2778     free_neighbour (n, GNUNET_NO);
2779     return;
2780   case S_INIT_ATS:
2781   case S_INIT_BLACKLIST:
2782     GNUNET_STATISTICS_update (GST_stats,
2783                               gettext_noop
2784                               ("# unexpected CONNECT_ACK messages (not ready)"),
2785                               1, GNUNET_NO);
2786     break;    
2787   case S_CONNECT_SENT:
2788     if (ts.abs_value != n->primary_address.connect_timestamp.abs_value)
2789       break; /* ACK does not match our original CONNECT message */
2790     n->state = S_CONNECTED;
2791     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2792     GNUNET_STATISTICS_set (GST_stats, 
2793                            gettext_noop ("# peers connected"), 
2794                            ++neighbours_connected,
2795                            GNUNET_NO);
2796     connect_notify_cb (callback_cls, &n->id,
2797                        n->primary_address.bandwidth_in,
2798                        n->primary_address.bandwidth_out);
2799     /* Tell ATS that the outbound session we created to send CONNECT was successfull */
2800     GST_ats_add_address (n->primary_address.address, n->primary_address.session);
2801     set_address (&n->primary_address,
2802                  n->primary_address.address,
2803                  n->primary_address.session,
2804                  n->primary_address.bandwidth_in,
2805                  n->primary_address.bandwidth_out,
2806                  GNUNET_YES);
2807     send_session_ack_message (n);
2808     break;
2809   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2810   case S_CONNECT_RECV_ATS:
2811   case S_CONNECT_RECV_BLACKLIST:
2812   case S_CONNECT_RECV_ACK:
2813     GNUNET_STATISTICS_update (GST_stats,
2814                               gettext_noop
2815                               ("# unexpected CONNECT_ACK messages (not ready)"),
2816                               1, GNUNET_NO);
2817     break;
2818   case S_CONNECTED:
2819     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2820     send_session_ack_message (n);
2821     break;
2822   case S_RECONNECT_ATS:
2823   case S_RECONNECT_BLACKLIST:
2824     /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2825        to give us a new address... */
2826     GNUNET_STATISTICS_update (GST_stats,
2827                               gettext_noop
2828                               ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2829                               1, GNUNET_NO);
2830     break;
2831   case S_RECONNECT_SENT:
2832     /* new address worked; go back to connected! */
2833     n->state = S_CONNECTED;
2834     send_session_ack_message (n);
2835     break;
2836   case S_CONNECTED_SWITCHING_BLACKLIST:
2837     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2838     send_session_ack_message (n);
2839     break;
2840   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2841     /* new address worked; adopt it and go back to connected! */
2842     n->state = S_CONNECTED;
2843     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2844     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2845
2846     GST_ats_add_address (n->alternative_address.address, n->alternative_address.session);
2847     set_address (&n->primary_address,
2848                  n->alternative_address.address,
2849                  n->alternative_address.session,
2850                  n->alternative_address.bandwidth_in,
2851                  n->alternative_address.bandwidth_out,
2852                  GNUNET_YES);
2853     free_address (&n->alternative_address);
2854     send_session_ack_message (n);
2855     break;    
2856   case S_DISCONNECT:
2857     GNUNET_STATISTICS_update (GST_stats,
2858                               gettext_noop
2859                               ("# unexpected CONNECT_ACK messages (disconnecting)"),
2860                               1, GNUNET_NO);
2861     break;
2862   case S_DISCONNECT_FINISHED:
2863     GNUNET_assert (0);
2864     break;
2865   default:
2866     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2867     GNUNET_break (0);
2868     break;   
2869   }
2870 }
2871
2872
2873 /**
2874  * A session was terminated. Take note; if needed, try to get
2875  * an alternative address from ATS.
2876  *
2877  * @param peer identity of the peer where the session died
2878  * @param session session that is gone
2879  * @return GNUNET_YES if this was a session used, GNUNET_NO if
2880  *        this session was not in use
2881  */
2882 int
2883 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2884                                    struct Session *session)
2885 {
2886   struct NeighbourMapEntry *n;
2887   struct BlackListCheckContext *bcc;
2888   struct BlackListCheckContext *bcc_next;
2889
2890   /* make sure to cancel all ongoing blacklist checks involving 'session' */
2891   bcc_next = bc_head;
2892   while (NULL != (bcc = bcc_next))
2893   {
2894     bcc_next = bcc->next;
2895     if (bcc->na.session == session)
2896     {
2897       GST_blacklist_test_cancel (bcc->bc);
2898       MEMDEBUG_free (bcc->na.address, __LINE__);
2899       //GNUNET_HELLO_address_free (bcc->na.address);
2900       GNUNET_CONTAINER_DLL_remove (bc_head,
2901                                    bc_tail,
2902                                    bcc);
2903       MEMDEBUG_free (bcc, __LINE__);
2904     }
2905   }
2906   if (NULL == (n = lookup_neighbour (peer)))
2907     return GNUNET_NO; /* can't affect us */
2908   if (session != n->primary_address.session)
2909   {
2910     if (session == n->alternative_address.session)
2911     {
2912       free_address (&n->alternative_address);
2913       if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2914            (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
2915         n->state = S_CONNECTED;
2916       else
2917         GNUNET_break (0);
2918     }
2919     return GNUNET_NO; /* doesn't affect us further */
2920   }
2921
2922   n->expect_latency_response = GNUNET_NO;
2923   switch (n->state)
2924   {
2925   case S_NOT_CONNECTED:
2926     GNUNET_break (0);
2927     free_neighbour (n, GNUNET_NO);
2928     return GNUNET_YES;
2929   case S_INIT_ATS:
2930     GNUNET_break (0);
2931     free_neighbour (n, GNUNET_NO);
2932     return GNUNET_YES;
2933   case S_INIT_BLACKLIST:
2934   case S_CONNECT_SENT:
2935     free_address (&n->primary_address);
2936     n->state = S_INIT_ATS;
2937     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2938     // FIXME: need to ask ATS for suggestions again?
2939     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2940     break;
2941   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2942   case S_CONNECT_RECV_ATS:    
2943   case S_CONNECT_RECV_BLACKLIST:
2944   case S_CONNECT_RECV_ACK:
2945     /* error on inbound session; free neighbour entirely */
2946     free_address (&n->primary_address);
2947     free_neighbour (n, GNUNET_NO);
2948     return GNUNET_YES;
2949   case S_CONNECTED:
2950     free_address (&n->primary_address);
2951     n->state = S_RECONNECT_ATS;
2952     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2953     /* FIXME: is this ATS call needed? */
2954     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2955     break;
2956   case S_RECONNECT_ATS:
2957     /* we don't have an address, how can it go down? */
2958     GNUNET_break (0);
2959     break;
2960   case S_RECONNECT_BLACKLIST:
2961   case S_RECONNECT_SENT:
2962     n->state = S_RECONNECT_ATS;
2963     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2964     // FIXME: need to ask ATS for suggestions again?
2965     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2966     break;
2967   case S_CONNECTED_SWITCHING_BLACKLIST:
2968     /* primary went down while we were checking secondary against
2969        blacklist, adopt secondary as primary */       
2970     free_address (&n->primary_address);
2971     n->primary_address = n->alternative_address;
2972     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
2973     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2974     n->state = S_RECONNECT_BLACKLIST;
2975     break;
2976   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2977     /* primary went down while we were waiting for CONNECT_ACK on secondary;
2978        secondary as primary */       
2979     free_address (&n->primary_address);
2980     n->primary_address = n->alternative_address;
2981     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
2982     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2983     n->state = S_RECONNECT_SENT;
2984     break;
2985   case S_DISCONNECT:
2986     free_address (&n->primary_address);
2987     break;
2988   case S_DISCONNECT_FINISHED:
2989     /* neighbour was freed and plugins told to terminate session */
2990     return GNUNET_NO;
2991     break;
2992   default:
2993     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
2994     GNUNET_break (0);
2995     break;
2996   }
2997   if (GNUNET_SCHEDULER_NO_TASK != n->task)
2998     GNUNET_SCHEDULER_cancel (n->task);
2999   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3000   return GNUNET_YES;
3001 }
3002
3003
3004 /**
3005  * We received a 'SESSION_ACK' message from the other peer.
3006  * If we sent a 'CONNECT_ACK' last, this means we are now
3007  * connected.  Otherwise, do nothing.
3008  *
3009  * @param message possibly a 'struct SessionConnectMessage' (check format)
3010  * @param peer identity of the peer to switch the address for
3011  * @param address address of the other peer, NULL if other peer
3012  *                       connected to us
3013  * @param session session to use (or NULL)
3014  */
3015 void
3016 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3017                                    const struct GNUNET_PeerIdentity *peer,
3018                                    const struct GNUNET_HELLO_Address *address,
3019                                    struct Session *session)
3020 {
3021   struct NeighbourMapEntry *n;
3022
3023   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
3024               "Received SESSION_ACK message from peer `%s'\n",
3025               GNUNET_i2s (peer));
3026   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3027   {
3028     GNUNET_break_op (0);
3029     return;
3030   }
3031   if (NULL == (n = lookup_neighbour (peer)))
3032     return;
3033   /* check if we are in a plausible state for having sent
3034      a CONNECT_ACK.  If not, return, otherwise break */
3035   if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3036          (S_CONNECT_SENT != n->state) ) ||
3037        (2 != n->send_connect_ack) )
3038   {
3039     GNUNET_STATISTICS_update (GST_stats,
3040                               gettext_noop ("# unexpected SESSION ACK messages"), 1,
3041                               GNUNET_NO);
3042     return;
3043   }
3044   n->state = S_CONNECTED;
3045   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3046   GNUNET_STATISTICS_set (GST_stats, 
3047                          gettext_noop ("# peers connected"), 
3048                          ++neighbours_connected,
3049                          GNUNET_NO);
3050   connect_notify_cb (callback_cls, &n->id,
3051                      n->primary_address.bandwidth_in,
3052                      n->primary_address.bandwidth_out);
3053
3054   GST_ats_add_address (n->primary_address.address, n->primary_address.session);
3055   set_address (&n->primary_address,
3056                n->primary_address.address,
3057                n->primary_address.session,
3058                n->primary_address.bandwidth_in,
3059                n->primary_address.bandwidth_out,
3060                GNUNET_YES);
3061 }
3062
3063
3064 /**
3065  * Test if we're connected to the given peer.
3066  *
3067  * @param target peer to test
3068  * @return GNUNET_YES if we are connected, GNUNET_NO if not
3069  */
3070 int
3071 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3072 {
3073   return test_connected (lookup_neighbour (target));
3074 }
3075
3076
3077 /**
3078  * Change the incoming quota for the given peer.
3079  *
3080  * @param neighbour identity of peer to change qutoa for
3081  * @param quota new quota
3082  */
3083 void
3084 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3085                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
3086 {
3087   struct NeighbourMapEntry *n;
3088
3089   if (NULL == (n = lookup_neighbour (neighbour)))
3090   {
3091     GNUNET_STATISTICS_update (GST_stats,
3092                               gettext_noop
3093                               ("# SET QUOTA messages ignored (no such peer)"),
3094                               1, GNUNET_NO);
3095     return;
3096   }
3097   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3098               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3099               ntohl (quota.value__), GNUNET_i2s (&n->id));
3100   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3101   if (0 != ntohl (quota.value__))
3102     return;
3103   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3104               GNUNET_i2s (&n->id), "SET_QUOTA");
3105   if (GNUNET_YES == test_connected (n))
3106     GNUNET_STATISTICS_update (GST_stats,
3107                               gettext_noop ("# disconnects due to quota of 0"),
3108                               1, GNUNET_NO);
3109   disconnect_neighbour (n);
3110 }
3111
3112
3113 /**
3114  * We received a disconnect message from the given peer,
3115  * validate and process.
3116  *
3117  * @param peer sender of the message
3118  * @param msg the disconnect message
3119  */
3120 void
3121 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3122                                           *peer,
3123                                           const struct GNUNET_MessageHeader
3124                                           *msg)
3125 {
3126   struct NeighbourMapEntry *n;
3127   const struct SessionDisconnectMessage *sdm;
3128   struct GNUNET_HashCode hc;
3129
3130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3131               "Received DISCONNECT message from peer `%s'\n",
3132               GNUNET_i2s (peer));
3133   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3134   {
3135     // GNUNET_break_op (0);
3136     GNUNET_STATISTICS_update (GST_stats,
3137                               gettext_noop
3138                               ("# disconnect messages ignored (old format)"), 1,
3139                               GNUNET_NO);
3140     return;
3141   }
3142   sdm = (const struct SessionDisconnectMessage *) msg;
3143   if (NULL == (n = lookup_neighbour (peer)))
3144     return;                     /* gone already */
3145   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value <= n->connect_ack_timestamp.abs_value)
3146   {
3147     GNUNET_STATISTICS_update (GST_stats,
3148                               gettext_noop
3149                               ("# disconnect messages ignored (timestamp)"), 1,
3150                               GNUNET_NO);
3151     return;
3152   }
3153   GNUNET_CRYPTO_hash (&sdm->public_key,
3154                       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
3155                       &hc);
3156   if (0 != memcmp (peer, &hc, sizeof (struct GNUNET_PeerIdentity)))
3157   {
3158     GNUNET_break_op (0);
3159     return;
3160   }
3161   if (ntohl (sdm->purpose.size) !=
3162       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3163       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded) +
3164       sizeof (struct GNUNET_TIME_AbsoluteNBO))
3165   {
3166     GNUNET_break_op (0);
3167     return;
3168   }
3169   if (GNUNET_OK !=
3170       GNUNET_CRYPTO_ecc_verify
3171       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3172        &sdm->signature, &sdm->public_key))
3173   {
3174     GNUNET_break_op (0);
3175     return;
3176   }
3177   if (GNUNET_YES == test_connected (n))
3178     GNUNET_STATISTICS_update (GST_stats,
3179                               gettext_noop
3180                               ("# other peer asked to disconnect from us"), 1,
3181                               GNUNET_NO);
3182   disconnect_neighbour (n);
3183 }
3184
3185
3186 /**
3187  * Closure for the neighbours_iterate function.
3188  */
3189 struct IteratorContext
3190 {
3191   /**
3192    * Function to call on each connected neighbour.
3193    */
3194   GST_NeighbourIterator cb;
3195
3196   /**
3197    * Closure for 'cb'.
3198    */
3199   void *cb_cls;
3200 };
3201
3202
3203 /**
3204  * Call the callback from the closure for each connected neighbour.
3205  *
3206  * @param cls the 'struct IteratorContext'
3207  * @param key the hash of the public key of the neighbour
3208  * @param value the 'struct NeighbourMapEntry'
3209  * @return GNUNET_OK (continue to iterate)
3210  */
3211 static int
3212 neighbours_iterate (void *cls, const struct GNUNET_HashCode * key, void *value)
3213 {
3214   struct IteratorContext *ic = cls;
3215   struct NeighbourMapEntry *n = value;
3216
3217   if (GNUNET_YES == test_connected (n))
3218   {
3219     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3220     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3221
3222     if (NULL != n->primary_address.address)
3223     {
3224       bandwidth_in = n->primary_address.bandwidth_in;
3225       bandwidth_out = n->primary_address.bandwidth_out;
3226     }
3227     else
3228     {
3229       bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3230       bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3231     }
3232
3233     ic->cb (ic->cb_cls, &n->id,
3234             n->primary_address.address,
3235             bandwidth_in, bandwidth_out);
3236   }
3237   return GNUNET_OK;
3238 }
3239
3240
3241 /**
3242  * Iterate over all connected neighbours.
3243  *
3244  * @param cb function to call
3245  * @param cb_cls closure for cb
3246  */
3247 void
3248 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3249 {
3250   struct IteratorContext ic;
3251
3252   if (NULL == neighbours)  
3253     return; /* can happen during shutdown */
3254   ic.cb = cb;
3255   ic.cb_cls = cb_cls;
3256   GNUNET_CONTAINER_multihashmap_iterate (neighbours, &neighbours_iterate, &ic);
3257 }
3258
3259
3260 /**
3261  * If we have an active connection to the given target, it must be shutdown.
3262  *
3263  * @param target peer to disconnect from
3264  */
3265 void
3266 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3267 {
3268   struct NeighbourMapEntry *n;
3269
3270   if (NULL == (n = lookup_neighbour (target)))
3271     return;  /* not active */
3272   if (GNUNET_YES == test_connected (n))
3273     GNUNET_STATISTICS_update (GST_stats,
3274                               gettext_noop
3275                               ("# disconnected from peer upon explicit request"), 1,
3276                               GNUNET_NO);
3277   disconnect_neighbour (n);
3278 }
3279
3280
3281 /**
3282  * Obtain current latency information for the given neighbour.
3283  *
3284  * @param peer to get the latency for
3285  * @return observed latency of the address, FOREVER if the 
3286  *         the connection is not up
3287  */
3288 struct GNUNET_TIME_Relative
3289 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3290 {
3291   struct NeighbourMapEntry *n;
3292
3293   n = lookup_neighbour (peer);
3294   if (NULL == n) 
3295     return GNUNET_TIME_UNIT_FOREVER_REL;
3296   switch (n->state)
3297   {
3298   case S_CONNECTED:
3299   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3300   case S_CONNECTED_SWITCHING_BLACKLIST:
3301   case S_RECONNECT_SENT:
3302   case S_RECONNECT_ATS:
3303   case S_RECONNECT_BLACKLIST:
3304     return n->latency;
3305   case S_NOT_CONNECTED:
3306   case S_INIT_BLACKLIST:
3307   case S_INIT_ATS:
3308   case S_CONNECT_RECV_BLACKLIST_INBOUND:
3309   case S_CONNECT_RECV_ATS:
3310   case S_CONNECT_RECV_BLACKLIST:
3311   case S_CONNECT_RECV_ACK:
3312   case S_CONNECT_SENT:
3313   case S_DISCONNECT:
3314   case S_DISCONNECT_FINISHED:
3315     return GNUNET_TIME_UNIT_FOREVER_REL;
3316   default:
3317     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
3318     GNUNET_break (0);
3319     break;
3320   }
3321   return GNUNET_TIME_UNIT_FOREVER_REL;   
3322 }
3323
3324
3325 /**
3326  * Obtain current address information for the given neighbour.
3327  *
3328  * @param peer
3329  * @return address currently used
3330  */
3331 struct GNUNET_HELLO_Address *
3332 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3333 {
3334   struct NeighbourMapEntry *n;
3335
3336   n = lookup_neighbour (peer);
3337   if (NULL == n)
3338     return NULL;
3339   return n->primary_address.address;
3340 }
3341
3342
3343 /**
3344  * Initialize the neighbours subsystem.
3345  *
3346  * @param cls closure for callbacks
3347  * @param connect_cb function to call if we connect to a peer
3348  * @param disconnect_cb function to call if we disconnect from a peer
3349  * @param peer_address_cb function to call if we change an active address
3350  *                   of a neighbour
3351  * @param max_fds maximum number of fds to use
3352  */
3353 void
3354 GST_neighbours_start (void *cls,
3355                                                                         NotifyConnect connect_cb,
3356                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3357                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3358                       unsigned int max_fds)
3359 {
3360   callback_cls = cls;
3361   connect_notify_cb = connect_cb;
3362   disconnect_notify_cb = disconnect_cb;
3363   address_change_cb = peer_address_cb;
3364   neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3365 }
3366
3367
3368 /**
3369  * Disconnect from the given neighbour.
3370  *
3371  * @param cls unused
3372  * @param key hash of neighbour's public key (not used)
3373  * @param value the 'struct NeighbourMapEntry' of the neighbour
3374  * @return GNUNET_OK (continue to iterate)
3375  */
3376 static int
3377 disconnect_all_neighbours (void *cls, const struct GNUNET_HashCode * key, void *value)
3378 {
3379   struct NeighbourMapEntry *n = value;
3380
3381   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
3382               "Disconnecting peer `%4s', %s\n",
3383               GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3384   n->state = S_DISCONNECT_FINISHED;
3385   free_neighbour (n, GNUNET_NO);
3386   return GNUNET_OK;
3387 }
3388
3389
3390 /**
3391  * Cleanup the neighbours subsystem.
3392  */
3393 void
3394 GST_neighbours_stop ()
3395 {
3396   if (NULL == neighbours)
3397     return;
3398   GNUNET_CONTAINER_multihashmap_iterate (neighbours, 
3399                                          &disconnect_all_neighbours,
3400                                          NULL);
3401   GNUNET_CONTAINER_multihashmap_destroy (neighbours);
3402   neighbours = NULL;
3403   callback_cls = NULL;
3404   connect_notify_cb = NULL;
3405   disconnect_notify_cb = NULL;
3406   address_change_cb = NULL;
3407 }
3408
3409
3410 /* end of file gnunet-service-transport_neighbours.c */