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