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