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