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