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