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