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