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