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