-indentation, doxygen, logging
[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  * Transmit a message using the current session of the given
964  * neighbour.
965  *
966  * @param n entry for the recipient
967  * @param msgbuf buffer to transmit
968  * @param msgbuf_size number of bytes in buffer
969  * @param priority transmission priority
970  * @param timeout transmission timeout
971  * @param cont continuation to call when finished (can be NULL)
972  * @param cont_cls closure for cont
973  */
974 static void
975 send_with_session (struct NeighbourMapEntry *n,
976                    const char *msgbuf, size_t msgbuf_size,
977                    uint32_t priority,
978                    struct GNUNET_TIME_Relative timeout,
979                    GNUNET_TRANSPORT_TransmitContinuation cont,
980                    void *cont_cls)
981 {
982   struct GNUNET_TRANSPORT_PluginFunctions *papi;
983
984   GNUNET_assert (n->primary_address.session != NULL);
985   if ( ((NULL == (papi = GST_plugins_find (n->primary_address.address->transport_name)) ||
986          (-1 == papi->send (papi->cls,
987                             n->primary_address.session,
988                             msgbuf, msgbuf_size,
989                             priority,
990                             timeout,
991                             cont, cont_cls)))) &&
992        (NULL != cont))
993     cont (cont_cls, &n->id, GNUNET_SYSERR, msgbuf_size, 0);
994   GST_neighbours_notify_data_sent (&n->id,
995       n->primary_address.address, n->primary_address.session, msgbuf_size);
996   GNUNET_break (NULL != papi);
997 }
998
999
1000 /**
1001  * Master task run for every neighbour.  Performs all of the time-related
1002  * activities (keep alive, send next message, disconnect if idle, finish
1003  * clean up after disconnect).
1004  *
1005  * @param cls the 'struct NeighbourMapEntry' for which we are running
1006  * @param tc scheduler context (unused)
1007  */
1008 static void
1009 master_task (void *cls,
1010              const struct GNUNET_SCHEDULER_TaskContext *tc);
1011
1012
1013 /**
1014  * Function called when the 'DISCONNECT' message has been sent by the
1015  * plugin.  Frees the neighbour --- if the entry still exists.
1016  *
1017  * @param cls NULL
1018  * @param target identity of the neighbour that was disconnected
1019  * @param result #GNUNET_OK if the disconnect got out successfully
1020  * @param payload bytes payload
1021  * @param physical bytes physical
1022  */
1023 static void
1024 send_disconnect_cont (void *cls, const struct GNUNET_PeerIdentity *target,
1025                       int result, size_t payload, size_t physical)
1026 {
1027   struct NeighbourMapEntry *n;
1028
1029   n = lookup_neighbour (target);
1030   if (NULL == n)
1031     return; /* already gone */
1032   if (S_DISCONNECT != n->state)
1033     return; /* have created a fresh entry since */
1034   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1035     GNUNET_SCHEDULER_cancel (n->task);
1036   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1037 }
1038
1039
1040 /**
1041  * Transmit a DISCONNECT message to the other peer.
1042  *
1043  * @param n neighbour to send DISCONNECT message.
1044  */
1045 static void
1046 send_disconnect (struct NeighbourMapEntry *n)
1047 {
1048   struct SessionDisconnectMessage disconnect_msg;
1049
1050   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1051               "Sending DISCONNECT message to peer `%4s'\n",
1052               GNUNET_i2s (&n->id));
1053   disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1054   disconnect_msg.header.type =
1055       htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1056   disconnect_msg.reserved = htonl (0);
1057   disconnect_msg.purpose.size =
1058       htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
1059              sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
1060              sizeof (struct GNUNET_TIME_AbsoluteNBO));
1061   disconnect_msg.purpose.purpose =
1062       htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1063   disconnect_msg.timestamp =
1064       GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1065   disconnect_msg.public_key = GST_my_identity.public_key;
1066   GNUNET_assert (GNUNET_OK ==
1067                  GNUNET_CRYPTO_eddsa_sign (GST_my_private_key,
1068                                          &disconnect_msg.purpose,
1069                                          &disconnect_msg.signature));
1070
1071   send_with_session (n,
1072                      (const char *) &disconnect_msg, sizeof (disconnect_msg),
1073                      UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
1074                      &send_disconnect_cont, NULL);
1075   GNUNET_STATISTICS_update (GST_stats,
1076                             gettext_noop
1077                             ("# DISCONNECT messages sent"), 1,
1078                             GNUNET_NO);
1079 }
1080
1081
1082 /**
1083  * Disconnect from the given neighbour, clean up the record.
1084  *
1085  * @param n neighbour to disconnect from
1086  */
1087 static void
1088 disconnect_neighbour (struct NeighbourMapEntry *n)
1089 {
1090   /* depending on state, notify neighbour and/or upper layers of this peer
1091      about disconnect */
1092   switch (n->state)
1093   {
1094   case S_NOT_CONNECTED:
1095   case S_INIT_ATS:
1096   case S_INIT_BLACKLIST:
1097     /* other peer is completely unaware of us, no need to send DISCONNECT */
1098     n->state = S_DISCONNECT_FINISHED;
1099     free_neighbour (n, GNUNET_NO);
1100     return;
1101   case S_CONNECT_SENT:
1102     send_disconnect (n);
1103     n->state = S_DISCONNECT;
1104     break;
1105   case S_CONNECT_RECV_BLACKLIST_INBOUND:
1106   case S_CONNECT_RECV_ATS:
1107   case S_CONNECT_RECV_BLACKLIST:
1108     /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
1109     n->state = S_DISCONNECT_FINISHED;
1110     free_neighbour (n, GNUNET_NO);
1111     return;
1112   case S_CONNECT_RECV_ACK:
1113     /* we DID ACK the other peer's request, must send DISCONNECT */
1114     send_disconnect (n);
1115     n->state = S_DISCONNECT;
1116     break;
1117   case S_CONNECTED:
1118   case S_RECONNECT_BLACKLIST:
1119   case S_RECONNECT_SENT:
1120   case S_CONNECTED_SWITCHING_BLACKLIST:
1121   case S_CONNECTED_SWITCHING_CONNECT_SENT:
1122     /* we are currently connected, need to send disconnect and do
1123        internal notifications and update statistics */
1124     send_disconnect (n);
1125     GNUNET_STATISTICS_set (GST_stats,
1126                            gettext_noop ("# peers connected"),
1127                            --neighbours_connected,
1128                            GNUNET_NO);
1129     disconnect_notify_cb (callback_cls, &n->id);
1130     n->state = S_DISCONNECT;
1131     break;
1132   case S_RECONNECT_ATS:
1133     /* ATS address request timeout, disconnect without sending disconnect message */
1134     GNUNET_STATISTICS_set (GST_stats,
1135                            gettext_noop ("# peers connected"),
1136                            --neighbours_connected,
1137                            GNUNET_NO);
1138     disconnect_notify_cb (callback_cls, &n->id);
1139     n->state = S_DISCONNECT;
1140     break;
1141   case S_DISCONNECT:
1142     /* already disconnected, ignore */
1143     break;
1144   case S_DISCONNECT_FINISHED:
1145     /* already cleaned up, how did we get here!? */
1146     GNUNET_assert (0);
1147     break;
1148   default:
1149     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1150                 "Unhandled state `%s'\n",
1151                 print_state (n->state));
1152     GNUNET_break (0);
1153     break;
1154   }
1155   /* schedule timeout to clean up */
1156   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1157     GNUNET_SCHEDULER_cancel (n->task);
1158   n->task = GNUNET_SCHEDULER_add_delayed (DISCONNECT_SENT_TIMEOUT,
1159                                           &master_task, n);
1160 }
1161
1162
1163 /**
1164  * We're done with our transmission attempt, continue processing.
1165  *
1166  * @param cls the 'struct MessageQueue' of the message
1167  * @param receiver intended receiver
1168  * @param success whether it worked or not
1169  * @param size_payload bytes payload sent
1170  * @param physical bytes sent on wire
1171  */
1172 static void
1173 transmit_send_continuation (void *cls,
1174                             const struct GNUNET_PeerIdentity *receiver,
1175                             int success, size_t size_payload, size_t physical)
1176 {
1177   struct MessageQueue *mq = cls;
1178   struct NeighbourMapEntry *n;
1179
1180   if (NULL == (n = lookup_neighbour (receiver)))
1181   {
1182     GNUNET_free (mq);
1183     return; /* disconnect or other error while transmitting, can happen */
1184   }
1185   if (n->is_active == mq)
1186   {
1187     /* this is still "our" neighbour, remove us from its queue
1188        and allow it to send the next message now */
1189     n->is_active = NULL;
1190     if (GNUNET_SCHEDULER_NO_TASK != n->task)
1191       GNUNET_SCHEDULER_cancel (n->task);
1192     n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1193   }
1194   if (bytes_in_send_queue < mq->message_buf_size)
1195   {
1196       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1197                   "Bytes_in_send_queue `%u', Message_size %u, result: %s, payload %u, on wire %u\n",
1198                   bytes_in_send_queue, mq->message_buf_size,
1199                   (GNUNET_OK == success) ? "OK" : "FAIL",
1200                   size_payload, physical);
1201       GNUNET_break (0);
1202   }
1203
1204
1205   GNUNET_break (size_payload == mq->message_buf_size);
1206   bytes_in_send_queue -= mq->message_buf_size;
1207   GNUNET_STATISTICS_set (GST_stats,
1208                         gettext_noop
1209                          ("# bytes in message queue for other peers"),
1210                          bytes_in_send_queue, GNUNET_NO);
1211   if (GNUNET_OK == success)
1212     GNUNET_STATISTICS_update (GST_stats,
1213                               gettext_noop
1214                               ("# messages transmitted to other peers"),
1215                               1, GNUNET_NO);
1216   else
1217     GNUNET_STATISTICS_update (GST_stats,
1218                               gettext_noop
1219                               ("# transmission failures for messages to other peers"),
1220                               1, GNUNET_NO);
1221   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1222               "Sending message to `%s' of type %u was a %s\n",
1223               GNUNET_i2s (receiver),
1224               ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
1225               (success == GNUNET_OK) ? "success" : "FAILURE");
1226   if (NULL != mq->cont)
1227     mq->cont (mq->cont_cls, success, size_payload, physical);
1228   GNUNET_free (mq);
1229 }
1230
1231
1232 /**
1233  * Check the message list for the given neighbour and if we can
1234  * send a message, do so.  This function should only be called
1235  * if the connection is at least generally ready for transmission.
1236  * While we will only send one message at a time, no bandwidth
1237  * quota management is performed here.  If a message was given to
1238  * the plugin, the continuation will automatically re-schedule
1239  * the 'master' task once the next message might be transmitted.
1240  *
1241  * @param n target peer for which to transmit
1242  */
1243 static void
1244 try_transmission_to_peer (struct NeighbourMapEntry *n)
1245 {
1246   struct MessageQueue *mq;
1247   struct GNUNET_TIME_Relative timeout;
1248
1249   if (NULL == n->primary_address.address)
1250   {
1251     /* no address, why are we here? */
1252     GNUNET_break (0);
1253     return;
1254   }
1255   if ((0 == n->primary_address.address->address_length) &&
1256       (NULL == n->primary_address.session))
1257   {
1258     /* no address, why are we here? */
1259     GNUNET_break (0);
1260     return;
1261   }
1262   if (NULL != n->is_active)
1263   {
1264     /* transmission already pending */
1265     return;
1266   }
1267
1268   /* timeout messages from the queue that are past their due date */
1269   while (NULL != (mq = n->messages_head))
1270   {
1271     timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
1272     if (timeout.rel_value_us > 0)
1273       break;
1274     GNUNET_STATISTICS_update (GST_stats,
1275                               gettext_noop
1276                               ("# messages timed out while in transport queue"),
1277                               1, GNUNET_NO);
1278     GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1279     n->is_active = mq;
1280     transmit_send_continuation (mq, &n->id, GNUNET_SYSERR, mq->message_buf_size, 0);     /* timeout */
1281   }
1282   if (NULL == mq)
1283     return;                     /* no more messages */
1284   GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
1285   n->is_active = mq;
1286   send_with_session (n,
1287                      mq->message_buf, mq->message_buf_size,
1288                      0 /* priority */, timeout,
1289                      &transmit_send_continuation, mq);
1290 }
1291
1292
1293 /**
1294  * Send keepalive message to the neighbour.  Must only be called
1295  * if we are on 'connected' state or while trying to switch addresses.
1296  * Will internally determine if a keepalive is truly needed (so can
1297  * always be called).
1298  *
1299  * @param n neighbour that went idle and needs a keepalive
1300  */
1301 static void
1302 send_keepalive (struct NeighbourMapEntry *n)
1303 {
1304   struct GNUNET_MessageHeader m;
1305
1306   GNUNET_assert ((S_CONNECTED == n->state) ||
1307                  (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
1308                  (S_CONNECTED_SWITCHING_CONNECT_SENT));
1309   if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
1310     return; /* no keepalive needed at this time */
1311   m.size = htons (sizeof (struct GNUNET_MessageHeader));
1312   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
1313   send_with_session (n,
1314                      (const void *) &m, sizeof (m),
1315                      UINT32_MAX /* priority */,
1316                      KEEPALIVE_FREQUENCY,
1317                      NULL, NULL);
1318   GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# keepalives sent"), 1,
1319                             GNUNET_NO);
1320   n->expect_latency_response = GNUNET_YES;
1321   n->last_keep_alive_time = GNUNET_TIME_absolute_get ();
1322   n->keep_alive_time = GNUNET_TIME_relative_to_absolute (KEEPALIVE_FREQUENCY);
1323 }
1324
1325
1326 /**
1327  * Keep the connection to the given neighbour alive longer,
1328  * we received a KEEPALIVE (or equivalent); send a response.
1329  *
1330  * @param neighbour neighbour to keep alive (by sending keep alive response)
1331  */
1332 void
1333 GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour)
1334 {
1335   struct NeighbourMapEntry *n;
1336   struct GNUNET_MessageHeader m;
1337
1338   if (NULL == (n = lookup_neighbour (neighbour)))
1339   {
1340     GNUNET_STATISTICS_update (GST_stats,
1341                               gettext_noop
1342                               ("# KEEPALIVE messages discarded (peer unknown)"),
1343                               1, GNUNET_NO);
1344     return;
1345   }
1346   if (NULL == n->primary_address.session)
1347   {
1348     GNUNET_STATISTICS_update (GST_stats,
1349                               gettext_noop
1350                               ("# KEEPALIVE messages discarded (no session)"),
1351                               1, GNUNET_NO);
1352     return;
1353   }
1354   /* send reply to allow neighbour to measure latency */
1355   m.size = htons (sizeof (struct GNUNET_MessageHeader));
1356   m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE);
1357   send_with_session(n,
1358                     (const void *) &m, sizeof (m),
1359                     UINT32_MAX /* priority */,
1360                     KEEPALIVE_FREQUENCY,
1361                     NULL, NULL);
1362 }
1363
1364
1365 /**
1366  * We received a KEEP_ALIVE_RESPONSE message and use this to calculate
1367  * latency to this peer.  Pass the updated information (existing ats
1368  * plus calculated latency) to ATS.
1369  *
1370  * @param neighbour neighbour to keep alive
1371  */
1372 void
1373 GST_neighbours_keepalive_response (const struct GNUNET_PeerIdentity *neighbour)
1374 {
1375   struct NeighbourMapEntry *n;
1376   uint32_t latency;
1377   struct GNUNET_ATS_Information ats;
1378
1379   if (NULL == (n = lookup_neighbour (neighbour)))
1380   {
1381     GNUNET_STATISTICS_update (GST_stats,
1382                               gettext_noop
1383                               ("# KEEPALIVE_RESPONSE messages discarded (not connected)"),
1384                               1, GNUNET_NO);
1385     return;
1386   }
1387   if ( (S_CONNECTED != n->state) ||
1388        (GNUNET_YES != n->expect_latency_response) )
1389   {
1390     GNUNET_STATISTICS_update (GST_stats,
1391                               gettext_noop
1392                               ("# KEEPALIVE_RESPONSE messages discarded (not expected)"),
1393                               1, GNUNET_NO);
1394     return;
1395   }
1396   n->expect_latency_response = GNUNET_NO;
1397   n->latency = GNUNET_TIME_absolute_get_duration (n->last_keep_alive_time);
1398   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
1399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1400               "Latency for peer `%s' is %s\n",
1401               GNUNET_i2s (&n->id),
1402               GNUNET_STRINGS_relative_time_to_string (n->latency,
1403                                                       GNUNET_YES));
1404   /* append latency */
1405   ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1406   if (n->latency.rel_value_us > UINT32_MAX)
1407     latency = UINT32_MAX;
1408   else
1409     latency = n->latency.rel_value_us;
1410   ats.value = htonl (latency);
1411   GST_ats_update_metrics (&n->id,
1412                                                                                           n->primary_address.address,
1413                                                                                         n->primary_address.session,
1414                                                                                         &ats, 1);
1415 }
1416
1417
1418 /**
1419  * We have received a message from the given sender.  How long should
1420  * we delay before receiving more?  (Also used to keep the peer marked
1421  * as live).
1422  *
1423  * @param sender sender of the message
1424  * @param size size of the message
1425  * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1426  *                   GNUNET_NO if the neighbour is not connected or violates the quota,
1427  *                   GNUNET_SYSERR if the connection is not fully up yet
1428  * @return how long to wait before reading more from this sender
1429  */
1430 struct GNUNET_TIME_Relative
1431 GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1432                                         *sender, ssize_t size, int *do_forward)
1433 {
1434   struct NeighbourMapEntry *n;
1435   struct GNUNET_TIME_Relative ret;
1436
1437   if (NULL == neighbours)
1438   {
1439     *do_forward = GNUNET_NO;
1440     return GNUNET_TIME_UNIT_FOREVER_REL; /* This can happen during shutdown */
1441   }
1442   if (NULL == (n = lookup_neighbour (sender)))
1443   {
1444     GST_neighbours_try_connect (sender);
1445     if (NULL == (n = lookup_neighbour (sender)))
1446     {
1447       GNUNET_STATISTICS_update (GST_stats,
1448                                 gettext_noop
1449                                 ("# messages discarded due to lack of neighbour record"),
1450                                 1, GNUNET_NO);
1451       *do_forward = GNUNET_NO;
1452       return GNUNET_TIME_UNIT_ZERO;
1453     }
1454   }
1455   if (! test_connected (n))
1456   {
1457     *do_forward = GNUNET_SYSERR;
1458     return GNUNET_TIME_UNIT_ZERO;
1459   }
1460   if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1461   {
1462     n->quota_violation_count++;
1463     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1464                 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1465                 n->in_tracker.available_bytes_per_s__,
1466                 n->quota_violation_count);
1467     /* Discount 32k per violation */
1468     GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1469   }
1470   else
1471   {
1472     if (n->quota_violation_count > 0)
1473     {
1474       /* try to add 32k back */
1475       GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1476       n->quota_violation_count--;
1477     }
1478   }
1479   if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1480   {
1481     GNUNET_STATISTICS_update (GST_stats,
1482                               gettext_noop
1483                               ("# bandwidth quota violations by other peers"),
1484                               1, GNUNET_NO);
1485     *do_forward = GNUNET_NO;
1486     return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1487   }
1488   *do_forward = GNUNET_YES;
1489   ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1490   if (ret.rel_value_us > 0)
1491   {
1492     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1493                 "Throttling read (%llu bytes excess at %u b/s), waiting %s before reading more.\n",
1494                 (unsigned long long) n->in_tracker.
1495                 consumption_since_last_update__,
1496                 (unsigned int) n->in_tracker.available_bytes_per_s__,
1497                 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
1498     GNUNET_STATISTICS_update (GST_stats,
1499                               gettext_noop ("# ms throttling suggested"),
1500                               (int64_t) ret.rel_value_us / 1000LL,
1501                               GNUNET_NO);
1502   }
1503   return ret;
1504 }
1505
1506
1507 /**
1508  * Transmit a message to the given target using the active connection.
1509  *
1510  * @param target destination
1511  * @param msg message to send
1512  * @param msg_size number of bytes in msg
1513  * @param timeout when to fail with timeout
1514  * @param cont function to call when done
1515  * @param cont_cls closure for 'cont'
1516  */
1517 void
1518 GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1519                      size_t msg_size, struct GNUNET_TIME_Relative timeout,
1520                      GST_NeighbourSendContinuation cont, void *cont_cls)
1521 {
1522   struct NeighbourMapEntry *n;
1523   struct MessageQueue *mq;
1524
1525   /* All ove these cases should never happen; they are all API violations.
1526      But we check anyway, just to be sure. */
1527   if (NULL == (n = lookup_neighbour (target)))
1528   {
1529     GNUNET_break (0);
1530     if (NULL != cont)
1531       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1532     return;
1533   }
1534   if (GNUNET_YES != test_connected (n))
1535   {
1536     GNUNET_break (0);
1537     if (NULL != cont)
1538       cont (cont_cls, GNUNET_SYSERR, msg_size, 0);
1539     return;
1540   }
1541   bytes_in_send_queue += msg_size;
1542   GNUNET_STATISTICS_set (GST_stats,
1543                          gettext_noop
1544                          ("# bytes in message queue for other peers"),
1545                          bytes_in_send_queue, GNUNET_NO);
1546   mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1547   mq->cont = cont;
1548   mq->cont_cls = cont_cls;
1549   memcpy (&mq[1], msg, msg_size);
1550   mq->message_buf = (const char *) &mq[1];
1551   mq->message_buf_size = msg_size;
1552   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1553   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1554   if ( (NULL != n->is_active) ||
1555        ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
1556     return;
1557   if (GNUNET_SCHEDULER_NO_TASK != n->task)
1558     GNUNET_SCHEDULER_cancel (n->task);
1559   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1560 }
1561
1562
1563 /**
1564  * Send a SESSION_CONNECT message via the given address.
1565  *
1566  * @param na address to use
1567  */
1568 static void
1569 send_session_connect (struct NeighbourAddress *na)
1570 {
1571   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1572   struct SessionConnectMessage connect_msg;
1573
1574   if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
1575   {
1576     GNUNET_break (0);
1577     return;
1578   }
1579   if (NULL == na->session)
1580     na->session = papi->get_session (papi->cls, na->address);
1581   if (NULL == na->session)
1582   {
1583     GNUNET_break (0);
1584     return;
1585   }
1586   GNUNET_STATISTICS_update (GST_stats,
1587                             gettext_noop
1588                             ("# SESSION_CONNECT messages sent"),
1589                             1, GNUNET_NO);
1590   na->connect_timestamp = GNUNET_TIME_absolute_get ();
1591   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1592   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1593   connect_msg.reserved = htonl (0);
1594   connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1595   (void) papi->send (papi->cls,
1596                      na->session,
1597                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1598                      UINT_MAX,
1599                      GNUNET_TIME_UNIT_FOREVER_REL,
1600                      NULL, NULL);
1601   GST_neighbours_notify_data_sent (&na->address->peer,
1602       na->address, na->session, sizeof (struct SessionConnectMessage));
1603
1604 }
1605
1606
1607 /**
1608  * Send a SESSION_CONNECT_ACK message via the given address.
1609  *
1610  * @param address address to use
1611  * @param session session to use
1612  * @param timestamp timestamp to use for the ACK message
1613  */
1614 static void
1615 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1616                                   struct Session *session,
1617                                   struct GNUNET_TIME_Absolute timestamp)
1618 {
1619   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1620   struct SessionConnectMessage connect_msg;
1621
1622   if (NULL == (papi = GST_plugins_find (address->transport_name)))
1623   {
1624     GNUNET_break (0);
1625     return;
1626   }
1627   if (NULL == session)
1628     session = papi->get_session (papi->cls, address);
1629   if (NULL == session)
1630   {
1631     GNUNET_break (0);
1632     return;
1633   }
1634   GNUNET_STATISTICS_update (GST_stats,
1635                             gettext_noop
1636                             ("# CONNECT_ACK messages sent"),
1637                             1, GNUNET_NO);
1638   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1639   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1640   connect_msg.reserved = htonl (0);
1641   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1642   (void) papi->send (papi->cls,
1643                      session,
1644                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1645                      UINT_MAX,
1646                      GNUNET_TIME_UNIT_FOREVER_REL,
1647                      NULL, NULL);
1648
1649 }
1650
1651
1652 /**
1653  * Create a fresh entry in the neighbour map for the given peer
1654  *
1655  * @param peer peer to create an entry for
1656  * @return new neighbour map entry
1657  */
1658 static struct NeighbourMapEntry *
1659 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1660 {
1661   struct NeighbourMapEntry *n;
1662
1663   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1664               "Creating new neighbour entry for `%s'\n",
1665               GNUNET_i2s (peer));
1666   n = GNUNET_new (struct NeighbourMapEntry);
1667   n->id = *peer;
1668   n->state = S_NOT_CONNECTED;
1669   n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1670   n->last_util_transmission = GNUNET_TIME_absolute_get();
1671   n->util_payload_bytes_recv = 0;
1672   n->util_payload_bytes_sent = 0;
1673   n->util_total_bytes_recv = 0;
1674   n->util_total_bytes_sent = 0;
1675   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1676                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1677                                  MAX_BANDWIDTH_CARRY_S);
1678   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1679   GNUNET_assert (GNUNET_OK ==
1680                  GNUNET_CONTAINER_multipeermap_put (neighbours,
1681                                                     &n->id, n,
1682                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1683   return n;
1684 }
1685
1686
1687 /**
1688  * Check if the two given addresses are the same.
1689  * Actually only checks if the sessions are non-NULL
1690  * (which they should be) and then if they are identical;
1691  * the actual addresses don't matter if the session
1692  * pointers match anyway, and we must have session pointers
1693  * at this time.
1694  *
1695  * @param a1 first address to compare
1696  * @param a2 other address to compare
1697  * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
1698  */
1699 static int
1700 address_matches (const struct NeighbourAddress *a1,
1701                  const struct NeighbourAddress *a2)
1702 {
1703   if ( (NULL == a1->session) ||
1704        (NULL == a2->session) )
1705   {
1706     GNUNET_break (0);
1707     return 0;
1708   }
1709   return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1710 }
1711
1712
1713 /**
1714  * Try to create a connection to the given target (eventually).
1715  *
1716  * @param target peer to try to connect to
1717  */
1718 void
1719 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1720 {
1721   struct NeighbourMapEntry *n;
1722
1723   if (NULL == neighbours)
1724   {
1725     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1726                 "Asked to connect to peer `%s' during shutdown\n",
1727                 GNUNET_i2s (target));
1728     return; /* during shutdown, do nothing */
1729   }
1730   if (0 == memcmp (target,
1731                    &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1732   {
1733     /* refuse to connect to myself */
1734     /* FIXME: can this happen? Is this not an API violation? */
1735     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1736                 "Refusing to try to connect to myself.\n");
1737     return;
1738   }
1739   n = lookup_neighbour (target);
1740   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1741               "Asked to connect to peer `%s' (state: %d)\n",
1742               GNUNET_i2s (target),
1743               (NULL != n) ? n->state : -1);
1744   if (NULL != n)
1745   {
1746     switch (n->state)
1747     {
1748     case S_NOT_CONNECTED:
1749       /* this should not be possible */
1750       GNUNET_break (0);
1751       free_neighbour (n, GNUNET_NO);
1752       break;
1753     case S_INIT_ATS:
1754     case S_INIT_BLACKLIST:
1755     case S_CONNECT_SENT:
1756     case S_CONNECT_RECV_BLACKLIST_INBOUND:
1757     case S_CONNECT_RECV_ATS:
1758     case S_CONNECT_RECV_BLACKLIST:
1759     case S_CONNECT_RECV_ACK:
1760       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1761                   "Ignoring request to try to connect to `%s', already trying!\n",
1762                   GNUNET_i2s (target));
1763       return; /* already trying */
1764     case S_CONNECTED:
1765     case S_RECONNECT_ATS:
1766     case S_RECONNECT_BLACKLIST:
1767     case S_RECONNECT_SENT:
1768     case S_CONNECTED_SWITCHING_BLACKLIST:
1769     case S_CONNECTED_SWITCHING_CONNECT_SENT:
1770       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1771                   "Ignoring request to try to connect, already connected to `%s'!\n",
1772                   GNUNET_i2s (target));
1773       return; /* already connected */
1774     case S_DISCONNECT:
1775       /* get rid of remains, ready to re-try immediately */
1776       free_neighbour (n, GNUNET_NO);
1777       break;
1778     case S_DISCONNECT_FINISHED:
1779       /* should not be possible */
1780       GNUNET_assert (0);
1781     default:
1782       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1783                   "Unhandled state `%s'\n",
1784                   print_state (n->state));
1785       GNUNET_break (0);
1786       free_neighbour (n, GNUNET_NO);
1787       break;
1788     }
1789   }
1790   n = setup_neighbour (target);
1791   n->state = S_INIT_ATS;
1792   n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1793
1794   GNUNET_ATS_reset_backoff (GST_ats, target);
1795   n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1796 }
1797
1798
1799 /**
1800  * Function called with the result of a blacklist check.
1801  *
1802  * @param cls closure with the 'struct BlackListCheckContext'
1803  * @param peer peer this check affects
1804  * @param result #GNUNET_OK if the address is allowed
1805  */
1806 static void
1807 handle_test_blacklist_cont (void *cls,
1808                             const struct GNUNET_PeerIdentity *peer,
1809                             int result)
1810 {
1811   struct BlackListCheckContext *bcc = cls;
1812   struct NeighbourMapEntry *n;
1813
1814   bcc->bc = NULL;
1815   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1816               "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1817               GNUNET_i2s (peer),
1818               (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1819   if (NULL == (n = lookup_neighbour (peer)))
1820     goto cleanup; /* nobody left to care about new address */
1821   switch (n->state)
1822   {
1823   case S_NOT_CONNECTED:
1824     /* this should not be possible */
1825     GNUNET_break (0);
1826     free_neighbour (n, GNUNET_NO);
1827     break;
1828   case S_INIT_ATS:
1829     /* still waiting on ATS suggestion */
1830     break;
1831   case S_INIT_BLACKLIST:
1832     /* check if the address the blacklist was fine with matches
1833        ATS suggestion, if so, we can move on! */
1834     if ( (GNUNET_OK == result) &&
1835          (1 == n->send_connect_ack) )
1836     {
1837       n->send_connect_ack = 2;
1838       send_session_connect_ack_message (bcc->na.address,
1839                                         bcc->na.session,
1840                                         n->connect_ack_timestamp);
1841     }
1842     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1843       break; /* result for an address we currently don't care about */
1844     if (GNUNET_OK == result)
1845     {
1846       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1847       n->state = S_CONNECT_SENT;
1848       send_session_connect (&n->primary_address);
1849     }
1850     else
1851     {
1852       // FIXME: should also possibly destroy session with plugin!?
1853       GNUNET_ATS_address_destroyed (GST_ats,
1854                                     bcc->na.address,
1855                                     NULL);
1856       free_address (&n->primary_address);
1857       n->state = S_INIT_ATS;
1858       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1859       // FIXME: do we need to ask ATS again for suggestions?
1860       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1861     }
1862     break;
1863   case S_CONNECT_SENT:
1864     /* waiting on CONNECT_ACK, send ACK if one is pending */
1865     if ( (GNUNET_OK == result) &&
1866          (1 == n->send_connect_ack) )
1867     {
1868       n->send_connect_ack = 2;
1869       send_session_connect_ack_message (n->primary_address.address,
1870                                         n->primary_address.session,
1871                                         n->connect_ack_timestamp);
1872     }
1873     break;
1874   case S_CONNECT_RECV_BLACKLIST_INBOUND:
1875     if (GNUNET_OK == result)
1876         GST_ats_add_address (bcc->na.address, bcc->na.session);
1877
1878     n->state = S_CONNECT_RECV_ATS;
1879     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1880     GNUNET_ATS_reset_backoff (GST_ats, peer);
1881     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
1882     break;
1883   case S_CONNECT_RECV_ATS:
1884     /* still waiting on ATS suggestion, don't care about blacklist */
1885     break;
1886   case S_CONNECT_RECV_BLACKLIST:
1887     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1888       break; /* result for an address we currently don't care about */
1889     if (GNUNET_OK == result)
1890     {
1891       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1892       n->state = S_CONNECT_RECV_ACK;
1893       send_session_connect_ack_message (bcc->na.address,
1894                                         bcc->na.session,
1895                                         n->connect_ack_timestamp);
1896       if (1 == n->send_connect_ack)
1897         n->send_connect_ack = 2;
1898     }
1899     else
1900     {
1901       // FIXME: should also possibly destroy session with plugin!?
1902       GNUNET_ATS_address_destroyed (GST_ats,
1903                                     bcc->na.address,
1904                                     NULL);
1905       free_address (&n->primary_address);
1906       n->state = S_INIT_ATS;
1907       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1908       // FIXME: do we need to ask ATS again for suggestions?
1909       GNUNET_ATS_reset_backoff (GST_ats, peer);
1910       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1911     }
1912     break;
1913   case S_CONNECT_RECV_ACK:
1914     /* waiting on SESSION_ACK, send ACK if one is pending */
1915     if ( (GNUNET_OK == result) &&
1916          (1 == n->send_connect_ack) )
1917     {
1918       n->send_connect_ack = 2;
1919       send_session_connect_ack_message (n->primary_address.address,
1920                                         n->primary_address.session,
1921                                         n->connect_ack_timestamp);
1922     }
1923     break;
1924   case S_CONNECTED:
1925     /* already connected, don't care about blacklist */
1926     break;
1927   case S_RECONNECT_ATS:
1928     /* still waiting on ATS suggestion, don't care about blacklist */
1929     break;
1930   case S_RECONNECT_BLACKLIST:
1931     if ( (GNUNET_OK == result) &&
1932          (1 == n->send_connect_ack) )
1933     {
1934       n->send_connect_ack = 2;
1935       send_session_connect_ack_message (bcc->na.address,
1936                                         bcc->na.session,
1937                                         n->connect_ack_timestamp);
1938     }
1939     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1940       break; /* result for an address we currently don't care about */
1941     if (GNUNET_OK == result)
1942     {
1943       send_session_connect (&n->primary_address);
1944       n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
1945       n->state = S_RECONNECT_SENT;
1946     }
1947     else
1948     {
1949       GNUNET_ATS_address_destroyed (GST_ats,
1950                                     bcc->na.address,
1951                                     NULL);
1952       n->state = S_RECONNECT_ATS;
1953       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1954       // FIXME: do we need to ask ATS again for suggestions?
1955       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1956     }
1957     break;
1958   case S_RECONNECT_SENT:
1959     /* waiting on CONNECT_ACK, don't care about blacklist */
1960     if ( (GNUNET_OK == result) &&
1961          (1 == n->send_connect_ack) )
1962     {
1963       n->send_connect_ack = 2;
1964       send_session_connect_ack_message (n->primary_address.address,
1965                                         n->primary_address.session,
1966                                         n->connect_ack_timestamp);
1967     }
1968     break;
1969   case S_CONNECTED_SWITCHING_BLACKLIST:
1970     if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
1971       break; /* result for an address we currently don't care about */
1972     if (GNUNET_OK == result)
1973     {
1974       send_session_connect (&n->alternative_address);
1975       n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
1976     }
1977     else
1978     {
1979       GNUNET_ATS_address_destroyed (GST_ats,
1980                                     bcc->na.address,
1981                                     NULL);
1982       free_address (&n->alternative_address);
1983       n->state = S_CONNECTED;
1984     }
1985     break;
1986   case S_CONNECTED_SWITCHING_CONNECT_SENT:
1987     /* waiting on CONNECT_ACK, don't care about blacklist */
1988     if ( (GNUNET_OK == result) &&
1989          (1 == n->send_connect_ack) )
1990     {
1991       n->send_connect_ack = 2;
1992       send_session_connect_ack_message (n->primary_address.address,
1993                                         n->primary_address.session,
1994                                         n->connect_ack_timestamp);
1995     }
1996     break;
1997   case S_DISCONNECT:
1998     /* Nothing to do here, ATS will already do what can be done */
1999     break;
2000   case S_DISCONNECT_FINISHED:
2001     /* should not be possible */
2002     GNUNET_assert (0);
2003     break;
2004   default:
2005     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2006                 "Unhandled state `%s'\n",
2007                 print_state (n->state));
2008     GNUNET_break (0);
2009     free_neighbour (n, GNUNET_NO);
2010     break;
2011   }
2012  cleanup:
2013   GNUNET_HELLO_address_free (bcc->na.address);
2014   GNUNET_CONTAINER_DLL_remove (bc_head,
2015                                bc_tail,
2016                                bcc);
2017   GNUNET_free (bcc);
2018 }
2019
2020
2021 /**
2022  * We want to know if connecting to a particular peer via
2023  * a particular address is allowed.  Check it!
2024  *
2025  * @param peer identity of the peer to switch the address for
2026  * @param ts time at which the check was initiated
2027  * @param address address of the other peer, NULL if other peer
2028  *                       connected to us
2029  * @param session session to use (or NULL)
2030  */
2031 static void
2032 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2033                  struct GNUNET_TIME_Absolute ts,
2034                  const struct GNUNET_HELLO_Address *address,
2035                  struct Session *session)
2036 {
2037   struct BlackListCheckContext *bcc;
2038   struct GST_BlacklistCheck *bc;
2039
2040   bcc = GNUNET_malloc (sizeof (struct BlackListCheckContext));
2041   bcc->na.address = GNUNET_HELLO_address_copy (address);
2042   bcc->na.session = session;
2043   bcc->na.connect_timestamp = ts;
2044   GNUNET_CONTAINER_DLL_insert (bc_head,
2045                                bc_tail,
2046                                bcc);
2047   if (NULL != (bc = GST_blacklist_test_allowed (peer,
2048                                                 address->transport_name,
2049                                                 &handle_test_blacklist_cont, bcc)))
2050     bcc->bc = bc;
2051   /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2052      we must only store 'bc' if 'bc' is non-NULL... */
2053 }
2054
2055
2056 /**
2057  * We received a 'SESSION_CONNECT' message from the other peer.
2058  * Consider switching to it.
2059  *
2060  * @param message possibly a 'struct SessionConnectMessage' (check format)
2061  * @param peer identity of the peer to switch the address for
2062  * @param address address of the other peer, NULL if other peer
2063  *                       connected to us
2064  * @param session session to use (or NULL)
2065  */
2066 void
2067 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2068                                const struct GNUNET_PeerIdentity *peer,
2069                                const struct GNUNET_HELLO_Address *address,
2070                                struct Session *session)
2071 {
2072   const struct SessionConnectMessage *scm;
2073   struct NeighbourMapEntry *n;
2074   struct GNUNET_TIME_Absolute ts;
2075
2076   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2077               "Received CONNECT message from peer `%s'\n",
2078               GNUNET_i2s (peer));
2079   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2080   {
2081     GNUNET_break_op (0);
2082     return;
2083   }
2084   GNUNET_STATISTICS_update (GST_stats,
2085                             gettext_noop
2086                             ("# CONNECT messages received"),
2087                             1, GNUNET_NO);
2088   if (NULL == neighbours)
2089     return; /* we're shutting down */
2090   scm = (const struct SessionConnectMessage *) message;
2091   GNUNET_break_op (0 == ntohl (scm->reserved));
2092   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2093   n = lookup_neighbour (peer);
2094   if (NULL == n)
2095     n = setup_neighbour (peer);
2096   n->send_connect_ack = 1;
2097   n->connect_ack_timestamp = ts;
2098
2099   switch (n->state)
2100   {
2101   case S_NOT_CONNECTED:
2102     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2103     /* Do a blacklist check for the new address */
2104     check_blacklist (peer, ts, address, session);
2105     break;
2106   case S_INIT_ATS:
2107     /* CONNECT message takes priority over us asking ATS for address */
2108     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2109     /* fallthrough */
2110   case S_INIT_BLACKLIST:
2111   case S_CONNECT_SENT:
2112   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2113   case S_CONNECT_RECV_ATS:
2114   case S_CONNECT_RECV_BLACKLIST:
2115   case S_CONNECT_RECV_ACK:
2116     /* It can never hurt to have an alternative address in the above cases,
2117        see if it is allowed */
2118     check_blacklist (peer, ts, address, session);
2119     break;
2120   case S_CONNECTED:
2121     /* we are already connected and can thus send the ACK immediately;
2122        still, it can never hurt to have an alternative address, so also
2123        tell ATS  about it */
2124     GNUNET_assert (NULL != n->primary_address.address);
2125     GNUNET_assert (NULL != n->primary_address.session);
2126     n->send_connect_ack = 0;
2127     send_session_connect_ack_message (n->primary_address.address,
2128                                       n->primary_address.session, ts);
2129     check_blacklist (peer, ts, address, session);
2130     break;
2131   case S_RECONNECT_ATS:
2132   case S_RECONNECT_BLACKLIST:
2133   case S_RECONNECT_SENT:
2134     /* It can never hurt to have an alternative address in the above cases,
2135        see if it is allowed */
2136     check_blacklist (peer, ts, address, session);
2137     break;
2138   case S_CONNECTED_SWITCHING_BLACKLIST:
2139   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2140     /* we are already connected and can thus send the ACK immediately;
2141        still, it can never hurt to have an alternative address, so also
2142        tell ATS  about it */
2143     GNUNET_assert (NULL != n->primary_address.address);
2144     GNUNET_assert (NULL != n->primary_address.session);
2145     n->send_connect_ack = 0;
2146     send_session_connect_ack_message (n->primary_address.address,
2147                                       n->primary_address.session, ts);
2148     check_blacklist (peer, ts, address, session);
2149     break;
2150   case S_DISCONNECT:
2151     /* get rid of remains without terminating sessions, ready to re-try */
2152     free_neighbour (n, GNUNET_YES);
2153     n = setup_neighbour (peer);
2154     n->state = S_CONNECT_RECV_ATS;
2155     GNUNET_ATS_reset_backoff (GST_ats, peer);
2156     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2157     break;
2158   case S_DISCONNECT_FINISHED:
2159     /* should not be possible */
2160     GNUNET_assert (0);
2161     break;
2162   default:
2163     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2164                 "Unhandled state `%s'\n",
2165                 print_state (n->state));
2166     GNUNET_break (0);
2167     free_neighbour (n, GNUNET_NO);
2168     break;
2169   }
2170 }
2171
2172
2173 /**
2174  * For an existing neighbour record, set the active connection to
2175  * use the given address.
2176  *
2177  * @param peer identity of the peer to switch the address for
2178  * @param address address of the other peer, NULL if other peer
2179  *                       connected to us
2180  * @param session session to use (or NULL)
2181  * @param ats performance data
2182  * @param ats_count number of entries in ats
2183  * @param bandwidth_in inbound quota to be used when connection is up,
2184  *      0 to disconnect from peer
2185  * @param bandwidth_out outbound quota to be used when connection is up,
2186  *      0 to disconnect from peer
2187  */
2188 void
2189 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2190                                   const struct GNUNET_HELLO_Address *address,
2191                                   struct Session *session,
2192                                   const struct GNUNET_ATS_Information *ats,
2193                                   uint32_t ats_count,
2194                                   struct GNUNET_BANDWIDTH_Value32NBO
2195                                   bandwidth_in,
2196                                   struct GNUNET_BANDWIDTH_Value32NBO
2197                                   bandwidth_out)
2198 {
2199   struct NeighbourMapEntry *n;
2200   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2201
2202   GNUNET_assert (address->transport_name != NULL);
2203   if (NULL == (n = lookup_neighbour (peer)))
2204     return;
2205
2206   /* Obtain an session for this address from plugin */
2207   if (NULL == (papi = GST_plugins_find (address->transport_name)))
2208   {
2209     /* we don't have the plugin for this address */
2210     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2211     return;
2212   }
2213   if ((NULL == session) && (0 == address->address_length))
2214   {
2215     GNUNET_break (0);
2216     if (strlen (address->transport_name) > 0)
2217       GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2218     return;
2219   }
2220
2221   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2222               "ATS tells us to switch to address '%s' session %p for "
2223               "peer `%s' in state %s/%d (quota in/out %u %u )\n",
2224               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2225               session,
2226               GNUNET_i2s (peer),
2227               print_state (n->state),
2228               n->send_connect_ack,
2229               ntohl (bandwidth_in.value__),
2230               ntohl (bandwidth_out.value__));
2231
2232   if (NULL == session)
2233   {
2234     session = papi->get_session (papi->cls, address);
2235     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2236                 "Obtained new session for peer `%s' and  address '%s': %p\n",
2237                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2238   }
2239   if (NULL == session)
2240   {
2241     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2242                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
2243                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
2244     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2245     return;
2246   }
2247   switch (n->state)
2248   {
2249   case S_NOT_CONNECTED:
2250     GNUNET_break (0);
2251     free_neighbour (n, GNUNET_NO);
2252     return;
2253   case S_INIT_ATS:
2254     set_address (&n->primary_address,
2255                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2256     n->state = S_INIT_BLACKLIST;
2257     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2258     check_blacklist (&n->id,
2259                      n->connect_ack_timestamp,
2260                      address, session);
2261     break;
2262   case S_INIT_BLACKLIST:
2263     /* ATS suggests a different address, switch again */
2264     set_address (&n->primary_address,
2265                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2266     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2267     check_blacklist (&n->id,
2268                      n->connect_ack_timestamp,
2269                      address, session);
2270     break;
2271   case S_CONNECT_SENT:
2272     /* ATS suggests a different address, switch again */
2273     set_address (&n->primary_address,
2274                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2275     n->state = S_INIT_BLACKLIST;
2276     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2277     check_blacklist (&n->id,
2278                      n->connect_ack_timestamp,
2279                      address, session);
2280     break;
2281   case S_CONNECT_RECV_ATS:
2282     set_address (&n->primary_address,
2283                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2284     n->state = S_CONNECT_RECV_BLACKLIST;
2285     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2286     check_blacklist (&n->id,
2287                      n->connect_ack_timestamp,
2288                      address, session);
2289     break;
2290   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2291     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2292     check_blacklist (&n->id,
2293                      n->connect_ack_timestamp,
2294                      address, session);
2295     break;
2296   case S_CONNECT_RECV_BLACKLIST:
2297   case S_CONNECT_RECV_ACK:
2298     /* ATS asks us to switch while we were trying to connect; switch to new
2299        address and check blacklist again */
2300     set_address (&n->primary_address,
2301                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2302     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2303     check_blacklist (&n->id,
2304                      n->connect_ack_timestamp,
2305                      address, session);
2306     break;
2307   case S_CONNECTED:
2308     GNUNET_assert (NULL != n->primary_address.address);
2309     GNUNET_assert (NULL != n->primary_address.session);
2310     if (n->primary_address.session == session)
2311     {
2312       /* not an address change, just a quota change */
2313       set_address (&n->primary_address,
2314                    address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2315       break;
2316     }
2317     /* ATS asks us to switch a life connection; see if we can get
2318        a CONNECT_ACK on it before we actually do this! */
2319     set_address (&n->alternative_address,
2320                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2321     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2322     check_blacklist (&n->id,
2323                      GNUNET_TIME_absolute_get (),
2324                      address, session);
2325     break;
2326   case S_RECONNECT_ATS:
2327     set_address (&n->primary_address,
2328                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2329     n->state = S_RECONNECT_BLACKLIST;
2330     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2331     check_blacklist (&n->id,
2332                      n->connect_ack_timestamp,
2333                      address, session);
2334     break;
2335   case S_RECONNECT_BLACKLIST:
2336     /* ATS asks us to switch while we were trying to reconnect; switch to new
2337        address and check blacklist again */
2338     set_address (&n->primary_address,
2339                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2340     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2341     check_blacklist (&n->id,
2342                      n->connect_ack_timestamp,
2343                      address, session);
2344     break;
2345   case S_RECONNECT_SENT:
2346     /* ATS asks us to switch while we were trying to reconnect; switch to new
2347        address and check blacklist again */
2348     set_address (&n->primary_address,
2349                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2350     n->state = S_RECONNECT_BLACKLIST;
2351     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2352     check_blacklist (&n->id,
2353                      n->connect_ack_timestamp,
2354                      address, session);
2355     break;
2356   case S_CONNECTED_SWITCHING_BLACKLIST:
2357     if (n->primary_address.session == session)
2358     {
2359       /* ATS switches back to still-active session */
2360       free_address (&n->alternative_address);
2361       n->state = S_CONNECTED;
2362       break;
2363     }
2364     /* ATS asks us to switch a life connection, update blacklist check */
2365     set_address (&n->alternative_address,
2366                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2367     check_blacklist (&n->id,
2368                      GNUNET_TIME_absolute_get (),
2369                      address, session);
2370     break;
2371   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2372     if (n->primary_address.session == session)
2373     {
2374       /* ATS switches back to still-active session */
2375       free_address (&n->alternative_address);
2376       n->state = S_CONNECTED;
2377       break;
2378     }
2379     /* ATS asks us to switch a life connection, update blacklist check */
2380     set_address (&n->alternative_address,
2381                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2382     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2383     check_blacklist (&n->id,
2384                      GNUNET_TIME_absolute_get (),
2385                      address, session);
2386     break;
2387   case S_DISCONNECT:
2388     /* not going to switch addresses while disconnecting */
2389     return;
2390   case S_DISCONNECT_FINISHED:
2391     GNUNET_assert (0);
2392     break;
2393   default:
2394     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2395                 "Unhandled state `%s'\n",
2396                 print_state (n->state));
2397     GNUNET_break (0);
2398     break;
2399   }
2400 }
2401
2402
2403 static int
2404 send_utilization_data (void *cls,
2405                        const struct GNUNET_PeerIdentity *key,
2406                        void *value)
2407 {
2408   struct NeighbourMapEntry *n = value;
2409   struct GNUNET_ATS_Information atsi[4];
2410   uint32_t bps_pl_in;
2411   uint32_t bps_pl_out;
2412   uint32_t bps_in;
2413   uint32_t bps_out;
2414   struct GNUNET_TIME_Relative delta;
2415
2416   delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2417                                                GNUNET_TIME_absolute_get ());
2418
2419   bps_pl_in = 0;
2420
2421   if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2422     bps_pl_in =  (1000LL * 1000LL *  n->util_payload_bytes_recv) / (delta.rel_value_us);
2423   bps_pl_out = 0;
2424   if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2425     bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2427               "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2428               GNUNET_i2s (key),
2429               bps_pl_in,
2430               bps_pl_out);
2431   bps_in = 0;
2432   if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2433     bps_in =  (1000LL * 1000LL *  n->util_total_bytes_recv) / (delta.rel_value_us);
2434   bps_out = 0;
2435   if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2436     bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2437
2438
2439   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2440               "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2441               GNUNET_i2s (key),
2442               bps_in,
2443               bps_out);
2444   atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2445   atsi[0].value = htonl (bps_out);
2446   atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2447   atsi[1].value = htonl (bps_in);
2448
2449   atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2450   atsi[2].value = htonl (bps_pl_out);
2451   atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2452   atsi[3].value = htonl (bps_pl_in);
2453
2454   GST_ats_update_metrics (key, n->primary_address.address,
2455       n->primary_address.session, atsi, 4);
2456   n->util_payload_bytes_recv = 0;
2457   n->util_payload_bytes_sent = 0;
2458   n->util_total_bytes_recv = 0;
2459   n->util_total_bytes_sent = 0;
2460   n->last_util_transmission = GNUNET_TIME_absolute_get();
2461   return GNUNET_OK;
2462 }
2463
2464
2465 /**
2466  * Task transmitting utilization in a regular interval
2467  *
2468  * @param cls the 'struct NeighbourMapEntry' for which we are running
2469  * @param tc scheduler context (unused)
2470  */
2471 static void
2472 utilization_transmission (void *cls,
2473                           const struct GNUNET_SCHEDULER_TaskContext *tc)
2474 {
2475   util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2476
2477   if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2478     GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2479
2480   util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2481       utilization_transmission, NULL);
2482
2483 }
2484
2485 void
2486 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2487                  const struct GNUNET_HELLO_Address *address,
2488                  struct Session *session,
2489                  const struct GNUNET_MessageHeader *message)
2490 {
2491   struct NeighbourMapEntry *n;
2492   n = lookup_neighbour (peer);
2493   if (NULL == n)
2494   {
2495       return;
2496   }
2497   n->util_total_bytes_recv += ntohs(message->size);
2498 }
2499
2500 void
2501 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2502                  const struct GNUNET_HELLO_Address *address,
2503                  struct Session *session,
2504                  const struct GNUNET_MessageHeader *message)
2505 {
2506   struct NeighbourMapEntry *n;
2507   n = lookup_neighbour (peer);
2508   if (NULL == n)
2509   {
2510       return;
2511   }
2512   n->util_payload_bytes_recv += ntohs(message->size);
2513 }
2514
2515
2516 void
2517 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2518                                  const struct GNUNET_HELLO_Address *address,
2519                                  struct Session *session,
2520                                  size_t size)
2521 {
2522   struct NeighbourMapEntry *n;
2523   n = lookup_neighbour (peer);
2524   if (NULL == n)
2525       return;
2526   if (n->primary_address.session != session)
2527     return;
2528   n->util_total_bytes_sent += size;
2529 }
2530
2531
2532 void
2533 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2534                                     size_t size)
2535 {
2536   struct NeighbourMapEntry *n;
2537   n = lookup_neighbour (peer);
2538   if (NULL == n)
2539     return;
2540   n->util_payload_bytes_sent += size;
2541 }
2542
2543
2544 /**
2545  * Master task run for every neighbour.  Performs all of the time-related
2546  * activities (keep alive, send next message, disconnect if idle, finish
2547  * clean up after disconnect).
2548  *
2549  * @param cls the 'struct NeighbourMapEntry' for which we are running
2550  * @param tc scheduler context (unused)
2551  */
2552 static void
2553 master_task (void *cls,
2554              const struct GNUNET_SCHEDULER_TaskContext *tc)
2555 {
2556   struct NeighbourMapEntry *n = cls;
2557   struct GNUNET_TIME_Relative delay;
2558
2559   n->task = GNUNET_SCHEDULER_NO_TASK;
2560   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2562               "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2563               GNUNET_i2s (&n->id),
2564               print_state(n->state),
2565               GNUNET_STRINGS_relative_time_to_string (delay,
2566                                                       GNUNET_YES));
2567   switch (n->state)
2568   {
2569   case S_NOT_CONNECTED:
2570     /* invalid state for master task, clean up */
2571     GNUNET_break (0);
2572     n->state = S_DISCONNECT_FINISHED;
2573     free_neighbour (n, GNUNET_NO);
2574     return;
2575   case S_INIT_ATS:
2576     if (0 == delay.rel_value_us)
2577     {
2578       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2579                   "Connection to `%s' timed out waiting for ATS to provide address\n",
2580                   GNUNET_i2s (&n->id));
2581       n->state = S_DISCONNECT_FINISHED;
2582       free_neighbour (n, GNUNET_NO);
2583       return;
2584     }
2585     break;
2586   case S_INIT_BLACKLIST:
2587     if (0 == delay.rel_value_us)
2588     {
2589       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2590                   "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2591                   GNUNET_i2s (&n->id));
2592       n->state = S_DISCONNECT_FINISHED;
2593       free_neighbour (n, GNUNET_NO);
2594       return;
2595     }
2596     break;
2597   case S_CONNECT_SENT:
2598     if (0 == delay.rel_value_us)
2599     {
2600       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2601                   "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2602                   GNUNET_i2s (&n->id));
2603       disconnect_neighbour (n);
2604       return;
2605     }
2606     break;
2607   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2608     if (0 == delay.rel_value_us)
2609     {
2610       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2611                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2612                   GNUNET_i2s (&n->id));
2613       n->state = S_DISCONNECT_FINISHED;
2614       free_neighbour (n, GNUNET_NO);
2615       return;
2616     }
2617     break;
2618   case S_CONNECT_RECV_ATS:
2619     if (0 == delay.rel_value_us)
2620     {
2621       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2622                   "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2623                   GNUNET_i2s (&n->id));
2624       n->state = S_DISCONNECT_FINISHED;
2625       free_neighbour (n, GNUNET_NO);
2626       return;
2627     }
2628     break;
2629   case S_CONNECT_RECV_BLACKLIST:
2630     if (0 == delay.rel_value_us)
2631     {
2632       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2633                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2634                   GNUNET_i2s (&n->id));
2635       n->state = S_DISCONNECT_FINISHED;
2636       free_neighbour (n, GNUNET_NO);
2637       return;
2638     }
2639     break;
2640   case S_CONNECT_RECV_ACK:
2641     if (0 == delay.rel_value_us)
2642     {
2643       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2644                   "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2645                   GNUNET_i2s (&n->id));
2646       disconnect_neighbour (n);
2647       return;
2648     }
2649     break;
2650   case S_CONNECTED:
2651     if (0 == delay.rel_value_us)
2652     {
2653       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2654                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2655                   GNUNET_i2s (&n->id));
2656       disconnect_neighbour (n);
2657       return;
2658     }
2659     try_transmission_to_peer (n);
2660     send_keepalive (n);
2661     break;
2662   case S_RECONNECT_ATS:
2663     if (0 == delay.rel_value_us)
2664     {
2665       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2666                   "Connection to `%s' timed out, waiting for ATS replacement address\n",
2667                   GNUNET_i2s (&n->id));
2668       disconnect_neighbour (n);
2669       return;
2670     }
2671     break;
2672   case S_RECONNECT_BLACKLIST:
2673     if (0 == delay.rel_value_us)
2674     {
2675       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2676                   "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2677                   GNUNET_i2s (&n->id));
2678       disconnect_neighbour (n);
2679       return;
2680     }
2681     break;
2682   case S_RECONNECT_SENT:
2683     if (0 == delay.rel_value_us)
2684     {
2685       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2686                   "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2687                   GNUNET_i2s (&n->id));
2688       disconnect_neighbour (n);
2689       return;
2690     }
2691     break;
2692   case S_CONNECTED_SWITCHING_BLACKLIST:
2693     if (0 == delay.rel_value_us)
2694     {
2695       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2696                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2697                   GNUNET_i2s (&n->id));
2698       disconnect_neighbour (n);
2699       return;
2700     }
2701     try_transmission_to_peer (n);
2702     send_keepalive (n);
2703     break;
2704   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2705     if (0 == delay.rel_value_us)
2706     {
2707       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2708                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2709                   GNUNET_i2s (&n->id));
2710       disconnect_neighbour (n);
2711       return;
2712     }
2713     try_transmission_to_peer (n);
2714     send_keepalive (n);
2715     break;
2716   case S_DISCONNECT:
2717     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2718                 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2719                 GNUNET_i2s (&n->id));
2720     free_neighbour (n, GNUNET_NO);
2721     return;
2722   case S_DISCONNECT_FINISHED:
2723     /* how did we get here!? */
2724     GNUNET_assert (0);
2725     break;
2726   default:
2727     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2728                 "Unhandled state `%s'\n",
2729                 print_state (n->state));
2730     GNUNET_break (0);
2731     break;
2732   }
2733   if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2734        (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2735        (S_CONNECTED == n->state) )
2736   {
2737     /* if we are *now* in one of these three states, we're sending
2738        keep alive messages, so we need to consider the keepalive
2739        delay, not just the connection timeout */
2740     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2741                                       delay);
2742   }
2743   if (GNUNET_SCHEDULER_NO_TASK == n->task)
2744     n->task = GNUNET_SCHEDULER_add_delayed (delay,
2745                                             &master_task,
2746                                             n);
2747 }
2748
2749
2750 /**
2751  * Send a SESSION_ACK message to the neighbour to confirm that we
2752  * got his CONNECT_ACK.
2753  *
2754  * @param n neighbour to send the SESSION_ACK to
2755  */
2756 static void
2757 send_session_ack_message (struct NeighbourMapEntry *n)
2758 {
2759   struct GNUNET_MessageHeader msg;
2760
2761   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2762   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2763   (void) send_with_session(n,
2764                            (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2765                            UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2766                            NULL, NULL);
2767 }
2768
2769
2770 /**
2771  * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2772  * Consider switching to it.
2773  *
2774  * @param message possibly a 'struct SessionConnectMessage' (check format)
2775  * @param peer identity of the peer to switch the address for
2776  * @param address address of the other peer, NULL if other peer
2777  *                       connected to us
2778  * @param session session to use (or NULL)
2779  */
2780 void
2781 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2782                                    const struct GNUNET_PeerIdentity *peer,
2783                                    const struct GNUNET_HELLO_Address *address,
2784                                    struct Session *session)
2785 {
2786   const struct SessionConnectMessage *scm;
2787   struct GNUNET_TIME_Absolute ts;
2788   struct NeighbourMapEntry *n;
2789
2790   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2791               "Received CONNECT_ACK message from peer `%s'\n",
2792               GNUNET_i2s (peer));
2793
2794   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2795   {
2796     GNUNET_break_op (0);
2797     return;
2798   }
2799   GNUNET_STATISTICS_update (GST_stats,
2800                             gettext_noop
2801                             ("# CONNECT_ACK messages received"),
2802                             1, GNUNET_NO);
2803   scm = (const struct SessionConnectMessage *) message;
2804   GNUNET_break_op (ntohl (scm->reserved) == 0);
2805   if (NULL == (n = lookup_neighbour (peer)))
2806   {
2807     GNUNET_STATISTICS_update (GST_stats,
2808                               gettext_noop
2809                               ("# unexpected CONNECT_ACK messages (no peer)"),
2810                               1, GNUNET_NO);
2811     return;
2812   }
2813   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2814   switch (n->state)
2815   {
2816   case S_NOT_CONNECTED:
2817     GNUNET_break (0);
2818     free_neighbour (n, GNUNET_NO);
2819     return;
2820   case S_INIT_ATS:
2821   case S_INIT_BLACKLIST:
2822     GNUNET_STATISTICS_update (GST_stats,
2823                               gettext_noop
2824                               ("# unexpected CONNECT_ACK messages (not ready)"),
2825                               1, GNUNET_NO);
2826     break;
2827   case S_CONNECT_SENT:
2828     if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
2829       break; /* ACK does not match our original CONNECT message */
2830     n->state = S_CONNECTED;
2831     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2832     GNUNET_STATISTICS_set (GST_stats,
2833                            gettext_noop ("# peers connected"),
2834                            ++neighbours_connected,
2835                            GNUNET_NO);
2836     connect_notify_cb (callback_cls, &n->id,
2837                        n->primary_address.bandwidth_in,
2838                        n->primary_address.bandwidth_out);
2839     /* Tell ATS that the outbound session we created to send CONNECT was successfull */
2840     GST_ats_add_address (n->primary_address.address, n->primary_address.session);
2841     set_address (&n->primary_address,
2842                  n->primary_address.address,
2843                  n->primary_address.session,
2844                  n->primary_address.bandwidth_in,
2845                  n->primary_address.bandwidth_out,
2846                  GNUNET_YES);
2847     send_session_ack_message (n);
2848     break;
2849   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2850   case S_CONNECT_RECV_ATS:
2851   case S_CONNECT_RECV_BLACKLIST:
2852   case S_CONNECT_RECV_ACK:
2853     GNUNET_STATISTICS_update (GST_stats,
2854                               gettext_noop
2855                               ("# unexpected CONNECT_ACK messages (not ready)"),
2856                               1, GNUNET_NO);
2857     break;
2858   case S_CONNECTED:
2859     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2860     send_session_ack_message (n);
2861     break;
2862   case S_RECONNECT_ATS:
2863   case S_RECONNECT_BLACKLIST:
2864     /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2865        to give us a new address... */
2866     GNUNET_STATISTICS_update (GST_stats,
2867                               gettext_noop
2868                               ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2869                               1, GNUNET_NO);
2870     break;
2871   case S_RECONNECT_SENT:
2872     /* new address worked; go back to connected! */
2873     n->state = S_CONNECTED;
2874     send_session_ack_message (n);
2875     break;
2876   case S_CONNECTED_SWITCHING_BLACKLIST:
2877     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2878     send_session_ack_message (n);
2879     break;
2880   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2881     /* new address worked; adopt it and go back to connected! */
2882     n->state = S_CONNECTED;
2883     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2884     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2885
2886     GST_ats_add_address (n->alternative_address.address, n->alternative_address.session);
2887     set_address (&n->primary_address,
2888                  n->alternative_address.address,
2889                  n->alternative_address.session,
2890                  n->alternative_address.bandwidth_in,
2891                  n->alternative_address.bandwidth_out,
2892                  GNUNET_YES);
2893     free_address (&n->alternative_address);
2894     send_session_ack_message (n);
2895     break;
2896   case S_DISCONNECT:
2897     GNUNET_STATISTICS_update (GST_stats,
2898                               gettext_noop
2899                               ("# unexpected CONNECT_ACK messages (disconnecting)"),
2900                               1, GNUNET_NO);
2901     break;
2902   case S_DISCONNECT_FINISHED:
2903     GNUNET_assert (0);
2904     break;
2905   default:
2906     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2907                 "Unhandled state `%s'\n",
2908                 print_state (n->state));
2909     GNUNET_break (0);
2910     break;
2911   }
2912 }
2913
2914
2915 /**
2916  * A session was terminated. Take note; if needed, try to get
2917  * an alternative address from ATS.
2918  *
2919  * @param peer identity of the peer where the session died
2920  * @param session session that is gone
2921  * @return #GNUNET_YES if this was a session used, #GNUNET_NO if
2922  *        this session was not in use
2923  */
2924 int
2925 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2926                                    struct Session *session)
2927 {
2928   struct NeighbourMapEntry *n;
2929   struct BlackListCheckContext *bcc;
2930   struct BlackListCheckContext *bcc_next;
2931
2932   /* make sure to cancel all ongoing blacklist checks involving 'session' */
2933   bcc_next = bc_head;
2934   while (NULL != (bcc = bcc_next))
2935   {
2936     bcc_next = bcc->next;
2937     if (bcc->na.session == session)
2938     {
2939       GST_blacklist_test_cancel (bcc->bc);
2940       GNUNET_HELLO_address_free (bcc->na.address);
2941       GNUNET_CONTAINER_DLL_remove (bc_head,
2942                                    bc_tail,
2943                                    bcc);
2944       GNUNET_free (bcc);
2945     }
2946   }
2947   if (NULL == (n = lookup_neighbour (peer)))
2948     return GNUNET_NO; /* can't affect us */
2949   if (session != n->primary_address.session)
2950   {
2951     if (session == n->alternative_address.session)
2952     {
2953       free_address (&n->alternative_address);
2954       if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2955            (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
2956         n->state = S_CONNECTED;
2957       else
2958         GNUNET_break (0);
2959     }
2960     return GNUNET_NO; /* doesn't affect us further */
2961   }
2962
2963   n->expect_latency_response = GNUNET_NO;
2964   switch (n->state)
2965   {
2966   case S_NOT_CONNECTED:
2967     GNUNET_break (0);
2968     free_neighbour (n, GNUNET_NO);
2969     return GNUNET_YES;
2970   case S_INIT_ATS:
2971     GNUNET_break (0);
2972     free_neighbour (n, GNUNET_NO);
2973     return GNUNET_YES;
2974   case S_INIT_BLACKLIST:
2975   case S_CONNECT_SENT:
2976     free_address (&n->primary_address);
2977     n->state = S_INIT_ATS;
2978     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2979     // FIXME: need to ask ATS for suggestions again?
2980     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2981     break;
2982   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2983   case S_CONNECT_RECV_ATS:
2984   case S_CONNECT_RECV_BLACKLIST:
2985   case S_CONNECT_RECV_ACK:
2986     /* error on inbound session; free neighbour entirely */
2987     free_address (&n->primary_address);
2988     free_neighbour (n, GNUNET_NO);
2989     return GNUNET_YES;
2990   case S_CONNECTED:
2991     free_address (&n->primary_address);
2992     n->state = S_RECONNECT_ATS;
2993     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2994     /* FIXME: is this ATS call needed? */
2995     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2996     break;
2997   case S_RECONNECT_ATS:
2998     /* we don't have an address, how can it go down? */
2999     GNUNET_break (0);
3000     break;
3001   case S_RECONNECT_BLACKLIST:
3002   case S_RECONNECT_SENT:
3003     n->state = S_RECONNECT_ATS;
3004     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
3005     // FIXME: need to ask ATS for suggestions again?
3006     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
3007     break;
3008   case S_CONNECTED_SWITCHING_BLACKLIST:
3009     /* primary went down while we were checking secondary against
3010        blacklist, adopt secondary as primary */
3011     free_address (&n->primary_address);
3012     n->primary_address = n->alternative_address;
3013     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3014     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3015     n->state = S_RECONNECT_BLACKLIST;
3016     break;
3017   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3018     /* primary went down while we were waiting for CONNECT_ACK on secondary;
3019        secondary as primary */
3020     free_address (&n->primary_address);
3021     n->primary_address = n->alternative_address;
3022     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3023     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3024     n->state = S_RECONNECT_SENT;
3025     break;
3026   case S_DISCONNECT:
3027     free_address (&n->primary_address);
3028     break;
3029   case S_DISCONNECT_FINISHED:
3030     /* neighbour was freed and plugins told to terminate session */
3031     return GNUNET_NO;
3032     break;
3033   default:
3034     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3035                 "Unhandled state `%s'\n",
3036                 print_state (n->state));
3037     GNUNET_break (0);
3038     break;
3039   }
3040   if (GNUNET_SCHEDULER_NO_TASK != n->task)
3041     GNUNET_SCHEDULER_cancel (n->task);
3042   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3043   return GNUNET_YES;
3044 }
3045
3046
3047 /**
3048  * We received a 'SESSION_ACK' message from the other peer.
3049  * If we sent a 'CONNECT_ACK' last, this means we are now
3050  * connected.  Otherwise, do nothing.
3051  *
3052  * @param message possibly a 'struct SessionConnectMessage' (check format)
3053  * @param peer identity of the peer to switch the address for
3054  * @param address address of the other peer, NULL if other peer
3055  *                       connected to us
3056  * @param session session to use (or NULL)
3057  */
3058 void
3059 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3060                                    const struct GNUNET_PeerIdentity *peer,
3061                                    const struct GNUNET_HELLO_Address *address,
3062                                    struct Session *session)
3063 {
3064   struct NeighbourMapEntry *n;
3065
3066   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3067               "Received SESSION_ACK message from peer `%s'\n",
3068               GNUNET_i2s (peer));
3069   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3070   {
3071     GNUNET_break_op (0);
3072     return;
3073   }
3074   GNUNET_STATISTICS_update (GST_stats,
3075                             gettext_noop
3076                             ("# SESSION_ACK messages received"),
3077                             1, GNUNET_NO);
3078   if (NULL == (n = lookup_neighbour (peer)))
3079     return;
3080   /* check if we are in a plausible state for having sent
3081      a CONNECT_ACK.  If not, return, otherwise break */
3082   if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3083          (S_CONNECT_SENT != n->state) ) ||
3084        (2 != n->send_connect_ack) )
3085   {
3086     GNUNET_STATISTICS_update (GST_stats,
3087                               gettext_noop ("# unexpected SESSION ACK messages"), 1,
3088                               GNUNET_NO);
3089     return;
3090   }
3091   n->state = S_CONNECTED;
3092   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3093   GNUNET_STATISTICS_set (GST_stats,
3094                          gettext_noop ("# peers connected"),
3095                          ++neighbours_connected,
3096                          GNUNET_NO);
3097   connect_notify_cb (callback_cls, &n->id,
3098                      n->primary_address.bandwidth_in,
3099                      n->primary_address.bandwidth_out);
3100
3101   GST_ats_add_address (n->primary_address.address, n->primary_address.session);
3102   set_address (&n->primary_address,
3103                n->primary_address.address,
3104                n->primary_address.session,
3105                n->primary_address.bandwidth_in,
3106                n->primary_address.bandwidth_out,
3107                GNUNET_YES);
3108 }
3109
3110
3111 /**
3112  * Test if we're connected to the given peer.
3113  *
3114  * @param target peer to test
3115  * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3116  */
3117 int
3118 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3119 {
3120   return test_connected (lookup_neighbour (target));
3121 }
3122
3123
3124 /**
3125  * Change the incoming quota for the given peer.
3126  *
3127  * @param neighbour identity of peer to change qutoa for
3128  * @param quota new quota
3129  */
3130 void
3131 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3132                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
3133 {
3134   struct NeighbourMapEntry *n;
3135
3136   if (NULL == (n = lookup_neighbour (neighbour)))
3137   {
3138     GNUNET_STATISTICS_update (GST_stats,
3139                               gettext_noop
3140                               ("# SET QUOTA messages ignored (no such peer)"),
3141                               1, GNUNET_NO);
3142     return;
3143   }
3144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3145               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3146               ntohl (quota.value__), GNUNET_i2s (&n->id));
3147   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3148   if (0 != ntohl (quota.value__))
3149     return;
3150   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3151               GNUNET_i2s (&n->id), "SET_QUOTA");
3152   if (GNUNET_YES == test_connected (n))
3153     GNUNET_STATISTICS_update (GST_stats,
3154                               gettext_noop ("# disconnects due to quota of 0"),
3155                               1, GNUNET_NO);
3156   disconnect_neighbour (n);
3157 }
3158
3159
3160 /**
3161  * We received a disconnect message from the given peer,
3162  * validate and process.
3163  *
3164  * @param peer sender of the message
3165  * @param msg the disconnect message
3166  */
3167 void
3168 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3169                                           *peer,
3170                                           const struct GNUNET_MessageHeader
3171                                           *msg)
3172 {
3173   struct NeighbourMapEntry *n;
3174   const struct SessionDisconnectMessage *sdm;
3175
3176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3177               "Received DISCONNECT message from peer `%s'\n",
3178               GNUNET_i2s (peer));
3179   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3180   {
3181     // GNUNET_break_op (0);
3182     GNUNET_STATISTICS_update (GST_stats,
3183                               gettext_noop
3184                               ("# disconnect messages ignored (old format)"), 1,
3185                               GNUNET_NO);
3186     return;
3187   }
3188   GNUNET_STATISTICS_update (GST_stats,
3189                             gettext_noop
3190                             ("# DISCONNECT messages received"),
3191                             1, GNUNET_NO);
3192   sdm = (const struct SessionDisconnectMessage *) msg;
3193   if (NULL == (n = lookup_neighbour (peer)))
3194     return;                     /* gone already */
3195   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3196   {
3197     GNUNET_STATISTICS_update (GST_stats,
3198                               gettext_noop
3199                               ("# disconnect messages ignored (timestamp)"), 1,
3200                               GNUNET_NO);
3201     return;
3202   }
3203   if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3204   {
3205     GNUNET_break_op (0);
3206     return;
3207   }
3208   if (ntohl (sdm->purpose.size) !=
3209       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3210       sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3211       sizeof (struct GNUNET_TIME_AbsoluteNBO))
3212   {
3213     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3214                 "%s message from peer `%s' has invalid size \n",
3215                 "DISCONNECT",
3216                 GNUNET_i2s (peer));
3217     GNUNET_break_op (0);
3218     return;
3219   }
3220   if (GNUNET_OK !=
3221       GNUNET_CRYPTO_eddsa_verify
3222       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3223        &sdm->signature, &sdm->public_key))
3224   {
3225     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3226                 "%s message from peer `%s' cannot be verified \n",
3227                 "DISCONNECT",
3228                 GNUNET_i2s (peer));
3229     GNUNET_break_op (0);
3230     return;
3231   }
3232   if (GNUNET_YES == test_connected (n))
3233     GNUNET_STATISTICS_update (GST_stats,
3234                               gettext_noop
3235                               ("# other peer asked to disconnect from us"), 1,
3236                               GNUNET_NO);
3237   disconnect_neighbour (n);
3238 }
3239
3240
3241 /**
3242  * Closure for the neighbours_iterate function.
3243  */
3244 struct IteratorContext
3245 {
3246   /**
3247    * Function to call on each connected neighbour.
3248    */
3249   GST_NeighbourIterator cb;
3250
3251   /**
3252    * Closure for 'cb'.
3253    */
3254   void *cb_cls;
3255 };
3256
3257
3258 /**
3259  * Call the callback from the closure for each connected neighbour.
3260  *
3261  * @param cls the `struct IteratorContext`
3262  * @param key the hash of the public key of the neighbour
3263  * @param value the `struct NeighbourMapEntry`
3264  * @return #GNUNET_OK (continue to iterate)
3265  */
3266 static int
3267 neighbours_iterate (void *cls,
3268                     const struct GNUNET_PeerIdentity *key,
3269                     void *value)
3270 {
3271   struct IteratorContext *ic = cls;
3272   struct NeighbourMapEntry *n = value;
3273   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3274   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3275
3276   if (GNUNET_YES != test_connected (n))
3277     return GNUNET_OK;
3278
3279   if (NULL != n->primary_address.address)
3280   {
3281     bandwidth_in = n->primary_address.bandwidth_in;
3282     bandwidth_out = n->primary_address.bandwidth_out;
3283   }
3284   else
3285   {
3286     bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3287     bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3288   }
3289   ic->cb (ic->cb_cls, &n->id,
3290           n->primary_address.address,
3291           bandwidth_in, bandwidth_out);
3292   return GNUNET_OK;
3293 }
3294
3295
3296 /**
3297  * Iterate over all connected neighbours.
3298  *
3299  * @param cb function to call
3300  * @param cb_cls closure for cb
3301  */
3302 void
3303 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3304 {
3305   struct IteratorContext ic;
3306
3307   if (NULL == neighbours)
3308     return; /* can happen during shutdown */
3309   ic.cb = cb;
3310   ic.cb_cls = cb_cls;
3311   GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3312 }
3313
3314
3315 /**
3316  * If we have an active connection to the given target, it must be shutdown.
3317  *
3318  * @param target peer to disconnect from
3319  */
3320 void
3321 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3322 {
3323   struct NeighbourMapEntry *n;
3324
3325   if (NULL == (n = lookup_neighbour (target)))
3326     return;  /* not active */
3327   if (GNUNET_YES == test_connected (n))
3328     GNUNET_STATISTICS_update (GST_stats,
3329                               gettext_noop
3330                               ("# disconnected from peer upon explicit request"), 1,
3331                               GNUNET_NO);
3332   disconnect_neighbour (n);
3333 }
3334
3335
3336 /**
3337  * Obtain current latency information for the given neighbour.
3338  *
3339  * @param peer to get the latency for
3340  * @return observed latency of the address, FOREVER if the
3341  *         the connection is not up
3342  */
3343 struct GNUNET_TIME_Relative
3344 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3345 {
3346   struct NeighbourMapEntry *n;
3347
3348   n = lookup_neighbour (peer);
3349   if (NULL == n)
3350     return GNUNET_TIME_UNIT_FOREVER_REL;
3351   switch (n->state)
3352   {
3353   case S_CONNECTED:
3354   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3355   case S_CONNECTED_SWITCHING_BLACKLIST:
3356   case S_RECONNECT_SENT:
3357   case S_RECONNECT_ATS:
3358   case S_RECONNECT_BLACKLIST:
3359     return n->latency;
3360   case S_NOT_CONNECTED:
3361   case S_INIT_BLACKLIST:
3362   case S_INIT_ATS:
3363   case S_CONNECT_RECV_BLACKLIST_INBOUND:
3364   case S_CONNECT_RECV_ATS:
3365   case S_CONNECT_RECV_BLACKLIST:
3366   case S_CONNECT_RECV_ACK:
3367   case S_CONNECT_SENT:
3368   case S_DISCONNECT:
3369   case S_DISCONNECT_FINISHED:
3370     return GNUNET_TIME_UNIT_FOREVER_REL;
3371   default:
3372     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3373                 "Unhandled state `%s'\n",
3374                 print_state (n->state));
3375     GNUNET_break (0);
3376     break;
3377   }
3378   return GNUNET_TIME_UNIT_FOREVER_REL;
3379 }
3380
3381
3382 /**
3383  * Obtain current address information for the given neighbour.
3384  *
3385  * @param peer
3386  * @return address currently used
3387  */
3388 struct GNUNET_HELLO_Address *
3389 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3390 {
3391   struct NeighbourMapEntry *n;
3392
3393   n = lookup_neighbour (peer);
3394   if (NULL == n)
3395     return NULL;
3396   return n->primary_address.address;
3397 }
3398
3399
3400 /**
3401  * Initialize the neighbours subsystem.
3402  *
3403  * @param cls closure for callbacks
3404  * @param connect_cb function to call if we connect to a peer
3405  * @param disconnect_cb function to call if we disconnect from a peer
3406  * @param peer_address_cb function to call if we change an active address
3407  *                   of a neighbour
3408  * @param max_fds maximum number of fds to use
3409  */
3410 void
3411 GST_neighbours_start (void *cls,
3412                       NotifyConnect connect_cb,
3413                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3414                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3415                       unsigned int max_fds)
3416 {
3417   callback_cls = cls;
3418   connect_notify_cb = connect_cb;
3419   disconnect_notify_cb = disconnect_cb;
3420   address_change_cb = peer_address_cb;
3421   neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3422   util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3423       utilization_transmission, NULL);
3424 }
3425
3426
3427 /**
3428  * Disconnect from the given neighbour.
3429  *
3430  * @param cls unused
3431  * @param key hash of neighbour's public key (not used)
3432  * @param value the 'struct NeighbourMapEntry' of the neighbour
3433  * @return #GNUNET_OK (continue to iterate)
3434  */
3435 static int
3436 disconnect_all_neighbours (void *cls,
3437                            const struct GNUNET_PeerIdentity *key,
3438                            void *value)
3439 {
3440   struct NeighbourMapEntry *n = value;
3441
3442   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3443               "Disconnecting peer `%4s', %s\n",
3444               GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3445   n->state = S_DISCONNECT_FINISHED;
3446   free_neighbour (n, GNUNET_NO);
3447   return GNUNET_OK;
3448 }
3449
3450
3451 /**
3452  * Cleanup the neighbours subsystem.
3453  */
3454 void
3455 GST_neighbours_stop ()
3456 {
3457   if (NULL == neighbours)
3458     return;
3459   if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3460   {
3461     GNUNET_SCHEDULER_cancel (util_transmission_tk);
3462     util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3463   }
3464
3465   GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3466                                          &disconnect_all_neighbours,
3467                                          NULL);
3468   GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3469   neighbours = NULL;
3470   callback_cls = NULL;
3471   connect_notify_cb = NULL;
3472   disconnect_notify_cb = NULL;
3473   address_change_cb = NULL;
3474 }
3475
3476
3477 /* end of file gnunet-service-transport_neighbours.c */